From ae813bfe3d5b54511417038ede8ddb057e30c892 Mon Sep 17 00:00:00 2001
From: Daiki Ueno <dueno@redhat.com>
Date: Wed, 25 Mar 2020 08:16:06 +0100
Subject: [PATCH] crypto: add GnuTLS backend

This adds a crypto backend based on GnuTLS.  While most of the
gnutls_* functions used in this backend are officially available in
upstream GnuTLS, the following functions are only available in the
'tmp-quic' branch, for ABI assurance reasons until the QUIC standard
is finalized:
- gnutls_handshake_write
- gnutls_quic_get_peer_transport_params

Signed-off-by: Daiki Ueno <dueno@redhat.com>
Signed-off-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
---
 CMakeLists.txt                                |  13 +
 Makefile.am                                   |   2 +-
 configure.ac                                  |  18 +
 crypto/CMakeLists.txt                         |  11 +-
 crypto/Makefile.am                            |  10 +-
 crypto/gnutls/.gitignore                      |   1 +
 crypto/gnutls/CMakeLists.txt                  |  62 ++++
 crypto/gnutls/Makefile.am                     |  39 +++
 crypto/gnutls/gnutls.c                        | 316 ++++++++++++++++++
 crypto/gnutls/libngtcp2_crypto_gnutls.pc.in   |  33 ++
 crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h |  30 ++
 11 files changed, 532 insertions(+), 3 deletions(-)
 create mode 100644 crypto/gnutls/.gitignore
 create mode 100644 crypto/gnutls/CMakeLists.txt
 create mode 100644 crypto/gnutls/Makefile.am
 create mode 100644 crypto/gnutls/gnutls.c
 create mode 100644 crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
 create mode 100644 crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h

diff --git a/CMakeLists.txt b/CMakeLists.txt
index d4efd0e2..a3c9d494 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -67,6 +67,7 @@ foreach(_build_type "Release" "MinSizeRel" "RelWithDebInfo")
 endforeach()
 
 
+find_package(GnuTLS 3.6.12)
 find_package(OpenSSL 1.1.1)
 find_package(Libev 4.11)
 find_package(Libnghttp3 0.0.0)
@@ -81,6 +82,7 @@ endif()
 set(HAVE_OPENSSL    ${OPENSSL_FOUND})
 if(OPENSSL_FOUND)
   set(OPENSSL_INCLUDE_DIRS  ${OPENSSL_INCLUDE_DIR})
+  set(HAVE_CRYPTO 1)
 else()
   set(OPENSSL_INCLUDE_DIRS  "")
   set(OPENSSL_LIBRARIES     "")
@@ -90,6 +92,16 @@ set(HAVE_LIBEV      ${LIBEV_FOUND})
 # libnghttp3 (for examples)
 set(HAVE_LIBNGHTTP3 ${LIBNGHTTP3_FOUND})
 
+# GnuTLS (for examples)
+set(HAVE_GNUTLS    ${GNUTLS_FOUND})
+if(GNUTLS_FOUND)
+  set(GNUTLS_INCLUDE_DIRS  ${GNUTLS_INCLUDE_DIR})
+  set(HAVE_CRYPTO 1)
+else()
+  set(GNUTLS_INCLUDE_DIRS  "")
+  set(GNUTLS_LIBRARIES     "")
+endif()
+
 # Checks for header files.
 include(CheckIncludeFile)
 check_include_file("arpa/inet.h"   HAVE_ARPA_INET_H)
