diff --git a/configure.ac b/configure.ac index b50c49f..6b7961d 100644 --- a/configure.ac +++ b/configure.ac @@ -1,733 +1,704 @@ # configure.ac - for the Not Too Bad TLS library # Copyright (C) 2014-2017 g10 Code GmbH # # This file is part of NTBTLS # # NTBTLS 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. # # NTBTLS 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 this program; if not, see . # Process this file with autoconf to produce a configure script. AC_PREREQ([2.69]) min_automake_version="1.14" # To build a release you need to create a tag with the version number # (git tag -s gnupg-2.n.m) and run "./autogen.sh --force". Please # bump the version number immediately *after* the release and do # another commit and push so that the git magic is able to work. m4_define([mym4_package],[ntbtls]) m4_define([mym4_major], [0]) m4_define([mym4_minor], [3]) m4_define([mym4_micro], [3]) # To start a new development series, i.e a new major or minor number # you need to mark an arbitrary commit before the first beta release # with an annotated tag. For example the 1.2 branch starts off with # the tag "ntbtls-1.2-base". This is used as the base for counting # beta numbers before the first release of a series. # Below is m4 magic to extract and compute the git revision number, # the decimalized short revision number, a beta version string and a # flag indicating a development version (mym4_isbeta). Note that the # m4 processing is done by autoconf and not during the configure run. m4_define([mym4_verslist], m4_split(m4_esyscmd([./autogen.sh --find-version] \ mym4_package mym4_major mym4_minor mym4_micro),[:])) m4_define([mym4_isbeta], m4_argn(2, mym4_verslist)) m4_define([mym4_version], m4_argn(4, mym4_verslist)) m4_define([mym4_revision], m4_argn(7, mym4_verslist)) m4_define([mym4_revision_dec], m4_argn(8, mym4_verslist)) m4_esyscmd([echo ]mym4_version[>VERSION]) AC_INIT([mym4_package],[mym4_version],[https://bugs.gnupg.org]) # LT Version numbers, remember to change them just *before* a release. # (Interfaces removed: CURRENT++, AGE=0, REVISION=0) # (Interfaces added: CURRENT++, AGE++, REVISION=0) # (No interfaces changed: REVISION++) LIBNTBTLS_LT_CURRENT=1 LIBNTBTLS_LT_AGE=1 LIBNTBTLS_LT_REVISION=3 # If the API is changed in an incompatible way: increment the next counter. NTBTLS_CONFIG_API_VERSION=1 # Minimum versions for required libraries NEED_GPG_ERROR_VERSION=1.25 NEED_LIBGCRYPT_API=1 NEED_LIBGCRYPT_VERSION=1.8.0 NEED_KSBA_API=1 NEED_KSBA_VERSION=1.2.0 # End of config section AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_SRCDIR([src/ntbtls.h.in]) AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([serial-tests dist-bzip2 no-dist-gzip]) AC_CANONICAL_HOST AC_USE_SYSTEM_EXTENSIONS # Some status variables. have_gpg_error=no have_libgcrypt=no have_ksba=no -use_zip=yes AC_SUBST(LIBNTBTLS_LT_CURRENT) AC_SUBST(LIBNTBTLS_LT_AGE) AC_SUBST(LIBNTBTLS_LT_REVISION) VERSION_NUMBER=m4_esyscmd(printf "0x%02x%02x%02x" mym4_major \ mym4_minor mym4_micro) AC_SUBST(VERSION_NUMBER) AC_DEFINE_UNQUOTED(NEED_LIBGCRYPT_VERSION, "$NEED_LIBGCRYPT_VERSION", [Required version of Libgcrypt]) AC_DEFINE_UNQUOTED(NEED_KSBA_VERSION, "$NEED_KSBA_VERSION", [Required version of Libksba]) # # To avoid double inclusion of config.h which might happen at some # places, we add the usual double inclusion protection at the top of # config.h. # AH_TOP([ #ifndef NTBTLS_CONFIG_H_INCLUDED #define NTBTLS_CONFIG_H_INCLUDED ]) # # Stuff which goes at the bottom of config.h. # AH_BOTTOM([ /* If the configure check for endianness has been disabled, get it from OS macros. This is intended for making fat binary builds on OS X. */ #ifdef DISABLED_ENDIAN_CHECK # if defined(__BIG_ENDIAN__) # define WORDS_BIGENDIAN 1 # elif defined(__LITTLE_ENDIAN__) # undef WORDS_BIGENDIAN # else # error "No endianness found" # endif #endif /*DISABLED_ENDIAN_CHECK*/ /* Provide the es_ macro for estream. */ #define GPGRT_ENABLE_ES_MACROS 1 /* Tell libgcrypt not to use its own libgpg-error implementation. */ #define USE_LIBGPG_ERROR 1 /* Tell Libgcrypt not to include deprecated definitions. */ #define GCRYPT_NO_DEPRECATED 1 #endif /*NTBTLS_CONFIG_H_INCLUDED*/ ]) # Substitutions to set generated files in a Emacs buffer to read-only. AC_SUBST(emacs_local_vars_begin, ['Local Variables:']) AC_SUBST(emacs_local_vars_read_only, ['buffer-read-only: t']) AC_SUBST(emacs_local_vars_end, ['End:']) AM_MAINTAINER_MODE AC_ARG_VAR(SYSROOT,[locate config scripts also below that directory]) # Checks for programs. AC_MSG_NOTICE([checking for programs]) AC_PROG_MAKE_SET AM_SANITY_CHECK missing_dir=`cd $ac_aux_dir && pwd` AM_MISSING_PROG(ACLOCAL, aclocal, $missing_dir) AM_MISSING_PROG(AUTOCONF, autoconf, $missing_dir) AM_MISSING_PROG(AUTOMAKE, automake, $missing_dir) AM_MISSING_PROG(AUTOHEADER, autoheader, $missing_dir) AM_MISSING_PROG(MAKEINFO, makeinfo, $missing_dir) AM_SILENT_RULES AC_PROG_AWK AC_PROG_CC AC_PROG_CPP AM_PROG_CC_C_O if test "x$ac_cv_prog_cc_c89" = "xno" ; then AC_MSG_ERROR([[No C-89 compiler found]]) fi AC_PROG_INSTALL AC_PROG_LN_S AC_PROG_RANLIB AC_CHECK_TOOL(AR, ar, :) AC_PATH_PROG(PERL,"perl") AC_CHECK_TOOL(WINDRES, windres, :) AC_SEARCH_LIBS([strerror],[cposix]) # We need to compile and run a program on the build machine. A # comment in libgpg-error says that the AC_PROG_CC_FOR_BUILD macro in # the AC archive is broken for autoconf 2.57. Given that there is no # newer version of that macro, we assume that it is also broken for # autoconf 2.61 and thus we use a simple but usually sufficient # approach. AC_MSG_CHECKING(for cc for build) if test "$cross_compiling" = "yes"; then CC_FOR_BUILD="${CC_FOR_BUILD-cc}" else CC_FOR_BUILD="${CC_FOR_BUILD-$CC}" fi AC_MSG_RESULT($CC_FOR_BUILD) AC_ARG_VAR(CC_FOR_BUILD,[build system C compiler]) LT_PREREQ([2.2.6]) LT_INIT([win32-dll disable-static]) LT_LANG([Windows Resource]) have_dosish_system=no have_w32_system=no have_w32ce_system=no have_android_system=no run_tests=yes case "${host}" in *-mingw32*) # special stuff for Windoze NT have_dosish_system=yes have_w32_system=yes run_tests=no ;; *-linux-androideabi) have_android_system=yes run_tests=no ;; *-apple-darwin*) AC_DEFINE(_DARWIN_C_SOURCE, 1, Expose all libc features (__DARWIN_C_FULL).) ;; *) ;; esac if test "$have_dosish_system" = yes; then AC_DEFINE(HAVE_DOSISH_SYSTEM,1, [Defined if we run on some of the PCDOS like systems (DOS, Windoze. OS/2) with special properties like no file modes, case insensitive file names and preferred use of backslashes as directory name separators.]) fi AM_CONDITIONAL(HAVE_DOSISH_SYSTEM, test "$have_dosish_system" = yes) if test "$have_w32_system" = yes; then AC_DEFINE(HAVE_W32_SYSTEM,1, [Defined if we run on a W32 API based system]) AC_CHECK_HEADERS([winsock2.h]) fi AM_CONDITIONAL(HAVE_W32_SYSTEM, test "$have_w32_system" = yes) if test "$have_android_system" = yes; then AC_DEFINE(HAVE_ANDROID_SYSTEM,1, [Defined if we build for an Android system]) fi AM_CONDITIONAL(HAVE_ANDROID_SYSTEM, test "$have_android_system" = yes) if test "$run_tests" = yes; then AC_DEFINE(RUN_TESTS,1, [Defined if we should run the tests]) fi AM_CONDITIONAL(RUN_TESTS, test "$run_tests" = yes) # # Checks for libraries. # AC_MSG_NOTICE([checking for libraries]) # # libgpg-error is a library with common runtime functions like error # codes, simple mutexes, and the estream functions. # AM_PATH_GPG_ERROR("$NEED_GPG_ERROR_VERSION", have_gpg_error=yes,have_gpg_error=no) AC_DEFINE(GPG_ERR_SOURCE_DEFAULT, GPG_ERR_SOURCE_TLS, [The default error source for NTBTLS.]) AM_CONDITIONAL(USE_GPGRT_CONFIG, [test -n "$GPGRT_CONFIG" \ -a "$ac_cv_path_GPG_ERROR_CONFIG" = no]) # # Libgcrypt is our generic crypto library # AM_PATH_LIBGCRYPT("$NEED_LIBGCRYPT_API:$NEED_LIBGCRYPT_VERSION", have_libgcrypt=yes,have_libgcrypt=no) # # libksba is our X.509 support library # AM_PATH_KSBA("$NEED_KSBA_API:$NEED_KSBA_VERSION",have_ksba=yes,have_ksba=no) AC_MSG_NOTICE([checking for networking options]) # # Must check for network library requirements before doing link tests # for ldap, for example. If ldap libs are static (or dynamic and without # ELF runtime link paths), then link will fail and LDAP support won't # be detected. # AC_CHECK_FUNC(gethostbyname, , AC_CHECK_LIB(nsl, gethostbyname, [NETLIBS="-lnsl $NETLIBS"])) AC_CHECK_FUNC(setsockopt, , AC_CHECK_LIB(socket, setsockopt, [NETLIBS="-lsocket $NETLIBS"])) # # Checks for header files. # AC_MSG_NOTICE([checking for header files]) AC_CHECK_HEADERS([string.h unistd.h stdint.h]) # # Checks for typedefs, structures, and compiler characteristics. # AC_MSG_NOTICE([checking for system characteristics]) AC_C_CONST AC_C_INLINE AC_C_VOLATILE AC_CHECK_SIZEOF(unsigned long) AC_TYPE_SIZE_T AC_TYPE_MODE_T # For some systems we know that we have ld_version scripts. # Use it then as default. have_ld_version_script=no case "${host}" in *-*-linux*) have_ld_version_script=yes ;; *-*-gnu*) have_ld_version_script=yes ;; esac AC_ARG_ENABLE([ld-version-script], AS_HELP_STRING([--enable-ld-version-script], [enable/disable use of linker version script. (default is system dependent)]), [have_ld_version_script=$enableval], [ : ] ) AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes") # # Specify how we support our local modification of libtool for Windows # 64-bit. Options are: # # (1) apply: when appying patch fails, it results failure of entire build # (2) never: never apply the patch (no try) # (3) try: use patched if it goes well, use original if fails # AC_ARG_WITH([libtool-modification], AS_HELP_STRING([--with-libtool-modification=apply|never|try], [how to handle libtool modification (default=never)]), build_libtool_modification=$withval, build_libtool_modification=never) # # Apply a patch (locally maintained one of ours) to libtool # case $host in x86_64-*mingw32*) AC_CONFIG_COMMANDS([libtool-patch],[[ if test "$build_selection" = never; then echo "patch not applied" elif (mv -f libtool libtool.orig; \ sed -f $srcdir/build-aux/libtool-patch.sed libtool.orig >libtool); then echo "applied successfully" elif test "$build_selection" = try; then mv -f libtool.orig libtool echo "patch failed, thus, using original" else echo "patch failed" as_fn_exit 1 fi ]],[build_selection=$build_libtool_modification]) ;; *) ;; esac AC_ARG_ENABLE(endian-check, AS_HELP_STRING([--disable-endian-check], [disable the endian check and trust the OS provided macros]), endiancheck=$enableval,endiancheck=yes) if test x"$endiancheck" = xyes ; then AC_C_BIGENDIAN else AC_DEFINE(DISABLED_ENDIAN_CHECK,1,[configure did not test for endianess]) fi # # Checks for library functions. # AC_MSG_NOTICE([checking for library functions]) AC_CHECK_FUNCS([strlwr flockfile]) # # Check for ELF visibility support. # AC_CACHE_CHECK(whether the visibility attribute is supported, ntbtls_cv_visibility_attribute, [ntbtls_cv_visibility_attribute=no AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo __attribute__ ((visibility ("hidden"))) = 1; int bar __attribute__ ((visibility ("protected"))) = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden.*foo' conftest.s >/dev/null 2>&1 ; then if grep '\.protected.*bar' conftest.s >/dev/null 2>&1; then ntbtls_cv_visibility_attribute=yes fi fi fi ]) if test "$ntbtls_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken visibility attribute, ntbtls_cv_broken_visibility_attribute, [ntbtls_cv_broken_visibility_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[int foo (int x); int bar (int x) __asm__ ("foo") __attribute__ ((visibility ("hidden"))); int bar (int x) { return x; } ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep '\.hidden@<:@ _@:>@foo' conftest.s >/dev/null 2>&1; then ntbtls_cv_broken_visibility_attribute=no fi fi ]) fi if test "$ntbtls_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(for broken alias attribute, ntbtls_cv_broken_alias_attribute, [ntbtls_cv_broken_alias_attribute=yes AC_LANG_CONFTEST([AC_LANG_SOURCE( [[extern int foo (int x) __asm ("xyzzy"); int bar (int x) { return x; } extern __typeof (bar) foo __attribute ((weak, alias ("bar"))); extern int dfoo; extern __typeof (dfoo) dfoo __asm ("abccb"); int dfoo = 1; ]])]) if ${CC-cc} -Werror -S conftest.c -o conftest.s \ 1>&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD ; then if grep 'xyzzy' conftest.s >/dev/null 2>&1 && \ grep 'abccb' conftest.s >/dev/null 2>&1; then ntbtls_cv_broken_alias_attribute=no fi fi ]) fi if test "$ntbtls_cv_visibility_attribute" = "yes"; then AC_CACHE_CHECK(if gcc supports -fvisibility=hidden, ntbtls_cv_gcc_has_f_visibility, [ntbtls_cv_gcc_has_f_visibility=no _gcc_cflags_save=$CFLAGS CFLAGS="-fvisibility=hidden" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], ntbtls_cv_gcc_has_f_visibility=yes) CFLAGS=$_gcc_cflags_save; ]) fi if test "$ntbtls_cv_visibility_attribute" = "yes" \ && test "$ntbtls_cv_broken_visibility_attribute" != "yes" \ && test "$ntbtls_cv_broken_alias_attribute" != "yes" \ && test "$ntbtls_cv_gcc_has_f_visibility" = "yes" then AC_DEFINE(NTBTLS_USE_VISIBILITY, 1, [Define to use the GNU C visibility attribute.]) CFLAGS="$CFLAGS -fvisibility=hidden" fi -# -# Do we have zlib? Must do it here because Solaris failed -# when compiling a conftest (due to the "-lz" from LIBS). -# Note that we combine zlib and bzlib2 in ZLIBS. -# -if test "$use_zip" = yes ; then - _cppflags="${CPPFLAGS}" - _ldflags="${LDFLAGS}" - AC_ARG_WITH(zlib, - [ --with-zlib=DIR use libz in DIR],[ - if test -d "$withval"; then - CPPFLAGS="${CPPFLAGS} -I$withval/include" - LDFLAGS="${LDFLAGS} -L$withval/lib" - fi - ]) - - AC_CHECK_HEADER(zlib.h, - AC_CHECK_LIB(z, deflateInit2_, - [ - ZLIBS="-lz" - AC_DEFINE(HAVE_ZIP,1, [Defined if ZIP and ZLIB are supported]) - ], - CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}), - CPPFLAGS=${_cppflags} LDFLAGS=${_ldflags}) -fi - - - AM_CONDITIONAL(CROSS_COMPILING, test x$cross_compiling = xyes) # Add some extra libs here so that previous tests don't fail for # mysterious reasons - the final link step should bail out. # W32SOCKLIBS is also defined so that if can be used for tools not # requiring any network stuff but linking to code in libcommon which # tracks in winsock stuff (e.g. init_common_subsystems). if test "$have_w32_system" = yes; then if test "$have_w32ce_system" = yes; then W32SOCKLIBS="-lws2" else W32SOCKLIBS="-lws2_32" fi NETLIBS="${NETLIBS} ${W32SOCKLIBS}" fi AC_SUBST(NETLIBS) AC_SUBST(W32SOCKLIBS) # # Setup gcc specific options # AC_MSG_NOTICE([checking for cc features]) if test "$GCC" = yes; then # Check whether gcc does not emit a diagnositc for unknow -Wno-* # options. This is the case for gcc >= 4.6 AC_MSG_CHECKING([if gcc ignores unknown -Wno-* options]) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 6 ) #kickerror #endif]],[])],[_gcc_silent_wno=yes],[_gcc_silent_wno=no]) AC_MSG_RESULT($_gcc_silent_wno) # Note that it is okay to use CFLAGS here because these are just # warning options and the user should have a chance of overriding # them. if test "$USE_MAINTAINER_MODE" = "yes"; then CFLAGS="$CFLAGS -O3 -Wall -Wcast-align -Wshadow -Wstrict-prototypes" CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security" if test x"$_gcc_silent_wno" = xyes ; then _gcc_wopt=yes else AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-missing-field-initializers" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_wopt=yes],[_gcc_wopt=no]) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -W -Wno-sign-compare -Wno-missing-field-initializers" fi AC_MSG_CHECKING([if gcc supports -Wdeclaration-after-statement]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wdeclaration-after-statement" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no) AC_MSG_RESULT($_gcc_wopt) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_wopt" = xyes ; then CFLAGS="$CFLAGS -Wdeclaration-after-statement" fi else CFLAGS="$CFLAGS -Wall" fi if test x"$_gcc_silent_wno" = xyes ; then _gcc_psign=yes else AC_MSG_CHECKING([if gcc supports -Wno-pointer-sign]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wno-pointer-sign" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])], [_gcc_psign=yes],[_gcc_psign=no]) AC_MSG_RESULT($_gcc_psign) CFLAGS=$_gcc_cflags_save; fi if test x"$_gcc_psign" = xyes ; then CFLAGS="$CFLAGS -Wno-pointer-sign" fi AC_MSG_CHECKING([if gcc supports -Wpointer-arith]) _gcc_cflags_save=$CFLAGS CFLAGS="-Wpointer-arith" AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_psign=yes,_gcc_psign=no) AC_MSG_RESULT($_gcc_psign) CFLAGS=$_gcc_cflags_save; if test x"$_gcc_psign" = xyes ; then CFLAGS="$CFLAGS -Wpointer-arith" fi fi # # This is handy for debugging so the compiler doesn't rearrange # things and eliminate variables. # AC_ARG_ENABLE(optimization, AS_HELP_STRING([--disable-optimization], [disable compiler optimization]), [if test $enableval = no ; then CFLAGS=`echo $CFLAGS | sed s/-O[[1-9]]\ /-O0\ /g` fi]) # # Set variables for use by automake makefiles. # # # Set variables for use by ntbtls-config. # NTBTLS_CONFIG_LIBS="-lntbtls" NTBTLS_CONFIG_CFLAGS="" NTBTLS_CONFIG_HOST="$host" AC_SUBST(NTBTLS_CONFIG_API_VERSION) AC_SUBST(NTBTLS_CONFIG_LIBS) AC_SUBST(NTBTLS_CONFIG_CFLAGS) AC_SUBST(NTBTLS_CONFIG_HOST) # # Provide information about the build. # BUILD_REVISION="mym4_revision" AC_SUBST(BUILD_REVISION) AC_DEFINE_UNQUOTED(BUILD_REVISION, "$BUILD_REVISION", [GIT commit id revision used to build this package]) changequote(,)dnl BUILD_FILEVERSION=`echo "$VERSION" | sed 's/\([0-9.]*\).*/\1./;s/\./,/g'` changequote([,])dnl BUILD_FILEVERSION="${BUILD_FILEVERSION}mym4_revision_dec" AC_SUBST(BUILD_FILEVERSION) AC_ARG_ENABLE([build-timestamp], AS_HELP_STRING([--enable-build-timestamp], [set an explicit build timestamp for reproducibility. (default is the current time in ISO-8601 format)]), [if test "$enableval" = "yes"; then BUILD_TIMESTAMP=`date -u +%Y-%m-%dT%H:%M+0000 2>/dev/null || date` else BUILD_TIMESTAMP="$enableval" fi], [BUILD_TIMESTAMP=""]) AC_SUBST(BUILD_TIMESTAMP) AC_DEFINE_UNQUOTED(BUILD_TIMESTAMP, "$BUILD_TIMESTAMP", [The time this package was configured for a build]) BUILD_HOSTNAME="$ac_hostname" AC_SUBST(BUILD_HOSTNAME) # # Print errors here so that they are visible all # together and the user can acquire them all together. # die=no if test "$have_gpg_error" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libgpg-error to build this program. ** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libgpg-error *** (at least version $NEED_GPG_ERROR_VERSION is required.) ***]]) fi if test "$have_libgcrypt" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libgcrypt to build this program. ** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libgcrypt/ *** (at least version $NEED_LIBGCRYPT_VERSION (API $NEED_LIBGCRYPT_API) is required.) ***]]) fi if test "$have_ksba" = "no"; then die=yes AC_MSG_NOTICE([[ *** *** You need libksba to build this program. *** This library is for example available at *** ftp://ftp.gnupg.org/gcrypt/libksba/ *** (at least version $NEED_KSBA_VERSION using API $NEED_KSBA_API is required). ***]]) fi if test "$die" = "yes"; then AC_MSG_ERROR([[ *** *** Required libraries not found. Please consult the above messages *** and install them before running configure again. ***]]) fi AC_CONFIG_FILES([ Makefile m4/Makefile src/Makefile src/ntbtls.h src/ntbtls-config src/ntbtls.pc src/versioninfo.rc ]) AC_CONFIG_COMMANDS([ntbtls-conf],[[ chmod +x src/ntbtls-config ]]) AC_OUTPUT echo " NTBTLS v${VERSION} has been configured as follows: Revision: mym4_revision (mym4_revision_dec) Platform: $host " if test "x${gpg_config_script_warn}" != x; then cat <. */ #ifndef NTBTLS_CONTEXT_H #define NTBTLS_CONTEXT_H -#include - - typedef enum gcry_md_algos md_algo_t; typedef enum gcry_mac_algos mac_algo_t; typedef enum gcry_cipher_algos cipher_algo_t; typedef enum gcry_cipher_modes cipher_mode_t; typedef enum gcry_pk_algos pk_algo_t; /* * TLS states (note that the order of the states is important) */ typedef enum { TLS_HELLO_REQUEST, TLS_CLIENT_HELLO, TLS_SERVER_HELLO, TLS_SERVER_CERTIFICATE, TLS_SERVER_KEY_EXCHANGE, TLS_CERTIFICATE_REQUEST, TLS_SERVER_HELLO_DONE, TLS_CLIENT_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_CERTIFICATE_VERIFY, TLS_CLIENT_CHANGE_CIPHER_SPEC, TLS_CLIENT_FINISHED, TLS_SERVER_CHANGE_CIPHER_SPEC, TLS_SERVER_FINISHED, TLS_FLUSH_BUFFERS, TLS_HANDSHAKE_WRAPUP, TLS_HANDSHAKE_OVER, TLS_SERVER_NEW_SESSION_TICKET } tls_state_t; /* * Renegotiation states */ typedef enum { TLS_INITIAL_HANDSHAKE = 0, TLS_RENEGOTIATION, /* In progress */ TLS_RENEGOTIATION_DONE, /* Done */ TLS_RENEGOTIATION_PENDING /* Requested (server only) */ } tls_renegotiation_state_t; /* * Key exchange protocols * * Reminder: Update premaster_secret_u when adding a new key exchange. */ typedef enum { KEY_EXCHANGE_NONE = 0, KEY_EXCHANGE_RSA, KEY_EXCHANGE_DHE_RSA, KEY_EXCHANGE_ECDHE_RSA, KEY_EXCHANGE_ECDHE_ECDSA, KEY_EXCHANGE_PSK, KEY_EXCHANGE_DHE_PSK, KEY_EXCHANGE_RSA_PSK, KEY_EXCHANGE_ECDHE_PSK, KEY_EXCHANGE_ECDH_RSA, KEY_EXCHANGE_ECDH_ECDSA } key_exchange_type_t; /* * Object to hold an X.509 CRL. */ struct x509_crl_s; typedef struct x509_crl_s *x509_crl_t; /* * Object to hold an X.509 private key. */ struct x509_privkey_s; typedef struct x509_privkey_s *x509_privkey_t; /* * Object to hold an DHM context. */ struct dhm_context_s; typedef struct dhm_context_s *dhm_context_t; /* * Object to hold an ECDH context. */ struct ecdh_context_s; typedef struct ecdh_context_s *ecdh_context_t; /* * This structure is used for storing current session data. */ struct _ntbtls_session_s { time_t start; /*!< starting time */ int ciphersuite; /*!< chosen ciphersuite */ int compression; /*!< chosen compression */ size_t length; /*!< session id length */ unsigned char id[32]; /*!< session identifier */ unsigned char master[48]; /*!< the master secret */ x509_cert_t peer_chain; /*!< peer X.509 cert chain */ int verify_result; /*!< verification result */ unsigned char *ticket; /*!< RFC 5077 session ticket */ size_t ticket_len; /*!< session ticket length */ uint32_t ticket_lifetime; /*!< ticket lifetime hint */ unsigned char mfl_code; /*!< MaxFragmentLength negotiated by peer */ int use_trunc_hmac; /* Flag for truncated hmac activation. */ }; typedef struct _ntbtls_session_s *session_t; /* * This structure is used for storing ciphersuite information */ struct _ntbtls_ciphersuite_s; typedef const struct _ntbtls_ciphersuite_s *ciphersuite_t; /* * This structure contains a full set of runtime transform parameters * either in negotiation or active. */ struct _ntbtls_transform_s { /* * Session specific crypto layer */ ciphersuite_t ciphersuite; /*!< Chosen cipersuite_info */ unsigned int keylen; /*!< symmetric key length */ size_t minlen; /*!< min. ciphertext length */ size_t ivlen; /*!< IV length */ size_t fixed_ivlen; /*!< Fixed part of IV (AEAD) */ size_t maclen; /* MAC length in bytes */ unsigned char iv_enc[16]; /*!< IV (encryption) */ unsigned char iv_dec[16]; /*!< IV (decryption) */ gcry_mac_hd_t mac_ctx_enc; /* MAC (encryption) */ gcry_mac_hd_t mac_ctx_dec; /* MAC (decryption) */ gcry_cipher_hd_t cipher_ctx_enc; /* Encryption context. */ cipher_mode_t cipher_mode_enc;/* Mode for encryption. */ gcry_cipher_hd_t cipher_ctx_dec; /* Decryption context. */ cipher_mode_t cipher_mode_dec;/* Mode for encryption. */ - - /* - * Session specific compression layer - */ - z_stream ctx_deflate; /*!< compression context */ - z_stream ctx_inflate; /*!< decompression context */ }; typedef struct _ntbtls_transform_s *transform_t; /* * List of certificate + private key pairs */ struct _ntbtls_key_cert_s { struct _ntbtls_key_cert_s *next; x509_cert_t cert; x509_privkey_t key; }; typedef struct _ntbtls_key_cert_s *key_cert_t; /* * This structure contains the parameters only needed during handshake. */ struct _ntbtls_handshake_params_s { /* * Handshake specific crypto variables */ int sig_alg; /*!< Hash algorithm for signature */ int cert_type; /*!< Requested cert type */ int verify_sig_alg; /*!< Signature algorithm for verify */ dhm_context_t dhm_ctx; /* DHM key exchange info. */ ecdh_context_t ecdh_ctx; /* ECDH key exchange info. */ const /*ecp_curve_info*/void **curves;/*!< Supported elliptic curves */ /** * //FIXME: Better explain this * Current key/cert or key/cert list. * On client: pointer to ssl->key_cert, only the first entry used. * On server: starts as a pointer to ssl->key_cert, then becomes * a pointer to the chosen key from this list or the SNI list. */ key_cert_t key_cert; key_cert_t sni_key_cert; /*!< key/cert list from SNI */ /* * Checksum contexts */ gcry_md_hd_t fin_sha256; /* Checksum of all handshake messages. */ gcry_md_hd_t fin_sha512; /* Ditto. */ void (*update_checksum) (ntbtls_t, const unsigned char *, size_t); void (*calc_verify) (ntbtls_t, unsigned char *); void (*calc_finished) (ntbtls_t, unsigned char *, int); gpg_error_t (*tls_prf) (const unsigned char *, size_t, const char *, const unsigned char *, size_t, unsigned char *, size_t); size_t pmslen; /*!< premaster length */ unsigned char randbytes[64]; /*!< random bytes */ unsigned char premaster[TLS_PREMASTER_SIZE]; /*!< premaster secret */ int resume; /*!< session resume indicator */ int max_major_ver; /*!< max. major version client */ int max_minor_ver; /*!< max. minor version client */ int cli_exts; /*!< client extension presence */ int new_session_ticket; /*!< use NewSessionTicket? */ }; typedef struct _ntbtls_handshake_params_s *handshake_params_t; /* * Parameters needed to secure session tickets */ struct _ntbtls_ticket_keys_s { unsigned char key_name[16]; /*!< name to quickly discard bad tickets */ gcry_cipher_hd_t enc; /*!< encryption context */ gcry_cipher_hd_t dec; /*!< decryption context */ unsigned char mac_key[16]; /*!< authentication key */ }; typedef struct _ntbtls_ticket_keys_s *ticket_keys_t; #if SIZEOF_UNSIGNED_LONG == 8 # define NTBTLS_CONTEXT_MAGIC 0x6e7462746c736378 /* "ntbtlscx" */ #else # define NTBTLS_CONTEXT_MAGIC 0x6e746243 /* "ntbC" */ #endif /* * The TLS context object. */ struct _ntbtls_context_s { unsigned long magic; /* * Miscellaneous */ int major_ver; /*!< equal to SSL_MAJOR_VERSION_3 */ int minor_ver; /*!< either 0 (SSL3) or 1 (TLS1.0) */ int max_major_ver; /*!< max. major version used */ int max_minor_ver; /*!< max. minor version used */ int min_major_ver; /*!< min. major version used */ int min_minor_ver; /*!< min. minor version used */ tls_state_t state; /* Current state of the handshake. */ tls_renegotiation_state_t renegotiation; /*!< Initial or renegotiation */ int renego_records_seen; /*!< Records since renego request */ struct { unsigned char any; unsigned char level; unsigned char type; } last_alert; /* Info about the last received alert. */ /* * Callbacks (RNG, debug, I/O, verification) */ void (*f_dbg) (void *, int, const char *); int (*f_recv) (void *, unsigned char *, size_t); int (*f_send) (void *, const unsigned char *, size_t); int (*f_get_cache) (void *, session_t); int (*f_set_cache) (void *, const session_t); void *p_dbg; /*!< context for the debug function */ void *p_recv; /*!< context for reading operations */ void *p_send; /*!< context for writing operations */ void *p_get_cache; /*!< context for cache retrieval */ void *p_set_cache; /*!< context for cache store */ void *p_hw_data; /*!< context for HW acceleration */ int (*f_sni) (void *, ntbtls_t, const unsigned char *, size_t); void *p_sni; /*!< context for SNI extension */ int (*f_vrfy) (void *, x509_cert_t, int, int *); void *p_vrfy; /*!< context for verification */ int (*f_psk) (void *, ntbtls_t, const unsigned char *, size_t); void *p_psk; /*!< context for PSK retrieval */ /* * Session layer */ session_t session_in; /*!< current session data (in) */ session_t session_out; /*!< current session data (out) */ session_t session; /*!< negotiated session data */ session_t session_negotiate; /* Session data in negotiation. */ handshake_params_t handshake; /* Params required only during the handshake process. */ /* * Record layer transformations */ transform_t transform_in; /*!< current transform params (in) */ transform_t transform_out; /*!< current transform params (in) */ transform_t transform; /*!< negotiated transform params */ transform_t transform_negotiate; /* Transform params in negotiation. */ /* * Record layer (incoming data) */ estream_t inbound; /* Stream used to receive TLS data. */ unsigned char *in_ctr; /*!< 64-bit incoming message counter */ unsigned char *in_hdr; /*!< 5-byte record header (in_ctr+8) */ unsigned char *in_iv; /*!< ivlen-byte IV (in_hdr+5) */ unsigned char *in_msg; /*!< message contents (in_iv+ivlen) */ unsigned char *in_offt; /*!< read offset in application data */ int in_msgtype; /*!< record header: message type */ size_t in_msglen; /*!< record header: message length */ size_t in_left; /* Amount of data read so far. */ size_t in_hslen; /*!< current handshake message length */ int nb_zero; /*!< # of 0-length encrypted messages */ int record_read; /*!< record is already present */ /* * Record layer (outgoing data) */ estream_t outbound; /* Stream used to send TLS data. */ unsigned char *out_ctr; /*!< 64-bit outgoing message counter */ unsigned char *out_hdr; /*!< 5-byte record header (out_ctr+8) */ unsigned char *out_iv; /*!< ivlen-byte IV (out_hdr+5) */ unsigned char *out_msg; /*!< message contents (out_iv+ivlen) */ int out_msgtype; /*!< record header: message type */ size_t out_msglen; /* Record header: message length. */ size_t out_left; /* Amount of data not yet written. */ unsigned char *compress_buf; /*!< zlib data buffer */ unsigned char mfl_code; /*!< MaxFragmentLength chosen by us */ /* * Layer to the TLS encrypted data */ estream_t readfp; /* Estream to read from the peer. */ estream_t writefp; /* Estream to write to the peer. */ /* * PKI layer */ key_cert_t key_cert; /*!< own certificate(s)/key(s) */ ntbtls_verify_cb_t verify_cb; /*!< the verify callback */ void *verify_cb_value;; /*!< the first arg passed to this cb */ /* * Support for generating and checking session tickets */ ticket_keys_t ticket_keys; /*!< keys for ticket encryption */ /* * User settings */ int is_client; /* True if we are in client mode. */ unsigned int flags; /* All flags from ntbtls_new. */ int authmode; /*!< verification mode */ int client_auth; /*!< flag for client auth. */ int verify_result; /*!< verification result */ int disable_renegotiation; /*!< enable/disable renegotiation */ int allow_legacy_renegotiation; /*!< allow legacy renegotiation */ int renego_max_records; /*!< grace period for renegotiation */ const int *ciphersuite_list[5]; /*!< allowed ciphersuites / version */ const /*ecp_group_id*/ void *curve_list; /*!< allowed curves */ int use_trunc_hmac; /* Use truncated HMAC flag. */ int use_session_tickets; /* Use session tickets flag. */ int ticket_lifetime; /*!< session ticket lifetime */ gcry_mpi_t dhm_P; /*!< prime modulus for DHM */ gcry_mpi_t dhm_G; /*!< generator for DHM */ char *hostname; /*!< expected peer CN for verification and SNI */ /* * PSK values */ unsigned char *psk; size_t psk_len; unsigned char *psk_identity; size_t psk_identity_len; /* * ALPN extension */ const char **alpn_list; /*!< ordered list of supported protocols */ const char *alpn_chosen; /*!< negotiated protocol */ /* * Secure renegotiation */ int secure_renegotiation; /*!< does peer support legacy or secure renegotiation */ size_t verify_data_len; /*!< length of verify data stored */ char own_verify_data[36]; /*!< previous handshake verify data */ char peer_verify_data[36]; /*!< previous handshake verify data */ }; #endif /*NTBTLS_CONTEXT_H*/ diff --git a/src/protocol-cli.c b/src/protocol-cli.c index 63509e0..9eb1806 100644 --- a/src/protocol-cli.c +++ b/src/protocol-cli.c @@ -1,2217 +1,2215 @@ /* protocol-cli.c - TLS 1.2 client side protocol * Copyright (C) 2006-2014, Brainspark B.V. * Copyright (C) 2014 g10 code GmbH * * This file is part of NTBTLS * * NTBTLS 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. * * NTBTLS 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 this program; if not, see . * * This file was part of PolarSSL (http://www.polarssl.org). Former * Lead Maintainer: Paul Bakker . * Please do not file bug reports to them but to the address given in * the file AUTHORS in the top directory of NTBTLS. */ #include #include #include #include "ntbtls-int.h" #include "ciphersuites.h" static void write_hostname_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t len; *olen = 0; if (!tls->hostname) return; debug_msg (3, "client_hello, adding server name extension: '%s'", tls->hostname); len = strlen (tls->hostname); /* * struct { * NameType name_type; * select (name_type) { * case host_name: HostName; * } name; * } ServerName; * * enum { * host_name(0), (255) * } NameType; * * opaque HostName<1..2^16-1>; * * struct { * ServerName server_name_list<1..2^16-1> * } ServerNameList; */ *p++ = (unsigned char) ((TLS_EXT_SERVERNAME >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF); *p++ = (unsigned char) (((len + 5) >> 8) & 0xFF); *p++ = (unsigned char) (((len + 5)) & 0xFF); *p++ = (unsigned char) (((len + 3) >> 8) & 0xFF); *p++ = (unsigned char) (((len + 3)) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SERVERNAME) & 0xFF); *p++ = (unsigned char) ((len >> 8) & 0xFF); *p++ = (unsigned char) ((len) & 0xFF); memcpy (p, tls->hostname, len); *olen = len + 9; } static void write_cli_renegotiation_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; *olen = 0; if (ssl->renegotiation != TLS_RENEGOTIATION) return; debug_msg (3, "client_hello, adding renegotiation extension"); /* * Secure renegotiation */ *p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_RENEGOTIATION_INFO) & 0xFF); *p++ = 0x00; *p++ = (ssl->verify_data_len + 1) & 0xFF; *p++ = ssl->verify_data_len & 0xFF; memcpy (p, ssl->own_verify_data, ssl->verify_data_len); *olen = 5 + ssl->verify_data_len; } static void write_signature_algorithms_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t sig_alg_len = 0; unsigned char *sig_alg_list = buf + 6; *olen = 0; if (ssl->max_minor_ver != TLS_MINOR_VERSION_4) return; debug_msg (3, "client_hello, adding signature_algorithms extension"); /* * Prepare signature_algorithms extension (TLS 1.2) */ sig_alg_list[sig_alg_len++] = TLS_HASH_SHA512; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA384; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA256; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA224; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA1; sig_alg_list[sig_alg_len++] = TLS_SIG_RSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA512; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA384; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA256; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA224; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; sig_alg_list[sig_alg_len++] = TLS_HASH_SHA1; sig_alg_list[sig_alg_len++] = TLS_SIG_ECDSA; /* * enum { * none(0), md5(1), sha1(2), sha224(3), sha256(4), sha384(5), * sha512(6), (255) * } HashAlgorithm; * * enum { anonymous(0), rsa(1), dsa(2), ecdsa(3), (255) } * SignatureAlgorithm; * * struct { * HashAlgorithm hash; * SignatureAlgorithm signature; * } SignatureAndHashAlgorithm; * * SignatureAndHashAlgorithm * supported_signature_algorithms<2..2^16-2>; */ *p++ = (unsigned char) ((TLS_EXT_SIG_ALG >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SIG_ALG) & 0xFF); *p++ = (unsigned char) (((sig_alg_len + 2) >> 8) & 0xFF); *p++ = (unsigned char) (((sig_alg_len + 2)) & 0xFF); *p++ = (unsigned char) ((sig_alg_len >> 8) & 0xFF); *p++ = (unsigned char) ((sig_alg_len) & 0xFF); *olen = 6 + sig_alg_len; } static void write_supported_versions_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t version_len; (void)tls; version_len = 2 * 2; debug_msg (3, "client hello, adding supported_versions extension"); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_VERSIONS >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_VERSIONS) & 0xFF); *p++ = (unsigned char) (((version_len + 1) >> 8) & 0xFF); *p++ = (unsigned char) (((version_len + 1)) & 0xFF); *p++ = (unsigned char) version_len; *p++ = (unsigned char) TLS_MAJOR_VERSION_3; *p++ = (unsigned char) TLS_MINOR_VERSION_3; *p++ = (unsigned char) TLS_MAJOR_VERSION_3; *p++ = (unsigned char) TLS_MINOR_VERSION_4; *olen = (p - buf); } static void write_supported_elliptic_curves_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; unsigned char *elliptic_curve_list = p + 6; size_t elliptic_curve_len = 0; (void)tls; debug_msg (3, "client hello, adding supported_elliptic_curves extension"); /* The 8 curves we support; see _ntbtls_ecdh_read_params. */ elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 23; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 24; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 25; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 26; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 27; elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 28; #ifdef SUPPORT_X25519 elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 29; #endif #ifdef SUPPORT_X448 elliptic_curve_list[elliptic_curve_len++] = 0; elliptic_curve_list[elliptic_curve_len++] = 30; #endif *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_ELLIPTIC_CURVES >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_ELLIPTIC_CURVES) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len + 2) >> 8) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len + 2)) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len) >> 8) & 0xFF); *p++ = (unsigned char) (((elliptic_curve_len)) & 0xFF); *olen = 6 + elliptic_curve_len; } static void write_cli_supported_point_formats_ext (ntbtls_t tls, unsigned char *buf, size_t *olen) { unsigned char *p = buf; (void)tls; debug_msg (3, "client hello, adding supported_point_formats extension"); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_POINT_FORMATS >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SUPPORTED_POINT_FORMATS) & 0xFF); *p++ = 0; *p++ = 2; *p++ = 1; /* One item. */ *p++ = 0; /* Uncompressed. */ *olen = 6; } static void write_cli_max_fragment_length_ext (ntbtls_t tls, unsigned char *buf, size_t *olen) { unsigned char *p = buf; if (tls->mfl_code == TLS_MAX_FRAG_LEN_NONE) { *olen = 0; return; } debug_msg (3, "client_hello, adding max_fragment_length extension"); *p++ = (unsigned char) ((TLS_EXT_MAX_FRAGMENT_LENGTH >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_MAX_FRAGMENT_LENGTH) & 0xFF); *p++ = 0x00; *p++ = 1; *p++ = tls->mfl_code; *olen = 5; } static void write_cli_truncated_hmac_ext (ntbtls_t tls, unsigned char *buf, size_t * olen) { unsigned char *p = buf; if (!tls->use_trunc_hmac) { *olen = 0; return; } debug_msg (3, "client_hello, adding truncated_hmac extension"); *p++ = (unsigned char) ((TLS_EXT_TRUNCATED_HMAC >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_TRUNCATED_HMAC) & 0xFF); *p++ = 0x00; *p++ = 0x00; *olen = 4; } static void write_cli_session_ticket_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; size_t tlen = ssl->session_negotiate->ticket_len; if (!ssl->use_session_tickets) { *olen = 0; return; } debug_msg (3, "client_hello, adding session ticket extension"); *p++ = (unsigned char) ((TLS_EXT_SESSION_TICKET >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_SESSION_TICKET) & 0xFF); *p++ = (unsigned char) ((tlen >> 8) & 0xFF); *p++ = (unsigned char) ((tlen) & 0xFF); *olen = 4; if (ssl->session_negotiate->ticket == NULL || ssl->session_negotiate->ticket_len == 0) { return; } debug_msg (3, "sending session_ticket of length %zu", tlen); memcpy (p, ssl->session_negotiate->ticket, tlen); *olen += tlen; } static void write_cli_alpn_ext (ntbtls_t ssl, unsigned char *buf, size_t * olen) { unsigned char *p = buf; const char **cur; if (ssl->alpn_list == NULL) { *olen = 0; return; } debug_msg (3, "client hello, adding alpn extension"); *p++ = (unsigned char) ((TLS_EXT_ALPN >> 8) & 0xFF); *p++ = (unsigned char) ((TLS_EXT_ALPN) & 0xFF); /* * opaque ProtocolName<1..2^8-1>; * * struct { * ProtocolName protocol_name_list<2..2^16-1> * } ProtocolNameList; */ /* Skip writing extension and list length for now */ p += 4; for (cur = ssl->alpn_list; *cur != NULL; cur++) { *p = (unsigned char) (strlen (*cur) & 0xFF); memcpy (p + 1, *cur, *p); p += 1 + *p; } *olen = p - buf; /* List length = olen - 2 (ext_type) - 2 (ext_len) - 2 (list_len) */ buf[4] = (unsigned char) (((*olen - 6) >> 8) & 0xFF); buf[5] = (unsigned char) (((*olen - 6)) & 0xFF); /* Extension length = olen - 2 (ext_type) - 2 (ext_len) */ buf[2] = (unsigned char) (((*olen - 4) >> 8) & 0xFF); buf[3] = (unsigned char) (((*olen - 4)) & 0xFF); } static gpg_error_t write_client_hello (ntbtls_t tls) { gpg_error_t err; size_t i, n, olen; size_t ext_len = 0; unsigned char *buf; unsigned char *p, *q; time_t t; const int *ciphersuites; ciphersuite_t suite; debug_msg (2, "write client_hello"); if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { tls->major_ver = tls->min_major_ver; tls->minor_ver = tls->min_minor_ver; } if (tls->max_major_ver == 0 && tls->max_minor_ver == 0) { tls->max_major_ver = TLS_MAX_MAJOR_VERSION; tls->max_minor_ver = TLS_MAX_MINOR_VERSION; } /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 5 highest version supported * 6 . 9 current UNIX time * 10 . 37 random bytes */ buf = tls->out_msg; p = buf + 4; *p++ = (unsigned char) tls->max_major_ver; *p++ = (unsigned char) tls->max_minor_ver; debug_msg (3, "client_hello, max version: [%d:%d]", buf[4], buf[5]); t = time (NULL); *p++ = (unsigned char) (t >> 24); *p++ = (unsigned char) (t >> 16); *p++ = (unsigned char) (t >> 8); *p++ = (unsigned char) (t); debug_msg (3, "client_hello, current time: %lu", t); //FIXME: Check RNG requirements. gcry_create_nonce (p, 28); p += 28; memcpy (tls->handshake->randbytes, buf + 6, 32); debug_buf (3, "client_hello, random bytes", buf + 6, 32); /* * 38 . 38 session id length * 39 . 39+n session id * 40+n . 41+n ciphersuitelist length * 42+n . .. ciphersuitelist * .. . .. compression methods length * .. . .. compression methods * .. . .. extensions length * .. . .. extensions */ n = tls->session_negotiate->length; if (tls->renegotiation != TLS_INITIAL_HANDSHAKE || n < 16 || n > 32 || tls->handshake->resume == 0) { n = 0; } /* * RFC 5077 section 3.4: "When presenting a ticket, the client MAY * generate and include a Session ID in the TLS ClientHello." */ if (tls->renegotiation == TLS_INITIAL_HANDSHAKE && tls->session_negotiate->ticket != NULL && tls->session_negotiate->ticket_len != 0) { gcry_create_nonce (tls->session_negotiate->id, 32); tls->session_negotiate->length = n = 32; } *p++ = (unsigned char) n; for (i = 0; i < n; i++) *p++ = tls->session_negotiate->id[i]; debug_msg (3, "client_hello, session id len.: %zu", n); debug_buf (3, "client_hello, session id", buf + 39, n); // Fixme: We do not have a way to set the ciphersuites. Thus // consider to replace this with simpler code. ciphersuites = tls->ciphersuite_list[tls->minor_ver]; n = 0; q = p; /* Skip writing ciphersuite length for now. */ p += 2; /* * Add TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { *p++ = (unsigned char) (TLS_EMPTY_RENEGOTIATION_INFO >> 8); *p++ = (unsigned char) (TLS_EMPTY_RENEGOTIATION_INFO); n++; } /*FIXME: We should add an explicit limit and not rely on the known length of the ciphersuites. */ for (i = 0; ciphersuites && ciphersuites[i]; i++) { suite = _ntbtls_ciphersuite_from_id (ciphersuites[i]); if (!suite) continue; if (!_ntbtls_ciphersuite_version_ok (suite, tls->min_minor_ver, tls->max_minor_ver)) continue; debug_msg (5, "client_hello, add ciphersuite: %5d %s", ciphersuites[i], _ntbtls_ciphersuite_get_name (ciphersuites[i])); n++; *p++ = (unsigned char) (ciphersuites[i] >> 8); *p++ = (unsigned char) (ciphersuites[i]); } /* Fixup the ciphersuite length. */ *q++ = (unsigned char) (n >> 7); *q++ = (unsigned char) (n << 1); debug_msg (3, "client_hello, got %zu ciphersuites", n); - debug_msg (3, "client_hello, compress len.: %d", 2); - debug_msg (3, "client_hello, compress alg.: %d %d", - TLS_COMPRESS_DEFLATE, TLS_COMPRESS_NULL); + debug_msg (3, "client_hello, compress len.: %d", 1); + debug_msg (3, "client_hello, compress alg.: %d", TLS_COMPRESS_NULL); - *p++ = 2; - *p++ = TLS_COMPRESS_DEFLATE; + *p++ = 1; *p++ = TLS_COMPRESS_NULL; /* First write extensions, then the total length. */ write_hostname_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_renegotiation_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_supported_elliptic_curves_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_supported_point_formats_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_signature_algorithms_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_supported_versions_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_max_fragment_length_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_truncated_hmac_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_session_ticket_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; write_cli_alpn_ext (tls, p + 2 + ext_len, &olen); ext_len += olen; debug_msg (3, "client_hello, total extension length: %zu", ext_len); if (ext_len > 0) { *p++ = (unsigned char) ((ext_len >> 8) & 0xFF); *p++ = (unsigned char) ((ext_len) & 0xFF); p += ext_len; } tls->out_msglen = p - buf; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CLIENT_HELLO; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t parse_renegotiation_info (ntbtls_t tls, const unsigned char *buf, size_t len) { gpg_error_t err; if (tls->renegotiation == TLS_INITIAL_HANDSHAKE) { if (len != 1 || buf[0] != 0x0) { debug_msg (1, "non-zero length renegotiated connection field"); err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } tls->secure_renegotiation = TLS_SECURE_RENEGOTIATION; } else { /* Check verify-data in constant-time. The length OTOH is no secret */ if (len != 1 + tls->verify_data_len * 2 || buf[0] != tls->verify_data_len * 2 || memcmpct (buf + 1, tls->own_verify_data, tls->verify_data_len) || memcmpct (buf + 1 + tls->verify_data_len, tls->peer_verify_data, tls->verify_data_len)) { debug_msg (1, "non-matching renegotiated connection field"); err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } } return 0; } static gpg_error_t parse_max_fragment_length_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { /* * server should use the extension only if we did, * and if so the server's value should match ours (and len is always 1) */ if (tls->mfl_code == TLS_MAX_FRAG_LEN_NONE || len != 1 || buf[0] != tls->mfl_code) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } return 0; } static gpg_error_t parse_truncated_hmac_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { (void)buf; if (!tls->use_trunc_hmac || len) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->session_negotiate->use_trunc_hmac = 1; return 0; } static gpg_error_t parse_session_ticket_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { (void)buf; if (!tls->use_session_tickets || len) { return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->handshake->new_session_ticket = 1; return 0; } static gpg_error_t parse_supported_point_formats_ext (ntbtls_t ssl, const unsigned char *buf, size_t len) { size_t list_size; const unsigned char *p; list_size = buf[0]; if (list_size + 1 != len) { debug_msg (1, "bad server hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } p = buf + 1; while (list_size > 0) { if (p[0] == 0) { /* Fixme: Store the format - right now not required because * we support only one format. */ /* ssl->handshake->ecdh_ctx.point_format = p[0]; */ (void)ssl; debug_msg (4, "point format selected: %d", p[0]); return 0; } list_size--; p++; } debug_msg (1, "no point format in common"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } static gpg_error_t parse_alpn_ext (ntbtls_t tls, const unsigned char *buf, size_t len) { size_t list_len, name_len; const char **p; /* If we didn't send it, the server shouldn't send it */ if (!tls->alpn_list) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); /* * opaque ProtocolName<1..2^8-1>; * * struct { * ProtocolName protocol_name_list<2..2^16-1> * } ProtocolNameList; * * the "ProtocolNameList" MUST contain exactly one "ProtocolName" */ /* Min length is 2 (list_len) + 1 (name_len) + 1 (name) */ if (len < 4) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); list_len = buf16_to_size_t (buf); if (list_len != len - 2) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); name_len = buf[2]; if (name_len != list_len - 1) return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); /* Check that the server chosen protocol was in our list and save it */ for (p = tls->alpn_list; *p; p++) { if (name_len == strlen (*p) && !memcmp (buf + 3, *p, name_len)) { tls->alpn_chosen = *p; return 0; } } return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } static gpg_error_t read_server_hello (ntbtls_t tls) { gpg_error_t err; int i, suite_id, comp; size_t n; size_t ext_len = 0; unsigned char *buf, *ext; int renegotiation_info_seen = 0; int handshake_failure = 0; const int *ciphersuites; uint32_t t; debug_msg (2, "read server_hello"); /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 5 protocol version * 6 . 9 UNIX time() * 10 . 37 random bytes */ buf = tls->in_msg; err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } debug_msg (1, "server_hello, chosen version: [%d:%d]", buf[4], buf[5]); if (tls->in_hslen < 42 || buf[0] != TLS_HS_SERVER_HELLO || buf[4] != TLS_MAJOR_VERSION_3) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } if (buf[5] > tls->max_minor_ver) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->minor_ver = buf[5]; if (tls->minor_ver < tls->min_minor_ver) { debug_msg (1, "server only supports TLS smaller than minimum" " [%d:%d] < [%d:%d]", tls->major_ver, tls->minor_ver, buf[4], buf[5]); _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_PROTOCOL_VERSION); return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); } t = buf32_to_u32 (buf+6); debug_msg (3, "server_hello, current time: %lu", (unsigned long)t); memcpy (tls->handshake->randbytes + 32, buf + 6, 32); n = buf[38]; debug_buf (3, "server_hello, random bytes", buf + 6, 32); if (n > 32) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } /* * 38 . 38 session id length * 39 . 38+n session id * 39+n . 40+n chosen ciphersuite * 41+n . 41+n chosen compression alg. * 42+n . 43+n extensions length * 44+n . 44+n+m extensions */ if (tls->in_hslen > 42 + n) { ext_len = buf16_to_size_t (buf + 42 + n); if ((ext_len > 0 && ext_len < 4) || tls->in_hslen != 44 + n + ext_len) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } } suite_id = buf16_to_uint (buf + 39 + n); comp = buf[41 + n]; /* * Initialize update checksum functions */ tls->transform_negotiate->ciphersuite = _ntbtls_ciphersuite_from_id (suite_id); if (!tls->transform_negotiate->ciphersuite) { debug_msg (1, "ciphersuite info for %04x not found", suite_id); return gpg_error (GPG_ERR_INV_ARG); } _ntbtls_optimize_checksum (tls, tls->transform_negotiate->ciphersuite); debug_msg (3, "server_hello, session id len.: %zu", n); debug_buf (3, "server_hello, session id", buf + 39, n); /* * Check if the session can be resumed */ if (tls->renegotiation != TLS_INITIAL_HANDSHAKE || !tls->handshake->resume || !n || tls->session_negotiate->ciphersuite != suite_id || tls->session_negotiate->compression != comp || tls->session_negotiate->length != n || memcmp (tls->session_negotiate->id, buf + 39, n)) { tls->state++; tls->handshake->resume = 0; tls->session_negotiate->start = time (NULL); tls->session_negotiate->ciphersuite = suite_id; tls->session_negotiate->compression = comp; tls->session_negotiate->length = n; memcpy (tls->session_negotiate->id, buf + 39, n); } else { tls->state = TLS_SERVER_CHANGE_CIPHER_SPEC; err = _ntbtls_derive_keys (tls); if (err) { debug_ret (1, "derive_keys", err); return err; } } debug_msg (3, "%s session has been resumed", tls->handshake->resume ? "a" : "no"); debug_msg (1, "server_hello, chosen ciphersuite: %d (%s)", suite_id, _ntbtls_ciphersuite_get_name (suite_id)); debug_msg (3, "server_hello, compress alg.: %d", buf[41 + n]); /* Check that we support the cipher suite. */ ciphersuites = tls->ciphersuite_list[tls->minor_ver]; if (ciphersuites) { for (i=0; ciphersuites[i]; i++) if (ciphersuites[i] == tls->session_negotiate->ciphersuite) break; } if (!ciphersuites || !ciphersuites[i]) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } if (comp != TLS_COMPRESS_NULL && comp != TLS_COMPRESS_DEFLATE) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } tls->session_negotiate->compression = comp; ext = buf + 44 + n; debug_msg (2, "server_hello, total extension length: %zu", ext_len); while (ext_len) { unsigned int ext_id = buf16_to_uint (ext); unsigned int ext_size = buf16_to_uint (ext+2); if (ext_size + 4 > ext_len) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } switch (ext_id) { case TLS_EXT_RENEGOTIATION_INFO: debug_msg (2, "found renegotiation extension"); renegotiation_info_seen = 1; err = parse_renegotiation_info (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_MAX_FRAGMENT_LENGTH: debug_msg (2, "found max_fragment_length extension"); err = parse_max_fragment_length_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_TRUNCATED_HMAC: debug_msg (2, "found truncated_hmac extension"); err = parse_truncated_hmac_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_SESSION_TICKET: debug_msg (2, "found session_ticket extension"); err = parse_session_ticket_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_SUPPORTED_POINT_FORMATS: debug_msg (2, "found supported_point_formats extension"); err = parse_supported_point_formats_ext (tls, ext + 4, ext_size); if (err) return err; break; case TLS_EXT_ALPN: debug_msg (2, "found alpn extension"); err = parse_alpn_ext (tls, ext + 4, ext_size); if (err) return err; break; default: debug_msg (2, "unknown extension found: %d (ignoring)", ext_id); break; } ext_len -= 4 + ext_size; ext += 4 + ext_size; if (ext_len > 0 && ext_len < 4) { debug_msg (1, "bad server_hello message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); } } /* * Renegotiation security checks */ if (tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && tls->allow_legacy_renegotiation == TLS_LEGACY_BREAK_HANDSHAKE) { debug_msg (1, "legacy renegotiation, breaking off handshake"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_SECURE_RENEGOTIATION && !renegotiation_info_seen) { debug_msg (1, "renegotiation_info extension missing (secure)"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && tls->allow_legacy_renegotiation == TLS_LEGACY_NO_RENEGOTIATION) { debug_msg (1, "legacy renegotiation not allowed"); handshake_failure = 1; } else if (tls->renegotiation == TLS_RENEGOTIATION && tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && renegotiation_info_seen) { debug_msg (1, "renegotiation_info extension present (legacy)"); handshake_failure = 1; } if (handshake_failure) { err = _ntbtls_send_fatal_handshake_failure (tls); if (!err) err = gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO); return err; } return 0; } static gpg_error_t parse_server_dh_params (ntbtls_t tls, unsigned char **p, unsigned char *end) { gpg_error_t err; unsigned int nbits; size_t n; /* * Ephemeral DH parameters: * * struct { * opaque dh_p<1..2^16-1>; * opaque dh_g<1..2^16-1>; * opaque dh_Ys<1..2^16-1>; * } ServerDHParams; */ err = _ntbtls_dhm_read_params (tls->handshake->dhm_ctx, *p, end - *p, &n); if (err) { debug_ret (2, "dhm_read_params", err); return err; } *p += n; nbits = _ntbtls_dhm_get_nbits (tls->handshake->dhm_ctx); if (nbits < 1024 || nbits > 4096) { debug_msg (1, "bad server key exchange message (DHM length: %u)", nbits); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } return 0; } static int parse_server_ecdh_params (ntbtls_t tls, unsigned char **p, unsigned char *end) { gpg_error_t err; size_t n; if ((err = _ntbtls_ecdh_read_params (tls->handshake->ecdh_ctx, *p, end - *p, &n))) { debug_ret (1, "ecdh_read_params", err); return err; } *p += n; return 0; } static gpg_error_t parse_server_psk_hint (ntbtls_t tls, unsigned char **p, unsigned char *end) { size_t len; (void)tls; /* * PSK parameters: * * opaque psk_identity_hint<0..2^16-1>; */ if (*p + 1 < end) { debug_msg (1, "bad server key exchange message" " (psk_identity_hint too short)"); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } len = buf16_to_size_t (*p); *p += 2; if ((*p) + len > end) { debug_msg (1, "bad server key exchange message" " (psk_identity_hint too long)"); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } // TODO: Retrieve PSK identity hint and callback to app // *p += len; return 0; } /* * Generate a pre-master secret and encrypt it with the server's RSA key */ static gpg_error_t write_encrypted_pms (ntbtls_t tls, size_t offset, size_t *olen, size_t pms_offset) { gpg_error_t err; size_t len_bytes = tls->minor_ver == TLS_MINOR_VERSION_0 ? 0 : 2; unsigned char *p = tls->handshake->premaster + pms_offset; /* * Generate (part of) the pre-master as * struct { * ProtocolVersion client_version; * opaque random[46]; * } PreMasterSecret; */ p[0] = (unsigned char) tls->max_major_ver; p[1] = (unsigned char) tls->max_minor_ver; gcry_randomize (p + 2, 46, GCRY_STRONG_RANDOM); tls->handshake->pmslen = 48; /* * Now write it out, encrypted */ //FIXME: Need a cert related can_do function. /* if (!_ntbtls_x509_foo_can_do (tls->session_negotiate->peer_chain, GCRY_PK_RSA)) */ /* { */ /* debug_msg (1, "certificate key type mismatch"); */ /* return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); */ /* } */ err = _ntbtls_pk_encrypt (tls->session_negotiate->peer_chain, p, tls->handshake->pmslen, tls->out_msg + offset + len_bytes, olen, TLS_MAX_CONTENT_LEN - offset - len_bytes); if (err) { debug_ret (1, "rsa_pkcs1_encrypt", err); return err; } if (len_bytes == 2) { tls->out_msg[offset + 0] = (unsigned char) (*olen >> 8); tls->out_msg[offset + 1] = (unsigned char) (*olen); *olen += 2; } return 0; } static gpg_error_t parse_signature_algorithm (ntbtls_t tls, unsigned char **p, unsigned char *end, md_algo_t *md_alg, pk_algo_t *pk_alg) { *md_alg = 0; *pk_alg = 0; /* Only in TLS 1.2/1.3 */ if (tls->minor_ver < TLS_MINOR_VERSION_3) { return 0; } if ((*p) + 2 > end) return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); /* * Get hash algorithm */ *md_alg = _ntbtls_md_alg_from_hash ((*p)[0]); if (!*md_alg) { debug_msg (2, "Server used unsupported HashAlgorithm %d", *(p)[0]); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } /* * Get signature algorithm */ *pk_alg = _ntbtls_pk_alg_from_sig ((*p)[1]); if (!*pk_alg) { debug_msg (2, "server used unsupported SignatureAlgorithm %d", (*p)[1]); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } debug_msg (2, "Server used HashAlgo %s", gcry_md_algo_name (*md_alg)); debug_msg (2, "Server used SignAlgo %s", gcry_pk_algo_name (*pk_alg)); *p += 2; return 0; } static gpg_error_t get_ecdh_params_from_cert (ntbtls_t tls) { (void)tls; //FIXME: /* int ret; */ /* const ecp_keypair *peer_key; */ /* if (!pk_can_do (&ssl->session_negotiate->peer_chain->pk, POLARSSL_PK_ECKEY)) */ /* { */ /* debug_msg (1, "server key not ECDH capable"); */ /* return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); */ /* } */ /* peer_key = pk_ec (ssl->session_negotiate->peer_chain->pk); */ /* if ((ret = ecdh_get_params (&ssl->handshake->ecdh_ctx, peer_key, */ /* POLARSSL_ECDH_THEIRS)) != 0) */ /* { */ /* debug_ret (1, ("ecdh_get_params"), ret); */ /* return (ret); */ /* } */ /* if (ssl_check_server_ecdh_params (ssl) != 0) */ /* { */ /* debug_msg (1, "bad server certificate (ECDH curve)"); */ /* return gpg_error (GPG_ERR_BAD_HS_CERT); */ /* } */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } static gpg_error_t read_server_key_exchange (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); unsigned char *p, *end; size_t sig_len, params_len; unsigned char hash[64]; md_algo_t md_alg = 0; size_t hashlen; pk_algo_t pk_alg = 0; if (kex == KEY_EXCHANGE_RSA) { debug_msg (2, "skipping read server_key_exchange"); tls->state++; return 0; } if (kex == KEY_EXCHANGE_ECDH_RSA || kex == KEY_EXCHANGE_ECDH_ECDSA) { err = get_ecdh_params_from_cert (tls); if (err) { debug_ret (1, "get_ecdh_params_from_cert", err); return err; } debug_msg (2, "skipping read server_key_exchange"); tls->state++; return 0; } debug_msg (2, "read server_key_exchange"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* * ServerKeyExchange may be skipped with PSK and RSA-PSK when the server * doesn't use a psk_identity_hint. */ if (tls->in_msg[0] != TLS_HS_SERVER_KEY_EXCHANGE) { if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK) { tls->record_read = 1; goto leave; } debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } p = tls->in_msg + 4; end = tls->in_msg + tls->in_hslen; debug_buf (3, "server_key_exchange", p, tls->in_hslen - 4); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { err = parse_server_psk_hint (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK) ; /* Nothing more to do. */ else if (kex == KEY_EXCHANGE_DHE_RSA || kex == KEY_EXCHANGE_DHE_PSK) { err = parse_server_dh_params (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_PSK || kex == KEY_EXCHANGE_ECDHE_ECDSA) { err = parse_server_ecdh_params (tls, &p, end); if (err) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } if (kex == KEY_EXCHANGE_DHE_RSA || kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_ECDSA) { params_len = p - (tls->in_msg + 4); /* * Handle the digitally-signed structure */ if (tls->minor_ver >= TLS_MINOR_VERSION_3) { err = parse_signature_algorithm (tls, &p, end, &md_alg, &pk_alg); if (err) { debug_msg (1, "bad server_key_exchange message (%d): %s", __LINE__, gpg_strerror (err)); return err; } if (pk_alg != _ntbtls_ciphersuite_get_sig_pk_alg (suite)) { debug_msg (1, "bad server_key_exchange message (%d): %s", __LINE__, gpg_strerror (err)); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } //FIXME: Check that the ECC subtype matches. */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* * Read signature */ sig_len = buf16_to_size_t (p); p += 2; if (end != p + sig_len) { debug_msg (1, "bad server_key_exchange message (%d)", __LINE__); return gpg_error (GPG_ERR_BAD_HS_SERVER_KEX); } debug_buf (3, "signature", p, sig_len); /* * Compute the hash that has been signed */ if (md_alg) { gcry_buffer_t iov[2]; memset (iov, 0, sizeof iov); /* * digitally-signed struct { * opaque client_random[32]; * opaque server_random[32]; * ServerDHParams params; * }; */ iov[0].data = tls->handshake->randbytes; iov[0].len = 64; iov[1].data = tls->in_msg + 4; iov[1].len = params_len; hashlen = gcry_md_get_algo_dlen (md_alg); if (hashlen > sizeof hash) err = gpg_error (GPG_ERR_BUG); else err = gcry_md_hash_buffers (md_alg, 0, hash, iov, 2); if (err) return err; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } debug_buf (3, "parameters hash", hash, hashlen); /* * Verify signature */ err = _ntbtls_pk_verify (tls->session_negotiate->peer_chain, pk_alg, md_alg, hash, hashlen, p, sig_len); debug_ret (1, "pk_verify", err); if (err) return err; } leave: tls->state++; return 0; } static gpg_error_t read_certificate_request (ntbtls_t tls) { gpg_error_t err; unsigned char *buf, *p; size_t n = 0, m = 0; size_t cert_type_len = 0; size_t dn_len = 0; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { debug_msg (2, "skipping read certificate_request"); tls->state++; return 0; } debug_msg (2, "read certificate_request"); /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 4 cert type count * 5 .. m-1 cert types * m .. m+1 sig alg length (TLS 1.2 only) * m+1 .. n-1 SignatureAndHashAlgorithms (TLS 1.2 only) * n .. n+1 length of all DNs * n+2 .. n+3 length of DN 1 * n+4 .. ... Distinguished Name #1 * ... .. ... length of DN 2, etc. */ if (!tls->record_read) { err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } tls->record_read = 1; } tls->client_auth = 0; tls->state++; if (tls->in_msg[0] == TLS_HS_CERTIFICATE_REQUEST) tls->client_auth++; debug_msg (3, "got %s certificate_request", tls->client_auth ? "a" : "no"); if (!tls->client_auth) goto leave; tls->record_read = 0; // TODO: handshake_failure alert for an anonymous server to request // client authentication buf = tls->in_msg; // Retrieve cert types // cert_type_len = buf[4]; n = cert_type_len; if (tls->in_hslen < 6 + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } p = buf + 5; while (cert_type_len > 0) { if (*p == TLS_CERT_TYPE_RSA_SIGN && _ntbtls_x509_can_do (tls_own_key (tls), GCRY_PK_RSA)) { tls->handshake->cert_type = TLS_CERT_TYPE_RSA_SIGN; break; } else if (*p == TLS_CERT_TYPE_ECDSA_SIGN && _ntbtls_x509_can_do (tls_own_key (tls), GCRY_PK_ECDSA)) { tls->handshake->cert_type = TLS_CERT_TYPE_ECDSA_SIGN; break; } else { /* Unsupported cert type, ignore */ } cert_type_len--; p++; } if (tls->minor_ver >= TLS_MINOR_VERSION_3) { /* Ignored, see comments about hash in write_certificate_verify */ // TODO: should check the signature part against our pk_key though size_t sig_alg_len = buf16_to_size_t (buf + 5 + n); p = buf + 7 + n; m += 2; n += sig_alg_len; if (tls->in_hslen < 6 + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } } /* Ignore certificate_authorities, we only have one cert anyway */ // TODO: should not send cert if no CA matches dn_len = buf16_to_size_t (buf + 5 + m + n); n += dn_len; if (tls->in_hslen != 7 + m + n) { debug_msg (1, "bad certificate_request message"); return gpg_error (GPG_ERR_BAD_HS_CERT_REQ); } leave: return 0; } static gpg_error_t read_server_hello_done (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "read server_hello_done"); if (!tls->record_read) { err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad server_hello_done message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } } tls->record_read = 0; if (tls->in_hslen != 4 || tls->in_msg[0] != TLS_HS_SERVER_HELLO_DONE) { debug_msg (1, "bad server_hello_done message"); return gpg_error (GPG_ERR_BAD_HS_SERVER_HELLO_DONE); } tls->state++; return 0; } static gpg_error_t write_client_key_exchange (ntbtls_t tls) { gpg_error_t err; size_t i, n; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); debug_msg (2, "write client_key_exchange"); if (kex == KEY_EXCHANGE_DHE_RSA) { /* * DHM key exchange -- send G^X mod P * * We don't have the remaining size of the buffer available, * thus we use a value which will always fit into our buffer. */ i = 4; err = _ntbtls_dhm_make_public (tls->handshake->dhm_ctx, tls->out_msg + i, 514, &n); if (err) { debug_ret (1, "dhm_make_public", err); return err; } err = _ntbtls_dhm_calc_secret (tls->handshake->dhm_ctx, tls->handshake->premaster, TLS_PREMASTER_SIZE, &tls->handshake->pmslen); if (err) { debug_ret (1, "dhm_calc_secret", err); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_RSA || kex == KEY_EXCHANGE_ECDHE_ECDSA || kex == KEY_EXCHANGE_ECDH_RSA || kex == KEY_EXCHANGE_ECDH_ECDSA) { /* * ECDH key exchange -- send client public value */ i = 4; err = _ntbtls_ecdh_make_public (tls->handshake->ecdh_ctx, tls->out_msg + i, 1000, &n); if (err) { debug_ret (1, "ecdh_make_public", err); return err; } err = _ntbtls_ecdh_calc_secret (tls->handshake->ecdh_ctx, tls->handshake->premaster, TLS_PREMASTER_SIZE, &tls->handshake->pmslen); if (err) { debug_ret (1, "ecdh_calc_secret", err); return err; } } else if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { /* * opaque psk_identity<0..2^16-1>; */ if (!tls->psk || !tls->psk_identity) return gpg_error (GPG_ERR_NO_SECKEY); i = 4; n = tls->psk_identity_len; tls->out_msg[i++] = (unsigned char) (n >> 8); tls->out_msg[i++] = (unsigned char) (n); memcpy (tls->out_msg + i, tls->psk_identity, tls->psk_identity_len); i += tls->psk_identity_len; if (kex == KEY_EXCHANGE_PSK) { n = 0; } else if (kex == KEY_EXCHANGE_RSA_PSK) { err = write_encrypted_pms (tls, i, &n, 2); if (err) return err; } else if (kex == KEY_EXCHANGE_DHE_PSK) { /* * ClientDiffieHellmanPublic public (DHM send G^X mod P) */ n = 0; //FIXME: tls->handshake->dhm_ctx.len; tls->out_msg[i++] = (unsigned char) (n >> 8); tls->out_msg[i++] = (unsigned char) (n); /* err = dhm_make_public (&tls->handshake->dhm_ctx, */ /* (int) mpi_size (&tls->handshake->dhm_ctx.P), */ /* &tls->out_msg[i], n); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "dhm_make_public", err); return err; } } else if (kex == KEY_EXCHANGE_ECDHE_PSK) { /* * ClientECDiffieHellmanPublic public; */ /* err = ecdh_make_public (&tls->handshake->ecdh_ctx, &n, */ /* &tls->out_msg[i], TLS_MAX_CONTENT_LEN - i); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "ecdh_make_public", err); return err; } /* SSL_DEBUG_ECP (3, "ECDH: Q", &tls->handshake->ecdh_ctx.Q); */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } err = _ntbtls_psk_derive_premaster (tls, kex); if (err) { debug_ret (1, "psk_derive_premaster", err); return err; } } else if (kex == KEY_EXCHANGE_RSA) { i = 4; err = write_encrypted_pms (tls, i, &n, 0); if (err) return err; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } err = _ntbtls_derive_keys (tls); if (err) { debug_ret (1, "derive_keys", err); return err; } tls->out_msglen = i + n; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CLIENT_KEY_EXCHANGE; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t write_certificate_verify (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); size_t n = 0; size_t offset = 0; unsigned char hash[48]; unsigned char *hash_start = hash; md_algo_t md_alg = 0; unsigned int hashlen; if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_RSA_PSK || kex == KEY_EXCHANGE_ECDHE_PSK || kex == KEY_EXCHANGE_DHE_PSK || !tls->client_auth || !tls_own_cert (tls)) { debug_msg (2, "skipping write certificate_verify"); tls->state++; return 0; } debug_msg (2, "write certificate_verify"); if (!tls_own_key (tls)) { debug_msg (1, "got no private key"); return gpg_error (GPG_ERR_NO_SECKEY); } /* * Make an RSA signature of the handshake digests */ tls->handshake->calc_verify (tls, hash); if (tls->minor_ver >= TLS_MINOR_VERSION_3) { /* * digitally-signed struct { * opaque handshake_messages[handshake_messages_length]; * }; * * Taking shortcut here. We assume that the server always allows the * PRF Hash function and has sent it in the allowed signature * algorithms list received in the Certificate Request message. * * Until we encounter a server that does not, we will take this * shortcut. * * Reason: Otherwise we should have running hashes for SHA512 and SHA224 * in order to satisfy 'weird' needs from the server side. */ if (_ntbtls_ciphersuite_get_mac (tls->transform_negotiate->ciphersuite) == GCRY_MAC_HMAC_SHA384) { md_alg = GCRY_MD_SHA384; tls->out_msg[4] = TLS_HASH_SHA384; hashlen = 48; } else { md_alg = GCRY_MD_SHA256; tls->out_msg[4] = TLS_HASH_SHA256; hashlen = 32; } tls->out_msg[5] = 0; //FIXME: ssl_sig_from_pk (ssl_own_key (tls)); offset = 2; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* err = pk_sign (tls_own_key (tls), md_alg, hash_start, hashlen, */ /* tls->out_msg + 6 + offset, &n); */ (void)md_alg; (void)hash_start; (void)hashlen; err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "pk_sign", err); return err; } tls->out_msg[4 + offset] = (unsigned char) (n >> 8); tls->out_msg[5 + offset] = (unsigned char) (n); tls->out_msglen = 6 + n + offset; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CERTIFICATE_VERIFY; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } static gpg_error_t parse_new_session_ticket (ntbtls_t tls) { gpg_error_t err; uint32_t lifetime; size_t ticket_len; unsigned char *ticket; debug_msg (2, "read new_session_ticket"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* * struct { * uint32 ticket_lifetime_hint; * opaque ticket<0..2^16-1>; * } NewSessionTicket; * * 0 . 0 handshake message type * 1 . 3 handshake message length * 4 . 7 ticket_lifetime_hint * 8 . 9 ticket_len (n) * 10 . 9+n ticket content */ if (tls->in_msg[0] != TLS_HS_NEW_SESSION_TICKET || tls->in_hslen < 10) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_BAD_TICKET); } lifetime = buf32_to_u32 (tls->in_msg + 4); ticket_len = buf16_to_size_t (tls->in_msg + 8); if (ticket_len + 10 != tls->in_hslen) { debug_msg (1, "bad new_session_ticket message"); return gpg_error (GPG_ERR_BAD_TICKET); } debug_msg (3, "ticket length: %zu", ticket_len); /* We're not waiting for a NewSessionTicket message any more */ tls->handshake->new_session_ticket = 0; /* * Zero-length ticket means the server changed his mind and doesn't want * to send a ticket after all, so just forget it */ if (!ticket_len) return 0; wipememory (tls->session_negotiate->ticket, tls->session_negotiate->ticket_len); free (tls->session_negotiate->ticket); tls->session_negotiate->ticket = NULL; tls->session_negotiate->ticket_len = 0; ticket = malloc (ticket_len); if (!ticket) { err = gpg_error_from_syserror (); debug_msg (1, "ticket malloc failed"); return err; } memcpy (ticket, tls->in_msg + 10, ticket_len); tls->session_negotiate->ticket = ticket; tls->session_negotiate->ticket_len = ticket_len; tls->session_negotiate->ticket_lifetime = lifetime; /* * RFC 5077 section 3.4: * "If the client receives a session ticket from the server, then it * discards any Session ID that was sent in the ServerHello." */ debug_msg (3, "ticket in use, discarding session id"); tls->session_negotiate->length = 0; return 0; } /* * SSL handshake -- client side -- single step */ gpg_error_t _ntbtls_handshake_client_step (ntbtls_t tls) { gpg_error_t err; if (tls->state == TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_STATE); debug_msg (2, "client state: %d (%s)", tls->state, _ntbtls_state2str (tls->state)); err = _ntbtls_flush_output (tls); if (err) return err; switch (tls->state) { case TLS_HELLO_REQUEST: tls->state = TLS_CLIENT_HELLO; break; /* * ==> ClientHello */ case TLS_CLIENT_HELLO: err = write_client_hello (tls); break; /* * <== ServerHello * Certificate * ( ServerKeyExchange ) * ( CertificateRequest ) * ServerHelloDone */ case TLS_SERVER_HELLO: err = read_server_hello (tls); break; case TLS_SERVER_CERTIFICATE: err = _ntbtls_read_certificate (tls); break; case TLS_SERVER_KEY_EXCHANGE: err = read_server_key_exchange (tls); break; case TLS_CERTIFICATE_REQUEST: err = read_certificate_request (tls); break; case TLS_SERVER_HELLO_DONE: err = read_server_hello_done (tls); break; /* * ==> ( Certificate/Alert ) * ClientKeyExchange * ( CertificateVerify ) * ChangeCipherSpec * Finished */ case TLS_CLIENT_CERTIFICATE: err = _ntbtls_write_certificate (tls); break; case TLS_CLIENT_KEY_EXCHANGE: err = write_client_key_exchange (tls); break; case TLS_CERTIFICATE_VERIFY: err = write_certificate_verify (tls); break; case TLS_CLIENT_CHANGE_CIPHER_SPEC: err = _ntbtls_write_change_cipher_spec (tls); break; case TLS_CLIENT_FINISHED: err = _ntbtls_write_finished (tls); break; /* * <== ( NewSessionTicket ) * ChangeCipherSpec * Finished */ case TLS_SERVER_CHANGE_CIPHER_SPEC: if (tls->handshake->new_session_ticket) err = parse_new_session_ticket (tls); else err = _ntbtls_read_change_cipher_spec (tls); break; case TLS_SERVER_FINISHED: err = _ntbtls_read_finished (tls); break; case TLS_FLUSH_BUFFERS: debug_msg (2, "handshake: done"); tls->state = TLS_HANDSHAKE_WRAPUP; break; case TLS_HANDSHAKE_WRAPUP: _ntbtls_handshake_wrapup (tls); break; default: debug_msg (1, "invalid state %d", tls->state); err = gpg_error (GPG_ERR_INV_STATE); break; } return err; } diff --git a/src/protocol.c b/src/protocol.c index f92f9ef..4b2eecc 100644 --- a/src/protocol.c +++ b/src/protocol.c @@ -1,4229 +1,4111 @@ /* protocol.c - TLS 1.2 protocol implementation * Copyright (C) 2006-2014, Brainspark B.V. * Copyright (C) 2014, 2017 g10 code GmbH * * This file is part of NTBTLS * * NTBTLS 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. * * NTBTLS 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 this program; if not, see . * * This file was part of PolarSSL (http://www.polarssl.org). Former * Lead Maintainer: Paul Bakker . * Please do not file bug reports to them but to the address given in * the file AUTHORS in the top directory of NTBTLS. */ #include #include #include #include "ntbtls-int.h" #include "ciphersuites.h" static void transform_deinit (transform_t transform); static void session_deinit (session_t session); static void handshake_params_deinit (handshake_params_t handshake); static void ticket_keys_deinit (ticket_keys_t tkeys); static void update_checksum_sha256 (ntbtls_t, const unsigned char *, size_t); static void calc_verify_tls_sha256 (ntbtls_t, unsigned char *); static void calc_finished_tls_sha256 (ntbtls_t, unsigned char *, int); static void calc_verify_tls_sha384 (ntbtls_t, unsigned char *); static void calc_finished_tls_sha384 (ntbtls_t, unsigned char *, int); static const char * alert_msg_to_string (int msgno) { switch (msgno) { case TLS_ALERT_MSG_CLOSE_NOTIFY: return "close notify"; case TLS_ALERT_MSG_UNEXPECTED_MESSAGE: return "unexpected msg"; case TLS_ALERT_MSG_BAD_RECORD_MAC: return "bad record mac "; case TLS_ALERT_MSG_DECRYPTION_FAILED: return "decryption failed"; case TLS_ALERT_MSG_RECORD_OVERFLOW: return "record overflow"; case TLS_ALERT_MSG_DECOMPRESSION_FAILURE:return "decompression failure"; case TLS_ALERT_MSG_HANDSHAKE_FAILURE: return "handshake failure"; case TLS_ALERT_MSG_NO_CERT: return "no cert"; case TLS_ALERT_MSG_BAD_CERT: return "bad cert"; case TLS_ALERT_MSG_UNSUPPORTED_CERT: return "unsupported cert"; case TLS_ALERT_MSG_CERT_REVOKED: return "cert revoked"; case TLS_ALERT_MSG_CERT_EXPIRED: return "cert expired"; case TLS_ALERT_MSG_CERT_UNKNOWN: return "cert unknown"; case TLS_ALERT_MSG_ILLEGAL_PARAMETER: return "illegal param"; case TLS_ALERT_MSG_UNKNOWN_CA: return "unknown CA"; case TLS_ALERT_MSG_ACCESS_DENIED: return "access denied"; case TLS_ALERT_MSG_DECODE_ERROR: return "decode error"; case TLS_ALERT_MSG_DECRYPT_ERROR: return "decrypt error"; case TLS_ALERT_MSG_EXPORT_RESTRICTION: return "export restriction"; case TLS_ALERT_MSG_PROTOCOL_VERSION: return "protocol version"; case TLS_ALERT_MSG_INSUFFICIENT_SECURITY:return "insufficient security"; case TLS_ALERT_MSG_INTERNAL_ERROR: return "internal error"; case TLS_ALERT_MSG_USER_CANCELED: return "user canceled"; case TLS_ALERT_MSG_NO_RENEGOTIATION: return "no renegotiation"; case TLS_ALERT_MSG_UNSUPPORTED_EXT: return "unsupported extenstion"; case TLS_ALERT_MSG_UNRECOGNIZED_NAME: return "unsupported name"; case TLS_ALERT_MSG_UNKNOWN_PSK_IDENTITY: return "unknown PSK identify"; case TLS_ALERT_MSG_NO_APPLICATION_PROTOCOL:return "no application protocol"; default: return "[?]"; } } /* * Convert max_fragment_length codes to length. * RFC 6066 says: * enum{ * 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255) * } MaxFragmentLength; * and we add 0 -> extension unused */ static unsigned int mfl_code_to_length[] = { TLS_MAX_CONTENT_LEN, /* TLS_MAX_FRAG_LEN_NONE */ 512, /* TLS_MAX_FRAG_LEN_512 */ 1024, /* TLS_MAX_FRAG_LEN_1024 */ 2048, /* TLS_MAX_FRAG_LEN_2048 */ 4096 /* TLS_MAX_FRAG_LEN_4096 */ }; /* Return true is MODE is an AEAD mode. */ static int is_aead_mode (cipher_mode_t mode) { switch (mode) { case GCRY_CIPHER_MODE_GCM: case GCRY_CIPHER_MODE_CCM: return 1; default: return 0; } } const char * _ntbtls_state2str (tls_state_t state) { const char *s = "?"; switch (state) { case TLS_HELLO_REQUEST: s = "hello_request"; break; case TLS_CLIENT_HELLO: s = "client_hello"; break; case TLS_SERVER_HELLO: s = "server_hello"; break; case TLS_SERVER_CERTIFICATE: s = "server_certificate"; break; case TLS_SERVER_KEY_EXCHANGE: s = "server_key_exchange"; break; case TLS_CERTIFICATE_REQUEST: s = "certificate_request"; break; case TLS_SERVER_HELLO_DONE: s = "server_hello_done"; break; case TLS_CLIENT_CERTIFICATE: s = "client_certificate"; break; case TLS_CLIENT_KEY_EXCHANGE: s = "client_key_exchange"; break; case TLS_CERTIFICATE_VERIFY: s = "certificate_verify"; break; case TLS_CLIENT_CHANGE_CIPHER_SPEC: s = "client_change_cipher_spec"; break; case TLS_CLIENT_FINISHED: s = "client_finished"; break; case TLS_SERVER_CHANGE_CIPHER_SPEC: s = "server_change_cipher_spec"; break; case TLS_SERVER_FINISHED: s = "server_finished"; break; case TLS_FLUSH_BUFFERS: s = "flush_buffers"; break; case TLS_HANDSHAKE_WRAPUP: s = "handshake_wrapup"; break; case TLS_HANDSHAKE_OVER: s = "handshake_over"; break; case TLS_SERVER_NEW_SESSION_TICKET: s = "server_new_session_tickets"; break; } return s; } static gpg_error_t session_copy (session_t dst, const session_t src) { session_deinit (dst); memcpy (dst, src, sizeof *src); if (src->peer_chain) { /* int ret; */ //FIXME: Use libksba /* dst->peer_cert = malloc (sizeof *dst->peer_cert); */ /* if (!dst->peer_cert) */ /* return gpg_error_from_syserror (); */ /* x509_crt_init (dst->peer_cert); */ /* if ((ret = x509_crt_parse_der (dst->peer_cert, src->peer_cert->raw.p, */ /* src->peer_cert->raw.len)) != 0) */ /* { */ /* free (dst->peer_cert); */ /* dst->peer_cert = NULL; */ /* return (ret); */ /* } */ } if (src->ticket) { dst->ticket = malloc (src->ticket_len); if (!dst->ticket) return gpg_error_from_syserror (); memcpy (dst->ticket, src->ticket, src->ticket_len); } return 0; } /* * output = HMAC-SHA-NNN( hmac key, input buffer ) * * The used algorithm depends on OUTPUTSIZE which is expected in bytes. */ static gpg_error_t sha_hmac (const unsigned char *key, size_t keylen, const unsigned char *input, size_t inputlen, unsigned char *output, int outputsize) { gpg_error_t err; gcry_mac_hd_t hd; size_t macoutlen; int algo; switch (outputsize) { case 32: algo = GCRY_MAC_HMAC_SHA256; break; case 48: algo = GCRY_MAC_HMAC_SHA384; break; case 64: algo = GCRY_MAC_HMAC_SHA512; break; default: return gpg_error (GPG_ERR_MAC_ALGO); } err = gcry_mac_open (&hd, algo, 0, NULL); if (!err) { err = gcry_mac_setkey (hd, key, keylen); if (!err) { err = gcry_mac_write (hd, input, inputlen); if (!err) { macoutlen = outputsize; err = gcry_mac_read (hd, output, &macoutlen); } } gcry_mac_close (hd); } return err; } /* * Key material generation */ static gpg_error_t do_tls_prf (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen, size_t hashlen) { gpg_error_t err; size_t nb; size_t i, j, k; unsigned char tmp[128]; unsigned char h_i[64]; if (sizeof (tmp) < hashlen + strlen (label) + rlen) return gpg_error (GPG_ERR_INV_ARG); nb = strlen (label); memcpy (tmp + hashlen, label, nb); memcpy (tmp + hashlen + nb, random, rlen); nb += rlen; /* * Compute P_(secret, label + random)[0..dlen] */ err = sha_hmac (secret, slen, tmp + hashlen, nb, tmp, hashlen); if (err) return err; for (i = 0; i < dlen; i += hashlen) { err = sha_hmac (secret, slen, tmp, hashlen + nb, h_i, hashlen); if (err) return err; err = sha_hmac (secret, slen, tmp, hashlen, tmp, hashlen); if (err) return err; k = (i + hashlen > dlen) ? dlen % hashlen : hashlen; for (j = 0; j < k; j++) dstbuf[i + j] = h_i[j]; } wipememory (tmp, sizeof (tmp)); wipememory (h_i, hashlen); return 0; } static gpg_error_t tls_prf_sha256 (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { return do_tls_prf (secret, slen, label, random, rlen, dstbuf, dlen, 32); } static gpg_error_t tls_prf_sha384 (const unsigned char *secret, size_t slen, const char *label, const unsigned char *random, size_t rlen, unsigned char *dstbuf, size_t dlen) { return do_tls_prf (secret, slen, label, random, rlen, dstbuf, dlen, 48); } gpg_error_t _ntbtls_derive_keys (ntbtls_t tls) { gpg_error_t err; unsigned char tmp[64]; unsigned char keyblk[256]; unsigned char *key1; unsigned char *key2; unsigned char *mac_enc; unsigned char *mac_dec; size_t iv_copy_len; cipher_algo_t cipher; cipher_mode_t ciphermode; mac_algo_t mac; session_t session = tls->session_negotiate; transform_t transform = tls->transform_negotiate; handshake_params_t handshake = tls->handshake; debug_msg (2, "derive keys"); if (tls->minor_ver < TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } cipher = _ntbtls_ciphersuite_get_cipher (transform->ciphersuite, &ciphermode); if (!cipher || !ciphermode) { debug_msg (1, "cipher algo not found"); return gpg_error (GPG_ERR_INV_ARG); } mac = _ntbtls_ciphersuite_get_mac (transform->ciphersuite); if (!mac) { debug_msg (1, "mac algo not found"); return gpg_error (GPG_ERR_INV_ARG); } /* * Set appropriate PRF function and other TLS functions */ if (mac == GCRY_MAC_HMAC_SHA384) { handshake->tls_prf = tls_prf_sha384; handshake->calc_verify = calc_verify_tls_sha384; handshake->calc_finished = calc_finished_tls_sha384; } else { handshake->tls_prf = tls_prf_sha256; handshake->calc_verify = calc_verify_tls_sha256; handshake->calc_finished = calc_finished_tls_sha256; } /* * TLSv1+: * master = PRF( premaster, "master secret", randbytes )[0..47] */ if (!handshake->resume) { debug_buf (3, "premaster secret", handshake->premaster, handshake->pmslen); handshake->tls_prf (handshake->premaster, handshake->pmslen, "master secret", handshake->randbytes, 64, session->master, 48); wipememory (handshake->premaster, sizeof (handshake->premaster)); } else debug_msg (3, "no premaster (session resumed)"); /* * Swap the client and server random values. */ memcpy (tmp, handshake->randbytes, 64); memcpy (handshake->randbytes, tmp + 32, 32); memcpy (handshake->randbytes + 32, tmp, 32); wipememory (tmp, sizeof (tmp)); /* * TLSv1: * key block = PRF( master, "key expansion", randbytes ) */ handshake->tls_prf (session->master, 48, "key expansion", handshake->randbytes, 64, keyblk, 256); debug_msg (3, "ciphersuite = %s", _ntbtls_ciphersuite_get_name (session->ciphersuite)); debug_buf (3, "master secret", session->master, 48); debug_buf (4, "random bytes", handshake->randbytes, 64); debug_buf (4, "key block", keyblk, 256); wipememory (handshake->randbytes, sizeof (handshake->randbytes)); /* * Determine the appropriate key, IV and MAC length. */ transform->keylen = gcry_cipher_get_algo_keylen (cipher); /* FIXME: Check that KEYLEN has an upper bound. 2015-06-23 wk: Why? */ if (is_aead_mode (ciphermode)) { transform->maclen = 0; transform->ivlen = 12; transform->fixed_ivlen = 4; /* Minimum length is expicit IV + tag */ transform->minlen = (transform->ivlen - transform->fixed_ivlen + ((_ntbtls_ciphersuite_get_flags (transform->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16)); } else { size_t blklen = gcry_cipher_get_algo_blklen (cipher); /* Initialize HMAC contexts */ /* Fixme: Check whether the context may really be open. */ gcry_mac_close (transform->mac_ctx_enc); err = gcry_mac_open (&transform->mac_ctx_enc, mac, 0, NULL); if (!err) { gcry_mac_close (transform->mac_ctx_dec); err = gcry_mac_open (&transform->mac_ctx_dec, mac, 0, NULL); } if (err) { debug_ret (1, "gcry_mac_open", err); return err; } /* Get MAC length */ transform->maclen = gcry_mac_get_algo_maclen (mac); if (transform->maclen < TLS_TRUNCATED_HMAC_LEN) { debug_bug (); return gpg_error (GPG_ERR_BUG); } /* * If HMAC is to be truncated, we shall keep the leftmost bytes, * (rfc 6066 page 13 or rfc 2104 section 4), * so we only need to adjust the length here. */ if (session->use_trunc_hmac) transform->maclen = TLS_TRUNCATED_HMAC_LEN; /* IV length. According to RFC-5246, Appendix C, we shall use the block length of the IV length. */ transform->ivlen = blklen; /* Minimum length for GenericBlockCipher: * First multiple of blocklen greater than maclen + IV. */ transform->minlen = (transform->maclen + blklen - (transform->maclen % blklen) + transform->ivlen); } debug_msg (3, "keylen: %d, minlen: %zu, ivlen: %zu, maclen: %zu", transform->keylen, transform->minlen, transform->ivlen, transform->maclen); /* * Finally setup the cipher contexts, IVs and MAC secrets. */ if (tls->is_client) { key1 = keyblk + transform->maclen * 2; key2 = keyblk + transform->maclen * 2 + transform->keylen; mac_enc = keyblk; mac_dec = keyblk + transform->maclen; /* * This is not used in TLS v1.1. FIXME: Check and remove. */ iv_copy_len = (transform->fixed_ivlen ? transform->fixed_ivlen : transform->ivlen); memcpy (transform->iv_enc, key2 + transform->keylen, iv_copy_len); memcpy (transform->iv_dec, key2 + transform->keylen + iv_copy_len, iv_copy_len); } else { key1 = keyblk + transform->maclen * 2 + transform->keylen; key2 = keyblk + transform->maclen * 2; mac_enc = keyblk + transform->maclen; mac_dec = keyblk; /* * This is not used in TLS v1.1. FIXME: Check and remove */ iv_copy_len = (transform->fixed_ivlen ? transform->fixed_ivlen : transform->ivlen); memcpy (transform->iv_dec, key1 + transform->keylen, iv_copy_len); memcpy (transform->iv_enc, key1 + transform->keylen + iv_copy_len, iv_copy_len); } if (!is_aead_mode (ciphermode)) { err = gcry_mac_setkey (transform->mac_ctx_enc, mac_enc, transform->maclen); if (!err) err = gcry_mac_setkey (transform->mac_ctx_dec, mac_dec, transform->maclen); if (err) { debug_ret (1, "gcry_mac_setkey", err); return err; } } gcry_cipher_close (transform->cipher_ctx_enc); err = gcry_cipher_open (&transform->cipher_ctx_enc, cipher, ciphermode, 0); if (!err) { gcry_cipher_close (transform->cipher_ctx_dec); err = gcry_cipher_open (&transform->cipher_ctx_dec, cipher, ciphermode,0); } if (err) { debug_ret (1, "gcry_cipher_open", err); return err; } transform->cipher_mode_enc = ciphermode; transform->cipher_mode_dec = ciphermode; err = gcry_cipher_setkey (transform->cipher_ctx_enc, key1, transform->keylen); if (!err) err = gcry_cipher_setkey (transform->cipher_ctx_dec, key2, transform->keylen); if (err) { debug_ret (1, "cipher_setkey", err); return err; } wipememory (keyblk, sizeof (keyblk)); /* Initialize compression. */ if (session->compression == TLS_COMPRESS_DEFLATE) { /* if (tls->compress_buf == NULL) */ /* { */ /* deboug_msg (3, "Allocating compression buffer"); */ /* ssl->compress_buf = malloc (SSL_BUFFER_LEN); */ /* if (!ssl->compress_buf) */ /* { */ /* err = gpg_error_from_syserror (); */ /* debug_msg (1, "malloc(%d bytes) failed", SSL_BUFFER_LEN); */ /* return err; */ /* } */ /* } */ /* debug_msg (3, "Initializing zlib states"); */ /* memset (&transform->ctx_deflate, 0, sizeof (transform->ctx_deflate));*/ /* memset (&transform->ctx_inflate, 0, sizeof (transform->ctx_inflate));*/ /* if (deflateInit (&transform->ctx_deflate, */ /* Z_DEFAULT_COMPRESSION) != Z_OK || */ /* inflateInit (&transform->ctx_inflate) != Z_OK) */ { debug_msg (1, "Failed to initialize compression"); return gpg_error (GPG_ERR_COMPR_FAILED); } } return 0; } static void calc_verify_tls (gcry_md_hd_t md_input, md_algo_t md_alg, unsigned char *hash, size_t hashlen) { gpg_error_t err; gcry_md_hd_t md; char *p; debug_msg (2, "calc_verify_tls sha%zu", hashlen*8); err = gcry_md_copy (&md, md_input); if (err) { debug_ret (1, "calc_verify_tls", err); memset (hash, 0, hashlen); return; } p = gcry_md_read (md, md_alg); if (!p) { debug_bug (); memset (hash, 0, hashlen); gcry_md_close (md); return; } memcpy (hash, p, hashlen); gcry_md_close (md); debug_buf (3, "calculated verify result", hash, hashlen); debug_msg (3, "calc_verify_tls sha%zu", hashlen*8); } static void calc_verify_tls_sha256 (ntbtls_t tls, unsigned char *hash) { calc_verify_tls (tls->handshake->fin_sha256, GCRY_MD_SHA256, hash, 32); } static void calc_verify_tls_sha384 (ntbtls_t tls, unsigned char *hash) { calc_verify_tls (tls->handshake->fin_sha512, GCRY_MD_SHA384, hash, 48); } gpg_error_t _ntbtls_psk_derive_premaster (ntbtls_t tls, key_exchange_type_t kex) { gpg_error_t err; unsigned char *p = tls->handshake->premaster; unsigned char *end = p + sizeof (tls->handshake->premaster); /* * PMS = struct { * opaque other_secret<0..2^16-1>; * opaque psk<0..2^16-1>; * }; * with "other_secret" depending on the particular key exchange */ if (kex == KEY_EXCHANGE_PSK) { if (end - p < 2 + (int) tls->psk_len) return gpg_error (GPG_ERR_INV_ARG); *(p++) = (unsigned char) (tls->psk_len >> 8); *(p++) = (unsigned char) (tls->psk_len); p += tls->psk_len; } else if (kex == KEY_EXCHANGE_RSA_PSK) { /* * other_secret already set by the ClientKeyExchange message, * and is 48 bytes long */ *p++ = 0; *p++ = 48; p += 48; } else if (kex == KEY_EXCHANGE_DHE_PSK) { size_t len = end - (p + 2); /* Write length only when we know the actual value. */ /* err = dhm_calc_secret (&tls->handshake->dhm_ctx, p + 2, &len); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "dhm_calc_secret", err); return err; } *(p++) = (unsigned char) (len >> 8); *(p++) = (unsigned char) (len); p += len; /* SSL_DEBUG_MPI (3, "DHM: K ", &tls->handshake->dhm_ctx.K); */ } else if (kex == KEY_EXCHANGE_ECDHE_PSK) { size_t zlen = 0; /* err = ecdh_calc_secret (&tls->handshake->ecdh_ctx, &zlen, */ /* p + 2, end - (p + 2)); */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); if (err) { debug_ret (1, "ecdh_calc_secret", err); return err; } *(p++) = (unsigned char) (zlen >> 8); *(p++) = (unsigned char) (zlen); p += zlen; /* SSL_DEBUG_MPI (3, "ECDH: z", &tls->handshake->ecdh_ctx.z); */ } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } /* opaque psk<0..2^16-1>; */ if (end - p < 2 + (int) tls->psk_len) return gpg_error (GPG_ERR_INV_ARG); *(p++) = (unsigned char) (tls->psk_len >> 8); *(p++) = (unsigned char) (tls->psk_len); memcpy (p, tls->psk, tls->psk_len); p += tls->psk_len; tls->handshake->pmslen = p - tls->handshake->premaster; return 0; } /* * Encryption/decryption functions */ static gpg_error_t encrypt_buf (ntbtls_t tls) { gpg_error_t err; size_t tmplen, i; cipher_mode_t mode = tls->transform_out->cipher_mode_enc; debug_msg (2, "encrypt buf"); if (tls->minor_ver < TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_BUG); } /* * Add MAC before encrypt, except for AEAD modes */ if (!is_aead_mode (mode)) { err = gcry_mac_write (tls->transform_out->mac_ctx_enc, tls->out_ctr, 13); if (!err) err = gcry_mac_write (tls->transform_out->mac_ctx_enc, tls->out_msg, tls->out_msglen); tmplen = tls->transform_out->maclen; if (!err) err = gcry_mac_read (tls->transform_out->mac_ctx_enc, tls->out_msg + tls->out_msglen, &tmplen); if (!err) err = gcry_mac_reset (tls->transform_out->mac_ctx_enc); if (err) { debug_ret (1, "encrypt_buf: MACing failed", err); return err; } debug_buf (4, "computed mac", tls->out_msg + tls->out_msglen, tls->transform_out->maclen); tls->out_msglen += tls->transform_out->maclen; } /* * Encrypt */ if (is_aead_mode (mode)) { size_t enc_msglen; unsigned char *enc_msg; unsigned char add_data[13]; unsigned char taglen; unsigned char iv[12]; taglen = (_ntbtls_ciphersuite_get_flags (tls->transform_out->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16; memcpy (add_data, tls->out_ctr, 8); add_data[8] = tls->out_msgtype; add_data[9] = tls->major_ver; add_data[10] = tls->minor_ver; add_data[11] = (tls->out_msglen >> 8) & 0xFF; add_data[12] = tls->out_msglen & 0xFF; debug_buf (4, "additional data used for AEAD", add_data, 13); /* * Generate IV */ memcpy (iv, tls->transform_out->iv_enc, tls->transform_out->fixed_ivlen); memcpy (iv + tls->transform_out->fixed_ivlen, tls->out_ctr, 8); memcpy (tls->out_iv, tls->out_ctr, 8); debug_buf (4, "IV used (internal)", iv, tls->transform_out->ivlen); debug_buf (4, "IV used (transmitted)", tls->out_iv, tls->transform_out->ivlen - tls->transform_out->fixed_ivlen); /* * Fix pointer positions and message length with added IV */ enc_msg = tls->out_msg; enc_msglen = tls->out_msglen; tls->out_msglen += (tls->transform_out->ivlen - tls->transform_out->fixed_ivlen); debug_msg (3, "before encrypt: msglen = %zu, " "including %d bytes of padding", enc_msglen, 0); debug_buf (4, "before encrypt: output payload", tls->out_msg, enc_msglen); err = gcry_cipher_reset (tls->transform_out->cipher_ctx_enc); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_enc, iv, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } /* * Encrypt and authenticate */ err = gcry_cipher_authenticate (tls->transform_out->cipher_ctx_enc, add_data, 13); if (err) { debug_ret (1, "cipher_authenticate", err); return err; } err = gcry_cipher_encrypt (tls->transform_out->cipher_ctx_enc, enc_msg, enc_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_encrypt", err); return err; } err = gcry_cipher_gettag (tls->transform_out->cipher_ctx_enc, enc_msg + enc_msglen, taglen); if (err) { debug_ret (1, "cipher_gettag", err); return err; } tls->out_msglen += taglen; debug_buf (4, "after encrypt: payload", enc_msg, enc_msglen); debug_buf (4, "after encrypt: tag", enc_msg + enc_msglen, taglen); } else if (mode == GCRY_CIPHER_MODE_CBC) { unsigned char *enc_msg; size_t enc_msglen, padlen; padlen = (tls->transform_out->ivlen - ((tls->out_msglen + 1) % tls->transform_out->ivlen)); if (padlen == tls->transform_out->ivlen) padlen = 0; for (i = 0; i <= padlen; i++) tls->out_msg[tls->out_msglen + i] = (unsigned char) padlen; tls->out_msglen += padlen + 1; enc_msglen = tls->out_msglen; enc_msg = tls->out_msg; /* * Prepend per-record IV for block cipher in TLS v1.1 and up as per * Method 1 (RFC-5246, 6.2.3.2) */ /* Generate IV. */ gcry_create_nonce (tls->transform_out->iv_enc, tls->transform_out->ivlen); memcpy (tls->out_iv, tls->transform_out->iv_enc, tls->transform_out->ivlen); /* Fix pointer positions and message length with added IV. */ enc_msg = tls->out_msg; enc_msglen = tls->out_msglen; tls->out_msglen += tls->transform_out->ivlen; debug_msg (3, "before encrypt: msglen = %zu, " "including %zu bytes of IV and %zu bytes of padding", tls->out_msglen, tls->transform_out->ivlen, padlen + 1); debug_buf (4, "before encrypt: output payload", tls->out_iv, tls->out_msglen); err = gcry_cipher_reset (tls->transform_out->cipher_ctx_enc); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_enc, tls->transform_out->iv_enc, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_encrypt (tls->transform_out->cipher_ctx_enc, enc_msg, enc_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_encrypt", err); return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } for (i = 8; i > 0; i--) if (++tls->out_ctr[i - 1] != 0) break; /* The loops goes to its end iff the counter is wrapping */ if (!i) { debug_msg (1, "outgoing message counter would wrap"); return gpg_error (GPG_ERR_WOULD_WRAP); } return 0; } static int decrypt_buf (ntbtls_t tls) { gpg_error_t err; cipher_mode_t mode = tls->transform_out->cipher_mode_dec; size_t padlen = 0; size_t correct = 1; size_t tmplen, i; debug_msg (2, "decrypt buf"); if (tls->minor_ver < TLS_MINOR_VERSION_3) { debug_bug (); return gpg_error (GPG_ERR_BUG); } if (tls->in_msglen < tls->transform_in->minlen) { debug_msg (1, "in_msglen (%zu) < minlen (%zu)", tls->in_msglen, tls->transform_in->minlen); return gpg_error (GPG_ERR_INV_MAC); } if (is_aead_mode (mode)) { size_t dec_msglen; unsigned char *dec_msg; unsigned char add_data[13]; unsigned char taglen, explicit_iv_len; unsigned char iv[12]; taglen = (_ntbtls_ciphersuite_get_flags (tls->transform_in->ciphersuite) & CIPHERSUITE_FLAG_SHORT_TAG)? 8 : 16; explicit_iv_len = (tls->transform_in->ivlen - tls->transform_in->fixed_ivlen); if (tls->in_msglen < explicit_iv_len + taglen) { debug_msg (1, "msglen (%zud) < explicit_iv_len (%d) " "+ taglen (%d)", tls->in_msglen, explicit_iv_len, taglen); return gpg_error (GPG_ERR_INV_MAC); } dec_msglen = tls->in_msglen - explicit_iv_len - taglen; dec_msg = tls->in_msg; tls->in_msglen = dec_msglen; memcpy (add_data, tls->in_ctr, 8); add_data[8] = tls->in_msgtype; add_data[9] = tls->major_ver; add_data[10] = tls->minor_ver; add_data[11] = (tls->in_msglen >> 8) & 0xFF; add_data[12] = tls->in_msglen & 0xFF; debug_buf (4, "additional data used for AEAD", add_data, 13); memcpy (iv, tls->transform_in->iv_dec, tls->transform_in->fixed_ivlen); memcpy (iv + tls->transform_in->fixed_ivlen, tls->in_iv, 8); debug_buf (4, "IV used", iv, 12); debug_buf (4, "TAG used", dec_msg + dec_msglen, taglen); /* * Decrypt and authenticate */ err = gcry_cipher_reset (tls->transform_in->cipher_ctx_dec); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_in->cipher_ctx_dec, iv, tls->transform_in->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_authenticate (tls->transform_in->cipher_ctx_dec, add_data, 13); if (err) { debug_ret (1, "cipher_authenticate", err); return err; } err = gcry_cipher_decrypt (tls->transform_in->cipher_ctx_dec, dec_msg, dec_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_decrypt", err); return err; } err = gcry_cipher_checktag (tls->transform_in->cipher_ctx_dec, dec_msg + dec_msglen, taglen); if (err) { debug_ret (1, "cipher_checktag", err); return err; } } else if (mode == GCRY_CIPHER_MODE_CBC) { /* * Decrypt and check the padding */ unsigned char *dec_msg; size_t pad_count, real_count, padding_idx; size_t dec_msglen; size_t minlen = 0; /* * Check immediate ciphertext sanity */ if ((tls->in_msglen % tls->transform_in->ivlen)) { debug_msg (1, "msglen (%zu) %% ivlen (%zu) != 0", tls->in_msglen, tls->transform_in->ivlen); return gpg_error (GPG_ERR_INV_MAC); } minlen += tls->transform_in->ivlen; if (tls->in_msglen < minlen + tls->transform_in->ivlen || tls->in_msglen < minlen + tls->transform_in->maclen + 1) { debug_msg (1, "msglen (%zu) < max( ivlen(%zu), maclen (%zu) " "+ 1 ) ( + expl IV )", tls->in_msglen, tls->transform_in->ivlen, tls->transform_in->maclen); return gpg_error (GPG_ERR_INV_MAC); } dec_msglen = tls->in_msglen; dec_msg = tls->in_msg; /* * Initialize for prepended IV. */ dec_msglen -= tls->transform_in->ivlen; tls->in_msglen -= tls->transform_in->ivlen; for (i = 0; i < tls->transform_in->ivlen; i++) tls->transform_in->iv_dec[i] = tls->in_iv[i]; err = gcry_cipher_reset (tls->transform_out->cipher_ctx_dec); if (err) { debug_ret (1, "cipher_reset", err); return err; } err = gcry_cipher_setiv (tls->transform_out->cipher_ctx_dec, tls->transform_out->iv_dec, tls->transform_out->ivlen); if (err) { debug_ret (1, "cipher_setiv", err); return err; } err = gcry_cipher_decrypt (tls->transform_out->cipher_ctx_dec, dec_msg, dec_msglen, NULL, 0); if (err) { debug_ret (1, "cipher_decrypt", err); return err; } padlen = 1 + tls->in_msg[tls->in_msglen - 1]; if (tls->in_msglen < tls->transform_in->maclen + padlen) { debug_msg (1, "msglen (%zu) < maclen (%zu) + padlen (%zu)", tls->in_msglen, tls->transform_in->maclen, padlen); padlen = 0; correct = 0; } /* * Always check the padding up to the first failure and fake * check up to 256 bytes of padding */ pad_count = 0; real_count = 1; padding_idx = tls->in_msglen - padlen - 1; /* * Padding is guaranteed to be incorrect if: * 1. padlen >= tls->in_msglen * * 2. padding_idx >= TLS_MAX_CONTENT_LEN + * tls->transform_in->maclen * * In both cases we reset padding_idx to a safe value (0) to * prevent out-of-buffer reads. */ correct &= (tls->in_msglen >= padlen + 1); correct &= (padding_idx < TLS_MAX_CONTENT_LEN + tls->transform_in->maclen); padding_idx *= correct; for (i = 1; i <= 256; i++) { real_count &= (i <= padlen); pad_count += real_count * (tls->in_msg[padding_idx + i] == padlen-1); } correct &= (pad_count == padlen); /* Only 1 on correct padding */ if (padlen > 0 && !correct) debug_msg (1, "bad padding byte detected"); padlen &= correct * 0x1FF; } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } debug_buf (4, "raw buffer after decryption", tls->in_msg, tls->in_msglen); /* * Always compute the MAC (RFC4346, CBCTIME), except for AEAD of course */ if (!is_aead_mode (mode)) { unsigned char tmp[TLS_MAX_MAC_SIZE]; size_t extra_run; tls->in_msglen -= (tls->transform_in->maclen + padlen); tls->in_hdr[3] = (unsigned char) (tls->in_msglen >> 8); tls->in_hdr[4] = (unsigned char) (tls->in_msglen); memcpy (tmp, tls->in_msg + tls->in_msglen, tls->transform_in->maclen); /* * Process MAC and always update for padlen afterwards to make * total time independent of padlen * * extra_run compensates MAC check for padlen * * Known timing attacks: * - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf) * * We use ( ( Lx + 8 ) / 64 ) to handle 'negative Lx' values * correctly. (We round down instead of up, so -56 is the correct * value for our calculations instead of -55). * * Fixme: Get the transform block size from Libgcrypt instead of * assuming 64. */ extra_run = ((13 + tls->in_msglen + padlen + 8) / 64 - (13 + tls->in_msglen + 8) / 64); extra_run &= correct * 0xFF; err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_ctr, 13); if (!err) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_msg, tls->in_msglen); tmplen = tls->transform_in->maclen; if (!err) err = gcry_mac_read (tls->transform_in->mac_ctx_dec, tls->in_msg + tls->in_msglen, &tmplen); /* Keep on hashing dummy blocks if needed. gcry_mac_write explictly declares this as a valid modus operandi. */ if (!err && extra_run) { int j; for (j = 0; j < extra_run && !err; j++) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, tls->in_msg, 64); if (!err) err = gcry_mac_write (tls->transform_in->mac_ctx_dec, NULL, 0); } if (!err) err = gcry_mac_reset (tls->transform_in->mac_ctx_dec); if (err) { /* Note that such an error is due to a bug in the code, a missing algorithm, or an out of core case. It is highly unlikely that a side channel attack can be constructed based on such an error. In any case, with failing MAC functions we are anyway not able to guarantee a constant time behavior. */ debug_ret (1, "decrypt_buf: MACing failed", err); return err; } debug_buf (4, "message mac", tmp, tls->transform_in->maclen); debug_buf (4, "computed mac", tls->in_msg + tls->in_msglen, tls->transform_in->maclen); if (memcmpct (tmp, tls->in_msg + tls->in_msglen, tls->transform_in->maclen)) { debug_msg (1, "message mac does not match"); correct = 0; } /* * Finally check the correct flag */ if (!correct) return gpg_error (GPG_ERR_BAD_MAC); } if (!tls->in_msglen) { tls->nb_zero++; /* * Three or more empty messages may be a DoS attack * (excessive CPU consumption). */ if (tls->nb_zero > 3) { debug_msg (1, "received four consecutive empty " "messages, possible DoS attack"); return gpg_error (GPG_ERR_INV_MAC); } } else tls->nb_zero = 0; for (i = 8; i > 0; i--) if (++tls->in_ctr[i - 1] != 0) break; /* The loops goes to its end iff the counter is wrapping */ if (!i) { debug_msg (1, "incoming message counter would wrap"); return gpg_error (GPG_ERR_WOULD_WRAP); } return 0; } -/* - * Compression/decompression functions - */ -static int -ssl_compress_buf (ntbtls_t ssl) -{ - int ret; - unsigned char *msg_post = ssl->out_msg; - size_t len_pre = ssl->out_msglen; - unsigned char *msg_pre = ssl->compress_buf; - - debug_msg (2, "compress buf"); - - if (len_pre == 0) - return (0); - - memcpy (msg_pre, ssl->out_msg, len_pre); - - debug_msg (3, "before compression: msglen = %zu, ", ssl->out_msglen); - - debug_buf (4, "before compression: output payload", - ssl->out_msg, ssl->out_msglen); - - ssl->transform_out->ctx_deflate.next_in = msg_pre; - ssl->transform_out->ctx_deflate.avail_in = len_pre; - ssl->transform_out->ctx_deflate.next_out = msg_post; - ssl->transform_out->ctx_deflate.avail_out = TLS_BUFFER_LEN; - - /* ret = deflate (&ssl->transform_out->ctx_deflate, Z_SYNC_FLUSH); */ - ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - if (ret != Z_OK) - { - debug_msg (1, "failed to perform compression (%d)", ret); - return gpg_error (GPG_ERR_COMPR_FAILED); - } - - ssl->out_msglen = (TLS_BUFFER_LEN - - ssl->transform_out->ctx_deflate.avail_out); - - debug_msg (3, "after compression: msglen = %zu, ", ssl->out_msglen); - - debug_buf (4, "after compression: output payload", - ssl->out_msg, ssl->out_msglen); - - return (0); -} - -static int -ssl_decompress_buf (ntbtls_t ssl) -{ - int ret; - unsigned char *msg_post = ssl->in_msg; - size_t len_pre = ssl->in_msglen; - unsigned char *msg_pre = ssl->compress_buf; - - debug_msg (2, "decompress buf"); - - if (len_pre == 0) - return (0); - - memcpy (msg_pre, ssl->in_msg, len_pre); - - debug_msg (3, "before decompression: msglen = %zu, ", ssl->in_msglen); - - debug_buf (4, "before decompression: input payload", - ssl->in_msg, ssl->in_msglen); - - ssl->transform_in->ctx_inflate.next_in = msg_pre; - ssl->transform_in->ctx_inflate.avail_in = len_pre; - ssl->transform_in->ctx_inflate.next_out = msg_post; - ssl->transform_in->ctx_inflate.avail_out = TLS_MAX_CONTENT_LEN; - - /* ret = inflate (&ssl->transform_in->ctx_inflate, Z_SYNC_FLUSH); */ - ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); - if (ret != Z_OK) - { - debug_msg (1, "failed to perform decompression (%d)", ret); - return gpg_error (GPG_ERR_COMPR_FAILED); - } - - ssl->in_msglen = (TLS_MAX_CONTENT_LEN - - ssl->transform_in->ctx_inflate.avail_out); - - debug_msg (3, "after decompression: msglen = %zu, ", ssl->in_msglen); - - debug_buf (4, "after decompression: input payload", - ssl->in_msg, ssl->in_msglen); - - return (0); -} - - /* Fill the input message buffer with NB_WANT bytes. The function * returns an error if the numer of requested bytes do not fit into * the record buffer, there is a read problem, or on EOF. */ gpg_error_t _ntbtls_fetch_input (ntbtls_t tls, size_t nb_want) { gpg_error_t err; size_t len, nread; debug_msg (3, "fetch input"); if (!tls->inbound) return gpg_error (GPG_ERR_NOT_INITIALIZED); if (nb_want > TLS_BUFFER_LEN - 8) { debug_msg (1, "requesting more data than fits"); return gpg_error (GPG_ERR_REQUEST_TOO_LONG); } err = 0; while (tls->in_left < nb_want) { len = nb_want - tls->in_left; if (es_read (tls->inbound, tls->in_hdr + tls->in_left, len, &nread)) err = gpg_error_from_syserror (); else if (!nread) /*ie. EOF*/ err = gpg_error (GPG_ERR_EOF); debug_msg (3, "in_left: %zu, nb_want: %zu", tls->in_left, nb_want); debug_ret (3, "es_read", err); if (err) break; tls->in_left += nread; } return err; } /* * Flush any data not yet written */ gpg_error_t _ntbtls_flush_output (ntbtls_t tls) { gpg_error_t err; unsigned char *buf; size_t nwritten; debug_msg (3, "flush output"); if (!tls->outbound) return gpg_error (GPG_ERR_NOT_INITIALIZED); err = 0; while (tls->out_left > 0) { debug_msg (3, "message length: %zu, out_left: %zu", 5 + tls->out_msglen, tls->out_left); buf = tls->out_hdr + 5 + tls->out_msglen - tls->out_left; if (es_write (tls->outbound, buf, tls->out_left, &nwritten)) err = gpg_error_from_syserror (); debug_ret (3, "es_write", err); if (err) break; tls->out_left -= nwritten; } return err; } /* * Record layer functions */ gpg_error_t _ntbtls_write_record (ntbtls_t tls) { gpg_error_t err; int done = 0; size_t len = tls->out_msglen; debug_msg (3, "write record"); if (tls->out_msgtype == TLS_MSG_HANDSHAKE) { tls->out_msg[1] = (unsigned char) ((len - 4) >> 16); tls->out_msg[2] = (unsigned char) ((len - 4) >> 8); tls->out_msg[3] = (unsigned char) ((len - 4)); if (tls->out_msg[0] != TLS_HS_HELLO_REQUEST) tls->handshake->update_checksum (tls, tls->out_msg, len); } - if (tls->transform_out - && tls->session_out->compression == TLS_COMPRESS_DEFLATE) - { - err = ssl_compress_buf (tls); - if (err) - { - debug_ret (1, "ssl_compress_buf", err); - return err; - } - - len = tls->out_msglen; - } - if (!done) { tls->out_hdr[0] = (unsigned char) tls->out_msgtype; tls->out_hdr[1] = (unsigned char) tls->major_ver; tls->out_hdr[2] = (unsigned char) tls->minor_ver; tls->out_hdr[3] = (unsigned char) (len >> 8); tls->out_hdr[4] = (unsigned char) (len); if (tls->transform_out) { err = encrypt_buf (tls); if (err) { debug_ret (1, "encrypt_buf", err); return err; } len = tls->out_msglen; tls->out_hdr[3] = (unsigned char) (len >> 8); tls->out_hdr[4] = (unsigned char) (len); } tls->out_left = 5 + tls->out_msglen; debug_msg (3, "output record: msgtype = %d, " "version = [%d:%d], msglen = %u", tls->out_hdr[0], tls->out_hdr[1], tls->out_hdr[2], buf16_to_uint (tls->out_hdr + 3)); debug_buf (4, "output record sent to network", tls->out_hdr, 5 + tls->out_msglen); } err = _ntbtls_flush_output (tls); if (err) debug_ret (1, "_ntbtls_flush_output", err); return err; } gpg_error_t _ntbtls_read_record (ntbtls_t tls) { gpg_error_t err; int done = 0; debug_msg (3, "read record"); if (tls->in_hslen != 0 && tls->in_hslen < tls->in_msglen) { /* * Get next Handshake message in the current record */ tls->in_msglen -= tls->in_hslen; memmove (tls->in_msg, tls->in_msg + tls->in_hslen, tls->in_msglen); tls->in_hslen = 4; tls->in_hslen += buf16_to_size_t (tls->in_msg + 2); debug_msg (3, "handshake message: msglen =" " %zu, type = %u, hslen = %zu", tls->in_msglen, tls->in_msg[0], tls->in_hslen); if (tls->in_msglen < 4 || tls->in_msg[1] != 0) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_msglen < tls->in_hslen) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->state != TLS_HANDSHAKE_OVER) tls->handshake->update_checksum (tls, tls->in_msg, tls->in_hslen); return 0; } tls->in_hslen = 0; read_record_header: /* * Read the record header and validate it */ err = _ntbtls_fetch_input (tls, 5); if (err) { debug_ret (1, "fetch_input", err); return err; } //FIXME: Handle EOF tls->in_msgtype = tls->in_hdr[0]; tls->in_msglen = buf16_to_size_t (tls->in_hdr + 3); debug_msg (3, "input record: msgtype = %d, " "version = [%d:%d], msglen = %u", tls->in_hdr[0], tls->in_hdr[1], tls->in_hdr[2], buf16_to_uint (tls->in_hdr + 3)); if (tls->in_hdr[1] != tls->major_ver) { debug_msg (1, "major version mismatch"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_hdr[2] > tls->max_minor_ver) { debug_msg (1, "minor version mismatch"); return gpg_error (GPG_ERR_INV_RECORD); } /* Sanity check (outer boundaries) */ if (tls->in_msglen < 1 || tls->in_msglen > TLS_BUFFER_LEN - 13) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } /* * Make sure the message length is acceptable for the current transform * and protocol version. */ if (!tls->transform_in) { if (tls->in_msglen > TLS_MAX_CONTENT_LEN) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } else { if (tls->in_msglen < tls->transform_in->minlen) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } /* * TLS encrypted messages can have up to 256 bytes of padding */ if (tls->minor_ver >= TLS_MINOR_VERSION_1 && tls->in_msglen > (tls->transform_in->minlen + TLS_MAX_CONTENT_LEN + 256)) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } /* * Read and optionally decrypt the message contents */ err = _ntbtls_fetch_input (tls, 5 + tls->in_msglen); if (err) { debug_ret (1, "fetch_input", err); return err; } //FIXME: Handle EOF debug_buf (4, "input record from network", tls->in_hdr, 5 + tls->in_msglen); if (!done && tls->transform_in) { err = decrypt_buf (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_INV_MAC || gpg_err_code (err) == GPG_ERR_BAD_MAC || gpg_err_code (err) == GPG_ERR_CHECKSUM) { _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_BAD_RECORD_MAC); } debug_ret (1, "decrypt_buf", err); return err; } debug_buf (4, "input payload after decrypt", tls->in_msg, tls->in_msglen); if (tls->in_msglen > TLS_MAX_CONTENT_LEN) { debug_msg (1, "bad message length"); return gpg_error (GPG_ERR_INV_RECORD); } } - if (tls->transform_in && tls->session_in->compression == TLS_COMPRESS_DEFLATE) - { - err = ssl_decompress_buf (tls); - if (err) - { - debug_ret (1, "decompress_buf", err); - return err; - } - - tls->in_hdr[3] = (unsigned char) (tls->in_msglen >> 8); - tls->in_hdr[4] = (unsigned char) (tls->in_msglen); - } - if ( tls->in_msgtype != TLS_MSG_HANDSHAKE && tls->in_msgtype != TLS_MSG_ALERT && tls->in_msgtype != TLS_MSG_CHANGE_CIPHER_SPEC && tls->in_msgtype != TLS_MSG_APPLICATION_DATA) { debug_msg (1, "unknown record type"); err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_UNEXPECTED_MESSAGE); if (!err) err = gpg_error (GPG_ERR_INV_RECORD); return err; } if (tls->in_msgtype == TLS_MSG_HANDSHAKE) { tls->in_hslen = 4; tls->in_hslen += buf16_to_size_t (tls->in_msg + 2); debug_msg (3, "handshake message: msglen =" " %zu, type = %u, hslen = %zu", tls->in_msglen, tls->in_msg[0], tls->in_hslen); /* * Additional checks to validate the handshake header */ if (tls->in_msglen < 4 || tls->in_msg[1] != 0) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->in_msglen < tls->in_hslen) { debug_msg (1, "bad handshake length"); return gpg_error (GPG_ERR_INV_RECORD); } if (tls->state != TLS_HANDSHAKE_OVER) tls->handshake->update_checksum (tls, tls->in_msg, tls->in_hslen); } if (tls->in_msgtype == TLS_MSG_ALERT) { tls->last_alert.any = 1; tls->last_alert.level = tls->in_msg[0]; tls->last_alert.type = tls->in_msg[1]; if (tls->in_msg[0] == TLS_ALERT_LEVEL_FATAL) debug_msg (1, "got fatal alert message %d: %s", tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); else if (tls->in_msg[0] == TLS_ALERT_LEVEL_WARNING) debug_msg (2, "got warning alert message %d: %s", tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); else debug_msg (2, "got alert message of unknown level %d type %d: %s", tls->in_msg[0], tls->in_msg[1], alert_msg_to_string (tls->in_msg[1])); /* * Ignore non-fatal alerts, except close_notify */ if (tls->in_msg[0] == TLS_ALERT_LEVEL_FATAL) { return gpg_error (GPG_ERR_FATAL_ALERT); } if (tls->in_msg[0] == TLS_ALERT_LEVEL_WARNING && tls->in_msg[1] == TLS_ALERT_MSG_CLOSE_NOTIFY) { return gpg_error (GPG_ERR_CLOSE_NOTIFY); } tls->in_left = 0; goto read_record_header; } tls->in_left = 0; return (0); } gpg_error_t _ntbtls_send_fatal_handshake_failure (ntbtls_t tls) { return _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_MSG_HANDSHAKE_FAILURE); } gpg_error_t _ntbtls_send_alert_message (ntbtls_t tls, unsigned char level, unsigned char message) { gpg_error_t err; debug_msg (2, "send alert message"); tls->out_msgtype = TLS_MSG_ALERT; tls->out_msglen = 2; tls->out_msg[0] = level; tls->out_msg[1] = message; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } /* * Handshake functions */ gpg_error_t _ntbtls_write_certificate (ntbtls_t tls) { gpg_error_t err; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); x509_cert_t cert; int idx; const unsigned char *der; size_t derlen; size_t i; if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK || (tls->is_client && !tls->client_auth)) { debug_msg (2, "skipping write certificate"); tls->state++; return 0; } debug_msg (2, "write certificate"); if (!tls->is_client && !tls_own_cert (tls)) { debug_msg (1, "got no certificate to send"); return gpg_error (GPG_ERR_MISSING_CERT); } /* SSL_DEBUG_CRT (3, "own certificate", tls_own_cert (tls)); */ /* * 0 . 0 handshake type * 1 . 3 handshake length * 4 . 6 length of all certs * 7 . 9 length of cert. 1 * 10 . n-1 peer certificate * n . n+2 length of cert. 2 * n+3 . ... upper level cert, etc. */ i = 7; cert = tls_own_cert (tls); for (idx = 0; (der = _ntbtls_x509_get_cert (cert, idx, &derlen)); idx++) { if (derlen > TLS_MAX_CONTENT_LEN - 3 - i) { debug_msg (1, "certificate too large, %zu > %d", i + 3 + derlen, TLS_MAX_CONTENT_LEN); return gpg_error (GPG_ERR_CERT_TOO_LARGE); } tls->out_msg[i] = (unsigned char) (derlen >> 16); tls->out_msg[i + 1] = (unsigned char) (derlen >> 8); tls->out_msg[i + 2] = (unsigned char) (derlen); i += 3; memcpy (tls->out_msg + i, der, derlen); i += derlen; } tls->out_msg[4] = (unsigned char) ((i - 7) >> 16); tls->out_msg[5] = (unsigned char) ((i - 7) >> 8); tls->out_msg[6] = (unsigned char) ((i - 7)); tls->out_msglen = i; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_CERTIFICATE; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return err; } gpg_error_t _ntbtls_read_certificate (ntbtls_t tls) { gpg_error_t err; size_t i, n; const ciphersuite_t suite = tls->transform_negotiate->ciphersuite; key_exchange_type_t kex = _ntbtls_ciphersuite_get_kex (suite); if (kex == KEY_EXCHANGE_PSK || kex == KEY_EXCHANGE_DHE_PSK || kex == KEY_EXCHANGE_ECDHE_PSK) { debug_msg (2, "skipping read certificate"); tls->state++; return 0; } if (!tls->is_client && (tls->authmode == TLS_VERIFY_NONE || kex == KEY_EXCHANGE_RSA_PSK)) { tls->session_negotiate->verify_result = BADCERT_SKIP_VERIFY; debug_msg (2, "skipping read certificate"); tls->state++; return 0; } debug_msg (3, "read certificate"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } tls->state++; if (!tls->is_client && tls->minor_ver != TLS_MINOR_VERSION_0) { if (tls->in_hslen == 7 && tls->in_msgtype == TLS_MSG_HANDSHAKE && tls->in_msg[0] == TLS_HS_CERTIFICATE && !memcmp (tls->in_msg + 4, "\0\0\0", 3)) { debug_msg (1, "TLSv1 client has no certificate"); tls->session_negotiate->verify_result = BADCERT_MISSING; if (tls->authmode == TLS_VERIFY_REQUIRED) return gpg_error (GPG_ERR_MISSING_CLIENT_CERT); else return 0; } } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->in_msg[0] != TLS_HS_CERTIFICATE || tls->in_hslen < 10) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } /* * Same message structure as in _ntbtls_write_certificate() */ n = buf16_to_size_t (tls->in_msg + 5); if (tls->in_msg[4] != 0 || tls->in_hslen != 7 + n) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } /* In case we tried to reuse a session but it failed. */ if (tls->session_negotiate->peer_chain) { _ntbtls_x509_cert_release (tls->session_negotiate->peer_chain); tls->session_negotiate->peer_chain = NULL; } err = _ntbtls_x509_cert_new (&tls->session_negotiate->peer_chain); if (err) { debug_msg (1, "allocating X.509 cert object failed"); return err; } for (i = 7; i < tls->in_hslen; ) { if (tls->in_msg[i] != 0) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } n = buf16_to_size_t (tls->in_msg + i + 1); i += 3; if (n < 128 || i + n > tls->in_hslen) { debug_msg (1, "bad certificate message"); return gpg_error (GPG_ERR_BAD_HS_CERT); } err = _ntbtls_x509_append_cert (tls->session_negotiate->peer_chain, tls->in_msg + i, n); if (err) { debug_ret (1, "x509_append_cert", err); return err; } i += n; } debug_crt (1, "peer certificate", tls->session_negotiate->peer_chain); /* * On client, make sure the server cert doesn't change during renego to * avoid "triple handshake" attack: https://secure-resumption.com/ */ if (tls->is_client && tls->renegotiation == TLS_RENEGOTIATION) { if (!tls->session->peer_chain) { debug_msg (1, "new server cert during renegotiation"); return gpg_error (GPG_ERR_BAD_HS_CERT); } //FIXME: Need to implement this in x509.c IMPORTANT! /* if (tls->session->peer_chain->raw.len != */ /* tls->session_negotiate->peer_chain->raw.len */ /* || memcmp (tls->session->peer_chain->raw.p, */ /* tls->session_negotiate->peer_chain->raw.p, */ /* tls->session->peer_chain->raw.len)) */ /* { */ /* debug_msg (1, "server cert changed during renegotiation"); */ /* return gpg_error (GPG_ERR_BAD_HS_CERT); */ /* } */ } if (tls->authmode != TLS_VERIFY_NONE) { /* * Verify hostname */ if (tls->hostname) { if (!tls->session_negotiate) err = gpg_error (GPG_ERR_MISSING_CERT); else err = _ntbtls_x509_check_hostname (tls->session_negotiate->peer_chain, tls->hostname); if (err) { debug_ret (1, "x509_check_hostname", err); } } else err = 0; /* * Verify certificate. We don't do this if the hostname check * already failed. */ if (!err) { if (!tls->verify_cb) { debug_msg (1, "verify callback not set"); return gpg_error (GPG_ERR_NOT_INITIALIZED); } err = tls->verify_cb (tls->verify_cb_value, tls, 0); if (err) { debug_ret (1, "error from the verify callback", err); } if (tls->authmode != TLS_VERIFY_REQUIRED) err = 0; } } return err; } gpg_error_t _ntbtls_write_change_cipher_spec (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "write change cipher spec"); tls->out_msgtype = TLS_MSG_CHANGE_CIPHER_SPEC; tls->out_msglen = 1; tls->out_msg[0] = 1; tls->state++; err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } gpg_error_t _ntbtls_read_change_cipher_spec (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "read change_cipher_spec"); err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_CHANGE_CIPHER_SPEC) { debug_msg (1, "bad change_cipher_spec message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->in_msglen != 1 || tls->in_msg[0] != 1) { debug_msg (1, "bad change_cipher_spec message"); return gpg_error (GPG_ERR_BAD_HS_CHANGE_CIPHER); } tls->state++; return 0; } static void update_checksum_sha256 (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha256, buf, len); } static void update_checksum_sha384 (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha512, buf, len); } void _ntbtls_optimize_checksum (ntbtls_t tls, const ciphersuite_t suite) { if (_ntbtls_ciphersuite_get_mac (suite) == GCRY_MAC_HMAC_SHA384) tls->handshake->update_checksum = update_checksum_sha384; else if (_ntbtls_ciphersuite_get_mac (suite) != GCRY_MAC_HMAC_SHA384) tls->handshake->update_checksum = update_checksum_sha256; else { debug_bug (); return; } } static void update_checksum_start (ntbtls_t tls, const unsigned char *buf, size_t len) { gcry_md_write (tls->handshake->fin_sha256, buf, len); gcry_md_write (tls->handshake->fin_sha512, buf, len); } static void calc_finished_tls (ntbtls_t tls, int is_sha384, unsigned char *buf, int is_client) { gpg_error_t err; gcry_md_hd_t md; int len = 12; const char *sender; unsigned char padbuf[48]; size_t hashlen = is_sha384? 48 : 32; session_t session; char *p; session = tls->session_negotiate; if (!session) session = tls->session; debug_msg (2, "calc finished tls sha%d", is_sha384? 384 : 256); err = gcry_md_copy (&md, (is_sha384 ? tls->handshake->fin_sha512 /* */ : tls->handshake->fin_sha256)); if (err) { debug_ret (1, "calc_finished_tls", err); memset (buf, 0, len); return; } /* * TLSv1.2: * hash = PRF( master, finished_label, * Hash( handshake ) )[0.11] */ sender = is_client ? "client finished" : "server finished"; p = gcry_md_read (md, is_sha384? GCRY_MD_SHA384 : GCRY_MD_SHA256); if (p) memcpy (padbuf, p, hashlen); gcry_md_close (md); if (!p) { debug_bug (); memset (buf, 0, len); return; } tls->handshake->tls_prf (session->master, 48, sender, padbuf, hashlen, buf, len); debug_buf (3, "calc finished result", buf, len); wipememory (padbuf, hashlen); } static void calc_finished_tls_sha256 (ntbtls_t tls, unsigned char *buf, int is_client) { calc_finished_tls (tls, 0, buf, is_client); } static void calc_finished_tls_sha384 (ntbtls_t tls, unsigned char *buf, int is_client) { calc_finished_tls (tls, 1, buf, is_client); } void _ntbtls_handshake_wrapup (ntbtls_t tls) { int resume = tls->handshake->resume; debug_msg (3, "handshake wrapup"); /* * Free our handshake params */ handshake_params_deinit (tls->handshake); free (tls->handshake); tls->handshake = NULL; if (tls->renegotiation == TLS_RENEGOTIATION) { tls->renegotiation = TLS_RENEGOTIATION_DONE; tls->renego_records_seen = 0; } /* * Switch in our now active transform context */ if (tls->transform) { transform_deinit (tls->transform); free (tls->transform); } tls->transform = tls->transform_negotiate; tls->transform_negotiate = NULL; if (tls->session) { session_deinit (tls->session); free (tls->session); } tls->session = tls->session_negotiate; tls->session_negotiate = NULL; /* * Add cache entry */ if (tls->f_set_cache && tls->session->length && !resume) { if (tls->f_set_cache (tls->p_set_cache, tls->session)) debug_msg (1, "cache did not store session"); } tls->state++; debug_msg (3, "handshake wrapup ready "); } gpg_error_t _ntbtls_write_finished (ntbtls_t tls) { gpg_error_t err; int hashlen; debug_msg (2, "write finished"); /* * Set the out_msg pointer to the correct location based on IV length */ if (tls->minor_ver >= TLS_MINOR_VERSION_2) { tls->out_msg = (tls->out_iv + tls->transform_negotiate->ivlen - tls->transform_negotiate->fixed_ivlen); } else tls->out_msg = tls->out_iv; tls->handshake->calc_finished (tls, tls->out_msg + 4, tls->is_client); /* TODO TLS/1.2 Hash length is determined by cipher suite (Page 63) but all currently defined cipher suite keep it at 12. */ hashlen = 12; tls->verify_data_len = hashlen; memcpy (tls->own_verify_data, tls->out_msg + 4, hashlen); tls->out_msglen = 4 + hashlen; tls->out_msgtype = TLS_MSG_HANDSHAKE; tls->out_msg[0] = TLS_HS_FINISHED; /* * In case of session resuming, invert the client and server * ChangeCipherSpec messages order. */ if (tls->handshake->resume) { if (tls->is_client) tls->state = TLS_HANDSHAKE_WRAPUP; else tls->state = TLS_CLIENT_CHANGE_CIPHER_SPEC; } else tls->state++; /* * Switch to our negotiated transform and session parameters for outbound * data. */ debug_msg (3, "switching to new transform spec for outbound data"); tls->transform_out = tls->transform_negotiate; tls->session_out = tls->session_negotiate; memset (tls->out_ctr, 0, 8); err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } return 0; } gpg_error_t _ntbtls_read_finished (ntbtls_t tls) { gpg_error_t err; unsigned int hashlen; unsigned char buf[36]; debug_msg (2, "read finished"); tls->handshake->calc_finished (tls, buf, !tls->is_client); /* * Switch to our negotiated transform and session parameters for inbound * data. */ debug_msg (3, "switching to new transform spec for inbound data"); tls->transform_in = tls->transform_negotiate; tls->session_in = tls->session_negotiate; memset (tls->in_ctr, 0, 8); /* * Set the in_msg pointer to the correct location based on IV length */ if (tls->minor_ver >= TLS_MINOR_VERSION_2) { tls->in_msg = (tls->in_iv + tls->transform_negotiate->ivlen - tls->transform_negotiate->fixed_ivlen); } else tls->in_msg = tls->in_iv; err = _ntbtls_read_record (tls); if (err) { debug_ret (1, "read_record", err); return err; } if (tls->in_msgtype != TLS_MSG_HANDSHAKE) { debug_msg (1, "bad finished message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } /* TODO TLS/1.2 Hash length is determined by cipher suite (Page 63). */ hashlen = 12; if (tls->in_msg[0] != TLS_HS_FINISHED || tls->in_hslen != 4 + hashlen) { debug_msg (1, "bad finished message"); return gpg_error (GPG_ERR_BAD_HS_FINISHED); } if (memcmpct (tls->in_msg + 4, buf, hashlen)) { debug_msg (1, "bad finished message"); debug_buf (2, "want", buf, hashlen); debug_buf (2, " got", tls->in_msg+4, hashlen); return gpg_error (GPG_ERR_BAD_HS_FINISHED); } tls->verify_data_len = hashlen; memcpy (tls->peer_verify_data, buf, hashlen); if (tls->handshake->resume) { if (tls->is_client) tls->state = TLS_CLIENT_CHANGE_CIPHER_SPEC; else tls->state = TLS_HANDSHAKE_WRAPUP; } else tls->state++; return 0; } static gpg_error_t transform_init (transform_t transform) { gpg_error_t err = 0; (void)transform; //FIXME: /* cipher_init (&transform->cipher_ctx_enc); */ /* cipher_init (&transform->cipher_ctx_dec); */ /* md_init (&transform->mac_ctx_enc); */ /* md_init (&transform->mac_ctx_dec); */ return err; } static void transform_deinit (transform_t transform) { if (!transform) return; //FIXME: /* deflateEnd (&transform->ctx_deflate); */ /* inflateEnd (&transform->ctx_inflate); */ /* cipher_free (&transform->cipher_ctx_enc); */ /* cipher_free (&transform->cipher_ctx_dec); */ /* md_free (&transform->mac_ctx_enc); */ /* md_free (&transform->mac_ctx_dec); */ wipememory (transform, sizeof *transform); } static gpg_error_t session_init (session_t session) { (void)session; return 0; } static void session_deinit (session_t session) { if (!session) return; _ntbtls_x509_cert_release (session->peer_chain); free (session->ticket); wipememory (session, sizeof *session); } static gpg_error_t handshake_params_init (handshake_params_t handshake) { gpg_error_t err; err = gcry_md_open (&handshake->fin_sha256, GCRY_MD_SHA256, 0); if (err) return err; err = gcry_md_open (&handshake->fin_sha512, GCRY_MD_SHA384, 0); if (err) { gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; return err; } err = _ntbtls_dhm_new (&handshake->dhm_ctx); if (err) { gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; gcry_md_close (handshake->fin_sha512); handshake->fin_sha512 = NULL; return err; } err = _ntbtls_ecdh_new (&handshake->ecdh_ctx); if (err) { _ntbtls_dhm_release (handshake->dhm_ctx); handshake->dhm_ctx = NULL; gcry_md_close (handshake->fin_sha256); handshake->fin_sha256 = NULL; gcry_md_close (handshake->fin_sha512); handshake->fin_sha512 = NULL; return err; } handshake->update_checksum = update_checksum_start; handshake->sig_alg = TLS_HASH_SHA256; return 0; } static void handshake_params_deinit (handshake_params_t handshake) { if (!handshake) return; _ntbtls_dhm_release (handshake->dhm_ctx); handshake->dhm_ctx = NULL; _ntbtls_ecdh_release (handshake->ecdh_ctx); handshake->ecdh_ctx = NULL; free (handshake->curves); /* Free only the linked list wrapper, not the keys themselves since the belong to the SNI callback. */ if (handshake->sni_key_cert) { //FIXME: /* ssl_key_cert *cur, *next; */ /* cur = handshake->sni_key_cert; */ /* while (cur) */ /* { */ /* next = cur->next; */ /* free (cur); */ /* cur = next; */ /* } */ } wipememory (handshake, sizeof *handshake); } static gpg_error_t handshake_init (ntbtls_t tls) { gpg_error_t err; /* Clear old handshake information if present. */ transform_deinit (tls->transform_negotiate); session_deinit (tls->session_negotiate); handshake_params_deinit (tls->handshake); /* * Either the pointers are now NULL or cleared properly and can be freed. * Now allocate missing structures. */ if (!tls->transform_negotiate) { tls->transform_negotiate = calloc (1, sizeof *tls->transform_negotiate); if (!tls->transform_negotiate) { err = gpg_error_from_syserror (); goto leave; } } if (!tls->session_negotiate) { tls->session_negotiate = calloc (1, sizeof *tls->session_negotiate); if (!tls->session_negotiate) { err = gpg_error_from_syserror (); goto leave; } } if (!tls->handshake) { tls->handshake = calloc (1, sizeof *tls->handshake); if (!tls->handshake) { err = gpg_error_from_syserror (); goto leave; } } /* Initialize structures */ err = transform_init (tls->transform_negotiate); if (err) goto leave; err = session_init (tls->session_negotiate); if (err) goto leave; err = handshake_params_init (tls->handshake); if (err) goto leave; /* Fixme: Document the owner of KEY_CERT or use a ref counter. */ tls->handshake->key_cert = tls->key_cert; leave: if (err) { transform_deinit (tls->transform_negotiate); free (tls->transform_negotiate); tls->transform_negotiate = NULL; session_deinit (tls->session_negotiate); free (tls->session_negotiate); tls->session_negotiate = NULL; handshake_params_deinit (tls->handshake); free (tls->handshake); tls->handshake = NULL; } return err; } /* * Create a new TLS context. Valid values for FLAGS are: * * NTBTLS_SERVER - This endpoint is a server (default). * NTBTLS_CLIENT - This endpoint is a client. * * On success a context object is returned at R_TLS. One error NULL * is stored at R_TLS and an error code is returned. */ gpg_error_t _ntbtls_new (ntbtls_t *r_tls, unsigned int flags) { gpg_error_t err; ntbtls_t tls; int buffer_len = TLS_BUFFER_LEN; const int ciphersuite_list_13[3] = { TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, 0 }; *r_tls = NULL; /* Note: NTBTLS_SERVER has value 0, thus we can't check for it. */ if ((flags & ~(NTBTLS_CLIENT|NTBTLS_SAMETRHEAD))) return gpg_error (GPG_ERR_EINVAL); tls = calloc (1, sizeof *tls); if (!tls) return gpg_error_from_syserror (); /* Return immediately. */ tls->magic = NTBTLS_CONTEXT_MAGIC; tls->min_major_ver = TLS_MIN_MAJOR_VERSION; tls->min_minor_ver = TLS_MIN_MINOR_VERSION; tls->max_major_ver = TLS_MAX_MAJOR_VERSION; tls->max_minor_ver = TLS_MAX_MINOR_VERSION; tls->flags = flags; if ((flags & NTBTLS_CLIENT)) { tls->is_client = 1; tls->use_session_tickets = 1; } /* We only support TLS 1.2/1.3 and thus we set the list for the other TLS versions to NULL. */ tls->ciphersuite_list[TLS_MINOR_VERSION_0] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_1] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_2] = NULL; tls->ciphersuite_list[TLS_MINOR_VERSION_3] = _ntbtls_ciphersuite_list (); tls->ciphersuite_list[TLS_MINOR_VERSION_4] = ciphersuite_list_13; tls->renego_max_records = TLS_RENEGO_MAX_RECORDS_DEFAULT; /* FIXME */ /* if ((ret = mpi_read_string (&tls->dhm_P, 16, */ /* POLARSSL_DHM_RFC5114_MODP_1024_P)) != 0 || */ /* (ret = mpi_read_string (&tls->dhm_G, 16, */ /* POLARSSL_DHM_RFC5114_MODP_1024_G)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* * Prepare base structures */ tls->in_ctr = malloc (buffer_len); if (!tls->in_ctr) { err = gpg_error_from_syserror (); goto leave; } tls->in_hdr = tls->in_ctr + 8; tls->in_iv = tls->in_ctr + 13; tls->in_msg = tls->in_ctr + 13; tls->out_ctr = malloc (buffer_len); if (!tls->out_ctr) { err = gpg_error_from_syserror (); goto leave; } tls->out_hdr = tls->out_ctr + 8; tls->out_iv = tls->out_ctr + 13; tls->out_msg = tls->out_ctr + 13; memset (tls->in_ctr, 0, buffer_len); memset (tls->out_ctr, 0, buffer_len); tls->ticket_lifetime = TLS_DEFAULT_TICKET_LIFETIME; // FIXME: tls->curve_list = ecp_grp_id_list (); err = handshake_init (tls); if (err) goto leave; if (tls->is_client) tls->use_session_tickets = 1; leave: if (err) { free (tls->in_ctr); free (tls); } else *r_tls = tls; return err; } /* * Release an TLS context. */ void _ntbtls_release (ntbtls_t tls) { if (!tls) return; debug_msg (2, "release"); if (tls->magic != NTBTLS_CONTEXT_MAGIC) debug_bug (); if (tls->out_ctr) { /* FIXME: At some points we are using a variable for the length. Either do that always or use always this constant. */ wipememory (tls->out_ctr, TLS_BUFFER_LEN); free (tls->out_ctr); } if (tls->in_ctr) { wipememory (tls->in_ctr, TLS_BUFFER_LEN); free (tls->in_ctr); } if (tls->compress_buf) { wipememory (tls->compress_buf, TLS_BUFFER_LEN); free (tls->compress_buf); } //FIXME: /* mpi_free (&tls->dhm_P); */ /* mpi_free (&tls->dhm_G); */ if (tls->transform) { transform_deinit (tls->transform); free (tls->transform); } if (tls->handshake) { handshake_params_deinit (tls->handshake); free (tls->handshake); transform_deinit (tls->transform_negotiate); free (tls->transform_negotiate); session_deinit (tls->session_negotiate); free (tls->session_negotiate); } if (tls->session) { session_deinit (tls->session); free (tls->session); } if (tls->ticket_keys) { ticket_keys_deinit (tls->ticket_keys); free (tls->ticket_keys); } free (tls->hostname); if (tls->psk) { wipememory (tls->psk, tls->psk_len); wipememory (tls->psk_identity, tls->psk_identity_len); free (tls->psk); free (tls->psk_identity); tls->psk_len = 0; tls->psk_identity_len = 0; } //FIXME: /* ssl_key_cert_free (tls->key_cert); */ /* Actually clear after last debug message */ wipememory (tls, sizeof *tls); free (tls); } /* Return info about the last rceeived alert. */ const char * _ntbtls_get_last_alert (ntbtls_t tls, unsigned int *r_level, unsigned int *r_type) { if (!tls || !tls->last_alert.any) { if (r_level) *r_level = 0; if (r_type) *r_type = 0; return NULL; } if (r_level) *r_level = tls->last_alert.level; if (r_type) *r_type = tls->last_alert.type; return alert_msg_to_string (tls->last_alert.type); } /* Set the transport stream for the context TLS. This needs to be called right after init and may not be changed later. INBOUND and OUTBOUND are usually connected to the same socket. The caller must ensure that the streams are not closed as long as the context TLS is valid. However, after destroying the context the streams may be closed. This behavior allows to setup a TLS connection on an existing stream, shutdown the TLS and continue unencrypted. Whether the latter is of any real use in practice is a different question. Using separate streams allow to run TLS over a pair of half-duplex connections. */ gpg_error_t _ntbtls_set_transport (ntbtls_t tls, estream_t inbound, estream_t outbound) { if (!tls || !inbound || !outbound) return gpg_error (GPG_ERR_INV_ARG); if (tls->inbound || tls->outbound) return gpg_error (GPG_ERR_CONFLICT); /* We do our own buffering thus we disable buffer of the transport streams. */ if (es_setvbuf (inbound, NULL, _IONBF, 0)) return gpg_error_from_syserror (); if (es_setvbuf (outbound, NULL, _IONBF, 0)) return gpg_error_from_syserror (); tls->inbound = inbound; tls->outbound = outbound; return 0; } /* * Reset an initialized and used SSL context for re-use while retaining * all application-set variables, function pointers and data. */ int ssl_session_reset (ntbtls_t ssl) { int ret; ssl->state = TLS_HELLO_REQUEST; ssl->renegotiation = TLS_INITIAL_HANDSHAKE; ssl->secure_renegotiation = TLS_LEGACY_RENEGOTIATION; ssl->verify_data_len = 0; memset (ssl->own_verify_data, 0, 36); memset (ssl->peer_verify_data, 0, 36); ssl->in_offt = NULL; ssl->in_msg = ssl->in_ctr + 13; ssl->in_msgtype = 0; ssl->in_msglen = 0; ssl->in_left = 0; ssl->in_hslen = 0; ssl->nb_zero = 0; ssl->record_read = 0; ssl->out_msg = ssl->out_ctr + 13; ssl->out_msgtype = 0; ssl->out_msglen = 0; ssl->out_left = 0; ssl->transform_in = NULL; ssl->transform_out = NULL; ssl->renego_records_seen = 0; memset (ssl->out_ctr, 0, TLS_BUFFER_LEN); memset (ssl->in_ctr, 0, TLS_BUFFER_LEN); if (ssl->transform) { transform_deinit (ssl->transform); free (ssl->transform); ssl->transform = NULL; } if (ssl->session) { session_deinit (ssl->session); free (ssl->session); ssl->session = NULL; } ssl->alpn_chosen = NULL; if ((ret = handshake_init (ssl)) != 0) return (ret); return (0); } static void ticket_keys_deinit (ticket_keys_t tkeys) { //FIXME: /* aes_free (&tkeys->enc); */ /* aes_free (&tkeys->dec); */ wipememory (tkeys, sizeof *tkeys); } /* * Allocate and initialize ticket keys in TLS if not yet done. */ static gpg_error_t ticket_keys_setup (ntbtls_t tls) { ticket_keys_t tkeys; unsigned char buf[16]; if (tls->ticket_keys) return 0; tkeys = malloc (sizeof *tkeys); if (!tkeys) return gpg_error_from_syserror (); //FIXME: /* aes_init (&tkeys->enc); */ /* aes_init (&tkeys->dec); */ gcry_randomize (tkeys->key_name, 16, GCRY_STRONG_RANDOM); gcry_randomize (buf, 16, GCRY_STRONG_RANDOM); //FIXME: /* if ((ret = aes_setkey_enc (&tkeys->enc, buf, 128)) != 0 || */ /* (ret = aes_setkey_dec (&tkeys->dec, buf, 128)) != 0) */ /* { */ /* ssl_ticket_keys_free (tkeys); */ /* polarssl_free (tkeys); */ /* return (ret); */ /* } */ gcry_randomize (tkeys->mac_key, 16, GCRY_STRONG_RANDOM); tls->ticket_keys = tkeys; return 0; } /* * SSL set accessors */ void ssl_set_authmode (ntbtls_t ssl, int authmode) { ssl->authmode = authmode; } #if defined(POLARSSL_X509_CRT_PARSE_C) void ssl_set_verify (ntbtls_t ssl, int (*f_vrfy) (void *, x509_crt *, int, int *), void *p_vrfy) { ssl->f_vrfy = f_vrfy; ssl->p_vrfy = p_vrfy; } #endif /* POLARSSL_X509_CRT_PARSE_C */ void ssl_set_session_cache (ntbtls_t ssl, int (*f_get_cache) (void *, session_t), void *p_get_cache, int (*f_set_cache) (void *, const session_t), void *p_set_cache) { ssl->f_get_cache = f_get_cache; ssl->p_get_cache = p_get_cache; ssl->f_set_cache = f_set_cache; ssl->p_set_cache = p_set_cache; } /* Request resumption of session (client-side only). Session data is copied from presented session structure. */ gpg_error_t _ntbtls_set_session (ntbtls_t tls, const session_t session) { gpg_error_t err; if (!tls || !session || !tls->session_negotiate || !tls->is_client) return gpg_error (GPG_ERR_INV_ARG); err = session_copy (tls->session_negotiate, session); if (err) return err; tls->handshake->resume = 1; return 0; } /* Add a new (empty) key_cert entry an return a pointer to it */ /* static ssl_key_cert * */ /* ssl_add_key_cert (ntbtls_t ssl) */ /* { */ /* ssl_key_cert *key_cert, *last; */ /* key_cert = calloc (1, sizeof *key_cert); */ /* if (!key_cert) */ /* return NULL; */ /* /\* Append the new key_cert to the (possibly empty) current list *\/ */ /* if (ssl->key_cert == NULL) */ /* { */ /* ssl->key_cert = key_cert; */ /* if (ssl->handshake != NULL) */ /* ssl->handshake->key_cert = key_cert; */ /* } */ /* else */ /* { */ /* last = ssl->key_cert; */ /* while (last->next != NULL) */ /* last = last->next; */ /* last->next = key_cert; */ /* } */ /* return (key_cert); */ /* } */ /* Set a certificate verify callback for the session TLS. */ gpg_error_t _ntbtls_set_verify_cb (ntbtls_t tls, ntbtls_verify_cb_t cb, void *cb_value) { if (!tls) return gpg_error (GPG_ERR_INV_ARG); tls->verify_cb = cb; tls->verify_cb_value = cb_value; /* Make sure we have an authmode set. Right now, there is no API to * change thye authmode. */ tls->authmode = cb ? TLS_VERIFY_REQUIRED : TLS_VERIFY_NONE; return 0; } /* int */ /* ssl_set_own_cert (ntbtls_t ssl, x509_crt * own_cert, pk_context * pk_key) */ /* { */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->cert = own_cert; */ /* key_cert->key = pk_key; */ /* return 0; */ /* } */ /* int */ /* ssl_set_own_cert_rsa (ntbtls_t ssl, x509_crt * own_cert, */ /* rsa_context * rsa_key) */ /* { */ /* int ret; */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->key = malloc (sizeof (pk_context)); */ /* if (!key_cert->key) */ /* return gpg_error_from_syserror (); */ /* pk_init (key_cert->key); */ /* ret = pk_init_ctx (key_cert->key, pk_info_from_type (POLARSSL_PK_RSA)); */ /* if (ret != 0) */ /* return (ret); */ /* if ((ret = rsa_copy (pk_rsa (*key_cert->key), rsa_key)) != 0) */ /* return (ret); */ /* key_cert->cert = own_cert; */ /* key_cert->key_own_alloc = 1; */ /* return (0); */ /* } */ /* int */ /* ssl_set_own_cert_alt (ntbtls_t ssl, x509_crt * own_cert, */ /* void *rsa_key, */ /* rsa_decrypt_func rsa_decrypt, */ /* rsa_sign_func rsa_sign, rsa_key_len_func rsa_key_len) */ /* { */ /* int ret; */ /* ssl_key_cert *key_cert; */ /* key_cert = ssl_add_key_cert (ssl); */ /* if (!key_cert) */ /* return gpg_error_from_syserror (); */ /* key_cert->key = malloc (sizeof (pk_context)); */ /* if (!key_cert->key) */ /* { */ /* err = gpg_error_from_syserror (); */ /* free (key_cert); */ /* return err; */ /* } */ /* pk_init (key_cert->key); */ /* if ((ret = pk_init_ctx_rsa_alt (key_cert->key, rsa_key, */ /* rsa_decrypt, rsa_sign, rsa_key_len)) != 0) */ /* return (ret); */ /* key_cert->cert = own_cert; */ /* key_cert->key_own_alloc = 1; */ /* return 0; */ /* } */ /* int */ /* ssl_set_psk (ntbtls_t ssl, const unsigned char *psk, size_t psk_len, */ /* const unsigned char *psk_identity, size_t psk_identity_len) */ /* { */ /* if (psk == NULL || psk_identity == NULL) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* if (psk_len > POLARSSL_PSK_MAX_LEN) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* if (ssl->psk != NULL) */ /* { */ /* free (ssl->psk); */ /* ssl->psk = NULL; */ /* free (ssl->psk_identity); */ /* ssl->psk_identity = NULL; */ /* } */ /* ssl->psk_len = psk_len; */ /* ssl->psk_identity_len = psk_identity_len; */ /* ssl->psk = malloc (ssl->psk_len); */ /* if (!ssl->psk) */ /* return gpg_error_from_syserror (); */ /* ssl->psk_identity = malloc (ssl->psk_identity_len); */ /* if (!ssl->psk_identity) */ /* { */ /* err = gpg_error_from_syserror (); */ /* free (ssl->psk); */ /* ssl->psk = NULL; */ /* return err; */ /* } */ /* memcpy (ssl->psk, psk, ssl->psk_len); */ /* memcpy (ssl->psk_identity, psk_identity, ssl->psk_identity_len); */ /* return (0); */ /* } */ /* void */ /* ssl_set_psk_cb (ntbtls_t ssl, */ /* int (*f_psk) (void *, ssl_context *, const unsigned char *, */ /* size_t), void *p_psk) */ /* { */ /* ssl->f_psk = f_psk; */ /* ssl->p_psk = p_psk; */ /* } */ /* int */ /* ssl_set_dh_param (ntbtls_t ssl, const char *dhm_P, const char *dhm_G) */ /* { */ /* int ret; */ /* if ((ret = mpi_read_string (&ssl->dhm_P, 16, dhm_P)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* if ((ret = mpi_read_string (&ssl->dhm_G, 16, dhm_G)) != 0) */ /* { */ /* debug_ret (1, "mpi_read_string", ret); */ /* return (ret); */ /* } */ /* return (0); */ /* } */ /* int */ /* ssl_set_dh_param_ctx (ntbtls_t ssl, dhm_context * dhm_ctx) */ /* { */ /* int ret; */ /* if ((ret = mpi_copy (&ssl->dhm_P, &dhm_ctx->P)) != 0) */ /* { */ /* debug_ret (1, "mpi_copy", ret); */ /* return (ret); */ /* } */ /* if ((ret = mpi_copy (&ssl->dhm_G, &dhm_ctx->G)) != 0) */ /* { */ /* debug_ret (1, "mpi_copy", ret); */ /* return (ret); */ /* } */ /* return (0); */ /* } */ /* * Set the allowed elliptic curves */ /* void */ /* ssl_set_curves (ntbtls_t ssl, const ecp_group_id * curve_list) */ /* { */ /* ssl->curve_list = curve_list; */ /* } */ gpg_error_t _ntbtls_set_hostname (ntbtls_t tls, const char *hostname) { size_t len; if (!tls) return gpg_error (GPG_ERR_INV_ARG); if (!hostname) { free (tls->hostname); tls->hostname = NULL; } len = strlen (hostname); if ( len + 1 < len ) return gpg_error (GPG_ERR_EOVERFLOW); tls->hostname = malloc (len + 1); if (!tls->hostname) return gpg_error_from_syserror (); strcpy (tls->hostname, hostname); return 0; } /* Return the hostname which has been set with ntbtls_set_hostname. * The returned value is valid as long as TLS is valid and * ntbtls_set_hostname has not been used again. */ const char * _ntbtls_get_hostname (ntbtls_t tls) { return tls ? tls->hostname : NULL; } /* void */ /* ssl_set_sni (ntbtls_t ssl, */ /* int (*f_sni) (void *, ntbtls_t, */ /* const unsigned char *, size_t), void *p_sni) */ /* { */ /* ssl->f_sni = f_sni; */ /* ssl->p_sni = p_sni; */ /* } */ /* int */ /* ssl_set_alpn_protocols (ntbtls_t ssl, const char **protos) */ /* { */ /* size_t cur_len, tot_len; */ /* const char **p; */ /* /\* */ /* * "Empty strings MUST NOT be included and byte strings MUST NOT be */ /* * truncated". Check lengths now rather than later. */ /* *\/ */ /* tot_len = 0; */ /* for (p = protos; *p != NULL; p++) */ /* { */ /* cur_len = strlen (*p); */ /* tot_len += cur_len; */ /* if (cur_len == 0 || cur_len > 255 || tot_len > 65535) */ /* return gpg_error (GPG_ERR_INV_ARG); */ /* } */ /* ssl->alpn_list = protos; */ /* return (0); */ /* } */ /* const char * */ /* ssl_get_alpn_protocol (const ntbtls_t ssl) */ /* { */ /* return (ssl->alpn_chosen); */ /* } */ /* void */ /* ssl_set_max_version (ntbtls_t ssl, int major, int minor) */ /* { */ /* if ( major >= TLS_MIN_MAJOR_VERSION && major <= TLS_MAX_MAJOR_VERSION */ /* && minor >= TLS_MIN_MINOR_VERSION && minor <= TLS_MAX_MINOR_VERSION) */ /* { */ /* ssl->max_major_ver = major; */ /* ssl->max_minor_ver = minor; */ /* } */ /* } */ /* void */ /* ssl_set_min_version (ntbtls_t ssl, int major, int minor) */ /* { */ /* if ( major >= TLS_MIN_MAJOR_VERSION && major <= TLS_MAX_MAJOR_VERSION */ /* && minor >= TLS_MIN_MINOR_VERSION && minor <= TLS_MAX_MINOR_VERSION) */ /* { */ /* ssl->min_major_ver = major; */ /* ssl->min_minor_ver = minor; */ /* } */ /* } */ int ssl_set_max_frag_len (ntbtls_t ssl, unsigned char mfl_code) { if (mfl_code >= DIM(mfl_code_to_length) || mfl_code_to_length[mfl_code] > TLS_MAX_CONTENT_LEN) { return gpg_error (GPG_ERR_INV_ARG); } ssl->mfl_code = mfl_code; return (0); } int ssl_set_truncated_hmac (ntbtls_t ssl, int truncate) { if (!ssl->is_client) return gpg_error (GPG_ERR_INV_ARG); ssl->use_trunc_hmac = !!truncate; return 0; } void ssl_set_renegotiation (ntbtls_t ssl, int renegotiation) { ssl->disable_renegotiation = renegotiation; } void ssl_legacy_renegotiation (ntbtls_t ssl, int allow_legacy) { ssl->allow_legacy_renegotiation = allow_legacy; } void ssl_set_renegotiation_enforced (ntbtls_t ssl, int max_records) { ssl->renego_max_records = max_records; } int _ntbtls_set_session_tickets (ntbtls_t tls, int use_tickets) { tls->use_session_tickets = !!use_tickets; if (tls->is_client) return 0; return ticket_keys_setup (tls); } void ssl_set_session_ticket_lifetime (ntbtls_t ssl, int lifetime) { ssl->ticket_lifetime = lifetime; } /* * SSL get accessors */ size_t ssl_get_bytes_avail (const ntbtls_t ssl) { return (ssl->in_offt == NULL ? 0 : ssl->in_msglen); } int ssl_get_verify_result (const ntbtls_t ssl) { return (ssl->session->verify_result); } /* * Return the name of the current ciphersuite */ const char * _ntbtls_get_ciphersuite (const ntbtls_t tls) { if (!tls || !tls->session) return NULL; return _ntbtls_ciphersuite_get_name (tls->session->ciphersuite); } /* const x509_crt * */ /* ssl_get_peer_chain (const ntbtls_t ssl) */ /* { */ /* if (ssl == NULL || ssl->session == NULL) */ /* return (NULL); */ /* return (ssl->session->peer_chain); */ /* } */ /* Save session in order to resume it later (client-side only). Session data is copied to presented session structure. */ gpg_error_t _ntbtls_get_session (const ntbtls_t tls, session_t dst) { if (!tls || !dst || !tls->session || !tls->is_client) { return gpg_error (GPG_ERR_INV_ARG); } return session_copy (dst, tls->session); } /* * Perform a single step of the SSL handshake */ static gpg_error_t handshake_step (ntbtls_t tls) { gpg_error_t err; if (tls->is_client) err = _ntbtls_handshake_client_step (tls); else err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*_ntbtls_handshake_server_step (tls);*/ return err; } /* * Perform the SSL handshake */ gpg_error_t _ntbtls_handshake (ntbtls_t tls) { gpg_error_t err = 0; debug_msg (2, "handshake"); while (tls->state != TLS_HANDSHAKE_OVER) { err = handshake_step (tls); if (err) break; } debug_msg (2, "handshake ready"); return err; } /* * Write HelloRequest to request renegotiation on server */ static int write_hello_request (ntbtls_t ssl) { int ret; debug_msg (2, "write hello_request"); ssl->out_msglen = 4; ssl->out_msgtype = TLS_MSG_HANDSHAKE; ssl->out_msg[0] = TLS_HS_HELLO_REQUEST; ret = _ntbtls_write_record (ssl); if (ret) { debug_ret (1, "write_record", ret); return ret; } ssl->renegotiation = TLS_RENEGOTIATION_PENDING; return (0); } /* * Actually renegotiate current connection, triggered by either: * - calling ssl_renegotiate() on client, * - receiving a HelloRequest on client during ssl_read(), * - receiving any handshake message on server during ssl_read() after the * initial handshake is completed * If the handshake doesn't complete due to waiting for I/O, it will continue * during the next calls to ssl_renegotiate() or ssl_read() respectively. */ static gpg_error_t start_renegotiation (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "renegotiate"); err = handshake_init (tls); if (err) return err; tls->state = TLS_HELLO_REQUEST; tls->renegotiation = TLS_RENEGOTIATION; err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } return 0; } /* * Renegotiate current connection on client, * or request renegotiation on server */ int ssl_renegotiate (ntbtls_t ssl) { int ret = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /* On server, just send the request */ if (!ssl->is_client) { if (ssl->state != TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_ARG); return write_hello_request (ssl); } /* * On client, either start the renegotiation process or, * if already in progress, continue the handshake */ if (ssl->renegotiation != TLS_RENEGOTIATION) { if (ssl->state != TLS_HANDSHAKE_OVER) return gpg_error (GPG_ERR_INV_ARG); if ((ret = start_renegotiation (ssl)) != 0) { debug_ret (1, "start_renegotiation", ret); return (ret); } } else { if ((ret = _ntbtls_handshake (ssl)) != 0) { debug_ret (1, "handshake", ret); return (ret); } } return (ret); } /* * Notify the peer that the connection is being closed */ gpg_error_t _ntbtls_close_notify (ntbtls_t tls) { gpg_error_t err; debug_msg (2, "write close_notify"); err = _ntbtls_flush_output (tls); if (err) { debug_ret (1, "flush_output", err); return err; } if (tls->state == TLS_HANDSHAKE_OVER) { err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_MSG_CLOSE_NOTIFY); if (err) return err; } return err; } /* static void */ /* ssl_key_cert_free (ssl_key_cert * key_cert) */ /* { */ /* ssl_key_cert *cur = key_cert, *next; */ /* while (cur != NULL) */ /* { */ /* next = cur->next; */ /* if (cur->key_own_alloc) */ /* { */ /* pk_free (cur->key); */ /* polarssl_free (cur->key); */ /* } */ /* polarssl_free (cur); */ /* cur = next; */ /* } */ /* } */ /* * Map gcrypt algo number to TLS algo number, return ANON if the algo * is not supported. */ //FIXME: // unsigned char // ssl_sig_from_pk (pk_context * pk) // { // if (pk_can_do (pk, POLARSSL_PK_RSA)) // return (SSL_SIG_RSA); // #endif // #if defined(POLARSSL_ECDSA_C) // if (pk_can_do (pk, POLARSSL_PK_ECDSA)) // return (SSL_SIG_ECDSA); // #endif // return (SSL_SIG_ANON); // } /* * Map TLS signature algorithm number to a gcrypt algo number. */ pk_algo_t _ntbtls_pk_alg_from_sig (unsigned char sig) { switch (sig) { case TLS_SIG_ANON: return 0; case TLS_SIG_RSA: return GCRY_PK_RSA; case TLS_SIG_ECDSA: return GCRY_PK_ECC; } return 0; } /* * Map TLS hash algorithm number to a gcrypt algo number. */ md_algo_t _ntbtls_md_alg_from_hash (unsigned char hash) { switch (hash) { case TLS_HASH_SHA1: return GCRY_MD_SHA1; case TLS_HASH_SHA224: return GCRY_MD_SHA224; case TLS_HASH_SHA256: return GCRY_MD_SHA256; case TLS_HASH_SHA384: return GCRY_MD_SHA384; case TLS_HASH_SHA512: return GCRY_MD_SHA512; } return 0; } /* * Check is a curve proposed by the peer is in our list. * Return 1 if we're willing to use it, 0 otherwise. */ /* int */ /* ssl_curve_is_acceptable (const ntbtls_t ssl, ecp_group_id grp_id) */ /* { */ /* const ecp_group_id *gid; */ /* for (gid = ssl->curve_list; *gid != POLARSSL_ECP_DP_NONE; gid++) */ /* if (*gid == grp_id) */ /* return (1); */ /* return (0); */ /* } */ /* int */ /* ssl_check_cert_usage (const x509_crt * cert, */ /* const ssl_ciphersuite_t * ciphersuite, */ /* int is_client) */ /* { */ /* int usage = 0; */ /* const char *ext_oid; */ /* size_t ext_len; */ /* if (!is_client) */ /* { */ /* /\* Server part of the key exchange *\/ */ /* switch (ciphersuite->key_exchange) */ /* { */ /* case KEY_EXCHANGE_RSA: */ /* case KEY_EXCHANGE_RSA_PSK: */ /* usage = KU_KEY_ENCIPHERMENT; */ /* break; */ /* case KEY_EXCHANGE_DHE_RSA: */ /* case KEY_EXCHANGE_ECDHE_RSA: */ /* case KEY_EXCHANGE_ECDHE_ECDSA: */ /* usage = KU_DIGITAL_SIGNATURE; */ /* break; */ /* case KEY_EXCHANGE_ECDH_RSA: */ /* case KEY_EXCHANGE_ECDH_ECDSA: */ /* usage = KU_KEY_AGREEMENT; */ /* break; */ /* /\* Don't use default: we want warnings when adding new values *\/ */ /* case KEY_EXCHANGE_NONE: */ /* case KEY_EXCHANGE_PSK: */ /* case KEY_EXCHANGE_DHE_PSK: */ /* case KEY_EXCHANGE_ECDHE_PSK: */ /* usage = 0; */ /* break; */ /* } */ /* } */ /* else */ /* { */ /* /\* Client auth: we only implement rsa_sign and ecdsa_sign for now *\/ */ /* usage = KU_DIGITAL_SIGNATURE; */ /* } */ /* if (x509_crt_check_key_usage (cert, usage) != 0) */ /* return (-1); */ /* if (!is_client) */ /* { */ /* ext_oid = OID_SERVER_AUTH; */ /* ext_len = OID_SIZE (OID_SERVER_AUTH); */ /* } */ /* else */ /* { */ /* ext_oid = OID_CLIENT_AUTH; */ /* ext_len = OID_SIZE (OID_CLIENT_AUTH); */ /* } */ /* if (x509_crt_check_extended_key_usage (cert, ext_oid, ext_len) != 0) */ /* return (-1); */ /* return (0); */ /* } */ /* * Receive application data decrypted from the SSL layer */ static gpg_error_t tls_read (ntbtls_t tls, unsigned char *buf, size_t len, size_t *nread) { gpg_error_t err; size_t n; *nread = 0; debug_msg (2, "tls read"); if (tls->state != TLS_HANDSHAKE_OVER) { err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } } if (!tls->in_offt) { err = _ntbtls_read_record (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) return 0; debug_ret (1, "read_record", err); return err; } if (!tls->in_msglen && tls->in_msgtype == TLS_MSG_APPLICATION_DATA) { /* * OpenSSL sends empty messages to randomize the IV */ err = _ntbtls_read_record (tls); if (err) { if (gpg_err_code (err) == GPG_ERR_EOF) return 0; debug_ret (1, "read_record", err); return err; } } if (tls->in_msgtype == TLS_MSG_HANDSHAKE) { debug_msg (1, "received handshake message"); if (tls->is_client && (tls->in_msg[0] != TLS_HS_HELLO_REQUEST || tls->in_hslen != 4)) { debug_msg (1, "handshake received (not HelloRequest)"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } if (tls->disable_renegotiation == TLS_RENEGOTIATION_DISABLED || (tls->secure_renegotiation == TLS_LEGACY_RENEGOTIATION && (tls->allow_legacy_renegotiation == TLS_LEGACY_NO_RENEGOTIATION))) { debug_msg (3, "ignoring renegotiation, sending alert"); if (tls->minor_ver >= TLS_MINOR_VERSION_1) { err = _ntbtls_send_alert_message (tls, TLS_ALERT_LEVEL_WARNING, TLS_ALERT_MSG_NO_RENEGOTIATION); if (err) { return err; } } else { debug_bug (); return gpg_error (GPG_ERR_INTERNAL); } } else { err = start_renegotiation (tls); if (err) { debug_ret (1, "start_renegotiation", err); return err; } return gpg_error (GPG_ERR_EAGAIN); } } else if (tls->renegotiation == TLS_RENEGOTIATION_PENDING) { tls->renego_records_seen++; if (tls->renego_max_records >= 0 && tls->renego_records_seen > tls->renego_max_records) { debug_msg (1, "renegotiation requested, " "but not honored by client"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } } else if (tls->in_msgtype != TLS_MSG_APPLICATION_DATA) { debug_msg (1, "bad application data message"); return gpg_error (GPG_ERR_UNEXPECTED_MSG); } tls->in_offt = tls->in_msg; } if (!len) /* Check only for pending bytes. */ { return tls->in_msglen? 0 : gpg_error (GPG_ERR_EOF); } n = (len < tls->in_msglen) ? len : tls->in_msglen; memcpy (buf, tls->in_offt, n); tls->in_msglen -= n; if (!tls->in_msglen) /* All bytes consumed. */ tls->in_offt = NULL; else /* More data available. */ tls->in_offt += n; debug_msg (2, "tls read ready"); *nread = n; return 0; } /* * Send application data to be encrypted by the TLS layer. */ static gpg_error_t tls_write (ntbtls_t tls, const unsigned char *buf, size_t len, size_t *nwritten) { gpg_error_t err; size_t n; unsigned int max_len = TLS_MAX_CONTENT_LEN; *nwritten = 0; debug_msg (2, "tls write"); if (tls->state != TLS_HANDSHAKE_OVER) { err = _ntbtls_handshake (tls); if (err) { debug_ret (1, "handshake", err); return err; } } /* * Assume mfl_code is correct since it was checked when set */ max_len = mfl_code_to_length[tls->mfl_code]; /* * Check if a smaller max length was negotiated */ if (tls->session_out && mfl_code_to_length[tls->session_out->mfl_code] < max_len) { max_len = mfl_code_to_length[tls->session_out->mfl_code]; } n = (len < max_len) ? len : max_len; if (tls->out_left) { err = _ntbtls_flush_output (tls); if (err) { debug_ret (1, "flush_output", err); return err; } } else { tls->out_msglen = n; tls->out_msgtype = TLS_MSG_APPLICATION_DATA; memcpy (tls->out_msg, buf, n); err = _ntbtls_write_record (tls); if (err) { debug_ret (1, "write_record", err); return err; } } debug_msg (2, "tls write ready"); *nwritten = n; return 0; } /* Read handler for estream. */ static gpgrt_ssize_t cookie_read (void *cookie, void *buffer, size_t size) { ntbtls_t tls = cookie; gpg_error_t err; size_t nread; again: err = tls_read (tls, buffer, size, &nread); if (err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN && gpg_err_source (err) == GPG_ERR_SOURCE_TLS) goto again; /* I.e. renegotiation. */ if (!size && gpg_err_code (err) == GPG_ERR_EOF) return -1; /* Nope, no pending bytes. */ debug_ret (1, "tls_read", err); /* Fixme: We shoud extend estream to allow setting extended errors. */ gpg_err_set_errno (EIO); return -1; } else if (!size) nread = 0; /* Yep, there are pending bytes. */ return nread; } /* Write handler for estream. */ static gpgrt_ssize_t cookie_write (void *cookie, const void *buffer_arg, size_t size) { ntbtls_t tls = cookie; const char *buffer = buffer_arg; gpg_error_t err; size_t nwritten = 0; int nleft = size; again: while (nleft > 0) { err = tls_write (tls, buffer, nleft, &nwritten); if (err) { if (gpg_err_code (err) == GPG_ERR_EAGAIN && gpg_err_source (err) == GPG_ERR_SOURCE_TLS) goto again; /* I.e. renegotiation. */ debug_ret (1, "tls_write", err); gpg_err_set_errno (EIO); return -1; } nleft -= nwritten; buffer += nwritten; } return nwritten; } static gpgrt_cookie_io_functions_t cookie_functions = { cookie_read, cookie_write, NULL, NULL }; /* Return the two streams used to read and write the plaintext. The streams are valid as long as TLS is valid and may thus not be used after TLS has been destroyed. Note: After adding a "fullduplex" feature to estream we will allow to pass NULL for r_writefp to make use of that feature. */ gpg_error_t _ntbtls_get_stream (ntbtls_t tls, estream_t *r_readfp, estream_t *r_writefp) { gpg_error_t err; if (!tls || !r_readfp || !r_writefp) return gpg_error (GPG_ERR_INV_ARG); *r_readfp = NULL; *r_writefp = NULL; if ((!tls->readfp ^ !tls->writefp)) return gpg_error (GPG_ERR_INTERNAL); if (!tls->readfp) { if ((tls->flags & NTBTLS_SAMETRHEAD)) tls->readfp = es_fopencookie (tls, "r,samethread", cookie_functions); else tls->readfp = es_fopencookie (tls, "r", cookie_functions); if (!tls->readfp) { err = gpg_error_from_syserror (); return err; } } if (!tls->writefp) { if ((tls->flags & NTBTLS_SAMETRHEAD)) tls->writefp = es_fopencookie (tls, "w,samethread", cookie_functions); else tls->writefp = es_fopencookie (tls, "w", cookie_functions); if (!tls->writefp) { err = gpg_error_from_syserror (); es_fclose (tls->readfp); tls->readfp = NULL; return err; } } *r_readfp = tls->readfp; *r_writefp = tls->writefp; return 0; }