From 1fa44a546134cd03a827387b664a2bbee1a209f8 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 15 May 2015 13:45:53 +0000 Subject: [PATCH] ecdh-eddsa implementation now works --- src/util/crypto_ecc.c | 193 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 189 insertions(+), 4 deletions(-) diff --git a/src/util/crypto_ecc.c b/src/util/crypto_ecc.c index c3a60e841..a29e27a44 100644 --- a/src/util/crypto_ecc.c +++ b/src/util/crypto_ecc.c @@ -1430,7 +1430,9 @@ derive_h (const struct GNUNET_CRYPTO_EcdsaPublicKey *pub, label, strlen (label), context, strlen (context), NULL, 0); - GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof (hc)); + GNUNET_CRYPTO_mpi_scan_unsigned (&h, + (unsigned char *) &hc, + sizeof (hc)); return h; } @@ -1466,7 +1468,9 @@ GNUNET_CRYPTO_ecdsa_private_key_derive (const struct GNUNET_CRYPTO_EcdsaPrivateK 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)); + 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); @@ -1542,6 +1546,78 @@ GNUNET_CRYPTO_ecdsa_public_key_derive (const struct GNUNET_CRYPTO_EcdsaPublicKey } +/** + * Reverse the sequence of the bytes in @a buffer + * + * @param[in|out] buffer buffer to invert + * @param length number of bytes in @a buffer + */ +static void +reverse_buffer (unsigned char *buffer, + size_t length) +{ + unsigned char tmp; + size_t i; + + for (i=0; i < length/2; i++) + { + tmp = buffer[i]; + buffer[i] = buffer[length-1-i]; + buffer[length-1-i] = tmp; + } +} + + +/** + * Convert the secret @a d of an EdDSA key to the + * value that is actually used in the EdDSA computation. + * + * @param d secret input + * @return value used for the calculation in EdDSA + */ +static gcry_mpi_t +eddsa_d_to_a (gcry_mpi_t d) +{ + unsigned char rawmpi[32]; /* 256-bit value */ + size_t rawmpilen; + unsigned char digest[64]; /* 512-bit hash value */ + gcry_buffer_t hvec[2]; + int b; + gcry_mpi_t a; + + b = 256 / 8; /* number of bytes in `d` */ + + /* Note that we clear DIGEST so we can use it as input to left pad + the key with zeroes for hashing. */ + memset (hvec, 0, sizeof hvec); + rawmpilen = sizeof (rawmpi); + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_USG, + rawmpi, rawmpilen, &rawmpilen, + d)); + hvec[0].data = digest; + hvec[0].off = 0; + hvec[0].len = b > rawmpilen? b - rawmpilen : 0; + hvec[1].data = rawmpi; + hvec[1].off = 0; + hvec[1].len = rawmpilen; + GNUNET_assert (0 == + gcry_md_hash_buffers (GCRY_MD_SHA512, + 0 /* flags */, + digest, + hvec, 2)); + /* Compute the A value. */ + reverse_buffer (digest, 32); /* Only the first half of the hash. */ + digest[0] = (digest[0] & 0x7f) | 0x40; + digest[31] &= 0xf8; + + GNUNET_CRYPTO_mpi_scan_unsigned (&a, + digest, + 32); + return a; +} + + /** * @ingroup crypto * Derive key material from a ECDH public key and a private EdDSA key. @@ -1557,7 +1633,64 @@ GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, const struct GNUNET_CRYPTO_EcdhePublicKey *pub, struct GNUNET_HashCode *key_material) { - return GNUNET_SYSERR; + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_mpi_t a; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* NOW, because this is EdDSA, HASH 'd' first! */ + a = eddsa_d_to_a (d); + gcry_mpi_release (d); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, a, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (a); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; } @@ -1576,7 +1709,59 @@ GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, const struct GNUNET_CRYPTO_EddsaPublicKey *pub, struct GNUNET_HashCode *key_material) { - return GNUNET_SYSERR; + gcry_mpi_point_t result; + gcry_mpi_point_t q; + gcry_mpi_t d; + gcry_ctx_t ctx; + gcry_sexp_t pub_sexpr; + gcry_mpi_t result_x; + unsigned char xbuf[256 / 8]; + size_t rsize; + + /* first, extract the q = dP value from the public key */ + if (0 != gcry_sexp_build (&pub_sexpr, NULL, + "(public-key(ecc(curve " CURVE ")(q %b)))", + (int)sizeof (pub->q_y), pub->q_y)) + return GNUNET_SYSERR; + GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, pub_sexpr, NULL)); + gcry_sexp_release (pub_sexpr); + q = gcry_mpi_ec_get_point ("q", ctx, 0); + + /* second, extract the d value from our private key */ + GNUNET_CRYPTO_mpi_scan_unsigned (&d, priv->d, sizeof (priv->d)); + + /* then call the 'multiply' function, to compute the product */ + result = gcry_mpi_point_new (0); + gcry_mpi_ec_mul (result, d, q, ctx); + gcry_mpi_point_release (q); + gcry_mpi_release (d); + + /* finally, convert point to string for hashing */ + result_x = gcry_mpi_new (256); + if (gcry_mpi_ec_get_affine (result_x, NULL, result, ctx)) + { + LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "get_affine failed", 0); + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + return GNUNET_SYSERR; + } + gcry_mpi_point_release (result); + gcry_ctx_release (ctx); + + rsize = sizeof (xbuf); + GNUNET_assert (! gcry_mpi_get_flag (result_x, GCRYMPI_FLAG_OPAQUE)); + /* result_x can be negative here, so we do not use 'GNUNET_CRYPTO_mpi_print_unsigned' + as that does not include the sign bit; x should be a 255-bit + value, so with the sign it should fit snugly into the 256-bit + xbuf */ + GNUNET_assert (0 == + gcry_mpi_print (GCRYMPI_FMT_STD, xbuf, rsize, &rsize, + result_x)); + GNUNET_CRYPTO_hash (xbuf, + rsize, + key_material); + gcry_mpi_release (result_x); + return GNUNET_OK; } -- 2.25.1