From 7b8b352039efd78338a7bf451a0550644ec8a8da Mon Sep 17 00:00:00 2001 From: Rene Mayrhofer Date: Sun, 28 Nov 2010 12:11:49 +0000 Subject: New upstream version. --- src/libtls/Makefile.am | 18 + src/libtls/Makefile.in | 559 ++++++++++++++ src/libtls/tls.c | 481 ++++++++++++ src/libtls/tls.h | 236 ++++++ src/libtls/tls_alert.c | 228 ++++++ src/libtls/tls_alert.h | 126 +++ src/libtls/tls_application.h | 63 ++ src/libtls/tls_compression.c | 72 ++ src/libtls/tls_compression.h | 80 ++ src/libtls/tls_crypto.c | 1674 ++++++++++++++++++++++++++++++++++++++++ src/libtls/tls_crypto.h | 554 +++++++++++++ src/libtls/tls_eap.c | 379 +++++++++ src/libtls/tls_eap.h | 81 ++ src/libtls/tls_fragmentation.c | 471 +++++++++++ src/libtls/tls_fragmentation.h | 88 +++ src/libtls/tls_handshake.h | 90 +++ src/libtls/tls_peer.c | 1099 ++++++++++++++++++++++++++ src/libtls/tls_peer.h | 54 ++ src/libtls/tls_prf.c | 190 +++++ src/libtls/tls_prf.h | 72 ++ src/libtls/tls_protection.c | 333 ++++++++ src/libtls/tls_protection.h | 98 +++ src/libtls/tls_reader.c | 200 +++++ src/libtls/tls_reader.h | 131 ++++ src/libtls/tls_server.c | 1032 +++++++++++++++++++++++++ src/libtls/tls_server.h | 55 ++ src/libtls/tls_socket.c | 219 ++++++ src/libtls/tls_socket.h | 75 ++ src/libtls/tls_writer.c | 237 ++++++ src/libtls/tls_writer.h | 136 ++++ 30 files changed, 9131 insertions(+) create mode 100644 src/libtls/Makefile.am create mode 100644 src/libtls/Makefile.in create mode 100644 src/libtls/tls.c create mode 100644 src/libtls/tls.h create mode 100644 src/libtls/tls_alert.c create mode 100644 src/libtls/tls_alert.h create mode 100644 src/libtls/tls_application.h create mode 100644 src/libtls/tls_compression.c create mode 100644 src/libtls/tls_compression.h create mode 100644 src/libtls/tls_crypto.c create mode 100644 src/libtls/tls_crypto.h create mode 100644 src/libtls/tls_eap.c create mode 100644 src/libtls/tls_eap.h create mode 100644 src/libtls/tls_fragmentation.c create mode 100644 src/libtls/tls_fragmentation.h create mode 100644 src/libtls/tls_handshake.h create mode 100644 src/libtls/tls_peer.c create mode 100644 src/libtls/tls_peer.h create mode 100644 src/libtls/tls_prf.c create mode 100644 src/libtls/tls_prf.h create mode 100644 src/libtls/tls_protection.c create mode 100644 src/libtls/tls_protection.h create mode 100644 src/libtls/tls_reader.c create mode 100644 src/libtls/tls_reader.h create mode 100644 src/libtls/tls_server.c create mode 100644 src/libtls/tls_server.h create mode 100644 src/libtls/tls_socket.c create mode 100644 src/libtls/tls_socket.h create mode 100644 src/libtls/tls_writer.c create mode 100644 src/libtls/tls_writer.h (limited to 'src/libtls') diff --git a/src/libtls/Makefile.am b/src/libtls/Makefile.am new file mode 100644 index 000000000..a58e783d7 --- /dev/null +++ b/src/libtls/Makefile.am @@ -0,0 +1,18 @@ + +INCLUDES = -I$(top_srcdir)/src/libstrongswan + +noinst_LTLIBRARIES = libtls.la +libtls_la_SOURCES = \ + tls_protection.h tls_protection.c \ + tls_compression.h tls_compression.c \ + tls_fragmentation.h tls_fragmentation.c \ + tls_alert.h tls_alert.c \ + tls_crypto.h tls_crypto.c \ + tls_prf.h tls_prf.c \ + tls_reader.h tls_reader.c \ + tls_writer.h tls_writer.c \ + tls_socket.h tls_socket.c \ + tls_eap.h tls_eap.c \ + tls_peer.h tls_peer.c \ + tls_server.h tls_server.c \ + tls_handshake.h tls_application.h tls.h tls.c diff --git a/src/libtls/Makefile.in b/src/libtls/Makefile.in new file mode 100644 index 000000000..9f0a817f5 --- /dev/null +++ b/src/libtls/Makefile.in @@ -0,0 +1,559 @@ +# Makefile.in generated by automake 1.11.1 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +subdir = src/libtls +DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/config/libtool.m4 \ + $(top_srcdir)/m4/config/ltoptions.m4 \ + $(top_srcdir)/m4/config/ltsugar.m4 \ + $(top_srcdir)/m4/config/ltversion.m4 \ + $(top_srcdir)/m4/config/lt~obsolete.m4 \ + $(top_srcdir)/m4/macros/with.m4 \ + $(top_srcdir)/m4/macros/enable-disable.m4 \ + $(top_srcdir)/m4/macros/add-plugin.m4 \ + $(top_srcdir)/configure.in +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +mkinstalldirs = $(install_sh) -d +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +LTLIBRARIES = $(noinst_LTLIBRARIES) +libtls_la_LIBADD = +am_libtls_la_OBJECTS = tls_protection.lo tls_compression.lo \ + tls_fragmentation.lo tls_alert.lo tls_crypto.lo tls_prf.lo \ + tls_reader.lo tls_writer.lo tls_socket.lo tls_eap.lo \ + tls_peer.lo tls_server.lo tls.lo +libtls_la_OBJECTS = $(am_libtls_la_OBJECTS) +DEFAULT_INCLUDES = -I.@am__isrc@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ + --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ + $(LDFLAGS) -o $@ +SOURCES = $(libtls_la_SOURCES) +DIST_SOURCES = $(libtls_la_SOURCES) +ETAGS = etags +CTAGS = ctags +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ALLOCA = @ALLOCA@ +AMTAR = @AMTAR@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BTLIB = @BTLIB@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLIB = @DLLIB@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GPERF = @GPERF@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LEX = @LEX@ +LEXLIB = @LEXLIB@ +LEX_OUTPUT_ROOT = @LEX_OUTPUT_ROOT@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBOBJS = @LTLIBOBJS@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +MYSQLCFLAG = @MYSQLCFLAG@ +MYSQLCONFIG = @MYSQLCONFIG@ +MYSQLLIB = @MYSQLLIB@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PERL = @PERL@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PTHREADLIB = @PTHREADLIB@ +RANLIB = @RANLIB@ +RTLIB = @RTLIB@ +RUBY = @RUBY@ +RUBYINCLUDE = @RUBYINCLUDE@ +SED = @SED@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOCKLIB = @SOCKLIB@ +STRIP = @STRIP@ +VERSION = @VERSION@ +YACC = @YACC@ +YFLAGS = @YFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +c_plugins = @c_plugins@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dbusservicedir = @dbusservicedir@ +default_pkcs11 = @default_pkcs11@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +gtk_CFLAGS = @gtk_CFLAGS@ +gtk_LIBS = @gtk_LIBS@ +h_plugins = @h_plugins@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +ipsecdir = @ipsecdir@ +ipsecgid = @ipsecgid@ +ipsecgroup = @ipsecgroup@ +ipsecuid = @ipsecuid@ +ipsecuser = @ipsecuser@ +libcharon_plugins = @libcharon_plugins@ +libdir = @libdir@ +libexecdir = @libexecdir@ +linux_headers = @linux_headers@ +localedir = @localedir@ +localstatedir = @localstatedir@ +lt_ECHO = @lt_ECHO@ +maemo_CFLAGS = @maemo_CFLAGS@ +maemo_LIBS = @maemo_LIBS@ +manager_plugins = @manager_plugins@ +mandir = @mandir@ +medsrv_plugins = @medsrv_plugins@ +mkdir_p = @mkdir_p@ +nm_CFLAGS = @nm_CFLAGS@ +nm_LIBS = @nm_LIBS@ +nm_ca_dir = @nm_ca_dir@ +oldincludedir = @oldincludedir@ +openac_plugins = @openac_plugins@ +p_plugins = @p_plugins@ +pdfdir = @pdfdir@ +piddir = @piddir@ +pki_plugins = @pki_plugins@ +plugindir = @plugindir@ +pluto_plugins = @pluto_plugins@ +pool_plugins = @pool_plugins@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +random_device = @random_device@ +resolv_conf = @resolv_conf@ +routing_table = @routing_table@ +routing_table_prio = @routing_table_prio@ +s_plugins = @s_plugins@ +sbindir = @sbindir@ +scepclient_plugins = @scepclient_plugins@ +scripts_plugins = @scripts_plugins@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +strongswan_conf = @strongswan_conf@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +urandom_device = @urandom_device@ +xml_CFLAGS = @xml_CFLAGS@ +xml_LIBS = @xml_LIBS@ +INCLUDES = -I$(top_srcdir)/src/libstrongswan +noinst_LTLIBRARIES = libtls.la +libtls_la_SOURCES = \ + tls_protection.h tls_protection.c \ + tls_compression.h tls_compression.c \ + tls_fragmentation.h tls_fragmentation.c \ + tls_alert.h tls_alert.c \ + tls_crypto.h tls_crypto.c \ + tls_prf.h tls_prf.c \ + tls_reader.h tls_reader.c \ + tls_writer.h tls_writer.c \ + tls_socket.h tls_socket.c \ + tls_eap.h tls_eap.c \ + tls_peer.h tls_peer.c \ + tls_server.h tls_server.c \ + tls_handshake.h tls_application.h tls.h tls.c + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/libtls/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu src/libtls/Makefile +.PRECIOUS: Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test "$$dir" != "$$p" || dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +libtls.la: $(libtls_la_OBJECTS) $(libtls_la_DEPENDENCIES) + $(LINK) $(libtls_la_OBJECTS) $(libtls_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_alert.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_compression.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_crypto.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_eap.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_fragmentation.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_peer.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_prf.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_protection.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_reader.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_server.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_socket.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tls_writer.Plo@am__quote@ + +.c.o: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c $< + +.c.obj: +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + mkid -fID $$unique +tags: TAGS + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + set x; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: CTAGS +CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in files) print i; }; }'`; \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool clean-noinstLTLIBRARIES \ + mostlyclean-am + +distclean: distclean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -rf ./$(DEPDIR) + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ + clean-libtool clean-noinstLTLIBRARIES ctags distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags uninstall uninstall-am + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/src/libtls/tls.c b/src/libtls/tls.c new file mode 100644 index 000000000..20141f235 --- /dev/null +++ b/src/libtls/tls.c @@ -0,0 +1,481 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls.h" + +#include + +#include "tls_protection.h" +#include "tls_compression.h" +#include "tls_fragmentation.h" +#include "tls_crypto.h" +#include "tls_server.h" +#include "tls_peer.h" + +ENUM_BEGIN(tls_version_names, SSL_2_0, SSL_2_0, + "SSLv2"); +ENUM_NEXT(tls_version_names, SSL_3_0, TLS_1_2, SSL_2_0, + "SSLv3", + "TLS 1.0", + "TLS 1.1", + "TLS 1.2"); +ENUM_END(tls_version_names, TLS_1_2); + +ENUM(tls_content_type_names, TLS_CHANGE_CIPHER_SPEC, TLS_APPLICATION_DATA, + "ChangeCipherSpec", + "Alert", + "Handshake", + "ApplicationData", +); + +ENUM_BEGIN(tls_handshake_type_names, TLS_HELLO_REQUEST, TLS_SERVER_HELLO, + "HelloRequest", + "ClientHello", + "ServerHello"); +ENUM_NEXT(tls_handshake_type_names, + TLS_CERTIFICATE, TLS_CLIENT_KEY_EXCHANGE, TLS_SERVER_HELLO, + "Certificate", + "ServerKeyExchange", + "CertificateRequest", + "ServerHelloDone", + "CertificateVerify", + "ClientKeyExchange"); +ENUM_NEXT(tls_handshake_type_names, + TLS_FINISHED, TLS_FINISHED, TLS_CLIENT_KEY_EXCHANGE, + "Finished"); +ENUM_END(tls_handshake_type_names, TLS_FINISHED); + +ENUM_BEGIN(tls_extension_names, TLS_EXT_SERVER_NAME, TLS_EXT_STATUS_REQUEST, + "server name", + "max fragment length", + "client certificate url", + "trusted ca keys", + "truncated hmac", + "status request"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_ELLIPTIC_CURVES, TLS_EXT_EC_POINT_FORMATS, + TLS_EXT_STATUS_REQUEST, + "elliptic curves", + "ec point formats"); +ENUM_NEXT(tls_extension_names, + TLS_EXT_SIGNATURE_ALGORITHMS, TLS_EXT_SIGNATURE_ALGORITHMS, + TLS_EXT_EC_POINT_FORMATS, + "signature algorithms"); +ENUM_END(tls_extension_names, TLS_EXT_SIGNATURE_ALGORITHMS); + +/** + * TLS record + */ +typedef struct __attribute__((packed)) { + u_int8_t type; + u_int16_t version; + u_int16_t length; + char data[]; +} tls_record_t; + +typedef struct private_tls_t private_tls_t; + +/** + * Private data of an tls_protection_t object. + */ +struct private_tls_t { + + /** + * Public tls_t interface. + */ + tls_t public; + + /** + * Role this TLS stack acts as. + */ + bool is_server; + + /** + * Server identity + */ + identification_t *server; + + /** + * Peer identity + */ + identification_t *peer; + + /** + * Negotiated TLS version + */ + tls_version_t version; + + /** + * TLS stack purpose, as given to constructor + */ + tls_purpose_t purpose; + + /** + * TLS record protection layer + */ + tls_protection_t *protection; + + /** + * TLS record compression layer + */ + tls_compression_t *compression; + + /** + * TLS record fragmentation layer + */ + tls_fragmentation_t *fragmentation; + + /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * TLS crypto helper context + */ + tls_crypto_t *crypto; + + /** + * TLS handshake protocol handler + */ + tls_handshake_t *handshake; + + /** + * TLS application data handler + */ + tls_application_t *application; + + /** + * Allocated input buffer + */ + chunk_t input; + + /** + * Number of bytes read in input buffer + */ + size_t inpos; + + /** + * Allocated output buffer + */ + chunk_t output; + + /** + * Number of bytes processed from output buffer + */ + size_t outpos; + + /** + * Partial TLS record header received + */ + tls_record_t head; + + /** + * Position in partially received record header + */ + size_t headpos; +}; + +METHOD(tls_t, process, status_t, + private_tls_t *this, void *buf, size_t buflen) +{ + tls_record_t *record; + status_t status; + u_int len; + + if (this->headpos) + { /* have a partial TLS record header, try to complete it */ + len = min(buflen, sizeof(this->head) - this->headpos); + memcpy(((char*)&this->head) + this->headpos, buf, len); + this->headpos += len; + buflen -= len; + buf += len; + if (this->headpos == sizeof(this->head)) + { /* header complete, allocate space with new header */ + len = untoh16(&this->head.length); + this->input = chunk_alloc(len + sizeof(tls_record_t)); + memcpy(this->input.ptr, &this->head, sizeof(this->head)); + this->inpos = sizeof(this->head); + this->headpos = 0; + } + } + + while (buflen) + { + if (this->input.len == 0) + { + if (buflen < sizeof(tls_record_t)) + { + DBG2(DBG_TLS, "received incomplete TLS record header"); + memcpy(&this->head, buf, buflen); + this->headpos = buflen; + break; + } + while (TRUE) + { + /* try to process records inline */ + record = buf; + len = untoh16(&record->length); + + if (len + sizeof(tls_record_t) > buflen) + { /* not a full record, read to buffer */ + this->input = chunk_alloc(len + sizeof(tls_record_t)); + this->inpos = 0; + break; + } + DBG2(DBG_TLS, "processing TLS %N record (%d bytes)", + tls_content_type_names, record->type, len); + status = this->protection->process(this->protection, + record->type, chunk_create(record->data, len)); + if (status != NEED_MORE) + { + return status; + } + buf += len + sizeof(tls_record_t); + buflen -= len + sizeof(tls_record_t); + if (buflen == 0) + { + return NEED_MORE; + } + } + } + len = min(buflen, this->input.len - this->inpos); + memcpy(this->input.ptr + this->inpos, buf, len); + buf += len; + buflen -= len; + this->inpos += len; + DBG2(DBG_TLS, "buffering %d bytes, %d bytes of %d byte TLS record received", + len, this->inpos, this->input.len); + if (this->input.len == this->inpos) + { + record = (tls_record_t*)this->input.ptr; + len = untoh16(&record->length); + + DBG2(DBG_TLS, "processing buffered TLS %N record (%d bytes)", + tls_content_type_names, record->type, len); + status = this->protection->process(this->protection, + record->type, chunk_create(record->data, len)); + chunk_free(&this->input); + this->inpos = 0; + if (status != NEED_MORE) + { + return status; + } + } + } + return NEED_MORE; +} + +METHOD(tls_t, build, status_t, + private_tls_t *this, void *buf, size_t *buflen, size_t *msglen) +{ + tls_content_type_t type; + tls_record_t record; + status_t status; + chunk_t data; + size_t len; + + len = *buflen; + if (this->output.len == 0) + { + /* query upper layers for new records, as many as we can get */ + while (TRUE) + { + status = this->protection->build(this->protection, &type, &data); + switch (status) + { + case NEED_MORE: + record.type = type; + htoun16(&record.version, this->version); + htoun16(&record.length, data.len); + this->output = chunk_cat("mcm", this->output, + chunk_from_thing(record), data); + DBG2(DBG_TLS, "sending TLS %N record (%d bytes)", + tls_content_type_names, type, data.len); + continue; + case INVALID_STATE: + if (this->output.len == 0) + { + return INVALID_STATE; + } + break; + default: + return status; + } + break; + } + if (msglen) + { + *msglen = this->output.len; + } + } + else + { + if (msglen) + { + *msglen = 0; + } + } + len = min(len, this->output.len - this->outpos); + memcpy(buf, this->output.ptr + this->outpos, len); + this->outpos += len; + *buflen = len; + if (this->outpos == this->output.len) + { + chunk_free(&this->output); + this->outpos = 0; + return ALREADY_DONE; + } + return NEED_MORE; +} + +METHOD(tls_t, is_server, bool, + private_tls_t *this) +{ + return this->is_server; +} + +METHOD(tls_t, get_version, tls_version_t, + private_tls_t *this) +{ + return this->version; +} + +METHOD(tls_t, set_version, bool, + private_tls_t *this, tls_version_t version) +{ + if (version > this->version) + { + return FALSE; + } + switch (version) + { + case TLS_1_0: + case TLS_1_1: + case TLS_1_2: + this->version = version; + this->protection->set_version(this->protection, version); + return TRUE; + case SSL_2_0: + case SSL_3_0: + default: + return FALSE; + } +} + +METHOD(tls_t, get_purpose, tls_purpose_t, + private_tls_t *this) +{ + return this->purpose; +} + +METHOD(tls_t, is_complete, bool, + private_tls_t *this) +{ + if (this->handshake->finished(this->handshake)) + { + if (!this->application) + { + return TRUE; + } + return this->fragmentation->application_finished(this->fragmentation); + } + return FALSE; +} + +METHOD(tls_t, get_eap_msk, chunk_t, + private_tls_t *this) +{ + return this->crypto->get_eap_msk(this->crypto); +} + +METHOD(tls_t, destroy, void, + private_tls_t *this) +{ + this->protection->destroy(this->protection); + this->compression->destroy(this->compression); + this->fragmentation->destroy(this->fragmentation); + this->crypto->destroy(this->crypto); + this->handshake->destroy(this->handshake); + DESTROY_IF(this->peer); + this->server->destroy(this->server); + DESTROY_IF(this->application); + this->alert->destroy(this->alert); + + free(this->input.ptr); + free(this->output.ptr); + + free(this); +} + +/** + * See header + */ +tls_t *tls_create(bool is_server, identification_t *server, + identification_t *peer, tls_purpose_t purpose, + tls_application_t *application) +{ + private_tls_t *this; + + switch (purpose) + { + case TLS_PURPOSE_EAP_TLS: + case TLS_PURPOSE_EAP_TTLS: + case TLS_PURPOSE_GENERIC: + break; + default: + return NULL; + } + + INIT(this, + .public = { + .process = _process, + .build = _build, + .is_server = _is_server, + .get_version = _get_version, + .set_version = _set_version, + .get_purpose = _get_purpose, + .is_complete = _is_complete, + .get_eap_msk = _get_eap_msk, + .destroy = _destroy, + }, + .is_server = is_server, + .version = TLS_1_2, + .server = server->clone(server), + .peer = peer ? peer->clone(peer) : NULL, + .application = application, + .purpose = purpose, + ); + + this->crypto = tls_crypto_create(&this->public); + this->alert = tls_alert_create(); + if (is_server) + { + this->handshake = &tls_server_create(&this->public, this->crypto, + this->alert, this->server, this->peer)->handshake; + } + else + { + this->handshake = &tls_peer_create(&this->public, this->crypto, + this->alert, this->peer, this->server)->handshake; + } + this->fragmentation = tls_fragmentation_create(this->handshake, this->alert, + this->application); + this->compression = tls_compression_create(this->fragmentation, this->alert); + this->protection = tls_protection_create(this->compression, this->alert); + this->crypto->set_protection(this->crypto, this->protection); + + return &this->public; +} diff --git a/src/libtls/tls.h b/src/libtls/tls.h new file mode 100644 index 000000000..1908f5dd4 --- /dev/null +++ b/src/libtls/tls.h @@ -0,0 +1,236 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup libtls libtls + * + * @addtogroup libtls + * TLS implementation on top of libstrongswan + * + * @defgroup tls tls + * @{ @ingroup libtls + */ + +#ifndef TLS_H_ +#define TLS_H_ + +typedef enum tls_version_t tls_version_t; +typedef enum tls_content_type_t tls_content_type_t; +typedef enum tls_handshake_type_t tls_handshake_type_t; +typedef enum tls_purpose_t tls_purpose_t; +typedef struct tls_t tls_t; + +#include + +#include "tls_application.h" + +/** + * TLS/SSL version numbers + */ +enum tls_version_t { + SSL_2_0 = 0x0200, + SSL_3_0 = 0x0300, + TLS_1_0 = 0x0301, + TLS_1_1 = 0x0302, + TLS_1_2 = 0x0303, +}; + +/** + * Enum names for tls_version_t + */ +extern enum_name_t *tls_version_names; + +/** + * TLS higher level content type + */ +enum tls_content_type_t { + TLS_CHANGE_CIPHER_SPEC = 20, + TLS_ALERT = 21, + TLS_HANDSHAKE = 22, + TLS_APPLICATION_DATA = 23, +}; + +/** + * Enum names for tls_content_type_t + */ +extern enum_name_t *tls_content_type_names; + +/** + * TLS handshake subtype + */ +enum tls_handshake_type_t { + TLS_HELLO_REQUEST = 0, + TLS_CLIENT_HELLO = 1, + TLS_SERVER_HELLO = 2, + TLS_CERTIFICATE = 11, + TLS_SERVER_KEY_EXCHANGE = 12, + TLS_CERTIFICATE_REQUEST = 13, + TLS_SERVER_HELLO_DONE = 14, + TLS_CERTIFICATE_VERIFY = 15, + TLS_CLIENT_KEY_EXCHANGE = 16, + TLS_FINISHED = 20, +}; + +/** + * Enum names for tls_handshake_type_t + */ +extern enum_name_t *tls_handshake_type_names; + +/** + * Purpose the TLS stack is initiated for. + */ +enum tls_purpose_t { + /** authentication in EAP-TLS */ + TLS_PURPOSE_EAP_TLS, + /** outer authentication and protection in EAP-TTLS */ + TLS_PURPOSE_EAP_TTLS, + /** non-EAP TLS */ + TLS_PURPOSE_GENERIC, + /** EAP binding for TNC */ + TLS_PURPOSE_EAP_TNC +}; + +/** + * TLS Hello extension types. + */ +enum tls_extension_t { + /** Server name the client wants to talk to */ + TLS_EXT_SERVER_NAME = 0, + /** request a maximum fragment size */ + TLS_EXT_MAX_FRAGMENT_LENGTH = 1, + /** indicate client certificate URL support */ + TLS_EXT_CLIENT_CERTIFICATE_URL = 2, + /** list of CA the client trusts */ + TLS_EXT_TRUSTED_CA_KEYS = 3, + /** request MAC truncation to 80-bit */ + TLS_EXT_TRUNCATED_HMAC = 4, + /** list of OCSP responders the client trusts */ + TLS_EXT_STATUS_REQUEST = 5, + /** list of supported elliptic curves */ + TLS_EXT_ELLIPTIC_CURVES = 10, + /** supported point formats */ + TLS_EXT_EC_POINT_FORMATS = 11, + /** list supported signature algorithms */ + TLS_EXT_SIGNATURE_ALGORITHMS = 13, +}; + +/** + * Enum names for tls_extension_t + */ +extern enum_name_t *tls_extension_names; + +/** + * A bottom-up driven TLS stack, suitable for EAP implementations. + */ +struct tls_t { + + /** + * Process one or more TLS records, pass it to upper layers. + * + * @param buf TLS record data, including headers + * @param buflen number of bytes in buf to process + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if more invocations to process/build needed + */ + status_t (*process)(tls_t *this, void *buf, size_t buflen); + + /** + * Query upper layer for one or more TLS records, build fragments. + * + * The TLS stack automatically fragments the records to the given buffer + * size. Fragmentation is indicated by the reclen ouput parameter and + * the return value. For the first fragment of a TLS record, a non-zero + * record length is returned in reclen. If more fragments follow, NEED_MORE + * is returned. A return value of ALREADY_DONE indicates that the final + * fragment has been returned. + * + * @param buf buffer to write TLS record fragments to + * @param buflen size of buffer, receives bytes written + * @param msglen receives size of all TLS fragments + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - INVALID_STATE if more input data required + * - NEED_MORE if more fragments available + * - ALREADY_DONE if the last available fragment returned + */ + status_t (*build)(tls_t *this, void *buf, size_t *buflen, size_t *msglen); + + /** + * Check if TLS stack is acting as a server. + * + * @return TRUE if server, FALSE if peer + */ + bool (*is_server)(tls_t *this); + + /** + * Get the negotiated TLS/SSL version. + * + * @return negotiated TLS version + */ + tls_version_t (*get_version)(tls_t *this); + + /** + * Set the negotiated TLS/SSL version. + * + * @param version negotiated TLS version + * @return TRUE if version acceptable + */ + bool (*set_version)(tls_t *this, tls_version_t version); + + /** + * Get the purpose of this TLS stack instance. + * + * @return purpose given during construction + */ + tls_purpose_t (*get_purpose)(tls_t *this); + + /** + * Check if TLS negotiation completed successfully. + * + * @return TRUE if TLS negotation and authentication complete + */ + bool (*is_complete)(tls_t *this); + + /** + * Get the MSK for EAP-TLS. + * + * @return MSK, internal data + */ + chunk_t (*get_eap_msk)(tls_t *this); + + /** + * Destroy a tls_t. + */ + void (*destroy)(tls_t *this); +}; + +/** + * Create a tls instance. + * + * @param is_server TRUE to act as server, FALSE for client + * @param server server identity + * @param peer peer identity, NULL for no client authentication + * @param purpose purpose this TLS stack instance is used for + * @param application higher layer application or NULL if none + * @return TLS stack + */ +tls_t *tls_create(bool is_server, identification_t *server, + identification_t *peer, tls_purpose_t purpose, + tls_application_t *application); + +#endif /** TLS_H_ @}*/ diff --git a/src/libtls/tls_alert.c b/src/libtls/tls_alert.c new file mode 100644 index 000000000..8a4fa7d77 --- /dev/null +++ b/src/libtls/tls_alert.c @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_alert.h" + +#include +#include + +ENUM_BEGIN(tls_alert_desc_names, TLS_CLOSE_NOTIFY, TLS_CLOSE_NOTIFY, + "close notify", +); +ENUM_NEXT(tls_alert_desc_names, TLS_UNEXPECTED_MESSAGE, TLS_UNEXPECTED_MESSAGE, + TLS_CLOSE_NOTIFY, + "unexpected message", +); +ENUM_NEXT(tls_alert_desc_names, TLS_BAD_RECORD_MAC, TLS_RECORD_OVERFLOW, + TLS_UNEXPECTED_MESSAGE, + "bad record mac", + "decryption failed", + "record overflow", +); +ENUM_NEXT(tls_alert_desc_names, TLS_DECOMPRESSION_FAILURE, TLS_DECOMPRESSION_FAILURE, + TLS_RECORD_OVERFLOW, + "decompression_failure", +); +ENUM_NEXT(tls_alert_desc_names, TLS_HANDSHAKE_FAILURE, TLS_DECRYPT_ERROR, + TLS_DECOMPRESSION_FAILURE, + "handshake failure", + "no certificate", + "bad certificate", + "unsupported certificate", + "certificate revoked", + "certificate expired", + "certificate unknown", + "illegal parameter", + "unknown ca", + "access denied", + "decode error", + "decrypt error", +); +ENUM_NEXT(tls_alert_desc_names, TLS_EXPORT_RESTRICTION, TLS_EXPORT_RESTRICTION, + TLS_DECRYPT_ERROR, + "export restriction", +); +ENUM_NEXT(tls_alert_desc_names, TLS_PROTOCOL_VERSION, TLS_INSUFFICIENT_SECURITY, + TLS_EXPORT_RESTRICTION, + "protocol version", + "insufficient security", +); +ENUM_NEXT(tls_alert_desc_names, TLS_INTERNAL_ERROR, TLS_INTERNAL_ERROR, + TLS_INSUFFICIENT_SECURITY, + "internal error", +); +ENUM_NEXT(tls_alert_desc_names, TLS_USER_CANCELED, TLS_USER_CANCELED, + TLS_INTERNAL_ERROR, + "user canceled", +); +ENUM_NEXT(tls_alert_desc_names, TLS_NO_RENEGOTIATION, TLS_NO_RENEGOTIATION, + TLS_USER_CANCELED, + "no renegotiation", +); +ENUM_NEXT(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION, TLS_UNSUPPORTED_EXTENSION, + TLS_NO_RENEGOTIATION, + "unsupported extension", +); +ENUM_END(tls_alert_desc_names, TLS_UNSUPPORTED_EXTENSION); + + +typedef struct private_tls_alert_t private_tls_alert_t; + +/** + * Private data of an tls_alert_t object. + */ +struct private_tls_alert_t { + + /** + * Public tls_alert_t interface. + */ + tls_alert_t public; + + /** + * Warning queue + */ + linked_list_t *warnings; + + /** + * Do we have a fatal alert? + */ + bool fatal; + + /** + * Has the fatal alert been consumed? + */ + bool consumed; + + /** + * Fatal alert discription + */ + tls_alert_desc_t desc; +}; + +METHOD(tls_alert_t, add, void, + private_tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t desc) +{ + if (level == TLS_FATAL) + { + if (!this->fatal) + { + this->desc = desc; + this->fatal = TRUE; + } + } + else + { + this->warnings->insert_last(this->warnings, (void*)(uintptr_t)desc); + } +} + +METHOD(tls_alert_t, get, bool, + private_tls_alert_t *this, tls_alert_level_t *level, + tls_alert_desc_t *desc) +{ + if (this->fatal && !this->consumed) + { + this->consumed = TRUE; + *level = TLS_FATAL; + *desc = this->desc; + if (this->desc == TLS_CLOSE_NOTIFY) + { + DBG1(DBG_TLS, "sending TLS close notify"); + } + else + { + DBG1(DBG_TLS, "sending fatal TLS alert '%N'", + tls_alert_desc_names, this->desc); + } + return TRUE; + } + else + { + uintptr_t warning; + + if (this->warnings->remove_first(this->warnings, + (void**)&warning) == SUCCESS) + { + *level = TLS_WARNING; + *desc = warning; + DBG1(DBG_TLS, "sending TLS alert warning '%N'", + tls_alert_desc_names, warning); + return TRUE; + } + } + return FALSE; +} + +METHOD(tls_alert_t, fatal, bool, + private_tls_alert_t *this) +{ + return this->fatal; +} + +METHOD(tls_alert_t, process, status_t, + private_tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t desc) +{ + if (desc == TLS_CLOSE_NOTIFY) + { + DBG1(DBG_TLS, "received TLS close notify"); + add(this, TLS_FATAL, TLS_CLOSE_NOTIFY); + return NEED_MORE; + } + switch (level) + { + case TLS_WARNING: + DBG1(DBG_TLS, "received TLS alert warning '%N'", + tls_alert_desc_names, desc); + return NEED_MORE; + case TLS_FATAL: + DBG1(DBG_TLS, "received fatal TLS alert '%N'", + tls_alert_desc_names, desc); + return FAILED; + default: + DBG1(DBG_TLS, "received unknown TLS alert '%N'", + tls_alert_desc_names, desc); + return FAILED; + } +} + +METHOD(tls_alert_t, destroy, void, + private_tls_alert_t *this) +{ + this->warnings->destroy(this->warnings); + free(this); +} + +/** + * See header + */ +tls_alert_t *tls_alert_create() +{ + private_tls_alert_t *this; + + INIT(this, + .public = { + .add = _add, + .get = _get, + .fatal = _fatal, + .process = _process, + .destroy = _destroy, + }, + .warnings = linked_list_create(), + ); + + return &this->public; +} diff --git a/src/libtls/tls_alert.h b/src/libtls/tls_alert.h new file mode 100644 index 000000000..95ba4d91b --- /dev/null +++ b/src/libtls/tls_alert.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_alert tls_alert + * @{ @ingroup libtls + */ + +#ifndef TLS_ALERT_H_ +#define TLS_ALERT_H_ + +#include + +typedef struct tls_alert_t tls_alert_t; +typedef enum tls_alert_level_t tls_alert_level_t; +typedef enum tls_alert_desc_t tls_alert_desc_t; + +/** + * Level of a TLS alert + */ +enum tls_alert_level_t { + TLS_WARNING = 1, + TLS_FATAL = 2, +}; + +/** + * Description of a TLS alert + */ +enum tls_alert_desc_t { + TLS_CLOSE_NOTIFY = 0, + TLS_UNEXPECTED_MESSAGE = 10, + TLS_BAD_RECORD_MAC = 20, + TLS_DECRYPTION_FAILED = 21, + TLS_RECORD_OVERFLOW = 22, + TLS_DECOMPRESSION_FAILURE = 30, + TLS_HANDSHAKE_FAILURE = 40, + TLS_NO_CERTIFICATE = 41, + TLS_BAD_CERTIFICATE = 42, + TLS_UNSUPPORTED_CERTIFICATE = 43, + TLS_CERTIFICATE_REVOKED = 44, + TLS_CERTIFICATE_EXPIRED = 45, + TLS_CERTIFICATE_UNKNOWN = 46, + TLS_ILLEGAL_PARAMETER = 47, + TLS_UNKNOWN_CA = 48, + TLS_ACCESS_DENIED = 49, + TLS_DECODE_ERROR = 50, + TLS_DECRYPT_ERROR = 51, + TLS_EXPORT_RESTRICTION = 60, + TLS_PROTOCOL_VERSION = 70, + TLS_INSUFFICIENT_SECURITY = 71, + TLS_INTERNAL_ERROR = 80, + TLS_USER_CANCELED = 90, + TLS_NO_RENEGOTIATION = 100, + TLS_UNSUPPORTED_EXTENSION = 110, +}; + +/** + * Enum names for alert descriptions + */ +extern enum_name_t *tls_alert_desc_names; + +/** + * TLS alert handling. + */ +struct tls_alert_t { + + /** + * Add an alert to the TLS alert queue, will be sent. + * + * @param level level of TLS alert + * @param description description of alert + */ + void (*add)(tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t description); + + /** + * Get an alert pushed to the alert queue, to send. + * + * @param level receives TLS alert level + * @param description receives TLS alert description + * @return TRUE if returned an alert + */ + bool (*get)(tls_alert_t *this, tls_alert_level_t *level, + tls_alert_desc_t *description); + + /** + * Did a fatal alert occur?. + * + * @return TRUE if a fatal alert has occured + */ + bool (*fatal)(tls_alert_t *this); + + /** + * Process a received TLS alert. + * + * @param level level of received alert + * @param description alert description + * @return status to pass down to TLS stack + */ + status_t (*process)(tls_alert_t *this, tls_alert_level_t level, + tls_alert_desc_t description); + + /** + * Destroy a tls_alert_t. + */ + void (*destroy)(tls_alert_t *this); +}; + +/** + * Create a tls_alert instance. + */ +tls_alert_t *tls_alert_create(); + +#endif /** TLS_ALERT_H_ @}*/ diff --git a/src/libtls/tls_application.h b/src/libtls/tls_application.h new file mode 100644 index 000000000..b54a25e22 --- /dev/null +++ b/src/libtls/tls_application.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Andreas Steffen + * Copyright (C) 2010 HSR Hochschule fuer Technik Rapperswil + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_handshake tls_handshake + * @{ @ingroup libtls + */ + +#ifndef TLS_APPLICATION_H_ +#define TLS_APPLICATION_H_ + +typedef struct tls_application_t tls_application_t; + +#include "tls_reader.h" +#include "tls_writer.h" + +/** + * TLS application data interface. + */ +struct tls_application_t { + + /** + * Process received TLS application data. + * + * @param reader TLS data buffer + * @return + * - SUCCESS if application completed + * - FAILED if application data processing failed + * - NEED_MORE if another invocation of process/build needed + */ + status_t (*process)(tls_application_t *this, tls_reader_t *reader); + + /** + * Build TLS application data to send out. + * + * @param writer TLS data buffer to write to + * @return + * - SUCCESS if application completed + * - FAILED if application data build failed + * - NEED_MORE if more data ready for delivery + * - INVALID_STATE if more input to process() required + */ + status_t (*build)(tls_application_t *this, tls_writer_t *writer); + + /** + * Destroy a tls_application_t. + */ + void (*destroy)(tls_application_t *this); +}; + +#endif /** TLS_APPLICATION_H_ @}*/ diff --git a/src/libtls/tls_compression.c b/src/libtls/tls_compression.c new file mode 100644 index 000000000..68266cd0c --- /dev/null +++ b/src/libtls/tls_compression.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_compression.h" + +typedef struct private_tls_compression_t private_tls_compression_t; + +/** + * Private data of an tls_compression_t object. + */ +struct private_tls_compression_t { + + /** + * Public tls_compression_t interface. + */ + tls_compression_t public; + + /** + * Upper layer, TLS record fragmentation + */ + tls_fragmentation_t *fragmentation; +}; + +METHOD(tls_compression_t, process, status_t, + private_tls_compression_t *this, tls_content_type_t type, chunk_t data) +{ + return this->fragmentation->process(this->fragmentation, type, data); +} + +METHOD(tls_compression_t, build, status_t, + private_tls_compression_t *this, tls_content_type_t *type, chunk_t *data) +{ + return this->fragmentation->build(this->fragmentation, type, data); +} + +METHOD(tls_compression_t, destroy, void, + private_tls_compression_t *this) +{ + free(this); +} + +/** + * See header + */ +tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation, + tls_alert_t *alert) +{ + private_tls_compression_t *this; + + INIT(this, + .public = { + .process = _process, + .build = _build, + .destroy = _destroy, + }, + .fragmentation = fragmentation, + ); + + return &this->public; +} diff --git a/src/libtls/tls_compression.h b/src/libtls/tls_compression.h new file mode 100644 index 000000000..b4832ab06 --- /dev/null +++ b/src/libtls/tls_compression.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_compression tls_compression + * @{ @ingroup libtls + */ + +#ifndef TLS_COMPRESSION_H_ +#define TLS_COMPRESSION_H_ + +#include + +#include "tls.h" +#include "tls_alert.h" +#include "tls_fragmentation.h" + +typedef struct tls_compression_t tls_compression_t; + +/** + * TLS record protocol compression layer. + */ +struct tls_compression_t { + + /** + * Process a compressed TLS record, pass it to upper layers. + * + * @param type type of the TLS record to process + * @param data associated TLS record data + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if more invocations to process/build needed + */ + status_t (*process)(tls_compression_t *this, + tls_content_type_t type, chunk_t data); + + /** + * Query upper layer for TLS record, build compressed record. + * + * @param type type of the built TLS record + * @param data allocated data of the built TLS record + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if upper layers have more records to send + * - INVALID_STATE if more input records required + */ + status_t (*build)(tls_compression_t *this, + tls_content_type_t *type, chunk_t *data); + + /** + * Destroy a tls_compression_t. + */ + void (*destroy)(tls_compression_t *this); +}; + +/** + * Create a tls_compression instance. + * + * @param fragmentation fragmentation layer of TLS stack + * @param alert TLS alert handler + * @return TLS compression layer. + */ +tls_compression_t *tls_compression_create(tls_fragmentation_t *fragmentation, + tls_alert_t *alert); + +#endif /** TLS_COMPRESSION_H_ @}*/ diff --git a/src/libtls/tls_crypto.c b/src/libtls/tls_crypto.c new file mode 100644 index 000000000..78f2a796d --- /dev/null +++ b/src/libtls/tls_crypto.c @@ -0,0 +1,1674 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_crypto.h" + +#include + +ENUM_BEGIN(tls_cipher_suite_names, TLS_NULL_WITH_NULL_NULL, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, + "TLS_NULL_WITH_NULL_NULL", + "TLS_RSA_WITH_NULL_MD5", + "TLS_RSA_WITH_NULL_SHA", + "TLS_RSA_EXPORT_WITH_RC4_40_MD5", + "TLS_RSA_WITH_RC4_128_MD5", + "TLS_RSA_WITH_RC4_128_SHA", + "TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5", + "TLS_RSA_WITH_IDEA_CBC_SHA", + "TLS_RSA_EXPORT_WITH_DES40_CBC_SHA", + "TLS_RSA_WITH_DES_CBC_SHA", + "TLS_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DH_DSS_WITH_DES_CBC_SHA", + "TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DH_RSA_WITH_DES_CBC_SHA", + "TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DHE_DSS_WITH_DES_CBC_SHA", + "TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DHE_RSA_WITH_DES_CBC_SHA", + "TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_DH_anon_EXPORT_WITH_RC4_40_MD5", + "TLS_DH_anon_WITH_RC4_128_MD5", + "TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA", + "TLS_DH_anon_WITH_DES_CBC_SHA", + "TLS_DH_anon_WITH_3DES_EDE_CBC_SHA"); +ENUM_NEXT(tls_cipher_suite_names, TLS_KRB5_WITH_DES_CBC_SHA, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, + "TLS_KRB5_WITH_DES_CBC_SHA", + "TLS_KRB5_WITH_3DES_EDE_CBC_SHA", + "TLS_KRB5_WITH_RC4_128_SHA", + "TLS_KRB5_WITH_IDEA_CBC_SHA", + "TLS_KRB5_WITH_DES_CBC_MD5", + "TLS_KRB5_WITH_3DES_EDE_CBC_MD5", + "TLS_KRB5_WITH_RC4_128_MD5", + "TLS_KRB5_WITH_IDEA_CBC_MD5", + "TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA", + "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA", + "TLS_KRB5_EXPORT_WITH_RC4_40_SHA", + "TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5", + "TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5", + "TLS_KRB5_EXPORT_WITH_RC4_40_MD5", + "TLS_PSK_WITH_NULL_SHA", + "TLS_DHE_PSK_WITH_NULL_SHA", + "TLS_RSA_PSK_WITH_NULL_SHA", + "TLS_RSA_WITH_AES_128_CBC_SHA", + "TLS_DH_DSS_WITH_AES_128_CBC_SHA", + "TLS_DH_RSA_WITH_AES_128_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_DH_anon_WITH_AES_128_CBC_SHA", + "TLS_RSA_WITH_AES_256_CBC_SHA", + "TLS_DH_DSS_WITH_AES_256_CBC_SHA", + "TLS_DH_RSA_WITH_AES_256_CBC_SHA", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_DH_anon_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_NULL_SHA256", + "TLS_RSA_WITH_AES_128_CBC_SHA256 ", + "TLS_RSA_WITH_AES_256_CBC_SHA256", + "TLS_DH_DSS_WITH_AES_128_CBC_SHA256", + "TLS_DH_RSA_WITH_AES_128_CBC_SHA256", + "TLS_DHE_DSS_WITH_AES_128_CBC_SHA256", + "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA", + "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA", + "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA", + "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA", + "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA", + "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA"); +ENUM_NEXT(tls_cipher_suite_names, TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DH_anon_WITH_AES_256_CBC_SHA256, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA, + "TLS_DHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_DH_DSS_WITH_AES_256_CBC_SHA256", + "TLS_DH_RSA_WITH_AES_256_CBC_SHA256", + "TLS_DHE_DSS_WITH_AES_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256", + "TLS_DH_anon_WITH_AES_128_CBC_SHA256", + "TLS_DH_anon_WITH_AES_256_CBC_SHA256"); +ENUM_NEXT(tls_cipher_suite_names, TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, + TLS_DH_anon_WITH_AES_256_CBC_SHA256, + "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA", + "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA", + "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA", + "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA", + "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA", + "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA", + "TLS_PSK_WITH_RC4_128_SHA", + "TLS_PSK_WITH_3DES_EDE_CBC_SHA2", + "TLS_PSK_WITH_AES_128_CBC_SHA", + "TLS_PSK_WITH_AES_256_CBC_SHA", + "TLS_DHE_PSK_WITH_RC4_128_SHA", + "TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA", + "TLS_DHE_PSK_WITH_AES_128_CBC_SHA", + "TLS_DHE_PSK_WITH_AES_256_CBC_SHA2", + "TLS_RSA_PSK_WITH_RC4_128_SHA", + "TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA", + "TLS_RSA_PSK_WITH_AES_128_CBC_SHA", + "TLS_RSA_PSK_WITH_AES_256_CBC_SHA", + "TLS_RSA_WITH_SEED_CBC_SHA", + "TLS_DH_DSS_WITH_SEED_CBC_SHA", + "TLS_DH_RSA_WITH_SEED_CBC_SHA", + "TLS_DHE_DSS_WITH_SEED_CBC_SHA", + "TLS_DHE_RSA_WITH_SEED_CBC_SHA", + "TLS_DH_anon_WITH_SEED_CBC_SHA", + "TLS_RSA_WITH_AES_128_GCM_SHA256", + "TLS_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_DH_RSA_WITH_AES_256_GCM_SHA384", + "TLS_DHE_DSS_WITH_AES_128_GCM_SHA256", + "TLS_DHE_DSS_WITH_AES_256_GCM_SHA384", + "TLS_DH_DSS_WITH_AES_128_GCM_SHA256", + "TLS_DH_DSS_WITH_AES_256_GCM_SHA384", + "TLS_DH_anon_WITH_AES_128_GCM_SHA256", + "TLS_DH_anon_WITH_AES_256_GCM_SHA384", + "TLS_PSK_WITH_AES_128_GCM_SHA256", + "TLS_PSK_WITH_AES_256_GCM_SHA384", + "TLS_DHE_PSK_WITH_AES_128_GCM_SHA256", + "TLS_DHE_PSK_WITH_AES_256_GCM_SHA384", + "TLS_RSA_PSK_WITH_AES_128_GCM_SHA256", + "TLS_RSA_PSK_WITH_AES_256_GCM_SHA384", + "TLS_PSK_WITH_AES_128_CBC_SHA256", + "TLS_PSK_WITH_AES_256_CBC_SHA384", + "TLS_PSK_WITH_NULL_SHA256", + "TLS_PSK_WITH_NULL_SHA384", + "TLS_DHE_PSK_WITH_AES_128_CBC_SHA256", + "TLS_DHE_PSK_WITH_AES_256_CBC_SHA384", + "TLS_DHE_PSK_WITH_NULL_SHA256", + "TLS_DHE_PSK_WITH_NULL_SHA384", + "TLS_RSA_PSK_WITH_AES_128_CBC_SHA256", + "TLS_RSA_PSK_WITH_AES_256_CBC_SHA384", + "TLS_RSA_PSK_WITH_NULL_SHA256", + "TLS_RSA_PSK_WITH_NULL_SHA384", + "TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256", + "TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256", + "TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256", + "TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256", + "TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256", + "TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256", + "TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256"); +ENUM_NEXT(tls_cipher_suite_names, TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256, + "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"); +ENUM_NEXT(tls_cipher_suite_names, TLS_ECDH_ECDSA_WITH_NULL_SHA, + TLS_ECDHE_PSK_WITH_NULL_SHA384, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, + "TLS_ECDH_ECDSA_WITH_NULL_SHA", + "TLS_ECDH_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_NULL_SHA", + "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA", + "TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_RSA_WITH_NULL_SHA", + "TLS_ECDH_RSA_WITH_RC4_128_SHA", + "TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_RSA_WITH_NULL_SHA", + "TLS_ECDHE_RSA_WITH_RC4_128_SHA", + "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA", + "TLS_ECDH_anon_WITH_NULL_SHA", + "TLS_ECDH_anon_WITH_RC4_128_SHA", + "TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_128_CBC_SHA", + "TLS_ECDH_anon_WITH_AES_256_CBC_SHA", + "TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA", + "TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA", + "TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA", + "TLS_SRP_SHA_WITH_AES_128_CBC_SHA", + "TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA", + "TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA", + "TLS_SRP_SHA_WITH_AES_256_CBC_SHA", + "TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA", + "TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256", + "TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256", + "TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384", + "TLS_ECDHE_PSK_WITH_RC4_128_SHA", + "TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA", + "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA", + "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA", + "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256", + "TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384", + "TLS_ECDHE_PSK_WITH_NULL_SHA", + "TLS_ECDHE_PSK_WITH_NULL_SHA256", + "TLS_ECDHE_PSK_WITH_NULL_SHA384"); +ENUM_END(tls_cipher_suite_names, TLS_ECDHE_PSK_WITH_NULL_SHA384); + +ENUM(tls_hash_algorithm_names, TLS_HASH_NONE, TLS_HASH_SHA512, + "NONE", + "MD5", + "SHA1", + "SHA224", + "SHA256", + "SHA384", + "SHA512", +); + +ENUM(tls_signature_algorithm_names, TLS_SIG_RSA, TLS_SIG_ECDSA, + "RSA", + "DSA", + "ECDSA", +); + +ENUM_BEGIN(tls_client_certificate_type_names, + TLS_RSA_SIGN, TLS_DSS_EPHEMERAL_DH, + "RSA_SIGN", + "DSA_SIGN", + "RSA_FIXED_DH", + "DSS_FIXED_DH", + "RSA_EPHEMERAL_DH", + "DSS_EPHEMERAL_DH"); +ENUM_NEXT(tls_client_certificate_type_names, + TLS_FORTEZZA_DMS, TLS_FORTEZZA_DMS, TLS_DSS_EPHEMERAL_DH, + "FORTEZZA_DMS"); +ENUM_NEXT(tls_client_certificate_type_names, + TLS_ECDSA_SIGN, TLS_ECDSA_FIXED_ECDH, TLS_FORTEZZA_DMS, + "ECDSA_SIGN", + "RSA_FIXED_ECDH", + "ECDSA_FIXED_ECDH"); +ENUM_END(tls_client_certificate_type_names, TLS_ECDSA_FIXED_ECDH); + +ENUM(tls_ecc_curve_type_names, TLS_ECC_EXPLICIT_PRIME, TLS_ECC_NAMED_CURVE, + "EXPLICIT_PRIME", + "EXPLICIT_CHAR2", + "NAMED_CURVE", +); + +ENUM(tls_named_curve_names, TLS_SECT163K1, TLS_SECP521R1, + "SECT163K1", + "SECT163R1", + "SECT163R2", + "SECT193R1", + "SECT193R2", + "SECT233K1", + "SECT233R1", + "SECT239K1", + "SECT283K1", + "SECT283R1", + "SECT409K1", + "SECT409R1", + "SECT571K1", + "SECT571R1", + "SECP160K1", + "SECP160R1", + "SECP160R2", + "SECP192K1", + "SECP192R1", + "SECP224K1", + "SECP224R1", + "SECP256K1", + "SECP256R1", + "SECP384R1", + "SECP521R1", +); + +ENUM(tls_ansi_point_format_names, TLS_ANSI_COMPRESSED, TLS_ANSI_HYBRID_Y, + "compressed", + "compressed y", + "uncompressed", + "uncompressed y", + "hybrid", + "hybrid y", +); + +ENUM(tls_ec_point_format_names, + TLS_EC_POINT_UNCOMPRESSED, TLS_EC_POINT_ANSIX962_COMPRESSED_CHAR2, + "uncompressed", + "ansiX962 compressed prime", + "ansiX962 compressed char2", +); + +typedef struct private_tls_crypto_t private_tls_crypto_t; + +/** + * Private data of an tls_crypto_t object. + */ +struct private_tls_crypto_t { + + /** + * Public tls_crypto_t interface. + */ + tls_crypto_t public; + + /** + * Protection layer + */ + tls_protection_t *protection; + + /** + * List of supported/acceptable cipher suites + */ + tls_cipher_suite_t *suites; + + /** + * Number of supported suites + */ + int suite_count; + + /** + * Selected cipher suite + */ + tls_cipher_suite_t suite; + + /** + * RSA supported? + */ + bool rsa; + + /** + * ECDSA supported? + */ + bool ecdsa; + + /** + * TLS context + */ + tls_t *tls; + + /** + * All handshake data concatentated + */ + chunk_t handshake; + + /** + * Connection state TLS PRF + */ + tls_prf_t *prf; + + /** + * Signer instance for inbound traffic + */ + signer_t *signer_in; + + /** + * Signer instance for outbound traffic + */ + signer_t *signer_out; + + /** + * Crypter instance for inbound traffic + */ + crypter_t *crypter_in; + + /** + * Crypter instance for outbound traffic + */ + crypter_t *crypter_out; + + /** + * IV for input decryption, if < TLSv1.2 + */ + chunk_t iv_in; + + /** + * IV for output decryption, if < TLSv1.2 + */ + chunk_t iv_out; + + /** + * EAP-[T]TLS MSK + */ + chunk_t msk; + + /** + * ASCII string constant used as seed for EAP-[T]TLS MSK PRF + */ + char *msk_label; +}; + +typedef struct { + tls_cipher_suite_t suite; + key_type_t key; + diffie_hellman_group_t dh; + hash_algorithm_t hash; + pseudo_random_function_t prf; + integrity_algorithm_t mac; + encryption_algorithm_t encr; + size_t encr_size; +} suite_algs_t; + +/** + * Mapping suites to a set of algorithms + */ +static suite_algs_t suite_algs[] = { + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + }, + { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + KEY_ECDSA, ECP_384_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + }, + { TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + KEY_ECDSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32 + }, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + }, + { TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + KEY_RSA, ECP_256_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + KEY_RSA, ECP_384_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + }, + { TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + KEY_RSA, ECP_384_BIT, + HASH_SHA384, PRF_HMAC_SHA2_384, + AUTH_HMAC_SHA2_384_384, ENCR_AES_CBC, 32 + }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, MODP_2048_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + }, + { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + KEY_RSA, MODP_3072_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + KEY_RSA, MODP_3072_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + }, + { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + KEY_RSA, MODP_4096_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32 + }, + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA, + KEY_RSA, MODP_2048_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + }, + { TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256, + KEY_RSA, MODP_3072_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 + }, + { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA, + KEY_RSA, MODP_3072_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 + }, + { TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256, + KEY_RSA, MODP_4096_BIT, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32 + }, + { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + KEY_RSA, MODP_2048_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + }, + { TLS_RSA_WITH_AES_128_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 16 + }, + { TLS_RSA_WITH_AES_128_CBC_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 16 + }, + { TLS_RSA_WITH_AES_256_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_AES_CBC, 32 + }, + { TLS_RSA_WITH_AES_256_CBC_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_AES_CBC, 32 + }, + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 16 + }, + { TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 16 + }, + { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_CAMELLIA_CBC, 32 + }, + { TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_CAMELLIA_CBC, 32 + }, + { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + }, + { TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + KEY_RSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + }, + { TLS_RSA_WITH_3DES_EDE_CBC_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_3DES, 0 + }, + { TLS_ECDHE_ECDSA_WITH_NULL_SHA, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + }, + { TLS_ECDHE_RSA_WITH_NULL_SHA, + KEY_ECDSA, ECP_256_BIT, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + }, + { TLS_RSA_WITH_NULL_SHA, + KEY_RSA, MODP_NONE, + HASH_SHA1, PRF_HMAC_SHA1, + AUTH_HMAC_SHA1_160, ENCR_NULL, 0 + }, + { TLS_RSA_WITH_NULL_SHA256, + KEY_RSA, MODP_NONE, + HASH_SHA256, PRF_HMAC_SHA2_256, + AUTH_HMAC_SHA2_256_256, ENCR_NULL, 0 + }, + { TLS_RSA_WITH_NULL_MD5, + KEY_RSA, MODP_NONE, + HASH_MD5, PRF_HMAC_MD5, + AUTH_HMAC_MD5_128, ENCR_NULL, 0 + }, +}; + +/** + * Look up algoritms by a suite + */ +static suite_algs_t *find_suite(tls_cipher_suite_t suite) +{ + int i; + + for (i = 0; i < countof(suite_algs); i++) + { + if (suite_algs[i].suite == suite) + { + return &suite_algs[i]; + } + } + return NULL; +} + +/** + * Filter a suite list using a transform enumerator + */ +static void filter_suite(private_tls_crypto_t *this, + suite_algs_t suites[], int *count, int offset, + enumerator_t*(*create_enumerator)(crypto_factory_t*)) +{ + suite_algs_t current; + int i, remaining = 0; + enumerator_t *enumerator; + + memset(¤t, 0, sizeof(current)); + for (i = 0; i < *count; i++) + { + enumerator = create_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, ((char*)¤t) + offset)) + { + if ((suites[i].encr == ENCR_NULL || + !current.encr || current.encr == suites[i].encr) && + (!current.mac || current.mac == suites[i].mac) && + (!current.prf || current.prf == suites[i].prf) && + (!current.hash || current.hash == suites[i].hash) && + (suites[i].dh == MODP_NONE || + !current.dh || current.dh == suites[i].dh)) + { + suites[remaining] = suites[i]; + remaining++; + break; + } + } + enumerator->destroy(enumerator); + } + *count = remaining; +} + +/** + * Purge NULL encryption cipher suites from list + */ +static void filter_null_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count) +{ + int i, remaining = 0; + + for (i = 0; i < *count; i++) + { + if (suites[i].encr != ENCR_NULL) + { + suites[remaining] = suites[i]; + remaining++; + } + } + *count = remaining; +} + +/** + * Purge suites using a given key type + */ +static void filter_key_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count, key_type_t key) +{ + int i, remaining = 0; + + DBG2(DBG_TLS, "disabling %N suites, no backend found", key_type_names, key); + for (i = 0; i < *count; i++) + { + if (suites[i].key != key) + { + suites[remaining] = suites[i]; + remaining++; + } + } + *count = remaining; +} + +/** + * Filter suites by key exchange user config + */ +static void filter_key_exchange_config_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count) +{ + enumerator_t *enumerator; + int i, remaining = 0; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "libtls.key_exchange", NULL); + if (config) + { + for (i = 0; i < *count; i++) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (strcaseeq(token, "ecdhe-ecdsa") && + diffie_hellman_group_is_ec(suites[i].dh) && + suites[i].key == KEY_ECDSA) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "ecdhe-rsa") && + diffie_hellman_group_is_ec(suites[i].dh) && + suites[i].key == KEY_RSA) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "dhe-rsa") && + !diffie_hellman_group_is_ec(suites[i].dh) && + suites[i].dh != MODP_NONE && + suites[i].key == KEY_RSA) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "rsa") && + suites[i].dh == MODP_NONE && + suites[i].key == KEY_RSA) + { + suites[remaining++] = suites[i]; + break; + } + } + enumerator->destroy(enumerator); + } + *count = remaining; + } +} + +/** + * Filter suites by cipher user config + */ +static void filter_cipher_config_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count) +{ + enumerator_t *enumerator; + int i, remaining = 0; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "libtls.cipher", NULL); + if (config) + { + for (i = 0; i < *count; i++) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (strcaseeq(token, "aes128") && + suites[i].encr == ENCR_AES_CBC && + suites[i].encr_size == 16) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "aes256") && + suites[i].encr == ENCR_AES_CBC && + suites[i].encr_size == 32) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "camellia128") && + suites[i].encr == ENCR_CAMELLIA_CBC && + suites[i].encr_size == 16) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "camellia256") && + suites[i].encr == ENCR_CAMELLIA_CBC && + suites[i].encr_size == 32) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "3des") && + suites[i].encr == ENCR_3DES) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "null") && + suites[i].encr == ENCR_NULL) + { + suites[remaining++] = suites[i]; + break; + } + } + enumerator->destroy(enumerator); + } + *count = remaining; + } +} + +/** + * Filter suites by mac user config + */ +static void filter_mac_config_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count) +{ + enumerator_t *enumerator; + int i, remaining = 0; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "libtls.mac", NULL); + if (config) + { + for (i = 0; i < *count; i++) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + if (strcaseeq(token, "md5") && + suites[i].hash == HASH_MD5) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "sha1") && + suites[i].hash == HASH_SHA1) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "sha256") && + suites[i].hash == HASH_SHA256) + { + suites[remaining++] = suites[i]; + break; + } + if (strcaseeq(token, "sha384") && + suites[i].hash == HASH_SHA384) + { + suites[remaining++] = suites[i]; + break; + } + } + enumerator->destroy(enumerator); + } + *count = remaining; + } +} + +/** + * Filter for specific suites specified in strongswan.conf + */ +static void filter_specific_config_suites(private_tls_crypto_t *this, + suite_algs_t suites[], int *count) +{ + enumerator_t *enumerator; + int i, remaining = 0, suite; + char *token, *config; + + config = lib->settings->get_str(lib->settings, "libtls.suites", NULL); + if (config) + { + for (i = 0; i < *count; i++) + { + enumerator = enumerator_create_token(config, ",", " "); + while (enumerator->enumerate(enumerator, &token)) + { + suite = enum_from_name(tls_cipher_suite_names, token); + if (suite == suites[i].suite) + { + suites[remaining++] = suites[i]; + break; + } + } + enumerator->destroy(enumerator); + } + *count = remaining; + } +} + +/** + * Initialize the cipher suite list + */ +static void build_cipher_suite_list(private_tls_crypto_t *this, + bool require_encryption) +{ + suite_algs_t suites[countof(suite_algs)]; + int count = countof(suite_algs), i; + + /* copy all suites */ + for (i = 0; i < count; i++) + { + suites[i] = suite_algs[i]; + } + if (require_encryption) + { + filter_null_suites(this, suites, &count); + } + if (!this->rsa) + { + filter_key_suites(this, suites, &count, KEY_RSA); + } + if (!this->ecdsa) + { + filter_key_suites(this, suites, &count, KEY_ECDSA); + } + + /* filter suite list by each algorithm */ + filter_suite(this, suites, &count, offsetof(suite_algs_t, encr), + lib->crypto->create_crypter_enumerator); + filter_suite(this, suites, &count, offsetof(suite_algs_t, mac), + lib->crypto->create_signer_enumerator); + filter_suite(this, suites, &count, offsetof(suite_algs_t, prf), + lib->crypto->create_prf_enumerator); + filter_suite(this, suites, &count, offsetof(suite_algs_t, hash), + lib->crypto->create_hasher_enumerator); + filter_suite(this, suites, &count, offsetof(suite_algs_t, dh), + lib->crypto->create_dh_enumerator); + + /* filter suites with strongswan.conf options */ + filter_key_exchange_config_suites(this, suites, &count); + filter_cipher_config_suites(this, suites, &count); + filter_mac_config_suites(this, suites, &count); + filter_specific_config_suites(this, suites, &count); + + free(this->suites); + this->suite_count = count; + this->suites = malloc(sizeof(tls_cipher_suite_t) * count); + + DBG2(DBG_TLS, "%d supported TLS cipher suites:", count); + for (i = 0; i < count; i++) + { + DBG2(DBG_TLS, " %N", tls_cipher_suite_names, suites[i].suite); + this->suites[i] = suites[i].suite; + } +} + +METHOD(tls_crypto_t, get_cipher_suites, int, + private_tls_crypto_t *this, tls_cipher_suite_t **suites) +{ + *suites = this->suites; + return this->suite_count; +} + +/** + * Create crypto primitives + */ +static bool create_ciphers(private_tls_crypto_t *this, suite_algs_t *algs) +{ + DESTROY_IF(this->prf); + if (this->tls->get_version(this->tls) < TLS_1_2) + { + this->prf = tls_prf_create_10(); + } + else + { + this->prf = tls_prf_create_12(algs->prf); + } + if (!this->prf) + { + DBG1(DBG_TLS, "selected TLS PRF not supported"); + return FALSE; + } + + DESTROY_IF(this->signer_in); + DESTROY_IF(this->signer_out); + this->signer_in = lib->crypto->create_signer(lib->crypto, algs->mac); + this->signer_out = lib->crypto->create_signer(lib->crypto, algs->mac); + if (!this->signer_in || !this->signer_out) + { + DBG1(DBG_TLS, "selected TLS MAC %N not supported", + integrity_algorithm_names, algs->mac); + return FALSE; + } + + DESTROY_IF(this->crypter_in); + DESTROY_IF(this->crypter_out); + if (algs->encr == ENCR_NULL) + { + this->crypter_in = this->crypter_out = NULL; + } + else + { + this->crypter_in = lib->crypto->create_crypter(lib->crypto, + algs->encr, algs->encr_size); + this->crypter_out = lib->crypto->create_crypter(lib->crypto, + algs->encr, algs->encr_size); + if (!this->crypter_in || !this->crypter_out) + { + DBG1(DBG_TLS, "selected TLS crypter %N not supported", + encryption_algorithm_names, algs->encr); + return FALSE; + } + } + return TRUE; +} + +METHOD(tls_crypto_t, select_cipher_suite, tls_cipher_suite_t, + private_tls_crypto_t *this, tls_cipher_suite_t *suites, int count, + key_type_t key) +{ + suite_algs_t *algs; + int i, j; + + for (i = 0; i < this->suite_count; i++) + { + for (j = 0; j < count; j++) + { + if (this->suites[i] == suites[j]) + { + algs = find_suite(this->suites[i]); + if (algs) + { + if (key == KEY_ANY || key == algs->key) + { + if (create_ciphers(this, algs)) + { + this->suite = this->suites[i]; + return this->suite; + } + } + } + } + } + } + return 0; +} + +METHOD(tls_crypto_t, get_dh_group, diffie_hellman_group_t, + private_tls_crypto_t *this) +{ + suite_algs_t *algs; + + algs = find_suite(this->suite); + if (algs) + { + return algs->dh; + } + return MODP_NONE; +} + +METHOD(tls_crypto_t, get_signature_algorithms, void, + private_tls_crypto_t *this, tls_writer_t *writer) +{ + tls_writer_t *supported; + enumerator_t *enumerator; + hash_algorithm_t alg; + tls_hash_algorithm_t hash; + + supported = tls_writer_create(32); + enumerator = lib->crypto->create_hasher_enumerator(lib->crypto); + while (enumerator->enumerate(enumerator, &alg)) + { + switch (alg) + { + case HASH_MD5: + hash = TLS_HASH_MD5; + break; + case HASH_SHA1: + hash = TLS_HASH_SHA1; + break; + case HASH_SHA224: + hash = TLS_HASH_SHA224; + break; + case HASH_SHA256: + hash = TLS_HASH_SHA256; + break; + case HASH_SHA384: + hash = TLS_HASH_SHA384; + break; + case HASH_SHA512: + hash = TLS_HASH_SHA512; + break; + default: + continue; + } + if (this->rsa) + { + supported->write_uint8(supported, hash); + supported->write_uint8(supported, TLS_SIG_RSA); + } + if (this->ecdsa && alg != HASH_MD5 && alg != HASH_SHA224) + { /* currently we have no signature scheme for MD5/SHA224 */ + supported->write_uint8(supported, hash); + supported->write_uint8(supported, TLS_SIG_ECDSA); + } + } + enumerator->destroy(enumerator); + + writer->write_data16(writer, supported->get_buf(supported)); + supported->destroy(supported); +} + +/** + * Mapping groups to TLS named curves + */ +static struct { + diffie_hellman_group_t group; + tls_named_curve_t curve; +} curves[] = { + { ECP_256_BIT, TLS_SECP256R1}, + { ECP_384_BIT, TLS_SECP384R1}, + { ECP_521_BIT, TLS_SECP521R1}, + { ECP_224_BIT, TLS_SECP224R1}, + { ECP_192_BIT, TLS_SECP192R1}, +}; + +/** + * Filter EC groups, add TLS curve + */ +static bool group_filter(void *null, + diffie_hellman_group_t *in, diffie_hellman_group_t *out, + void* dummy1, tls_named_curve_t *curve) +{ + int i; + + for (i = 0; i < countof(curves); i++) + { + if (curves[i].group == *in) + { + if (out) + { + *out = curves[i].group; + } + if (curve) + { + *curve = curves[i].curve; + } + return TRUE; + } + } + return FALSE; +} + +METHOD(tls_crypto_t, create_ec_enumerator, enumerator_t*, + private_tls_crypto_t *this) +{ + return enumerator_create_filter( + lib->crypto->create_dh_enumerator(lib->crypto), + (void*)group_filter, NULL, NULL); +} + +METHOD(tls_crypto_t, set_protection, void, + private_tls_crypto_t *this, tls_protection_t *protection) +{ + this->protection = protection; +} + +METHOD(tls_crypto_t, append_handshake, void, + private_tls_crypto_t *this, tls_handshake_type_t type, chunk_t data) +{ + u_int32_t header; + + /* reconstruct handshake header */ + header = htonl(data.len | (type << 24)); + this->handshake = chunk_cat("mcc", this->handshake, + chunk_from_thing(header), data); +} + +/** + * Create a hash using the suites HASH algorithm + */ +static bool hash_data(private_tls_crypto_t *this, chunk_t data, chunk_t *hash) +{ + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + hasher_t *hasher; + suite_algs_t *alg; + + alg = find_suite(this->suite); + if (!alg) + { + return FALSE; + } + hasher = lib->crypto->create_hasher(lib->crypto, alg->hash); + if (!hasher) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, alg->hash); + return FALSE; + } + hasher->allocate_hash(hasher, data, hash); + hasher->destroy(hasher); + } + else + { + hasher_t *md5, *sha1; + char buf[HASH_SIZE_MD5 + HASH_SIZE_SHA1]; + + md5 = lib->crypto->create_hasher(lib->crypto, HASH_MD5); + if (!md5) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, HASH_MD5); + return FALSE; + } + md5->get_hash(md5, data, buf); + md5->destroy(md5); + sha1 = lib->crypto->create_hasher(lib->crypto, HASH_SHA1); + if (!sha1) + { + DBG1(DBG_TLS, "%N not supported", hash_algorithm_names, HASH_SHA1); + return FALSE; + } + sha1->get_hash(sha1, data, buf + HASH_SIZE_MD5); + sha1->destroy(sha1); + + *hash = chunk_clone(chunk_from_thing(buf)); + } + return TRUE; +} + +/** + * Get the signature scheme from a TLS 1.2 hash/sig algorithm pair + */ +static signature_scheme_t hashsig_to_scheme(key_type_t type, + tls_hash_algorithm_t hash, tls_signature_algorithm_t sig) +{ + switch (sig) + { + case TLS_SIG_RSA: + if (type != KEY_RSA) + { + return SIGN_UNKNOWN; + } + switch (hash) + { + case TLS_HASH_MD5: + return SIGN_RSA_EMSA_PKCS1_MD5; + case TLS_HASH_SHA1: + return SIGN_RSA_EMSA_PKCS1_SHA1; + case TLS_HASH_SHA224: + return SIGN_RSA_EMSA_PKCS1_SHA224; + case TLS_HASH_SHA256: + return SIGN_RSA_EMSA_PKCS1_SHA256; + case TLS_HASH_SHA384: + return SIGN_RSA_EMSA_PKCS1_SHA384; + case TLS_HASH_SHA512: + return SIGN_RSA_EMSA_PKCS1_SHA512; + default: + return SIGN_UNKNOWN; + } + case TLS_SIG_ECDSA: + if (type != KEY_ECDSA) + { + return SIGN_UNKNOWN; + } + switch (hash) + { + case TLS_HASH_SHA224: + return SIGN_ECDSA_WITH_SHA1_DER; + case TLS_HASH_SHA256: + return SIGN_ECDSA_WITH_SHA256_DER; + case TLS_HASH_SHA384: + return SIGN_ECDSA_WITH_SHA384_DER; + case TLS_HASH_SHA512: + return SIGN_ECDSA_WITH_SHA512_DER; + default: + return SIGN_UNKNOWN; + } + default: + return SIGN_UNKNOWN; + } +} + +METHOD(tls_crypto_t, sign, bool, + private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer, + chunk_t data, chunk_t hashsig) +{ + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + signature_scheme_t scheme; + tls_reader_t *reader; + u_int8_t hash, alg; + chunk_t sig; + bool done = FALSE; + + if (!hashsig.len) + { /* fallback if none given */ + hashsig = chunk_from_chars( + TLS_HASH_SHA1, TLS_SIG_RSA, TLS_HASH_SHA1, TLS_SIG_ECDSA); + } + reader = tls_reader_create(hashsig); + while (reader->remaining(reader) >= 2) + { + if (reader->read_uint8(reader, &hash) && + reader->read_uint8(reader, &alg)) + { + scheme = hashsig_to_scheme(key->get_type(key), hash, alg); + if (scheme != SIGN_UNKNOWN && + key->sign(key, scheme, data, &sig)) + { + done = TRUE; + break; + } + } + } + reader->destroy(reader); + if (!done) + { + DBG1(DBG_TLS, "none of the proposed hash/sig algorithms supported"); + return FALSE; + } + DBG2(DBG_TLS, "created signature with %N/%N", + tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); + writer->write_uint8(writer, hash); + writer->write_uint8(writer, alg); + writer->write_data16(writer, sig); + free(sig.ptr); + } + else + { + chunk_t sig, hash; + bool done; + + switch (key->get_type(key)) + { + case KEY_RSA: + if (!hash_data(this, data, &hash)) + { + return FALSE; + } + done = key->sign(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, &sig); + free(hash.ptr); + if (!done) + { + return FALSE; + } + DBG2(DBG_TLS, "created signature with MD5+SHA1/RSA"); + break; + case KEY_ECDSA: + if (!key->sign(key, SIGN_ECDSA_WITH_SHA1_DER, data, &sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "created signature with SHA1/ECDSA"); + break; + default: + return FALSE; + } + writer->write_data16(writer, sig); + free(sig.ptr); + } + return TRUE; +} + +METHOD(tls_crypto_t, verify, bool, + private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader, + chunk_t data) +{ + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + signature_scheme_t scheme = SIGN_UNKNOWN; + u_int8_t hash, alg; + chunk_t sig; + + if (!reader->read_uint8(reader, &hash) || + !reader->read_uint8(reader, &alg) || + !reader->read_data16(reader, &sig)) + { + DBG1(DBG_TLS, "received invalid signature"); + return FALSE; + } + scheme = hashsig_to_scheme(key->get_type(key), hash, alg); + if (scheme == SIGN_UNKNOWN) + { + DBG1(DBG_TLS, "signature algorithms %N/%N not supported", + tls_hash_algorithm_names, hash, + tls_signature_algorithm_names, alg); + return FALSE; + } + if (!key->verify(key, scheme, data, sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "verified signature with %N/%N", + tls_hash_algorithm_names, hash, tls_signature_algorithm_names, alg); + } + else + { + chunk_t sig, hash; + bool done; + + if (!reader->read_data16(reader, &sig)) + { + DBG1(DBG_TLS, "received invalid signature"); + return FALSE; + } + switch (key->get_type(key)) + { + case KEY_RSA: + if (!hash_data(this, data, &hash)) + { + return FALSE; + } + done = key->verify(key, SIGN_RSA_EMSA_PKCS1_NULL, hash, sig); + free(hash.ptr); + if (!done) + { + return FALSE; + } + DBG2(DBG_TLS, "verified signature data with MD5+SHA1/RSA"); + break; + case KEY_ECDSA: + if (!key->verify(key, SIGN_ECDSA_WITH_SHA1_DER, data, sig)) + { + return FALSE; + } + DBG2(DBG_TLS, "verified signature with SHA1/ECDSA"); + break; + default: + return FALSE; + } + } + return TRUE; +} + +METHOD(tls_crypto_t, sign_handshake, bool, + private_tls_crypto_t *this, private_key_t *key, tls_writer_t *writer, + chunk_t hashsig) +{ + return sign(this, key, writer, this->handshake, hashsig); +} + +METHOD(tls_crypto_t, verify_handshake, bool, + private_tls_crypto_t *this, public_key_t *key, tls_reader_t *reader) +{ + return verify(this, key, reader, this->handshake); +} + +METHOD(tls_crypto_t, calculate_finished, bool, + private_tls_crypto_t *this, char *label, char out[12]) +{ + chunk_t seed; + + if (!this->prf) + { + return FALSE; + } + if (!hash_data(this, this->handshake, &seed)) + { + return FALSE; + } + this->prf->get_bytes(this->prf, label, seed, 12, out); + free(seed.ptr); + return TRUE; +} + +METHOD(tls_crypto_t, derive_secrets, void, + private_tls_crypto_t *this, chunk_t premaster, + chunk_t client_random, chunk_t server_random) +{ + char master[48]; + chunk_t seed, block, client_write, server_write; + int mks, eks = 0, ivs = 0; + + /* derive master secret */ + seed = chunk_cata("cc", client_random, server_random); + this->prf->set_key(this->prf, premaster); + this->prf->get_bytes(this->prf, "master secret", seed, + sizeof(master), master); + + this->prf->set_key(this->prf, chunk_from_thing(master)); + memset(master, 0, sizeof(master)); + + /* derive key block for key expansion */ + mks = this->signer_out->get_key_size(this->signer_out); + if (this->crypter_out) + { + eks = this->crypter_out->get_key_size(this->crypter_out); + if (this->tls->get_version(this->tls) < TLS_1_1) + { + ivs = this->crypter_out->get_iv_size(this->crypter_out); + } + } + seed = chunk_cata("cc", server_random, client_random); + block = chunk_alloca((mks + eks + ivs) * 2); + this->prf->get_bytes(this->prf, "key expansion", seed, block.len, block.ptr); + + /* signer keys */ + client_write = chunk_create(block.ptr, mks); + block = chunk_skip(block, mks); + server_write = chunk_create(block.ptr, mks); + block = chunk_skip(block, mks); + if (this->tls->is_server(this->tls)) + { + this->signer_in->set_key(this->signer_in, client_write); + this->signer_out->set_key(this->signer_out, server_write); + } + else + { + this->signer_out->set_key(this->signer_out, client_write); + this->signer_in->set_key(this->signer_in, server_write); + } + + /* crypter keys, and IVs if < TLSv1.2 */ + if (this->crypter_out && this->crypter_in) + { + client_write = chunk_create(block.ptr, eks); + block = chunk_skip(block, eks); + server_write = chunk_create(block.ptr, eks); + block = chunk_skip(block, eks); + + if (this->tls->is_server(this->tls)) + { + this->crypter_in->set_key(this->crypter_in, client_write); + this->crypter_out->set_key(this->crypter_out, server_write); + } + else + { + this->crypter_out->set_key(this->crypter_out, client_write); + this->crypter_in->set_key(this->crypter_in, server_write); + } + if (ivs) + { + client_write = chunk_create(block.ptr, ivs); + block = chunk_skip(block, ivs); + server_write = chunk_create(block.ptr, ivs); + block = chunk_skip(block, ivs); + + if (this->tls->is_server(this->tls)) + { + this->iv_in = chunk_clone(client_write); + this->iv_out = chunk_clone(server_write); + } + else + { + this->iv_out = chunk_clone(client_write); + this->iv_in = chunk_clone(server_write); + } + } + } +} + +METHOD(tls_crypto_t, change_cipher, void, + private_tls_crypto_t *this, bool inbound) +{ + if (this->protection) + { + if (inbound) + { + this->protection->set_cipher(this->protection, TRUE, + this->signer_in, this->crypter_in, this->iv_in); + } + else + { + this->protection->set_cipher(this->protection, FALSE, + this->signer_out, this->crypter_out, this->iv_out); + } + } +} + +METHOD(tls_crypto_t, derive_eap_msk, void, + private_tls_crypto_t *this, chunk_t client_random, chunk_t server_random) +{ + if (this->msk_label) + { + chunk_t seed; + + seed = chunk_cata("cc", client_random, server_random); + free(this->msk.ptr); + this->msk = chunk_alloc(64); + this->prf->get_bytes(this->prf, this->msk_label, seed, + this->msk.len, this->msk.ptr); + } +} + +METHOD(tls_crypto_t, get_eap_msk, chunk_t, + private_tls_crypto_t *this) +{ + return this->msk; +} + +METHOD(tls_crypto_t, destroy, void, + private_tls_crypto_t *this) +{ + DESTROY_IF(this->signer_in); + DESTROY_IF(this->signer_out); + DESTROY_IF(this->crypter_in); + DESTROY_IF(this->crypter_out); + free(this->iv_in.ptr); + free(this->iv_out.ptr); + free(this->handshake.ptr); + free(this->msk.ptr); + DESTROY_IF(this->prf); + free(this->suites); + free(this); +} + +/** + * See header + */ +tls_crypto_t *tls_crypto_create(tls_t *tls) +{ + private_tls_crypto_t *this; + enumerator_t *enumerator; + credential_type_t type; + int subtype; + + INIT(this, + .public = { + .get_cipher_suites = _get_cipher_suites, + .select_cipher_suite = _select_cipher_suite, + .get_dh_group = _get_dh_group, + .get_signature_algorithms = _get_signature_algorithms, + .create_ec_enumerator = _create_ec_enumerator, + .set_protection = _set_protection, + .append_handshake = _append_handshake, + .sign = _sign, + .verify = _verify, + .sign_handshake = _sign_handshake, + .verify_handshake = _verify_handshake, + .calculate_finished = _calculate_finished, + .derive_secrets = _derive_secrets, + .change_cipher = _change_cipher, + .derive_eap_msk = _derive_eap_msk, + .get_eap_msk = _get_eap_msk, + .destroy = _destroy, + }, + .tls = tls, + ); + + enumerator = lib->creds->create_builder_enumerator(lib->creds); + while (enumerator->enumerate(enumerator, &type, &subtype)) + { + if (type == CRED_PUBLIC_KEY) + { + switch (subtype) + { + case KEY_RSA: + this->rsa = TRUE; + break; + case KEY_ECDSA: + this->ecdsa = TRUE; + break; + default: + break; + } + } + } + enumerator->destroy(enumerator); + + switch (tls->get_purpose(tls)) + { + case TLS_PURPOSE_EAP_TLS: + /* MSK PRF ASCII constant label according to EAP-TLS RFC 5216 */ + this->msk_label = "client EAP encryption"; + build_cipher_suite_list(this, FALSE); + break; + case TLS_PURPOSE_EAP_TTLS: + /* MSK PRF ASCII constant label according to EAP-TTLS RFC 5281 */ + this->msk_label = "ttls keying material"; + build_cipher_suite_list(this, TRUE); + break; + case TLS_PURPOSE_GENERIC: + build_cipher_suite_list(this, TRUE); + break; + default: + break; + } + return &this->public; +} diff --git a/src/libtls/tls_crypto.h b/src/libtls/tls_crypto.h new file mode 100644 index 000000000..f57b8f3e1 --- /dev/null +++ b/src/libtls/tls_crypto.h @@ -0,0 +1,554 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_crypto tls_crypto + * @{ @ingroup libtls + */ + +#ifndef TLS_CRYPTO_H_ +#define TLS_CRYPTO_H_ + +typedef struct tls_crypto_t tls_crypto_t; +typedef enum tls_cipher_suite_t tls_cipher_suite_t; +typedef enum tls_hash_algorithm_t tls_hash_algorithm_t; +typedef enum tls_signature_algorithm_t tls_signature_algorithm_t; +typedef enum tls_client_certificate_type_t tls_client_certificate_type_t; +typedef enum tls_ecc_curve_type_t tls_ecc_curve_type_t; +typedef enum tls_named_curve_t tls_named_curve_t; +typedef enum tls_ansi_point_format_t tls_ansi_point_format_t; +typedef enum tls_ec_point_format_t tls_ec_point_format_t; + +#include "tls.h" +#include "tls_prf.h" +#include "tls_protection.h" + +#include + +#include + +/** + * TLS cipher suites + */ +enum tls_cipher_suite_t { + TLS_NULL_WITH_NULL_NULL = 0x0000, + TLS_RSA_WITH_NULL_MD5 = 0x0001, + TLS_RSA_WITH_NULL_SHA = 0x0002, + TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003, + TLS_RSA_WITH_RC4_128_MD5 = 0x0004, + TLS_RSA_WITH_RC4_128_SHA = 0x0005, + TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006, + TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007, + TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008, + TLS_RSA_WITH_DES_CBC_SHA = 0x0009, + TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A, + TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B, + TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C, + TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D, + TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E, + TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F, + TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010, + TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011, + TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012, + TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013, + TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014, + TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016, + TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017, + TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018, + TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019, + TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A, + TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B, + + TLS_KRB5_WITH_DES_CBC_SHA = 0x001E, + TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F, + TLS_KRB5_WITH_RC4_128_SHA = 0x0020, + TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021, + TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022, + TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023, + TLS_KRB5_WITH_RC4_128_MD5 = 0x0024, + TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027, + TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028, + TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029, + TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A, + TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B, + TLS_PSK_WITH_NULL_SHA = 0x002C, + TLS_DHE_PSK_WITH_NULL_SHA = 0x002D, + TLS_RSA_PSK_WITH_NULL_SHA = 0x002E, + TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F, + TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030, + TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033, + TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034, + TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035, + TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036, + TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039, + TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A, + TLS_RSA_WITH_NULL_SHA256 = 0x003B, + TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C, + TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D, + TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E, + TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F, + TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046, + + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067, + TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068, + TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069, + TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B, + TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C, + TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D, + + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089, + TLS_PSK_WITH_RC4_128_SHA = 0x008A, + TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B, + TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C, + TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D, + TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E, + TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091, + TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092, + TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095, + TLS_RSA_WITH_SEED_CBC_SHA = 0x0096, + TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097, + TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098, + TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099, + TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A, + TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B, + TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C, + TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D, + TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E, + TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F, + TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0, + TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1, + TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2, + TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3, + TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4, + TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5, + TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6, + TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7, + TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8, + TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9, + TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA, + TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB, + TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC, + TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD, + TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE, + TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF, + TLS_PSK_WITH_NULL_SHA256 = 0x00B0, + TLS_PSK_WITH_NULL_SHA384 = 0x00B1, + TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2, + TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3, + TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4, + TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5, + TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6, + TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7, + TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8, + TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9, + TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA, + TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB, + TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC, + TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD, + TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE, + TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF, + TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0, + TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1, + TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2, + TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3, + TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4, + TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5, + + TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF, + + TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001, + TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002, + TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005, + TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006, + TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007, + TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A, + TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B, + TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C, + TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F, + TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010, + TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014, + TLS_ECDH_anon_WITH_NULL_SHA = 0xC015, + TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016, + TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017, + TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018, + TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019, + TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A, + TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B, + TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C, + TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D, + TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E, + TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F, + TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020, + TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021, + TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024, + TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025, + TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028, + TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029, + TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A, + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B, + TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C, + TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D, + TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030, + TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031, + TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032, + TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033, + TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036, + TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037, + TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038, + TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039, + TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A, + TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B +}; + +/** + * Enum names for tls_cipher_suite_t + */ +extern enum_name_t *tls_cipher_suite_names; + +/** + * TLS HashAlgorithm identifiers + */ +enum tls_hash_algorithm_t { + TLS_HASH_NONE = 0, + TLS_HASH_MD5 = 1, + TLS_HASH_SHA1 = 2, + TLS_HASH_SHA224 = 3, + TLS_HASH_SHA256 = 4, + TLS_HASH_SHA384 = 5, + TLS_HASH_SHA512 = 6, +}; + +/** + * Enum names for tls_hash_algorithm_t + */ +extern enum_name_t *tls_hash_algorithm_names; + +/** + * TLS SignatureAlgorithm identifiers + */ +enum tls_signature_algorithm_t { + TLS_SIG_RSA = 1, + TLS_SIG_DSA = 2, + TLS_SIG_ECDSA = 3, +}; + +/** + * Enum names for tls_signature_algorithm_t + */ +extern enum_name_t *tls_signature_algorithm_names; + +/** + * TLS ClientCertificateType + */ +enum tls_client_certificate_type_t { + TLS_RSA_SIGN = 1, + TLS_DSA_SIGN = 2, + TLS_RSA_FIXED_DH = 3, + TLS_DSS_FIXED_DH = 4, + TLS_RSA_EPHEMERAL_DH = 5, + TLS_DSS_EPHEMERAL_DH = 6, + TLS_FORTEZZA_DMS = 20, + TLS_ECDSA_SIGN = 64, + TLS_RSA_FIXED_ECDH = 65, + TLS_ECDSA_FIXED_ECDH = 66, +}; + +/** + * Enum names for tls_client_certificate_type_t + */ +extern enum_name_t *tls_client_certificate_type_names; + +/** + * TLS EccCurveType + */ +enum tls_ecc_curve_type_t { + TLS_ECC_EXPLICIT_PRIME = 1, + TLS_ECC_EXPLICIT_CHAR2 = 2, + TLS_ECC_NAMED_CURVE = 3, +}; + +/** + * Enum names for tls_ecc_curve_type_t + */ +extern enum_name_t *tls_ecc_curve_type_names; + +/** + * TLS Named Curve identifiers + */ +enum tls_named_curve_t { + TLS_SECT163K1 = 1, + TLS_SECT163R1 = 2, + TLS_SECT163R2 = 3, + TLS_SECT193R1 = 4, + TLS_SECT193R2 = 5, + TLS_SECT233K1 = 6, + TLS_SECT233R1 = 7, + TLS_SECT239K1 = 8, + TLS_SECT283K1 = 9, + TLS_SECT283R1 = 10, + TLS_SECT409K1 = 11, + TLS_SECT409R1 = 12, + TLS_SECT571K1 = 13, + TLS_SECT571R1 = 14, + TLS_SECP160K1 = 15, + TLS_SECP160R1 = 16, + TLS_SECP160R2 = 17, + TLS_SECP192K1 = 18, + TLS_SECP192R1 = 19, + TLS_SECP224K1 = 20, + TLS_SECP224R1 = 21, + TLS_SECP256K1 = 22, + TLS_SECP256R1 = 23, + TLS_SECP384R1 = 24, + TLS_SECP521R1 = 25, +}; + +/** + * Enum names for tls_named_curve_t + */ +extern enum_name_t *tls_named_curve_names; + +/** + * EC Point format, ANSI X9.62. + */ +enum tls_ansi_point_format_t { + TLS_ANSI_COMPRESSED = 2, + TLS_ANSI_COMPRESSED_Y = 3, + TLS_ANSI_UNCOMPRESSED = 4, + TLS_ANSI_HYBRID = 6, + TLS_ANSI_HYBRID_Y = 7, +}; + +/** + * Enum names for tls_ansi_point_format_t. + */ +extern enum_name_t *tls_ansi_point_format_names; + +/** + * EC Point format, TLS specific identifiers. + */ +enum tls_ec_point_format_t { + TLS_EC_POINT_UNCOMPRESSED = 0, + TLS_EC_POINT_ANSIX962_COMPRESSED_PRIME = 1, + TLS_EC_POINT_ANSIX962_COMPRESSED_CHAR2 = 2, +}; + +/** + * Enum names for tls_ec_point_format_t. + */ +extern enum_name_t *tls_ec_point_format_names; + +/** + * TLS crypto helper functions. + */ +struct tls_crypto_t { + + /** + * Get a list of supported TLS cipher suites. + * + * @param suites list of suites, points to internal data + * @return number of suites returned + */ + int (*get_cipher_suites)(tls_crypto_t *this, tls_cipher_suite_t **suites); + + /** + * Select and store a cipher suite from a given list of candidates. + * + * @param suites list of candidates to select from + * @param count number of suites + * @param key key type used, or KEY_ANY + * @return selected suite, 0 if none acceptable + */ + tls_cipher_suite_t (*select_cipher_suite)(tls_crypto_t *this, + tls_cipher_suite_t *suites, int count, + key_type_t key); + + /** + * Get the Diffie-Hellman group to use, if any. + * + * @return Diffie Hellman group, ord MODP_NONE + */ + diffie_hellman_group_t (*get_dh_group)(tls_crypto_t *this); + + /** + * Write the list of supported hash/sig algorithms to writer. + * + * @param writer writer to write supported hash/sig algorithms + */ + void (*get_signature_algorithms)(tls_crypto_t *this, tls_writer_t *writer); + + /** + * Create an enumerator over supported ECDH groups. + * + * Enumerates over (diffie_hellman_group_t, tls_named_curve_t) + * + * @return enumerator + */ + enumerator_t* (*create_ec_enumerator)(tls_crypto_t *this); + + /** + * Set the protection layer of the TLS stack to control it. + * + * @param protection protection layer to work on + */ + void (*set_protection)(tls_crypto_t *this, tls_protection_t *protection); + + /** + * Store exchanged handshake data, used for cryptographic operations. + * + * @param type handshake sub type + * @param data data to append to handshake buffer + */ + void (*append_handshake)(tls_crypto_t *this, + tls_handshake_type_t type, chunk_t data); + + /** + * Sign a blob of data, append signature to writer. + * + * @param key private key to use for signature + * @param writer TLS writer to write signature to + * @param data data to sign + * @param hashsig list of TLS1.2 hash/sig algorithms to select from + * @return TRUE if signature create successfully + */ + bool (*sign)(tls_crypto_t *this, private_key_t *key, + tls_writer_t *writer, chunk_t data, chunk_t hashsig); + + /** + * Verify a blob of data, read signature from a reader. + * + * @param key public key to verify signature with + * @param reader TLS reader to read signature from + * @param data data to verify signature + * @return TRUE if signature valid + */ + bool (*verify)(tls_crypto_t *this, public_key_t *key, + tls_reader_t *reader, chunk_t data); + + /** + * Create a signature of the handshake data using a given private key. + * + * @param key private key to use for signature + * @param writer TLS writer to write signature to + * @param hashsig list of TLS1.2 hash/sig algorithms to select from + * @return TRUE if signature create successfully + */ + bool (*sign_handshake)(tls_crypto_t *this, private_key_t *key, + tls_writer_t *writer, chunk_t hashsig); + + /** + * Verify the signature over handshake data using a given public key. + * + * @param key public key to verify signature with + * @param reader TLS reader to read signature from + * @return TRUE if signature valid + */ + bool (*verify_handshake)(tls_crypto_t *this, public_key_t *key, + tls_reader_t *reader); + + /** + * Calculate the data of a TLS finished message. + * + * @param label ASCII label to use for calculation + * @param out buffer to write finished data to + * @return TRUE if calculation successful + */ + bool (*calculate_finished)(tls_crypto_t *this, char *label, char out[12]); + + /** + * Derive the master secret, MAC and encryption keys. + * + * @param premaster premaster secret + * @param client_random random data from client hello + * @param server_random random data from server hello + */ + void (*derive_secrets)(tls_crypto_t *this, chunk_t premaster, + chunk_t client_random, chunk_t server_random); + + /** + * Change the cipher used at protection layer. + * + * @param inbound TRUE to change inbound cipher, FALSE for outbound + */ + void (*change_cipher)(tls_crypto_t *this, bool inbound); + + /** + * Derive the EAP-TLS MSK. + * + * @param client_random random data from client hello + * @param server_random random data from server hello + */ + void (*derive_eap_msk)(tls_crypto_t *this, + chunk_t client_random, chunk_t server_random); + + /** + * Get the MSK to use in EAP-TLS. + * + * @return MSK, points to internal data + */ + chunk_t (*get_eap_msk)(tls_crypto_t *this); + + /** + * Destroy a tls_crypto_t. + */ + void (*destroy)(tls_crypto_t *this); +}; + +/** + * Create a tls_crypto instance. + */ +tls_crypto_t *tls_crypto_create(tls_t *tls); + +#endif /** TLS_CRYPTO_H_ @}*/ diff --git a/src/libtls/tls_eap.c b/src/libtls/tls_eap.c new file mode 100644 index 000000000..a8c3a5053 --- /dev/null +++ b/src/libtls/tls_eap.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_eap.h" + +#include "tls.h" + +#include +#include + +/** Size limit for a single TLS message */ +#define MAX_TLS_MESSAGE_LEN 65536 + +typedef struct private_tls_eap_t private_tls_eap_t; + +/** + * Private data of an tls_eap_t object. + */ +struct private_tls_eap_t { + + /** + * Public tls_eap_t interface. + */ + tls_eap_t public; + + /** + * Type of EAP method, EAP-TLS, EAP-TTLS, or EAP-TNC + */ + eap_type_t type; + + /** + * TLS stack + */ + tls_t *tls; + + /** + * Role + */ + bool is_server; + + /** + * First fragment of a multi-fragment record? + */ + bool first_fragment; + + /** + * Maximum size of an outgoing EAP-TLS fragment + */ + size_t frag_size; + + /** + * Number of EAP messages/fragments processed so far + */ + int processed; + + /** + * Maximum number of processed EAP messages/fragments + */ + int max_msg_count; +}; + +/** + * Flags of an EAP-TLS/TTLS/TNC message + */ +typedef enum { + EAP_TLS_LENGTH = (1<<7), /* shared with EAP-TTLS/TNC */ + EAP_TLS_MORE_FRAGS = (1<<6), /* shared with EAP-TTLS/TNC */ + EAP_TLS_START = (1<<5), /* shared with EAP-TTLS/TNC */ + EAP_TTLS_VERSION = (0x07), /* shared with EAP-TNC */ +} eap_tls_flags_t; + +#define EAP_TTLS_SUPPORTED_VERSION 0 +#define EAP_TNC_SUPPORTED_VERSION 1 + +/** + * EAP-TLS/TTLS packet format + */ +typedef struct __attribute__((packed)) { + u_int8_t code; + u_int8_t identifier; + u_int16_t length; + u_int8_t type; + u_int8_t flags; +} eap_tls_packet_t; + +METHOD(tls_eap_t, initiate, status_t, + private_tls_eap_t *this, chunk_t *out) +{ + if (this->is_server) + { + eap_tls_packet_t pkt = { + .type = this->type, + .code = EAP_REQUEST, + .flags = EAP_TLS_START, + }; + switch (this->type) + { + case EAP_TTLS: + pkt.flags |= EAP_TTLS_SUPPORTED_VERSION; + break; + case EAP_TNC: + pkt.flags |= EAP_TNC_SUPPORTED_VERSION; + break; + default: + break; + } + htoun16(&pkt.length, sizeof(eap_tls_packet_t)); + do + { /* start with non-zero random identifier */ + pkt.identifier = random(); + } + while (!pkt.identifier); + + DBG2(DBG_IKE, "sending %N start packet", eap_type_names, this->type); + *out = chunk_clone(chunk_from_thing(pkt)); + return NEED_MORE; + } + return FAILED; +} + +/** + * Process a received packet + */ +static status_t process_pkt(private_tls_eap_t *this, eap_tls_packet_t *pkt) +{ + u_int32_t msg_len; + u_int16_t pkt_len; + + pkt_len = untoh16(&pkt->length); + if (pkt->flags & EAP_TLS_LENGTH) + { + if (pkt_len < sizeof(eap_tls_packet_t) + sizeof(msg_len)) + { + DBG1(DBG_TLS, "%N packet too short", eap_type_names, this->type); + return FAILED; + } + msg_len = untoh32(pkt + 1); + if (msg_len < pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len) || + msg_len > MAX_TLS_MESSAGE_LEN) + { + DBG1(DBG_TLS, "invalid %N packet length", eap_type_names, this->type); + return FAILED; + } + return this->tls->process(this->tls, (char*)(pkt + 1) + sizeof(msg_len), + pkt_len - sizeof(eap_tls_packet_t) - sizeof(msg_len)); + } + return this->tls->process(this->tls, (char*)(pkt + 1), + pkt_len - sizeof(eap_tls_packet_t)); +} + +/** + * Build a packet to send + */ +static status_t build_pkt(private_tls_eap_t *this, + u_int8_t identifier, chunk_t *out) +{ + char buf[this->frag_size]; + eap_tls_packet_t *pkt; + size_t len, reclen; + status_t status; + char *kind; + + pkt = (eap_tls_packet_t*)buf; + pkt->code = this->is_server ? EAP_REQUEST : EAP_RESPONSE; + pkt->identifier = this->is_server ? identifier + 1 : identifier; + pkt->type = this->type; + pkt->flags = 0; + + switch (this->type) + { + case EAP_TTLS: + pkt->flags |= EAP_TTLS_SUPPORTED_VERSION; + break; + case EAP_TNC: + pkt->flags |= EAP_TNC_SUPPORTED_VERSION; + break; + default: + break; + } + + if (this->first_fragment) + { + pkt->flags |= EAP_TLS_LENGTH; + len = sizeof(buf) - sizeof(eap_tls_packet_t) - sizeof(u_int32_t); + status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t) + + sizeof(u_int32_t), &len, &reclen); + } + else + { + len = sizeof(buf) - sizeof(eap_tls_packet_t); + status = this->tls->build(this->tls, buf + sizeof(eap_tls_packet_t), + &len, &reclen); + } + switch (status) + { + case NEED_MORE: + pkt->flags |= EAP_TLS_MORE_FRAGS; + kind = "further fragment"; + if (this->first_fragment) + { + this->first_fragment = FALSE; + kind = "first fragment"; + } + break; + case ALREADY_DONE: + kind = "packet"; + if (!this->first_fragment) + { + this->first_fragment = TRUE; + kind = "final fragment"; + } + break; + default: + return status; + } + DBG2(DBG_TLS, "sending %N %s (%u bytes)", + eap_type_names, this->type, kind, len); + if (reclen) + { + htoun32(pkt + 1, reclen); + len += sizeof(u_int32_t); + pkt->flags |= EAP_TLS_LENGTH; + } + len += sizeof(eap_tls_packet_t); + htoun16(&pkt->length, len); + *out = chunk_clone(chunk_create(buf, len)); + return NEED_MORE; +} + +/** + * Send an ack to request next fragment + */ +static chunk_t create_ack(private_tls_eap_t *this, u_int8_t identifier) +{ + eap_tls_packet_t pkt = { + .code = this->is_server ? EAP_REQUEST : EAP_RESPONSE, + .identifier = this->is_server ? identifier + 1 : identifier, + .type = this->type, + }; + htoun16(&pkt.length, sizeof(pkt)); + switch (this->type) + { + case EAP_TTLS: + pkt.flags |= EAP_TTLS_SUPPORTED_VERSION; + break; + case EAP_TNC: + pkt.flags |= EAP_TNC_SUPPORTED_VERSION; + break; + default: + break; + } + DBG2(DBG_TLS, "sending %N acknowledgement packet", + eap_type_names, this->type); + return chunk_clone(chunk_from_thing(pkt)); +} + +METHOD(tls_eap_t, process, status_t, + private_tls_eap_t *this, chunk_t in, chunk_t *out) +{ + eap_tls_packet_t *pkt; + status_t status; + + if (++this->processed > this->max_msg_count) + { + DBG1(DBG_IKE, "%N packet count exceeded (%d > %d)", + eap_type_names, this->type, + this->processed, this->max_msg_count); + return FAILED; + } + + pkt = (eap_tls_packet_t*)in.ptr; + if (in.len < sizeof(eap_tls_packet_t) || + untoh16(&pkt->length) != in.len) + { + DBG1(DBG_IKE, "invalid %N packet length", + eap_type_names, this->type); + return FAILED; + } + if (pkt->flags & EAP_TLS_START) + { + if (this->type == EAP_TTLS || this->type == EAP_TNC) + { + DBG1(DBG_TLS, "%N version is v%u", eap_type_names, this->type, + pkt->flags & EAP_TTLS_VERSION); + } + } + else + { + if (in.len == sizeof(eap_tls_packet_t)) + { + DBG2(DBG_TLS, "received %N acknowledgement packet", + eap_type_names, this->type); + status = build_pkt(this, pkt->identifier, out); + if (status == INVALID_STATE && + this->tls->is_complete(this->tls)) + { + return SUCCESS; + } + return status; + } + status = process_pkt(this, pkt); + if (status != NEED_MORE) + { + return status; + } + } + status = build_pkt(this, pkt->identifier, out); + switch (status) + { + case INVALID_STATE: + *out = create_ack(this, pkt->identifier); + return NEED_MORE; + case FAILED: + if (!this->is_server) + { + *out = create_ack(this, pkt->identifier); + return NEED_MORE; + } + return FAILED; + default: + return status; + } +} + +METHOD(tls_eap_t, get_msk, chunk_t, + private_tls_eap_t *this) +{ + return this->tls->get_eap_msk(this->tls); +} + +METHOD(tls_eap_t, destroy, void, + private_tls_eap_t *this) +{ + this->tls->destroy(this->tls); + free(this); +} + +/** + * See header + */ +tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size, + int max_msg_count) +{ + private_tls_eap_t *this; + + if (!tls) + { + return NULL; + } + + INIT(this, + .public = { + .initiate = _initiate, + .process = _process, + .get_msk = _get_msk, + .destroy = _destroy, + }, + .type = type, + .is_server = tls->is_server(tls), + .first_fragment = TRUE, + .frag_size = frag_size, + .max_msg_count = max_msg_count, + .tls = tls, + ); + + return &this->public; +} diff --git a/src/libtls/tls_eap.h b/src/libtls/tls_eap.h new file mode 100644 index 000000000..ebda2636d --- /dev/null +++ b/src/libtls/tls_eap.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_eap tls_eap + * @{ @ingroup libtls + */ + +#ifndef TLS_EAP_H_ +#define TLS_EAP_H_ + +typedef struct tls_eap_t tls_eap_t; + +#include + +#include "tls.h" + +/** + * TLS over EAP helper, as used by EAP-TLS and EAP-TTLS. + */ +struct tls_eap_t { + + /** + * Initiate TLS/TTLS/TNC over EAP exchange (as client). + * + * @param out allocated EAP packet data to send + * @return + * - NEED_MORE if more exchanges required + * - FAILED if initiation failed + */ + status_t (*initiate)(tls_eap_t *this, chunk_t *out); + + /** + * Process a received EAP-TLS/TTLS/TNC packet, create response. + * + * @param in EAP packet data to process + * @param out allocated EAP packet data to send + * @return + * - SUCCESS if TLS negotiation completed + * - FAILED if TLS negotiation failed + * - NEED_MORE if more exchanges required + */ + status_t (*process)(tls_eap_t *this, chunk_t in, chunk_t *out); + + /** + * Get the EAP-MSK. + * + * @return MSK + */ + chunk_t (*get_msk)(tls_eap_t *this); + + /** + * Destroy a tls_eap_t. + */ + void (*destroy)(tls_eap_t *this); +}; + +/** + * Create a tls_eap instance. + * + * @param type EAP type, EAP-TLS or EAP-TTLS + * @param tls TLS implementation + * @param frag_size maximum size of a TLS fragment we send + * @param max_msg_count maximum number of processed messages + */ +tls_eap_t *tls_eap_create(eap_type_t type, tls_t *tls, size_t frag_size, + int max_msg_count); + +#endif /** TLS_EAP_H_ @}*/ diff --git a/src/libtls/tls_fragmentation.c b/src/libtls/tls_fragmentation.c new file mode 100644 index 000000000..5a598cfc4 --- /dev/null +++ b/src/libtls/tls_fragmentation.c @@ -0,0 +1,471 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_fragmentation.h" + +#include "tls_reader.h" + +#include + +typedef struct private_tls_fragmentation_t private_tls_fragmentation_t; + +/** + * Alert state + */ +typedef enum { + /* no alert received/sent */ + ALERT_NONE, + /* currently sending an alert */ + ALERT_SENDING, + /* alert sent and out */ + ALERT_SENT, +} alert_state_t; + +/** + * Private data of an tls_fragmentation_t object. + */ +struct private_tls_fragmentation_t { + + /** + * Public tls_fragmentation_t interface. + */ + tls_fragmentation_t public; + + /** + * Upper layer handshake protocol + */ + tls_handshake_t *handshake; + + /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * State of alert handling + */ + alert_state_t state; + + /** + * Did the application layer complete successfully? + */ + bool application_finished; + + /** + * Handshake input buffer + */ + chunk_t input; + + /** + * Position in input buffer + */ + size_t inpos; + + /** + * Currently processed handshake message type + */ + tls_handshake_type_t type; + + /** + * Handshake output buffer + */ + chunk_t output; + + /** + * Type of data in output buffer + */ + tls_content_type_t output_type; + + /** + * Upper layer application data protocol + */ + tls_application_t *application; +}; + +/** + * Maximum size of a TLS fragment + */ +#define MAX_TLS_FRAGMENT_LEN 16384 + +/** + * Maximum size of a TLS handshake message we accept + */ +#define MAX_TLS_HANDSHAKE_LEN 65536 + +/** + * Process a TLS alert + */ +static status_t process_alert(private_tls_fragmentation_t *this, + tls_reader_t *reader) +{ + u_int8_t level, description; + + if (!reader->read_uint8(reader, &level) || + !reader->read_uint8(reader, &description)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + return this->alert->process(this->alert, level, description); +} + +/** + * Process TLS handshake protocol data + */ +static status_t process_handshake(private_tls_fragmentation_t *this, + tls_reader_t *reader) +{ + while (reader->remaining(reader)) + { + tls_reader_t *msg; + u_int8_t type; + u_int32_t len; + status_t status; + chunk_t data; + + if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN) + { + DBG1(DBG_TLS, "TLS fragment has invalid length"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (this->input.len == 0) + { /* new handshake message */ + if (!reader->read_uint8(reader, &type) || + !reader->read_uint24(reader, &len)) + { + DBG1(DBG_TLS, "TLS handshake header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + this->type = type; + if (len > MAX_TLS_HANDSHAKE_LEN) + { + DBG1(DBG_TLS, "TLS handshake exceeds maximum length"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + chunk_free(&this->input); + this->inpos = 0; + if (len) + { + this->input = chunk_alloc(len); + } + } + + len = min(this->input.len - this->inpos, reader->remaining(reader)); + if (!reader->read_data(reader, len, &data)) + { + DBG1(DBG_TLS, "TLS fragment has invalid length"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + memcpy(this->input.ptr + this->inpos, data.ptr, len); + this->inpos += len; + + if (this->input.len == this->inpos) + { /* message completely defragmented, process */ + msg = tls_reader_create(this->input); + DBG2(DBG_TLS, "received TLS %N handshake (%u bytes)", + tls_handshake_type_names, this->type, this->input.len); + status = this->handshake->process(this->handshake, this->type, msg); + msg->destroy(msg); + chunk_free(&this->input); + if (status != NEED_MORE) + { + return status; + } + } + if (this->alert->fatal(this->alert)) + { + break; + } + } + return NEED_MORE; +} + +/** + * Process TLS application data + */ +static status_t process_application(private_tls_fragmentation_t *this, + tls_reader_t *reader) +{ + while (reader->remaining(reader)) + { + status_t status; + + if (reader->remaining(reader) > MAX_TLS_FRAGMENT_LEN) + { + DBG1(DBG_TLS, "TLS fragment has invalid length"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + status = this->application->process(this->application, reader); + switch (status) + { + case NEED_MORE: + continue; + case SUCCESS: + this->application_finished = TRUE; + return SUCCESS; + case FAILED: + default: + this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); + return NEED_MORE; + } + } + return NEED_MORE; +} + +METHOD(tls_fragmentation_t, process, status_t, + private_tls_fragmentation_t *this, tls_content_type_t type, chunk_t data) +{ + tls_reader_t *reader; + status_t status; + + switch (this->state) + { + case ALERT_SENDING: + case ALERT_SENT: + /* don't accept more input, fatal error ocurred */ + return NEED_MORE; + case ALERT_NONE: + break; + } + reader = tls_reader_create(data); + switch (type) + { + case TLS_CHANGE_CIPHER_SPEC: + if (this->handshake->change_cipherspec(this->handshake)) + { + status = NEED_MORE; + break; + } + status = FAILED; + break; + case TLS_ALERT: + status = process_alert(this, reader); + break; + case TLS_HANDSHAKE: + status = process_handshake(this, reader); + break; + case TLS_APPLICATION_DATA: + status = process_application(this, reader); + break; + default: + DBG1(DBG_TLS, "received unknown TLS content type %d, ignored", type); + status = NEED_MORE; + break; + } + reader->destroy(reader); + return status; +} + +/** + * Check if alerts are pending + */ +static bool check_alerts(private_tls_fragmentation_t *this, chunk_t *data) +{ + tls_alert_level_t level; + tls_alert_desc_t desc; + tls_writer_t *writer; + + if (this->alert->get(this->alert, &level, &desc)) + { + writer = tls_writer_create(2); + + writer->write_uint8(writer, level); + writer->write_uint8(writer, desc); + + *data = chunk_clone(writer->get_buf(writer)); + writer->destroy(writer); + return TRUE; + } + return FALSE; +} + +/** + * Build hanshake message + */ +static status_t build_handshake(private_tls_fragmentation_t *this) +{ + tls_writer_t *hs, *msg; + tls_handshake_type_t type; + status_t status; + + msg = tls_writer_create(64); + while (TRUE) + { + hs = tls_writer_create(64); + status = this->handshake->build(this->handshake, &type, hs); + switch (status) + { + case NEED_MORE: + if (this->alert->fatal(this->alert)) + { + break; + } + msg->write_uint8(msg, type); + msg->write_data24(msg, hs->get_buf(hs)); + DBG2(DBG_TLS, "sending TLS %N handshake (%u bytes)", + tls_handshake_type_names, type, hs->get_buf(hs).len); + hs->destroy(hs); + continue; + case INVALID_STATE: + this->output_type = TLS_HANDSHAKE; + this->output = chunk_clone(msg->get_buf(msg)); + break; + default: + break; + } + hs->destroy(hs); + break; + } + msg->destroy(msg); + return status; +} + +/** + * Build TLS application data + */ +static status_t build_application(private_tls_fragmentation_t *this) +{ + tls_writer_t *msg; + status_t status; + + msg = tls_writer_create(64); + while (TRUE) + { + status = this->application->build(this->application, msg); + switch (status) + { + case NEED_MORE: + continue; + case INVALID_STATE: + this->output_type = TLS_APPLICATION_DATA; + this->output = chunk_clone(msg->get_buf(msg)); + break; + case SUCCESS: + this->application_finished = TRUE; + break; + case FAILED: + default: + this->alert->add(this->alert, TLS_FATAL, TLS_CLOSE_NOTIFY); + break; + } + break; + } + msg->destroy(msg); + return status; +} + +METHOD(tls_fragmentation_t, build, status_t, + private_tls_fragmentation_t *this, tls_content_type_t *type, chunk_t *data) +{ + status_t status = INVALID_STATE; + + switch (this->state) + { + case ALERT_SENDING: + this->state = ALERT_SENT; + return INVALID_STATE; + case ALERT_SENT: + return FAILED; + case ALERT_NONE: + break; + } + if (check_alerts(this, data)) + { + this->state = ALERT_SENDING; + *type = TLS_ALERT; + return NEED_MORE; + } + if (!this->output.len) + { + if (this->handshake->cipherspec_changed(this->handshake)) + { + *type = TLS_CHANGE_CIPHER_SPEC; + *data = chunk_clone(chunk_from_chars(0x01)); + return NEED_MORE; + } + if (!this->handshake->finished(this->handshake)) + { + status = build_handshake(this); + } + else if (this->application) + { + status = build_application(this); + } + if (check_alerts(this, data)) + { + this->state = ALERT_SENDING; + *type = TLS_ALERT; + return NEED_MORE; + } + } + if (this->output.len) + { + *type = this->output_type; + if (this->output.len <= MAX_TLS_FRAGMENT_LEN) + { + *data = this->output; + this->output = chunk_empty; + return NEED_MORE; + } + *data = chunk_create(this->output.ptr, MAX_TLS_FRAGMENT_LEN); + this->output = chunk_clone(chunk_skip(this->output, MAX_TLS_FRAGMENT_LEN)); + return NEED_MORE; + } + return status; +} + +METHOD(tls_fragmentation_t, application_finished, bool, + private_tls_fragmentation_t *this) +{ + return this->application_finished; +} + +METHOD(tls_fragmentation_t, destroy, void, + private_tls_fragmentation_t *this) +{ + free(this->input.ptr); + free(this->output.ptr); + free(this); +} + +/** + * See header + */ +tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, + tls_alert_t *alert, tls_application_t *application) +{ + private_tls_fragmentation_t *this; + + INIT(this, + .public = { + .process = _process, + .build = _build, + .application_finished = _application_finished, + .destroy = _destroy, + }, + .handshake = handshake, + .alert = alert, + .state = ALERT_NONE, + .application = application, + ); + + return &this->public; +} diff --git a/src/libtls/tls_fragmentation.h b/src/libtls/tls_fragmentation.h new file mode 100644 index 000000000..d80278916 --- /dev/null +++ b/src/libtls/tls_fragmentation.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_fragmentation tls_fragmentation + * @{ @ingroup libtls + */ + +#ifndef TLS_FRAGMENTATION_H_ +#define TLS_FRAGMENTATION_H_ + +#include + +#include "tls.h" +#include "tls_alert.h" +#include "tls_handshake.h" + +typedef struct tls_fragmentation_t tls_fragmentation_t; + +/** + * TLS record protocol fragmentation layer. + */ +struct tls_fragmentation_t { + + /** + * Process a fragmented TLS record, pass it to upper layers. + * + * @param type type of the TLS record to process + * @param data associated TLS record data + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if more invocations to process/build needed + */ + status_t (*process)(tls_fragmentation_t *this, + tls_content_type_t type, chunk_t data); + + /** + * Query upper layer for TLS messages, build fragmented records. + * + * @param type type of the built TLS record + * @param data allocated data of the built TLS record + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if upper layers have more records to send + * - INVALID_STATE if more input records required + */ + status_t (*build)(tls_fragmentation_t *this, + tls_content_type_t *type, chunk_t *data); + + /** + * Has the application layer finished (returned SUCCESS)?. + * + * @return TRUE if application layer finished + */ + bool (*application_finished)(tls_fragmentation_t *this); + + /** + * Destroy a tls_fragmentation_t. + */ + void (*destroy)(tls_fragmentation_t *this); +}; + +/** + * Create a tls_fragmentation instance. + * + * @param handshake upper layer handshake protocol + * @param alert TLS alert handler + * @param application upper layer application data or NULL + * @return TLS fragmentation layer + */ +tls_fragmentation_t *tls_fragmentation_create(tls_handshake_t *handshake, + tls_alert_t *alert, tls_application_t *application); + +#endif /** TLS_FRAGMENTATION_H_ @}*/ diff --git a/src/libtls/tls_handshake.h b/src/libtls/tls_handshake.h new file mode 100644 index 000000000..6703b341b --- /dev/null +++ b/src/libtls/tls_handshake.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_handshake tls_handshake + * @{ @ingroup libtls + */ + +#ifndef TLS_HANDSHAKE_H_ +#define TLS_HANDSHAKE_H_ + +typedef struct tls_handshake_t tls_handshake_t; + +#include "tls.h" +#include "tls_reader.h" +#include "tls_writer.h" + +/** + * TLS handshake state machine interface. + */ +struct tls_handshake_t { + + /** + * Process received TLS handshake message. + * + * @param type TLS handshake message type + * @param reader TLS data buffer + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if a fatal TLS alert queued + * - NEED_MORE if more invocations to process/build needed + * - DESTROY_ME if a fatal TLS alert received + */ + status_t (*process)(tls_handshake_t *this, + tls_handshake_type_t type, tls_reader_t *reader); + + /** + * Build TLS handshake messages to send out. + * + * @param type type of created handshake message + * @param writer TLS data buffer to write to + * @return + * - SUCCESS if handshake complete + * - FAILED if handshake failed + * - NEED_MORE if more messages ready for delivery + * - INVALID_STATE if more input to process() required + */ + status_t (*build)(tls_handshake_t *this, + tls_handshake_type_t *type, tls_writer_t *writer); + + /** + * Check if the cipher spec for outgoing messages has changed. + * + * @return TRUE if cipher spec changed + */ + bool (*cipherspec_changed)(tls_handshake_t *this); + + /** + * Change the cipher spec for incoming messages. + * + * @return TRUE if cipher spec changed + */ + bool (*change_cipherspec)(tls_handshake_t *this); + + /** + * Check if the finished message was decoded successfully. + * + * @return TRUE if finished message was decoded successfully + */ + bool (*finished)(tls_handshake_t *this); + + /** + * Destroy a tls_handshake_t. + */ + void (*destroy)(tls_handshake_t *this); +}; + +#endif /** TLS_HANDSHAKE_H_ @}*/ diff --git a/src/libtls/tls_peer.c b/src/libtls/tls_peer.c new file mode 100644 index 000000000..c1fd33eea --- /dev/null +++ b/src/libtls/tls_peer.c @@ -0,0 +1,1099 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_peer.h" + +#include +#include + +#include + +typedef struct private_tls_peer_t private_tls_peer_t; + +typedef enum { + STATE_INIT, + STATE_HELLO_SENT, + STATE_HELLO_RECEIVED, + STATE_HELLO_DONE, + STATE_CERT_SENT, + STATE_CERT_RECEIVED, + STATE_KEY_EXCHANGE_RECEIVED, + STATE_CERTREQ_RECEIVED, + STATE_KEY_EXCHANGE_SENT, + STATE_VERIFY_SENT, + STATE_CIPHERSPEC_CHANGED_OUT, + STATE_FINISHED_SENT, + STATE_CIPHERSPEC_CHANGED_IN, + STATE_COMPLETE, +} peer_state_t; + +/** + * Private data of an tls_peer_t object. + */ +struct private_tls_peer_t { + + /** + * Public tls_peer_t interface. + */ + tls_peer_t public; + + /** + * TLS stack + */ + tls_t *tls; + + /** + * TLS crypto context + */ + tls_crypto_t *crypto; + + /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * Peer identity, NULL for no client authentication + */ + identification_t *peer; + + /** + * Server identity + */ + identification_t *server; + + /** + * State we are in + */ + peer_state_t state; + + /** + * Hello random data selected by client + */ + char client_random[32]; + + /** + * Hello random data selected by server + */ + char server_random[32]; + + /** + * Auth helper for peer authentication + */ + auth_cfg_t *peer_auth; + + /** + * Auth helper for server authentication + */ + auth_cfg_t *server_auth; + + /** + * Peer private key + */ + private_key_t *private; + + /** + * DHE exchange + */ + diffie_hellman_t *dh; + + /** + * List of server-supported hashsig algorithms + */ + chunk_t hashsig; + + /** + * List of server-supported client certificate types + */ + chunk_t cert_types; +}; + +/** + * Process a server hello message + */ +static status_t process_server_hello(private_tls_peer_t *this, + tls_reader_t *reader) +{ + u_int8_t compression; + u_int16_t version, cipher; + chunk_t random, session, ext = chunk_empty; + tls_cipher_suite_t suite; + + this->crypto->append_handshake(this->crypto, + TLS_SERVER_HELLO, reader->peek(reader)); + + if (!reader->read_uint16(reader, &version) || + !reader->read_data(reader, sizeof(this->server_random), &random) || + !reader->read_data8(reader, &session) || + !reader->read_uint16(reader, &cipher) || + !reader->read_uint8(reader, &compression) || + (reader->remaining(reader) && !reader->read_data16(reader, &ext))) + { + DBG1(DBG_TLS, "received invalid ServerHello"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + memcpy(this->server_random, random.ptr, sizeof(this->server_random)); + + if (!this->tls->set_version(this->tls, version)) + { + DBG1(DBG_TLS, "negotiated version %N not supported", + tls_version_names, version); + this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); + return NEED_MORE; + } + suite = cipher; + if (!this->crypto->select_cipher_suite(this->crypto, &suite, 1, KEY_ANY)) + { + DBG1(DBG_TLS, "received TLS cipher suite %N inacceptable", + tls_cipher_suite_names, suite); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", + tls_version_names, version, tls_cipher_suite_names, suite); + this->state = STATE_HELLO_RECEIVED; + return NEED_MORE; +} + +/** + * Check if a server certificate is acceptable for the given server identity + */ +static bool check_certificate(private_tls_peer_t *this, certificate_t *cert) +{ + identification_t *id; + + if (cert->has_subject(cert, this->server)) + { + return TRUE; + } + id = cert->get_subject(cert); + if (id->matches(id, this->server)) + { + return TRUE; + } + if (cert->get_type(cert) == CERT_X509) + { + x509_t *x509 = (x509_t*)cert; + enumerator_t *enumerator; + + enumerator = x509->create_subjectAltName_enumerator(x509); + while (enumerator->enumerate(enumerator, &id)) + { + if (id->matches(id, this->server)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + } + DBG1(DBG_TLS, "server certificate does not match to '%Y'", this->server); + return FALSE; +} + +/** + * Process a Certificate message + */ +static status_t process_certificate(private_tls_peer_t *this, + tls_reader_t *reader) +{ + certificate_t *cert; + tls_reader_t *certs; + chunk_t data; + bool first = TRUE; + + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE, reader->peek(reader)); + + if (!reader->read_data24(reader, &data)) + { + DBG1(DBG_TLS, "certificate message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + certs = tls_reader_create(data); + while (certs->remaining(certs)) + { + if (!certs->read_data24(certs, &data)) + { + DBG1(DBG_TLS, "certificate message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + certs->destroy(certs); + return NEED_MORE; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + if (cert) + { + if (first) + { + if (!check_certificate(this, cert)) + { + cert->destroy(cert); + certs->destroy(certs); + this->alert->add(this->alert, TLS_FATAL, TLS_ACCESS_DENIED); + return NEED_MORE; + } + this->server_auth->add(this->server_auth, + AUTH_HELPER_SUBJECT_CERT, cert); + DBG1(DBG_TLS, "received TLS server certificate '%Y'", + cert->get_subject(cert)); + first = FALSE; + } + else + { + DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + this->server_auth->add(this->server_auth, + AUTH_HELPER_IM_CERT, cert); + } + } + else + { + DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); + this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); + } + } + certs->destroy(certs); + this->state = STATE_CERT_RECEIVED; + return NEED_MORE; +} + +/** + * Find a trusted public key to encrypt/verify key exchange data + */ +static public_key_t *find_public_key(private_tls_peer_t *this) +{ + public_key_t *public = NULL, *current; + certificate_t *cert; + enumerator_t *enumerator; + auth_cfg_t *auth; + + cert = this->server_auth->get(this->server_auth, AUTH_HELPER_SUBJECT_CERT); + if (cert) + { + enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, + KEY_ANY, cert->get_subject(cert), this->server_auth); + while (enumerator->enumerate(enumerator, ¤t, &auth)) + { + public = current->get_ref(current); + break; + } + enumerator->destroy(enumerator); + } + return public; +} + +/** + * Process a Key Exchange message using MODP Diffie Hellman + */ +static status_t process_modp_key_exchange(private_tls_peer_t *this, + tls_reader_t *reader) +{ + chunk_t prime, generator, pub, chunk; + public_key_t *public; + + chunk = reader->peek(reader); + if (!reader->read_data16(reader, &prime) || + !reader->read_data16(reader, &generator) || + !reader->read_data16(reader, &pub)) + { + DBG1(DBG_TLS, "received invalid Server Key Exchange"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + public = find_public_key(this); + if (!public) + { + DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + + chunk.len = 2 + prime.len + 2 + generator.len + 2 + pub.len; + chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random), chunk); + if (!this->crypto->verify(this->crypto, public, reader, chunk)) + { + public->destroy(public); + free(chunk.ptr); + DBG1(DBG_TLS, "verifying DH parameters failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); + return NEED_MORE; + } + public->destroy(public); + free(chunk.ptr); + + this->dh = lib->crypto->create_dh(lib->crypto, MODP_CUSTOM, + generator, prime); + if (!this->dh) + { + DBG1(DBG_TLS, "custom DH parameters not supported"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->dh->set_other_public_value(this->dh, pub); + + this->state = STATE_KEY_EXCHANGE_RECEIVED; + return NEED_MORE; +} + +/** + * Get the EC group for a TLS named curve + */ +static diffie_hellman_group_t curve_to_ec_group(private_tls_peer_t *this, + tls_named_curve_t curve) +{ + diffie_hellman_group_t group; + tls_named_curve_t current; + enumerator_t *enumerator; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, &group, ¤t)) + { + if (current == curve) + { + enumerator->destroy(enumerator); + return group; + } + } + enumerator->destroy(enumerator); + return 0; +} + +/** + * Process a Key Exchange message using EC Diffie Hellman + */ +static status_t process_ec_key_exchange(private_tls_peer_t *this, + tls_reader_t *reader) +{ + diffie_hellman_group_t group; + public_key_t *public; + u_int8_t type; + u_int16_t curve; + chunk_t pub, chunk; + + chunk = reader->peek(reader); + if (!reader->read_uint8(reader, &type)) + { + DBG1(DBG_TLS, "received invalid Server Key Exchange"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (type != TLS_ECC_NAMED_CURVE) + { + DBG1(DBG_TLS, "ECDH curve type %N not supported", + tls_ecc_curve_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + if (!reader->read_uint16(reader, &curve) || + !reader->read_data8(reader, &pub) || pub.len == 0) + { + DBG1(DBG_TLS, "received invalid Server Key Exchange"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + group = curve_to_ec_group(this, curve); + if (!group) + { + DBG1(DBG_TLS, "ECDH curve %N not supported", + tls_named_curve_names, curve); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + + public = find_public_key(this); + if (!public) + { + DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + + chunk.len = 4 + pub.len; + chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random), chunk); + if (!this->crypto->verify(this->crypto, public, reader, chunk)) + { + public->destroy(public); + free(chunk.ptr); + DBG1(DBG_TLS, "verifying DH parameters failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); + return NEED_MORE; + } + public->destroy(public); + free(chunk.ptr); + + this->dh = lib->crypto->create_dh(lib->crypto, group); + if (!this->dh) + { + DBG1(DBG_TLS, "DH group %N not supported", + diffie_hellman_group_names, group); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, pub.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->dh->set_other_public_value(this->dh, chunk_skip(pub, 1)); + + this->state = STATE_KEY_EXCHANGE_RECEIVED; + return NEED_MORE; +} + +/** + * Process a Server Key Exchange + */ +static status_t process_key_exchange(private_tls_peer_t *this, + tls_reader_t *reader) +{ + diffie_hellman_group_t group; + + this->crypto->append_handshake(this->crypto, + TLS_SERVER_KEY_EXCHANGE, reader->peek(reader)); + + group = this->crypto->get_dh_group(this->crypto); + if (group == MODP_NONE) + { + DBG1(DBG_TLS, "received Server Key Exchange, but not required " + "for current suite"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + if (diffie_hellman_group_is_ec(group)) + { + return process_ec_key_exchange(this, reader); + } + return process_modp_key_exchange(this, reader); +} + +/** + * Process a Certificate Request message + */ +static status_t process_certreq(private_tls_peer_t *this, tls_reader_t *reader) +{ + chunk_t types, hashsig, data; + tls_reader_t *authorities; + identification_t *id; + certificate_t *cert; + + if (!this->peer) + { + DBG1(DBG_TLS, "server requested a certificate, but client " + "authentication disabled"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE_REQUEST, reader->peek(reader)); + + if (!reader->read_data8(reader, &types)) + { + DBG1(DBG_TLS, "certreq message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + this->cert_types = chunk_clone(types); + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + if (!reader->read_data16(reader, &hashsig)) + { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + this->hashsig = chunk_clone(hashsig); + } + if (!reader->read_data16(reader, &data)) + { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + authorities = tls_reader_create(data); + while (authorities->remaining(authorities)) + { + if (!authorities->read_data16(authorities, &data)) + { + DBG1(DBG_TLS, "certreq message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + authorities->destroy(authorities); + return NEED_MORE; + } + id = identification_create_from_encoding(ID_DER_ASN1_DN, data); + cert = lib->credmgr->get_cert(lib->credmgr, + CERT_X509, KEY_ANY, id, TRUE); + if (cert) + { + DBG1(DBG_TLS, "received TLS cert request for '%Y", id); + this->peer_auth->add(this->peer_auth, AUTH_RULE_CA_CERT, cert); + } + else + { + DBG1(DBG_TLS, "received TLS cert request for unknown CA '%Y'", id); + } + id->destroy(id); + } + authorities->destroy(authorities); + this->state = STATE_CERTREQ_RECEIVED; + return NEED_MORE; +} + +/** + * Process Hello Done message + */ +static status_t process_hello_done(private_tls_peer_t *this, + tls_reader_t *reader) +{ + this->crypto->append_handshake(this->crypto, + TLS_SERVER_HELLO_DONE, reader->peek(reader)); + this->state = STATE_HELLO_DONE; + return NEED_MORE; +} + +/** + * Process finished message + */ +static status_t process_finished(private_tls_peer_t *this, tls_reader_t *reader) +{ + chunk_t received; + char buf[12]; + + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_TLS, "received server finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) + { + DBG1(DBG_TLS, "calculating server finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + if (!chunk_equals(received, chunk_from_thing(buf))) + { + DBG1(DBG_TLS, "received server finished invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; + } + this->state = STATE_COMPLETE; + this->crypto->derive_eap_msk(this->crypto, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + return NEED_MORE; +} + +METHOD(tls_handshake_t, process, status_t, + private_tls_peer_t *this, tls_handshake_type_t type, tls_reader_t *reader) +{ + tls_handshake_type_t expected; + + switch (this->state) + { + case STATE_HELLO_SENT: + if (type == TLS_SERVER_HELLO) + { + return process_server_hello(this, reader); + } + expected = TLS_SERVER_HELLO; + break; + case STATE_HELLO_RECEIVED: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + expected = TLS_CERTIFICATE; + break; + case STATE_CERT_RECEIVED: + if (type == TLS_SERVER_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + /* fall through since TLS_SERVER_KEY_EXCHANGE is optional */ + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_REQUEST) + { + return process_certreq(this, reader); + } + this->peer = NULL; + /* fall through since TLS_CERTIFICATE_REQUEST is optional */ + case STATE_CERTREQ_RECEIVED: + if (type == TLS_SERVER_HELLO_DONE) + { + return process_hello_done(this, reader); + } + expected = TLS_SERVER_HELLO_DONE; + break; + case STATE_CIPHERSPEC_CHANGED_IN: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + DBG1(DBG_TLS, "TLS %N expected, but received %N", + tls_handshake_type_names, expected, tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; +} + +/** + * Send a client hello + */ +static status_t send_client_hello(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + tls_cipher_suite_t *suites; + tls_writer_t *extensions, *curves = NULL; + tls_version_t version; + tls_named_curve_t curve; + enumerator_t *enumerator; + int count, i; + rng_t *rng; + + htoun32(&this->client_random, time(NULL)); + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_TLS, "no suitable RNG found to generate client random"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + rng->get_bytes(rng, sizeof(this->client_random) - 4, this->client_random + 4); + rng->destroy(rng); + + /* TLS version */ + version = this->tls->get_version(this->tls); + writer->write_uint16(writer, version); + writer->write_data(writer, chunk_from_thing(this->client_random)); + + /* session identifier => none */ + writer->write_data8(writer, chunk_empty); + + /* add TLS cipher suites */ + count = this->crypto->get_cipher_suites(this->crypto, &suites); + writer->write_uint16(writer, count * 2); + for (i = 0; i < count; i++) + { + writer->write_uint16(writer, suites[i]); + } + + /* NULL compression only */ + writer->write_uint8(writer, 1); + writer->write_uint8(writer, 0); + + extensions = tls_writer_create(32); + + extensions->write_uint16(extensions, TLS_EXT_SIGNATURE_ALGORITHMS); + this->crypto->get_signature_algorithms(this->crypto, extensions); + + /* add supported Elliptic Curves, if any */ + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, NULL, &curve)) + { + if (!curves) + { + extensions->write_uint16(extensions, TLS_EXT_ELLIPTIC_CURVES); + curves = tls_writer_create(16); + } + curves->write_uint16(curves, curve); + } + enumerator->destroy(enumerator); + if (curves) + { + extensions->write_data16(extensions, curves->get_buf(curves)); + curves->destroy(curves); + + /* if we support curves, add point format extension */ + extensions->write_uint16(extensions, TLS_EXT_EC_POINT_FORMATS); + extensions->write_uint16(extensions, 2); + extensions->write_uint8(extensions, 1); + extensions->write_uint8(extensions, TLS_EC_POINT_UNCOMPRESSED); + } + + writer->write_data16(writer, extensions->get_buf(extensions)); + extensions->destroy(extensions); + + *type = TLS_CLIENT_HELLO; + this->state = STATE_HELLO_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Find a private key suitable to sign Certificate Verify + */ +static private_key_t *find_private_key(private_tls_peer_t *this) +{ + private_key_t *key = NULL; + tls_reader_t *reader; + key_type_t type; + u_int8_t cert; + + if (!this->peer) + { + return NULL; + } + reader = tls_reader_create(this->cert_types); + while (reader->remaining(reader) && reader->read_uint8(reader, &cert)) + { + switch (cert) + { + case TLS_RSA_SIGN: + type = KEY_RSA; + break; + case TLS_ECDSA_SIGN: + type = KEY_ECDSA; + break; + default: + continue; + } + key = lib->credmgr->get_private(lib->credmgr, type, + this->peer, this->peer_auth); + if (key) + { + break; + } + } + reader->destroy(reader); + return key; +} + +/** + * Send Certificate + */ +static status_t send_certificate(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + enumerator_t *enumerator; + certificate_t *cert; + auth_rule_t rule; + tls_writer_t *certs; + chunk_t data; + + this->private = find_private_key(this); + if (!this->private) + { + DBG1(DBG_TLS, "no TLS peer certificate found for '%Y'", this->peer); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + /* generate certificate payload */ + certs = tls_writer_create(256); + cert = this->peer_auth->get(this->peer_auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) + { + DBG1(DBG_TLS, "sending TLS peer certificate '%Y'", + cert->get_subject(cert)); + certs->write_data24(certs, data); + free(data.ptr); + } + } + enumerator = this->peer_auth->create_enumerator(this->peer_auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_IM_CERT) + { + if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) + { + DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + certs->write_data24(certs, data); + free(data.ptr); + } + } + } + enumerator->destroy(enumerator); + + writer->write_data24(writer, certs->get_buf(certs)); + certs->destroy(certs); + + *type = TLS_CERTIFICATE; + this->state = STATE_CERT_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send client key exchange, using premaster encryption + */ +static status_t send_key_exchange_encrypt(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + public_key_t *public; + rng_t *rng; + char premaster[48]; + chunk_t encrypted; + + rng = lib->crypto->create_rng(lib->crypto, RNG_STRONG); + if (!rng) + { + DBG1(DBG_TLS, "no suitable RNG found for TLS premaster secret"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2); + rng->destroy(rng); + htoun16(premaster, TLS_1_2); + + this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + + public = find_public_key(this); + if (!public) + { + DBG1(DBG_TLS, "no TLS public key found for server '%Y'", this->server); + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + if (!public->encrypt(public, ENCRYPT_RSA_PKCS1, + chunk_from_thing(premaster), &encrypted)) + { + public->destroy(public); + DBG1(DBG_TLS, "encrypting TLS premaster secret failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_CERTIFICATE); + return NEED_MORE; + } + public->destroy(public); + + writer->write_data16(writer, encrypted); + free(encrypted.ptr); + + *type = TLS_CLIENT_KEY_EXCHANGE; + this->state = STATE_KEY_EXCHANGE_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send client key exchange, using DHE exchange + */ +static status_t send_key_exchange_dhe(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + chunk_t premaster, pub; + + if (this->dh->get_shared_secret(this->dh, &premaster) != SUCCESS) + { + DBG1(DBG_TLS, "calculating premaster from DH failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->crypto->derive_secrets(this->crypto, premaster, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + chunk_clear(&premaster); + + this->dh->get_my_public_value(this->dh, &pub); + if (this->dh->get_dh_group(this->dh) == MODP_CUSTOM) + { + writer->write_data16(writer, pub); + } + else + { /* ECP uses 8bit length header only, but a point format */ + writer->write_uint8(writer, pub.len + 1); + writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); + writer->write_data(writer, pub); + } + free(pub.ptr); + + *type = TLS_CLIENT_KEY_EXCHANGE; + this->state = STATE_KEY_EXCHANGE_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send client key exchange, depending on suite + */ +static status_t send_key_exchange(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + if (this->dh) + { + return send_key_exchange_dhe(this, type, writer); + } + return send_key_exchange_encrypt(this, type, writer); +} + +/** + * Send certificate verify + */ +static status_t send_certificate_verify(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + if (!this->private || + !this->crypto->sign_handshake(this->crypto, this->private, + writer, this->hashsig)) + { + DBG1(DBG_TLS, "creating TLS Certificate Verify signature failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + *type = TLS_CERTIFICATE_VERIFY; + this->state = STATE_VERIFY_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Finished + */ +static status_t send_finished(private_tls_peer_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + char buf[12]; + + if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + { + DBG1(DBG_TLS, "calculating client finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + writer->write_data(writer, chunk_from_thing(buf)); + + *type = TLS_FINISHED; + this->state = STATE_FINISHED_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +METHOD(tls_handshake_t, build, status_t, + private_tls_peer_t *this, tls_handshake_type_t *type, tls_writer_t *writer) +{ + switch (this->state) + { + case STATE_INIT: + return send_client_hello(this, type, writer); + case STATE_HELLO_DONE: + if (this->peer) + { + return send_certificate(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERT_SENT: + return send_key_exchange(this, type, writer); + case STATE_KEY_EXCHANGE_SENT: + if (this->peer) + { + return send_certificate_verify(this, type, writer); + } + else + { + return INVALID_STATE; + } + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + default: + return INVALID_STATE; + } +} + +METHOD(tls_handshake_t, cipherspec_changed, bool, + private_tls_peer_t *this) +{ + if ((this->peer && this->state == STATE_VERIFY_SENT) || + (!this->peer && this->state == STATE_KEY_EXCHANGE_SENT)) + { + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + return TRUE; + } + return FALSE; +} + +METHOD(tls_handshake_t, change_cipherspec, bool, + private_tls_peer_t *this) +{ + if (this->state == STATE_FINISHED_SENT) + { + this->crypto->change_cipher(this->crypto, TRUE); + this->state = STATE_CIPHERSPEC_CHANGED_IN; + return TRUE; + } + return FALSE; +} + +METHOD(tls_handshake_t, finished, bool, + private_tls_peer_t *this) +{ + return this->state == STATE_COMPLETE; +} + +METHOD(tls_handshake_t, destroy, void, + private_tls_peer_t *this) +{ + DESTROY_IF(this->private); + DESTROY_IF(this->dh); + this->peer_auth->destroy(this->peer_auth); + this->server_auth->destroy(this->server_auth); + free(this->hashsig.ptr); + free(this->cert_types.ptr); + free(this); +} + +/** + * See header + */ +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *peer, identification_t *server) +{ + private_tls_peer_t *this; + + INIT(this, + .public = { + .handshake = { + .process = _process, + .build = _build, + .cipherspec_changed = _cipherspec_changed, + .change_cipherspec = _change_cipherspec, + .finished = _finished, + .destroy = _destroy, + }, + }, + .state = STATE_INIT, + .tls = tls, + .crypto = crypto, + .alert = alert, + .peer = peer, + .server = server, + .peer_auth = auth_cfg_create(), + .server_auth = auth_cfg_create(), + ); + + return &this->public; +} diff --git a/src/libtls/tls_peer.h b/src/libtls/tls_peer.h new file mode 100644 index 000000000..f773ea72e --- /dev/null +++ b/src/libtls/tls_peer.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_peer tls_peer + * @{ @ingroup libtls + */ + +#ifndef TLS_PEER_H_ +#define TLS_PEER_H_ + +typedef struct tls_peer_t tls_peer_t; + +#include "tls_handshake.h" +#include "tls_crypto.h" + +#include + +/** + * TLS handshake protocol handler as peer. + */ +struct tls_peer_t { + + /** + * Implements the TLS handshake protocol handler. + */ + tls_handshake_t handshake; +}; + +/** + * Create a tls_peer instance. +* + * @param tls TLS stack + * @param crypto TLS crypto helper + * @param alert TLS alert handler + * @param peer peer identity + * @param server server identity + */ +tls_peer_t *tls_peer_create(tls_t *tls, tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *peer, identification_t *server); + +#endif /** TLS_PEER_H_ @}*/ diff --git a/src/libtls/tls_prf.c b/src/libtls/tls_prf.c new file mode 100644 index 000000000..f181d01d3 --- /dev/null +++ b/src/libtls/tls_prf.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_prf.h" + +typedef struct private_tls_prf12_t private_tls_prf12_t; + +/** + * Private data of an tls_prf_t object. + */ +struct private_tls_prf12_t { + + /** + * Public tls_prf_t interface. + */ + tls_prf_t public; + + /** + * Underlying primitive PRF + */ + prf_t *prf; +}; + +METHOD(tls_prf_t, set_key12, void, + private_tls_prf12_t *this, chunk_t key) +{ + this->prf->set_key(this->prf, key); +} + +/** + * The P_hash function as in TLS 1.0/1.2 + */ +static void p_hash(prf_t *prf, char *label, chunk_t seed, size_t block_size, + size_t bytes, char *out) +{ + char buf[block_size], abuf[block_size]; + chunk_t a; + + /* seed = label + seed */ + seed = chunk_cata("cc", chunk_create(label, strlen(label)), seed); + /* A(0) = seed */ + a = seed; + + while (TRUE) + { + /* A(i) = HMAC_hash(secret, A(i-1)) */ + prf->get_bytes(prf, a, abuf); + a = chunk_from_thing(abuf); + /* HMAC_hash(secret, A(i) + seed) */ + prf->get_bytes(prf, a, NULL); + prf->get_bytes(prf, seed, buf); + + if (bytes <= block_size) + { + memcpy(out, buf, bytes); + break; + } + memcpy(out, buf, block_size); + out += block_size; + bytes -= block_size; + } +} + +METHOD(tls_prf_t, get_bytes12, void, + private_tls_prf12_t *this, char *label, chunk_t seed, + size_t bytes, char *out) +{ + p_hash(this->prf, label, seed, this->prf->get_block_size(this->prf), + bytes, out); +} + +METHOD(tls_prf_t, destroy12, void, + private_tls_prf12_t *this) +{ + this->prf->destroy(this->prf); + free(this); +} + +/** + * See header + */ +tls_prf_t *tls_prf_create_12(pseudo_random_function_t prf) +{ + private_tls_prf12_t *this; + + INIT(this, + .public = { + .set_key = _set_key12, + .get_bytes = _get_bytes12, + .destroy = _destroy12, + }, + .prf = lib->crypto->create_prf(lib->crypto, prf), + ); + if (!this->prf) + { + free(this); + return NULL; + } + return &this->public; +} + + +typedef struct private_tls_prf10_t private_tls_prf10_t; + +/** + * Private data of an tls_prf_t object. + */ +struct private_tls_prf10_t { + + /** + * Public tls_prf_t interface. + */ + tls_prf_t public; + + /** + * Underlying MD5 PRF + */ + prf_t *md5; + + /** + * Underlying SHA1 PRF + */ + prf_t *sha1; +}; + +METHOD(tls_prf_t, set_key10, void, + private_tls_prf10_t *this, chunk_t key) +{ + size_t len = key.len / 2 + key.len % 2; + + this->md5->set_key(this->md5, chunk_create(key.ptr, len)); + this->sha1->set_key(this->sha1, chunk_create(key.ptr + key.len - len, len)); +} + +METHOD(tls_prf_t, get_bytes10, void, + private_tls_prf10_t *this, char *label, chunk_t seed, + size_t bytes, char *out) +{ + char buf[bytes]; + + p_hash(this->md5, label, seed, this->md5->get_block_size(this->md5), + bytes, out); + p_hash(this->sha1, label, seed, this->sha1->get_block_size(this->sha1), + bytes, buf); + memxor(out, buf, bytes); +} + +METHOD(tls_prf_t, destroy10, void, + private_tls_prf10_t *this) +{ + DESTROY_IF(this->md5); + DESTROY_IF(this->sha1); + free(this); +} + +/** + * See header + */ +tls_prf_t *tls_prf_create_10(pseudo_random_function_t prf) +{ + private_tls_prf10_t *this; + + INIT(this, + .public = { + .set_key = _set_key10, + .get_bytes = _get_bytes10, + .destroy = _destroy10, + }, + .md5 = lib->crypto->create_prf(lib->crypto, PRF_HMAC_MD5), + .sha1 = lib->crypto->create_prf(lib->crypto, PRF_HMAC_SHA1), + ); + if (!this->md5 || !this->sha1) + { + destroy10(this); + return NULL; + } + return &this->public; +} diff --git a/src/libtls/tls_prf.h b/src/libtls/tls_prf.h new file mode 100644 index 000000000..9fb9bc2de --- /dev/null +++ b/src/libtls/tls_prf.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_prf tls_prf + * @{ @ingroup libtls + */ + +#ifndef TLS_PRF_H_ +#define TLS_PRF_H_ + +typedef struct tls_prf_t tls_prf_t; + +#include + +/** + * The PRF function specified on TLS, based on HMAC. + */ +struct tls_prf_t { + + /** + * Set the key of the PRF function. + * + * @param key key to set + */ + void (*set_key)(tls_prf_t *this, chunk_t key); + + /** + * Generate a series of bytes using a label and a seed. + * + * @param label ASCII input label + * @param seed seed input value + * @param bytes number of bytes to get + * @param out buffer receiving bytes + */ + void (*get_bytes)(tls_prf_t *this, char *label, chunk_t seed, + size_t bytes, char *out); + + /** + * Destroy a tls_prf_t. + */ + void (*destroy)(tls_prf_t *this); +}; + +/** + * Create a tls_prf instance with specific algorithm as in TLS 1.2. + * + * @param prf underlying PRF function to use + * @return TLS PRF algorithm + */ +tls_prf_t *tls_prf_create_12(pseudo_random_function_t prf); + +/** + * Create a tls_prf instance with XOred SHA1/MD5 as in TLS 1.0/1.1. + * + * @return TLS PRF algorithm + */ +tls_prf_t *tls_prf_create_10(); + +#endif /** TLS_PRF_H_ @}*/ diff --git a/src/libtls/tls_protection.c b/src/libtls/tls_protection.c new file mode 100644 index 000000000..d823bae04 --- /dev/null +++ b/src/libtls/tls_protection.c @@ -0,0 +1,333 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_protection.h" + +#include + +typedef struct private_tls_protection_t private_tls_protection_t; + +/** + * Private data of an tls_protection_t object. + */ +struct private_tls_protection_t { + + /** + * Public tls_protection_t interface. + */ + tls_protection_t public; + + /** + * negotiated TLS version + */ + tls_version_t version; + + /** + * Upper layer, TLS record compression + */ + tls_compression_t *compression; + + /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * RNG if we generate IVs ourself + */ + rng_t *rng; + + /** + * Sequence number of incoming records + */ + u_int32_t seq_in; + + /** + * Sequence number for outgoing records + */ + u_int32_t seq_out; + + /** + * Signer instance for inbound traffic + */ + signer_t *signer_in; + + /** + * Signer instance for outbound traffic + */ + signer_t *signer_out; + + /** + * Crypter instance for inbound traffic + */ + crypter_t *crypter_in; + + /** + * Crypter instance for outbound traffic + */ + crypter_t *crypter_out; + + /** + * Current IV for input decryption + */ + chunk_t iv_in; + + /** + * Current IV for output decryption + */ + chunk_t iv_out; +}; + +/** + * Create the header to append to the record data to create the MAC + */ +static chunk_t sigheader(u_int32_t seq, u_int8_t type, + u_int16_t version, u_int16_t length) +{ + /* we only support 32 bit sequence numbers, but TLS uses 64 bit */ + u_int32_t seq_high = 0; + + seq = htonl(seq); + version = htons(version); + length = htons(length); + + return chunk_cat("ccccc", chunk_from_thing(seq_high), + chunk_from_thing(seq), chunk_from_thing(type), + chunk_from_thing(version), chunk_from_thing(length)); +} + +METHOD(tls_protection_t, process, status_t, + private_tls_protection_t *this, tls_content_type_t type, chunk_t data) +{ + if (this->alert->fatal(this->alert)) + { /* don't accept more input, fatal error ocurred */ + return NEED_MORE; + } + + if (this->crypter_in) + { + chunk_t iv, next_iv = chunk_empty; + u_int8_t bs, padding_length; + + bs = this->crypter_in->get_block_size(this->crypter_in); + if (this->iv_in.len) + { /* < TLSv1.1 uses IV from key derivation/last block */ + if (data.len < bs || data.len % bs) + { + DBG1(DBG_TLS, "encrypted TLS record length invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } + iv = this->iv_in; + next_iv = chunk_clone(chunk_create(data.ptr + data.len - bs, bs)); + } + else + { /* TLSv1.1 uses random IVs, prepended to record */ + iv.len = this->crypter_in->get_iv_size(this->crypter_in); + iv = chunk_create(data.ptr, iv.len); + data = chunk_skip(data, iv.len); + if (data.len < bs || data.len % bs) + { + DBG1(DBG_TLS, "encrypted TLS record length invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } + } + this->crypter_in->decrypt(this->crypter_in, data, iv, NULL); + + if (next_iv.len) + { /* next record IV is last ciphertext block of this record */ + memcpy(this->iv_in.ptr, next_iv.ptr, next_iv.len); + free(next_iv.ptr); + } + + padding_length = data.ptr[data.len - 1]; + if (padding_length >= data.len) + { + DBG1(DBG_TLS, "invalid TLS record padding"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } + data.len -= padding_length + 1; + } + if (this->signer_in) + { + chunk_t mac, macdata, header; + u_int8_t bs; + + bs = this->signer_in->get_block_size(this->signer_in); + if (data.len < bs) + { + DBG1(DBG_TLS, "TLS record too short to verify MAC"); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } + mac = chunk_skip(data, data.len - bs); + data.len -= bs; + + header = sigheader(this->seq_in, type, this->version, data.len); + macdata = chunk_cat("mc", header, data); + if (!this->signer_in->verify_signature(this->signer_in, macdata, mac)) + { + DBG1(DBG_TLS, "TLS record MAC verification failed"); + free(macdata.ptr); + this->alert->add(this->alert, TLS_FATAL, TLS_BAD_RECORD_MAC); + return NEED_MORE; + } + free(macdata.ptr); + } + + if (type == TLS_CHANGE_CIPHER_SPEC) + { + this->seq_in = 0; + } + else + { + this->seq_in++; + } + return this->compression->process(this->compression, type, data); +} + +METHOD(tls_protection_t, build, status_t, + private_tls_protection_t *this, tls_content_type_t *type, chunk_t *data) +{ + status_t status; + + status = this->compression->build(this->compression, type, data); + if (*type == TLS_CHANGE_CIPHER_SPEC) + { + this->seq_out = 0; + return status; + } + + if (status == NEED_MORE) + { + if (this->signer_out) + { + chunk_t mac, header; + + header = sigheader(this->seq_out, *type, this->version, data->len); + this->signer_out->get_signature(this->signer_out, header, NULL); + free(header.ptr); + this->signer_out->allocate_signature(this->signer_out, *data, &mac); + if (this->crypter_out) + { + chunk_t padding, iv; + u_int8_t bs, padding_length; + + bs = this->crypter_out->get_block_size(this->crypter_out); + padding_length = bs - ((data->len + mac.len + 1) % bs); + + padding = chunk_alloca(padding_length); + memset(padding.ptr, padding_length, padding.len); + + if (this->iv_out.len) + { /* < TLSv1.1 uses IV from key derivation/last block */ + iv = this->iv_out; + } + else + { /* TLSv1.1 uses random IVs, prepended to record */ + if (!this->rng) + { + DBG1(DBG_TLS, "no RNG supported to generate TLS IV"); + free(data->ptr); + return FAILED; + } + iv.len = this->crypter_out->get_iv_size(this->crypter_out); + this->rng->allocate_bytes(this->rng, iv.len, &iv); + } + + *data = chunk_cat("mmcc", *data, mac, padding, + chunk_from_thing(padding_length)); + /* encrypt inline */ + this->crypter_out->encrypt(this->crypter_out, *data, iv, NULL); + + if (this->iv_out.len) + { /* next record IV is last ciphertext block of this record */ + memcpy(this->iv_out.ptr, data->ptr + data->len - + this->iv_out.len, this->iv_out.len); + } + else + { /* prepend IV */ + *data = chunk_cat("mm", iv, *data); + } + } + else + { /* NULL encryption */ + *data = chunk_cat("mm", *data, mac); + } + } + this->seq_out++; + } + return status; +} + +METHOD(tls_protection_t, set_cipher, void, + private_tls_protection_t *this, bool inbound, signer_t *signer, + crypter_t *crypter, chunk_t iv) +{ + if (inbound) + { + this->signer_in = signer; + this->crypter_in = crypter; + this->iv_in = iv; + } + else + { + this->signer_out = signer; + this->crypter_out = crypter; + this->iv_out = iv; + if (!iv.len) + { /* generate IVs if none given */ + this->rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + } + } +} + +METHOD(tls_protection_t, set_version, void, + private_tls_protection_t *this, tls_version_t version) +{ + this->version = version; +} + +METHOD(tls_protection_t, destroy, void, + private_tls_protection_t *this) +{ + DESTROY_IF(this->rng); + free(this); +} + +/** + * See header + */ +tls_protection_t *tls_protection_create(tls_compression_t *compression, + tls_alert_t *alert) +{ + private_tls_protection_t *this; + + INIT(this, + .public = { + .process = _process, + .build = _build, + .set_cipher = _set_cipher, + .set_version = _set_version, + .destroy = _destroy, + }, + .alert = alert, + .compression = compression, + ); + + return &this->public; +} diff --git a/src/libtls/tls_protection.h b/src/libtls/tls_protection.h new file mode 100644 index 000000000..99c94e935 --- /dev/null +++ b/src/libtls/tls_protection.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_protection tls_protection + * @{ @ingroup libtls + */ + +#ifndef TLS_PROTECTION_H_ +#define TLS_PROTECTION_H_ + +#include + +#include "tls.h" +#include "tls_alert.h" +#include "tls_compression.h" + +typedef struct tls_protection_t tls_protection_t; + +/** + * TLS record protocol protection layer. + */ +struct tls_protection_t { + + /** + * Process a protected TLS record, pass it to upper layers. + * + * @param type type of the TLS record to process + * @param data associated TLS record data + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if more invocations to process/build needed + */ + status_t (*process)(tls_protection_t *this, + tls_content_type_t type, chunk_t data); + + /** + * Query upper layer for TLS record, build protected record. + * + * @param type type of the built TLS record + * @param data allocated data of the built TLS record + * @return + * - SUCCESS if TLS negotiation complete + * - FAILED if TLS handshake failed + * - NEED_MORE if upper layers have more records to send + * - INVALID_STATE if more input records required + */ + status_t (*build)(tls_protection_t *this, + tls_content_type_t *type, chunk_t *data); + + /** + * Set a new cipher, including encryption and integrity algorithms. + * + * @param inbound TRUE to use cipher for inbound data, FALSE for outbound + * @param signer new signer to use, gets owned by protection layer + * @param crypter new crypter to use, gets owned by protection layer + * @param iv initial IV for crypter, gets owned by protection layer + */ + void (*set_cipher)(tls_protection_t *this, bool inbound, signer_t *signer, + crypter_t *crypter, chunk_t iv); + + /** + * Set the TLS version negotiated, used for MAC calculation. + * + * @param version TLS version negotiated + */ + void (*set_version)(tls_protection_t *this, tls_version_t version); + + /** + * Destroy a tls_protection_t. + */ + void (*destroy)(tls_protection_t *this); +}; + +/** + * Create a tls_protection instance. + * + * @param compression compression layer of TLS stack + * @param alert TLS alert handler + * @return TLS protection layer. + */ +tls_protection_t *tls_protection_create(tls_compression_t *compression, + tls_alert_t *alert); + +#endif /** TLS_PROTECTION_H_ @}*/ diff --git a/src/libtls/tls_reader.c b/src/libtls/tls_reader.c new file mode 100644 index 000000000..17ec68fd5 --- /dev/null +++ b/src/libtls/tls_reader.c @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_reader.h" + +#include + +typedef struct private_tls_reader_t private_tls_reader_t; + +/** + * Private data of an tls_reader_t object. + */ +struct private_tls_reader_t { + + /** + * Public tls_reader_t interface. + */ + tls_reader_t public; + + /** + * Remaining data to process + */ + chunk_t buf; +}; + +METHOD(tls_reader_t, remaining, u_int32_t, + private_tls_reader_t *this) +{ + return this->buf.len; +} + +METHOD(tls_reader_t, peek, chunk_t, + private_tls_reader_t *this) +{ + return this->buf; +} + +METHOD(tls_reader_t, read_uint8, bool, + private_tls_reader_t *this, u_int8_t *res) +{ + if (this->buf.len < 1) + { + DBG1(DBG_TLS, "%d bytes insufficient to parse uint%d TLS data", + this->buf.len, 8); + return FALSE; + } + *res = this->buf.ptr[0]; + this->buf = chunk_skip(this->buf, 1); + return TRUE; +} + +METHOD(tls_reader_t, read_uint16, bool, + private_tls_reader_t *this, u_int16_t *res) +{ + if (this->buf.len < 2) + { + DBG1(DBG_TLS, "%d bytes insufficient to parse uint%d TLS data", + this->buf.len, 16); + return FALSE; + } + *res = untoh16(this->buf.ptr); + this->buf = chunk_skip(this->buf, 2); + return TRUE; +} + +METHOD(tls_reader_t, read_uint24, bool, + private_tls_reader_t *this, u_int32_t *res) +{ + if (this->buf.len < 3) + { + DBG1(DBG_TLS, "%d bytes insufficient to parse uint%d TLS data", + this->buf.len, 24); + return FALSE; + } + *res = untoh32(this->buf.ptr) >> 8; + this->buf = chunk_skip(this->buf, 3); + return TRUE; +} + +METHOD(tls_reader_t, read_uint32, bool, + private_tls_reader_t *this, u_int32_t *res) +{ + if (this->buf.len < 4) + { + DBG1(DBG_TLS, "%d bytes insufficient to parse uint%d TLS data", + this->buf.len, 32); + return FALSE; + } + *res = untoh32(this->buf.ptr); + this->buf = chunk_skip(this->buf, 4); + return TRUE; +} + +METHOD(tls_reader_t, read_data, bool, + private_tls_reader_t *this, u_int32_t len, chunk_t *res) +{ + if (this->buf.len < len) + { + DBG1(DBG_TLS, "%d bytes insufficient to parse %d bytes TLS data", + this->buf.len, len); + return FALSE; + } + *res = chunk_create(this->buf.ptr, len); + this->buf = chunk_skip(this->buf, len); + return TRUE; +} + +METHOD(tls_reader_t, read_data8, bool, + private_tls_reader_t *this, chunk_t *res) +{ + u_int8_t len; + + if (!read_uint8(this, &len)) + { + return FALSE; + } + return read_data(this, len, res); +} + +METHOD(tls_reader_t, read_data16, bool, + private_tls_reader_t *this, chunk_t *res) +{ + u_int16_t len; + + if (!read_uint16(this, &len)) + { + return FALSE; + } + return read_data(this, len, res); +} + +METHOD(tls_reader_t, read_data24, bool, + private_tls_reader_t *this, chunk_t *res) +{ + u_int32_t len; + + if (!read_uint24(this, &len)) + { + return FALSE; + } + return read_data(this, len, res); +} + +METHOD(tls_reader_t, read_data32, bool, + private_tls_reader_t *this, chunk_t *res) +{ + u_int32_t len; + + if (!read_uint32(this, &len)) + { + return FALSE; + } + return read_data(this, len, res); +} + +METHOD(tls_reader_t, destroy, void, + private_tls_reader_t *this) +{ + free(this); +} + +/** + * See header + */ +tls_reader_t *tls_reader_create(chunk_t data) +{ + private_tls_reader_t *this; + + INIT(this, + .public = { + .remaining = _remaining, + .peek = _peek, + .read_uint8 = _read_uint8, + .read_uint16 = _read_uint16, + .read_uint24 = _read_uint24, + .read_uint32 = _read_uint32, + .read_data = _read_data, + .read_data8 = _read_data8, + .read_data16 = _read_data16, + .read_data24 = _read_data24, + .read_data32 = _read_data32, + .destroy = _destroy, + }, + .buf = data, + ); + + return &this->public; +} diff --git a/src/libtls/tls_reader.h b/src/libtls/tls_reader.h new file mode 100644 index 000000000..a8978b486 --- /dev/null +++ b/src/libtls/tls_reader.h @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_reader tls_reader + * @{ @ingroup libtls + */ + +#ifndef TLS_READER_H_ +#define TLS_READER_H_ + +typedef struct tls_reader_t tls_reader_t; + +#include + +/** + * TLS record parser. + */ +struct tls_reader_t { + + /** + * Get the number of remaining bytes. + * + * @return number of remaining bytes in buffer + */ + u_int32_t (*remaining)(tls_reader_t *this); + + /** + * Peek the remaining data, not consuming any bytes. + * + * @return remaining data + */ + chunk_t (*peek)(tls_reader_t *this); + + /** + * Read a 8-bit integer from the buffer, advance. + * + * @param res pointer to result + * @return TRUE if integer read successfully + */ + bool (*read_uint8)(tls_reader_t *this, u_int8_t *res); + + /** + * Read a 16-bit integer from the buffer, advance. + * + * @param res pointer to result + * @return TRUE if integer read successfully + */ + bool (*read_uint16)(tls_reader_t *this, u_int16_t *res); + + /** + * Read a 24-bit integer from the buffer, advance. + * + * @param res pointer to result + * @return TRUE if integer read successfully + */ + bool (*read_uint24)(tls_reader_t *this, u_int32_t *res); + + /** + * Read a 32-bit integer from the buffer, advance. + * + * @param res pointer to result + * @return TRUE if integer read successfully + */ + bool (*read_uint32)(tls_reader_t *this, u_int32_t *res); + + /** + * Read a chunk of len bytes, advance. + * + * @param len number of bytes to read + * @param res pointer to result, not cloned + * @return TRUE if data read successfully + */ + bool (*read_data)(tls_reader_t *this, u_int32_t len, chunk_t *res); + + /** + * Read a chunk of bytes with a 8-bit length header, advance. + * + * @param res pointer to result, not cloned + * @return TRUE if data read successfully + */ + bool (*read_data8)(tls_reader_t *this, chunk_t *res); + + /** + * Read a chunk of bytes with a 16-bit length header, advance. + * + * @param res pointer to result, not cloned + * @return TRUE if data read successfully + */ + bool (*read_data16)(tls_reader_t *this, chunk_t *res); + + /** + * Read a chunk of bytes with a 24-bit length header, advance. + * + * @param res pointer to result, not cloned + * @return TRUE if data read successfully + */ + bool (*read_data24)(tls_reader_t *this, chunk_t *res); + + /** + * Read a chunk of bytes with a 32-bit length header, advance. + * + * @param res pointer to result, not cloned + * @return TRUE if data read successfully + */ + bool (*read_data32)(tls_reader_t *this, chunk_t *res); + + /** + * Destroy a tls_reader_t. + */ + void (*destroy)(tls_reader_t *this); +}; + +/** + * Create a tls_reader instance. + */ +tls_reader_t *tls_reader_create(chunk_t data); + +#endif /** tls_reader_H_ @}*/ diff --git a/src/libtls/tls_server.c b/src/libtls/tls_server.c new file mode 100644 index 000000000..b0417f6cb --- /dev/null +++ b/src/libtls/tls_server.c @@ -0,0 +1,1032 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_server.h" + +#include + +#include +#include + +typedef struct private_tls_server_t private_tls_server_t; + + +typedef enum { + STATE_INIT, + STATE_HELLO_RECEIVED, + STATE_HELLO_SENT, + STATE_CERT_SENT, + STATE_KEY_EXCHANGE_SENT, + STATE_CERTREQ_SENT, + STATE_HELLO_DONE, + STATE_CERT_RECEIVED, + STATE_KEY_EXCHANGE_RECEIVED, + STATE_CERT_VERIFY_RECEIVED, + STATE_CIPHERSPEC_CHANGED_IN, + STATE_FINISHED_RECEIVED, + STATE_CIPHERSPEC_CHANGED_OUT, + STATE_FINISHED_SENT, +} server_state_t; + +/** + * Private data of an tls_server_t object. + */ +struct private_tls_server_t { + + /** + * Public tls_server_t interface. + */ + tls_server_t public; + + /** + * TLS stack + */ + tls_t *tls; + + /** + * TLS crypto context + */ + tls_crypto_t *crypto; + + /** + * TLS alert handler + */ + tls_alert_t *alert; + + /** + * Server identity + */ + identification_t *server; + + /** + * Peer identity, NULL for no client authentication + */ + identification_t *peer; + + /** + * State we are in + */ + server_state_t state; + + /** + * Hello random data selected by client + */ + char client_random[32]; + + /** + * Hello random data selected by server + */ + char server_random[32]; + + /** + * Auth helper for peer authentication + */ + auth_cfg_t *peer_auth; + + /** + * Auth helper for server authentication + */ + auth_cfg_t *server_auth; + + /** + * Peer private key + */ + private_key_t *private; + + /** + * DHE exchange + */ + diffie_hellman_t *dh; + + /** + * Selected TLS cipher suite + */ + tls_cipher_suite_t suite; + + /** + * Offered TLS version of the client + */ + tls_version_t client_version; + + /** + * Hash and signature algorithms supported by peer + */ + chunk_t hashsig; + + /** + * Elliptic curves supported by peer + */ + chunk_t curves; + + /** + * Did we receive the curves from the client? + */ + bool curves_received; +}; + +/** + * Find a cipher suite and a server key + */ +static bool select_suite_and_key(private_tls_server_t *this, + tls_cipher_suite_t *suites, int count) +{ + private_key_t *key; + key_type_t type; + + key = lib->credmgr->get_private(lib->credmgr, KEY_ANY, this->server, + this->server_auth); + if (!key) + { + DBG1(DBG_TLS, "no usable TLS server certificate found for '%Y'", + this->server); + return FALSE; + } + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, key->get_type(key)); + if (!this->suite) + { /* no match for this key, try to find another type */ + if (key->get_type(key) == KEY_ECDSA) + { + type = KEY_RSA; + } + else + { + type = KEY_ECDSA; + } + key->destroy(key); + + this->suite = this->crypto->select_cipher_suite(this->crypto, + suites, count, type); + if (!this->suite) + { + DBG1(DBG_TLS, "received cipher suites inacceptable"); + return FALSE; + } + this->server_auth->destroy(this->server_auth); + this->server_auth = auth_cfg_create(); + key = lib->credmgr->get_private(lib->credmgr, type, this->server, + this->server_auth); + if (!key) + { + DBG1(DBG_TLS, "received cipher suites inacceptable"); + return FALSE; + } + } + this->private = key; + return TRUE; +} + +/** + * Process client hello message + */ +static status_t process_client_hello(private_tls_server_t *this, + tls_reader_t *reader) +{ + u_int16_t version, extension; + chunk_t random, session, ciphers, compression, ext = chunk_empty; + tls_reader_t *extensions; + tls_cipher_suite_t *suites; + int count, i; + + this->crypto->append_handshake(this->crypto, + TLS_CLIENT_HELLO, reader->peek(reader)); + + if (!reader->read_uint16(reader, &version) || + !reader->read_data(reader, sizeof(this->client_random), &random) || + !reader->read_data8(reader, &session) || + !reader->read_data16(reader, &ciphers) || + !reader->read_data8(reader, &compression) || + (reader->remaining(reader) && !reader->read_data16(reader, &ext))) + { + DBG1(DBG_TLS, "received invalid ClientHello"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (ext.len) + { + extensions = tls_reader_create(ext); + while (extensions->remaining(extensions)) + { + if (!extensions->read_uint16(extensions, &extension) || + !extensions->read_data16(extensions, &ext)) + { + DBG1(DBG_TLS, "received invalid ClientHello Extensions"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + extensions->destroy(extensions); + return NEED_MORE; + } + DBG1(DBG_TLS, "received TLS '%N' extension", + tls_extension_names, extension); + DBG3(DBG_TLS, "%B", &ext); + switch (extension) + { + case TLS_EXT_SIGNATURE_ALGORITHMS: + this->hashsig = chunk_clone(ext); + break; + case TLS_EXT_ELLIPTIC_CURVES: + this->curves_received = TRUE; + this->curves = chunk_clone(ext); + break; + default: + break; + } + } + extensions->destroy(extensions); + } + + memcpy(this->client_random, random.ptr, sizeof(this->client_random)); + + if (!this->tls->set_version(this->tls, version)) + { + DBG1(DBG_TLS, "negotiated version %N not supported", + tls_version_names, version); + this->alert->add(this->alert, TLS_FATAL, TLS_PROTOCOL_VERSION); + return NEED_MORE; + } + count = ciphers.len / sizeof(u_int16_t); + suites = alloca(count * sizeof(tls_cipher_suite_t)); + DBG2(DBG_TLS, "received %d TLS cipher suites:", count); + for (i = 0; i < count; i++) + { + suites[i] = untoh16(&ciphers.ptr[i * sizeof(u_int16_t)]); + DBG2(DBG_TLS, " %N", tls_cipher_suite_names, suites[i]); + } + + if (!select_suite_and_key(this, suites, count)) + { + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + DBG1(DBG_TLS, "negotiated TLS version %N with suite %N", + tls_version_names, this->tls->get_version(this->tls), + tls_cipher_suite_names, this->suite); + this->client_version = version; + this->state = STATE_HELLO_RECEIVED; + return NEED_MORE; +} + +/** + * Process certificate + */ +static status_t process_certificate(private_tls_server_t *this, + tls_reader_t *reader) +{ + certificate_t *cert; + tls_reader_t *certs; + chunk_t data; + bool first = TRUE; + + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE, reader->peek(reader)); + + if (!reader->read_data24(reader, &data)) + { + DBG1(DBG_TLS, "certificate message header invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + certs = tls_reader_create(data); + while (certs->remaining(certs)) + { + if (!certs->read_data24(certs, &data)) + { + DBG1(DBG_TLS, "certificate message invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + certs->destroy(certs); + return NEED_MORE; + } + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_BLOB_ASN1_DER, data, BUILD_END); + if (cert) + { + if (first) + { + this->peer_auth->add(this->peer_auth, + AUTH_HELPER_SUBJECT_CERT, cert); + DBG1(DBG_TLS, "received TLS peer certificate '%Y'", + cert->get_subject(cert)); + first = FALSE; + } + else + { + DBG1(DBG_TLS, "received TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + this->peer_auth->add(this->peer_auth, AUTH_HELPER_IM_CERT, cert); + } + } + else + { + DBG1(DBG_TLS, "parsing TLS certificate failed, skipped"); + this->alert->add(this->alert, TLS_WARNING, TLS_BAD_CERTIFICATE); + } + } + certs->destroy(certs); + this->state = STATE_CERT_RECEIVED; + return NEED_MORE; +} + +/** + * Process Client Key Exchange, using premaster encryption + */ +static status_t process_key_exchange_encrypted(private_tls_server_t *this, + tls_reader_t *reader) +{ + chunk_t encrypted, decrypted; + char premaster[48]; + rng_t *rng; + + this->crypto->append_handshake(this->crypto, + TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader)); + + if (!reader->read_data16(reader, &encrypted)) + { + DBG1(DBG_TLS, "received invalid Client Key Exchange"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + htoun16(premaster, this->client_version); + /* pre-randomize premaster for failure cases */ + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_TLS, "creating RNG failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + rng->get_bytes(rng, sizeof(premaster) - 2, premaster + 2); + rng->destroy(rng); + + if (this->private && + this->private->decrypt(this->private, + ENCRYPT_RSA_PKCS1, encrypted, &decrypted)) + { + if (decrypted.len == sizeof(premaster) && + untoh16(decrypted.ptr) == this->client_version) + { + memcpy(premaster + 2, decrypted.ptr + 2, sizeof(premaster) - 2); + } + else + { + DBG1(DBG_TLS, "decrypted premaster has invalid length/version"); + } + chunk_clear(&decrypted); + } + else + { + DBG1(DBG_TLS, "decrypting Client Key Exchange failed"); + } + + this->crypto->derive_secrets(this->crypto, chunk_from_thing(premaster), + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + + this->state = STATE_KEY_EXCHANGE_RECEIVED; + return NEED_MORE; +} + +/** + * Process client key exchange, using DHE exchange + */ +static status_t process_key_exchange_dhe(private_tls_server_t *this, + tls_reader_t *reader) +{ + chunk_t premaster, pub; + bool ec; + + this->crypto->append_handshake(this->crypto, + TLS_CLIENT_KEY_EXCHANGE, reader->peek(reader)); + + ec = diffie_hellman_group_is_ec(this->dh->get_dh_group(this->dh)); + if ((ec && !reader->read_data8(reader, &pub)) || + (!ec && (!reader->read_data16(reader, &pub) || pub.len == 0))) + { + DBG1(DBG_TLS, "received invalid Client Key Exchange"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + + if (ec) + { + if (pub.ptr[0] != TLS_ANSI_UNCOMPRESSED) + { + DBG1(DBG_TLS, "DH point format '%N' not supported", + tls_ansi_point_format_names, pub.ptr[0]); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + pub = chunk_skip(pub, 1); + } + this->dh->set_other_public_value(this->dh, pub); + if (this->dh->get_shared_secret(this->dh, &premaster) != SUCCESS) + { + DBG1(DBG_TLS, "calculating premaster from DH failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + + this->crypto->derive_secrets(this->crypto, premaster, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + chunk_clear(&premaster); + + this->state = STATE_KEY_EXCHANGE_RECEIVED; + return NEED_MORE; +} + +/** + * Process Client Key Exchange + */ +static status_t process_key_exchange(private_tls_server_t *this, + tls_reader_t *reader) +{ + if (this->dh) + { + return process_key_exchange_dhe(this, reader); + } + return process_key_exchange_encrypted(this, reader); +} + +/** + * Process Certificate verify + */ +static status_t process_cert_verify(private_tls_server_t *this, + tls_reader_t *reader) +{ + bool verified = FALSE; + enumerator_t *enumerator; + public_key_t *public; + auth_cfg_t *auth; + tls_reader_t *sig; + + enumerator = lib->credmgr->create_public_enumerator(lib->credmgr, + KEY_ANY, this->peer, this->peer_auth); + while (enumerator->enumerate(enumerator, &public, &auth)) + { + sig = tls_reader_create(reader->peek(reader)); + verified = this->crypto->verify_handshake(this->crypto, public, sig); + sig->destroy(sig); + if (verified) + { + break; + } + DBG1(DBG_TLS, "signature verification failed, trying another key"); + } + enumerator->destroy(enumerator); + + if (!verified) + { + DBG1(DBG_TLS, "no trusted certificate found for '%Y' to verify TLS peer", + this->peer); + this->alert->add(this->alert, TLS_FATAL, TLS_CERTIFICATE_UNKNOWN); + return NEED_MORE; + } + + this->crypto->append_handshake(this->crypto, + TLS_CERTIFICATE_VERIFY, reader->peek(reader)); + this->state = STATE_CERT_VERIFY_RECEIVED; + return NEED_MORE; +} + +/** + * Process finished message + */ +static status_t process_finished(private_tls_server_t *this, + tls_reader_t *reader) +{ + chunk_t received; + char buf[12]; + + if (!reader->read_data(reader, sizeof(buf), &received)) + { + DBG1(DBG_TLS, "received client finished too short"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECODE_ERROR); + return NEED_MORE; + } + if (!this->crypto->calculate_finished(this->crypto, "client finished", buf)) + { + DBG1(DBG_TLS, "calculating client finished failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + if (!chunk_equals(received, chunk_from_thing(buf))) + { + DBG1(DBG_TLS, "received client finished invalid"); + this->alert->add(this->alert, TLS_FATAL, TLS_DECRYPT_ERROR); + return NEED_MORE; + } + + this->crypto->append_handshake(this->crypto, TLS_FINISHED, received); + this->state = STATE_FINISHED_RECEIVED; + return NEED_MORE; +} + +METHOD(tls_handshake_t, process, status_t, + private_tls_server_t *this, tls_handshake_type_t type, tls_reader_t *reader) +{ + tls_handshake_type_t expected; + + switch (this->state) + { + case STATE_INIT: + if (type == TLS_CLIENT_HELLO) + { + return process_client_hello(this, reader); + } + expected = TLS_CLIENT_HELLO; + break; + case STATE_HELLO_DONE: + if (type == TLS_CERTIFICATE) + { + return process_certificate(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE; + break; + } + /* otherwise fall through to next state */ + case STATE_CERT_RECEIVED: + if (type == TLS_CLIENT_KEY_EXCHANGE) + { + return process_key_exchange(this, reader); + } + expected = TLS_CLIENT_KEY_EXCHANGE; + break; + case STATE_KEY_EXCHANGE_RECEIVED: + if (type == TLS_CERTIFICATE_VERIFY) + { + return process_cert_verify(this, reader); + } + if (this->peer) + { + expected = TLS_CERTIFICATE_VERIFY; + break; + } + else + { + return INVALID_STATE; + } + case STATE_CIPHERSPEC_CHANGED_IN: + if (type == TLS_FINISHED) + { + return process_finished(this, reader); + } + expected = TLS_FINISHED; + break; + default: + DBG1(DBG_TLS, "TLS %N not expected in current state", + tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; + } + DBG1(DBG_TLS, "TLS %N expected, but received %N", + tls_handshake_type_names, expected, tls_handshake_type_names, type); + this->alert->add(this->alert, TLS_FATAL, TLS_UNEXPECTED_MESSAGE); + return NEED_MORE; +} + +/** + * Send ServerHello message + */ +static status_t send_server_hello(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + tls_version_t version; + rng_t *rng; + + htoun32(&this->server_random, time(NULL)); + rng = lib->crypto->create_rng(lib->crypto, RNG_WEAK); + if (!rng) + { + DBG1(DBG_TLS, "no suitable RNG found to generate server random"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return FAILED; + } + rng->get_bytes(rng, sizeof(this->server_random) - 4, this->server_random + 4); + rng->destroy(rng); + + /* TLS version */ + version = this->tls->get_version(this->tls); + writer->write_uint16(writer, version); + writer->write_data(writer, chunk_from_thing(this->server_random)); + + /* session identifier => none, we don't support session resumption */ + writer->write_data8(writer, chunk_empty); + + /* add selected TLS cipher suite */ + writer->write_uint16(writer, this->suite); + + /* NULL compression only */ + writer->write_uint8(writer, 0); + + *type = TLS_SERVER_HELLO; + this->state = STATE_HELLO_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Certificate + */ +static status_t send_certificate(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + enumerator_t *enumerator; + certificate_t *cert; + auth_rule_t rule; + tls_writer_t *certs; + chunk_t data; + + /* generate certificate payload */ + certs = tls_writer_create(256); + cert = this->server_auth->get(this->server_auth, AUTH_RULE_SUBJECT_CERT); + if (cert) + { + if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) + { + DBG1(DBG_TLS, "sending TLS server certificate '%Y'", + cert->get_subject(cert)); + certs->write_data24(certs, data); + free(data.ptr); + } + } + enumerator = this->server_auth->create_enumerator(this->server_auth); + while (enumerator->enumerate(enumerator, &rule, &cert)) + { + if (rule == AUTH_RULE_IM_CERT) + { + if (cert->get_encoding(cert, CERT_ASN1_DER, &data)) + { + DBG1(DBG_TLS, "sending TLS intermediate certificate '%Y'", + cert->get_subject(cert)); + certs->write_data24(certs, data); + free(data.ptr); + } + } + } + enumerator->destroy(enumerator); + + writer->write_data24(writer, certs->get_buf(certs)); + certs->destroy(certs); + + *type = TLS_CERTIFICATE; + this->state = STATE_CERT_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Certificate Request + */ +static status_t send_certificate_request(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + tls_writer_t *authorities, *supported; + enumerator_t *enumerator; + certificate_t *cert; + x509_t *x509; + identification_t *id; + + supported = tls_writer_create(4); + /* we propose both RSA and ECDSA */ + supported->write_uint8(supported, TLS_RSA_SIGN); + supported->write_uint8(supported, TLS_ECDSA_SIGN); + writer->write_data8(writer, supported->get_buf(supported)); + supported->destroy(supported); + if (this->tls->get_version(this->tls) >= TLS_1_2) + { + this->crypto->get_signature_algorithms(this->crypto, writer); + } + + authorities = tls_writer_create(64); + enumerator = lib->credmgr->create_cert_enumerator(lib->credmgr, + CERT_X509, KEY_RSA, NULL, TRUE); + while (enumerator->enumerate(enumerator, &cert)) + { + x509 = (x509_t*)cert; + if (x509->get_flags(x509) & X509_CA) + { + id = cert->get_subject(cert); + DBG1(DBG_TLS, "sending TLS cert request for '%Y'", id); + authorities->write_data16(authorities, id->get_encoding(id)); + } + } + enumerator->destroy(enumerator); + writer->write_data16(writer, authorities->get_buf(authorities)); + authorities->destroy(authorities); + + *type = TLS_CERTIFICATE_REQUEST; + this->state = STATE_CERTREQ_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Get the TLS curve of a given EC DH group + */ +static tls_named_curve_t ec_group_to_curve(private_tls_server_t *this, + diffie_hellman_group_t group) +{ + diffie_hellman_group_t current; + tls_named_curve_t curve; + enumerator_t *enumerator; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, ¤t, &curve)) + { + if (current == group) + { + enumerator->destroy(enumerator); + return curve; + } + } + enumerator->destroy(enumerator); + return 0; +} + +/** + * Check if the peer supports a given TLS curve + */ +bool peer_supports_curve(private_tls_server_t *this, tls_named_curve_t curve) +{ + tls_reader_t *reader; + u_int16_t current; + + if (!this->curves_received) + { /* none received, assume yes */ + return TRUE; + } + reader = tls_reader_create(this->curves); + while (reader->remaining(reader) && reader->read_uint16(reader, ¤t)) + { + if (current == curve) + { + reader->destroy(reader); + return TRUE; + } + } + reader->destroy(reader); + return FALSE; +} + +/** + * Try to find a curve supported by both, client and server + */ +static bool find_supported_curve(private_tls_server_t *this, + tls_named_curve_t *curve) +{ + tls_named_curve_t current; + enumerator_t *enumerator; + + enumerator = this->crypto->create_ec_enumerator(this->crypto); + while (enumerator->enumerate(enumerator, NULL, ¤t)) + { + if (peer_supports_curve(this, current)) + { + *curve = current; + enumerator->destroy(enumerator); + return TRUE; + } + } + enumerator->destroy(enumerator); + return FALSE; +} + +/** + * Send Server key Exchange + */ +static status_t send_server_key_exchange(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer, + diffie_hellman_group_t group) +{ + diffie_hellman_params_t *params = NULL; + tls_named_curve_t curve; + chunk_t chunk; + + if (diffie_hellman_group_is_ec(group)) + { + curve = ec_group_to_curve(this, group); + if (!curve || (!peer_supports_curve(this, curve) && + !find_supported_curve(this, &curve))) + { + DBG1(DBG_TLS, "no EC group supported by client and server"); + this->alert->add(this->alert, TLS_FATAL, TLS_HANDSHAKE_FAILURE); + return NEED_MORE; + } + DBG2(DBG_TLS, "selected ECDH group %N", tls_named_curve_names, curve); + writer->write_uint8(writer, TLS_ECC_NAMED_CURVE); + writer->write_uint16(writer, curve); + } + else + { + params = diffie_hellman_get_params(group); + if (!params) + { + DBG1(DBG_TLS, "no parameters found for DH group %N", + diffie_hellman_group_names, group); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + DBG2(DBG_TLS, "selected DH group %N", diffie_hellman_group_names, group); + writer->write_data16(writer, params->prime); + writer->write_data16(writer, params->generator); + } + this->dh = lib->crypto->create_dh(lib->crypto, group); + if (!this->dh) + { + DBG1(DBG_TLS, "DH group %N not supported", + diffie_hellman_group_names, group); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return NEED_MORE; + } + this->dh->get_my_public_value(this->dh, &chunk); + if (params) + { + writer->write_data16(writer, chunk); + } + else + { /* ECP uses 8bit length header only, but a point format */ + writer->write_uint8(writer, chunk.len + 1); + writer->write_uint8(writer, TLS_ANSI_UNCOMPRESSED); + writer->write_data(writer, chunk); + } + free(chunk.ptr); + + chunk = chunk_cat("ccc", chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random), writer->get_buf(writer)); + if (!this->private || !this->crypto->sign(this->crypto, this->private, + writer, chunk, this->hashsig)) + { + DBG1(DBG_TLS, "signing DH parameters failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + free(chunk.ptr); + return NEED_MORE; + } + free(chunk.ptr); + *type = TLS_SERVER_KEY_EXCHANGE; + this->state = STATE_KEY_EXCHANGE_SENT; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Hello Done + */ +static status_t send_hello_done(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + *type = TLS_SERVER_HELLO_DONE; + this->state = STATE_HELLO_DONE; + this->crypto->append_handshake(this->crypto, *type, writer->get_buf(writer)); + return NEED_MORE; +} + +/** + * Send Finished + */ +static status_t send_finished(private_tls_server_t *this, + tls_handshake_type_t *type, tls_writer_t *writer) +{ + char buf[12]; + + if (!this->crypto->calculate_finished(this->crypto, "server finished", buf)) + { + DBG1(DBG_TLS, "calculating server finished data failed"); + this->alert->add(this->alert, TLS_FATAL, TLS_INTERNAL_ERROR); + return FAILED; + } + + writer->write_data(writer, chunk_from_thing(buf)); + + *type = TLS_FINISHED; + this->state = STATE_FINISHED_SENT; + this->crypto->derive_eap_msk(this->crypto, + chunk_from_thing(this->client_random), + chunk_from_thing(this->server_random)); + return NEED_MORE; +} + +METHOD(tls_handshake_t, build, status_t, + private_tls_server_t *this, tls_handshake_type_t *type, tls_writer_t *writer) +{ + diffie_hellman_group_t group; + + switch (this->state) + { + case STATE_HELLO_RECEIVED: + return send_server_hello(this, type, writer); + case STATE_HELLO_SENT: + return send_certificate(this, type, writer); + case STATE_CERT_SENT: + group = this->crypto->get_dh_group(this->crypto); + if (group) + { + return send_server_key_exchange(this, type, writer, group); + } + /* otherwise fall through to next state */ + case STATE_KEY_EXCHANGE_SENT: + if (this->peer) + { + return send_certificate_request(this, type, writer); + } + /* otherwise fall through to next state */ + case STATE_CERTREQ_SENT: + return send_hello_done(this, type, writer); + case STATE_CIPHERSPEC_CHANGED_OUT: + return send_finished(this, type, writer); + case STATE_FINISHED_SENT: + return INVALID_STATE; + default: + return INVALID_STATE; + } +} + +METHOD(tls_handshake_t, cipherspec_changed, bool, + private_tls_server_t *this) +{ + if (this->state == STATE_FINISHED_RECEIVED) + { + this->crypto->change_cipher(this->crypto, FALSE); + this->state = STATE_CIPHERSPEC_CHANGED_OUT; + return TRUE; + } + return FALSE; +} + +METHOD(tls_handshake_t, change_cipherspec, bool, + private_tls_server_t *this) +{ + if ((this->peer && this->state == STATE_CERT_VERIFY_RECEIVED) || + (!this->peer && this->state == STATE_KEY_EXCHANGE_RECEIVED)) + { + this->crypto->change_cipher(this->crypto, TRUE); + this->state = STATE_CIPHERSPEC_CHANGED_IN; + return TRUE; + } + return FALSE; +} + +METHOD(tls_handshake_t, finished, bool, + private_tls_server_t *this) +{ + return this->state == STATE_FINISHED_SENT; +} + +METHOD(tls_handshake_t, destroy, void, + private_tls_server_t *this) +{ + DESTROY_IF(this->private); + DESTROY_IF(this->dh); + this->peer_auth->destroy(this->peer_auth); + this->server_auth->destroy(this->server_auth); + free(this->hashsig.ptr); + free(this->curves.ptr); + free(this); +} + +/** + * See header + */ +tls_server_t *tls_server_create(tls_t *tls, + tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *server, identification_t *peer) +{ + private_tls_server_t *this; + + INIT(this, + .public = { + .handshake = { + .process = _process, + .build = _build, + .cipherspec_changed = _cipherspec_changed, + .change_cipherspec = _change_cipherspec, + .finished = _finished, + .destroy = _destroy, + }, + }, + .tls = tls, + .crypto = crypto, + .alert = alert, + .server = server, + .peer = peer, + .state = STATE_INIT, + .peer_auth = auth_cfg_create(), + .server_auth = auth_cfg_create(), + ); + + return &this->public; +} diff --git a/src/libtls/tls_server.h b/src/libtls/tls_server.h new file mode 100644 index 000000000..6289dc8eb --- /dev/null +++ b/src/libtls/tls_server.h @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_server tls_server + * @{ @ingroup libtls + */ + +#ifndef TLS_SERVER_H_ +#define TLS_SERVER_H_ + +typedef struct tls_server_t tls_server_t; + +#include "tls_handshake.h" +#include "tls_crypto.h" + +#include + +/** + * TLS handshake protocol handler as peer. + */ +struct tls_server_t { + + /** + * Implements the TLS handshake protocol handler. + */ + tls_handshake_t handshake; +}; + +/** + * Create a tls_server instance. + * + * @param tls TLS stack + * @param crypto TLS crypto helper + * @param alert TLS alert handler + * @param server server identity + * @param peer peer identity + */ +tls_server_t *tls_server_create(tls_t *tls, + tls_crypto_t *crypto, tls_alert_t *alert, + identification_t *server, identification_t *peer); + +#endif /** TLS_SERVER_H_ @}*/ diff --git a/src/libtls/tls_socket.c b/src/libtls/tls_socket.c new file mode 100644 index 000000000..e0c440a4c --- /dev/null +++ b/src/libtls/tls_socket.c @@ -0,0 +1,219 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_socket.h" + +#include + +#include + +typedef struct private_tls_socket_t private_tls_socket_t; +typedef struct private_tls_application_t private_tls_application_t; + +struct private_tls_application_t { + + /** + * Implements tls_application layer. + */ + tls_application_t application; + + /** + * Chunk of data to send + */ + chunk_t out; + + /** + * Chunk of data received + */ + chunk_t in; +}; + +/** + * Private data of an tls_socket_t object. + */ +struct private_tls_socket_t { + + /** + * Public tls_socket_t interface. + */ + tls_socket_t public; + + /** + * TLS application implementation + */ + private_tls_application_t app; + + /** + * TLS stack + */ + tls_t *tls; + + /** + * Underlying OS socket + */ + int fd; +}; + +METHOD(tls_application_t, process, status_t, + private_tls_application_t *this, tls_reader_t *reader) +{ + chunk_t data; + + if (!reader->read_data(reader, reader->remaining(reader), &data)) + { + return FAILED; + } + this->in = chunk_cat("mc", this->in, data); + return NEED_MORE; +} + +METHOD(tls_application_t, build, status_t, + private_tls_application_t *this, tls_writer_t *writer) +{ + if (this->out.len) + { + writer->write_data(writer, this->out); + this->out = chunk_empty; + return NEED_MORE; + } + return INVALID_STATE; +} + +/** + * TLS data exchange loop + */ +static bool exchange(private_tls_socket_t *this, bool wr) +{ + char buf[1024]; + ssize_t len; + int round = 0; + + for (round = 0; TRUE; round++) + { + while (TRUE) + { + len = sizeof(buf); + switch (this->tls->build(this->tls, buf, &len, NULL)) + { + case NEED_MORE: + case ALREADY_DONE: + len = write(this->fd, buf, len); + if (len == -1) + { + return FALSE; + } + continue; + case INVALID_STATE: + break; + default: + return FALSE; + } + break; + } + if (wr) + { + if (this->app.out.len == 0) + { /* all data written */ + return TRUE; + } + } + else + { + if (this->app.in.len) + { /* some data received */ + return TRUE; + } + if (round > 0) + { /* did some handshaking, return empty chunk to not block */ + return TRUE; + } + } + len = read(this->fd, buf, sizeof(buf)); + if (len <= 0) + { + return FALSE; + } + if (this->tls->process(this->tls, buf, len) != NEED_MORE) + { + return FALSE; + } + } +} + +METHOD(tls_socket_t, read_, bool, + private_tls_socket_t *this, chunk_t *buf) +{ + if (exchange(this, FALSE)) + { + *buf = this->app.in; + this->app.in = chunk_empty; + return TRUE; + } + return FALSE; +} + +METHOD(tls_socket_t, write_, bool, + private_tls_socket_t *this, chunk_t buf) +{ + this->app.out = buf; + if (exchange(this, TRUE)) + { + return TRUE; + } + return FALSE; +} + +METHOD(tls_socket_t, destroy, void, + private_tls_socket_t *this) +{ + this->tls->destroy(this->tls); + free(this->app.in.ptr); + free(this); +} + +/** + * See header + */ +tls_socket_t *tls_socket_create(bool is_server, identification_t *server, + identification_t *peer, int fd) +{ + private_tls_socket_t *this; + + INIT(this, + .public = { + .read = _read_, + .write = _write_, + .destroy = _destroy, + }, + .app = { + .application = { + .build = _build, + .process = _process, + .destroy = (void*)nop, + }, + }, + .fd = fd, + ); + + this->tls = tls_create(is_server, server, peer, TLS_PURPOSE_GENERIC, + &this->app.application); + if (!this->tls) + { + free(this); + return NULL; + } + + return &this->public; +} diff --git a/src/libtls/tls_socket.h b/src/libtls/tls_socket.h new file mode 100644 index 000000000..ac714a385 --- /dev/null +++ b/src/libtls/tls_socket.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_socket tls_socket + * @{ @ingroup libtls + */ + +#ifndef TLS_SOCKET_H_ +#define TLS_SOCKET_H_ + +#include "tls.h" + +typedef struct tls_socket_t tls_socket_t; + +/** + * TLS secured socket. + * + * Wraps a blocking (socket) file descriptor for a reliable transport into a + * TLS secured socket. TLS negotiation happens on demand, certificates and + * private keys are fetched from any registered credential set. + */ +struct tls_socket_t { + + /** + * Read data from secured socket, return allocated chunk. + * + * This call is blocking, you may use select() on the underlying socket to + * wait for data. If the there was non-application data available, the + * read function can return an empty chunk. + * + * @param data pointer to allocate received data + * @return TRUE if data received successfully + */ + bool (*read)(tls_socket_t *this, chunk_t *data); + + /** + * Write a chunk of data over the secured socket. + * + * @param data data to send + * @return TRUE if data sent successfully + */ + bool (*write)(tls_socket_t *this, chunk_t data); + + /** + * Destroy a tls_socket_t. + */ + void (*destroy)(tls_socket_t *this); +}; + +/** + * Create a tls_socket instance. + * + * @param is_server TRUE to act as TLS server + * @param server server identity + * @param peer client identity, NULL for no client authentication + * @param fd socket to read/write from + * @return TLS socket wrapper + */ +tls_socket_t *tls_socket_create(bool is_server, identification_t *server, + identification_t *peer, int fd); + +#endif /** TLS_SOCKET_H_ @}*/ diff --git a/src/libtls/tls_writer.c b/src/libtls/tls_writer.c new file mode 100644 index 000000000..235dc2cdf --- /dev/null +++ b/src/libtls/tls_writer.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +#include "tls_writer.h" + +typedef struct private_tls_writer_t private_tls_writer_t; + +/** + * Private data of an tls_writer_t object. + */ +struct private_tls_writer_t { + + /** + * Public tls_writer_t interface. + */ + tls_writer_t public; + + /** + * Allocated buffer + */ + chunk_t buf; + + /** + * Used bytes in buffer + */ + size_t used; + + /** + * Number of bytes to increase buffer size + */ + size_t increase; +}; + +/** + * Increase buffer size + */ +static void increase(private_tls_writer_t *this) +{ + this->buf.len += this->increase; + this->buf.ptr = realloc(this->buf.ptr, this->buf.len); +} + +METHOD(tls_writer_t, write_uint8, void, + private_tls_writer_t *this, u_int8_t value) +{ + if (this->used + 1 > this->buf.len) + { + increase(this); + } + this->buf.ptr[this->used] = value; + this->used += 1; +} + +METHOD(tls_writer_t, write_uint16, void, + private_tls_writer_t *this, u_int16_t value) +{ + if (this->used + 2 > this->buf.len) + { + increase(this); + } + htoun16(this->buf.ptr + this->used, value); + this->used += 2; +} + +METHOD(tls_writer_t, write_uint24, void, + private_tls_writer_t *this, u_int32_t value) +{ + if (this->used + 3 > this->buf.len) + { + increase(this); + } + value = htonl(value); + memcpy(this->buf.ptr + this->used, ((char*)&value) + 1, 3); + this->used += 3; +} + +METHOD(tls_writer_t, write_uint32, void, + private_tls_writer_t *this, u_int32_t value) +{ + if (this->used + 4 > this->buf.len) + { + increase(this); + } + htoun32(this->buf.ptr + this->used, value); + this->used += 4; +} + +METHOD(tls_writer_t, write_data, void, + private_tls_writer_t *this, chunk_t value) +{ + while (this->used + value.len > this->buf.len) + { + increase(this); + } + memcpy(this->buf.ptr + this->used, value.ptr, value.len); + this->used += value.len; +} + +METHOD(tls_writer_t, write_data8, void, + private_tls_writer_t *this, chunk_t value) +{ + write_uint8(this, value.len); + write_data(this, value); +} + +METHOD(tls_writer_t, write_data16, void, + private_tls_writer_t *this, chunk_t value) +{ + write_uint16(this, value.len); + write_data(this, value); +} + +METHOD(tls_writer_t, write_data24, void, + private_tls_writer_t *this, chunk_t value) +{ + write_uint24(this, value.len); + write_data(this, value); +} + +METHOD(tls_writer_t, write_data32, void, + private_tls_writer_t *this, chunk_t value) +{ + write_uint32(this, value.len); + write_data(this, value); +} + +METHOD(tls_writer_t, wrap8, void, + private_tls_writer_t *this) +{ + if (this->used + 1 > this->buf.len) + { + increase(this); + } + memmove(this->buf.ptr + 1, this->buf.ptr, 1); + this->buf.ptr[0] = this->used; + this->used += 1; +} + +METHOD(tls_writer_t, wrap16, void, + private_tls_writer_t *this) +{ + if (this->used + 2 > this->buf.len) + { + increase(this); + } + memmove(this->buf.ptr + 2, this->buf.ptr, 2); + htoun16(this->buf.ptr, this->used); + this->used += 2; +} + +METHOD(tls_writer_t, wrap24, void, + private_tls_writer_t *this) +{ + u_int32_t len; + + if (this->used + 3 > this->buf.len) + { + increase(this); + } + memmove(this->buf.ptr + 3, this->buf.ptr, 3); + + len = htonl(this->used); + memcpy(this->buf.ptr, ((char*)&len) + 1, 3); + this->used += 3; +} + +METHOD(tls_writer_t, wrap32, void, + private_tls_writer_t *this) +{ + if (this->used + 4 > this->buf.len) + { + increase(this); + } + memmove(this->buf.ptr + 4, this->buf.ptr, 4); + htoun32(this->buf.ptr, this->used); + this->used += 4; +} + +METHOD(tls_writer_t, get_buf, chunk_t, + private_tls_writer_t *this) +{ + return chunk_create(this->buf.ptr, this->used); +} + +METHOD(tls_writer_t, destroy, void, + private_tls_writer_t *this) +{ + free(this->buf.ptr); + free(this); +} + +/** + * See header + */ +tls_writer_t *tls_writer_create(u_int32_t bufsize) +{ + private_tls_writer_t *this; + + INIT(this, + .public = { + .write_uint8 = _write_uint8, + .write_uint16 = _write_uint16, + .write_uint24 = _write_uint24, + .write_uint32 = _write_uint32, + .write_data = _write_data, + .write_data8 = _write_data8, + .write_data16 = _write_data16, + .write_data24 = _write_data24, + .write_data32 = _write_data32, + .wrap8 = _wrap8, + .wrap16 = _wrap16, + .wrap24 = _wrap24, + .wrap32 = _wrap32, + .get_buf = _get_buf, + .destroy = _destroy, + }, + .increase = bufsize ?: 32, + ); + if (bufsize) + { + this->buf = chunk_alloc(bufsize); + } + + return &this->public; +} diff --git a/src/libtls/tls_writer.h b/src/libtls/tls_writer.h new file mode 100644 index 000000000..d3f09d5da --- /dev/null +++ b/src/libtls/tls_writer.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2010 Martin Willi + * Copyright (C) 2010 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program 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. + */ + +/** + * @defgroup tls_writer tls_writer + * @{ @ingroup libtls + */ + +#ifndef TLS_WRITER_H_ +#define TLS_WRITER_H_ + +typedef struct tls_writer_t tls_writer_t; + +#include + +/** + * TLS record generator. + */ +struct tls_writer_t { + + /** + * Append a 8-bit integer to the buffer. + * + * @param value value to append + */ + void (*write_uint8)(tls_writer_t *this, u_int8_t value); + + /** + * Append a 16-bit integer to the buffer. + * + * @param value value to append + */ + void (*write_uint16)(tls_writer_t *this, u_int16_t value); + + /** + * Append a 24-bit integer to the buffer. + * + * @param value value to append + */ + void (*write_uint24)(tls_writer_t *this, u_int32_t value); + + /** + * Append a 32-bit integer to the buffer. + * + * @param value value to append + */ + void (*write_uint32)(tls_writer_t *this, u_int32_t value); + + /** + * Append a chunk of data without a length header. + * + * @param value value to append + */ + void (*write_data)(tls_writer_t *this, chunk_t value); + + /** + * Append a chunk of data with a 8-bit length header. + * + * @param value value to append + */ + void (*write_data8)(tls_writer_t *this, chunk_t value); + + /** + * Append a chunk of data with a 16-bit length header. + * + * @param value value to append + */ + void (*write_data16)(tls_writer_t *this, chunk_t value); + + /** + * Append a chunk of data with a 24-bit length header. + * + * @param value value to append + */ + void (*write_data24)(tls_writer_t *this, chunk_t value); + + /** + * Append a chunk of data with a 32-bit length header. + * + * @param value value to append + */ + void (*write_data32)(tls_writer_t *this, chunk_t value); + + /** + * Prepend a 8-bit length header to existing data. + */ + void (*wrap8)(tls_writer_t *this); + + /** + * Prepend a 16-bit length header to existing data. + */ + void (*wrap16)(tls_writer_t *this); + + /** + * Prepend a 24-bit length header to existing data. + */ + void (*wrap24)(tls_writer_t *this); + + /** + * Prepend a 32-bit length header to existing data. + */ + void (*wrap32)(tls_writer_t *this); + + /** + * Get the encoded data buffer. + * + * @return chunk to internal buffer + */ + chunk_t (*get_buf)(tls_writer_t *this); + + /** + * Destroy a tls_writer_t. + */ + void (*destroy)(tls_writer_t *this); +}; + +/** + * Create a tls_writer instance. + * + * @param bufsize initially allocated buffer size + */ +tls_writer_t *tls_writer_create(u_int32_t bufsize); + +#endif /** TLS_WRITER_H_ @}*/ -- cgit v1.2.3