X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fcrypto_ecc.c;h=4bba395b32b8fc77a7d5425078ba7bf12ad20b3f;hb=225ea594e86e56160b0f89b5cace24291cc6184b;hp=1ef1ec7b53e5c2948f06cf9df6290fa4f8ac20c0;hpb=4b452a365b674cc69bf62be7d15950ccbdd7329c;p=oweals%2Fgnunet.git diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c index 1ef1ec7b5..4bba395b3 100644 --- a/src/util/crypto_ecc.c +++ b/src/util/crypto_ecc.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2001, 2002, 2003, 2004, 2005, 2006, 2009, 2012 Christian Grothoff (and other contributing authors) + Copyright (C) 2012, 2013, 2015 GNUnet e.V. GNUnet is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. GNUnet is distributed in the hope that it will be useful, but @@ -14,31 +14,29 @@ You should have received a copy of the GNU General Public License along with GNUnet; see the file COPYING. If not, write to the - Free Software Foundation, Inc., 59 Temple Place - Suite 330, - Boston, MA 02111-1307, USA. + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. */ /** * @file util/crypto_ecc.c * @brief public key cryptography (ECC) with libgcrypt * @author Christian Grothoff - * - * This is just a first, completely untested, draft hack for future ECC support. - * TODO: - * - declare public API in gnunet_crypto_lib (move some structs, etc.) - * - implement gnunet-ecc binary - * - convert existing RSA testcases - * - adjust encoding length and other parameters - * - actually test it! */ #include "platform.h" #include -#include "gnunet_common.h" -#include "gnunet_util_lib.h" +#include "gnunet_crypto_lib.h" +#include "gnunet_strings_lib.h" -#define EXTRA_CHECKS ALLOW_EXTRA_CHECKS +#define EXTRA_CHECKS 0 -#define CURVE "NIST P-521" +/** + * Name of the curve we are using. Note that we have hard-coded + * structs that use 256 bits, so using a bigger curve will require + * changes that break stuff badly. The name of the curve given here + * must be agreed by all peers and be supported by libgcrypt. + */ +#define CURVE "Ed25519" #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__) @@ -46,169 +44,12 @@ #define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename) - -/** - * FIXME: what is an acceptable value here? - */ -#define GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH 64 - -/** - * Length of the q-point (Q = dP) in the public key. - * FIXME: double-check that this is right. - */ -#define GNUNET_CRYPTO_ECC_PUBLIC_KEY_LENGTH 64 - - -/** - * @brief an ECC signature - */ -struct GNUNET_CRYPTO_EccSignature -{ - unsigned char sig[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; -}; - - -GNUNET_NETWORK_STRUCT_BEGIN - -/** - * @brief header of what an ECC signature signs - * this must be followed by "size - 8" bytes of - * the actual signed data - */ -struct GNUNET_CRYPTO_EccSignaturePurpose -{ - /** - * How many bytes does this signature sign? - * (including this purpose header); in network - * byte order (!). - */ - uint32_t size GNUNET_PACKED; - - /** - * What does this signature vouch for? This - * must contain a GNUNET_SIGNATURE_PURPOSE_XXX - * constant (from gnunet_signatures.h). In - * network byte order! - */ - uint32_t purpose GNUNET_PACKED; - -}; - - -/** - * Public ECC key (always for NIST P-521) encoded in a format suitable - * for network transmission. - */ -struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded -{ - /** - * Size of the encoding, in network byte order. - */ - uint16_t size; - - /** - * Actual length of the q-point binary encoding. - */ - uint16_t len; - - /** - * 0-padded q-point in binary encoding (GCRYPT_MPI_FMT_USG). - */ - unsigned char key[GNUNET_CRYPTO_ECC_PUBLIC_KEY_LENGTH]; -}; - - -struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded -{ - /** - * Overall size of the private key. - */ - uint16_t size; - - /** - * Size of the q and d components of the private key. - * Note that the other parameters are from NIST P-521. - */ - uint16_t sizes[2]; -}; - - -/** - * ECC Encrypted data. - */ -struct GNUNET_CRYPTO_EccEncryptedData -{ - unsigned char encoding[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; -}; - -GNUNET_NETWORK_STRUCT_END - - - -/** - * The private information of an ECC private key. - */ -struct GNUNET_CRYPTO_EccPrivateKey -{ - - /** - * Libgcrypt S-expression for the ECC key. - */ - gcry_sexp_t sexp; -}; - - -/** - * Function called upon completion of 'GNUNET_CRYPTO_ecc_key_create_async'. - * - * @param cls closure - * @param pk NULL on error, otherwise the private key (which must be free'd by the callee) - * @param emsg NULL on success, otherwise an error message - */ -typedef void (*GNUNET_CRYPTO_EccKeyCallback)(void *cls, - struct GNUNET_CRYPTO_EccPrivateKey *pk, - const char *emsg); - - - /** * Log an error message at log-level 'level' that indicates * a failure of the command 'cmd' with the message given * by gcry_strerror(rc). */ -#define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0); - - -/** - * If target != size, move target bytes to the - * end of the size-sized buffer and zero out the - * first target-size bytes. - * - * @param buf original buffer - * @param size number of bytes in the buffer - * @param target target size of the buffer - */ -static void -adjust (unsigned char *buf, size_t size, size_t target) -{ - if (size < target) - { - memmove (&buf[target - size], buf, size); - memset (buf, 0, target - size); - } -} - - -/** - * Free memory occupied by hostkey - * @param privatekey pointer to the memory to free - */ -void -GNUNET_CRYPTO_ecc_key_free (struct GNUNET_CRYPTO_EccPrivateKey *privatekey) -{ - gcry_sexp_release (privatekey->sexp); - GNUNET_free (privatekey); -} +#define LOG_GCRY(level, cmd, rc) do { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0) /** @@ -221,7 +62,9 @@ GNUNET_CRYPTO_ecc_key_free (struct GNUNET_CRYPTO_EccPrivateKey *privatekey) * @return 0 on success */ static int -key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, +key_from_sexp (gcry_mpi_t * array, + gcry_sexp_t sexp, + const char *topname, const char *elems) { gcry_sexp_t list; @@ -231,13 +74,13 @@ key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, unsigned int idx; list = gcry_sexp_find_token (sexp, topname, 0); - if (! list) - return 1; + if (! list) + return 1; l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; - if (! list) - return 2; + if (! list) + return 2; idx = 0; for (s = elems; *s; s++, idx++) @@ -272,1043 +115,826 @@ key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, /** - * Extract the public key for the given private key. - * - * @param priv the private key - * @param pub where to write the public key - */ -void -GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv, - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) -{ - gcry_mpi_t skey; - size_t size; - int rc; - - rc = key_from_sexp (&skey, priv->sexp, "public-key", "q"); - if (rc) - rc = key_from_sexp (&skey, priv->sexp, "private-key", "q"); - if (rc) - rc = key_from_sexp (&skey, priv->sexp, "ecc", "q"); - GNUNET_assert (0 == rc); - pub->size = htons (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)); - size = GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH; - GNUNET_assert (0 == - gcry_mpi_print (GCRYMPI_FMT_USG, pub->key, size, &size, - skey)); - pub->len = htons (size); - adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH); - gcry_mpi_release (skey); -} - - -/** - * Convert a public key to a string. - * - * @param pub key to convert - * @return string representing 'pub' - */ -char * -GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) -{ - char *pubkeybuf; - size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; - char *end; - - if (keylen % 5 > 0) - keylen += 5 - keylen % 5; - keylen /= 5; - pubkeybuf = GNUNET_malloc (keylen + 1); - end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, - sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded), - pubkeybuf, - keylen); - if (NULL == end) - { - GNUNET_free (pubkeybuf); - return NULL; - } - *end = '\0'; - return pubkeybuf; -} - - -/** - * Convert a string representing a public key to a public key. - * - * @param enc encoded public key - * @param enclen number of bytes in enc (without 0-terminator) - * @param pub where to store the public key - * @return GNUNET_OK on success - */ -int -GNUNET_CRYPTO_ecc_public_key_from_string (const char *enc, - size_t enclen, - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) -{ - size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; - - if (keylen % 5 > 0) - keylen += 5 - keylen % 5; - keylen /= 5; - if (enclen != keylen) - return GNUNET_SYSERR; - - if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, - (unsigned char*) pub, - sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded))) - return GNUNET_SYSERR; - if ( (ntohs (pub->size) != sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) || - (ntohs (pub->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) ) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -/** - * Convert the given public key from the network format to the + * Convert the given private key from the network format to the * S-expression that can be used by libgcrypt. * - * @param publicKey public key to decode + * @param priv private key to decode * @return NULL on error */ static gcry_sexp_t -decode_public_key (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicKey) +decode_private_ecdsa_key (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv) { gcry_sexp_t result; - gcry_mpi_t q; - size_t size; - size_t erroff; int rc; - if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) - { - GNUNET_break (0); - return NULL; - } - size = ntohs (publicKey->size); - if (0 != (rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, publicKey->key, size, &size))) + rc = gcry_sexp_build (&result, NULL, + "(private-key(ecc(curve \"" CURVE "\")" + "(d %b)))", + (int) sizeof (priv->d), priv->d); + if (0 != rc) { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); - return NULL; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + GNUNET_assert (0); } - rc = gcry_sexp_build (&result, &erroff, "(public-key(ecc((curve \"" CURVE "\")(q %m)))", q); - gcry_mpi_release (q); - if (0 != rc) +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (result))) { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ - return NULL; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + GNUNET_assert (0); } +#endif return result; } /** - * Encode the private key in a format suitable for - * storing it into a file. + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. * - * @returns encoding of the private key. - * The first 4 bytes give the size of the array, as usual. + * @param priv private key to decode + * @return NULL on error */ -struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded * -GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *hostkey) +static gcry_sexp_t +decode_private_eddsa_key (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv) { - struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *retval; - gcry_mpi_t pkv[2]; - void *pbu[2]; - size_t sizes[2]; - size_t off; + gcry_sexp_t result; int rc; - unsigned int i; - size_t size; - -#if EXTRA_CHECKS - if (gcry_pk_testkey (hostkey->sexp)) - { - GNUNET_break (0); - return NULL; - } -#endif - memset (pkv, 0, sizeof (gcry_mpi_t) * 2); - rc = key_from_sexp (pkv, hostkey->sexp, "private-key", "qd"); - if (rc) - rc = key_from_sexp (pkv, hostkey->sexp, "ecc", "qd"); - GNUNET_assert (0 == rc); - size = sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded); - for (i=0;i<2;i++) + rc = gcry_sexp_build (&result, NULL, + "(private-key(ecc(curve \"" CURVE "\")" + "(flags eddsa)(d %b)))", + (int)sizeof (priv->d), priv->d); + if (0 != rc) { - if (NULL != pkv[i]) - { - GNUNET_assert (0 == - gcry_mpi_aprint (GCRYMPI_FMT_USG, - (unsigned char **) &pbu[i], &sizes[i], - pkv[i])); - size += sizes[i]; - } - else - { - pbu[i] = NULL; - sizes[i] = 0; - } + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + GNUNET_assert (0); } - GNUNET_assert (size < 65536); - retval = GNUNET_malloc (size); - retval->size = htons (size); - off = 0; - for (i=0;i<2;i++) +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (result))) { - retval->sizes[i] = htons (sizes[0]); - memcpy (&((char *) (&retval[1]))[off], pbu[i], sizes[i]); - off += sizes[i]; - if (NULL != pkv[i]) - gcry_mpi_release (pkv[i]); - if (NULL != pbu[i]) - free (pbu[i]); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + GNUNET_assert (0); } - return retval; +#endif + return result; } /** - * Decode the private key from the file-format back - * to the "normal", internal format. + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. * - * @param buf the buffer where the private key data is stored - * @param len the length of the data in 'buffer' + * @param priv private key to decode * @return NULL on error */ -struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_decode_key (const char *buf, uint16_t len) +static gcry_sexp_t +decode_private_ecdhe_key (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv) { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - const struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *encoding = - (const struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *) buf; - gcry_sexp_t res; - gcry_mpi_t q; - gcry_mpi_t d; + gcry_sexp_t result; int rc; - size_t size; - size_t pos; - uint16_t enc_len; - size_t erroff; - enc_len = ntohs (encoding->size); - if (len != enc_len) - return NULL; - pos = 0; - size = ntohs (encoding->sizes[0]); - rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, - &((const unsigned char *) (&encoding[1]))[pos], size, - &size); - pos += ntohs (encoding->sizes[0]); - if (0 != rc) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); - return NULL; - } - size = ntohs (encoding->sizes[1]); - rc = gcry_mpi_scan (&d, GCRYMPI_FMT_USG, - &((const unsigned char *) (&encoding[1]))[pos], size, - &size); - pos += ntohs (encoding->sizes[1]); + rc = gcry_sexp_build (&result, NULL, + "(private-key(ecc(curve \"" CURVE "\")" + "(d %b)))", + (int)sizeof (priv->d), priv->d); if (0 != rc) { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); - gcry_mpi_release (d); - return NULL; - } - rc = gcry_sexp_build (&res, &erroff, - "(private-key(ecc(q %m)(d %m)(curve \"" CURVE "\")))", - q, d); - gcry_mpi_release (q); - gcry_mpi_release (d); - if (0 != rc) LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + GNUNET_assert (0); + } #if EXTRA_CHECKS - if (0 != (rc = gcry_pk_testkey (res))) + if (0 != (rc = gcry_pk_testkey (result))) { LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); - return NULL; + GNUNET_assert (0); } #endif - ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); - ret->sexp = res; - return ret; + return result; } /** - * Create a new private key. Caller must free return value. + * Extract the public key for the given private key. * - * @return fresh private key + * @param priv the private key + * @param pub where to write the public key */ -static struct GNUNET_CRYPTO_EccPrivateKey * -ecc_key_create () +void +GNUNET_CRYPTO_ecdsa_key_get_public (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - gcry_sexp_t s_key; - gcry_sexp_t s_keyparam; + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; - GNUNET_assert (0 == - gcry_sexp_build (&s_keyparam, NULL, - "(genkey(ecc(curve \"" CURVE "\")))")); - GNUNET_assert (0 == gcry_pk_genkey (&s_key, s_keyparam)); - gcry_sexp_release (s_keyparam); -#if EXTRA_CHECKS - GNUNET_assert (0 == gcry_pk_testkey (s_key)); -#endif - ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); - ret->sexp = s_key; - return ret; + sexp = decode_private_ecdsa_key (priv); + GNUNET_assert (NULL != sexp); + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, sexp, NULL)); + gcry_sexp_release (sexp); + q = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); + GNUNET_assert (NULL != q); + GNUNET_CRYPTO_mpi_print_unsigned (pub->q_y, sizeof (pub->q_y), q); + gcry_mpi_release (q); + gcry_ctx_release (ctx); } /** - * Try to read the private key from the given file. + * Extract the public key for the given private key. * - * @param filename file to read the key from - * @return NULL on error + * @param priv the private key + * @param pub where to write the public key */ -static struct GNUNET_CRYPTO_EccPrivateKey * -try_read_key (const char *filename) +void +GNUNET_CRYPTO_eddsa_key_get_public (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + struct GNUNET_CRYPTO_EddsaPublicKey *pub) { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc; - struct GNUNET_DISK_FileHandle *fd; - OFF_T fs; - uint16_t len; - - if (GNUNET_YES != GNUNET_DISK_file_test (filename)) - return NULL; - - /* hostkey file exists already, read it! */ - if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE))) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); - return NULL; - } - if (GNUNET_OK != (GNUNET_DISK_file_handle_size (fd, &fs))) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "stat", filename); - (void) GNUNET_DISK_file_close (fd); - return NULL; - } - if (0 == fs) - { - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); - return NULL; - } - if (fs > UINT16_MAX) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("File `%s' does not contain a valid private key (too long, %llu bytes). Deleting it.\n"), - filename, - (unsigned long long) fs); - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); - if (0 != UNLINK (filename)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); - return NULL; - } - - enc = GNUNET_malloc (fs); - GNUNET_break (fs == GNUNET_DISK_file_read (fd, enc, fs)); - len = ntohs (enc->size); - ret = NULL; - if ((len != fs) || - (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len)))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("File `%s' does not contain a valid private key (failed decode, %llu bytes). Deleting it.\n"), - filename, - (unsigned long long) fs); - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); - if (0 != UNLINK (filename)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); - GNUNET_free (enc); - return NULL; - } - GNUNET_free (enc); + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); - return ret; + sexp = decode_private_eddsa_key (priv); + GNUNET_assert (NULL != sexp); + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, sexp, NULL)); + gcry_sexp_release (sexp); + q = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); + GNUNET_assert (q); + GNUNET_CRYPTO_mpi_print_unsigned (pub->q_y, sizeof (pub->q_y), q); + gcry_mpi_release (q); + gcry_ctx_release (ctx); } /** - * Wait for a short time (we're trying to lock a file or want - * to give another process a shot at finishing a disk write, etc.). - * Sleeps for 100ms (as that should be long enough for virtually all - * modern systems to context switch and allow another process to do - * some 'real' work). + * Extract the public key for the given private key. + * + * @param priv the private key + * @param pub where to write the public key */ -static void -short_wait () +void +GNUNET_CRYPTO_ecdhe_key_get_public (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + struct GNUNET_CRYPTO_EcdhePublicKey *pub) { - struct GNUNET_TIME_Relative timeout; + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; - timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100); - (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout); + sexp = decode_private_ecdhe_key (priv); + GNUNET_assert (NULL != sexp); + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, sexp, NULL)); + gcry_sexp_release (sexp); + q = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); + GNUNET_assert (q); + GNUNET_CRYPTO_mpi_print_unsigned (pub->q_y, sizeof (pub->q_y), q); + gcry_mpi_release (q); + gcry_ctx_release (ctx); } /** - * Create a new private key by reading it from a file. If the - * files does not exist, create a new key and write it to the - * file. Caller must free return value. Note that this function - * can not guarantee that another process might not be trying - * the same operation on the same file at the same time. - * If the contents of the file - * are invalid the old file is deleted and a fresh key is - * created. + * Convert a public key to a string. * - * @return new private key, NULL on error (for example, - * permission denied) + * @param pub key to convert + * @return string representing @a pub */ -struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename) +char * +GNUNET_CRYPTO_ecdsa_public_key_to_string (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc; - uint16_t len; - struct GNUNET_DISK_FileHandle *fd; - unsigned int cnt; - int ec; - uint64_t fs; - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub; - struct GNUNET_PeerIdentity pid; - - if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename)) - return NULL; - while (GNUNET_YES != GNUNET_DISK_file_test (filename)) - { - fd = GNUNET_DISK_file_open (filename, - GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE - | GNUNET_DISK_OPEN_FAILIFEXISTS, - GNUNET_DISK_PERM_USER_READ | - GNUNET_DISK_PERM_USER_WRITE); - if (NULL == fd) - { - if (errno == EEXIST) - { - if (GNUNET_YES != GNUNET_DISK_file_test (filename)) - { - /* must exist but not be accessible, fail for good! */ - if (0 != ACCESS (filename, R_OK)) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename); - else - GNUNET_break (0); /* what is going on!? */ - return NULL; - } - continue; - } - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); - return NULL; - } - cnt = 0; + char *pubkeybuf; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8; + char *end; - while (GNUNET_YES != - GNUNET_DISK_file_lock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded), - GNUNET_YES)) - { - short_wait (); - if (0 == ++cnt % 10) - { - ec = errno; - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Could not acquire lock on file `%s': %s...\n"), filename, - STRERROR (ec)); - } - } - LOG (GNUNET_ERROR_TYPE_INFO, - _("Creating a new private key. This may take a while.\n")); - ret = ecc_key_create (); - GNUNET_assert (ret != NULL); - enc = GNUNET_CRYPTO_ecc_encode_key (ret); - GNUNET_assert (enc != NULL); - GNUNET_assert (ntohs (enc->size) == - GNUNET_DISK_file_write (fd, enc, ntohs (enc->size))); - GNUNET_free (enc); - - GNUNET_DISK_file_sync (fd); - if (GNUNET_YES != - GNUNET_DISK_file_unlock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); - GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd)); - GNUNET_CRYPTO_ecc_key_get_public (ret, &pub); - GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); - LOG (GNUNET_ERROR_TYPE_INFO, - _("I am host `%s'. Stored new private key in `%s'.\n"), - GNUNET_i2s (&pid), filename); - return ret; - } - /* hostkey file exists already, read it! */ - fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE); - if (NULL == fd) + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + pubkeybuf = GNUNET_malloc (keylen + 1); + end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey), + pubkeybuf, + keylen); + if (NULL == end) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); + GNUNET_free (pubkeybuf); return NULL; } - cnt = 0; - while (1) - { - if (GNUNET_YES != - GNUNET_DISK_file_lock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded), - GNUNET_NO)) - { - if (0 == ++cnt % 60) - { - ec = errno; - LOG (GNUNET_ERROR_TYPE_ERROR, - _("Could not acquire lock on file `%s': %s...\n"), filename, - STRERROR (ec)); - LOG (GNUNET_ERROR_TYPE_ERROR, - _ - ("This may be ok if someone is currently generating a hostkey.\n")); - } - short_wait (); - continue; - } - if (GNUNET_YES != GNUNET_DISK_file_test (filename)) - { - /* eh, what!? File we opened is now gone!? */ - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename); - if (GNUNET_YES != - GNUNET_DISK_file_unlock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); - GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd)); - - return NULL; - } - if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES, GNUNET_YES)) - fs = 0; - if (fs < sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded)) - { - /* maybe we got the read lock before the hostkey generating - * process had a chance to get the write lock; give it up! */ - if (GNUNET_YES != - GNUNET_DISK_file_unlock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); - if (0 == ++cnt % 10) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _ - ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"), - filename, (unsigned int) fs, - (unsigned int) sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded)); - LOG (GNUNET_ERROR_TYPE_ERROR, - _ - ("This may be ok if someone is currently generating a hostkey.\n")); - } - short_wait (); /* wait a bit longer! */ - continue; - } - break; - } - enc = GNUNET_malloc (fs); - GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs)); - len = ntohs (enc->size); - ret = NULL; - if ((len != fs) || - (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len)))) - { - LOG (GNUNET_ERROR_TYPE_ERROR, - _("File `%s' does not contain a valid private key. Deleting it.\n"), - filename); - if (0 != UNLINK (filename)) - { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename); - } - } - GNUNET_free (enc); - if (GNUNET_YES != - GNUNET_DISK_file_unlock (fd, 0, - sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))) - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename); - GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd)); - if (ret != NULL) + *end = '\0'; + return pubkeybuf; +} + + +/** + * Convert a public key to a string. + * + * @param pub key to convert + * @return string representing @a pub + */ +char * +GNUNET_CRYPTO_eddsa_public_key_to_string (const struct GNUNET_CRYPTO_EddsaPublicKey *pub) +{ + char *pubkeybuf; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)) * 8; + char *end; + + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + pubkeybuf = GNUNET_malloc (keylen + 1); + end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + pubkeybuf, + keylen); + if (NULL == end) { - GNUNET_CRYPTO_ecc_key_get_public (ret, &pub); - GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); - LOG (GNUNET_ERROR_TYPE_INFO, - _("I am host `%s'. Read private key from `%s'.\n"), GNUNET_i2s (&pid), - filename); + GNUNET_free (pubkeybuf); + return NULL; } - return ret; + *end = '\0'; + return pubkeybuf; } /** - * Handle to cancel private key generation and state for the - * key generation operation. + * Convert a string representing a public key to a public key. + * + * @param enc encoded public key + * @param enclen number of bytes in @a enc (without 0-terminator) + * @param pub where to store the public key + * @return #GNUNET_OK on success */ -struct GNUNET_CRYPTO_EccKeyGenerationContext +int +GNUNET_CRYPTO_ecdsa_public_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - - /** - * Continuation to call upon completion. - */ - GNUNET_CRYPTO_EccKeyCallback cont; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8; - /** - * Closure for 'cont'. - */ - void *cont_cls; + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + if (enclen != keylen) + return GNUNET_SYSERR; - /** - * Name of the file. - */ - char *filename; + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, + pub, + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) + return GNUNET_SYSERR; + return GNUNET_OK; +} - /** - * Handle to the helper process which does the key generation. - */ - struct GNUNET_OS_Process *gnunet_ecc; - - /** - * Handle to 'stdout' of gnunet-ecc. We 'read' on stdout to detect - * process termination (instead of messing with SIGCHLD). - */ - struct GNUNET_DISK_PipeHandle *gnunet_ecc_out; - /** - * Location where we store the private key if it already existed. - * (if this is used, 'filename', 'gnunet_ecc' and 'gnunet_ecc_out' will - * not be used). - */ - struct GNUNET_CRYPTO_EccPrivateKey *pk; - - /** - * Task reading from 'gnunet_ecc_out' to wait for process termination. - */ - GNUNET_SCHEDULER_TaskIdentifier read_task; - -}; +/** + * Convert a string representing a public key to a public key. + * + * @param enc encoded public key + * @param enclen number of bytes in @a enc (without 0-terminator) + * @param pub where to store the public key + * @return #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_eddsa_public_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EddsaPublicKey *pub) +{ + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)) * 8; + + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + if (enclen != keylen) + return GNUNET_SYSERR; + + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, + pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * Convert a string representing a private key to a private key. + * + * @param enc encoded public key + * @param enclen number of bytes in @a enc (without 0-terminator) + * @param priv where to store the private key + * @return #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_eddsa_private_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EddsaPrivateKey *pub) +{ + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)) * 8; + + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + if (enclen != keylen) + return GNUNET_SYSERR; + + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, + pub, + sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey))) + return GNUNET_SYSERR; + return GNUNET_OK; +} + + +/** + * @ingroup crypto + * Clear memory that was used to store a private key. + * + * @param pk location of the key + */ +void +GNUNET_CRYPTO_ecdhe_key_clear (struct GNUNET_CRYPTO_EcdhePrivateKey *pk) +{ + memset (pk, 0, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); +} + + +/** + * @ingroup crypto + * Clear memory that was used to store a private key. + * + * @param pk location of the key + */ +void +GNUNET_CRYPTO_ecdsa_key_clear (struct GNUNET_CRYPTO_EcdsaPrivateKey *pk) +{ + memset (pk, 0, sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey)); +} /** - * Abort ECC key generation. + * @ingroup crypto + * Clear memory that was used to store a private key. * - * @param gc key generation context to abort + * @param pk location of the key */ void -GNUNET_CRYPTO_ecc_key_create_stop (struct GNUNET_CRYPTO_EccKeyGenerationContext *gc) +GNUNET_CRYPTO_eddsa_key_clear (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) { - if (GNUNET_SCHEDULER_NO_TASK != gc->read_task) + memset (pk, 0, sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); +} + + +/** + * Create a new private key. Caller must free return value. + * + * @return fresh private key + */ +struct GNUNET_CRYPTO_EcdhePrivateKey * +GNUNET_CRYPTO_ecdhe_key_create () +{ + struct GNUNET_CRYPTO_EcdhePrivateKey *priv; + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + int rc; + + /* NOTE: For libgcrypt >= 1.7, we do not need the 'eddsa' flag here, + but should also be harmless. For libgcrypt < 1.7, using 'eddsa' + disables an expensive key testing routine. We do not want to run + the expensive check for ECDHE, as we generate TONS of keys to + use for a very short time. */ + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags eddsa no-keytest)))"))) { - GNUNET_SCHEDULER_cancel (gc->read_task); - gc->read_task = GNUNET_SCHEDULER_NO_TASK; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return NULL; } - if (NULL != gc->gnunet_ecc) + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) { - (void) GNUNET_OS_process_kill (gc->gnunet_ecc, SIGKILL); - GNUNET_break (GNUNET_OK == - GNUNET_OS_process_wait (gc->gnunet_ecc)); - GNUNET_OS_process_destroy (gc->gnunet_ecc); - GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); + return NULL; } - - if (NULL != gc->filename) + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (priv_sexp))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } +#endif + if (0 != (rc = key_from_sexp (&d, priv_sexp, "private-key", "d"))) { - if (0 != UNLINK (gc->filename)) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", gc->filename); - GNUNET_free (gc->filename); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc); + gcry_sexp_release (priv_sexp); + return NULL; } - if (NULL != gc->pk) - GNUNET_CRYPTO_ecc_key_free (gc->pk); - GNUNET_free (gc); + gcry_sexp_release (priv_sexp); + priv = GNUNET_new (struct GNUNET_CRYPTO_EcdhePrivateKey); + GNUNET_CRYPTO_mpi_print_unsigned (priv->d, sizeof (priv->d), d); + gcry_mpi_release (d); + return priv; } /** - * Task called upon shutdown or process termination of 'gnunet-ecc' during - * ECC key generation. Check where we are and perform the appropriate - * action. + * Create a new private key. Caller must free return value. + * + * @return fresh private key + */ +struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_CRYPTO_ecdsa_key_create () +{ + struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + int rc; + + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags)))"))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return NULL; + } + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); + return NULL; + } + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (priv_sexp))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } +#endif + if (0 != (rc = key_from_sexp (&d, priv_sexp, "private-key", "d"))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } + gcry_sexp_release (priv_sexp); + priv = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); + GNUNET_CRYPTO_mpi_print_unsigned (priv->d, sizeof (priv->d), d); + gcry_mpi_release (d); + return priv; +} + +/** + * Create a new private key. Caller must free return value. * - * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' - * @param tc scheduler context + * @return fresh private key */ -static void -check_key_generation_completion (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +struct GNUNET_CRYPTO_EddsaPrivateKey * +GNUNET_CRYPTO_eddsa_key_create () { - struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; - struct GNUNET_CRYPTO_EccPrivateKey *pk; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + int rc; - gc->read_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags eddsa)))"))) { - gc->cont (gc->cont_cls, NULL, _("interrupted by shutdown")); - GNUNET_CRYPTO_ecc_key_create_stop (gc); - return; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return NULL; } - GNUNET_assert (GNUNET_OK == - GNUNET_OS_process_wait (gc->gnunet_ecc)); - GNUNET_OS_process_destroy (gc->gnunet_ecc); - gc->gnunet_ecc = NULL; - if (NULL == (pk = try_read_key (gc->filename))) + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) { - GNUNET_break (0); - gc->cont (gc->cont_cls, NULL, _("gnunet-ecc failed")); - GNUNET_CRYPTO_ecc_key_create_stop (gc); - return; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); + return NULL; } - gc->cont (gc->cont_cls, pk, NULL); - GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); - GNUNET_free (gc->filename); - GNUNET_free (gc); + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (priv_sexp))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } +#endif + if (0 != (rc = key_from_sexp (&d, priv_sexp, "private-key", "d"))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc); + gcry_sexp_release (priv_sexp); + return NULL; + } + gcry_sexp_release (priv_sexp); + priv = GNUNET_new (struct GNUNET_CRYPTO_EddsaPrivateKey); + GNUNET_CRYPTO_mpi_print_unsigned (priv->d, sizeof (priv->d), d); + gcry_mpi_release (d); + return priv; } /** - * Return the private ECC key which already existed on disk - * (asynchronously) to the caller. + * Get the shared private key we use for anonymous users. * - * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' - * @param tc scheduler context (unused) + * @return "anonymous" private key */ -static void -async_return_key (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +const struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_CRYPTO_ecdsa_key_get_anonymous () { - struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; + /** + * 'anonymous' pseudonym (global static, d=1, public key = G + * (generator). + */ + static struct GNUNET_CRYPTO_EcdsaPrivateKey anonymous; + static int once; + + if (once) + return &anonymous; + GNUNET_CRYPTO_mpi_print_unsigned (anonymous.d, + sizeof (anonymous.d), + GCRYMPI_CONST_ONE); + once = 1; + return &anonymous; +} + - gc->cont (gc->cont_cls, - gc->pk, - NULL); - GNUNET_free (gc); +/** + * Compare two Peer Identities. + * + * @param first first peer identity + * @param second second peer identity + * @return bigger than 0 if first > second, + * 0 if they are the same + * smaller than 0 if second > first + */ +int +GNUNET_CRYPTO_cmp_peer_identity (const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second) +{ + return memcmp (first, second, sizeof (struct GNUNET_PeerIdentity)); } /** - * Create a new private key by reading it from a file. If the files - * does not exist, create a new key and write it to the file. If the - * contents of the file are invalid the old file is deleted and a - * fresh key is created. + * Convert the data specified in the given purpose argument to an + * S-expression suitable for signature operations. * - * @param filename name of file to use for storage - * @param cont function to call when done (or on errors) - * @param cont_cls closure for 'cont' - * @return handle to abort operation, NULL on fatal errors (cont will not be called if NULL is returned) + * @param purpose data to convert + * @return converted s-expression */ -struct GNUNET_CRYPTO_EccKeyGenerationContext * -GNUNET_CRYPTO_ecc_key_create_start (const char *filename, - GNUNET_CRYPTO_EccKeyCallback cont, - void *cont_cls) +static gcry_sexp_t +data_to_eddsa_value (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) { - struct GNUNET_CRYPTO_EccKeyGenerationContext *gc; - struct GNUNET_CRYPTO_EccPrivateKey *pk; - const char *weak_random; + struct GNUNET_HashCode hc; + gcry_sexp_t data; + int rc; - if (NULL != (pk = try_read_key (filename))) - { - /* quick happy ending: key already exists! */ - gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccKeyGenerationContext)); - gc->pk = pk; - gc->cont = cont; - gc->cont_cls = cont_cls; - gc->read_task = GNUNET_SCHEDULER_add_now (&async_return_key, - gc); - return gc; - } - gc = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccKeyGenerationContext)); - gc->filename = GNUNET_strdup (filename); - gc->cont = cont; - gc->cont_cls = cont_cls; - gc->gnunet_ecc_out = GNUNET_DISK_pipe (GNUNET_NO, - GNUNET_NO, - GNUNET_NO, - GNUNET_YES); - if (NULL == gc->gnunet_ecc_out) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "pipe"); - GNUNET_free (gc->filename); - GNUNET_free (gc); - return NULL; - } - weak_random = NULL; - if (GNUNET_YES == - GNUNET_CRYPTO_random_is_weak ()) - weak_random = "-w"; - gc->gnunet_ecc = GNUNET_OS_start_process (GNUNET_NO, - GNUNET_OS_INHERIT_STD_ERR, - NULL, - gc->gnunet_ecc_out, - "gnunet-ecc", - "gnunet-ecc", - gc->filename, - weak_random, - NULL); - if (NULL == gc->gnunet_ecc) + GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); + if (0 != (rc = gcry_sexp_build (&data, NULL, + "(data(flags eddsa)(hash-algo %s)(value %b))", + "sha512", + (int)sizeof (hc), &hc))) { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "fork"); - GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); - GNUNET_free (gc->filename); - GNUNET_free (gc); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); return NULL; } - GNUNET_assert (GNUNET_OK == - GNUNET_DISK_pipe_close_end (gc->gnunet_ecc_out, - GNUNET_DISK_PIPE_END_WRITE)); - gc->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, - GNUNET_DISK_pipe_handle (gc->gnunet_ecc_out, - GNUNET_DISK_PIPE_END_READ), - &check_key_generation_completion, - gc); - return gc; + return data; } /** - * Setup a hostkey file for a peer given the name of the - * configuration file (!). This function is used so that - * at a later point code can be certain that reading a - * hostkey is fast (for example in time-dependent testcases). + * Convert the data specified in the given purpose argument to an + * S-expression suitable for signature operations. * - * @param cfg_name name of the configuration file to use + * @param purpose data to convert + * @return converted s-expression */ -void -GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name) +static gcry_sexp_t +data_to_ecdsa_value (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) { - struct GNUNET_CONFIGURATION_Handle *cfg; - struct GNUNET_CRYPTO_EccPrivateKey *pk; - char *fn; - - cfg = GNUNET_CONFIGURATION_create (); - (void) GNUNET_CONFIGURATION_load (cfg, cfg_name); - if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY", &fn)) + struct GNUNET_HashCode hc; + gcry_sexp_t data; + int rc; + + GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); + if (0 != (rc = gcry_sexp_build (&data, NULL, + "(data(flags rfc6979)(hash %s %b))", + "sha512", + (int)sizeof (hc), &hc))) { - pk = GNUNET_CRYPTO_ecc_key_create_from_file (fn); - if (NULL != pk) - GNUNET_CRYPTO_ecc_key_free (pk); - GNUNET_free (fn); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return NULL; } - GNUNET_CONFIGURATION_destroy (cfg); + return data; } /** - * Encrypt a block with the public key of another host that uses the - * same cipher. + * Sign a given block. * - * @param block the block to encrypt - * @param size the size of block - * @param publicKey the encoded public key used to encrypt - * @param target where to store the encrypted block - * @returns GNUNET_SYSERR on error, GNUNET_OK if ok + * @param priv private key to use for the signing + * @param purpose what to sign (size, purpose) + * @param sig where to write the signature + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ int -GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, - const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded - *publicKey, - struct GNUNET_CRYPTO_EccEncryptedData *target) +GNUNET_CRYPTO_ecdsa_sign (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EcdsaSignature *sig) { - gcry_sexp_t result; + gcry_sexp_t priv_sexp; + gcry_sexp_t sig_sexp; gcry_sexp_t data; - gcry_sexp_t psexp; - gcry_mpi_t val; - gcry_mpi_t rval; - size_t isize; - size_t erroff; - - GNUNET_assert (size <= sizeof (struct GNUNET_HashCode)); - if (! (psexp = decode_public_key (publicKey))) + int rc; + gcry_mpi_t rs[2]; + + priv_sexp = decode_private_ecdsa_key (priv); + data = data_to_ecdsa_value (purpose); + if (0 != (rc = gcry_pk_sign (&sig_sexp, data, priv_sexp))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("ECC signing failed at %s:%d: %s\n"), __FILE__, + __LINE__, gcry_strerror (rc)); + gcry_sexp_release (data); + gcry_sexp_release (priv_sexp); return GNUNET_SYSERR; - isize = size; - GNUNET_assert (0 == - gcry_mpi_scan (&val, GCRYMPI_FMT_USG, block, isize, &isize)); - GNUNET_assert (0 == - gcry_sexp_build (&data, &erroff, - "(data (flags pkcs1)(value %m))", val)); - gcry_mpi_release (val); - GNUNET_assert (0 == gcry_pk_encrypt (&result, data, psexp)); + } + gcry_sexp_release (priv_sexp); gcry_sexp_release (data); - gcry_sexp_release (psexp); - GNUNET_assert (0 == key_from_sexp (&rval, result, "ecc", "a")); - gcry_sexp_release (result); - isize = sizeof (struct GNUNET_CRYPTO_EccEncryptedData); - GNUNET_assert (0 == - gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) target, - isize, &isize, rval)); - gcry_mpi_release (rval); - adjust (&target->encoding[0], isize, - sizeof (struct GNUNET_CRYPTO_EccEncryptedData)); + /* extract 'r' and 's' values from sexpression 'sig_sexp' and store in + 'signature' */ + if (0 != (rc = key_from_sexp (rs, sig_sexp, "sig-val", "rs"))) + { + GNUNET_break (0); + gcry_sexp_release (sig_sexp); + return GNUNET_SYSERR; + } + gcry_sexp_release (sig_sexp); + GNUNET_CRYPTO_mpi_print_unsigned (sig->r, sizeof (sig->r), rs[0]); + GNUNET_CRYPTO_mpi_print_unsigned (sig->s, sizeof (sig->s), rs[1]); + gcry_mpi_release (rs[0]); + gcry_mpi_release (rs[1]); return GNUNET_OK; } /** - * Decrypt a given block with the hostkey. + * Sign a given block. * - * @param key the key with which to decrypt this block - * @param block the data to decrypt, encoded as returned by encrypt - * @param result pointer to a location where the result can be stored - * @param max the maximum number of bits to store for the result, if - * the decrypted block is bigger, an error is returned - * @return the size of the decrypted block, -1 on error + * @param priv private key to use for the signing + * @param purpose what to sign (size, purpose) + * @param sig where to write the signature + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ -ssize_t -GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey * key, - const struct GNUNET_CRYPTO_EccEncryptedData * block, - void *result, size_t max) +int +GNUNET_CRYPTO_eddsa_sign (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EddsaSignature *sig) { - gcry_sexp_t resultsexp; + gcry_sexp_t priv_sexp; + gcry_sexp_t sig_sexp; gcry_sexp_t data; - size_t erroff; - size_t size; - gcry_mpi_t val; - unsigned char *endp; - unsigned char *tmp; + int rc; + gcry_mpi_t rs[2]; -#if EXTRA_CHECKS - GNUNET_assert (0 == gcry_pk_testkey (key->sexp)); -#endif - size = sizeof (struct GNUNET_CRYPTO_EccEncryptedData); - GNUNET_assert (0 == - gcry_mpi_scan (&val, GCRYMPI_FMT_USG, &block->encoding[0], - size, &size)); - GNUNET_assert (0 == - gcry_sexp_build (&data, &erroff, "(enc-val(flags)(ecc(a %m)))", - val)); - gcry_mpi_release (val); - GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, key->sexp)); + priv_sexp = decode_private_eddsa_key (priv); + data = data_to_eddsa_value (purpose); + if (0 != (rc = gcry_pk_sign (&sig_sexp, data, priv_sexp))) + { + LOG (GNUNET_ERROR_TYPE_WARNING, + _("EdDSA signing failed at %s:%d: %s\n"), __FILE__, + __LINE__, gcry_strerror (rc)); + gcry_sexp_release (data); + gcry_sexp_release (priv_sexp); + return GNUNET_SYSERR; + } + gcry_sexp_release (priv_sexp); gcry_sexp_release (data); - /* resultsexp has format "(value %m)" */ - GNUNET_assert (NULL != - (val = gcry_sexp_nth_mpi (resultsexp, 1, GCRYMPI_FMT_USG))); - gcry_sexp_release (resultsexp); - size = max + GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH * 2; - tmp = GNUNET_malloc (size); - GNUNET_assert (0 == gcry_mpi_print (GCRYMPI_FMT_USG, tmp, size, &size, val)); - gcry_mpi_release (val); - endp = tmp; - endp += (size - max); - size = max; - memcpy (result, endp, size); - GNUNET_free (tmp); - return size; + + /* extract 'r' and 's' values from sexpression 'sig_sexp' and store in + 'signature' */ + if (0 != (rc = key_from_sexp (rs, sig_sexp, "sig-val", "rs"))) + { + GNUNET_break (0); + gcry_sexp_release (sig_sexp); + return GNUNET_SYSERR; + } + gcry_sexp_release (sig_sexp); + GNUNET_CRYPTO_mpi_print_unsigned (sig->r, sizeof (sig->r), rs[0]); + GNUNET_CRYPTO_mpi_print_unsigned (sig->s, sizeof (sig->s), rs[1]); + gcry_mpi_release (rs[0]); + gcry_mpi_release (rs[1]); + return GNUNET_OK; } /** - * Sign a given block. + * Verify signature. * - * @param key private key to use for the signing - * @param purpose what to sign (size, purpose) - * @param sig where to write the signature - * @return GNUNET_SYSERR on error, GNUNET_OK on success + * @param purpose what is the purpose that the signature should have? + * @param validate block to validate (size, purpose, data) + * @param sig signature that is being validated + * @param pub public key of the signer + * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid */ int -GNUNET_CRYPTO_ecc_sign (const struct GNUNET_CRYPTO_EccPrivateKey *key, - const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, - struct GNUNET_CRYPTO_EccSignature *sig) +GNUNET_CRYPTO_ecdsa_verify (uint32_t purpose, + const struct GNUNET_CRYPTO_EccSignaturePurpose *validate, + const struct GNUNET_CRYPTO_EcdsaSignature *sig, + const struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - gcry_sexp_t result; gcry_sexp_t data; - size_t ssize; - gcry_mpi_t rval; - struct GNUNET_HashCode hc; - char *buff; - int bufSize; + gcry_sexp_t sig_sexpr; + gcry_sexp_t pub_sexpr; + int rc; - GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); -#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))" - bufSize = strlen (FORMATSTRING) + 1; - buff = GNUNET_malloc (bufSize); - memcpy (buff, FORMATSTRING, bufSize); - memcpy (&buff - [bufSize - - strlen - ("0123456789012345678901234567890123456789012345678901234567890123))") - - 1], &hc, sizeof (struct GNUNET_HashCode)); - GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); - GNUNET_free (buff); - GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp)); + if (purpose != ntohl (validate->purpose)) + return GNUNET_SYSERR; /* purpose mismatch */ + + /* build s-expression for signature */ + if (0 != (rc = gcry_sexp_build (&sig_sexpr, NULL, + "(sig-val(ecdsa(r %b)(s %b)))", + (int) sizeof (sig->r), sig->r, + (int) sizeof (sig->s), sig->s))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return GNUNET_SYSERR; + } + data = data_to_ecdsa_value (validate); + if (0 != (rc = gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int) sizeof (pub->q_y), pub->q_y))) + { + gcry_sexp_release (data); + gcry_sexp_release (sig_sexpr); + return GNUNET_SYSERR; + } + rc = gcry_pk_verify (sig_sexpr, data, pub_sexpr); + gcry_sexp_release (pub_sexpr); gcry_sexp_release (data); - GNUNET_assert (0 == key_from_sexp (&rval, result, "ecc", "s")); - gcry_sexp_release (result); - ssize = sizeof (struct GNUNET_CRYPTO_EccSignature); - GNUNET_assert (0 == - gcry_mpi_print (GCRYMPI_FMT_USG, (unsigned char *) sig, ssize, - &ssize, rval)); - gcry_mpi_release (rval); - adjust (sig->sig, ssize, sizeof (struct GNUNET_CRYPTO_EccSignature)); + gcry_sexp_release (sig_sexpr); + if (0 != rc) + { + LOG (GNUNET_ERROR_TYPE_INFO, + _("ECDSA signature verification failed at %s:%d: %s\n"), __FILE__, + __LINE__, gcry_strerror (rc)); + return GNUNET_SYSERR; + } return GNUNET_OK; } + /** * Verify signature. * * @param purpose what is the purpose that the signature should have? * @param validate block to validate (size, purpose, data) * @param sig signature that is being validated - * @param publicKey public key of the signer - * @returns GNUNET_OK if ok, GNUNET_SYSERR if invalid + * @param pub public key of the signer + * @returns #GNUNET_OK if ok, #GNUNET_SYSERR if invalid */ int -GNUNET_CRYPTO_ecc_verify (uint32_t purpose, - const struct GNUNET_CRYPTO_EccSignaturePurpose - *validate, - const struct GNUNET_CRYPTO_EccSignature *sig, - const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded - *publicKey) +GNUNET_CRYPTO_eddsa_verify (uint32_t purpose, + const struct GNUNET_CRYPTO_EccSignaturePurpose *validate, + const struct GNUNET_CRYPTO_EddsaSignature *sig, + const struct GNUNET_CRYPTO_EddsaPublicKey *pub) { gcry_sexp_t data; - gcry_sexp_t sigdata; - size_t size; - gcry_mpi_t val; - gcry_sexp_t psexp; - struct GNUNET_HashCode hc; - char *buff; - int bufSize; - size_t erroff; + gcry_sexp_t sig_sexpr; + gcry_sexp_t pub_sexpr; int rc; if (purpose != ntohl (validate->purpose)) return GNUNET_SYSERR; /* purpose mismatch */ - GNUNET_CRYPTO_hash (validate, ntohl (validate->size), &hc); - size = sizeof (struct GNUNET_CRYPTO_EccSignature); - GNUNET_assert (0 == - gcry_mpi_scan (&val, GCRYMPI_FMT_USG, - (const unsigned char *) sig, size, &size)); - GNUNET_assert (0 == - gcry_sexp_build (&sigdata, &erroff, "(sig-val(ecc(s %m)))", - val)); - gcry_mpi_release (val); - bufSize = strlen (FORMATSTRING) + 1; - buff = GNUNET_malloc (bufSize); - memcpy (buff, FORMATSTRING, bufSize); - memcpy (&buff - [strlen (FORMATSTRING) - - strlen - ("0123456789012345678901234567890123456789012345678901234567890123))")], - &hc, sizeof (struct GNUNET_HashCode)); - GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); - GNUNET_free (buff); - if (! (psexp = decode_public_key (publicKey))) + + /* build s-expression for signature */ + if (0 != (rc = gcry_sexp_build (&sig_sexpr, NULL, + "(sig-val(eddsa(r %b)(s %b)))", + (int)sizeof (sig->r), sig->r, + (int)sizeof (sig->s), sig->s))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); + return GNUNET_SYSERR; + } + data = data_to_eddsa_value (validate); + if (0 != (rc = gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(flags eddsa)(q %b)))", + (int)sizeof (pub->q_y), pub->q_y))) { gcry_sexp_release (data); - gcry_sexp_release (sigdata); + gcry_sexp_release (sig_sexpr); return GNUNET_SYSERR; } - rc = gcry_pk_verify (sigdata, data, psexp); - gcry_sexp_release (psexp); + rc = gcry_pk_verify (sig_sexpr, data, pub_sexpr); + gcry_sexp_release (pub_sexpr); gcry_sexp_release (data); - gcry_sexp_release (sigdata); - if (rc) + gcry_sexp_release (sig_sexpr); + if (0 != rc) { - LOG (GNUNET_ERROR_TYPE_WARNING, - _("ECC signature verification failed at %s:%d: %s\n"), __FILE__, + LOG (GNUNET_ERROR_TYPE_INFO, + _("EdDSA signature verification failed at %s:%d: %s\n"), __FILE__, __LINE__, gcry_strerror (rc)); return GNUNET_SYSERR; } @@ -1316,4 +942,523 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose, } +/** + * Derive key material from a public and a private ECDHE key. + * + * @param priv private key to use for the ECDH (x) + * @param pub public key to use for the ECDH (yG) + * @param key_material where to write the key material (xyG) + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_HashCode *key_material) +{ + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, d, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (d); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; +} + + +/** + * Derive the 'h' value for key derivation, where + * 'h = H(l,P)'. + * + * @param pub public key for deriviation + * @param label label for deriviation + * @param context additional context to use for HKDF of 'h'; + * typically the name of the subsystem/application + * @return h value + */ +static gcry_mpi_t +derive_h (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub, + const char *label, + const char *context) +{ + gcry_mpi_t h; + struct GNUNET_HashCode hc; + static const char *const salt = "key-derivation"; + + GNUNET_CRYPTO_kdf (&hc, sizeof (hc), + salt, strlen (salt), + pub, sizeof (*pub), + label, strlen (label), + context, strlen (context), + NULL, 0); + GNUNET_CRYPTO_mpi_scan_unsigned (&h, + (unsigned char *) &hc, + sizeof (hc)); + return h; +} + + +/** + * Derive a private key from a given private key and a label. + * Essentially calculates a private key 'd = H(l,P) * x mod n' + * where n is the size of the ECC group and P is the public + * key associated with the private key 'd'. + * + * @param priv original private key + * @param label label to use for key deriviation + * @param context additional context to use for HKDF of 'h'; + * typically the name of the subsystem/application + * @return derived private key + */ +struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_CRYPTO_ecdsa_private_key_derive (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + const char *label, + const char *context) +{ + struct GNUNET_CRYPTO_EcdsaPublicKey pub; + struct GNUNET_CRYPTO_EcdsaPrivateKey *ret; + gcry_mpi_t h; + gcry_mpi_t x; + gcry_mpi_t d; + gcry_mpi_t n; + gcry_ctx_t ctx; + + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE)); + + n = gcry_mpi_ec_get_mpi ("n", ctx, 1); + GNUNET_CRYPTO_ecdsa_key_get_public (priv, &pub); + + h = derive_h (&pub, label, context); + GNUNET_CRYPTO_mpi_scan_unsigned (&x, + priv->d, + sizeof (priv->d)); + d = gcry_mpi_new (256); + gcry_mpi_mulm (d, h, x, n); + gcry_mpi_release (h); + gcry_mpi_release (x); + gcry_mpi_release (n); + gcry_ctx_release (ctx); + ret = GNUNET_new (struct GNUNET_CRYPTO_EcdsaPrivateKey); + GNUNET_CRYPTO_mpi_print_unsigned (ret->d, sizeof (ret->d), d); + gcry_mpi_release (d); + return ret; +} + + +/** + * Derive a public key from a given public key and a label. + * Essentially calculates a public key 'V = H(l,P) * P'. + * + * @param pub original public key + * @param label label to use for key derivation + * @param context additional context to use for HKDF of 'h'; + * typically the name of the subsystem/application + * @param result where to write the derived public key + */ +void +GNUNET_CRYPTO_ecdsa_public_key_derive (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub, + const char *label, + const char *context, + struct GNUNET_CRYPTO_EcdsaPublicKey *result) +{ + gcry_ctx_t ctx; + gcry_mpi_t q_y; + gcry_mpi_t h; + gcry_mpi_t n; + gcry_mpi_t h_mod_n; + gcry_mpi_point_t q; + gcry_mpi_point_t v; + + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, CURVE)); + + /* obtain point 'q' from original public key. The provided 'q' is + compressed thus we first store it in the context and then get it + back as a (decompresssed) point. */ + q_y = gcry_mpi_set_opaque_copy (NULL, pub->q_y, 8*sizeof (pub->q_y)); + GNUNET_assert (NULL != q_y); + GNUNET_assert (0 == gcry_mpi_ec_set_mpi ("q", q_y, ctx)); + gcry_mpi_release (q_y); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + GNUNET_assert (q); + + /* calculate h_mod_n = h % n */ + h = derive_h (pub, label, context); + n = gcry_mpi_ec_get_mpi ("n", ctx, 1); + h_mod_n = gcry_mpi_new (256); + gcry_mpi_mod (h_mod_n, h, n); + /* calculate v = h_mod_n * q */ + v = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (v, h_mod_n, q, ctx); + gcry_mpi_release (h_mod_n); + gcry_mpi_release (h); + gcry_mpi_release (n); + gcry_mpi_point_release (q); + + /* convert point 'v' to public key that we return */ + GNUNET_assert (0 == gcry_mpi_ec_set_point ("q", v, ctx)); + gcry_mpi_point_release (v); + q_y = gcry_mpi_ec_get_mpi ("q@eddsa", ctx, 0); + GNUNET_assert (q_y); + GNUNET_CRYPTO_mpi_print_unsigned (result->q_y, + sizeof (result->q_y), + q_y); + gcry_mpi_release (q_y); + gcry_ctx_release (ctx); +} + + +/** + * Reverse the sequence of the bytes in @a buffer + * + * @param[in|out] buffer buffer to invert + * @param length number of bytes in @a buffer + */ +static void +reverse_buffer (unsigned char *buffer, + size_t length) +{ + unsigned char tmp; + size_t i; + + for (i=0; i < length/2; i++) + { + tmp = buffer[i]; + buffer[i] = buffer[length-1-i]; + buffer[length-1-i] = tmp; + } +} + + +/** + * Convert the secret @a d of an EdDSA key to the + * value that is actually used in the EdDSA computation. + * + * @param d secret input + * @return value used for the calculation in EdDSA + */ +static gcry_mpi_t +eddsa_d_to_a (gcry_mpi_t d) +{ + unsigned char rawmpi[32]; /* 256-bit value */ + size_t rawmpilen; + unsigned char digest[64]; /* 512-bit hash value */ + gcry_buffer_t hvec[2]; + int b; + gcry_mpi_t a; + + b = 256 / 8; /* number of bytes in `d` */ + + /* Note that we clear DIGEST so we can use it as input to left pad + the key with zeroes for hashing. */ + memset (hvec, 0, sizeof hvec); + rawmpilen = sizeof (rawmpi); + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, + rawmpi, rawmpilen, &rawmpilen, + d)); + hvec[0].data = digest; + hvec[0].off = 0; + hvec[0].len = b > rawmpilen? b - rawmpilen : 0; + hvec[1].data = rawmpi; + hvec[1].off = 0; + hvec[1].len = rawmpilen; + GNUNET_assert (0 == + gcry_md_hash_buffers (GCRY_MD_SHA512, + 0 /* flags */, + digest, + hvec, 2)); + /* Compute the A value. */ + reverse_buffer (digest, 32); /* Only the first half of the hash. */ + digest[0] = (digest[0] & 0x7f) | 0x40; + digest[31] &= 0xf8; + + GNUNET_CRYPTO_mpi_scan_unsigned (&a, + digest, + 32); + return a; +} + + +/** + * @ingroup crypto + * Derive key material from a ECDH public key and a private EdDSA key. + * Dual to #GNUNET_CRRYPTO_ecdh_eddsa. + * + * @param priv private key from EdDSA to use for the ECDH (x) + * @param pub public key to use for the ECDH (yG) + * @param key_material where to write the key material H(h(x)yG) + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_HashCode *key_material) +{ + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_mpi_t a; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* NOW, because this is EdDSA, HASH 'd' first! */ + a = eddsa_d_to_a (d); + gcry_mpi_release (d); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, a, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (a); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; +} + +/** + * @ingroup crypto + * Derive key material from a ECDH public key and a private ECDSA key. + * Dual to #GNUNET_CRRYPTO_ecdh_eddsa. + * + * @param priv private key from ECDSA to use for the ECDH (x) + * @param pub public key to use for the ECDH (yG) + * @param key_material where to write the key material H(h(x)yG) + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_ecdsa_ecdh (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_HashCode *key_material) +{ + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, d, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (d); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; +} + + + +/** + * @ingroup crypto + * Derive key material from a EdDSA public key and a private ECDH key. + * Dual to #GNUNET_CRRYPTO_eddsa_ecdh. + * + * @param priv private key to use for the ECDH (y) + * @param pub public key from EdDSA to use for the ECDH (X=h(x)G) + * @param key_material where to write the key material H(yX)=H(h(x)yG) + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + const struct GNUNET_CRYPTO_EddsaPublicKey *pub, + struct GNUNET_HashCode *key_material) +{ + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, d, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (d); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; +} + +/** + * @ingroup crypto + * Derive key material from a ECDSA public key and a private ECDH key. + * Dual to #GNUNET_CRRYPTO_eddsa_ecdh. + * + * @param priv private key to use for the ECDH (y) + * @param pub public key from ECDSA to use for the ECDH (X=h(x)G) + * @param key_material where to write the key material H(yX)=H(h(x)yG) + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success + */ +int +GNUNET_CRYPTO_ecdh_ecdsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + const struct GNUNET_CRYPTO_EcdsaPublicKey *pub, + struct GNUNET_HashCode *key_material) +{ + return GNUNET_CRYPTO_ecdh_eddsa (priv, + (const struct GNUNET_CRYPTO_EddsaPublicKey *)pub, + key_material); +} + /* end of crypto_ecc.c */