* @file util/crypto_ecc.c
* @brief public key cryptography (ECC) with libgcrypt
* @author Christian Grothoff
- *
- * This is just a first, completely untested, draft hack for future ECC support.
- * TODO:
- * - adjust encoding length and other parameters
- * - actually test it!
*/
#include "platform.h"
#include <gcrypt.h>
};
-/**
- * If target != size, move target bytes to the
- * end of the size-sized buffer and zero out the
- * first target-size bytes.
- *
- * @param buf original buffer
- * @param size number of bytes in the buffer
- * @param target target size of the buffer
- */
-static void
-adjust (unsigned char *buf, size_t size, size_t target)
-{
- if (size < target)
- {
- memmove (&buf[target - size], buf, size);
- memset (buf, 0, target - size);
- }
-}
-
-
/**
* Free memory occupied by ECC key
*
size_t size;
int rc;
+ 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");
gcry_mpi_print (GCRYMPI_FMT_USG, pub->key, size, &size,
skey));
pub->len = htons (size);
- adjust (&pub->key[0], size, GNUNET_CRYPTO_ECC_MAX_PUBLIC_KEY_LENGTH);
gcry_mpi_release (skey);
}
* @return string representing 'pub'
*/
char *
-GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
+GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
{
char *pubkeybuf;
size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8;
sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)))
return GNUNET_SYSERR;
if ( (ntohs (pub->size) != sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) ||
- (ntohs (pub->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH) )
+ (ntohs (pub->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH) )
return GNUNET_SYSERR;
return GNUNET_OK;
}
size_t erroff;
int rc;
- if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH)
+ if (ntohs (publicKey->len) > GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH)
{
GNUNET_break (0);
return NULL;
}
- size = ntohs (publicKey->size);
+ 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;
}
- rc = gcry_sexp_build (&result, &erroff, "(public-key(ecc((curve \"" CURVE "\")(q %m)))", q);
+
+ 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;
}
+ // FIXME: is this key expected to pass pk_testkey?
+#if 0
+#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
+#endif
return result;
}
if (len < sizeof (uint16_t))
return NULL;
memcpy (&be, buf, sizeof (be));
- if (len != ntohs (be))
+ if (len < ntohs (be))
return NULL;
+ len = ntohs (be);
if (0 != (rc = gcry_sexp_sscan (&sexp,
&erroff,
&buf[2],
*
* @return fresh private key
*/
-static struct GNUNET_CRYPTO_EccPrivateKey *
-ecc_key_create ()
+struct GNUNET_CRYPTO_EccPrivateKey *
+GNUNET_CRYPTO_ecc_key_create ()
{
struct GNUNET_CRYPTO_EccPrivateKey *ret;
gcry_sexp_t s_key;
int rc;
if (0 != (rc = gcry_sexp_build (&s_keyparam, NULL,
- "(genkey(ecdsa(curve 10:NIST P-521)))")))
+ "(genkey(ecdsa(curve \"" CURVE "\")))")))
{
LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc);
return NULL;
if (GNUNET_YES != GNUNET_DISK_file_test (filename))
return NULL;
- /* hostkey file exists already, read it! */
+ /* key file exists already, read it! */
if (NULL == (fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
GNUNET_DISK_PERM_NONE)))
{
}
LOG (GNUNET_ERROR_TYPE_INFO,
_("Creating a new private key. This may take a while.\n"));
- ret = ecc_key_create ();
+ ret = GNUNET_CRYPTO_ecc_key_create ();
GNUNET_assert (ret != NULL);
enc = GNUNET_CRYPTO_ecc_encode_key (ret);
GNUNET_assert (enc != NULL);
GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
GNUNET_CRYPTO_ecc_key_get_public (ret, &pub);
GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("I am host `%s'. Stored new private key in `%s'.\n"),
- GNUNET_i2s (&pid), filename);
return ret;
}
- /* hostkey file exists already, read it! */
+ /* key file exists already, read it! */
fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
GNUNET_DISK_PERM_NONE);
if (NULL == fd)
STRERROR (ec));
LOG (GNUNET_ERROR_TYPE_ERROR,
_
- ("This may be ok if someone is currently generating a hostkey.\n"));
+ ("This may be ok if someone is currently generating a private key.\n"));
}
short_wait ();
continue;
fs = 0;
if (fs < sizeof (struct GNUNET_CRYPTO_EccPrivateKeyBinaryEncoded))
{
- /* maybe we got the read lock before the hostkey generating
+ /* 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,
{
LOG (GNUNET_ERROR_TYPE_ERROR,
_
- ("When trying to read hostkey file `%s' I found %u bytes but I need at least %u.\n"),
+ ("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 hostkey.\n"));
+ ("This may be ok if someone is currently generating a key.\n"));
}
short_wait (); /* wait a bit longer! */
continue;
GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs));
len = ntohs (enc->size);
ret = NULL;
- if ((len != fs) ||
+ if ((len > fs) ||
(NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len))))
{
LOG (GNUNET_ERROR_TYPE_ERROR,
{
GNUNET_CRYPTO_ecc_key_get_public (ret, &pub);
GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
- LOG (GNUNET_ERROR_TYPE_INFO,
- _("I am host `%s'. Read private key from `%s'.\n"), GNUNET_i2s (&pid),
- filename);
}
return ret;
}
{
struct GNUNET_CRYPTO_EccKeyGenerationContext *gc;
struct GNUNET_CRYPTO_EccPrivateKey *pk;
- const char *weak_random;
if (NULL != (pk = try_read_key (filename)))
{
GNUNET_free (gc);
return NULL;
}
- weak_random = NULL;
- if (GNUNET_YES ==
- GNUNET_CRYPTO_random_is_weak ())
- weak_random = "-w";
gc->gnunet_ecc = GNUNET_OS_start_process (GNUNET_NO,
GNUNET_OS_INHERIT_STD_ERR,
NULL,
"gnunet-ecc",
"gnunet-ecc",
gc->filename,
- weak_random,
NULL);
if (NULL == gc->gnunet_ecc)
{
/**
- * Setup a hostkey file for a peer given the name of the
+ * 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
- * hostkey is fast (for example in time-dependent testcases).
+ * key 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)
+GNUNET_CRYPTO_ecc_setup_key (const char *cfg_name)
{
struct GNUNET_CONFIGURATION_Handle *cfg;
struct GNUNET_CRYPTO_EccPrivateKey *pk;
static gcry_sexp_t
data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
{
- struct GNUNET_HashCode hc;
+ struct GNUNET_CRYPTO_ShortHashCode 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))"
+ GNUNET_CRYPTO_short_hash (purpose, ntohl (purpose->size), &hc);
+#define FORMATSTRING "(4:data(5:flags3:raw)(5:value32:01234567890123456789012345678901))"
bufSize = strlen (FORMATSTRING) + 1;
{
char buff[bufSize];
memcpy (&buff
[bufSize -
strlen
- ("0123456789012345678901234567890123456789012345678901234567890123))")
- - 1], &hc, sizeof (struct GNUNET_HashCode));
+ ("01234567890123456789012345678901))")
+ - 1], &hc, sizeof (struct GNUNET_CRYPTO_ShortHashCode));
GNUNET_assert (0 == gcry_sexp_new (&data, buff, bufSize, 0));
}
#undef FORMATSTRING
gcry_sexp_t result;
gcry_sexp_t data;
size_t ssize;
+ int rc;
data = data_to_pkcs1 (purpose);
- GNUNET_assert (0 == gcry_pk_sign (&result, data, key->sexp));
+ if (0 != (rc = gcry_pk_sign (&result, data, key->sexp)))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("ECC signing failed at %s:%d: %s\n"), __FILE__,
+ __LINE__, gcry_strerror (rc));
+ }
gcry_sexp_release (data);
ssize = gcry_sexp_sprint (result,
GCRYSEXP_FMT_DEFAULT,
sig->sexpr,
- GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH);
+ GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH);
if (0 == ssize)
{
GNUNET_break (0);
}
sig->size = htons ((uint16_t) (ssize + sizeof (uint16_t)));
/* padd with zeros */
- memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - ssize);
+ memset (&sig->sexpr[ssize], 0, GNUNET_CRYPTO_ECC_SIGNATURE_DATA_ENCODING_LENGTH - ssize);
gcry_sexp_release (result);
return GNUNET_OK;
}
return GNUNET_SYSERR; /* purpose mismatch */
size = ntohs (sig->size);
if ( (size < sizeof (uint16_t)) ||
- (size > GNUNET_CRYPTO_ECC_DATA_ENCODING_LENGTH - 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 ==
}
+/**
+ * Derive key material from a public and a private ECC key.
+ *
+ * @param key private key to use for the ECDH (x)
+ * @param pub public key to use for the ECDY (yG)
+ * @param key_material where to write the key material (xyG)
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key,
+ const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub,
+ struct GNUNET_HashCode *key_material)
+{
+ size_t size;
+ size_t slen;
+ int rc;
+ gcry_sexp_t data;
+ unsigned char sdata_buf[2048]; /* big enough to print 'sdata' and 'r_sig' */
+
+ /* first, extract the q value from the public key */
+ {
+ gcry_sexp_t psexp;
+ gcry_mpi_t sdata;
+
+ if (! (psexp = decode_public_key (pub)))
+ return GNUNET_SYSERR;
+ rc = key_from_sexp (&sdata, psexp, "public-key", "q");
+ if (rc)
+ rc = key_from_sexp (&sdata, psexp, "ecc", "q");
+ GNUNET_assert (0 == rc);
+ gcry_sexp_release (psexp);
+ size = sizeof (sdata_buf);
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, sdata_buf, size, &size,
+ sdata));
+ gcry_mpi_release (sdata);
+ }
+ /* convert q value into an S-expression -- whatever format libgcrypt wants,
+ re-using format from sign operation for now... */
+ {
+ char *sexp_string;
+
+#define FORMATPREFIX "(4:data(5:flags3:raw)(5:value%u:"
+#define FORMATPOSTFIX "))"
+ sexp_string = GNUNET_malloc (strlen (FORMATPREFIX) + size + 12 +
+ strlen (FORMATPOSTFIX) + 1);
+ GNUNET_snprintf (sexp_string,
+ strlen (FORMATPREFIX) + 12,
+ FORMATPREFIX,
+ size);
+ slen = strlen (sexp_string);
+ memcpy (&sexp_string[slen],
+ sdata_buf,
+ size);
+ memcpy (&sexp_string[slen + size],
+ FORMATPOSTFIX,
+ strlen (FORMATPOSTFIX) + 1);
+ GNUNET_assert (0 == gcry_sexp_new (&data,
+ sexp_string,
+ slen + size + strlen (FORMATPOSTFIX),
+ 0));
+ GNUNET_free (sexp_string);
+ }
+ /* then call the 'multiply' function, hoping it simply multiplies the points;
+ here we need essentially a WRAPPER around _gcry_mpi_ex_mul_point! - FIXME-WK!*/
+#if WK
+ {
+ gcry_sexp_t result;
+
+ rc = gcry_ecc_mul_point (&result, data /* scalar */, key->sexp /* point and ctx */);
+ GNUNET_assert (0 == rc);
+ slen = gcry_sexp_sprint (result, GCRYSEXP_FMT_DEFAULT, sdata_buf, sizeof (sdata_buf));
+ GNUNET_assert (0 != slen);
+ }
+#else
+ /* use broken version, insecure! */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("To be implemented: not secure at the moment, please read README\n"));
+ slen = sprintf ((char*) sdata_buf, "FIXME-this is not key material");
+#endif
+ gcry_sexp_release (data);
+
+ /* 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;
+}
+
+
/* end of crypto_ecc.c */