+/**
+ * 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 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_mpi_t result_x;
+ gcry_mpi_t result_y;
+
+ /* first, extract the q = dP value from the public key */
+ if (! (psexp = decode_public_key (pub)))
+ return GNUNET_SYSERR;
+ if (0 != (rc = gcry_mpi_ec_new (&ctx, psexp, NULL)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_ec_new", rc); /* erroff gives more info */
+ return GNUNET_SYSERR;
+ }
+ gcry_sexp_release (psexp);
+ q = gcry_mpi_ec_get_point ("q", ctx, 0);
+ 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)
+ {
+ GNUNET_break (0);
+ gcry_mpi_point_release (q);
+ return GNUNET_SYSERR;
+ }
+
+ /* 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")))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_mpi_ec_new", rc); /* erroff gives more info */
+ gcry_mpi_release (d);
+ gcry_mpi_point_release (q);
+ return GNUNET_SYSERR;
+ }
+
+ /* 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);
+ gcry_mpi_release (d);
+
+ /* 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))
+ {
+ 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);
+ if (0 != (rc = gcry_sexp_build (&psexp, &erroff,
+ "(dh-shared-secret (x %m)(y %m))",
+ result_x,
+ result_y)))
+ {
+ LOG_GCRY (GNUNET_ERROR_TYPE_ERROR, "gcry_sexp_build", rc); /* erroff gives more info */
+ gcry_mpi_release (result_x);
+ gcry_mpi_release (result_y);
+ return GNUNET_SYSERR;
+ }
+ 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;
+}
+
+