commit 620f652542f66beb6debd80046e34c7821d9d36e
parent 32e6d39c43b72c8c4d0ec60d88fbca21119297b8
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date: Wed, 17 Jul 2024 23:56:01 +0200
util: draft implementation of HPKE / RFC9180
Diffstat:
10 files changed, 1254 insertions(+), 679 deletions(-)
diff --git a/src/include/gnunet_crypto_lib.h b/src/include/gnunet_crypto_lib.h
@@ -2087,6 +2087,58 @@ GNUNET_CRYPTO_x25519_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk,
const struct GNUNET_CRYPTO_EcdhePublicKey *pk,
struct GNUNET_CRYPTO_EcdhePublicKey *dh);
+
+/** HPKE **/
+
+enum GNUNET_CRYPTO_HpkeMode
+{
+ GNUNET_CRYPTO_HPKE_MODE_BASE = 0x00,
+ GNUNET_CRYPTO_HPKE_MODE_PSK = 0x01,
+ GNUNET_CRYPTO_HPKE_MODE_AUTH = 0x02,
+ GNUNET_CRYPTO_HPKE_MODE_AUTH_PSK = 0x03
+};
+
+// Nt
+#define GNUNET_CRYPTO_HPKE_AEAD_ID 0x0003
+
+// Nn
+#define GNUNET_CRYPTO_HPKE_NONCE_LEN 12
+
+// Nk
+#define GNUNET_CRYPTO_HPKE_KEY_LEN 32
+
+// Nt
+#define GNUNET_CRYPTO_HPKE_TAG_LEN 16
+
+enum GNUNET_CRYPTO_HpkeRole
+{
+ GNUNET_CRYPTO_HPKE_ROLE_R,
+ GNUNET_CRYPTO_HPKE_ROLE_S
+};
+
+struct GNUNET_CRYPTO_HpkeContext
+{
+ enum GNUNET_CRYPTO_HpkeRole role;
+ uint8_t key[GNUNET_CRYPTO_HPKE_KEY_LEN];
+ uint8_t base_nonce[GNUNET_CRYPTO_HPKE_NONCE_LEN];
+ uint64_t seq;
+ struct GNUNET_ShortHashCode exporter_secret;
+};
+
+/**
+ * HPKE DHKEM encapsulation (X25519)
+ * See RFC 9180
+ */
+struct GNUNET_CRYPTO_HpkeEncapsulation
+{
+ /**
+ * Q consists of an x- and a y-value, each mod p (256 bits), given
+ * here in affine coordinates and Ed25519 standard compact format.
+ */
+ unsigned char q_y[256 / 8];
+};
+
+
/**
* @ingroup crypto
* Decapsulate a key for a private X25519 key.
@@ -2102,7 +2154,7 @@ GNUNET_CRYPTO_x25519_ecdh (const struct GNUNET_CRYPTO_EcdhePrivateKey *sk,
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_kem_decaps (const struct
GNUNET_CRYPTO_EcdhePrivateKey *priv,
- const struct GNUNET_CRYPTO_EcdhePublicKey *c,
+ const struct GNUNET_CRYPTO_HpkeEncapsulation *c,
struct GNUNET_ShortHashCode *prk);
/**
@@ -2119,7 +2171,7 @@ GNUNET_CRYPTO_kem_decaps (const struct
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_kem_encaps (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
- struct GNUNET_CRYPTO_EcdhePublicKey *c,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *c,
struct GNUNET_ShortHashCode *prk);
/**
@@ -2136,11 +2188,10 @@ GNUNET_CRYPTO_kem_encaps (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_kem_encaps_norand (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
- struct GNUNET_CRYPTO_EcdhePublicKey *c,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *c,
struct GNUNET_CRYPTO_EcdhePrivateKey *skE,
struct GNUNET_ShortHashCode *prk);
-
/**
* @ingroup crypto
* Decapsulate a key for a private EdDSA key.
@@ -2156,7 +2207,7 @@ GNUNET_CRYPTO_kem_encaps_norand (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_eddsa_kem_decaps (const struct
GNUNET_CRYPTO_EddsaPrivateKey *priv,
- const struct GNUNET_CRYPTO_EcdhePublicKey *c,
+ const struct GNUNET_CRYPTO_HpkeEncapsulation *c,
struct GNUNET_ShortHashCode *prk);
/**
@@ -2173,9 +2224,128 @@ GNUNET_CRYPTO_eddsa_kem_decaps (const struct
*/
enum GNUNET_GenericReturnValue
GNUNET_CRYPTO_eddsa_kem_encaps (const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
- struct GNUNET_CRYPTO_EcdhePublicKey *c,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *c,
struct GNUNET_ShortHashCode *prk);
+
+/**
+ * RFC9180 HPKE encryption.
+ * This sets the encryption context up for a sender of
+ * encrypted messages.
+ * Algorithm: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
+ *
+ * The encapsulation "enc" must be exchanged with the receiver.
+ * From then on, encrypted messages can be created and sent using "ctx"
+ *
+ * @param pkR the X25519 receiver public key
+ * @param info the info context separator
+ * @param info_len length of info in bytes
+ * @param enc the encapsulation to exchange with the other party
+ * @param ctx the encryption context allocated by caller
+ * @return GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_sender_setup (const struct GNUNET_CRYPTO_EcdhePublicKey *pkR,
+ const uint8_t *info, size_t info_len,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ struct GNUNET_CRYPTO_HpkeContext *ctx);
+
+/**
+ * RFC9180 HPKE encryption.
+ * This sets the encryption context up for a sender of
+ * encrypted messages.
+ * Algorithm: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
+ *
+ * The encapsulation "enc" must be exchanged with the receiver.
+ * From then on, encrypted messages can be created and sent using "ctx"
+ *
+ * @param pkR the X25519 receiver public key
+ * @param info the info context separator
+ * @param info_len length of info in bytes
+ * @param enc the encapsulation to exchange with the other party
+ * @param ctx the encryption context allocated by caller
+ * @return GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_sender_setup_norand (
+ struct GNUNET_CRYPTO_EcdhePrivateKey *skR,
+ const struct GNUNET_CRYPTO_EcdhePublicKey *pkR,
+ const uint8_t *info, size_t info_len,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ struct GNUNET_CRYPTO_HpkeContext *ctx);
+
+/**
+ * RFC9180 HPKE encryption.
+ * This sets the encryption context up for a receiver of
+ * encrypted messages.
+ * Algorithm: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
+ *
+ * The encapsulation "enc" must be exchanged with the receiver.
+ * From then on, encrypted messages can be decrypted using "ctx"
+ *
+ * @param enc the encapsulation from the sender
+ * @param pkR the X25519 receiver secret key
+ * @param info the info context separator
+ * @param info_len length of info in bytes
+ * @param ctx the encryption context allocated by caller
+ * @return GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_receiver_setup (
+ const struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ const struct GNUNET_CRYPTO_EcdhePrivateKey *skR,
+ const uint8_t *info,
+ size_t info_len,
+ struct GNUNET_CRYPTO_HpkeContext *ctx);
+
+/**
+ * RFC9180 HPKE encryption.
+ * Encrypt messages in a context.
+ * Algorithm: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
+ *
+ * The encapsulation "enc" must be exchanged with the receiver.
+ * From then on, encrypted messages can be decrypted using "ctx"
+ *
+ * @param ctx the encryption context
+ * @param aad addition authenticated data to send (not encrypted)
+ * @param aad_len length of aad in bytes
+ * @param pt plaintext data to encrypt
+ * @param pt_len length of pt in bytes
+ * @param ct ciphertext to send (to be allocated by caller)
+ * @param ct_len length of written bytes in ct
+ * @return GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_seal (struct GNUNET_CRYPTO_HpkeContext *ctx,
+ const uint8_t*aad, size_t aad_len,
+ const uint8_t *pt, size_t pt_len,
+ uint8_t *ct, unsigned long long ct_len);
+
+/**
+ * RFC9180 HPKE encryption.
+ * Decrypt messages in a context.
+ * Algorithm: DHKEM(X25519, HKDF-SHA256), HKDF-SHA256, ChaCha20Poly1305
+ *
+ * The encapsulation "enc" must be exchanged with the receiver.
+ * From then on, encrypted messages can be decrypted using "ctx"
+ *
+ * @param ctx the encryption context
+ * @param aad addition authenticated data to send (not encrypted)
+ * @param aad_len length of aad in bytes
+ * @param ct ciphertext to decrypt
+ * @param ct_len length of ct in bytes
+ * @param pt plaintext (to be allocated by caller)
+ * @param pt_len length of written bytes in pt
+ * @return GNUNET_OK on success
+ */
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_open (struct GNUNET_CRYPTO_HpkeContext *ctx,
+ const uint8_t*aad, size_t aad_len,
+ const uint8_t *ct, size_t ct_len,
+ uint8_t *pt, unsigned long long pt_len);
+
+/** HPKE END **/
+
/**
* This is the encapsulated key of our FO-KEM.
*/
diff --git a/src/lib/util/Makefile.am b/src/lib/util/Makefile.am
@@ -64,7 +64,7 @@ libgnunetutil_la_SOURCES = \
crypto_hash.c \
crypto_hash_file.c \
crypto_hkdf.c \
- crypto_kem.c \
+ crypto_hpke.c \
crypto_kdf.c \
crypto_mpi.c \
crypto_paillier.c \
@@ -214,7 +214,7 @@ check_PROGRAMS = \
test_crypto_hash \
test_crypto_hash_context \
test_crypto_hkdf \
- test_crypto_kem \
+ test_crypto_hpke \
test_crypto_kdf \
test_crypto_paillier \
test_crypto_random \
@@ -439,9 +439,9 @@ test_crypto_hkdf_SOURCES = \
test_crypto_hkdf_LDADD = \
libgnunetutil.la
-test_crypto_kem_SOURCES = \
- test_crypto_kem.c
-test_crypto_kem_LDADD = \
+test_crypto_hpke_SOURCES = \
+ test_crypto_hpke.c
+test_crypto_hpke_LDADD = \
libgnunetutil.la -lgcrypt
test_crypto_kdf_SOURCES = \
diff --git a/src/lib/util/crypto_hpke.c b/src/lib/util/crypto_hpke.c
@@ -0,0 +1,768 @@
+/*
+ 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_hpke.c
+ * @brief Hybrid Public Key Encryption (HPKE) and Key encapsulation mechnisms (KEMs)
+ * @author Martin Schanzenbach
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include <sodium.h>
+#include <stdint.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)
+{
+ size_t labeled_ikm_len = strlen (ctx_str) + suite_id_len
+ + label_len + ikm_len;
+ uint8_t labeled_ikm[labeled_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, labeled_ikm_len);
+}
+
+
+/**
+ * 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 = htons (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);
+}
+
+
+static enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_labeled_extract_and_expand (const struct
+ GNUNET_CRYPTO_EcdhePublicKey *dh,
+ const char *extract_ctx,
+ const char *expand_ctx,
+ const void*extract_lbl, size_t
+ extract_lbl_len,
+ const void*expand_lbl, size_t
+ expand_lbl_len,
+ 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)
+ labeled_extract (extract_ctx,
+ NULL, 0,
+ extract_lbl, extract_lbl_len,
+ dh, sizeof *dh,
+ suite_id, suite_id_len,
+ &prk);
+ return labeled_expand (expand_ctx,
+ &prk,
+ expand_lbl, expand_lbl_len,
+ kem_context, kem_context_len,
+ suite_id, suite_id_len,
+ shared_secret, sizeof *shared_secret);
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_kem_encaps_norand (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *c,
+ struct GNUNET_CRYPTO_EcdhePrivateKey *skE,
+ struct GNUNET_ShortHashCode *shared_secret)
+{
+ struct GNUNET_CRYPTO_EcdhePublicKey dh;
+ uint8_t kem_context[sizeof *c + sizeof *pub];
+ uint8_t suite_id[strlen ("KEM") + 2];
+ uint16_t kem_id = htons (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_get_public (skE,
+ (struct GNUNET_CRYPTO_EcdhePublicKey*) c);
+
+ // dh = DH(skE, pkR)
+ if (GNUNET_OK != GNUNET_CRYPTO_ecdh_x25519 (skE, pub,
+ &dh))
+ return GNUNET_SYSERR; // ValidationError
+ // 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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
+ &dh,
+ "HPKE-v1",
+ "HPKE-v1",
+ "eae_prk", strlen ("eae_prk"),
+ "shared_secret", strlen ("shared_secret"),
+ kem_context, sizeof kem_context,
+ suite_id, sizeof suite_id,
+ shared_secret);
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_kem_encaps (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *c,
+ struct GNUNET_ShortHashCode *shared_secret)
+{
+ struct GNUNET_CRYPTO_EcdhePrivateKey sk;
+ // skE, pkE = GenerateKeyPair()
+ GNUNET_CRYPTO_ecdhe_key_create (&sk);
+
+ return GNUNET_CRYPTO_kem_encaps_norand (pub, c, &sk, shared_secret);
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_eddsa_kem_encaps (const struct GNUNET_CRYPTO_EddsaPublicKey *pub,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *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_HpkeEncapsulation *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 = htons (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)
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_x25519_ecdh (skR,
+ (struct GNUNET_CRYPTO_EcdhePublicKey*) c,
+ &dh))
+ return GNUNET_SYSERR; // ValidationError
+
+ // 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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
+ &dh,
+ "HPKE-v1",
+ "HPKE-v1",
+ "eae_prk", strlen ("eae_prk"),
+ "shared_secret", strlen ("shared_secret"),
+ 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_HpkeEncapsulation *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 = htons (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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
+ &dh,
+ "HPKE-v1",
+ "HPKE-v1",
+ "eae_prk", strlen ("eae_prk"),
+ "shared_secret", strlen ("shared_secret"),
+ 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 = htons (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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
+ &dh,
+ "HPKE-v1",
+ "HPKE-v1",
+ "eae_prk", strlen ("eae_prk"),
+ "shared_secret", strlen ("shared_secret"),
+ 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);
+}
+
+
+static enum GNUNET_GenericReturnValue
+verify_psk_inputs (enum GNUNET_CRYPTO_HpkeMode mode,
+ const uint8_t *psk, size_t psk_len,
+ const uint8_t *psk_id, size_t psk_id_len)
+{
+ bool got_psk;
+ bool got_psk_id;
+
+ got_psk = (0 != psk_len);
+ got_psk_id = (0 != psk_id_len);
+
+ if (got_psk != got_psk_id)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Inconsistent PSK inputs\n");
+ return GNUNET_SYSERR;
+ }
+
+ if (got_psk &&
+ ((GNUNET_CRYPTO_HPKE_MODE_BASE == mode) ||
+ (GNUNET_CRYPTO_HPKE_MODE_AUTH == mode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "PSK input provided when not needed\n");
+ return GNUNET_SYSERR;
+ }
+ if (! got_psk &&
+ ((GNUNET_CRYPTO_HPKE_MODE_PSK == mode) ||
+ (GNUNET_CRYPTO_HPKE_MODE_AUTH_PSK == mode)))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Missing required PSK input\n");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+key_schedule (enum GNUNET_CRYPTO_HpkeRole role,
+ enum GNUNET_CRYPTO_HpkeMode mode,
+ const struct GNUNET_ShortHashCode *shared_secret,
+ const uint8_t *info, size_t info_len,
+ const uint8_t *psk, size_t psk_len,
+ const uint8_t *psk_id, size_t psk_id_len,
+ struct GNUNET_CRYPTO_HpkeContext *ctx)
+{
+ struct GNUNET_ShortHashCode psk_id_hash;
+ struct GNUNET_ShortHashCode info_hash;
+ struct GNUNET_ShortHashCode secret;
+ uint8_t key_schedule_context[1 + sizeof info_hash * 2];
+ uint8_t suite_id[strlen ("HPKE") + 6];
+ uint16_t kem_id = htons (32); // FIXME hardcode as constant
+ uint16_t kdf_id = htons (1); // HKDF-256 FIXME hardcode as constant
+ uint16_t aead_id = htons (3); // ChaCha20Poly1305 FIXME hardcode as constant
+
+ // DHKEM(X25519, HKDF-256): kem_id = 32
+ // concat("KEM", I2OSP(kem_id, 2))
+ memcpy (suite_id, "HPKE", 4);
+ memcpy (suite_id + 4, &kem_id, 2);
+ memcpy (suite_id + 6, &kdf_id, 2);
+ memcpy (suite_id + 8, &aead_id, 2);
+
+ if (GNUNET_OK != verify_psk_inputs (mode, psk, psk_len, psk_id, psk_id_len))
+ return GNUNET_SYSERR;
+
+ if (GNUNET_OK != labeled_extract ("HPKE-v1", NULL, 0,
+ "psk_id_hash", strlen ("psk_id_hash"),
+ psk_id, psk_id_len,
+ suite_id, sizeof suite_id, &psk_id_hash))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != labeled_extract ("HPKE-v1", NULL, 0,
+ "info_hash", strlen ("info_hash"),
+ info, info_len,
+ suite_id, sizeof suite_id, &info_hash))
+ return GNUNET_SYSERR;
+ memcpy (key_schedule_context, &mode, 1);
+ memcpy (key_schedule_context + 1, &psk_id_hash, sizeof psk_id_hash);
+ memcpy (key_schedule_context + 1 + sizeof psk_id_hash,
+ &info_hash, sizeof info_hash);
+ if (GNUNET_OK != labeled_extract ("HPKE-v1",
+ shared_secret, sizeof *shared_secret,
+ "secret", strlen ("secret"),
+ psk, psk_len,
+ suite_id, sizeof suite_id, &secret))
+ return GNUNET_SYSERR;
+ // key = LabeledExpand(secret, "key", key_schedule_context, Nk)
+ // Note: Nk == sizeof ctx->key
+ if (GNUNET_OK != labeled_expand ("HPKE-v1",
+ &secret,
+ "key", strlen ("key"),
+ &key_schedule_context,
+ sizeof key_schedule_context,
+ suite_id, sizeof suite_id,
+ ctx->key, sizeof ctx->key))
+ return GNUNET_SYSERR;
+ // base_nonce = LabeledExpand(secret, "base_nonce",
+ // key_schedule_context, Nn)
+ if (GNUNET_OK != labeled_expand ("HPKE-v1",
+ &secret,
+ "base_nonce", strlen ("base_nonce"),
+ &key_schedule_context,
+ sizeof key_schedule_context,
+ suite_id, sizeof suite_id,
+ ctx->base_nonce, sizeof ctx->base_nonce))
+ return GNUNET_SYSERR;
+ // exporter_secret = LabeledExpand(secret, "exp",
+ // key_schedule_context, Nh)
+ if (GNUNET_OK != labeled_expand ("HPKE-v1",
+ &secret,
+ "exp", strlen ("exp"),
+ &key_schedule_context,
+ sizeof key_schedule_context,
+ suite_id, sizeof suite_id,
+ &ctx->exporter_secret,
+ sizeof ctx->exporter_secret))
+ return GNUNET_SYSERR;
+ ctx->seq = 0;
+ ctx->role = role;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_sender_setup_norand (
+ struct GNUNET_CRYPTO_EcdhePrivateKey *skR,
+ const struct
+ GNUNET_CRYPTO_EcdhePublicKey *pkR,
+ const uint8_t *info, size_t info_len,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ struct GNUNET_CRYPTO_HpkeContext *ctx)
+{
+ struct GNUNET_ShortHashCode shared_secret;
+
+ if (GNUNET_OK != GNUNET_CRYPTO_kem_encaps_norand (pkR, enc, skR,
+ &shared_secret))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != key_schedule (GNUNET_CRYPTO_HPKE_ROLE_S,
+ GNUNET_CRYPTO_HPKE_MODE_BASE,
+ &shared_secret,
+ info, info_len,
+ NULL, 0,
+ NULL, 0,
+ ctx))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_sender_setup (const struct GNUNET_CRYPTO_EcdhePublicKey *pkR,
+ const uint8_t *info, size_t info_len,
+ struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ struct GNUNET_CRYPTO_HpkeContext *ctx)
+{
+ struct GNUNET_CRYPTO_EcdhePrivateKey sk;
+ // skE, pkE = GenerateKeyPair()
+ GNUNET_CRYPTO_ecdhe_key_create (&sk);
+
+ return GNUNET_CRYPTO_hpke_sender_setup_norand (&sk, pkR, info, info_len, enc,
+ ctx);
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_receiver_setup (
+ const struct GNUNET_CRYPTO_HpkeEncapsulation *enc,
+ const struct GNUNET_CRYPTO_EcdhePrivateKey *skR,
+ const uint8_t *info, size_t info_len,
+ struct GNUNET_CRYPTO_HpkeContext *ctx)
+{
+ struct GNUNET_ShortHashCode shared_secret;
+
+ if (GNUNET_OK != GNUNET_CRYPTO_kem_decaps (skR, enc, &shared_secret))
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != key_schedule (GNUNET_CRYPTO_HPKE_ROLE_R,
+ GNUNET_CRYPTO_HPKE_MODE_BASE,
+ &shared_secret,
+ info, info_len,
+ NULL, 0,
+ NULL, 0,
+ ctx))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+static enum GNUNET_GenericReturnValue
+increment_seq (struct GNUNET_CRYPTO_HpkeContext *ctx)
+{
+ if (ctx->seq >= UINT64_MAX)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "MessageLimitReached\n");
+ return GNUNET_SYSERR;
+ }
+ ctx->seq = GNUNET_htonll (GNUNET_ntohll (ctx->seq) + 1);
+ return GNUNET_OK;
+}
+
+
+static void
+compute_nonce (struct GNUNET_CRYPTO_HpkeContext *ctx,
+ uint8_t *nonce)
+{
+ size_t offset = GNUNET_CRYPTO_HPKE_NONCE_LEN - sizeof ctx->seq;
+ int j = 0;
+ for (int i = 0; i < GNUNET_CRYPTO_HPKE_NONCE_LEN; i++)
+ {
+ // FIXME correct byte order?
+ if (i < offset)
+ memset (&nonce[i], ctx->base_nonce[i], 1);
+ else
+ nonce[i] = ctx->base_nonce[i] ^ ((uint8_t*) &ctx->seq)[j++];
+ }
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_seal (struct GNUNET_CRYPTO_HpkeContext *ctx,
+ const uint8_t*aad, size_t aad_len,
+ const uint8_t *pt, size_t pt_len,
+ uint8_t *ct, unsigned long long ct_len)
+{
+ uint8_t comp_nonce[GNUNET_CRYPTO_HPKE_NONCE_LEN];
+ compute_nonce (ctx, comp_nonce);
+ crypto_aead_chacha20poly1305_ietf_encrypt (ct, &ct_len,
+ pt, pt_len,
+ aad, aad_len,
+ NULL,
+ comp_nonce,
+ ctx->key);
+ if (GNUNET_OK != increment_seq (ctx))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+GNUNET_CRYPTO_hpke_open (struct GNUNET_CRYPTO_HpkeContext *ctx,
+ const uint8_t*aad, size_t aad_len,
+ const uint8_t *ct, size_t ct_len,
+ uint8_t *pt, unsigned long long pt_len)
+{
+ uint8_t comp_nonce[GNUNET_CRYPTO_HPKE_NONCE_LEN];
+ compute_nonce (ctx, comp_nonce);
+ if (0 != crypto_aead_chacha20poly1305_ietf_decrypt (pt, &pt_len,
+ NULL,
+ ct, ct_len,
+ aad, aad_len,
+ comp_nonce,
+ ctx->key))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "OpenError\n");
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != increment_seq (ctx))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
diff --git a/src/lib/util/crypto_kem.c b/src/lib/util/crypto_kem.c
@@ -1,503 +0,0 @@
-/*
- 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)
-{
- size_t labeled_ikm_len = strlen (ctx_str) + suite_id_len
- + label_len + ikm_len;
- uint8_t labeled_ikm[labeled_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, labeled_ikm_len);
-}
-
-
-/**
- * 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 = htons (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);
-}
-
-
-static enum GNUNET_GenericReturnValue
-GNUNET_CRYPTO_hpke_labeled_extract_and_expand (const struct
- GNUNET_CRYPTO_EcdhePublicKey *dh,
- const char *extract_ctx,
- const char *expand_ctx,
- const void*extract_lbl, size_t
- extract_lbl_len,
- const void*expand_lbl, size_t
- expand_lbl_len,
- 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)
- labeled_extract (extract_ctx,
- NULL, 0,
- extract_lbl, extract_lbl_len,
- dh, sizeof *dh,
- suite_id, suite_id_len,
- &prk);
- return labeled_expand (expand_ctx,
- &prk,
- expand_lbl, expand_lbl_len,
- kem_context, kem_context_len,
- suite_id, suite_id_len,
- shared_secret, sizeof *shared_secret);
-}
-
-
-enum GNUNET_GenericReturnValue
-GNUNET_CRYPTO_kem_encaps_norand (const struct GNUNET_CRYPTO_EcdhePublicKey *pub,
- struct GNUNET_CRYPTO_EcdhePublicKey *c,
- struct GNUNET_CRYPTO_EcdhePrivateKey *skE,
- struct GNUNET_ShortHashCode *shared_secret)
-{
- struct GNUNET_CRYPTO_EcdhePublicKey dh;
- uint8_t kem_context[sizeof *c + sizeof *pub];
- uint8_t suite_id[strlen ("KEM") + 2];
- uint16_t kem_id = htons (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_get_public (skE, c);
-
- // dh = DH(skE, pkR)
- if (GNUNET_OK != GNUNET_CRYPTO_ecdh_x25519 (skE, pub,
- &dh))
- return GNUNET_SYSERR; // ValidationError
- // 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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
- &dh,
- "HPKE-v1",
- "HPKE-v1",
- "eae_prk", strlen ("eae_prk"),
- "shared_secret", strlen ("shared_secret"),
- kem_context, sizeof kem_context,
- suite_id, sizeof suite_id,
- 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;
- // skE, pkE = GenerateKeyPair()
- GNUNET_CRYPTO_ecdhe_key_create (&sk);
-
- return GNUNET_CRYPTO_kem_encaps_norand (pub, c, &sk, 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 = htons (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)
- if (GNUNET_OK != GNUNET_CRYPTO_x25519_ecdh (skR, c,
- &dh))
- return GNUNET_SYSERR; // ValidationError
-
- // 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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
- &dh,
- "HPKE-v1",
- "HPKE-v1",
- "eae_prk", strlen ("eae_prk"),
- "shared_secret", strlen ("shared_secret"),
- 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 = htons (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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
- &dh,
- "HPKE-v1",
- "HPKE-v1",
- "eae_prk", strlen ("eae_prk"),
- "shared_secret", strlen ("shared_secret"),
- 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 = htons (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 GNUNET_CRYPTO_hpke_labeled_extract_and_expand (
- &dh,
- "HPKE-v1",
- "HPKE-v1",
- "eae_prk", strlen ("eae_prk"),
- "shared_secret", strlen ("shared_secret"),
- 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,7 +31,7 @@ libgnunetutil_src = ['bandwidth.c',
'crypto_hash.c',
'crypto_hash_file.c',
'crypto_hkdf.c',
- 'crypto_kem.c',
+ 'crypto_hpke.c',
'crypto_kdf.c',
'crypto_mpi.c',
'crypto_paillier.c',
@@ -321,13 +321,13 @@ test('test_crypto_ecdh_ecdsa', testcrypto_ecdh_ecdsa,
workdir: meson.current_build_dir(),
suite: ['util', 'util-crypto'])
-testcrypto_kem = executable ('test_crypto_kem',
- ['test_crypto_kem.c'],
+testcrypto_hpke = executable ('test_crypto_hpke',
+ ['test_crypto_hpke.c'],
dependencies: [libgnunetutil_dep],
include_directories: [incdir, configuration_inc],
build_by_default: false,
install: false)
-test('test_crypto_kem', testcrypto_kem,
+test('test_crypto_hpke', testcrypto_hpke,
workdir: meson.current_build_dir(),
suite: ['util', 'util-crypto'])
diff --git a/src/lib/util/test_crypto_hpke.c b/src/lib/util/test_crypto_hpke.c
@@ -0,0 +1,287 @@
+/*
+ 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/test_crypto_kem.c
+ * @brief testcase for KEMs including RFC9180 DHKEM
+ * @author Martin Schanzenbach
+ */
+
+#include "gnunet_common.h"
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+static char *rfc9180_a2_skEm_str =
+ "f4ec9b33b792c372c1d2c2063507b684ef925b8c75a42dbcbf57d63ccd381600";
+static char *rfc9180_a2_skRm_str =
+ "8057991eef8f1f1af18f4a9491d16a1ce333f695d4db8e38da75975c4478e0fb";
+static char *rfc9180_a2_enc_str =
+ "1afa08d3dec047a643885163f1180476fa7ddb54c6a8029ea33f95796bf2ac4a";
+static char *rfc9180_a2_shared_secret_str =
+ "0bbe78490412b4bbea4812666f7916932b828bba79942424abb65244930d69a7";
+static char *rfc9180_a2_key_str =
+ "ad2744de8e17f4ebba575b3f5f5a8fa1f69c2a07f6e7500bc60ca6e3e3ec1c91";
+static char *rfc9180_a2_base_nonce_str =
+ "5c4d98150661b848853b547f";
+static char *rfc9180_a2_info_str =
+ "4f6465206f6e2061204772656369616e2055726e";
+static char *rfc9180_a2_pt_str =
+ "4265617574792069732074727574682c20747275746820626561757479";
+static char *rfc9180_a2_aad_seq0_str =
+ "436f756e742d30";
+static char *rfc9180_a2_aad_seq1_str =
+ "436f756e742d31";
+static char *rfc9180_a2_aad_seq255_str =
+ "436f756e742d323535";
+static char *rfc9180_a2_ct_seq0_str =
+ "1c5250d8034ec2b784ba2cfd69dbdb8af406cfe3ff938e131f0def8c8b60b4db21993c62ce81883d2dd1b51a28";
+static char *rfc9180_a2_ct_seq1_str =
+ "6b53c051e4199c518de79594e1c4ab18b96f081549d45ce015be002090bb119e85285337cc95ba5f59992dc98c";
+static char *rfc9180_a2_ct_seq255_str =
+ "18ab939d63ddec9f6ac2b60d61d36a7375d2070c9b683861110757062c52b8880a5f6b3936da9cd6c23ef2a95c";
+
+
+static int
+parsehex (char *src, char *dst, size_t dstlen, int invert)
+{
+ char *line = src;
+ char *data = line;
+ int off;
+ int read_byte;
+ int data_len = 0;
+
+ while (sscanf (data, " %02x%n", &read_byte, &off) == 1)
+ {
+ if (invert)
+ dst[dstlen - 1 - data_len++] = read_byte;
+ else
+ dst[data_len++] = read_byte;
+ data += off;
+ }
+ return data_len;
+}
+
+
+static void
+print_bytes_ (void *buf,
+ size_t buf_len,
+ int fold,
+ int in_be)
+{
+ int i;
+
+ for (i = 0; i < buf_len; i++)
+ {
+ if (0 != i)
+ {
+ if ((0 != fold) && (i % fold == 0))
+ printf ("\n ");
+ else
+ printf (" ");
+ }
+ else
+ {
+ printf (" ");
+ }
+ if (in_be)
+ printf ("%02x", ((unsigned char*) buf)[buf_len - 1 - i]);
+ else
+ printf ("%02x", ((unsigned char*) buf)[i]);
+ }
+ printf ("\n");
+}
+
+
+static void
+print_bytes (void *buf,
+ size_t buf_len,
+ int fold)
+{
+ print_bytes_ (buf, buf_len, fold, 0);
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ struct GNUNET_CRYPTO_EcdhePrivateKey rfc9180_a2_skEm;
+ struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a2_pkEm;
+ struct GNUNET_CRYPTO_EcdhePrivateKey rfc9180_a2_skRm;
+ struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a2_pkRm;
+ struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a2_enc;
+ struct GNUNET_CRYPTO_EcdhePublicKey enc;
+ struct GNUNET_ShortHashCode rfc9180_a2_shared_secret;
+ struct GNUNET_ShortHashCode shared_secret;
+ struct GNUNET_CRYPTO_HpkeContext ctxS;
+ struct GNUNET_CRYPTO_HpkeContext ctxR;
+ uint8_t rfc9180_a2_base_nonce[GNUNET_CRYPTO_HPKE_NONCE_LEN];
+ uint8_t rfc9180_a2_key[GNUNET_CRYPTO_HPKE_NONCE_LEN];
+ uint8_t rfc9180_a2_info[strlen (rfc9180_a2_info_str) / 2];
+ uint8_t rfc9180_a2_pt[strlen (rfc9180_a2_pt_str) / 2];
+ uint8_t rfc9180_a2_aad[strlen (rfc9180_a2_aad_seq0_str) / 2];
+ uint8_t rfc9180_a2_aad_seq255[strlen (rfc9180_a2_aad_seq255_str) / 2];
+ uint8_t rfc9180_a2_ct_seq0[strlen (rfc9180_a2_ct_seq0_str) / 2];
+ uint8_t rfc9180_a2_ct_seq1[strlen (rfc9180_a2_ct_seq1_str) / 2];
+ uint8_t rfc9180_a2_ct_seq255[strlen (rfc9180_a2_ct_seq255_str) / 2];
+ uint8_t test_ct[strlen (rfc9180_a2_ct_seq0_str) / 2];
+ uint8_t test_pt[strlen (rfc9180_a2_pt_str) / 2];
+
+ GNUNET_log_setup ("test-crypto-kem", "WARNING", NULL);
+
+ parsehex (rfc9180_a2_skEm_str,
+ (char*) &rfc9180_a2_skEm.d,
+ sizeof rfc9180_a2_skEm, 0);
+ parsehex (rfc9180_a2_skRm_str,
+ (char*) &rfc9180_a2_skRm.d,
+ sizeof rfc9180_a2_skRm, 0);
+ parsehex (rfc9180_a2_enc_str,
+ (char*) &rfc9180_a2_enc,
+ sizeof rfc9180_a2_enc, 0);
+ parsehex (rfc9180_a2_shared_secret_str,
+ (char*) &rfc9180_a2_shared_secret,
+ sizeof rfc9180_a2_shared_secret, 0);
+ parsehex (rfc9180_a2_base_nonce_str,
+ (char*) &rfc9180_a2_base_nonce,
+ sizeof rfc9180_a2_base_nonce, 0);
+ parsehex (rfc9180_a2_key_str,
+ (char*) &rfc9180_a2_key,
+ sizeof rfc9180_a2_key, 0);
+ parsehex (rfc9180_a2_info_str,
+ (char*) &rfc9180_a2_info,
+ sizeof rfc9180_a2_info, 0);
+ parsehex (rfc9180_a2_pt_str,
+ (char*) &rfc9180_a2_pt,
+ sizeof rfc9180_a2_pt, 0);
+ parsehex (rfc9180_a2_aad_seq0_str,
+ (char*) &rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad, 0);
+ parsehex (rfc9180_a2_ct_seq0_str,
+ (char*) &rfc9180_a2_ct_seq0,
+ sizeof rfc9180_a2_ct_seq0, 0);
+ parsehex (rfc9180_a2_ct_seq1_str,
+ (char*) &rfc9180_a2_ct_seq1,
+ sizeof rfc9180_a2_ct_seq1, 0);
+ parsehex (rfc9180_a2_ct_seq255_str,
+ (char*) &rfc9180_a2_ct_seq255,
+ sizeof rfc9180_a2_ct_seq255, 0);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&rfc9180_a2_skEm, &rfc9180_a2_pkEm);
+ GNUNET_CRYPTO_ecdhe_key_get_public (&rfc9180_a2_skRm, &rfc9180_a2_pkRm);
+ printf ("pkRm: ");
+ print_bytes (&rfc9180_a2_pkRm, sizeof rfc9180_a2_pkRm, 0);
+ printf ("\n");
+ printf ("pkEm: ");
+ print_bytes (&rfc9180_a2_pkEm, sizeof rfc9180_a2_pkEm, 0);
+ printf ("\n");
+ GNUNET_CRYPTO_kem_encaps_norand (&rfc9180_a2_pkRm, &enc, &rfc9180_a2_skEm, &
+ shared_secret);
+ GNUNET_assert (0 == GNUNET_memcmp (&enc, &rfc9180_a2_enc));
+ printf ("enc: ");
+ print_bytes (&enc, sizeof enc, 0);
+ printf ("\n");
+ printf ("shared_secret: ");
+ print_bytes (&shared_secret, sizeof shared_secret, 0);
+ GNUNET_assert (0 == GNUNET_memcmp (&shared_secret, &rfc9180_a2_shared_secret))
+ ;
+ printf ("\n");
+ GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hpke_sender_setup_norand (
+ &rfc9180_a2_skEm, &rfc9180_a2_pkRm,
+ rfc9180_a2_info, sizeof rfc9180_a2_info,
+ &enc, &ctxS));
+ GNUNET_assert (GNUNET_OK == GNUNET_CRYPTO_hpke_receiver_setup (
+ &enc,
+ &rfc9180_a2_skRm,
+ rfc9180_a2_info, sizeof rfc9180_a2_info,
+ &ctxR));
+ GNUNET_assert (0 == GNUNET_memcmp (ctxR.key, ctxS.key));
+ GNUNET_assert (0 == GNUNET_memcmp (ctxR.base_nonce, ctxS.base_nonce));
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_seal (&ctxS, rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad,
+ rfc9180_a2_pt, sizeof rfc9180_a2_pt,
+ test_ct, sizeof test_ct));
+ GNUNET_assert (0 == memcmp (rfc9180_a2_ct_seq0, test_ct, sizeof test_ct));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_open (&ctxR,
+ rfc9180_a2_aad, sizeof rfc9180_a2_aad,
+ rfc9180_a2_ct_seq0, sizeof
+ rfc9180_a2_ct_seq0,
+ test_pt, sizeof test_pt));
+ GNUNET_assert (0 == memcmp (rfc9180_a2_pt, test_pt, sizeof test_pt));
+ parsehex (rfc9180_a2_aad_seq1_str,
+ (char*) &rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad, 0);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_seal (&ctxS,
+ rfc9180_a2_aad,sizeof rfc9180_a2_aad,
+ rfc9180_a2_pt, sizeof rfc9180_a2_pt,
+ test_ct, sizeof test_ct));
+ print_bytes (rfc9180_a2_ct_seq1, sizeof test_ct, 0);
+ print_bytes (test_ct, sizeof test_ct, 0);
+ GNUNET_assert (0 == memcmp (rfc9180_a2_ct_seq1, test_ct, sizeof test_ct));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_open (&ctxR,
+ rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad,
+ test_ct,
+ sizeof test_ct,
+ test_pt, sizeof test_pt));
+ GNUNET_assert (0 == memcmp (rfc9180_a2_pt, test_pt, sizeof test_pt));
+ parsehex (rfc9180_a2_aad_seq255_str,
+ (char*) &rfc9180_a2_aad_seq255,
+ sizeof rfc9180_a2_aad_seq255, 0);
+ for (int i = 0; i < 253; i++)
+ {
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_seal (&ctxS, rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad,
+ rfc9180_a2_pt, sizeof rfc9180_a2_pt,
+ test_ct, sizeof test_ct));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_open (&ctxR,
+ rfc9180_a2_aad,
+ sizeof rfc9180_a2_aad,
+ test_ct,
+ sizeof test_ct,
+ test_pt, sizeof test_pt));
+ GNUNET_assert (0 == memcmp (rfc9180_a2_pt, test_pt, sizeof test_pt));
+ }
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_seal (&ctxS,
+ rfc9180_a2_aad_seq255, sizeof
+ rfc9180_a2_aad_seq255,
+ rfc9180_a2_pt, sizeof rfc9180_a2_pt,
+ test_ct, sizeof test_ct));
+ print_bytes (rfc9180_a2_ct_seq255, sizeof test_ct, 0);
+ print_bytes (test_ct, sizeof test_ct, 0);
+ GNUNET_assert (0 == memcmp (rfc9180_a2_ct_seq255, test_ct, sizeof test_ct));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CRYPTO_hpke_open (&ctxR,
+ rfc9180_a2_aad_seq255,
+ sizeof rfc9180_a2_aad_seq255,
+ test_ct,
+ sizeof test_ct,
+ test_pt, sizeof test_pt));
+ GNUNET_assert (0 == memcmp (rfc9180_a2_pt, test_pt, sizeof test_pt));
+ return 0;
+}
+
+
+/* end of test_crypto_hpke.c */
diff --git a/src/lib/util/test_crypto_kem.c b/src/lib/util/test_crypto_kem.c
@@ -1,149 +0,0 @@
-/*
- 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/test_crypto_kem.c
- * @brief testcase for KEMs including RFC9180 DHKEM
- * @author Martin Schanzenbach
- */
-
-#include "gnunet_common.h"
-#include "platform.h"
-#include "gnunet_util_lib.h"
-
-static char *rfc9180_a1_skEm_str =
- "52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736";
-static char *rfc9180_a1_skRm_str =
- "4612c550263fc8ad58375df3f557aac531d26850903e55a9f23f21d8534e8ac8";
-static char *rfc9180_a1_enc_str =
- "37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431";
-static char *rfc9180_a1_shared_secret_str =
- "fe0e18c9f024ce43799ae393c7e8fe8fce9d218875e8227b0187c04e7d2ea1fc";
-
-
-
-
-static int
-parsehex (char *src, char *dst, size_t dstlen, int invert)
-{
- char *line = src;
- char *data = line;
- int off;
- int read_byte;
- int data_len = 0;
-
- while (sscanf (data, " %02x%n", &read_byte, &off) == 1)
- {
- if (invert)
- dst[dstlen - 1 - data_len++] = read_byte;
- else
- dst[data_len++] = read_byte;
- data += off;
- }
- return data_len;
-}
-
-
-static void
-print_bytes_ (void *buf,
- size_t buf_len,
- int fold,
- int in_be)
-{
- int i;
-
- for (i = 0; i < buf_len; i++)
- {
- if (0 != i)
- {
- if ((0 != fold) && (i % fold == 0))
- printf ("\n ");
- else
- printf (" ");
- }
- else
- {
- printf (" ");
- }
- if (in_be)
- printf ("%02x", ((unsigned char*) buf)[buf_len - 1 - i]);
- else
- printf ("%02x", ((unsigned char*) buf)[i]);
- }
- printf ("\n");
-}
-
-
-static void
-print_bytes (void *buf,
- size_t buf_len,
- int fold)
-{
- print_bytes_ (buf, buf_len, fold, 0);
-}
-
-int
-main (int argc, char *argv[])
-{
- struct GNUNET_CRYPTO_EcdhePrivateKey rfc9180_a1_skEm;
- struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a1_pkEm;
- struct GNUNET_CRYPTO_EcdhePrivateKey rfc9180_a1_skRm;
- struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a1_pkRm;
- struct GNUNET_CRYPTO_EcdhePublicKey rfc9180_a1_enc;
- struct GNUNET_CRYPTO_EcdhePublicKey enc;
- struct GNUNET_ShortHashCode rfc9180_a1_shared_secret;
- struct GNUNET_ShortHashCode shared_secret;
-
- GNUNET_log_setup ("test-crypto-kem", "WARNING", NULL);
-
- parsehex (rfc9180_a1_skEm_str,
- (char*)&rfc9180_a1_skEm.d,
- sizeof rfc9180_a1_skEm, 0);
- parsehex (rfc9180_a1_skRm_str,
- (char*)&rfc9180_a1_skRm.d,
- sizeof rfc9180_a1_skRm, 0);
- parsehex (rfc9180_a1_enc_str,
- (char*)&rfc9180_a1_enc,
- sizeof rfc9180_a1_enc, 0);
- parsehex (rfc9180_a1_shared_secret_str,
- (char*)&rfc9180_a1_shared_secret,
- sizeof rfc9180_a1_shared_secret, 0);
- GNUNET_CRYPTO_ecdhe_key_get_public(&rfc9180_a1_skEm, &rfc9180_a1_pkEm);
- GNUNET_CRYPTO_ecdhe_key_get_public(&rfc9180_a1_skRm, &rfc9180_a1_pkRm);
- printf ("pkRm: ");
- print_bytes(&rfc9180_a1_pkRm, sizeof rfc9180_a1_pkRm, 0);
- printf ("\n");
- printf ("pkEm: ");
- print_bytes(&rfc9180_a1_pkEm, sizeof rfc9180_a1_pkEm, 0);
- printf ("\n");
- GNUNET_CRYPTO_kem_encaps_norand(&rfc9180_a1_pkRm, &enc, &rfc9180_a1_skEm, &shared_secret);
- GNUNET_assert (0 == GNUNET_memcmp(&enc, &rfc9180_a1_enc));
- printf ("enc: ");
- print_bytes(&enc, sizeof enc, 0);
- printf ("\n");
- printf ("shared_secret: ");
- print_bytes(&shared_secret, sizeof shared_secret, 0);
- GNUNET_assert (0 == GNUNET_memcmp(&shared_secret, &rfc9180_a1_shared_secret));
- printf ("\n");
- return 0;
-}
-
-
-/* end of test_crypto_kem.c */
diff --git a/src/service/transport/gnunet-communicator-tcp.c b/src/service/transport/gnunet-communicator-tcp.c
@@ -301,7 +301,7 @@ struct TCPRekey
/**
* New ephemeral key.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral;
/**
* Sender's signature of type #GNUNET_SIGNATURE_PURPOSE_COMMUNICATOR_TCP_REKEY
@@ -339,7 +339,7 @@ struct TcpRekeySignature
/**
* Ephemeral key used by the @e sender.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral;
/**
* Monotonic time of @e sender, to possibly help detect replay attacks
@@ -1364,7 +1364,7 @@ setup_in_cipher_elligator (
* @param[in,out] queue queue to initialize decryption cipher for
*/
static void
-setup_in_cipher (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral,
+setup_in_cipher (const struct GNUNET_CRYPTO_HpkeEncapsulation *ephemeral,
struct Queue *queue)
{
struct GNUNET_ShortHashCode k;
@@ -1406,7 +1406,8 @@ do_rekey (struct Queue *queue, const struct TCPRekey *rekey)
thp.ephemeral = rekey->ephemeral;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"ephemeral %s\n",
- GNUNET_e2s (&thp.ephemeral));
+ GNUNET_e2s ((struct GNUNET_CRYPTO_EcdhePublicKey*) &thp.ephemeral)
+ );
thp.monotonic_time = rekey->monotonic_time;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"time %s\n",
@@ -1619,7 +1620,8 @@ inject_rekey (struct Queue *queue)
thp.ephemeral = rekey.ephemeral;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"ephemeral %s\n",
- GNUNET_e2s (&thp.ephemeral));
+ GNUNET_e2s ((struct GNUNET_CRYPTO_EcdhePublicKey*) &thp.ephemeral)
+ );
thp.monotonic_time = rekey.monotonic_time;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"time %s\n",
diff --git a/src/service/transport/gnunet-communicator-udp.c b/src/service/transport/gnunet-communicator-udp.c
@@ -350,7 +350,7 @@ struct UDPRekey
/**
* Ephemeral key to rekey with.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral;
};
GNUNET_NETWORK_STRUCT_END
@@ -1347,7 +1347,7 @@ try_decrypt (const struct SharedSecret *ss,
* @return new shared secret
*/
static struct SharedSecret *
-setup_shared_secret_dec (const struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral)
+setup_shared_secret_dec (const struct GNUNET_CRYPTO_HpkeEncapsulation *ephemeral)
{
struct SharedSecret *ss;
@@ -1387,7 +1387,7 @@ setup_initial_shared_secret_dec (const struct
* @return new shared secret
*/
static struct SharedSecret *
-setup_shared_secret_ephemeral (struct GNUNET_CRYPTO_EcdhePublicKey *ephemeral,
+setup_shared_secret_ephemeral (struct GNUNET_CRYPTO_HpkeEncapsulation *ephemeral,
struct ReceiverAddress *receiver)
{
struct SharedSecret *ss;
diff --git a/src/service/transport/gnunet-service-transport.c b/src/service/transport/gnunet-service-transport.c
@@ -404,7 +404,7 @@ struct EphemeralConfirmationPS
* Ephemeral key setup by the sender for @e target, used
* to encrypt the payload.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral_key;
};
@@ -788,7 +788,7 @@ struct TransportDVBoxMessage
* Ephemeral key setup by the sender for target, used to encrypt the
* payload. Intermediaries must not change this value.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral_key;
/**
* We use an IV here as the @e ephemeral_key is re-used for
@@ -1804,7 +1804,7 @@ struct DistanceVector
/**
* Our ephemeral key.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey ephemeral_key;
+ struct GNUNET_CRYPTO_HpkeEncapsulation ephemeral_key;
/**
* Master secret for the setup of the Key material for the backchannel.
@@ -2781,7 +2781,7 @@ struct Backtalker
/**
* Last (valid) ephemeral key received from this sender.
*/
- struct GNUNET_CRYPTO_EcdhePublicKey last_ephemeral;
+ struct GNUNET_CRYPTO_HpkeEncapsulation last_ephemeral;
/**
* Task associated with this backtalker. Can be for timeout,