From 69c3f226a7e62844b7dc08da92affeed8a062f4b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 17 Oct 2012 19:35:13 +0000 Subject: -fixing obvious ecc issues, adding gnunet-ecc based on gnunet-rsa --- src/include/gnunet_crypto_lib.h | 322 ++++++++++++++++++++++++++++++ src/util/Makefile.am | 10 + src/util/crypto_ecc.c | 430 +++++++++++++--------------------------- src/util/gnunet-ecc.c | 246 +++++++++++++++++++++++ src/util/util.conf | 3 + 5 files changed, 714 insertions(+), 297 deletions(-) create mode 100644 src/util/gnunet-ecc.c diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h index f73c1ae76..59f1dad78 100644 --- a/src/include/gnunet_crypto_lib.h +++ b/src/include/gnunet_crypto_lib.h @@ -96,11 +96,31 @@ enum GNUNET_CRYPTO_Quality */ #define GNUNET_CRYPTO_HASH_LENGTH 512/8 + +/** + * FIXME: what is an acceptable value here? + * Note: round to multiple of 8 minus 2. + */ +#define GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH 510 + +/** + * FIXME: what is an acceptable value here? + * Maximum length of the public key (q-point, Q = dP) when encoded. + */ +#define GNUNET_CRYPTO_ECC_MAX_PUBLIC_KEY_LENGTH 254 + + /** * The private information of an RSA key pair. */ struct GNUNET_CRYPTO_RsaPrivateKey; +/** + * The private information of an ECC private key. + */ +struct GNUNET_CRYPTO_EccPrivateKey; + + GNUNET_NETWORK_STRUCT_BEGIN /** @@ -219,6 +239,102 @@ struct GNUNET_CRYPTO_RsaEncryptedData }; +/** + * @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; + +}; + + +/** + * @brief an ECC signature + */ +struct GNUNET_CRYPTO_EccSignature +{ + /** + * Overall size of the encrypted data. + */ + uint16_t size; + + /** + * S-expression, padded with zeros. + */ + char sexpr[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; +}; + + +/** + * Public ECC key (always for NIST P-521) encoded in a format suitable + * for network transmission as created using 'gcry_sexp_sprint'. + */ +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_MAX_PUBLIC_KEY_LENGTH]; +}; + + +struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded +{ + /** + * Overall size of the private key. + */ + uint16_t size; + + /* followd by S-expression, opaque to applications */ + + /* FIXME: consider defining padding to make this a fixed-size struct */ + +}; + + +/** + * ECC Encrypted data. + */ +struct GNUNET_CRYPTO_EccEncryptedData +{ + /** + * Overall size of the encrypted data. + */ + uint16_t size; + + /** + * S-expression, padded with zeros. + */ + char encoding[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; +}; + + /** * @brief type for session keys */ @@ -1067,6 +1183,212 @@ GNUNET_CRYPTO_rsa_verify (uint32_t purpose, +/** + * 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); + + +/** + * 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); + + +/** + * 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); + +/** + * 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); + + +/** + * 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); + + +/** + * Encode the private key in a format suitable for + * storing it into a file. + * + * @param key key to encode + * @return encoding of the private key. + * The first 4 bytes give the size of the array, as usual. + */ +struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded * +GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *key); + + +/** + * Decode the private key from the file-format back + * to the "normal", internal format. + * + * @param buf the buffer where the private key data is stored + * @param len the length of the data in 'buffer' + * @return NULL on error + */ +struct GNUNET_CRYPTO_EccPrivateKey * +GNUNET_CRYPTO_ecc_decode_key (const char *buf, + size_t len); + + +/** + * 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. + * + * @return new private key, NULL on error (for example, + * permission denied) + */ +struct GNUNET_CRYPTO_EccPrivateKey * +GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename); + + +/** + * Handle to cancel private key generation and state for the + * key generation operation. + */ +struct GNUNET_CRYPTO_EccKeyGenerationContext; + + +/** + * 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. + * + * @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) + */ +struct GNUNET_CRYPTO_EccKeyGenerationContext * +GNUNET_CRYPTO_ecc_key_create_start (const char *filename, + GNUNET_CRYPTO_EccKeyCallback cont, + void *cont_cls); + + +/** + * Abort ECC key generation. + * + * @param gc key generation context to abort + */ +void +GNUNET_CRYPTO_ecc_key_create_stop (struct GNUNET_CRYPTO_EccKeyGenerationContext *gc); + +/** + * 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). + * + * @param cfg_name name of the configuration file to use + */ +void +GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name); + + +/** + * Encrypt a block with the public key of another host that uses the + * same cipher. + * + * @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 + */ +int +GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, + const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded + *publicKey, + struct GNUNET_CRYPTO_EccEncryptedData *target); + + +/** + * Decrypt a given block with the hostkey. + * + * @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 + */ +ssize_t +GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey *key, + const struct GNUNET_CRYPTO_EccEncryptedData *block, + void *result, size_t max); + + +/** + * Sign a given block. + * + * @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 + */ +int +GNUNET_CRYPTO_ecc_sign (const struct GNUNET_CRYPTO_EccPrivateKey *key, + const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose, + struct GNUNET_CRYPTO_EccSignature *sig); + + +/** + * 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 + */ +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); + + /** * This function should only be called in testcases * where strong entropy gathering is not desired diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 28d3d6a3e..e25bb8556 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -124,6 +124,7 @@ bin_PROGRAMS = \ gnunet-service-resolver \ gnunet-resolver \ gnunet-config \ + gnunet-ecc \ gnunet-rsa \ gnunet-uri @@ -155,6 +156,15 @@ gnunet_rsa_DEPENDENCIES = \ libgnunetutil.la +gnunet_ecc_SOURCES = \ + gnunet-ecc.c +gnunet_ecc_LDADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) -lgcrypt +gnunet_ecc_DEPENDENCIES = \ + libgnunetutil.la + + gnunet_config_SOURCES = \ gnunet-config.c gnunet_config_LDADD = \ diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c index 1ef1ec7b5..af3fe3359 100644 --- a/src/util/crypto_ecc.c +++ b/src/util/crypto_ecc.c @@ -46,103 +46,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. + * Log an error message at log-level 'level' that indicates + * a failure of the command 'cmd' with the message given + * by gcry_strerror(rc). */ -struct GNUNET_CRYPTO_EccEncryptedData -{ - unsigned char encoding[GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH]; -}; - -GNUNET_NETWORK_STRUCT_END - +#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); /** @@ -158,27 +67,6 @@ struct GNUNET_CRYPTO_EccPrivateKey }; -/** - * 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 @@ -200,7 +88,8 @@ adjust (unsigned char *buf, size_t size, size_t target) /** - * Free memory occupied by hostkey + * Free memory occupied by ECC key + * * @param privatekey pointer to the memory to free */ void @@ -292,12 +181,12 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv 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; + 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); - adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH); + adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_MAX_PUBLIC_KEY_LENGTH); gcry_mpi_release (skey); } @@ -407,19 +296,16 @@ decode_public_key (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *publicK * Encode the private key in a format suitable for * storing it into a file. * - * @returns encoding of the 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. */ struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded * -GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *hostkey) +GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *key) { struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *retval; - gcry_mpi_t pkv[2]; - void *pbu[2]; - size_t sizes[2]; - size_t off; - int rc; - unsigned int i; + char buf[65536]; + uint16_t be; size_t size; #if EXTRA_CHECKS @@ -429,43 +315,20 @@ GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *hostkey) 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++) + size = gcry_sexp_sprint (key->sexp, + GCRYSEXP_FMT_DEFAULT, + &buf[2], sizeof (buf) - sizeof (uint16_t)); + if (0 == size) { - 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; - } + GNUNET_break (0); + return NULL; } - GNUNET_assert (size < 65536); + GNUNET_assert (size < 65536 - sizeof (uint16_t)); + be = htons ((uint16_t) size); + memcpy (buf, &be, sizeof (be)); + size += sizeof (be); retval = GNUNET_malloc (size); - retval->size = htons (size); - off = 0; - for (i=0;i<2;i++) - { - 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]); - } + memcpy (retval, buf, size); return retval; } @@ -479,61 +342,35 @@ GNUNET_CRYPTO_ecc_encode_key (const struct GNUNET_CRYPTO_EccPrivateKey *hostkey) * @return NULL on error */ struct GNUNET_CRYPTO_EccPrivateKey * -GNUNET_CRYPTO_ecc_decode_key (const char *buf, uint16_t len) +GNUNET_CRYPTO_ecc_decode_key (const char *buf, + size_t len) { 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; + uint16_t be; + gcry_sexp_t sexp; int rc; - size_t size; - size_t pos; - uint16_t enc_len; size_t erroff; - enc_len = ntohs (encoding->size); - if (len != enc_len) + if (len < sizeof (uint16_t)) 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); + memcpy (&be, buf, sizeof (be)); + if (len != ntohs (be)) 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]); - if (0 != rc) + if (0 != (rc = gcry_sexp_sscan (&sexp, + &erroff, + &buf[2], + len - sizeof (uint16_t)))) { - LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_scan", rc); - gcry_mpi_release (d); + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_scan", rc); 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); -#if EXTRA_CHECKS - if (0 != (rc = gcry_pk_testkey (res))) + if (0 != (rc = gcry_pk_testkey (sexp))) { LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_pk_testkey", rc); return NULL; } -#endif ret = GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_EccPrivateKey)); - ret->sexp = res; + ret->sexp = sexp; return ret; } @@ -574,10 +411,8 @@ static struct GNUNET_CRYPTO_EccPrivateKey * try_read_key (const char *filename) { 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; @@ -611,26 +446,22 @@ try_read_key (const char *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); + char enc[fs]; + GNUNET_break (fs == GNUNET_DISK_file_read (fd, enc, fs)); + if (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, fs))) + { + 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; } @@ -1084,7 +915,7 @@ GNUNET_CRYPTO_ecc_setup_hostkey (const char *cfg_name) cfg = GNUNET_CONFIGURATION_create (); (void) GNUNET_CONFIGURATION_load (cfg, cfg_name); if (GNUNET_OK == - GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY", &fn)) + GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY", &fn)) { pk = GNUNET_CRYPTO_ecc_key_create_from_file (fn); if (NULL != pk) @@ -1115,7 +946,6 @@ GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, gcry_sexp_t data; gcry_sexp_t psexp; gcry_mpi_t val; - gcry_mpi_t rval; size_t isize; size_t erroff; @@ -1132,16 +962,18 @@ GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, GNUNET_assert (0 == gcry_pk_encrypt (&result, data, psexp)); 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)); + isize = gcry_sexp_sprint (result, + GCRYSEXP_FMT_DEFAULT, + target->encoding, + GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH); + if (0 == isize) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + target->size = htons ((uint16_t) isize); + /* padd with zeros */ + memset (&target->encoding[isize], 0, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - isize); return GNUNET_OK; } @@ -1157,8 +989,8 @@ GNUNET_CRYPTO_ecc_encrypt (const void *block, size_t size, * @return the size of the decrypted block, -1 on error */ ssize_t -GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey * key, - const struct GNUNET_CRYPTO_EccEncryptedData * block, +GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey *key, + const struct GNUNET_CRYPTO_EccEncryptedData *block, void *result, size_t max) { gcry_sexp_t resultsexp; @@ -1167,19 +999,15 @@ GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey * key, size_t size; gcry_mpi_t val; unsigned char *endp; - unsigned char *tmp; #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)); + size = ntohs (block->size); GNUNET_assert (0 == - gcry_sexp_build (&data, &erroff, "(enc-val(flags)(ecc(a %m)))", - val)); - gcry_mpi_release (val); + gcry_sexp_sscan (&data, + &erroff, + block->encoding, size)); GNUNET_assert (0 == gcry_pk_decrypt (&resultsexp, data, key->sexp)); gcry_sexp_release (data); /* resultsexp has format "(value %m)" */ @@ -1187,18 +1015,53 @@ GNUNET_CRYPTO_ecc_decrypt (const struct GNUNET_CRYPTO_EccPrivateKey * key, (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); + { + unsigned char tmp[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); + } return size; } +/** + * Convert the data specified in the given purpose argument to an + * S-expression suitable for signature operations. + * + * @param purpose data to convert + * @return converted s-expression + */ +static gcry_sexp_t +data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose) +{ + struct GNUNET_HashCode hc; + size_t bufSize; + gcry_sexp_t data; + + GNUNET_CRYPTO_hash (purpose, ntohl (purpose->size), &hc); +#define FORMATSTRING "(4:data(5:flags5:pkcs1)(4:hash6:sha51264:0123456789012345678901234567890123456789012345678901234567890123))" + bufSize = strlen (FORMATSTRING) + 1; + { + char buff[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)); + } +#undef FORMATSTRING + return data; +} + + /** * Sign a given block. * @@ -1215,33 +1078,23 @@ GNUNET_CRYPTO_ecc_sign (const struct GNUNET_CRYPTO_EccPrivateKey *key, gcry_sexp_t result; gcry_sexp_t data; size_t ssize; - gcry_mpi_t rval; - struct GNUNET_HashCode hc; - char *buff; - int bufSize; - 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); + data = data_to_pkcs1 (purpose); GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp)); gcry_sexp_release (data); - GNUNET_assert (0 == key_from_sexp (&rval, result, "ecc", "s")); + ssize = gcry_sexp_sprint (result, + GCRYSEXP_FMT_DEFAULT, + sig->sexpr, + GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH); + if (0 == ssize) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + sig->size = htons ((uint16_t) ssize); + /* padd with zeros */ + memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - ssize); 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)); return GNUNET_OK; } @@ -1266,35 +1119,18 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose, 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; 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); + size = ntohs (sig->size); + if (size > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - sizeof (uint16_t)) + return GNUNET_SYSERR; /* size out of range */ + data = data_to_pkcs1 (validate); 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); + gcry_sexp_sscan (&sigdata, &erroff, sig->sexpr, size)); if (! (psexp = decode_public_key (publicKey))) { gcry_sexp_release (data); @@ -1305,7 +1141,7 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose, gcry_sexp_release (psexp); gcry_sexp_release (data); gcry_sexp_release (sigdata); - if (rc) + if (0 != rc) { LOG (GNUNET_ERROR_TYPE_WARNING, _("ECC signature verification failed at %s:%d: %s\n"), __FILE__, diff --git a/src/util/gnunet-ecc.c b/src/util/gnunet-ecc.c new file mode 100644 index 000000000..f966763a8 --- /dev/null +++ b/src/util/gnunet-ecc.c @@ -0,0 +1,246 @@ +/* + This file is part of GNUnet. + (C) 2012 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 3, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + 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. +*/ + +/** + * @file util/gnunet-ecc.c + * @brief tool to manipulate ECC key files + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_util_lib.h" +#include "gnunet_testing_lib-new.h" +#include + + +/** + * Flag for printing public key. + */ +static int print_public_key; + +/** + * Flag for printing hash of public key. + */ +static int print_peer_identity; + +/** + * Flag for printing short hash of public key. + */ +static int print_short_identity; + +/** + * Use weak random number generator for key generation. + */ +static int weak_random; + +/** + * Option set to create a bunch of keys at once. + */ +static unsigned int make_keys; + +/** + * The private information of an ECC key pair. + * NOTE: this must match the definition in crypto_ksk.c and crypto_ecc.c! + */ +struct GNUNET_CRYPTO_EccPrivateKey +{ + gcry_sexp_t sexp; +}; + + +/** + * Create a new private key. Caller must free return value. + * + * @return fresh private key + */ +static struct GNUNET_CRYPTO_EccPrivateKey * +ecc_key_create () +{ + struct GNUNET_CRYPTO_EccPrivateKey *ret; + gcry_sexp_t s_key; + gcry_sexp_t s_keyparam; + + GNUNET_assert (0 == + gcry_sexp_build (&s_keyparam, NULL, + "(genkey(ecc(nbits %d)(ecc-use-e 3:257)))", + 2048)); + 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; +} + + +/** + * Create a flat file with a large number of key pairs for testing. + */ +static void +create_keys (const char *fn) +{ + FILE *f; + struct GNUNET_CRYPTO_EccPrivateKey *pk; + struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded *enc; + + if (NULL == (f = fopen (fn, "w+"))) + { + fprintf (stderr, + _("Failed to open `%s': %s\n"), + fn, + STRERROR (errno)); + return; + } + fprintf (stderr, + _("Generating %u keys, please wait"), + make_keys); + while (0 < make_keys--) + { + fprintf (stderr, + "."); + if (NULL == (pk = ecc_key_create ())) + { + GNUNET_break (0); + break; + } + enc = GNUNET_CRYPTO_ecc_encode_key (pk); + if (htons (enc->size) != fwrite (enc, 1, htons (enc->size), f)) + { + fprintf (stderr, + _("\nFailed to write to `%s': %s\n"), + fn, + STRERROR (errno)); + GNUNET_CRYPTO_ecc_key_free (pk); + GNUNET_free (enc); + break; + } + GNUNET_CRYPTO_ecc_key_free (pk); + GNUNET_free (enc); + } + if (0 == make_keys) + fprintf (stderr, + _("Finished!\n")); + fclose (f); +} + + +/** + * Main function that will be run by the scheduler. + * + * @param cls closure + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param cfg configuration + */ +static void +run (void *cls, char *const *args, const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_CRYPTO_EccPrivateKey *pk; + struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded pub; + struct GNUNET_PeerIdentity pid; + + if (NULL == args[0]) + { + fprintf (stderr, _("No hostkey file specified on command line\n")); + return; + } + if (0 != weak_random) + GNUNET_CRYPTO_random_disable_entropy_gathering (); + if (make_keys > 0) + { + create_keys (args[0]); + return; + } + pk = GNUNET_CRYPTO_ecc_key_create_from_file (args[0]); + if (NULL == pk) + return; + if (print_public_key) + { + char *s; + + GNUNET_CRYPTO_ecc_key_get_public (pk, &pub); + s = GNUNET_CRYPTO_ecc_public_key_to_string (&pub); + fprintf (stdout, "%s\n", s); + GNUNET_free (s); + } + if (print_peer_identity) + { + struct GNUNET_CRYPTO_HashAsciiEncoded enc; + + GNUNET_CRYPTO_ecc_key_get_public (pk, &pub); + GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey); + GNUNET_CRYPTO_hash_to_enc (&pid.hashPubKey, &enc); + fprintf (stdout, "%s\n", enc.encoding); + } + if (print_short_identity) + { + struct GNUNET_CRYPTO_ShortHashAsciiEncoded enc; + struct GNUNET_CRYPTO_ShortHashCode sh; + + GNUNET_CRYPTO_ecc_key_get_public (pk, &pub); + GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &sh); + GNUNET_CRYPTO_short_hash_to_enc (&sh, &enc); + fprintf (stdout, "%s\n", enc.short_encoding); + } + GNUNET_CRYPTO_ecc_key_free (pk); +} + + +/** + * Program to manipulate ECC key files. + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, char *const *argv) +{ + static const struct GNUNET_GETOPT_CommandLineOption options[] = { + { 'g', "generate-keys", "COUNT", + gettext_noop ("create COUNT public-private key pairs (for testing)"), + 1, &GNUNET_GETOPT_set_uint, &make_keys }, + { 'p', "print-public-key", NULL, + gettext_noop ("print the public key in ASCII format"), + 0, &GNUNET_GETOPT_set_one, &print_public_key }, + { 'P', "print-peer-identity", NULL, + gettext_noop ("print the hash of the public key in ASCII format"), + 0, &GNUNET_GETOPT_set_one, &print_peer_identity }, + { 's', "print-short-identity", NULL, + gettext_noop ("print the short hash of the public key in ASCII format"), + 0, &GNUNET_GETOPT_set_one, &print_short_identity }, + { 'w', "weak-random", NULL, + gettext_noop ("use insecure, weak random number generator for key generation (for testing only)"), + 0, &GNUNET_GETOPT_set_one, &weak_random }, + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv)) + return 2; + + return (GNUNET_OK == + GNUNET_PROGRAM_run (argc, argv, "gnunet-ecc [OPTIONS] keyfile", + gettext_noop ("Manipulate GNUnet private ECC key files"), + options, &run, NULL)) ? 0 : 1; +} + +/* end of gnunet-ecc.c */ diff --git a/src/util/util.conf b/src/util/util.conf index f3d301e48..ea8ddc071 100644 --- a/src/util/util.conf +++ b/src/util/util.conf @@ -9,6 +9,9 @@ SERVICEHOME = ~/.gnunet/ [gnunetd] HOSTKEY = $SERVICEHOME/.hostkey +[PEER] +PRIVATE_KEY = $SERVICEHOME/private.ecc + [client] HOME = $SERVICEHOME -- cgit v1.2.3