@@ -280,4 +292,5 @@ message(STATUS "summary of build options:
       OpenSSL:        ${HAVE_OPENSSL} (LIBS='${OPENSSL_LIBRARIES}')
       Libev:          ${HAVE_LIBEV} (LIBS='${LIBEV_LIBRARIES}')
       Libnghttp3:     ${HAVE_LIBNGHTTP3} (LIBS='${LIBNGHTTP3_LIBRARIES}')
+      GnuTLS:         ${HAVE_GNUTLS} (LIBS='${GNUTLS_LIBRARIES}')
 ")
diff --git a/Makefile.am b/Makefile.am
index 2359589b..42bc1d8b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -22,7 +22,7 @@
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 SUBDIRS = lib tests
 
-if HAVE_OPENSSL
+if HAVE_CRYPTO
 SUBDIRS += crypto
 endif
 
diff --git a/configure.ac b/configure.ac
index dd9e7c3a..8701840f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,6 +49,10 @@ AC_SUBST(CRYPTO_OPENSSL_LT_CURRENT, 0)
 AC_SUBST(CRYPTO_OPENSSL_LT_REVISION, 0)
 AC_SUBST(CRYPTO_OPENSSL_LT_AGE, 0)
 
+AC_SUBST(CRYPTO_GNUTLS_LT_CURRENT, 0)
+AC_SUBST(CRYPTO_GNUTLS_LT_REVISION, 0)
+AC_SUBST(CRYPTO_GNUTLS_LT_AGE, 0)
+
 # from nghttp2
 major=`echo $PACKAGE_VERSION |cut -d. -f1 | sed -e "s/[^0-9]//g"`
 minor=`echo $PACKAGE_VERSION |cut -d. -f2 | sed -e "s/[^0-9]//g"`
@@ -144,6 +148,16 @@ if test "x${have_openssl}" = "xno"; then
 fi
 AM_CONDITIONAL([HAVE_OPENSSL], [ test "x${have_openssl}" = "xyes" ])
 
+# GnuTLS (for examples)
+PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.6.11],
+                  [have_gnutls=yes], [have_gnutls=no])
+if test "x${have_gnutls}" = "xno"; then
+  AC_MSG_NOTICE($GNUTLS_PKG_ERRORS)
+fi
+AM_CONDITIONAL([HAVE_GNUTLS], [ test "x${have_gnutls}" = "xyes" ])
+
+AM_CONDITIONAL([HAVE_CRYPTO], [ test "x${have_openssl}" = "xyes" || test "x${have_gnutls}" = "xyes" ])
+
 # libnghttp3 (for examples)
 PKG_CHECK_MODULES([LIBNGHTTP3], [libnghttp3 >= 0.0.0],
                   [have_libnghttp3=yes], [have_libnghttp3=no])
@@ -368,6 +382,8 @@ AC_CONFIG_FILES([
   crypto/openssl/Makefile
   crypto/openssl/libngtcp2_crypto_openssl.pc
   crypto/includes/Makefile
+  crypto/gnutls/Makefile
+  crypto/gnutls/libngtcp2_crypto_gnutls.pc
   third-party/Makefile
   examples/Makefile
 ])
