const net_flags_t flags,
const tls_params_t *tls_params,
const https_params_t *https_params,
+ const quic_params_t *quic_params,
net_t *net)
{
if (remote == NULL || net == NULL) {
}
} else
#endif //LIBNGHTTP2
+#ifdef LIBNGTCP2
+ if (quic_params != NULL && quic_params->enable) {
+ ret = tls_ctx_init(&net->tls, tls_params,
+ GNUTLS_NONBLOCK | GNUTLS_ENABLE_EARLY_DATA |
+ GNUTLS_NO_END_OF_EARLY_DATA, net->wait,
+ quic_alpn, 4, QUIC_PRIORITY); // TODO will be 1 on release
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ ret = quic_ctx_init(&net->quic, &net->tls, quic_params);
+ if (ret != KNOT_EOK) {
+ net_clean(net);
+ return ret;
+ }
+ } else
+#endif //LIBNGTCP2
{
ret = tls_ctx_init(&net->tls, tls_params,
GNUTLS_NONBLOCK, net->wait,
-/* Copyright (C) 2020 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
+/* Copyright (C) 2022 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "utils/common/https.h"
#include "utils/common/params.h"
+#include "utils/common/quic.h"
#include "utils/common/tls.h"
/*! \brief Structure containing server information. */
/*! HTTPS context. */
https_ctx_t https;
#endif
+#ifdef LIBNGTCP2
+ /*! QUIC context. */
+ quic_ctx_t quic;
+#endif
} net_t;
/*!
const net_flags_t flags,
const tls_params_t *tls_params,
const https_params_t *https_params,
+ const quic_params_t *quic_params,
net_t *net);
/*!
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#include <assert.h>
#include <stddef.h>
+#include <gnutls/crypto.h>
+
+#include "libdnssec/error.h"
+#include "libdnssec/random.h"
+
#include "libknot/errcode.h"
#include "utils/common/quic.h"
params->enable = false;
}
+
+#ifdef LIBNGTCP2
+
+const gnutls_datum_t quic_alpn[] = {
+ {
+ .data = (unsigned char *)"doq",
+ .size = 3
+ },{
+ .data = (unsigned char *)"doq-i12",
+ .size = 7
+ },{
+ .data = (unsigned char *)"doq-i11",
+ .size = 7
+ },{
+ .data = (unsigned char *)"doq-i03",
+ .size = 7
+ }
+};
+
+uint64_t quic_timestamp(void)
+{
+ struct timespec ts;
+ if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
+ return 0;
+ }
+
+ return (uint64_t)ts.tv_sec * NGTCP2_SECONDS + (uint64_t)ts.tv_nsec;
+}
+
+int quic_generate_secret(uint8_t *buf, size_t buflen)
+{
+ assert(buf != NULL && buflen > 0 && buflen <= 32);
+ uint8_t rand[16], hash[32];
+ int ret = dnssec_random_buffer(rand, sizeof(rand));
+ if (ret != DNSSEC_EOK) {
+ return ret;
+ }
+ ret = gnutls_hash_fast(GNUTLS_DIG_SHA256, rand, sizeof(rand), hash);
+ if (ret != 0) {
+ return ret;
+ }
+ memcpy(buf, hash, buflen);
+ return KNOT_EOK;
+}
+
+
+static int verify_certificate(gnutls_session_t session)
+{
+ quic_ctx_t *ctx = gnutls_session_get_ptr(session);
+ return tls_certificate_verification(ctx->tls);
+}
+
+int quic_ctx_init(quic_ctx_t *ctx, tls_ctx_t *tls_ctx, const quic_params_t *params)
+{
+ if (ctx == NULL || tls_ctx == NULL || params == NULL) {
+ return KNOT_EINVAL;
+ }
+
+ ctx->params = *params;
+ ctx->tls = tls_ctx;
+ ctx->state = OPENING;
+ ctx->stream.id = -1;
+ ctx->timestamp = quic_timestamp();
+ if (quic_generate_secret(ctx->secret, sizeof(ctx->secret)) != KNOT_EOK) {
+ tls_ctx_deinit(ctx->tls);
+ return KNOT_ENOMEM;
+ }
+
+ gnutls_certificate_set_verify_function(tls_ctx->credentials,
+ verify_certificate);
+
+ return KNOT_EOK;
+}
+
+
+#endif
void quic_params_clean(quic_params_t *params);
+#ifdef LIBNGTCP2
+
+#include <ngtcp2/ngtcp2.h>
+
+#include "utils/common/tls.h"
+
+#define QUIC_DEFAULT_VERSION "-VERS-ALL:+VERS-TLS1.3"
+#define QUIC_DEFAULT_CIPHERS "-CIPHER-ALL:+AES-128-GCM:+AES-256-GCM:+CHACHA20-POLY1305:+AES-128-CCM"
+#define QUIC_DEFAULT_GROUPS "-GROUP-ALL:+GROUP-SECP256R1:+GROUP-X25519:+GROUP-SECP384R1:+GROUP-SECP521R1"
+#define QUIC_PRIORITY "%DISABLE_TLS13_COMPAT_MODE:NORMAL:"QUIC_DEFAULT_VERSION":"QUIC_DEFAULT_CIPHERS":"QUIC_DEFAULT_GROUPS
+
+
+typedef enum {
+ OPENING,
+ CONNECTED,
+ CLOSING
+} quic_state_t;
+
+typedef struct {
+ // Parameters
+ quic_params_t params;
+
+ // Context
+ ngtcp2_settings settings;
+ struct {
+ int64_t id;
+ uint64_t out_ack;
+ struct iovec in_buffer;
+ struct iovec *in_parsed;
+ size_t in_parsed_size;
+ size_t in_parsed_total;
+ size_t in_parsed_it;
+ } stream;
+ ngtcp2_connection_close_error last_err;
+ uint8_t secret[32];
+ tls_ctx_t *tls;
+ ngtcp2_conn *conn;
+ ngtcp2_pkt_info pi;
+ quic_state_t state;
+ uint64_t idle_ts;
+} quic_ctx_t;
+
+extern const gnutls_datum_t quic_alpn[];
+
+uint64_t quic_timestamp(void);
+
+int quic_generate_secret(uint8_t *buf, size_t buflen);
+
+int quic_ctx_init(quic_ctx_t *ctx, tls_ctx_t *tls_ctx, const quic_params_t *params);
+
+#endif //LIBNGTCP2
!EMPTY_LIST(params->ca_files) || params->ocsp_stapling > 0;
}
-static int verify_certificate(gnutls_session_t session)
+int tls_certificate_verification(tls_ctx_t *ctx)
{
- tls_ctx_t *ctx = gnutls_session_get_ptr(session);
-
+ gnutls_session_t session = ctx->session;
// Check for pinned certificates and print certificate hierarchy.
int ret = check_certificates(session, &ctx->params->pins);
if (ret != GNUTLS_E_SUCCESS) {
return GNUTLS_E_SUCCESS;
}
+static int verify_certificate(gnutls_session_t session)
+{
+ tls_ctx_t *ctx = gnutls_session_get_ptr(session);
+ return tls_certificate_verification(ctx);
+}
+
int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params,
unsigned int flags, int wait, const gnutls_datum_t *alpn,
size_t alpn_size, const char *priority)
}
}
- // gnutls_certificate_set_verify_function(ctx->credentials, verify_certificate);
+ gnutls_certificate_set_verify_function(ctx->credentials, verify_certificate);
// Setup client keypair if specified. Both key and cert files must be provided.
if (params->keyfile != NULL && params->certfile != NULL) {
int tls_params_copy(tls_params_t *dst, const tls_params_t *src);
void tls_params_clean(tls_params_t *params);
+int tls_certificate_verification(tls_ctx_t *ctx);
+
int tls_ctx_init(tls_ctx_t *ctx, const tls_params_t *params,
unsigned int flags, int wait, const gnutls_datum_t *alpn,
size_t alpn_size, const char *priority);
for (size_t i = 0; i <= query->retries; i++) {
// Initialize network structure for current server.
ret = net_init(query->local, remote, iptype, socktype,
- query->wait, flags, &query->tls, &query->https, net);
+ query->wait, flags, &query->tls,
+ &query->https, &query->quic, net);
if (ret != KNOT_EOK) {
if (ret == KNOT_NET_EADDR) {
// Requested address family not available.
// Initialize network structure.
ret = net_init(query->local, remote, iptype, socktype, query->wait,
- flags, &query->tls, &query->https, net);
+ flags, &query->tls, &query->https, &query->quic, net);
if (ret != KNOT_EOK) {
sign_context_deinit(&sign_ctx);
knot_pkt_free(out_packet);
NET_FLAGS_NONE,
NULL,
NULL,
+ NULL,
&net);
if (ret != KNOT_EOK) {
return -1;