gnunet

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

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:
Msrc/include/gnunet_crypto_lib.h | 182++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Msrc/lib/util/Makefile.am | 10+++++-----
Asrc/lib/util/crypto_hpke.c | 768+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/util/crypto_kem.c | 503-------------------------------------------------------------------------------
Msrc/lib/util/meson.build | 8++++----
Asrc/lib/util/test_crypto_hpke.c | 287+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dsrc/lib/util/test_crypto_kem.c | 149-------------------------------------------------------------------------------
Msrc/service/transport/gnunet-communicator-tcp.c | 12+++++++-----
Msrc/service/transport/gnunet-communicator-udp.c | 6+++---
Msrc/service/transport/gnunet-service-transport.c | 8++++----
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,