summaryrefslogtreecommitdiff
path: root/src/libtls
diff options
context:
space:
mode:
Diffstat (limited to 'src/libtls')
-rw-r--r--src/libtls/Makefile.am18
-rw-r--r--src/libtls/Makefile.in559
-rw-r--r--src/libtls/tls.c481
-rw-r--r--src/libtls/tls.h236
-rw-r--r--src/libtls/tls_alert.c228
-rw-r--r--src/libtls/tls_alert.h126
-rw-r--r--src/libtls/tls_application.h63
-rw-r--r--src/libtls/tls_compression.c72
-rw-r--r--src/libtls/tls_compression.h80
-rw-r--r--src/libtls/tls_crypto.c1674
-rw-r--r--src/libtls/tls_crypto.h554
-rw-r--r--src/libtls/tls_eap.c379
-rw-r--r--src/libtls/tls_eap.h81
-rw-r--r--src/libtls/tls_fragmentation.c471
-rw-r--r--src/libtls/tls_fragmentation.h88
-rw-r--r--src/libtls/tls_handshake.h90
-rw-r--r--src/libtls/tls_peer.c1099
-rw-r--r--src/libtls/tls_peer.h54
-rw-r--r--src/libtls/tls_prf.c190
-rw-r--r--src/libtls/tls_prf.h72
-rw-r--r--src/libtls/tls_protection.c333
-rw-r--r--src/libtls/tls_protection.h98
-rw-r--r--src/libtls/tls_reader.c200
-rw-r--r--src/libtls/tls_reader.h131
-rw-r--r--src/libtls/tls_server.c1032
-rw-r--r--src/libtls/tls_server.h55
-rw-r--r--src/libtls/tls_socket.c219
-rw-r--r--src/libtls/tls_socket.h75
-rw-r--r--src/libtls/tls_writer.c237
-rw-r--r--src/libtls/tls_writer.h136
30 files changed, 9131 insertions, 0 deletions
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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+#include <utils/linked_list.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+
+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(&current, 0, sizeof(current));
+ for (i = 0; i < *count; i++)
+ {
+ enumerator = create_enumerator(lib->crypto);
+ while (enumerator->enumerate(enumerator, ((char*)&current) + 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+#include <credentials/keys/private_key.h>
+
+/**
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+#include <library.h>
+
+/** 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <eap/eap.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+#include <credentials/certificates/x509.h>
+
+#include <time.h>
+
+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, &current, &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, &current))
+ {
+ 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+/**
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <crypto/prfs/prf.h>
+
+/**
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+#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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <debug.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+/**
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <time.h>
+
+#include <debug.h>
+#include <credentials/certificates/x509.h>
+
+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, &current, &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, &current))
+ {
+ 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, &current))
+ {
+ 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+/**
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <unistd.h>
+
+#include <debug.h>
+
+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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <http://www.fsf.org/copyleft/gpl.txt>.
+ *
+ * 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 <library.h>
+
+/**
+ * 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_ @}*/