/*
This file is part of GNUnet.
Copyright (C) 2012, 2013, 2015 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
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
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file util/crypto_ecc_gnsrecord.c
* @brief public key cryptography (ECC) for GNS records (LSD0001)
* @author Christian Grothoff
* @author Florian Dold
* @author Martin Schanzenbach
*/
#include "platform.h"
#include
#include
#include "gnunet_crypto_lib.h"
#include "gnunet_strings_lib.h"
#define CURVE "Ed25519"
/**
* Derive the 'h' value for key derivation, where
* 'h = H(l,P)'.
*
* @param pub public key for deriviation
* @param pubsize the size of the public key
* @param label label for deriviation
* @param context additional context to use for HKDF of 'h';
* typically the name of the subsystem/application
* @param hc where to write the result
*/
void
derive_h (const void *pub,
size_t pubsize,
const char *label,
const char *context,
struct GNUNET_HashCode *hc)
{
static const char *const salt = "key-derivation";
GNUNET_CRYPTO_kdf (hc,
sizeof(*hc),
salt,
strlen (salt),
pub,
pubsize,
label,
strlen (label),
context,
strlen (context),
NULL,
0);
}
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_eddsa_sign_derived (
const struct GNUNET_CRYPTO_EddsaPrivateKey *pkey,
const char *label,
const char *context,
const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
struct GNUNET_CRYPTO_EddsaSignature *sig)
{
struct GNUNET_CRYPTO_EddsaPrivateScalar priv;
crypto_hash_sha512_state hs;
unsigned char sk[64];
unsigned char r[64];
unsigned char hram[64];
unsigned char R[32];
unsigned char zk[32];
unsigned char tmp[32];
/**
* Derive the private key
*/
GNUNET_CRYPTO_eddsa_private_key_derive (pkey,
label,
context,
&priv);
crypto_hash_sha512_init (&hs);
/**
* Instead of expanding the private here, we already
* have the secret scalar as input. Use it.
* Note that sk is not plain SHA512 (d).
* sk[0..31] contains the derived private scalar
* sk[0..31] = h * SHA512 (d)[0..31]
* sk[32..63] = SHA512 (d)[32..63]
*/
memcpy (sk, priv.s, 64);
/**
* Calculate the derived zone key zk' from the
* derived private scalar.
*/
crypto_scalarmult_ed25519_base_noclamp (zk,
sk);
/**
* Calculate r:
* r = SHA512 (sk[32..63] | M)
* where M is our message (purpose).
* Note that sk[32..63] is the other half of the
* expansion from the original, non-derived private key
* "d".
*/
crypto_hash_sha512_update (&hs, sk + 32, 32);
crypto_hash_sha512_update (&hs, (uint8_t*) purpose, ntohl (purpose->size));
crypto_hash_sha512_final (&hs, r);
/**
* Temporarily put zk into S
*/
memcpy (sig->s, zk, 32);
/**
* Reduce the scalar value r
*/
unsigned char r_mod[64];
crypto_core_ed25519_scalar_reduce (r_mod, r);
/**
* Calculate R := r * G of the signature
*/
crypto_scalarmult_ed25519_base_noclamp (R, r_mod);
memcpy (sig->r, R, sizeof (R));
/**
* Calculate
* hram := SHA512 (R | zk' | M)
*/
crypto_hash_sha512_init (&hs);
crypto_hash_sha512_update (&hs, (uint8_t*) sig, 64);
crypto_hash_sha512_update (&hs, (uint8_t*) purpose,
ntohl (purpose->size));
crypto_hash_sha512_final (&hs, hram);
/**
* Reduce the resulting scalar value
*/
unsigned char hram_mod[64];
crypto_core_ed25519_scalar_reduce (hram_mod, hram);
/**
* Calculate
* S := r + hram * s mod L
*/
crypto_core_ed25519_scalar_mul (tmp, hram_mod, sk);
crypto_core_ed25519_scalar_add (sig->s, tmp, r_mod);
sodium_memzero (sk, sizeof (sk));
sodium_memzero (r, sizeof (r));
sodium_memzero (r_mod, sizeof (r_mod));
return GNUNET_OK;
}
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_ecdsa_sign_derived (
const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv,
const char *label,
const char *context,
const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
struct GNUNET_CRYPTO_EcdsaSignature *sig)
{
struct GNUNET_CRYPTO_EcdsaPrivateKey *key;
enum GNUNET_GenericReturnValue res;
key = GNUNET_CRYPTO_ecdsa_private_key_derive (priv,
label,
context);
res = GNUNET_CRYPTO_ecdsa_sign_ (key,
purpose,
sig);
GNUNET_free (key);
return res;
}
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;
struct GNUNET_HashCode hc;
uint8_t dc[32];
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);
derive_h (&pub, sizeof (pub), label, context, &hc);
GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
/* Convert to big endian for libgcrypt */
for (size_t i = 0; i < 32; i++)
dc[i] = priv->d[31 - i];
GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc));
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 (dc, sizeof(dc), d);
/* Convert to big endian for libgcrypt */
for (size_t i = 0; i < 32; i++)
ret->d[i] = dc[31 - i];
sodium_memzero (dc, sizeof(dc));
gcry_mpi_release (d);
return ret;
}
void
GNUNET_CRYPTO_ecdsa_public_key_derive (
const struct GNUNET_CRYPTO_EcdsaPublicKey *pub,
const char *label,
const char *context,
struct GNUNET_CRYPTO_EcdsaPublicKey *result)
{
struct GNUNET_HashCode hc;
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 */
derive_h (pub, sizeof (*pub), label, context, &hc);
GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
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);
}
void
GNUNET_CRYPTO_eddsa_private_key_derive (
const struct GNUNET_CRYPTO_EddsaPrivateKey *priv,
const char *label,
const char *context,
struct GNUNET_CRYPTO_EddsaPrivateScalar *result)
{
struct GNUNET_CRYPTO_EddsaPublicKey pub;
struct GNUNET_HashCode hc;
uint8_t dc[32];
unsigned char sk[64];
gcry_mpi_t h;
gcry_mpi_t h_mod_n;
gcry_mpi_t x;
gcry_mpi_t d;
gcry_mpi_t n;
gcry_mpi_t a1;
gcry_mpi_t a2;
gcry_ctx_t ctx;
/**
* Libsodium does not offer an API with arbitrary arithmetic.
* Hence we have to use libgcrypt here.
*/
GNUNET_assert (0 == gcry_mpi_ec_new (&ctx, NULL, "Ed25519"));
/**
* Get our modulo
*/
n = gcry_mpi_ec_get_mpi ("n", ctx, 1);
GNUNET_CRYPTO_eddsa_key_get_public (priv, &pub);
/**
* This is the standard private key expansion in Ed25519.
* The first 32 octets are used as a little-endian private
* scalar.
* We derive this scalar using our "h".
*/
crypto_hash_sha512 (sk, priv->d, 32);
sk[0] &= 248;
sk[31] &= 127;
sk[31] |= 64;
/**
* Get h mod n
*/
derive_h (&pub, sizeof (pub), label, context, &hc);
GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
h_mod_n = gcry_mpi_new (256);
gcry_mpi_mod (h_mod_n, h, n);
/* Convert scalar to big endian for libgcrypt */
for (size_t i = 0; i < 32; i++)
dc[i] = sk[31 - i];
/**
* dc now contains the private scalar "a".
* We carefully remove the clamping and derive a'.
* Calculate:
* a1 := a / 8
* a2 := h * a1 mod n
* a' := a2 * 8 mod n
*/
GNUNET_CRYPTO_mpi_scan_unsigned (&x, dc, sizeof(dc)); // a
a1 = gcry_mpi_new (256);
gcry_mpi_t eight = gcry_mpi_set_ui (NULL, 8);
gcry_mpi_div (a1, NULL, x, eight, 0); // a1 := a / 8
a2 = gcry_mpi_new (256);
gcry_mpi_mulm (a2, h_mod_n, a1, n); // a2 := h * a1 mod n
d = gcry_mpi_new (256);
gcry_mpi_mul (d, a2, eight); // a' := a2 * 8
gcry_mpi_release (h);
gcry_mpi_release (x);
gcry_mpi_release (n);
gcry_mpi_release (a1);
gcry_mpi_release (a2);
gcry_ctx_release (ctx);
GNUNET_CRYPTO_mpi_print_unsigned (dc, sizeof(dc), d);
/**
* We hash the derived "h" parameter with the
* other half of the expanded private key. This ensures
* that for signature generation, the "R" is derived from
* the same derivation path as "h" and is not reused.
*/
crypto_hash_sha256_state hs;
crypto_hash_sha256_init (&hs);
crypto_hash_sha256_update (&hs, sk + 32, 32);
crypto_hash_sha256_update (&hs, (unsigned char*) &hc, sizeof (hc));
crypto_hash_sha256_final (&hs, result->s + 32);
//memcpy (result->s, sk, sizeof (sk));
/* Convert to little endian for libsodium */
for (size_t i = 0; i < 32; i++)
result->s[i] = dc[31 - i];
sodium_memzero (dc, sizeof(dc));
gcry_mpi_release (d);
}
void
GNUNET_CRYPTO_eddsa_public_key_derive (
const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
const char *label,
const char *context,
struct GNUNET_CRYPTO_EddsaPublicKey *result)
{
struct GNUNET_HashCode hc;
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, "Ed25519"));
/* 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 */
derive_h (pub, sizeof (*pub), label, context, &hc);
GNUNET_CRYPTO_mpi_scan_unsigned (&h, (unsigned char *) &hc, sizeof(hc));
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);
}
void
GNUNET_CRYPTO_eddsa_key_get_public_from_scalar (
const struct GNUNET_CRYPTO_EddsaPrivateScalar *priv,
struct GNUNET_CRYPTO_EddsaPublicKey *pkey)
{
unsigned char sk[32];
memcpy (sk, priv->s, 32);
/**
* Calculate the derived zone key zk' from the
* derived private scalar.
*/
crypto_scalarmult_ed25519_base_noclamp (pkey->q_y,
sk);
}