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:
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>