gnunet

Main GNUnet Logic
Log | Files | Refs | Submodules | README | LICENSE

commit b856442cd0fe6aecb3d80fb6083d80b525fc4de7
parent 42bad19dd914786841009699e9aa40b277bb95a4
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Tue, 16 Jul 2024 21:55:13 +0200

UTIL: Update KEM instantiations to follow RFC 9180

NEWS: New RFC9180-compliant KEMs

Diffstat:
Msrc/include/gnunet_crypto_lib.h | 30+++++++++++-------------------
Msrc/lib/util/Makefile.am | 1+
Msrc/lib/util/crypto_ecc.c | 203++++---------------------------------------------------------------------------
Msrc/lib/util/crypto_elligator.c | 39---------------------------------------
Asrc/lib/util/crypto_kem.c | 513+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/lib/util/meson.build | 1+
Msrc/lib/util/test_crypto_ecdh_eddsa.c | 43++-----------------------------------------
Msrc/lib/util/test_crypto_elligator.c | 1+
8 files changed, 538 insertions(+), 293 deletions(-)

diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h @@ -2069,16 +2069,12 @@ GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, /** * @ingroup crypto - * Derive key material from a ECDH public key and a private EdDSA key. - * Dual to #GNUNET_CRRYPTO_ecdh_eddsa. - * This uses the Ed25519 private seed as X25519 seed. - * As such, this also is a X25519 DH (see #GNUNET_CRYPTO_ecc_ecdh). - * The resulting X25519 secret is then derived to a key using a - * KDF (see also https://eprint.iacr.org/2021/509.pdf) + * Derive key material from a ECDH public key and a private X25519 key. + * Dual to #GNUNET_CRRYPTO_ecdh_x25519. * NOTE: Whenever you can get away with it, use separate key pairs * for signing and encryption (DH)! * - * @param sk private key from EdDSA to use for the ECDH (x) + * @param sk private key from X25519 to use for the ECDH (x) * @param pk public key to use for the ECDH (yG) * @param additional_data this is fed into HKDF-Extract along with the ECDH shared secret @@ -2087,9 +2083,9 @@ GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_ecdh_nohash (const struct GNUNET_CRYPTO_EddsaPrivateKey *sk, - const struct GNUNET_CRYPTO_EcdhePublicKey *pk, - struct GNUNET_CRYPTO_EcdhePublicKey *dh); +GNUNET_CRYPTO_x25519_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk, + const struct GNUNET_CRYPTO_EcdhePublicKey *pk, + struct GNUNET_CRYPTO_EcdhePublicKey *dh); /** @@ -2254,11 +2250,7 @@ GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, /** * @ingroup crypto * Derive key material from a EdDSA public key and a private ECDH key. - * Dual to #GNUNET_CRRYPTO_eddsa_ecdh_nohash. - * This converts the Edwards25519 public key @a pub to a Curve25519 - * public key before computing a X25519 DH (see #GNUNET_CRYPTO_ecc_ecdh). - * The resulting X25519 secret is then derived to a key using a - * KDF (see also https://eprint.iacr.org/2021/509.pdf) + * Dual to #GNUNET_CRRYPTO_x25519_ecdh. * NOTE: Whenever you can get away with it, use separate key pairs * for signing and encryption (DH)! * @@ -2268,10 +2260,10 @@ GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, * @return #GNUNET_SYSERR on error, #GNUNET_OK on success */ enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_ecdh_eddsa_nohash (const struct GNUNET_CRYPTO_EcdhePrivateKey * - priv, - const struct GNUNET_CRYPTO_EddsaPublicKey *pub, - struct GNUNET_CRYPTO_EcdhePublicKey *dh); +GNUNET_CRYPTO_ecdh_x25519 (const struct GNUNET_CRYPTO_EcdhePrivateKey * + priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *dh); /** * @ingroup crypto diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am @@ -64,6 +64,7 @@ libgnunetutil_la_SOURCES = \ crypto_hash.c \ crypto_hash_file.c \ crypto_hkdf.c \ + crypto_kem.c \ crypto_kdf.c \ crypto_mpi.c \ crypto_paillier.c \ diff --git a/src/lib/util/crypto_ecc.c b/src/lib/util/crypto_ecc.c @@ -31,6 +31,7 @@ #include "gnunet_util_lib.h" #include "benchmark.h" #include "sodium/crypto_scalarmult.h" +#include "sodium/crypto_scalarmult_curve25519.h" #include "sodium/utils.h" #define EXTRA_CHECKS 0 @@ -763,61 +764,28 @@ GNUNET_CRYPTO_eddsa_ecdh (const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_ecdh_nohash (const struct GNUNET_CRYPTO_EddsaPrivateKey *sk, - const struct GNUNET_CRYPTO_EcdhePublicKey *pub, - struct GNUNET_CRYPTO_EcdhePublicKey *dh) +GNUNET_CRYPTO_x25519_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk, + const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *dh) { - struct GNUNET_HashCode hc; - uint8_t a[crypto_scalarmult_SCALARBYTES]; - - GNUNET_CRYPTO_hash (sk, - sizeof (struct GNUNET_CRYPTO_EcdsaPrivateKey), - &hc); - memcpy (a, &hc, sizeof (struct GNUNET_CRYPTO_EcdhePrivateKey)); - // Z := X25519(x,A) with x = p - if (0 != crypto_scalarmult (dh->q_y, a, pub->q_y)) + if (0 != crypto_scalarmult_curve25519 (dh->q_y, sk->d, pub->q_y)) return GNUNET_SYSERR; return GNUNET_OK; } enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_ecdh_eddsa_nohash (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk, - const struct GNUNET_CRYPTO_EddsaPublicKey *pk, - struct GNUNET_CRYPTO_EcdhePublicKey *dh) +GNUNET_CRYPTO_ecdh_x25519 (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk, + const struct GNUNET_CRYPTO_EcdhePublicKey *pk, + struct GNUNET_CRYPTO_EcdhePublicKey *dh) { - uint8_t curve25510_pk[crypto_scalarmult_BYTES]; - - // This maps the ed25519 point to X25519 - if (0 != crypto_sign_ed25519_pk_to_curve25519 (curve25510_pk, pk->q_y)) - return GNUNET_SYSERR; - // Z := X25519(a,X) - if (0 != crypto_scalarmult (dh->q_y, sk->d, curve25510_pk)) + if (0 != crypto_scalarmult_curve25519 (dh->q_y, sk->d, pk->q_y)) return GNUNET_SYSERR; return GNUNET_OK; } enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_kem_decaps (const struct - GNUNET_CRYPTO_EddsaPrivateKey *priv, - const struct GNUNET_CRYPTO_EcdhePublicKey *c, - struct GNUNET_ShortHashCode *prk) -{ - struct GNUNET_CRYPTO_EcdhePublicKey pub; - struct GNUNET_CRYPTO_EcdhePublicKey Z; - uint8_t ikm[sizeof *c + crypto_scalarmult_BYTES]; - - return GNUNET_CRYPTO_eddsa_ecdh_nohash (priv, &pub, - &Z); - memcpy (ikm, c, sizeof *c); - memcpy (ikm + crypto_scalarmult_BYTES, &Z, crypto_scalarmult_BYTES); - // KDF(AD, Z) - return GNUNET_CRYPTO_hkdf_extract (prk, NULL, 0, ikm, sizeof ikm); -} - - -enum GNUNET_GenericReturnValue GNUNET_CRYPTO_ecdsa_ecdh (const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv, const struct GNUNET_CRYPTO_EcdhePublicKey *pub, struct GNUNET_HashCode *key_material) @@ -852,159 +820,6 @@ GNUNET_CRYPTO_ecdh_eddsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, } -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_kem_encaps (const struct GNUNET_CRYPTO_EddsaPublicKey *pub, - struct GNUNET_CRYPTO_EcdhePublicKey *c, - struct GNUNET_ShortHashCode *prk) -{ - struct GNUNET_CRYPTO_EcdhePrivateKey sk; - struct GNUNET_CRYPTO_EcdhePublicKey Z; - uint8_t ikm[sizeof *c + crypto_scalarmult_BYTES]; - - GNUNET_CRYPTO_ecdhe_key_create (&sk); - GNUNET_CRYPTO_ecdhe_key_get_public (&sk, c); - - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_eddsa_nohash (&sk, pub, - &Z)); - memcpy (ikm, c, sizeof *c); - memcpy (ikm + crypto_scalarmult_BYTES, &Z, crypto_scalarmult_BYTES); - // KDF(AD, Z) - return GNUNET_CRYPTO_hkdf_extract (prk, NULL, 0, ikm, sizeof ikm); -} - - -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_ecdsa_fo_kem_encaps (const struct - GNUNET_CRYPTO_EcdsaPublicKey *pub, - struct GNUNET_CRYPTO_FoKemC *c, - struct GNUNET_HashCode *key_material) -{ - struct GNUNET_HashCode x; - struct GNUNET_HashCode ux; - struct GNUNET_HashCode w; - struct GNUNET_CRYPTO_EcdhePrivateKey sk; - - // This is the input to the FO OWTF - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x)); - - // We build our OWTF using a FO-transformation of ElGamal: - // U(x) - GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); - GNUNET_memcpy (&sk, &ux, sizeof (sk)); - - // B := g^U(x) - GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub); - - if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdh_ecdsa (&sk, pub, &w)) - return -1; - // w xor x (one-time pad) - GNUNET_CRYPTO_hash_xor (&w, &x, &c->y); - - // k := H(x) FIXME: U and H must be different? - GNUNET_memcpy (key_material, &ux, sizeof (ux)); - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_fo_kem_encaps (const struct - GNUNET_CRYPTO_EddsaPublicKey *pub, - struct GNUNET_CRYPTO_FoKemC *c, - struct GNUNET_HashCode *key_material) -{ - struct GNUNET_HashCode x; - struct GNUNET_HashCode ux; - struct GNUNET_HashCode w; - struct GNUNET_CRYPTO_EcdhePrivateKey sk; - enum GNUNET_GenericReturnValue ret; - - // This is the input to the FO OWTF - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x)); - - // We build our OWTF using a FO-transformation of ElGamal: - // U(x) - GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); - GNUNET_memcpy (&sk, &ux, sizeof (sk)); - - // B := g^U(x) - GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub); - - ret = GNUNET_CRYPTO_ecdh_eddsa (&sk, pub, &w); - if (GNUNET_OK != ret) - return ret; - // w xor x (one-time pad) - GNUNET_CRYPTO_hash_xor (&w, &x, &c->y); - - // k := H(x) FIXME: U and H must be different? - GNUNET_memcpy (key_material, &ux, sizeof (ux)); - return GNUNET_OK; -} - - -static enum GNUNET_GenericReturnValue -fo_kem_decaps (const struct GNUNET_HashCode *w, - const struct GNUNET_CRYPTO_FoKemC *c, - struct GNUNET_HashCode *key_material) -{ - struct GNUNET_HashCode x; - struct GNUNET_HashCode ux; - struct GNUNET_CRYPTO_EcdhePrivateKey sk; - struct GNUNET_CRYPTO_EcdhePublicKey pub_test; - - // w xor x (one-time pad) - GNUNET_CRYPTO_hash_xor (w, &c->y, &x); - - // We build our OWTF using a FO-transformation of ElGamal: - // U(x) - GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); - GNUNET_memcpy (&sk, &ux, sizeof (sk)); - - // B := g^U(x) - GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &pub_test); - - if (0 != memcmp (&pub_test, &c->pub, sizeof (c->pub))) - return GNUNET_SYSERR; // Reject - - // k := H(x) FIXME: U and H must be different? - GNUNET_memcpy (key_material, &ux, sizeof (ux)); - return GNUNET_OK; -} - - -/** - * This implementation is not testes/publicly exposed yet - */ -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_fo_kem_decaps (const struct - GNUNET_CRYPTO_EddsaPrivateKey *priv, - const struct GNUNET_CRYPTO_FoKemC *c, - struct GNUNET_HashCode *key_material) -{ - struct GNUNET_HashCode w; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_CRYPTO_eddsa_ecdh (priv, &c->pub, &w); - if (GNUNET_OK != ret) - return ret; - return fo_kem_decaps (&w, c, key_material); -} - - -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_ecdsa_fo_kem_decaps (const struct - GNUNET_CRYPTO_EcdsaPrivateKey *priv, - struct GNUNET_CRYPTO_FoKemC *c, - struct GNUNET_HashCode *key_material) -{ - struct GNUNET_HashCode w; - enum GNUNET_GenericReturnValue ret; - - ret = GNUNET_CRYPTO_ecdsa_ecdh (priv, &c->pub, &w); - if (GNUNET_OK != ret) - return ret; - return fo_kem_decaps (&w, c, key_material); -} - enum GNUNET_GenericReturnValue GNUNET_CRYPTO_ecdh_ecdsa (const struct GNUNET_CRYPTO_EcdhePrivateKey *priv, diff --git a/src/lib/util/crypto_elligator.c b/src/lib/util/crypto_elligator.c @@ -648,42 +648,3 @@ GNUNET_CRYPTO_ecdhe_elligator_key_create ( } -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_elligator_kem_encaps ( - const struct GNUNET_CRYPTO_EddsaPublicKey *pub, - struct GNUNET_CRYPTO_ElligatorRepresentative *r, - struct GNUNET_ShortHashCode *prk) -{ - struct GNUNET_CRYPTO_EcdhePrivateKey sk; - struct GNUNET_CRYPTO_EcdhePublicKey Z; - uint8_t ikm[sizeof *r + crypto_scalarmult_BYTES]; - - GNUNET_CRYPTO_ecdhe_elligator_key_create (r, &sk); - - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_eddsa_nohash (&sk, pub, - &Z)); - memcpy (ikm, r, sizeof *r); - memcpy (ikm + crypto_scalarmult_BYTES, &Z, crypto_scalarmult_BYTES); - // KDF(AD, Z) - return GNUNET_CRYPTO_hkdf_extract (prk, NULL, 0, ikm, sizeof ikm); -} - - -enum GNUNET_GenericReturnValue -GNUNET_CRYPTO_eddsa_elligator_kem_decaps ( - const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, - const struct GNUNET_CRYPTO_ElligatorRepresentative *r, - struct GNUNET_ShortHashCode *prk) -{ - struct GNUNET_CRYPTO_EcdhePublicKey pub; - struct GNUNET_CRYPTO_EcdhePublicKey Z; - uint8_t ikm[sizeof *r + crypto_scalarmult_BYTES]; - - GNUNET_CRYPTO_ecdhe_elligator_decoding (&pub, NULL, r); - GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_eddsa_ecdh_nohash (priv, &pub, - &Z)); - memcpy (ikm, r, sizeof *r); - memcpy (ikm + crypto_scalarmult_BYTES, &Z, crypto_scalarmult_BYTES); - // KDF(AD, Z) - return GNUNET_CRYPTO_hkdf_extract (prk, NULL, 0, ikm, sizeof ikm); -} diff --git a/src/lib/util/crypto_kem.c b/src/lib/util/crypto_kem.c @@ -0,0 +1,513 @@ +/* + This file is part of GNUnet. + Copyright (C) 2024 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 <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file util/crypto_kem.c + * @brief Key encapsulation mechnisms (KEMs) + * @author Martin Schanzenbach + */ +#include "platform.h" +#include "gnunet_common.h" +#include <sodium.h> +#include "gnunet_util_lib.h" +#include "sodium/crypto_scalarmult.h" +#include "sodium/crypto_scalarmult_curve25519.h" +#include "sodium/utils.h" + +/** + * A RFC9180 inspired labeled extract. + * + * @param ctx_str the context to label with (c string) + * @param salt the extract salt + * @param salt_len salt length in bytes + * @param label the label to label with + * @param label_len label length in bytes + * @param ikm initial keying material + * @param ikm_len ikm length in bytes + * @param suite_id the suite ID + * @param suite_id_len suite_id length in bytes + * @param prk the resulting extracted PRK + * @return GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +labeled_extract (const char *ctx_str, + const void *salt, size_t salt_len, + const void *label, size_t label_len, + const void *ikm, size_t ikm_len, + const uint8_t *suite_id, size_t suite_id_len, + struct GNUNET_ShortHashCode *prk) +{ + uint8_t labeled_ikm[strlen (ctx_str) + suite_id_len + label_len + + ikm_len]; + uint8_t *tmp = labeled_ikm; + + // labeled_ikm = concat("HPKE-v1", suite_id, label, ikm) + memcpy (tmp, ctx_str, strlen (ctx_str)); + tmp += strlen (ctx_str); + memcpy (tmp, suite_id, suite_id_len); + tmp += suite_id_len; + memcpy (tmp, label, label_len); + tmp += label_len; + memcpy (tmp, ikm, ikm_len); + // return Extract(salt, labeled_ikm) + return GNUNET_CRYPTO_hkdf_extract (prk, + salt, salt_len, + labeled_ikm, sizeof *labeled_ikm); +} + + +/** + * RFC9180 labeled extract. + * + * @param salt the extract salt + * @param salt_len salt length in bytes + * @param label the label to label with + * @param label_len label length in bytes + * @param ikm initial keying material + * @param ikm_len ikm length in bytes + * @param suite_id the suite ID + * @param suite_id_len suite_id length in bytes + * @param prk the resulting extracted PRK + * @return GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +rfc9180_labeled_extract (const void *salt, size_t salt_len, + const void *label, size_t label_len, + const void *ikm, size_t ikm_len, + const uint8_t *suite_id, size_t suite_id_len, + struct GNUNET_ShortHashCode *prk) +{ + return labeled_extract ("HPKE-v1", salt, salt_len, + label, label_len, + ikm, ikm_len, + suite_id, suite_id_len, + prk); +} + + +/** + * A RFC9180 inspired labeled extract. + * + * @param ctx_str the context to label with (c string) + * @param prk the extracted PRK + * @param label the label to label with + * @param label_len label length in bytes + * @param info context info + * @param info_len info in bytes + * @param suite_id the suite ID + * @param suite_id_len suite_id length in bytes + * @param out_buf output buffer, must be allocated + * @param out_len out_buf length in bytes + * @return GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +labeled_expand (const char *ctx_str, + const struct GNUNET_ShortHashCode *prk, + const char *label, size_t label_len, + const void *info, size_t info_len, + const uint8_t *suite_id, size_t suite_id_len, + void *out_buf, + uint16_t out_len) +{ + uint8_t labeled_info[2 + strlen (ctx_str) + suite_id_len + label_len + + info_len]; + uint8_t *tmp = labeled_info; + uint16_t out_len_nbo = ntohs (out_len); + + // labeled_info = concat(I2OSP(L, 2), "HPKE-v1", suite_id, + // label, info) + memcpy (tmp, &out_len_nbo, 2); + tmp += 2; + memcpy (tmp, ctx_str, strlen (ctx_str)); + tmp += strlen (ctx_str); + memcpy (tmp, suite_id, suite_id_len); + tmp += suite_id_len; + memcpy (tmp, label, label_len); + tmp += label_len; + memcpy (tmp, info, info_len); + return GNUNET_CRYPTO_hkdf_expand (out_buf, out_len, prk, + labeled_info, sizeof *labeled_info, NULL); +} + + +/** + * RFC9180 labeled extract. + * + * @param prk the extracted PRK + * @param label the label to label with + * @param label_len label length in bytes + * @param info context info + * @param info_len info in bytes + * @param suite_id the suite ID (c string) + * @param out_buf output buffer, must be allocated + * @param out_len out_buf length in bytes + * @return GNUNET_OK on success + */ +static enum GNUNET_GenericReturnValue +rfc9180_labeled_expand (const struct GNUNET_ShortHashCode *prk, + const char *label, size_t label_len, + const void *info, size_t info_len, + const uint8_t *suite_id, size_t suite_id_len, + void *out_buf, + uint16_t out_len) +{ + return labeled_expand ("HPKE-v1", + prk, + label, label_len, + info, info_len, + suite_id, suite_id_len, + out_buf, out_len); +} + + +static enum GNUNET_GenericReturnValue +rfc9180_extract_and_expand (const struct GNUNET_CRYPTO_EcdhePublicKey *dh, + const uint8_t *kem_context, size_t kem_context_len, + const uint8_t *suite_id, size_t suite_id_len, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_ShortHashCode prk; + // eae_prk = LabeledExtract("", "eae_prk", dh) + rfc9180_labeled_extract (NULL, 0, "eae_prk", strlen ("eae_prk"), + dh, sizeof *dh, + suite_id, suite_id_len, + &prk); + return rfc9180_labeled_expand (&prk, + "shared_secret", strlen ("shared_secret"), + kem_context, kem_context_len, + suite_id, suite_id_len, + shared_secret, sizeof *shared_secret); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_kem_encaps (const struct GNUNET_CRYPTO_EcdhePublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *c, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePrivateKey sk; + struct GNUNET_CRYPTO_EcdhePublicKey dh; + uint8_t kem_context[sizeof *c + sizeof *pub]; + uint8_t suite_id[strlen ("KEM") + 2]; + uint16_t kem_id = ntohs (32); // FIXME hardcode as constant + + // DHKEM(X25519, HKDF-256): kem_id = 32 + // concat("KEM", I2OSP(kem_id, 2)) + memcpy (suite_id, "KEM", 3); + memcpy (suite_id + 3, &kem_id, 2); + + // skE, pkE = GenerateKeyPair() + GNUNET_CRYPTO_ecdhe_key_create (&sk); + GNUNET_CRYPTO_ecdhe_key_get_public (&sk, c); + + // dh = DH(skE, pkR) + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_x25519 (&sk, pub, + &dh)); + // enc = SerializePublicKey(pkE) is a NOP, see Section 7.1.1 + // pkRm = SerializePublicKey(pkR) is a NOP, see Section 7.1.1 + // kem_context = concat(enc, pkRm) + memcpy (kem_context, c, sizeof *c); + memcpy (kem_context + sizeof *c, pub, sizeof *pub); + // shared_secret = ExtractAndExpand(dh, kem_context) + return rfc9180_extract_and_expand (&dh, + kem_context, sizeof *kem_context, + suite_id, sizeof *suite_id, + shared_secret); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_kem_encaps (const struct GNUNET_CRYPTO_EddsaPublicKey *pub, + struct GNUNET_CRYPTO_EcdhePublicKey *c, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePublicKey pkR; + + // This maps the ed25519 point to X25519 + if (0 != crypto_sign_ed25519_pk_to_curve25519 (pkR.q_y, pub->q_y)) + return GNUNET_SYSERR; + + return GNUNET_CRYPTO_kem_encaps (&pkR, c, shared_secret); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_kem_decaps (const struct GNUNET_CRYPTO_EcdhePrivateKey *skR, + const struct GNUNET_CRYPTO_EcdhePublicKey *c, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePublicKey dh; + uint8_t kem_context[sizeof *c + crypto_scalarmult_curve25519_BYTES]; + uint8_t pkR[crypto_scalarmult_BYTES]; + uint8_t suite_id[strlen ("KEM") + 2]; + uint16_t kem_id = ntohs (32); // FIXME hardcode as constant + + // DHKEM(X25519, HKDF-256): kem_id = 32 + // concat("KEM", I2OSP(kem_id, 2)) + memcpy (suite_id, "KEM", 3); + memcpy (suite_id + 3, &kem_id, 2); + + // pkE = DeserializePublicKey(enc) is a NOP, see Section 7.1.1 + // dh = DH(skR, pkE) + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_x25519_ecdh (skR, c, + &dh)); + // pkRm = DeserializePublicKey(pk(skR)) is a NOP, see Section 7.1.1 + crypto_scalarmult_curve25519_base (pkR, skR->d); + // kem_context = concat(enc, pkRm) + memcpy (kem_context, c, sizeof *c); + memcpy (kem_context + sizeof *c, pkR, sizeof pkR); + // shared_secret = ExtractAndExpand(dh, kem_context) + return rfc9180_extract_and_expand (&dh, + kem_context, sizeof *kem_context, + suite_id, sizeof *suite_id, + shared_secret); +} + + +// FIXME use Ed -> Curve conversion??? +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_kem_decaps (const struct + GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_EcdhePublicKey *c, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePrivateKey skR; + + // This maps the ed25519 point to X25519 + if (0 != crypto_sign_ed25519_sk_to_curve25519 (skR.d, priv->d)) + return GNUNET_SYSERR; + return GNUNET_CRYPTO_kem_decaps (&skR, c, shared_secret); + +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_elligator_kem_encaps ( + const struct GNUNET_CRYPTO_EddsaPublicKey *pub, + struct GNUNET_CRYPTO_ElligatorRepresentative *r, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePrivateKey sk; + struct GNUNET_CRYPTO_EcdhePublicKey pkR; + struct GNUNET_CRYPTO_EcdhePublicKey dh; + uint8_t kem_context[sizeof *r + sizeof *pub]; + uint8_t suite_id[strlen ("KEM") + 2]; + uint16_t kem_id = ntohs (256); // FIXME hardcode as constant + + // DHKEM(X25519, HKDF-256): kem_id = 32 + // concat("KEM", I2OSP(kem_id, 2)) + memcpy (suite_id, "KEM", 3); + memcpy (suite_id + 3, &kem_id, 2); + + // This maps the ed25519 point to X25519 + if (0 != crypto_sign_ed25519_pk_to_curve25519 (pkR.q_y, pub->q_y)) + return GNUNET_SYSERR; + + // skE, pkE = GenerateElligatorKeyPair() + GNUNET_CRYPTO_ecdhe_elligator_key_create (r, &sk); + + // dh = DH(skE, pkR) + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_ecdh_x25519 (&sk, &pkR, + &dh)); + // kem_context = concat(enc, pkRm) + // enc = SerializePublicKey(pkE) == r + // pkRm = SerializePublicKey(pkR) is a NOP, see Section 7.1.1 + // kem_context = concat(enc, pkRm) + memcpy (kem_context, r, sizeof *r); + memcpy (kem_context + sizeof *r, &pkR, sizeof pkR); + // shared_secret = ExtractAndExpand(dh, kem_context) + return rfc9180_extract_and_expand (&dh, + kem_context, sizeof *kem_context, + suite_id, sizeof *suite_id, + shared_secret); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_elligator_kem_decaps ( + const struct GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_ElligatorRepresentative *r, + struct GNUNET_ShortHashCode *shared_secret) +{ + struct GNUNET_CRYPTO_EcdhePrivateKey skR; + struct GNUNET_CRYPTO_EcdhePublicKey pkE; + struct GNUNET_CRYPTO_EcdhePublicKey dh; + uint8_t kem_context[sizeof *r + crypto_scalarmult_curve25519_BYTES]; + uint8_t pkR[crypto_scalarmult_BYTES]; + uint8_t suite_id[strlen ("KEM") + 2]; + uint16_t kem_id = ntohs (256); // FIXME hardcode as constant + + // DHKEM(X25519, HKDF-256): kem_id = 32 + // concat("KEM", I2OSP(kem_id, 2)) + memcpy (suite_id, "KEM", 3); + memcpy (suite_id + 3, &kem_id, 2); + + // This maps the ed25519 point to X25519 + if (0 != crypto_sign_ed25519_sk_to_curve25519 (skR.d, priv->d)) + return GNUNET_SYSERR; + + // pkE = DeserializePublicKey(enc) Elligator deserialize! + GNUNET_CRYPTO_ecdhe_elligator_decoding (&pkE, NULL, r); + // dh = DH(skR, pkE) + GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_x25519_ecdh (&skR, &pkE, + &dh)); + // pkRm = DeserializePublicKey(pk(skR)) is a NOP, see Section 7.1.1 + crypto_scalarmult_curve25519_base (pkR, skR.d); + memcpy (kem_context, r, sizeof *r); + memcpy (kem_context + sizeof *r, pkR, sizeof pkR); + // shared_secret = ExtractAndExpand(dh, kem_context) + return rfc9180_extract_and_expand (&dh, + kem_context, sizeof *kem_context, + suite_id, sizeof *suite_id, + shared_secret); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_ecdsa_fo_kem_encaps (const struct + GNUNET_CRYPTO_EcdsaPublicKey *pub, + struct GNUNET_CRYPTO_FoKemC *c, + struct GNUNET_HashCode *key_material) +{ + struct GNUNET_HashCode x; + struct GNUNET_HashCode ux; + struct GNUNET_HashCode w; + struct GNUNET_CRYPTO_EcdhePrivateKey sk; + + // This is the input to the FO OWTF + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x)); + + // We build our OWTF using a FO-transformation of ElGamal: + // U(x) + GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); + GNUNET_memcpy (&sk, &ux, sizeof (sk)); + + // B := g^U(x) + GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub); + + if (GNUNET_SYSERR == GNUNET_CRYPTO_ecdh_ecdsa (&sk, pub, &w)) + return -1; + // w xor x (one-time pad) + GNUNET_CRYPTO_hash_xor (&w, &x, &c->y); + + // k := H(x) FIXME: U and H must be different? + GNUNET_memcpy (key_material, &ux, sizeof (ux)); + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_fo_kem_encaps (const struct + GNUNET_CRYPTO_EddsaPublicKey *pub, + struct GNUNET_CRYPTO_FoKemC *c, + struct GNUNET_HashCode *key_material) +{ + struct GNUNET_HashCode x; + struct GNUNET_HashCode ux; + struct GNUNET_HashCode w; + struct GNUNET_CRYPTO_EcdhePrivateKey sk; + enum GNUNET_GenericReturnValue ret; + + // This is the input to the FO OWTF + GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &x, sizeof(x)); + + // We build our OWTF using a FO-transformation of ElGamal: + // U(x) + GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); + GNUNET_memcpy (&sk, &ux, sizeof (sk)); + + // B := g^U(x) + GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &c->pub); + + ret = GNUNET_CRYPTO_ecdh_eddsa (&sk, pub, &w); + if (GNUNET_OK != ret) + return ret; + // w xor x (one-time pad) + GNUNET_CRYPTO_hash_xor (&w, &x, &c->y); + + // k := H(x) FIXME: U and H must be different? + GNUNET_memcpy (key_material, &ux, sizeof (ux)); + return GNUNET_OK; +} + + +static enum GNUNET_GenericReturnValue +fo_kem_decaps (const struct GNUNET_HashCode *w, + const struct GNUNET_CRYPTO_FoKemC *c, + struct GNUNET_HashCode *key_material) +{ + struct GNUNET_HashCode x; + struct GNUNET_HashCode ux; + struct GNUNET_CRYPTO_EcdhePrivateKey sk; + struct GNUNET_CRYPTO_EcdhePublicKey pub_test; + + // w xor x (one-time pad) + GNUNET_CRYPTO_hash_xor (w, &c->y, &x); + + // We build our OWTF using a FO-transformation of ElGamal: + // U(x) + GNUNET_CRYPTO_hash (&x, sizeof (x), &ux); + GNUNET_memcpy (&sk, &ux, sizeof (sk)); + + // B := g^U(x) + GNUNET_CRYPTO_ecdhe_key_get_public (&sk, &pub_test); + + if (0 != memcmp (&pub_test, &c->pub, sizeof (c->pub))) + return GNUNET_SYSERR; // Reject + + // k := H(x) FIXME: U and H must be different? + GNUNET_memcpy (key_material, &ux, sizeof (ux)); + return GNUNET_OK; +} + + +/** + * This implementation is not testes/publicly exposed yet + */ +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_eddsa_fo_kem_decaps (const struct + GNUNET_CRYPTO_EddsaPrivateKey *priv, + const struct GNUNET_CRYPTO_FoKemC *c, + struct GNUNET_HashCode *key_material) +{ + struct GNUNET_HashCode w; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_CRYPTO_eddsa_ecdh (priv, &c->pub, &w); + if (GNUNET_OK != ret) + return ret; + return fo_kem_decaps (&w, c, key_material); +} + + +enum GNUNET_GenericReturnValue +GNUNET_CRYPTO_ecdsa_fo_kem_decaps (const struct + GNUNET_CRYPTO_EcdsaPrivateKey *priv, + struct GNUNET_CRYPTO_FoKemC *c, + struct GNUNET_HashCode *key_material) +{ + struct GNUNET_HashCode w; + enum GNUNET_GenericReturnValue ret; + + ret = GNUNET_CRYPTO_ecdsa_ecdh (priv, &c->pub, &w); + if (GNUNET_OK != ret) + return ret; + return fo_kem_decaps (&w, c, key_material); +} diff --git a/src/lib/util/meson.build b/src/lib/util/meson.build @@ -31,6 +31,7 @@ libgnunetutil_src = ['bandwidth.c', 'crypto_hash.c', 'crypto_hash_file.c', 'crypto_hkdf.c', + 'crypto_kem.c', 'crypto_kdf.c', 'crypto_mpi.c', 'crypto_paillier.c', diff --git a/src/lib/util/test_crypto_ecdh_eddsa.c b/src/lib/util/test_crypto_ecdh_eddsa.c @@ -65,45 +65,6 @@ test_ecdh () return 0; } - -static int -test_ecdh_nohash () -{ - struct GNUNET_CRYPTO_EddsaPrivateKey priv_dsa; - struct GNUNET_CRYPTO_EcdhePrivateKey priv_ecdh; - struct GNUNET_CRYPTO_EddsaPublicKey id1; - struct GNUNET_CRYPTO_EcdhePublicKey id2; - struct GNUNET_CRYPTO_EcdhePublicKey dh[2]; - - /* Generate keys */ - GNUNET_CRYPTO_eddsa_key_create (&priv_dsa); - GNUNET_CRYPTO_eddsa_key_get_public (&priv_dsa, - &id1); - for (unsigned int j = 0; j < 4; j++) - { - fprintf (stderr, ","); - GNUNET_CRYPTO_ecdhe_key_create (&priv_ecdh); - /* Extract public keys */ - GNUNET_CRYPTO_ecdhe_key_get_public (&priv_ecdh, - &id2); - /* Do ECDH */ - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_eddsa_ecdh_nohash (&priv_dsa, - &id2, - &dh[0])); - GNUNET_assert (GNUNET_OK == - GNUNET_CRYPTO_ecdh_eddsa_nohash (&priv_ecdh, - &id1, - &dh[1])); - /* Check that both DH results are equal. */ - GNUNET_assert (0 == - GNUNET_memcmp (&dh[0], - &dh[1])); - } - return 0; -} - - int main (int argc, char *argv[]) { @@ -114,8 +75,8 @@ main (int argc, char *argv[]) "."); if (0 != test_ecdh ()) return 1; - if (0 != test_ecdh_nohash ()) - return 1; + /*if (0 != test_ecdh_nohash ()) + return 1;*/ } return 0; } diff --git a/src/lib/util/test_crypto_elligator.c b/src/lib/util/test_crypto_elligator.c @@ -1,3 +1,4 @@ +#include "gnunet_common.h" #include "gnunet_util_lib.h" #include <gcrypt.h> #include <stdio.h>