libgnutls_la_LIBADD += nettle/libcrypto.la
endif
+if ENABLE_LIBOQS
+libgnutls_la_LIBADD += liboqs/libcrypto.la
+endif
+
if HAVE_LD_OUTPUT_DEF
libgnutls_la_LDFLAGS += -Wl,--output-def,libgnutls-$(DLL_VERSION).def
libgnutls-$(DLL_VERSION).def: libgnutls.la
.q_bits = &gnutls_ffdhe_8192_key_bits,
.pk = GNUTLS_PK_DH,
.tls_id = 0x104 },
+#endif
+#ifdef HAVE_LIBOQS
+ { .name = "X25519-KYBER768",
+ .id = GNUTLS_GROUP_EXP_X25519_KYBER768,
+ .curve = GNUTLS_ECC_CURVE_X25519,
+ .pk = GNUTLS_PK_ECDH_X25519,
+ .pk2 = GNUTLS_PK_EXP_KYBER768,
+ .tls_id = 0x6399 },
#endif
{ 0, 0, 0 }
};
.oid = ECDH_X448_OID,
.id = GNUTLS_PK_ECDH_X448,
.curve = GNUTLS_ECC_CURVE_X448 },
+#ifdef HAVE_LIBOQS
+ { .name = "KYBER768",
+ .oid = NULL,
+ .id = GNUTLS_PK_EXP_KYBER768,
+ .curve = GNUTLS_ECC_CURVE_INVALID },
+#endif
{ .name = "UNKNOWN",
.oid = NULL,
.id = GNUTLS_PK_UNKNOWN,
const gnutls_pk_params_st *pub,
const gnutls_datum_t *nonce, unsigned int flags);
+ int (*encaps)(gnutls_pk_algorithm_t, gnutls_datum_t *ciphertext,
+ gnutls_datum_t *shared_secret, const gnutls_datum_t *pub);
+
+ int (*decaps)(gnutls_pk_algorithm_t, gnutls_datum_t *shared_secret,
+ const gnutls_datum_t *ciphertext,
+ const gnutls_datum_t *priv);
+
int (*curve_exists)(gnutls_ecc_curve_t); /* true/false */
int (*pk_exists)(gnutls_pk_algorithm_t); /* true/false */
int (*sign_exists)(gnutls_sign_algorithm_t); /* true/false */
} else if (group->pk == GNUTLS_PK_ECDH_X25519 ||
group->pk == GNUTLS_PK_ECDH_X448) {
+ unsigned int length_pos;
+
gnutls_pk_params_release(&session->key.kshare.ecdhx_params);
gnutls_pk_params_init(&session->key.kshare.ecdhx_params);
if (ret < 0)
return gnutls_assert_val(ret);
+ length_pos = extdata->length;
+
ret = _gnutls_buffer_append_data_prefix(
extdata, 16,
session->key.kshare.ecdhx_params.raw_pub.data,
session->key.kshare.ecdhx_params.algo = group->pk;
session->key.kshare.ecdhx_params.curve = group->curve;
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ gnutls_pk_params_release(
+ &session->key.kshare.kem_params);
+ gnutls_pk_params_init(&session->key.kshare.kem_params);
+
+ ret = _gnutls_pk_generate_keys(
+ group->pk2, 0, &session->key.kshare.kem_params,
+ 1);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ ret = _gnutls_buffer_append_data(
+ extdata,
+ session->key.kshare.kem_params.raw_pub.data,
+ session->key.kshare.kem_params.raw_pub.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+
+ /* copy actual length */
+ _gnutls_write_uint16(extdata->length - length_pos - 2,
+ &extdata->data[length_pos]);
+ }
+
ret = 0;
} else if (group->pk == GNUTLS_PK_DH) {
} else if (group->pk == GNUTLS_PK_ECDH_X25519 ||
group->pk == GNUTLS_PK_ECDH_X448) {
+ unsigned int length_pos;
+
+ length_pos = extdata->length;
+
ret = _gnutls_buffer_append_data_prefix(
extdata, 16,
session->key.kshare.ecdhx_params.raw_pub.data,
if (ret < 0)
return gnutls_assert_val(ret);
- ret = 0;
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ ret = _gnutls_buffer_append_data(
+ extdata,
+ session->key.kshare.kem_params.raw_pub.data,
+ session->key.kshare.kem_params.raw_pub.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto cleanup;
+ }
+ /* copy actual length */
+ _gnutls_write_uint16(extdata->length - length_pos - 2,
+ &extdata->data[length_pos]);
+ }
+
+ ret = 0;
} else if (group->pk == GNUTLS_PK_DH) {
ret = _gnutls_buffer_append_prefix(extdata, 16,
group->prime->size);
curve = _gnutls_ecc_curve_get_params(group->curve);
- if (curve->size != data_size)
- return gnutls_assert_val(
- GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ if (curve->size > data_size)
+ return gnutls_assert_val(
+ GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ } else {
+ if (curve->size != data_size)
+ return gnutls_assert_val(
+ GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
/* generate our key */
ret = _gnutls_pk_generate_keys(
pub.curve = curve->id;
pub.raw_pub.data = (void *)data;
- pub.raw_pub.size = data_size;
+ pub.raw_pub.size = curve->size;
/* We don't mask the MSB in the final byte as required
* by RFC7748. This will be done internally by nettle 3.3 or later.
return gnutls_assert_val(ret);
}
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ gnutls_datum_t key;
+ gnutls_datum_t peer_pub;
+
+ gnutls_pk_params_release(
+ &session->key.kshare.kem_params);
+ gnutls_pk_params_init(&session->key.kshare.kem_params);
+
+ /* generate our key */
+ ret = _gnutls_pk_generate_keys(
+ group->pk2, 0, &session->key.kshare.kem_params,
+ 1);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ /* server's public key is unused, but the raw_pub field
+ * is used to store ciphertext */
+ gnutls_free(
+ session->key.kshare.kem_params.raw_pub.data);
+
+ peer_pub.data = (uint8_t *)data + curve->size;
+ peer_pub.size = data_size - curve->size;
+
+ ret = _gnutls_pk_encaps(
+ group->pk2,
+ &session->key.kshare.kem_params.raw_pub, &key,
+ &peer_pub);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->key.key.data = gnutls_realloc_fast(
+ session->key.key.data,
+ session->key.key.size + key.size);
+ if (!session->key.key.data) {
+ _gnutls_free_datum(&key);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ memcpy(session->key.key.data + session->key.key.size,
+ key.data, key.size);
+ session->key.key.size += key.size;
+ _gnutls_free_datum(&key);
+ }
+
ret = 0;
} else if (group->pk == GNUTLS_PK_DH) {
return gnutls_assert_val(
GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
- if (curve->size != data_size)
- return gnutls_assert_val(
- GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ if (curve->size > data_size)
+ return gnutls_assert_val(
+ GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ } else {
+ if (curve->size != data_size)
+ return gnutls_assert_val(
+ GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ }
/* read the public key and generate shared */
gnutls_pk_params_init(&pub);
return gnutls_assert_val(ret);
}
+ if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+ gnutls_datum_t key;
+ gnutls_datum_t ciphertext;
+
+ ciphertext.data = (uint8_t *)data + curve->size;
+ ciphertext.size = data_size - curve->size;
+
+ ret = _gnutls_pk_decaps(
+ group->pk2, &key, &ciphertext,
+ &session->key.kshare.kem_params.raw_priv);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ session->key.key.data = gnutls_realloc_fast(
+ session->key.key.data,
+ session->key.key.size + key.size);
+ if (!session->key.key.data) {
+ _gnutls_free_datum(&key);
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+
+ memcpy(session->key.key.data + session->key.key.size,
+ key.data, key.size);
+ session->key.key.size += key.size;
+ _gnutls_free_datum(&key);
+ }
+
ret = 0;
} else if (group->pk == GNUTLS_PK_DH) {
gnutls_pk_params_st ecdh_params;
gnutls_pk_params_st ecdhx_params;
gnutls_pk_params_st dh_params;
+ gnutls_pk_params_st kem_params;
} kshare;
/* The union contents depend on the negotiated protocol.
const unsigned *q_bits;
gnutls_ecc_curve_t curve;
gnutls_pk_algorithm_t pk;
+ gnutls_pk_algorithm_t pk2;
unsigned tls_id; /* The RFC4492 namedCurve ID or TLS 1.3 group ID */
} gnutls_group_entry_st;
GNUTLS_PK_ECDH_X448 = 11,
GNUTLS_PK_EDDSA_ED448 = 12,
GNUTLS_PK_RSA_OAEP = 13,
- GNUTLS_PK_MAX = GNUTLS_PK_RSA_OAEP
+ GNUTLS_PK_MAX = GNUTLS_PK_RSA_OAEP,
+
+ /* Experimental algorithms */
+ GNUTLS_PK_EXP_MIN = 256,
+ GNUTLS_PK_EXP_KYBER768 = GNUTLS_PK_EXP_MIN,
+ GNUTLS_PK_EXP_MAX = GNUTLS_PK_EXP_KYBER768
} gnutls_pk_algorithm_t;
const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm);
GNUTLS_GROUP_FFDHE8192,
GNUTLS_GROUP_FFDHE6144,
GNUTLS_GROUP_MAX = GNUTLS_GROUP_FFDHE6144,
+
+ /* Experimental algorithms */
+ GNUTLS_GROUP_EXP_MIN = 512,
+ GNUTLS_GROUP_EXP_X25519_KYBER768 = GNUTLS_GROUP_EXP_MIN,
+ GNUTLS_GROUP_EXP_MAX = GNUTLS_GROUP_EXP_X25519_KYBER768
} gnutls_group_t;
/* macros to allow specifying a specific curve in gnutls_privkey_generate()
AM_CPPFLAGS += -I$(srcdir)/../minitasn1
endif
+if ENABLE_DLOPEN
+AM_CPPFLAGS += $(LIBOQS_CFLAGS) -DGNUTLS_OQS_ENABLE_DLOPEN=1
+endif
+
noinst_LTLIBRARIES = libcrypto.la
libcrypto_la_SOURCES = pk.c mpi.c mac.c cipher.c init.c \
#include "gnettle.h"
#include "fips.h"
#include "dh.h"
+#ifdef HAVE_LIBOQS
+#include "dlwrap/oqs.h"
+#endif
static inline const struct ecc_curve *get_supported_nist_curve(int curve);
static inline const struct ecc_curve *get_supported_gost_curve(int curve);
return ret;
}
+static int _wrap_nettle_pk_encaps(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t *ciphertext,
+ gnutls_datum_t *shared_secret,
+ const gnutls_datum_t *pub)
+{
+ int ret;
+
+ switch (algo) {
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768: {
+ OQS_KEM *kem = NULL;
+ OQS_STATUS rc;
+
+ kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+ if (kem == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ ciphertext->data = gnutls_malloc(kem->length_ciphertext);
+ if (ciphertext->data == NULL) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+ ciphertext->size = kem->length_ciphertext;
+
+ shared_secret->data = gnutls_malloc(kem->length_shared_secret);
+ if (shared_secret->data == NULL) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+ shared_secret->size = kem->length_shared_secret;
+
+ rc = GNUTLS_OQS_FUNC(OQS_KEM_encaps)(
+ kem, ciphertext->data, shared_secret->data, pub->data);
+ if (rc != OQS_SUCCESS) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = 0;
+ } break;
+#endif
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+ goto cleanup;
+ }
+
+cleanup:
+ if (ret < 0) {
+ gnutls_free(ciphertext->data);
+ gnutls_free(shared_secret->data);
+ }
+ return ret;
+}
+
+static int _wrap_nettle_pk_decaps(gnutls_pk_algorithm_t algo,
+ gnutls_datum_t *shared_secret,
+ const gnutls_datum_t *ciphertext,
+ const gnutls_datum_t *priv)
+{
+ int ret;
+
+ switch (algo) {
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768: {
+ OQS_KEM *kem = NULL;
+ OQS_STATUS rc;
+
+ kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+ if (kem == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+ shared_secret->data = gnutls_malloc(kem->length_shared_secret);
+ if (shared_secret->data == NULL) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+ shared_secret->size = kem->length_shared_secret;
+
+ rc = GNUTLS_OQS_FUNC(OQS_KEM_decaps)(
+ kem, shared_secret->data, ciphertext->data, priv->data);
+ if (rc != OQS_SUCCESS) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ goto cleanup;
+ }
+
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = 0;
+ } break;
+#endif
+ default:
+ ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+ goto cleanup;
+ }
+cleanup:
+ if (ret < 0)
+ gnutls_free(shared_secret->data);
+ return ret;
+}
+
/* This wraps nettle_rsa_encrypt so it returns ciphertext as a byte
* array instead of a mpz_t value. Returns 1 on success; 0 otherwise.
*/
case GNUTLS_PK_RSA_PSS:
case GNUTLS_PK_RSA_OAEP:
case GNUTLS_PK_EDDSA_ED25519:
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768:
+#endif
#if ENABLE_GOST
case GNUTLS_PK_GOST_01:
case GNUTLS_PK_GOST_12_256:
}
case GNUTLS_PK_ECDH_X25519:
case GNUTLS_PK_ECDH_X448:
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768:
+#endif
ret = 0;
goto cleanup;
default:
goto cleanup;
break;
}
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768: {
+ OQS_KEM *kem = NULL;
+ OQS_STATUS rc;
+
+ not_approved = true;
+
+ kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+ if (kem == NULL) {
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ params->raw_priv.size = kem->length_secret_key;
+ params->raw_priv.data = gnutls_malloc(params->raw_priv.size);
+ if (params->raw_priv.data == NULL) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ params->raw_pub.size = kem->length_public_key;
+ params->raw_pub.data = gnutls_malloc(params->raw_pub.size);
+ if (params->raw_pub.data == NULL) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ goto cleanup;
+ }
+
+ rc = GNUTLS_OQS_FUNC(OQS_KEM_keypair)(kem, params->raw_pub.data,
+ params->raw_priv.data);
+ if (rc != OQS_SUCCESS) {
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+ ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+ goto cleanup;
+ }
+
+ GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+
+ ret = 0;
+ break;
+ }
+#endif
default:
gnutls_assert();
return GNUTLS_E_INVALID_REQUEST;
ret = 0;
break;
}
+#ifdef HAVE_LIBOQS
+ case GNUTLS_PK_EXP_KYBER768:
+ ret = 0;
+ break;
+#endif
#if ENABLE_GOST
case GNUTLS_PK_GOST_01:
case GNUTLS_PK_GOST_12_256:
.generate_keys = wrap_nettle_pk_generate_keys,
.pk_fixup_private_params = wrap_nettle_pk_fixup,
.derive = _wrap_nettle_pk_derive,
+ .encaps = _wrap_nettle_pk_encaps,
+ .decaps = _wrap_nettle_pk_decaps,
.curve_exists = _wrap_nettle_pk_curve_exists,
.pk_exists = _wrap_nettle_pk_exists,
.sign_exists = _wrap_nettle_pk_sign_exists
_gnutls_pk_ops.derive(algo, out, pub, priv, nonce, 0)
#define _gnutls_pk_derive_tls13(algo, out, pub, priv) \
_gnutls_pk_ops.derive(algo, out, pub, priv, NULL, PK_DERIVE_TLS13)
+#define _gnutls_pk_encaps(algo, ciphertext, shared_secret, pub) \
+ _gnutls_pk_ops.encaps(algo, ciphertext, shared_secret, pub)
+#define _gnutls_pk_decaps(algo, shared_secret, ciphertext, priv) \
+ _gnutls_pk_ops.decaps(algo, shared_secret, ciphertext, priv)
#define _gnutls_pk_generate_keys(algo, bits, params, temporal) \
_gnutls_pk_ops.generate_keys(algo, bits, params, temporal)
#define _gnutls_pk_generate_params(algo, bits, priv) \
gnutls_pk_params_release(&session->key.kshare.ecdhx_params);
gnutls_pk_params_release(&session->key.kshare.ecdh_params);
gnutls_pk_params_release(&session->key.kshare.dh_params);
+ gnutls_pk_params_release(&session->key.kshare.kem_params);
if (!vers->tls13_sem && session->key.binders[0].prf == NULL) {
gnutls_pk_params_release(&session->key.proto.tls12.ecdh.params);
endif
+if ENABLE_LIBOQS
+dist_check_SCRIPTS += pqc-hybrid-kx.sh
+endif
+
cpptests =
if ENABLE_CXX
if HAVE_CMOCKA
--- /dev/null
+#!/bin/sh
+
+# Copyright (C) 2022 Red Hat, Inc.
+#
+# Author: Daiki Ueno
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS 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
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GnuTLS. If not, see <https://www.gnu.org/licenses/>.
+
+: ${srcdir=.}
+: ${SERV=../src/gnutls-serv${EXEEXT}}
+: ${CLI=../src/gnutls-cli${EXEEXT}}
+
+if ! test -x "${SERV}"; then
+ exit 77
+fi
+
+if ! test -x "${CLI}"; then
+ exit 77
+fi
+
+. "${srcdir}/scripts/common.sh"
+testdir=`create_testdir pqc-hybrid-kx`
+
+KEY="$srcdir/../doc/credentials/x509/key-ed25519.pem"
+CERT="$srcdir/../doc/credentials/x509/cert-ed25519.pem"
+CACERT="$srcdir/../doc/credentials/x509/ca.pem"
+
+eval "${GETPORT}"
+launch_server --echo --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509keyfile="$KEY" --x509certfile="$CERT"
+PID=$!
+wait_server ${PID}
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
+
+kill ${PID}
+wait
+
+grep -- '- Description: (TLS1.3-X.509)-(ECDHE-X25519-KYBER768)-(EdDSA-Ed25519)-(AES-256-GCM)' "$testdir/cli.log" || { echo "unexpected handshake description"; exit 1; }
+
+rm -rf "$testdir"
+exit 0