+{
+ 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;
+}
+
+
+/**
+ * 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;
+ static const char *const salt = "key-derivation";
+
+ GNUNET_CRYPTO_kdf (&hc, sizeof (hc),
+ salt, strlen (salt),
+ 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);
+}
+
+
+/**
+ * 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;