@@ -399,6 +415,7 @@ AC_MSG_NOTICE([summary of build options:
       Static:         ${enable_static}
     Crypto helper libraries:
       libngtcp2_crypto_openssl: ${have_openssl}
+      libngtcp2_crypto_gnutls:  ${have_gnutls}
     Test:
       CUnit:          ${have_cunit} (CFLAGS='${CUNIT_CFLAGS}' LIBS='${CUNIT_LIBS}')
     Debug:
@@ -408,5 +425,6 @@ AC_MSG_NOTICE([summary of build options:
       Libev:          ${have_libev} (CFLAGS='${LIBEV_CFLAGS}' LIBS='${LIBEV_LIBS}')
       Libnghttp3:     ${have_libnghttp3} (CFLAGS='${LIBNGHTTP3_CFLAGS}' LIBS='${LIBNGHTTP3_LIBS}')
       Jemalloc:       ${have_jemalloc} (LIBS='${JEMALLOC_LIBS}')
+      GnuTLS:         ${have_gnutls} (CFLAGS='${GNUTLS_CFLAGS}' LIBS='${GNUTLS_LIBS}')
     Examples:         ${enable_examples}
 ])
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 0e705969..f916455e 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -21,9 +21,18 @@
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 
-if(OPENSSL_FOUND)
+if(HAVE_CRYPTO)
   add_subdirectory(includes)
+endif()
+
+if(OPENSSL_FOUND)
   add_subdirectory(openssl)
 else()
   message(WARNING "libngtcp2_crypto_openssl library is disabled due to lack of good OpenSSL")
 endif()
+
+if(GNUTLS_FOUND)
+  add_subdirectory(gnutls)
+else()
+  message(WARNING "libngtcp2_crypto_gnutls library is disabled due to lack of good GnuTLS")
+endif()
diff --git a/crypto/Makefile.am b/crypto/Makefile.am
index a3468643..fc7cb8f2 100644
--- a/crypto/Makefile.am
+++ b/crypto/Makefile.am
@@ -22,6 +22,14 @@
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 SUBDIRS =
 
+if HAVE_CRYPTO
+SUBDIRS += includes
+endif
+
 if HAVE_OPENSSL
-SUBDIRS += includes openssl
+SUBDIRS += openssl
+endif
+
+if HAVE_GNUTLS
+SUBDIRS += gnutls
 endif
diff --git a/crypto/gnutls/.gitignore b/crypto/gnutls/.gitignore
new file mode 100644
index 00000000..b11bf3d7
--- /dev/null
+++ b/crypto/gnutls/.gitignore
@@ -0,0 +1 @@
+/libngtcp2_crypto_gnutls.pc
diff --git a/crypto/gnutls/CMakeLists.txt b/crypto/gnutls/CMakeLists.txt
new file mode 100644
index 00000000..d78b3f2a
--- /dev/null
+++ b/crypto/gnutls/CMakeLists.txt
@@ -0,0 +1,62 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+add_definitions(-DBUILDING_NGTCP2)
+
+set(ngtcp2_crypto_gnutls_SOURCES
+  gnutls.c
+  ../shared.c
+)
+
+set(ngtcp2_crypto_gnutls_INCLUDE_DIRS
+  "${CMAKE_CURRENT_SOURCE_DIR}/../../lib/includes"
+  "${CMAKE_CURRENT_BINARY_DIR}/../../lib/includes"
+  "${CMAKE_CURRENT_SOURCE_DIR}/../../lib"
+  "${CMAKE_CURRENT_SOURCE_DIR}/../../crypto/includes"
+  "${CMAKE_CURRENT_BINARY_DIR}/../../crypto/includes"
+  "${GNUTLS_INCLUDE_DIRS}"
+)
+
+foreach(name libngtcp2_crypto_gnutls.pc)
+  configure_file("${name}.in" "${name}" @ONLY)
+endforeach()
+
+# Public shared library
+add_library(ngtcp2_crypto_gnutls ${ngtcp2_crypto_gnutls_SOURCES})
+set_target_properties(ngtcp2_crypto_gnutls PROPERTIES
+  COMPILE_FLAGS "${WARNCFLAGS}"
+  VERSION ${LT_VERSION} SOVERSION ${LT_SOVERSION}
+  C_VISIBILITY_PRESET hidden
+)
+
+target_include_directories(ngtcp2_crypto_gnutls PUBLIC
+  ${ngtcp2_crypto_gnutls_INCLUDE_DIRS})
+if(NOT BUILD_SHARED_LIBS)
+  target_compile_definitions(ngtcp2_crypto_gnutls PUBLIC "-DNGTCP2_STATICLIB")
+endif()
+
+install(TARGETS ngtcp2_crypto_gnutls
+  DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libngtcp2_crypto_gnutls.pc"
+  DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
diff --git a/crypto/gnutls/Makefile.am b/crypto/gnutls/Makefile.am
new file mode 100644
index 00000000..b2310ef5
--- /dev/null
+++ b/crypto/gnutls/Makefile.am
@@ -0,0 +1,39 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+AM_CFLAGS = $(WARNCFLAGS) $(DEBUGCFLAGS) $(EXTRACFLAG)
+AM_CPPFLAGS = -I$(top_srcdir)/lib/includes -I$(top_builddir)/lib/includes \
+	-I$(top_srcdir)/lib -DBUILDING_NGTCP2 \
+	-I$(top_srcdir)/crypto/includes -I$(top_builddir)/crypto/includes \
+	@GNUTLS_CFLAGS@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libngtcp2_crypto_gnutls.pc
+DISTCLEANFILES = $(pkgconfig_DATA)
+
+lib_LTLIBRARIES = libngtcp2_crypto_gnutls.la
+
+libngtcp2_crypto_gnutls_la_SOURCES = gnutls.c ../shared.c ../shared.h
+libngtcp2_crypto_gnutls_la_LDFLAGS = -no-undefined \
+	-version-info $(CRYPTO_GNUTLS_LT_CURRENT):$(CRYPTO_GNUTLS_LT_REVISION):$(CRYPTO_GNUTLS_LT_AGE) \
+	@JEMALLOC_LIBS@ @GNUTLS_LIBS@
+libngtcp2_crypto_gnutls_la_LIBADD = $(top_builddir)/lib/libngtcp2.la
diff --git a/crypto/gnutls/gnutls.c b/crypto/gnutls/gnutls.c
new file mode 100644
index 00000000..d28191b7
--- /dev/null
+++ b/crypto/gnutls/gnutls.c
@@ -0,0 +1,316 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <assert.h>
+
+#include <ngtcp2/ngtcp2_crypto.h>
+#include <ngtcp2/ngtcp2_crypto_gnutls.h>
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <string.h>
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_initial(ngtcp2_crypto_ctx *ctx) {
+  ctx->aead.native_handle = (void *)GNUTLS_CIPHER_AES_128_GCM;
+  ctx->md.native_handle = (void *)GNUTLS_DIG_SHA256;
+  ctx->hp.native_handle = (void *)GNUTLS_CIPHER_AES_128_CBC;
+  return ctx;
+}
+
+ngtcp2_crypto_aead *ngtcp2_crypto_aead_retry(ngtcp2_crypto_aead *aead) {
+  aead->native_handle = (void *)GNUTLS_CIPHER_AES_128_GCM;
+  return aead;
+}
+
+static gnutls_cipher_algorithm_t crypto_get_hp(gnutls_session_t session) {
+  switch (gnutls_cipher_get(session)) {
+  case GNUTLS_CIPHER_AES_128_GCM:
+  case GNUTLS_CIPHER_AES_128_CCM:
+    return GNUTLS_CIPHER_AES_128_CBC;
+  case GNUTLS_CIPHER_AES_256_GCM:
+  case GNUTLS_CIPHER_AES_256_CCM:
+    return GNUTLS_CIPHER_AES_256_CBC;
+  case GNUTLS_CIPHER_CHACHA20_POLY1305:
+    return GNUTLS_CIPHER_CHACHA20_32;
+  default:
+    return GNUTLS_CIPHER_UNKNOWN;
+  }
+}
+
+ngtcp2_crypto_ctx *ngtcp2_crypto_ctx_tls(ngtcp2_crypto_ctx *ctx,
+                                         void *tls_native_handle) {
+  gnutls_session_t session = tls_native_handle;
+  gnutls_cipher_algorithm_t cipher;
+  gnutls_digest_algorithm_t hash;
+  gnutls_cipher_algorithm_t hp_cipher;
+
+  cipher = gnutls_cipher_get(session);
+  if (cipher != GNUTLS_CIPHER_UNKNOWN || cipher != GNUTLS_CIPHER_NULL) {
+    ctx->aead.native_handle = (void *)cipher;
+  }
+
+  hash = gnutls_prf_hash_get(session);
+  if (hash != GNUTLS_DIG_UNKNOWN || hash != GNUTLS_DIG_NULL) {
+    ctx->md.native_handle = (void *)hash;
+  }
+
+  hp_cipher = crypto_get_hp(session);
+  if (hp_cipher != GNUTLS_CIPHER_UNKNOWN) {
+    ctx->hp.native_handle = (void *)hp_cipher;
+  }
+
+  return ctx;
+}
+
+size_t ngtcp2_crypto_md_hashlen(const ngtcp2_crypto_md *md) {
+  return gnutls_hash_get_len((gnutls_digest_algorithm_t)md->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_keylen(const ngtcp2_crypto_aead *aead) {
+  return gnutls_cipher_get_key_size((gnutls_cipher_algorithm_t)aead->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_noncelen(const ngtcp2_crypto_aead *aead) {
+  return gnutls_cipher_get_iv_size((gnutls_cipher_algorithm_t)aead->native_handle);
+}
+
+size_t ngtcp2_crypto_aead_taglen(const ngtcp2_crypto_aead *aead) {
+  return gnutls_cipher_get_tag_size((gnutls_cipher_algorithm_t)aead->native_handle);
+}
+
+int ngtcp2_crypto_hkdf_extract(uint8_t *dest, const ngtcp2_crypto_md *md,
+                               const uint8_t *secret, size_t secretlen,
+                               const uint8_t *salt, size_t saltlen) {
+  gnutls_mac_algorithm_t prf = (gnutls_mac_algorithm_t)md->native_handle;
+  gnutls_datum_t _secret = {(void*)secret, secretlen};
+  gnutls_datum_t _salt = {(void*)salt, saltlen};
+
+  if (gnutls_hkdf_extract(prf, &_secret, &_salt, dest) != 0) {
+    return -1;
+  }
+
+  return 0;
+}
+
+int ngtcp2_crypto_hkdf_expand(uint8_t *dest, size_t destlen,
+                              const ngtcp2_crypto_md *md, const uint8_t *secret,
+                              size_t secretlen, const uint8_t *info,
+                              size_t infolen) {
+  gnutls_mac_algorithm_t prf = (gnutls_mac_algorithm_t)md->native_handle;
+  gnutls_datum_t _secret = {(void*)secret, secretlen};
+  gnutls_datum_t _info = {(void*)info, infolen};
+
+  if (gnutls_hkdf_expand(prf, &_secret, &_info, dest, destlen) != 0) {
+    return -1;
+  }
+
+  return 0;
+}
+
+int ngtcp2_crypto_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+                          const uint8_t *plaintext, size_t plaintextlen,
+                          const uint8_t *key, const uint8_t *nonce,
+                          size_t noncelen, const uint8_t *ad, size_t adlen) {
+  gnutls_cipher_algorithm_t cipher = (gnutls_cipher_algorithm_t)aead->native_handle;
+  gnutls_aead_cipher_hd_t hd = NULL;
+  gnutls_datum_t _key;
+  size_t taglen = ngtcp2_crypto_aead_taglen(aead);
+  size_t ciphertextlen = plaintextlen + taglen;
+  int rv = 0;
+
+  _key.data = (void *)key;
+  _key.size = ngtcp2_crypto_aead_keylen(aead);
+
+  if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0 ||
+      gnutls_aead_cipher_encrypt(hd,
+				 nonce, noncelen,
+				 ad, adlen, taglen,
+				 plaintext, plaintextlen,
+				 dest, &ciphertextlen) != 0) {
+    rv = -1;
+  }
+
+  gnutls_aead_cipher_deinit(hd);
+
+  return rv;
+}
+
+int ngtcp2_crypto_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead,
+                          const uint8_t *ciphertext, size_t ciphertextlen,
+                          const uint8_t *key, const uint8_t *nonce,
+                          size_t noncelen, const uint8_t *ad, size_t adlen) {
+  gnutls_cipher_algorithm_t cipher =
+    (gnutls_cipher_algorithm_t)aead->native_handle;
+  size_t taglen = gnutls_cipher_get_tag_size(cipher);
+  gnutls_aead_cipher_hd_t hd = NULL;
+  gnutls_datum_t _key;
+  int rv = 0;
+  size_t plaintextlen;
+
+  if (taglen > ciphertextlen) {
+    return -1;
+  }
+
+  plaintextlen = ciphertextlen - taglen;
+
+  _key.data = (void *)key;
+  _key.size = ngtcp2_crypto_aead_keylen(aead);
+
+  if (gnutls_aead_cipher_init(&hd, cipher, &_key) != 0 ||
+      gnutls_aead_cipher_decrypt(hd,
+				 nonce, noncelen,
+				 ad, adlen,
+				 taglen,
+				 ciphertext, ciphertextlen,
+				 dest, &plaintextlen) != 0) {
+    rv = -1;
+  }
+
+  gnutls_aead_cipher_deinit(hd);
+
+  return rv;
+}
+
+int ngtcp2_crypto_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp,
+                          const uint8_t *hp_key, const uint8_t *sample) {
+  gnutls_cipher_algorithm_t cipher = (gnutls_cipher_algorithm_t)hp->native_handle;
+  int rv = 0;
+
+  switch (cipher) {
+  case GNUTLS_CIPHER_AES_128_CBC:
+  case GNUTLS_CIPHER_AES_256_CBC:
+    {
+      gnutls_cipher_hd_t hd = NULL;
+      gnutls_datum_t _hp_key;
+      uint8_t iv[16];
+      gnutls_datum_t _iv;
+      uint8_t buf[16];
+
+      _hp_key.data = (void *)hp_key;
+      _hp_key.size = gnutls_cipher_get_key_size(cipher);
+
+      /* Emulate one block AES-ECB by invalidating the effect of IV */
+      memset(iv, 0, sizeof(iv));
+      _iv.data = iv;
+      _iv.size = 16;
+
+      if (gnutls_cipher_init(&hd, cipher, &_hp_key, &_iv) != 0 ||
+	  gnutls_cipher_encrypt2(hd, sample, 16, buf, sizeof(buf)) != 0) {
+	rv = -1;
+      }
+
+      memcpy(dest, buf, 5);
+      gnutls_cipher_deinit(hd);
+    }
+    break;
+
+  case GNUTLS_CIPHER_CHACHA20_32:
+    {
+      gnutls_cipher_hd_t hd = NULL;
+      static const uint8_t PLAINTEXT[] = "\x00\x00\x00\x00\x00";
+      gnutls_datum_t _hp_key;
+      gnutls_datum_t _iv;
+      uint8_t buf[5 + 16];
+      size_t buflen = sizeof(buf);
+
+      _hp_key.data = (void *)hp_key;
+      _hp_key.size = gnutls_cipher_get_key_size(cipher);
+
+      _iv.data = (void *)sample;
+      _iv.size = 16;
+
+      if (gnutls_cipher_init(&hd, cipher, &_hp_key, &_iv) != 0 ||
+	  gnutls_cipher_encrypt2(hd,
+				 PLAINTEXT, sizeof(PLAINTEXT) - 1,
+				 buf, buflen) != 0) {
+	rv = -1;
+      }
+
+      memcpy(dest, buf, 5);
+      gnutls_cipher_deinit(hd);
+    }
+    break;
+  default:
+    assert(0);
+  }
+
+  return rv;
+}
+
+static gnutls_record_encryption_level_t
+from_ngtcp2_level(ngtcp2_crypto_level crypto_level) {
+  switch (crypto_level) {
+  case NGTCP2_CRYPTO_LEVEL_INITIAL:
+    return GNUTLS_ENCRYPTION_LEVEL_INITIAL;
+  case NGTCP2_CRYPTO_LEVEL_HANDSHAKE:
+    return GNUTLS_ENCRYPTION_LEVEL_HANDSHAKE;
+  case NGTCP2_CRYPTO_LEVEL_APP:
+    return GNUTLS_ENCRYPTION_LEVEL_APPLICATION;
+  case NGTCP2_CRYPTO_LEVEL_EARLY:
+    return GNUTLS_ENCRYPTION_LEVEL_EARLY;
+  default:
+    assert(0);
+  }
+}
+
+int ngtcp2_crypto_read_write_crypto_data(ngtcp2_conn *conn, void *tls,
+                                         ngtcp2_crypto_level crypto_level,
+                                         const uint8_t *data, size_t datalen) {
+  gnutls_session_t session = tls;
+  int rv;
+
+  if (datalen > 0) {
+    if (gnutls_handshake_write(session, from_ngtcp2_level(crypto_level), data,
+			       datalen) != 0) {
+      return -1;
+    }
+  }
+
+  if (!ngtcp2_conn_get_handshake_completed(conn)) {
+    rv = gnutls_handshake(session);
+    if (rv < 0) {
+      if (!gnutls_error_is_fatal(rv)) {
+        return 0;
+      }
+      gnutls_alert_send_appropriate(session, rv);
+      return -1;
+    }
+
+    ngtcp2_conn_handshake_completed(conn);
+  }
+
+  return 0;
+}
+
+int ngtcp2_crypto_set_remote_transport_params(ngtcp2_conn *conn, void *tls,
+                                              ngtcp2_crypto_side side) {
+  /* Nothing to do; GnuTLS applications are supposed to register the
+     quic_transport_parameters extension with
+     gnutls_session_ext_register. */
+  return 0;
+}
diff --git a/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in b/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
new file mode 100644
index 00000000..890e89d4
--- /dev/null
+++ b/crypto/gnutls/libngtcp2_crypto_gnutls.pc.in
@@ -0,0 +1,33 @@
+# ngtcp2
+
+# Copyright (c) 2020 ngtcp2 contributors
+
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+
+# The above copyright notice and this permission notice shall be
+# included in all copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+Name: libngtcp2_crypto_gnutls
+Description: ngtcp2 GnuTLS crypto library
+URL: https://github.com/ngtcp2/ngtcp2
+Version: @VERSION@
+Libs: -L${libdir} -lngtcp2_crypto_gnutls
+Cflags: -I${includedir}
diff --git a/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h b/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h
new file mode 100644
index 00000000..caf1a348
--- /dev/null
+++ b/crypto/includes/ngtcp2/ngtcp2_crypto_gnutls.h
@@ -0,0 +1,30 @@
+/*
+ * ngtcp2
+ *
+ * Copyright (c) 2020 ngtcp2 contributors
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef NGTCP2_CRYPTO_GNUTLS_H
+#define NGTCP2_CRYPTO_GNUTLS_H
+
+#include <ngtcp2/ngtcp2.h>
+
+#endif /* NGTCP2_CRYPTO_GNUTLS_H */
-- 
GitLab