X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Futil%2Fcrypto_ecc.c;h=c4e101369f4f346672bca4e063829d05324a1b67;hb=71e181512d1cd61d3865f93f5b85b208b5720ba5;hp=b5a057aed560a876b6e22a5c3d1de3052fb49caa;hpb=a4406e4f8c9c84f429be507d797f4ff210bb74f0;p=oweals%2Fgnunet.git diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c index b5a057aed..c4e101369 100644 --- a/src/util/crypto_ecc.c +++ b/src/util/crypto_ecc.c @@ -1,10 +1,10 @@ /* This file is part of GNUnet. - (C) 2012, 2013 Christian Grothoff (and other contributing authors) + Copyright (C) 2012, 2013, 2015 Christian Grothoff (and other contributing authors) 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,8 +14,8 @@ 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. */ /** @@ -25,12 +25,18 @@ */ #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-256" +/** + * 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__) @@ -43,33 +49,7 @@ * 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); - - -/** - * The private information of an ECC private key. - */ -struct GNUNET_CRYPTO_EccPrivateKey -{ - - /** - * Libgcrypt S-expression for the ECC key. - */ - gcry_sexp_t sexp; -}; - - -/** - * Free memory occupied by ECC key - * - * @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) /** @@ -82,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; @@ -92,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++) @@ -132,6 +114,105 @@ key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, } +/** + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. + * + * @param priv private key to decode + * @return NULL on error + */ +static gcry_sexp_t +decode_private_ecdsa_key (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv) +{ + gcry_sexp_t result; + int rc; + + 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_sexp_build", rc); + GNUNET_assert (0); + } +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (result))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + GNUNET_assert (0); + } +#endif + return result; +} + + +/** + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. + * + * @param priv private key to decode + * @return NULL on error + */ +static gcry_sexp_t +decode_private_eddsa_key (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv) +{ + gcry_sexp_t result; + int rc; + + rc = gcry_sexp_build (&result, NULL, + "(private-key(ecc(curve \"" CURVE "\")" + "(flags eddsa)(d %b)))", + (int)sizeof (priv->d), priv->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 (result))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + GNUNET_assert (0); + } +#endif + return result; +} + + +/** + * Convert the given private key from the network format to the + * S-expression that can be used by libgcrypt. + * + * @param priv private key to decode + * @return NULL on error + */ +static gcry_sexp_t +decode_private_ecdhe_key (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv) +{ + gcry_sexp_t result; + int rc; + + 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_sexp_build", rc); + GNUNET_assert (0); + } +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (result))) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + GNUNET_assert (0); + } +#endif + return result; +} + + /** * Extract the public key for the given private key. * @@ -139,27 +220,105 @@ key_from_sexp (gcry_mpi_t * array, gcry_sexp_t sexp, const char *topname, * @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) +GNUNET_CRYPTO_ecdsa_key_get_public (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - gcry_mpi_t skey; - size_t size; - int rc; + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; - memset (pub, 0, sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)); - 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_MAX_PUBLIC_KEY_LENGTH; - GNUNET_assert (0 == - gcry_mpi_print (GCRYMPI_FMT_USG, pub->key, size, &size, - skey)); - pub->len = htons (size); - gcry_mpi_release (skey); + 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); +} + + +/** + * 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_eddsa_key_get_public (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + struct GNUNET_CRYPTO_EddsaPublicKey *pub) +{ + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; + + 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); +} + + +/** + * 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_ecdhe_key_get_public (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + struct GNUNET_CRYPTO_EcdhePublicKey *pub) +{ + gcry_sexp_t sexp; + gcry_ctx_t ctx; + gcry_mpi_t q; + + 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); +} + + +/** + * Convert a public key to a string. + * + * @param pub key to convert + * @return string representing @a pub + */ +char * +GNUNET_CRYPTO_ecdsa_public_key_to_string (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub) +{ + char *pubkeybuf; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 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_EcdsaPublicKey), + pubkeybuf, + keylen); + if (NULL == end) + { + GNUNET_free (pubkeybuf); + return NULL; + } + *end = '\0'; + return pubkeybuf; } @@ -167,22 +326,22 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv * Convert a public key to a string. * * @param pub key to convert - * @return string representing 'pub' + * @return string representing @a pub */ char * -GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) +GNUNET_CRYPTO_eddsa_public_key_to_string (const struct GNUNET_CRYPTO_EddsaPublicKey *pub) { char *pubkeybuf; - size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; + 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_EccPublicKeyBinaryEncoded), - pubkeybuf, + end = GNUNET_STRINGS_data_to_string ((unsigned char *) pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey), + pubkeybuf, keylen); if (NULL == end) { @@ -198,16 +357,16 @@ GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyB * 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 enclen number of bytes in @a enc (without 0-terminator) * @param pub where to store the public key - * @return GNUNET_OK on success + * @return #GNUNET_OK on success */ int -GNUNET_CRYPTO_ecc_public_key_from_string (const char *enc, - size_t enclen, - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub) +GNUNET_CRYPTO_ecdsa_public_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EcdsaPublicKey *pub) { - size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey)) * 8; if (keylen % 5 > 0) keylen += 5 - keylen % 5; @@ -217,150 +376,106 @@ GNUNET_CRYPTO_ecc_public_key_from_string (const char *enc, if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, pub, - sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded))) - return GNUNET_SYSERR; - if ( (ntohs (pub->size) != sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) || - (ntohs (pub->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH) ) + sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey))) return GNUNET_SYSERR; return GNUNET_OK; } /** - * Convert the given public key from the network format to the - * S-expression that can be used by libgcrypt. + * Convert a string representing a public key to a public key. * - * @param publicKey public key to decode - * @return NULL on error + * @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 */ -static gcry_sexp_t -decode_public_key (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicKey) +int +GNUNET_CRYPTO_eddsa_public_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EddsaPublicKey *pub) { - gcry_sexp_t result; - gcry_mpi_t q; - size_t size; - size_t erroff; - int rc; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)) * 8; - if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH) - { - GNUNET_break (0); - return NULL; - } - size = ntohs (publicKey->len); - if (0 != (rc = gcry_mpi_scan (&q, GCRYMPI_FMT_USG, publicKey->key, size, &size))) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); - return NULL; - } + if (keylen % 5 > 0) + keylen += 5 - keylen % 5; + keylen /= 5; + if (enclen != keylen) + return GNUNET_SYSERR; - rc = gcry_sexp_build (&result, &erroff, - "(public-key(ecdsa(curve \"" CURVE "\")(q %m)))", - q); - gcry_mpi_release (q); - if (0 != rc) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */ - return NULL; - } -#if EXTRA_CHECKS - if (0 != (rc = gcry_pk_testkey (result))) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); - gcry_sexp_release (result); - return NULL; - } -#endif - return result; + if (GNUNET_OK != GNUNET_STRINGS_string_to_data (enc, enclen, + pub, + sizeof (struct GNUNET_CRYPTO_EddsaPublicKey))) + return GNUNET_SYSERR; + return GNUNET_OK; } /** - * Encode the private key in a format suitable for - * storing it into a file. + * Convert a string representing a private key to a private key. * - * @param key key to encode - * @return encoding of the private key. - * The first 4 bytes give the size of the array, as usual. + * @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 */ -struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded * -GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *key) +int +GNUNET_CRYPTO_eddsa_private_key_from_string (const char *enc, + size_t enclen, + struct GNUNET_CRYPTO_EddsaPrivateKey *pub) { - struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *retval; - char buf[65536]; - uint16_t be; - size_t size; + size_t keylen = (sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)) * 8; -#if EXTRA_CHECKS - if (0 != gcry_pk_testkey (key->sexp)) - { - GNUNET_break (0); - return NULL; - } -#endif - size = gcry_sexp_sprint (key->sexp, - GCRYSEXP_FMT_DEFAULT, - &buf[2], sizeof (buf) - sizeof (uint16_t)); - if (0 == size) - { - GNUNET_break (0); - return NULL; - } - GNUNET_assert (size < 65536 - sizeof (uint16_t)); - be = htons ((uint16_t) size + (sizeof (be))); - memcpy (buf, &be, sizeof (be)); - size += sizeof (be); - retval = GNUNET_malloc (size); - memcpy (retval, buf, size); - return retval; + 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; } /** - * Decode the private key from the file-format back - * to the "normal", internal format. + * @ingroup crypto + * Clear memory that was used to store a private key. * - * @param buf the buffer where the private key data is stored - * @param len the length of the data in 'buffer' - * @param validate GNUNET_YES to validate that the key is well-formed, - * GNUNET_NO if the key comes from a totally trusted source - * and validation is considered too expensive - * @return NULL on error + * @param pk location of the key */ -struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_decode_key (const char *buf, - size_t len, - int validate) +void +GNUNET_CRYPTO_ecdhe_key_clear (struct GNUNET_CRYPTO_EcdhePrivateKey *pk) { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - uint16_t be; - gcry_sexp_t sexp; - int rc; - size_t erroff; + memset (pk, 0, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); +} - if (len < sizeof (uint16_t)) - return NULL; - memcpy (&be, buf, sizeof (be)); - if (len < ntohs (be)) - return NULL; - len = ntohs (be); - if (0 != (rc = gcry_sexp_sscan (&sexp, - &erroff, - &buf[2], - len - sizeof (uint16_t)))) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_scan", rc); - return NULL; - } - if ( (GNUNET_YES == validate) && - (0 != (rc = gcry_pk_testkey (sexp))) ) - { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); - return NULL; - } - ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); - ret->sexp = sexp; - return ret; + +/** + * @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)); +} + + +/** + * @ingroup crypto + * Clear memory that was used to store a private key. + * + * @param pk location of the key + */ +void +GNUNET_CRYPTO_eddsa_key_clear (struct GNUNET_CRYPTO_EddsaPrivateKey *pk) +{ + memset (pk, 0, sizeof (struct GNUNET_CRYPTO_EddsaPrivateKey)); } @@ -369,21 +484,28 @@ GNUNET_CRYPTO_ecc_decode_key (const char *buf, * * @return fresh private key */ -struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_key_create () +struct GNUNET_CRYPTO_EcdhePrivateKey * +GNUNET_CRYPTO_ecdhe_key_create () { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - gcry_sexp_t s_key; + 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(ecdsa(curve \"" CURVE "\")))"))) + "(genkey(ecc(curve \"" CURVE "\")" + "(flags eddsa no-keytest)))"))) { LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); return NULL; } - if (0 != (rc = gcry_pk_genkey (&s_key, s_keyparam))) + 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); @@ -391,698 +513,428 @@ GNUNET_CRYPTO_ecc_key_create () } gcry_sexp_release (s_keyparam); #if EXTRA_CHECKS - if (0 != (rc = gcry_pk_testkey (s_key))) + if (0 != (rc = gcry_pk_testkey (priv_sexp))) { LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); - gcry_sexp_release (s_key); + gcry_sexp_release (priv_sexp); return NULL; } #endif - ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); - ret->sexp = s_key; - return ret; + 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_EcdhePrivateKey); + GNUNET_CRYPTO_mpi_print_unsigned (priv->d, sizeof (priv->d), d); + gcry_mpi_release (d); + return priv; } /** - * Try to read the private key from the given file. + * Create a new private key. Caller must free return value. * - * @param filename file to read the key from - * @return NULL on error + * @return fresh private key */ -static struct GNUNET_CRYPTO_EccPrivateKey * -try_read_key (const char *filename) +struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_CRYPTO_ecdsa_key_create () { - struct GNUNET_CRYPTO_EccPrivateKey *ret; - struct GNUNET_DISK_FileHandle *fd; - OFF_T fs; - - if (GNUNET_YES != GNUNET_DISK_file_test (filename)) - return NULL; + struct GNUNET_CRYPTO_EcdsaPrivateKey *priv; + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + int rc; - /* key file exists already, read it! */ - if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE))) + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags)))"))) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); return NULL; } - if (GNUNET_OK != (GNUNET_DISK_file_handle_size (fd, &fs))) + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "stat", filename); - (void) GNUNET_DISK_file_close (fd); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); return NULL; } - if (0 == fs) + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (priv_sexp))) { - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); return NULL; } - if (fs > UINT16_MAX) +#endif + if (0 != (rc = key_from_sexp (&d, priv_sexp, "private-key", "d"))) { - 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); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc); + gcry_sexp_release (priv_sexp); return NULL; } - { - char enc[fs]; - - GNUNET_break (fs == GNUNET_DISK_file_read (fd, enc, fs)); - if (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, fs, GNUNET_YES))) - { - 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); - return NULL; - } - } - GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fd)); - return ret; -} - - -/** - * 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). - */ -static void -short_wait () -{ - struct GNUNET_TIME_Relative timeout; - - timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100); - (void) GNUNET_NETWORK_socket_select (NULL, NULL, NULL, timeout); + 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 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. + * Create a new private key. Caller must free return value. * - * @return new private key, NULL on error (for example, - * permission denied) + * @return fresh private key */ -struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename) +struct GNUNET_CRYPTO_EddsaPrivateKey * +GNUNET_CRYPTO_eddsa_key_create () { - 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; + struct GNUNET_CRYPTO_EddsaPrivateKey *priv; + gcry_sexp_t priv_sexp; + gcry_sexp_t s_keyparam; + gcry_mpi_t d; + int rc; - 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 = GNUNET_CRYPTO_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); - return ret; - } - /* key file exists already, read it! */ - fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, - GNUNET_DISK_PERM_NONE); - if (NULL == fd) + if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(curve \"" CURVE "\")" + "(flags eddsa)))"))) { - LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); return NULL; } - cnt = 0; - while (1) + if (0 != (rc = gcry_pk_genkey (&priv_sexp, s_keyparam))) { - 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 private key.\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 key 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 key 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 key.\n")); - } - short_wait (); /* wait a bit longer! */ - continue; - } - break; + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_genkey", rc); + gcry_sexp_release (s_keyparam); + return NULL; } - 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, GNUNET_YES)))) + gcry_sexp_release (s_keyparam); +#if EXTRA_CHECKS + if (0 != (rc = gcry_pk_testkey (priv_sexp))) { - 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); - } + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); + gcry_sexp_release (priv_sexp); + return NULL; } - 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) +#endif + if (0 != (rc = key_from_sexp (&d, priv_sexp, "private-key", "d"))) { - GNUNET_CRYPTO_ecc_key_get_public (ret, &pub); - GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "key_from_sexp", rc); + gcry_sexp_release (priv_sexp); + return NULL; } - return ret; + 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; } /** - * Handle to cancel private key generation and state for the - * key generation operation. + * Get the shared private key we use for anonymous users. + * + * @return "anonymous" private key */ -struct GNUNET_CRYPTO_EccKeyGenerationContext +const struct GNUNET_CRYPTO_EcdsaPrivateKey * +GNUNET_CRYPTO_ecdsa_key_get_anonymous () { - - /** - * Continuation to call upon completion. - */ - GNUNET_CRYPTO_EccKeyCallback cont; - /** - * Closure for 'cont'. + * 'anonymous' pseudonym (global static, d=1, public key = G + * (generator). */ - void *cont_cls; - - /** - * Name of the file. - */ - char *filename; - - /** - * 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; - -}; - - -/** - * Abort ECC key generation. - * - * @param gc key generation context to abort - */ -void -GNUNET_CRYPTO_ecc_key_create_stop (struct GNUNET_CRYPTO_EccKeyGenerationContext *gc) -{ - if (GNUNET_SCHEDULER_NO_TASK != gc->read_task) - { - GNUNET_SCHEDULER_cancel (gc->read_task); - gc->read_task = GNUNET_SCHEDULER_NO_TASK; - } - if (NULL != gc->gnunet_ecc) - { - (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); - } - - if (NULL != gc->filename) - { - if ( (0 != UNLINK (gc->filename)) && - (ENOENT != errno) ) - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", gc->filename); - GNUNET_free (gc->filename); - } - if (NULL != gc->pk) - GNUNET_CRYPTO_ecc_key_free (gc->pk); - GNUNET_free (gc); + 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; } /** - * Task called upon shutdown or process termination of 'gnunet-ecc' during - * ECC key generation. Check where we are and perform the appropriate - * action. + * Compare two Peer Identities. * - * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' - * @param tc scheduler context + * @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 */ -static void -check_key_generation_completion (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) -{ - struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; - struct GNUNET_CRYPTO_EccPrivateKey *pk; - - gc->read_task = GNUNET_SCHEDULER_NO_TASK; - if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) - { - gc->cont (gc->cont_cls, NULL, _("interrupted by shutdown")); - GNUNET_CRYPTO_ecc_key_create_stop (gc); - return; - } - 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))) - { - GNUNET_break (0); - gc->cont (gc->cont_cls, NULL, _("gnunet-ecc failed")); - GNUNET_CRYPTO_ecc_key_create_stop (gc); - return; - } - gc->cont (gc->cont_cls, pk, NULL); - GNUNET_DISK_pipe_close (gc->gnunet_ecc_out); - GNUNET_free (gc->filename); - GNUNET_free (gc); -} - - -/** - * Return the private ECC key which already existed on disk - * (asynchronously) to the caller. - * - * @param cls the 'struct GNUNET_CRYPTO_EccKeyGenerationContext' - * @param tc scheduler context (unused) - */ -static void -async_return_key (void *cls, - const struct GNUNET_SCHEDULER_TaskContext *tc) +int +GNUNET_CRYPTO_cmp_peer_identity (const struct GNUNET_PeerIdentity *first, + const struct GNUNET_PeerIdentity *second) { - struct GNUNET_CRYPTO_EccKeyGenerationContext *gc = cls; - - gc->cont (gc->cont_cls, - gc->pk, - NULL); - GNUNET_free (gc); + 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; + 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; - } - 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, - 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 key 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 - * key 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_key (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, "PEER", "PRIVATE_KEY", &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; } /** - * Retrieve the identity of the host's peer. + * Sign a given block. * - * @param cfg configuration to use - * @param dst pointer to where to write the peer identity - * @return GNUNET_OK on success, GNUNET_SYSERR if the identity - * could not be retrieved + * @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_get_host_identity (const struct GNUNET_CONFIGURATION_Handle *cfg, - struct GNUNET_PeerIdentity *dst) +GNUNET_CRYPTO_ecdsa_sign (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EcdsaSignature *sig) { - struct GNUNET_CRYPTO_EccPrivateKey *my_private_key; - struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key; - char *keyfile; + gcry_sexp_t priv_sexp; + gcry_sexp_t sig_sexp; + gcry_sexp_t data; + int rc; + gcry_mpi_t rs[2]; - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY", - &keyfile)) + priv_sexp = decode_private_ecdsa_key (priv); + data = data_to_ecdsa_value (purpose); + if (0 != (rc = gcry_pk_sign (&sig_sexp, data, priv_sexp))) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - _("Lacking key configuration settings.\n")); + 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; } - if (NULL == (my_private_key = GNUNET_CRYPTO_ecc_key_create_from_file (keyfile))) + gcry_sexp_release (priv_sexp); + gcry_sexp_release (data); + + /* 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_log (GNUNET_ERROR_TYPE_ERROR, - _("Could not access hostkey file `%s'.\n"), keyfile); - GNUNET_free (keyfile); + GNUNET_break (0); + gcry_sexp_release (sig_sexp); return GNUNET_SYSERR; } - GNUNET_free (keyfile); - GNUNET_CRYPTO_ecc_key_get_public (my_private_key, &my_public_key); - GNUNET_CRYPTO_ecc_key_free (my_private_key); - GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &dst->hashPubKey); + 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; } /** - * Convert the data specified in the given purpose argument to an - * S-expression suitable for signature operations. + * Sign a given block. * - * @param purpose data to convert - * @return converted s-expression + * @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 */ -static gcry_sexp_t -data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) +int +GNUNET_CRYPTO_eddsa_sign (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EddsaSignature *sig) { - struct GNUNET_CRYPTO_ShortHashCode hc; - size_t bufSize; + gcry_sexp_t priv_sexp; + gcry_sexp_t sig_sexp; gcry_sexp_t data; + int rc; + gcry_mpi_t rs[2]; - GNUNET_CRYPTO_short_hash (purpose, ntohl (purpose->size), &hc); -#define FORMATSTRING "(4:data(5:flags3:raw)(5:value32:01234567890123456789012345678901))" - bufSize = strlen (FORMATSTRING) + 1; + priv_sexp = decode_private_eddsa_key (priv); + data = data_to_eddsa_value (purpose); + if (0 != (rc = gcry_pk_sign (&sig_sexp, data, priv_sexp))) { - char buff[bufSize]; - - memcpy (buff, FORMATSTRING, bufSize); - memcpy (&buff - [bufSize - - strlen - ("01234567890123456789012345678901))") - - 1], &hc, sizeof (struct GNUNET_CRYPTO_ShortHashCode)); - GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0)); + 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; } -#undef FORMATSTRING - return data; + gcry_sexp_release (priv_sexp); + gcry_sexp_release (data); + + /* 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_sexp_t sig_sexpr; + gcry_sexp_t pub_sexpr; int rc; - data = data_to_pkcs1 (purpose); - if (0 != (rc = 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))) { - 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 (sig_sexpr); return GNUNET_SYSERR; } + rc = gcry_pk_verify (sig_sexpr, data, pub_sexpr); + gcry_sexp_release (pub_sexpr); gcry_sexp_release (data); - ssize = gcry_sexp_sprint (result, - GCRYSEXP_FMT_DEFAULT, - sig->sexpr, - GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH); - if (0 == ssize) + gcry_sexp_release (sig_sexpr); + if (0 != rc) { - GNUNET_break (0); + LOG (GNUNET_ERROR_TYPE_INFO, + _("ECDSA signature verification failed at %s:%d: %s\n"), __FILE__, + __LINE__, gcry_strerror (rc)); return GNUNET_SYSERR; } - sig->size = htons ((uint16_t) (ssize + sizeof (uint16_t))); - /* padd with zeros */ - memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH - ssize); - gcry_sexp_release (result); 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_sexp_t psexp; - 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 */ - size = ntohs (sig->size); - if ( (size < sizeof (uint16_t)) || - (size > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH - sizeof (uint16_t)) ) - return GNUNET_SYSERR; /* size out of range */ - data = data_to_pkcs1 (validate); - GNUNET_assert (0 == - gcry_sexp_sscan (&sigdata, &erroff, - sig->sexpr, size - sizeof (uint16_t))); - 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); + 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; } @@ -1091,65 +943,398 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose, /** - * Derive key material from a public and a private ECC key. + * Derive key material from a public and a private ECDHE key. * - * @param key private key to use for the ECDH (x) - * @param pub public key to use for the ECDY (yG) + * @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 + * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ int -GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key, - const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub, +GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, struct GNUNET_HashCode *key_material) -{ - size_t slen; - size_t erroff; - int rc; - unsigned char sdata_buf[2048]; /* big enough to print dh-shared-secret as S-expression */ +{ gcry_mpi_point_t result; gcry_mpi_point_t q; gcry_mpi_t d; gcry_ctx_t ctx; - gcry_sexp_t psexp; + gcry_sexp_t pub_sexpr; gcry_mpi_t result_x; - gcry_mpi_t result_y; + unsigned char xbuf[256 / 8]; + size_t rsize; /* first, extract the q = dP value from the public key */ - if (! (psexp = decode_public_key (pub))) + 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; - if (0 != (rc = gcry_mpi_ec_new (&ctx, psexp, NULL))) + 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, "gcry_mpi_ec_new", rc); /* erroff gives more info */ + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); return GNUNET_SYSERR; } - gcry_sexp_release (psexp); + 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; + + GNUNET_CRYPTO_kdf (&hc, sizeof (hc), + "key-derivation", strlen ("key-derivation"), + 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); +} - /* second, extract the d value from our private key */ - rc = key_from_sexp (&d, key->sexp, "private-key", "d"); - if (rc) - rc = key_from_sexp (&d, key->sexp, "ecc", "d"); - if (0 != rc) + +/** + * 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++) { - GNUNET_break (0); - gcry_mpi_point_release (q); - return GNUNET_SYSERR; + tmp = buffer[i]; + buffer[i] = buffer[length-1-i]; + buffer[length-1-i] = tmp; } +} - /* create a new context for definitively the correct curve; - theoretically the 'public_key' might not use the right curve */ - if (0 != (rc = gcry_mpi_ec_new (&ctx, NULL, "NIST P-256"))) + +/** + * 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, "gcry_mpi_ec_new", rc); /* erroff gives more info */ - gcry_mpi_release (d); - gcry_mpi_point_release (q); + 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 */ - GNUNET_assert (NULL != ctx); result = gcry_mpi_point_new (0); gcry_mpi_ec_mul (result, d, q, ctx); gcry_mpi_point_release (q); @@ -1157,8 +1342,7 @@ GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key, /* finally, convert point to string for hashing */ result_x = gcry_mpi_new (256); - result_y = gcry_mpi_new (256); - if (gcry_mpi_ec_get_affine (result_x, result_y, result, ctx)) + 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); @@ -1167,25 +1351,113 @@ GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key, } gcry_mpi_point_release (result); gcry_ctx_release (ctx); - if (0 != (rc = gcry_sexp_build (&psexp, &erroff, - "(dh-shared-secret (x %m)(y %m))", - result_x, - result_y))) + + 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, "gcry_sexp_build", rc); /* erroff gives more info */ - gcry_mpi_release (result_x); - gcry_mpi_release (result_y); + 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); - gcry_mpi_release (result_y); - slen = gcry_sexp_sprint (psexp, GCRYSEXP_FMT_DEFAULT, sdata_buf, sizeof (sdata_buf)); - GNUNET_assert (0 != slen); - gcry_sexp_release (psexp); - /* finally, get a string of the resulting S-expression and hash it to generate the key material */ - GNUNET_CRYPTO_hash (sdata_buf, slen, key_material); 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 */