/*
This file is part of GNUnet
Copyright (C) 2014,2016,2019 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_cs.c
* @brief Clause Blind Schnorr signatures using Curve25519
* @author Lucien Heuzeveldt
* @author Gian Demarmels
*/
#include "platform.h"
#include "gnunet_crypto_lib.h"
#include
#include
/**
* IMPLEMENTATION NOTICE:
*
* This is an implementation of the Clause Blind Schnorr Signature Scheme using Curve25519.
* Further details about the Clause Blind Schnorr Signature Scheme can be found here:
* https://eprint.iacr.org/2019/877.pdf
*
* We use libsodium wherever possible.
*/
/**
* Create a new random private key.
*
* @param[out] priv where to write the fresh private key
*/
void
GNUNET_CRYPTO_cs_private_key_generate (struct GNUNET_CRYPTO_CsPrivateKey *priv)
{
crypto_core_ed25519_scalar_random (priv->scalar.d);
}
/**
* Extract the public key of the given private key.
*
* @param priv the private key
* @param[out] pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_private_key_get_public (const struct
GNUNET_CRYPTO_CsPrivateKey *priv,
struct GNUNET_CRYPTO_CsPublicKey *pub)
{
GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (pub->point.y,
priv->scalar.d));
}
/**
* maps 32 random bytes to a scalar
* this is necessary because libsodium expects scalar to be in the prime order subgroup
* @param[out] scalar containing 32 byte char array, is modified to be in prime order subgroup
*/
static void
map_to_scalar_subgroup (struct GNUNET_CRYPTO_Cs25519Scalar *scalar)
{
// perform clamping as described in RFC7748
scalar->d[0] &= 248;
scalar->d[31] &= 127;
scalar->d[31] |= 64;
}
/**
* Derive a new secret r pair r0 and r1.
* In original papers r is generated randomly
* To provide abort-idempotency, r needs to be derived but still needs to be UNPREDICTABLE
* To ensure unpredictability a new nonce should be used when a new r needs to be derived.
* Uses HKDF internally.
* Comment: Can be done in one HKDF shot and split output.
*
* @param nonce is a random nonce
* @param lts is a long-term-secret in form of a private key
* @param[out] r array containing derived secrets r0 and r1
*/
void
GNUNET_CRYPTO_cs_r_derive (const struct GNUNET_CRYPTO_CsNonce *nonce,
const struct GNUNET_CRYPTO_CsPrivateKey *lts,
struct GNUNET_CRYPTO_CsRSecret r[2])
{
GNUNET_assert (GNUNET_YES ==
GNUNET_CRYPTO_hkdf (r,
sizeof (struct GNUNET_CRYPTO_CsRSecret)
* 2,
GCRY_MD_SHA512,
GCRY_MD_SHA256,
"r",
strlen ("r"),
lts,
sizeof (*lts),
nonce,
sizeof (*nonce),
NULL,
0));
map_to_scalar_subgroup (&r[0].scalar);
map_to_scalar_subgroup (&r[1].scalar);
}
/**
* Extract the public R of the given secret r.
*
* @param r_priv the private key
* @param[out] r_pub where to write the public key
*/
void
GNUNET_CRYPTO_cs_r_get_public (const struct GNUNET_CRYPTO_CsRSecret *r_priv,
struct GNUNET_CRYPTO_CsRPublic *r_pub)
{
GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (r_pub->point.y,
r_priv->scalar.d));
}
/**
* Derives new random blinding factors.
* In original papers blinding factors are generated randomly
* To provide abort-idempotency, blinding factors need to be derived but still need to be UNPREDICTABLE
* To ensure unpredictability a new nonce has to be used.
* Uses HKDF internally
*
* @param secret is secret to derive blinding factors
* @param secret_len secret length
* @param[out] bs array containing the two derived blinding secrets
*/
void
GNUNET_CRYPTO_cs_blinding_secrets_derive (const struct
GNUNET_CRYPTO_CsNonce *blind_seed,
struct GNUNET_CRYPTO_CsBlindingSecret
bs[2])
{
GNUNET_assert (GNUNET_YES ==
GNUNET_CRYPTO_hkdf (bs,
sizeof (struct
GNUNET_CRYPTO_CsBlindingSecret)
* 2,
GCRY_MD_SHA512,
GCRY_MD_SHA256,
"alphabeta",
strlen ("alphabeta"),
blind_seed,
sizeof(*blind_seed),
NULL,
0));
map_to_scalar_subgroup (&bs[0].alpha);
map_to_scalar_subgroup (&bs[0].beta);
map_to_scalar_subgroup (&bs[1].alpha);
map_to_scalar_subgroup (&bs[1].beta);
}
/*
order of subgroup required for scalars by libsodium
2^252 + 27742317777372353535851937790883648493
copied from https://github.com/jedisct1/libsodium/blob/master/src/libsodium/crypto_core/ed25519/ref10/ed25519_ref10.c
and converted to big endian
*/
static const unsigned char L_BIG_ENDIAN[32] = {
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xde, 0xf9, 0xde, 0xa2, 0xf7,
0x9c, 0xd6, 0x58, 0x12, 0x63, 0x1a, 0x5c, 0xf5, 0xd3, 0xed
};
/**
* Computes a Hash of (R', m) mapped to a Curve25519 scalar
*
* @param hash initial hash of the message to be signed
* @param pub denomination public key (used as salt)
* @param[out] c C containing scalar
*/
static void
cs_full_domain_hash (const struct GNUNET_CRYPTO_CsRPublic *r_dash,
const void *msg,
size_t msg_len,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
struct GNUNET_CRYPTO_CsC *c)
{
// SHA-512 hash of R' and message
size_t r_m_concat_len = sizeof(struct GNUNET_CRYPTO_CsRPublic) + msg_len;
char r_m_concat[r_m_concat_len];
memcpy (r_m_concat, r_dash, sizeof(struct GNUNET_CRYPTO_CsRPublic));
memcpy (r_m_concat + sizeof(struct GNUNET_CRYPTO_CsRPublic), msg, msg_len);
struct GNUNET_HashCode prehash;
GNUNET_CRYPTO_hash (r_m_concat, r_m_concat_len, &prehash);
// modulus converted to MPI representation
gcry_mpi_t l_mpi;
GNUNET_CRYPTO_mpi_scan_unsigned (&l_mpi, L_BIG_ENDIAN, sizeof(L_BIG_ENDIAN));
// calculate full domain hash
gcry_mpi_t c_mpi;
GNUNET_CRYPTO_kdf_mod_mpi (&c_mpi,
l_mpi,
pub,
sizeof(struct GNUNET_CRYPTO_CsPublicKey),
&prehash,
sizeof(struct GNUNET_HashCode),
"Curve25519FDH");
gcry_mpi_release (l_mpi);
// convert c from mpi
unsigned char c_big_endian[256 / 8];
GNUNET_CRYPTO_mpi_print_unsigned (c_big_endian, sizeof(c_big_endian), c_mpi);
gcry_mpi_release (c_mpi);
for (size_t i = 0; i<32; i++)
c->scalar.d[i] = c_big_endian[31 - i];
}
/**
* calculate R'
*
* @param bs blinding secret
* @param r_pub R
* @param pub public key
* @param[out] blinded_r_pub R'
*/
static void
calc_r_dash (const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
const struct GNUNET_CRYPTO_CsRPublic *r_pub,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
struct GNUNET_CRYPTO_CsRPublic *blinded_r_pub)
{
// R'i = Ri + alpha i*G + beta i*pub
struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base;
GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (
alpha_mul_base.y,
bs->alpha.d));
struct GNUNET_CRYPTO_Cs25519Point beta_mul_pub;
GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (beta_mul_pub.y,
bs->beta.d,
pub->point.y));
struct GNUNET_CRYPTO_Cs25519Point alpha_mul_base_plus_beta_mul_pub;
GNUNET_assert (0 == crypto_core_ed25519_add (
alpha_mul_base_plus_beta_mul_pub.y,
alpha_mul_base.y,
beta_mul_pub.y));
GNUNET_assert (0 == crypto_core_ed25519_add (blinded_r_pub->point.y,
r_pub->point.y,
alpha_mul_base_plus_beta_mul_pub.
y));
}
/**
* Calculate two blinded c's
* Comment: One would be insecure due to Wagner's algorithm solving ROS
*
* @param bs array of the two blinding factor structs each containing alpha and beta
* @param r_pub array of the two signer's nonce R
* @param pub the public key of the signer
* @param msg the message to blind in preparation for signing
* @param msg_len length of message msg
* @param[out] blinded_c array of the two blinded c's
* @param[out] blinded_r_pub array of the two blinded R
*/
void
GNUNET_CRYPTO_cs_calc_blinded_c (const struct GNUNET_CRYPTO_CsBlindingSecret
bs[2],
const struct GNUNET_CRYPTO_CsRPublic r_pub[2],
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len,
struct GNUNET_CRYPTO_CsC blinded_c[2],
struct GNUNET_CRYPTO_CsRPublic
blinded_r_pub[2])
{
// for i 0/1: R'i = Ri + alpha i*G + beta i*pub
calc_r_dash (&bs[0], &r_pub[0], pub, &blinded_r_pub[0]);
calc_r_dash (&bs[1], &r_pub[1], pub, &blinded_r_pub[1]);
// for i 0/1: c'i = H(R'i, msg)
struct GNUNET_CRYPTO_CsC c_dash_0;
struct GNUNET_CRYPTO_CsC c_dash_1;
cs_full_domain_hash (&blinded_r_pub[0], msg, msg_len, pub, &c_dash_0);
cs_full_domain_hash (&blinded_r_pub[1], msg, msg_len, pub, &c_dash_1);
// for i 0/1: ci = c'i + beta i mod p
crypto_core_ed25519_scalar_add (blinded_c[0].scalar.d,
c_dash_0.scalar.d,
bs[0].beta.d);
crypto_core_ed25519_scalar_add (blinded_c[1].scalar.d,
c_dash_1.scalar.d,
bs[1].beta.d);
}
/**
* Sign a blinded c
* This function derives b from a nonce and a longterm secret
* In original papers b is generated randomly
* To provide abort-idempotency, b needs to be derived but still need to be UNPREDICTABLE.
* To ensure unpredictability a new nonce has to be used for every signature
* HKDF is used internally for derivation
* r0 and r1 can be derived prior by using GNUNET_CRYPTO_cs_r_derive
*
* @param priv private key to use for the signing and as LTS in HKDF
* @param r array of the two secret nonce from the signer
* @param c array of the two blinded c to sign c_b
* @param nonce is a random nonce
* @param[out] blinded_signature_scalar where to write the signature
* @return 0 or 1 for b (see Clause Blind Signature Scheme)
*/
unsigned int
GNUNET_CRYPTO_cs_sign_derive (const struct GNUNET_CRYPTO_CsPrivateKey *priv,
const struct GNUNET_CRYPTO_CsRSecret r[2],
const struct GNUNET_CRYPTO_CsC c[2],
const struct GNUNET_CRYPTO_CsNonce *nonce,
struct GNUNET_CRYPTO_CsBlindS *
blinded_signature_scalar
)
{
uint32_t hkdf_out;
// derive clause session identifier b (random bit)
GNUNET_assert (GNUNET_YES ==
GNUNET_CRYPTO_hkdf (&hkdf_out,
sizeof (hkdf_out),
GCRY_MD_SHA512,
GCRY_MD_SHA256,
"b",
strlen ("b"),
priv,
sizeof (*priv),
nonce,
sizeof (*nonce),
NULL,
0));
unsigned int b = hkdf_out % 2;
// s = r_b + c_b priv
struct GNUNET_CRYPTO_Cs25519Scalar c_b_mul_priv;
crypto_core_ed25519_scalar_mul (c_b_mul_priv.d,
c[b].scalar.d,
priv->scalar.d);
crypto_core_ed25519_scalar_add (blinded_signature_scalar->scalar.d,
r[b].scalar.d,
c_b_mul_priv.d);
return b;
}
/**
* Unblind a blind-signed signature using a c that was blinded
*
* @param blinded_signature_scalar the signature made on the blinded c
* @param bs the blinding factors used in the blinding
* @param[out] signature_scalar where to write the unblinded signature
*/
void
GNUNET_CRYPTO_cs_unblind (const struct
GNUNET_CRYPTO_CsBlindS *blinded_signature_scalar,
const struct GNUNET_CRYPTO_CsBlindingSecret *bs,
struct GNUNET_CRYPTO_CsS *signature_scalar)
{
crypto_core_ed25519_scalar_add (signature_scalar->scalar.d,
blinded_signature_scalar->scalar.d,
bs->alpha.d);
}
/**
* Verify whether the given message corresponds to the given signature and the
* signature is valid with respect to the given public key.
*
* @param sig signature that is being validated
* @param pub public key of the signer
* @param msg is the message that should be signed by @a sig (message is used to calculate c)
* @param msg_len is the message length
* @returns #GNUNET_YES on success, #GNUNET_SYSERR if signature invalid
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_cs_verify (const struct GNUNET_CRYPTO_CsSignature *sig,
const struct GNUNET_CRYPTO_CsPublicKey *pub,
const void *msg,
size_t msg_len)
{
// calculate c' = H(R, m)
struct GNUNET_CRYPTO_CsC c_dash;
cs_full_domain_hash (&sig->r_point, msg, msg_len, pub, &c_dash);
// s'G ?= R' + c' pub
struct GNUNET_CRYPTO_Cs25519Point sig_scal_mul_base;
GNUNET_assert (0 == crypto_scalarmult_ed25519_base_noclamp (
sig_scal_mul_base.y,
sig->s_scalar.scalar.d));
struct GNUNET_CRYPTO_Cs25519Point c_dash_mul_pub;
GNUNET_assert (0 == crypto_scalarmult_ed25519_noclamp (c_dash_mul_pub.y,
c_dash.scalar.d,
pub->point.y));
struct GNUNET_CRYPTO_Cs25519Point R_add_c_dash_mul_pub;
GNUNET_assert (0 == crypto_core_ed25519_add (R_add_c_dash_mul_pub.y,
sig->r_point.point.y,
c_dash_mul_pub.y));
return 0 == GNUNET_memcmp (&sig_scal_mul_base,
&R_add_c_dash_mul_pub)
? GNUNET_OK
: GNUNET_SYSERR;
}