diff options
Diffstat (limited to 'src/lib-dcrypt')
-rw-r--r-- | src/lib-dcrypt/Makefile.am | 75 | ||||
-rw-r--r-- | src/lib-dcrypt/Makefile.in | 1142 | ||||
-rw-r--r-- | src/lib-dcrypt/dcrypt-iostream.h | 16 | ||||
-rw-r--r-- | src/lib-dcrypt/dcrypt-openssl.c | 3807 | ||||
-rw-r--r-- | src/lib-dcrypt/dcrypt-private.h | 211 | ||||
-rw-r--r-- | src/lib-dcrypt/dcrypt.c | 660 | ||||
-rw-r--r-- | src/lib-dcrypt/dcrypt.h | 409 | ||||
-rw-r--r-- | src/lib-dcrypt/istream-decrypt.c | 1146 | ||||
-rw-r--r-- | src/lib-dcrypt/istream-decrypt.h | 48 | ||||
-rw-r--r-- | src/lib-dcrypt/ostream-encrypt.c | 800 | ||||
-rw-r--r-- | src/lib-dcrypt/ostream-encrypt.h | 30 | ||||
-rw-r--r-- | src/lib-dcrypt/sample-v1.asc | 48 | ||||
-rw-r--r-- | src/lib-dcrypt/sample-v1_short.asc | 4 | ||||
-rw-r--r-- | src/lib-dcrypt/sample-v2.asc | 287 | ||||
-rw-r--r-- | src/lib-dcrypt/test-crypto.c | 1314 | ||||
-rw-r--r-- | src/lib-dcrypt/test-stream.c | 639 |
16 files changed, 10636 insertions, 0 deletions
diff --git a/src/lib-dcrypt/Makefile.am b/src/lib-dcrypt/Makefile.am new file mode 100644 index 0000000..e9e5116 --- /dev/null +++ b/src/lib-dcrypt/Makefile.am @@ -0,0 +1,75 @@ +noinst_LTLIBRARIES = libdcrypt.la +pkglib_LTLIBRARIES = + +NOPLUGIN_LDFLAGS= + +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-ssl-iostream \ + -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" + +libdcrypt_la_SOURCES = \ + dcrypt.c \ + istream-decrypt.c \ + ostream-encrypt.c + +libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ + -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" + +if BUILD_DCRYPT_OPENSSL +pkglib_LTLIBRARIES += libdcrypt_openssl.la +libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c +libdcrypt_openssl_la_LDFLAGS = -module -avoid-version ../lib-ssl-iostream/libssl_iostream_openssl.la +libdcrypt_openssl_la_LIBADD = $(SSL_LIBS) +libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libssl_iostream_openssl.la +libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ + $(SSL_CFLAGS) +endif + +headers = \ + dcrypt.h \ + dcrypt-iostream.h \ + dcrypt-private.h \ + ostream-encrypt.h \ + istream-decrypt.h + +pkginc_libdir=$(pkgincludedir) +pkginc_lib_HEADERS = $(headers) + +EXTRA_DIST = \ + sample-v1.asc \ + sample-v1_short.asc \ + sample-v2.asc + +test_programs = test-crypto test-stream +noinst_PROGRAMS = $(test_programs) + +check-local: + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +LIBDOVECOT_TEST_DEPS = \ + ../lib-ssl-iostream/libssl_iostream.la \ + ../lib-test/libtest.la \ + ../lib/liblib.la +LIBDOVECOT_TEST = \ + $(LIBDOVECOT_TEST_DEPS) \ + $(MODULE_LIBS) + +test_crypto_LDADD = $(LIBDOVECOT_TEST) +test_crypto_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) +if HAVE_WHOLE_ARCHIVE +test_crypto_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) +endif +test_crypto_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" +test_crypto_SOURCES = $(libdcrypt_la_SOURCES) test-crypto.c + +test_stream_LDADD = $(LIBDOVECOT_TEST) +test_stream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) +if HAVE_WHOLE_ARCHIVE +test_stream_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) +endif +test_stream_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" +test_stream_SOURCES = $(libdcrypt_la_SOURCES) test-stream.c diff --git a/src/lib-dcrypt/Makefile.in b/src/lib-dcrypt/Makefile.in new file mode 100644 index 0000000..1c8effe --- /dev/null +++ b/src/lib-dcrypt/Makefile.in @@ -0,0 +1,1142 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 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@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +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@ +@BUILD_DCRYPT_OPENSSL_TRUE@am__append_1 = libdcrypt_openssl.la +noinst_PROGRAMS = $(am__EXEEXT_1) +subdir = src/lib-dcrypt +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ac_checktype2.m4 \ + $(top_srcdir)/m4/ac_typeof.m4 $(top_srcdir)/m4/arc4random.m4 \ + $(top_srcdir)/m4/blockdev.m4 $(top_srcdir)/m4/c99_vsnprintf.m4 \ + $(top_srcdir)/m4/clock_gettime.m4 $(top_srcdir)/m4/crypt.m4 \ + $(top_srcdir)/m4/crypt_xpg6.m4 $(top_srcdir)/m4/dbqlk.m4 \ + $(top_srcdir)/m4/dirent_dtype.m4 $(top_srcdir)/m4/dovecot.m4 \ + $(top_srcdir)/m4/fd_passing.m4 $(top_srcdir)/m4/fdatasync.m4 \ + $(top_srcdir)/m4/flexible_array_member.m4 \ + $(top_srcdir)/m4/glibc.m4 $(top_srcdir)/m4/gmtime_max.m4 \ + $(top_srcdir)/m4/gmtime_tm_gmtoff.m4 \ + $(top_srcdir)/m4/ioloop.m4 $(top_srcdir)/m4/iovec.m4 \ + $(top_srcdir)/m4/ipv6.m4 $(top_srcdir)/m4/libcap.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/libwrap.m4 \ + $(top_srcdir)/m4/linux_mremap.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/mmap_write.m4 \ + $(top_srcdir)/m4/mntctl.m4 $(top_srcdir)/m4/modules.m4 \ + $(top_srcdir)/m4/notify.m4 $(top_srcdir)/m4/nsl.m4 \ + $(top_srcdir)/m4/off_t_max.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/pr_set_dumpable.m4 \ + $(top_srcdir)/m4/q_quotactl.m4 $(top_srcdir)/m4/quota.m4 \ + $(top_srcdir)/m4/random.m4 $(top_srcdir)/m4/rlimit.m4 \ + $(top_srcdir)/m4/sendfile.m4 $(top_srcdir)/m4/size_t_signed.m4 \ + $(top_srcdir)/m4/sockpeercred.m4 $(top_srcdir)/m4/sql.m4 \ + $(top_srcdir)/m4/ssl.m4 $(top_srcdir)/m4/st_tim.m4 \ + $(top_srcdir)/m4/static_array.m4 $(top_srcdir)/m4/test_with.m4 \ + $(top_srcdir)/m4/time_t.m4 $(top_srcdir)/m4/typeof.m4 \ + $(top_srcdir)/m4/typeof_dev_t.m4 \ + $(top_srcdir)/m4/uoff_t_max.m4 $(top_srcdir)/m4/vararg.m4 \ + $(top_srcdir)/m4/want_apparmor.m4 \ + $(top_srcdir)/m4/want_bsdauth.m4 \ + $(top_srcdir)/m4/want_bzlib.m4 \ + $(top_srcdir)/m4/want_cassandra.m4 \ + $(top_srcdir)/m4/want_cdb.m4 \ + $(top_srcdir)/m4/want_checkpassword.m4 \ + $(top_srcdir)/m4/want_clucene.m4 $(top_srcdir)/m4/want_db.m4 \ + $(top_srcdir)/m4/want_gssapi.m4 $(top_srcdir)/m4/want_icu.m4 \ + $(top_srcdir)/m4/want_ldap.m4 $(top_srcdir)/m4/want_lua.m4 \ + $(top_srcdir)/m4/want_lz4.m4 $(top_srcdir)/m4/want_lzma.m4 \ + $(top_srcdir)/m4/want_mysql.m4 $(top_srcdir)/m4/want_pam.m4 \ + $(top_srcdir)/m4/want_passwd.m4 $(top_srcdir)/m4/want_pgsql.m4 \ + $(top_srcdir)/m4/want_prefetch.m4 \ + $(top_srcdir)/m4/want_shadow.m4 \ + $(top_srcdir)/m4/want_sodium.m4 $(top_srcdir)/m4/want_solr.m4 \ + $(top_srcdir)/m4/want_sqlite.m4 \ + $(top_srcdir)/m4/want_stemmer.m4 \ + $(top_srcdir)/m4/want_systemd.m4 \ + $(top_srcdir)/m4/want_textcat.m4 \ + $(top_srcdir)/m4/want_unwind.m4 $(top_srcdir)/m4/want_zlib.m4 \ + $(top_srcdir)/m4/want_zstd.m4 $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(pkginc_lib_HEADERS) \ + $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +am__EXEEXT_1 = test-crypto$(EXEEXT) test-stream$(EXEEXT) +PROGRAMS = $(noinst_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(pkglibdir)" \ + "$(DESTDIR)$(pkginc_libdir)" +LTLIBRARIES = $(noinst_LTLIBRARIES) $(pkglib_LTLIBRARIES) +libdcrypt_la_LIBADD = +am_libdcrypt_la_OBJECTS = libdcrypt_la-dcrypt.lo \ + libdcrypt_la-istream-decrypt.lo \ + libdcrypt_la-ostream-encrypt.lo +libdcrypt_la_OBJECTS = $(am_libdcrypt_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdcrypt_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libdcrypt_la_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__DEPENDENCIES_1 = +am__libdcrypt_openssl_la_SOURCES_DIST = dcrypt-openssl.c +@BUILD_DCRYPT_OPENSSL_TRUE@am_libdcrypt_openssl_la_OBJECTS = libdcrypt_openssl_la-dcrypt-openssl.lo +libdcrypt_openssl_la_OBJECTS = $(am_libdcrypt_openssl_la_OBJECTS) +libdcrypt_openssl_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) \ + $(libdcrypt_openssl_la_LDFLAGS) $(LDFLAGS) -o $@ +@BUILD_DCRYPT_OPENSSL_TRUE@am_libdcrypt_openssl_la_rpath = -rpath \ +@BUILD_DCRYPT_OPENSSL_TRUE@ $(pkglibdir) +am__objects_1 = test_crypto-dcrypt.$(OBJEXT) \ + test_crypto-istream-decrypt.$(OBJEXT) \ + test_crypto-ostream-encrypt.$(OBJEXT) +am_test_crypto_OBJECTS = $(am__objects_1) \ + test_crypto-test-crypto.$(OBJEXT) +test_crypto_OBJECTS = $(am_test_crypto_OBJECTS) +am__DEPENDENCIES_2 = $(LIBDOVECOT_TEST_DEPS) $(am__DEPENDENCIES_1) +test_crypto_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_crypto_CFLAGS) \ + $(CFLAGS) $(test_crypto_LDFLAGS) $(LDFLAGS) -o $@ +am__objects_2 = test_stream-dcrypt.$(OBJEXT) \ + test_stream-istream-decrypt.$(OBJEXT) \ + test_stream-ostream-encrypt.$(OBJEXT) +am_test_stream_OBJECTS = $(am__objects_2) \ + test_stream-test-stream.$(OBJEXT) +test_stream_OBJECTS = $(am_test_stream_OBJECTS) +test_stream_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(test_stream_CFLAGS) \ + $(CFLAGS) $(test_stream_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir) +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libdcrypt_la-dcrypt.Plo \ + ./$(DEPDIR)/libdcrypt_la-istream-decrypt.Plo \ + ./$(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo \ + ./$(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo \ + ./$(DEPDIR)/test_crypto-dcrypt.Po \ + ./$(DEPDIR)/test_crypto-istream-decrypt.Po \ + ./$(DEPDIR)/test_crypto-ostream-encrypt.Po \ + ./$(DEPDIR)/test_crypto-test-crypto.Po \ + ./$(DEPDIR)/test_stream-dcrypt.Po \ + ./$(DEPDIR)/test_stream-istream-decrypt.Po \ + ./$(DEPDIR)/test_stream-ostream-encrypt.Po \ + ./$(DEPDIR)/test_stream-test-stream.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdcrypt_la_SOURCES) $(libdcrypt_openssl_la_SOURCES) \ + $(test_crypto_SOURCES) $(test_stream_SOURCES) +DIST_SOURCES = $(libdcrypt_la_SOURCES) \ + $(am__libdcrypt_openssl_la_SOURCES_DIST) \ + $(test_crypto_SOURCES) $(test_stream_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(pkginc_lib_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +ACLOCAL = @ACLOCAL@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +APPARMOR_LIBS = @APPARMOR_LIBS@ +AR = @AR@ +AUTH_CFLAGS = @AUTH_CFLAGS@ +AUTH_LIBS = @AUTH_LIBS@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINARY_CFLAGS = @BINARY_CFLAGS@ +BINARY_LDFLAGS = @BINARY_LDFLAGS@ +BISON = @BISON@ +CASSANDRA_CFLAGS = @CASSANDRA_CFLAGS@ +CASSANDRA_LIBS = @CASSANDRA_LIBS@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CDB_LIBS = @CDB_LIBS@ +CFLAGS = @CFLAGS@ +CLUCENE_CFLAGS = @CLUCENE_CFLAGS@ +CLUCENE_LIBS = @CLUCENE_LIBS@ +COMPRESS_LIBS = @COMPRESS_LIBS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CRYPT_LIBS = @CRYPT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DICT_LIBS = @DICT_LIBS@ +DLLIB = @DLLIB@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +FLEX = @FLEX@ +FUZZER_CPPFLAGS = @FUZZER_CPPFLAGS@ +FUZZER_LDFLAGS = @FUZZER_LDFLAGS@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +KRB5CONFIG = @KRB5CONFIG@ +KRB5_CFLAGS = @KRB5_CFLAGS@ +KRB5_LIBS = @KRB5_LIBS@ +LD = @LD@ +LDAP_LIBS = @LDAP_LIBS@ +LDFLAGS = @LDFLAGS@ +LD_NO_WHOLE_ARCHIVE = @LD_NO_WHOLE_ARCHIVE@ +LD_WHOLE_ARCHIVE = @LD_WHOLE_ARCHIVE@ +LIBCAP = @LIBCAP@ +LIBDOVECOT = @LIBDOVECOT@ +LIBDOVECOT_COMPRESS = @LIBDOVECOT_COMPRESS@ +LIBDOVECOT_DEPS = @LIBDOVECOT_DEPS@ +LIBDOVECOT_DSYNC = @LIBDOVECOT_DSYNC@ +LIBDOVECOT_LA_LIBS = @LIBDOVECOT_LA_LIBS@ +LIBDOVECOT_LDA = @LIBDOVECOT_LDA@ +LIBDOVECOT_LDAP = @LIBDOVECOT_LDAP@ +LIBDOVECOT_LIBFTS = @LIBDOVECOT_LIBFTS@ +LIBDOVECOT_LIBFTS_DEPS = @LIBDOVECOT_LIBFTS_DEPS@ +LIBDOVECOT_LOGIN = @LIBDOVECOT_LOGIN@ +LIBDOVECOT_LUA = @LIBDOVECOT_LUA@ +LIBDOVECOT_LUA_DEPS = @LIBDOVECOT_LUA_DEPS@ +LIBDOVECOT_SQL = @LIBDOVECOT_SQL@ +LIBDOVECOT_STORAGE = @LIBDOVECOT_STORAGE@ +LIBDOVECOT_STORAGE_DEPS = @LIBDOVECOT_STORAGE_DEPS@ +LIBEXTTEXTCAT_CFLAGS = @LIBEXTTEXTCAT_CFLAGS@ +LIBEXTTEXTCAT_LIBS = @LIBEXTTEXTCAT_LIBS@ +LIBICONV = @LIBICONV@ +LIBICU_CFLAGS = @LIBICU_CFLAGS@ +LIBICU_LIBS = @LIBICU_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LIBSODIUM_CFLAGS = @LIBSODIUM_CFLAGS@ +LIBSODIUM_LIBS = @LIBSODIUM_LIBS@ +LIBTIRPC_CFLAGS = @LIBTIRPC_CFLAGS@ +LIBTIRPC_LIBS = @LIBTIRPC_LIBS@ +LIBTOOL = @LIBTOOL@ +LIBUNWIND_CFLAGS = @LIBUNWIND_CFLAGS@ +LIBUNWIND_LIBS = @LIBUNWIND_LIBS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LINKED_STORAGE_LDADD = @LINKED_STORAGE_LDADD@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTLIBICONV = @LTLIBICONV@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +LUA_CFLAGS = @LUA_CFLAGS@ +LUA_LIBS = @LUA_LIBS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +MODULE_LIBS = @MODULE_LIBS@ +MODULE_SUFFIX = @MODULE_SUFFIX@ +MYSQL_CFLAGS = @MYSQL_CFLAGS@ +MYSQL_CONFIG = @MYSQL_CONFIG@ +MYSQL_LIBS = @MYSQL_LIBS@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NOPLUGIN_LDFLAGS = +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@ +PANDOC = @PANDOC@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PGSQL_CFLAGS = @PGSQL_CFLAGS@ +PGSQL_LIBS = @PGSQL_LIBS@ +PG_CONFIG = @PG_CONFIG@ +PIE_CFLAGS = @PIE_CFLAGS@ +PIE_LDFLAGS = @PIE_LDFLAGS@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +QUOTA_LIBS = @QUOTA_LIBS@ +RANLIB = @RANLIB@ +RELRO_LDFLAGS = @RELRO_LDFLAGS@ +RPCGEN = @RPCGEN@ +RUN_TEST = @RUN_TEST@ +SED = @SED@ +SETTING_FILES = @SETTING_FILES@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SQLITE_CFLAGS = @SQLITE_CFLAGS@ +SQLITE_LIBS = @SQLITE_LIBS@ +SQL_CFLAGS = @SQL_CFLAGS@ +SQL_LIBS = @SQL_LIBS@ +SSL_CFLAGS = @SSL_CFLAGS@ +SSL_LIBS = @SSL_LIBS@ +STRIP = @STRIP@ +SYSTEMD_CFLAGS = @SYSTEMD_CFLAGS@ +SYSTEMD_LIBS = @SYSTEMD_LIBS@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +ZSTD_CFLAGS = @ZSTD_CFLAGS@ +ZSTD_LIBS = @ZSTD_LIBS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +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@ +datadir = @datadir@ +datarootdir = @datarootdir@ +dict_drivers = @dict_drivers@ +docdir = @docdir@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +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@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +moduledir = @moduledir@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +rundir = @rundir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +sql_drivers = @sql_drivers@ +srcdir = @srcdir@ +ssldir = @ssldir@ +statedir = @statedir@ +sysconfdir = @sysconfdir@ +systemdservicetype = @systemdservicetype@ +systemdsystemunitdir = @systemdsystemunitdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +noinst_LTLIBRARIES = libdcrypt.la +pkglib_LTLIBRARIES = $(am__append_1) +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-ssl-iostream \ + -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" + +libdcrypt_la_SOURCES = \ + dcrypt.c \ + istream-decrypt.c \ + ostream-encrypt.c + +libdcrypt_la_CFLAGS = $(AM_CPPFLAGS) \ + -DDCRYPT_MODULE_DIR=\"$(pkglibdir)\" + +@BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_SOURCES = dcrypt-openssl.c +@BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_LDFLAGS = -module -avoid-version ../lib-ssl-iostream/libssl_iostream_openssl.la +@BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_LIBADD = $(SSL_LIBS) +@BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_DEPENDENCIES = ../lib-ssl-iostream/libssl_iostream_openssl.la +@BUILD_DCRYPT_OPENSSL_TRUE@libdcrypt_openssl_la_CFLAGS = $(AM_CPPFLAGS) \ +@BUILD_DCRYPT_OPENSSL_TRUE@ $(SSL_CFLAGS) + +headers = \ + dcrypt.h \ + dcrypt-iostream.h \ + dcrypt-private.h \ + ostream-encrypt.h \ + istream-decrypt.h + +pkginc_libdir = $(pkgincludedir) +pkginc_lib_HEADERS = $(headers) +EXTRA_DIST = \ + sample-v1.asc \ + sample-v1_short.asc \ + sample-v2.asc + +test_programs = test-crypto test-stream +LIBDOVECOT_TEST_DEPS = \ + ../lib-ssl-iostream/libssl_iostream.la \ + ../lib-test/libtest.la \ + ../lib/liblib.la + +LIBDOVECOT_TEST = \ + $(LIBDOVECOT_TEST_DEPS) \ + $(MODULE_LIBS) + +test_crypto_LDADD = $(LIBDOVECOT_TEST) +test_crypto_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) +@HAVE_WHOLE_ARCHIVE_TRUE@test_crypto_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) +test_crypto_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" +test_crypto_SOURCES = $(libdcrypt_la_SOURCES) test-crypto.c +test_stream_LDADD = $(LIBDOVECOT_TEST) +test_stream_DEPENDENCIES = $(LIBDOVECOT_TEST_DEPS) +@HAVE_WHOLE_ARCHIVE_TRUE@test_stream_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) +test_stream_CFLAGS = $(AM_CPPFLAGS) -DDCRYPT_SRC_DIR=\"$(top_srcdir)/src/lib-dcrypt\" +test_stream_SOURCES = $(libdcrypt_la_SOURCES) test-stream.c +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(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) --foreign src/lib-dcrypt/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign src/lib-dcrypt/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__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-noinstPROGRAMS: + @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +clean-noinstLTLIBRARIES: + -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES) + @list='$(noinst_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +install-pkglibLTLIBRARIES: $(pkglib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkglibdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkglibdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(pkglibdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(pkglibdir)"; \ + } + +uninstall-pkglibLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(pkglib_LTLIBRARIES)'; test -n "$(pkglibdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(pkglibdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(pkglibdir)/$$f"; \ + done + +clean-pkglibLTLIBRARIES: + -test -z "$(pkglib_LTLIBRARIES)" || rm -f $(pkglib_LTLIBRARIES) + @list='$(pkglib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libdcrypt.la: $(libdcrypt_la_OBJECTS) $(libdcrypt_la_DEPENDENCIES) $(EXTRA_libdcrypt_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdcrypt_la_LINK) $(libdcrypt_la_OBJECTS) $(libdcrypt_la_LIBADD) $(LIBS) + +libdcrypt_openssl.la: $(libdcrypt_openssl_la_OBJECTS) $(libdcrypt_openssl_la_DEPENDENCIES) $(EXTRA_libdcrypt_openssl_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdcrypt_openssl_la_LINK) $(am_libdcrypt_openssl_la_rpath) $(libdcrypt_openssl_la_OBJECTS) $(libdcrypt_openssl_la_LIBADD) $(LIBS) + +test-crypto$(EXEEXT): $(test_crypto_OBJECTS) $(test_crypto_DEPENDENCIES) $(EXTRA_test_crypto_DEPENDENCIES) + @rm -f test-crypto$(EXEEXT) + $(AM_V_CCLD)$(test_crypto_LINK) $(test_crypto_OBJECTS) $(test_crypto_LDADD) $(LIBS) + +test-stream$(EXEEXT): $(test_stream_OBJECTS) $(test_stream_DEPENDENCIES) $(EXTRA_test_stream_DEPENDENCIES) + @rm -f test-stream$(EXEEXT) + $(AM_V_CCLD)$(test_stream_LINK) $(test_stream_OBJECTS) $(test_stream_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-dcrypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-istream-decrypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-dcrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-istream-decrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-ostream-encrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto-test-crypto.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-dcrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-istream-decrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-ostream-encrypt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_stream-test-stream.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +libdcrypt_la-dcrypt.lo: dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-dcrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-dcrypt.Tpo -c -o libdcrypt_la-dcrypt.lo `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-dcrypt.Tpo $(DEPDIR)/libdcrypt_la-dcrypt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='libdcrypt_la-dcrypt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-dcrypt.lo `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c + +libdcrypt_la-istream-decrypt.lo: istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-istream-decrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-istream-decrypt.Tpo -c -o libdcrypt_la-istream-decrypt.lo `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-istream-decrypt.Tpo $(DEPDIR)/libdcrypt_la-istream-decrypt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='libdcrypt_la-istream-decrypt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-istream-decrypt.lo `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c + +libdcrypt_la-ostream-encrypt.lo: ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -MT libdcrypt_la-ostream-encrypt.lo -MD -MP -MF $(DEPDIR)/libdcrypt_la-ostream-encrypt.Tpo -c -o libdcrypt_la-ostream-encrypt.lo `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_la-ostream-encrypt.Tpo $(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='libdcrypt_la-ostream-encrypt.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_la-ostream-encrypt.lo `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c + +libdcrypt_openssl_la-dcrypt-openssl.lo: dcrypt-openssl.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) -MT libdcrypt_openssl_la-dcrypt-openssl.lo -MD -MP -MF $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Tpo -c -o libdcrypt_openssl_la-dcrypt-openssl.lo `test -f 'dcrypt-openssl.c' || echo '$(srcdir)/'`dcrypt-openssl.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Tpo $(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt-openssl.c' object='libdcrypt_openssl_la-dcrypt-openssl.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libdcrypt_openssl_la_CFLAGS) $(CFLAGS) -c -o libdcrypt_openssl_la-dcrypt-openssl.lo `test -f 'dcrypt-openssl.c' || echo '$(srcdir)/'`dcrypt-openssl.c + +test_crypto-dcrypt.o: dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-dcrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-dcrypt.Tpo -c -o test_crypto-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-dcrypt.Tpo $(DEPDIR)/test_crypto-dcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_crypto-dcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c + +test_crypto-dcrypt.obj: dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-dcrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-dcrypt.Tpo -c -o test_crypto-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-dcrypt.Tpo $(DEPDIR)/test_crypto-dcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_crypto-dcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` + +test_crypto-istream-decrypt.o: istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-istream-decrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-istream-decrypt.Tpo -c -o test_crypto-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-istream-decrypt.Tpo $(DEPDIR)/test_crypto-istream-decrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_crypto-istream-decrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c + +test_crypto-istream-decrypt.obj: istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-istream-decrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-istream-decrypt.Tpo -c -o test_crypto-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-istream-decrypt.Tpo $(DEPDIR)/test_crypto-istream-decrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_crypto-istream-decrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` + +test_crypto-ostream-encrypt.o: ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-ostream-encrypt.o -MD -MP -MF $(DEPDIR)/test_crypto-ostream-encrypt.Tpo -c -o test_crypto-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-ostream-encrypt.Tpo $(DEPDIR)/test_crypto-ostream-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_crypto-ostream-encrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c + +test_crypto-ostream-encrypt.obj: ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-ostream-encrypt.obj -MD -MP -MF $(DEPDIR)/test_crypto-ostream-encrypt.Tpo -c -o test_crypto-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-ostream-encrypt.Tpo $(DEPDIR)/test_crypto-ostream-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_crypto-ostream-encrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` + +test_crypto-test-crypto.o: test-crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.o -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crypto.c' object='test_crypto-test-crypto.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.o `test -f 'test-crypto.c' || echo '$(srcdir)/'`test-crypto.c + +test_crypto-test-crypto.obj: test-crypto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -MT test_crypto-test-crypto.obj -MD -MP -MF $(DEPDIR)/test_crypto-test-crypto.Tpo -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_crypto-test-crypto.Tpo $(DEPDIR)/test_crypto-test-crypto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-crypto.c' object='test_crypto-test-crypto.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_crypto_CFLAGS) $(CFLAGS) -c -o test_crypto-test-crypto.obj `if test -f 'test-crypto.c'; then $(CYGPATH_W) 'test-crypto.c'; else $(CYGPATH_W) '$(srcdir)/test-crypto.c'; fi` + +test_stream-dcrypt.o: dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-dcrypt.o -MD -MP -MF $(DEPDIR)/test_stream-dcrypt.Tpo -c -o test_stream-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-dcrypt.Tpo $(DEPDIR)/test_stream-dcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_stream-dcrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-dcrypt.o `test -f 'dcrypt.c' || echo '$(srcdir)/'`dcrypt.c + +test_stream-dcrypt.obj: dcrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-dcrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-dcrypt.Tpo -c -o test_stream-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-dcrypt.Tpo $(DEPDIR)/test_stream-dcrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dcrypt.c' object='test_stream-dcrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-dcrypt.obj `if test -f 'dcrypt.c'; then $(CYGPATH_W) 'dcrypt.c'; else $(CYGPATH_W) '$(srcdir)/dcrypt.c'; fi` + +test_stream-istream-decrypt.o: istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-istream-decrypt.o -MD -MP -MF $(DEPDIR)/test_stream-istream-decrypt.Tpo -c -o test_stream-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-istream-decrypt.Tpo $(DEPDIR)/test_stream-istream-decrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_stream-istream-decrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-istream-decrypt.o `test -f 'istream-decrypt.c' || echo '$(srcdir)/'`istream-decrypt.c + +test_stream-istream-decrypt.obj: istream-decrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-istream-decrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-istream-decrypt.Tpo -c -o test_stream-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-istream-decrypt.Tpo $(DEPDIR)/test_stream-istream-decrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='istream-decrypt.c' object='test_stream-istream-decrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-istream-decrypt.obj `if test -f 'istream-decrypt.c'; then $(CYGPATH_W) 'istream-decrypt.c'; else $(CYGPATH_W) '$(srcdir)/istream-decrypt.c'; fi` + +test_stream-ostream-encrypt.o: ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-ostream-encrypt.o -MD -MP -MF $(DEPDIR)/test_stream-ostream-encrypt.Tpo -c -o test_stream-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-ostream-encrypt.Tpo $(DEPDIR)/test_stream-ostream-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_stream-ostream-encrypt.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-ostream-encrypt.o `test -f 'ostream-encrypt.c' || echo '$(srcdir)/'`ostream-encrypt.c + +test_stream-ostream-encrypt.obj: ostream-encrypt.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-ostream-encrypt.obj -MD -MP -MF $(DEPDIR)/test_stream-ostream-encrypt.Tpo -c -o test_stream-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-ostream-encrypt.Tpo $(DEPDIR)/test_stream-ostream-encrypt.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ostream-encrypt.c' object='test_stream-ostream-encrypt.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-ostream-encrypt.obj `if test -f 'ostream-encrypt.c'; then $(CYGPATH_W) 'ostream-encrypt.c'; else $(CYGPATH_W) '$(srcdir)/ostream-encrypt.c'; fi` + +test_stream-test-stream.o: test-stream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-test-stream.o -MD -MP -MF $(DEPDIR)/test_stream-test-stream.Tpo -c -o test_stream-test-stream.o `test -f 'test-stream.c' || echo '$(srcdir)/'`test-stream.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-test-stream.Tpo $(DEPDIR)/test_stream-test-stream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stream.c' object='test_stream-test-stream.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-test-stream.o `test -f 'test-stream.c' || echo '$(srcdir)/'`test-stream.c + +test_stream-test-stream.obj: test-stream.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -MT test_stream-test-stream.obj -MD -MP -MF $(DEPDIR)/test_stream-test-stream.Tpo -c -o test_stream-test-stream.obj `if test -f 'test-stream.c'; then $(CYGPATH_W) 'test-stream.c'; else $(CYGPATH_W) '$(srcdir)/test-stream.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/test_stream-test-stream.Tpo $(DEPDIR)/test_stream-test-stream.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='test-stream.c' object='test_stream-test-stream.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(test_stream_CFLAGS) $(CFLAGS) -c -o test_stream-test-stream.obj `if test -f 'test-stream.c'; then $(CYGPATH_W) 'test-stream.c'; else $(CYGPATH_W) '$(srcdir)/test-stream.c'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-pkginc_libHEADERS: $(pkginc_lib_HEADERS) + @$(NORMAL_INSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkginc_libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkginc_libdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkginc_libdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(pkginc_libdir)" || exit $$?; \ + done + +uninstall-pkginc_libHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(pkginc_lib_HEADERS)'; test -n "$(pkginc_libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkginc_libdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + 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-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + 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" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(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 + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(pkglibdir)" "$(DESTDIR)$(pkginc_libdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +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: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +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 \ + clean-noinstPROGRAMS clean-pkglibLTLIBRARIES mostlyclean-am + +distclean: distclean-am + -rm -f ./$(DEPDIR)/libdcrypt_la-dcrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_la-istream-decrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo + -rm -f ./$(DEPDIR)/test_crypto-dcrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-istream-decrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-ostream-encrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-test-crypto.Po + -rm -f ./$(DEPDIR)/test_stream-dcrypt.Po + -rm -f ./$(DEPDIR)/test_stream-istream-decrypt.Po + -rm -f ./$(DEPDIR)/test_stream-ostream-encrypt.Po + -rm -f ./$(DEPDIR)/test_stream-test-stream.Po + -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-pkginc_libHEADERS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-pkglibLTLIBRARIES + +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 -f ./$(DEPDIR)/libdcrypt_la-dcrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_la-istream-decrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_la-ostream-encrypt.Plo + -rm -f ./$(DEPDIR)/libdcrypt_openssl_la-dcrypt-openssl.Plo + -rm -f ./$(DEPDIR)/test_crypto-dcrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-istream-decrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-ostream-encrypt.Po + -rm -f ./$(DEPDIR)/test_crypto-test-crypto.Po + -rm -f ./$(DEPDIR)/test_stream-dcrypt.Po + -rm -f ./$(DEPDIR)/test_stream-istream-decrypt.Po + -rm -f ./$(DEPDIR)/test_stream-ostream-encrypt.Po + -rm -f ./$(DEPDIR)/test_stream-test-stream.Po + -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: uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am \ + check-local clean clean-generic clean-libtool \ + clean-noinstLTLIBRARIES clean-noinstPROGRAMS \ + clean-pkglibLTLIBRARIES cscopelist-am ctags ctags-am 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-pkginc_libHEADERS \ + install-pkglibLTLIBRARIES 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 tags-am uninstall uninstall-am \ + uninstall-pkginc_libHEADERS uninstall-pkglibLTLIBRARIES + +.PRECIOUS: Makefile + + +check-local: + for bin in $(test_programs); do \ + if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ + done + +# 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/lib-dcrypt/dcrypt-iostream.h b/src/lib-dcrypt/dcrypt-iostream.h new file mode 100644 index 0000000..ec78978 --- /dev/null +++ b/src/lib-dcrypt/dcrypt-iostream.h @@ -0,0 +1,16 @@ +#ifndef DCRYPT_IOSTREAM_H +#define DCRYPT_IOSTREAM_H 1 + +static const unsigned char IOSTREAM_CRYPT_MAGIC[] = + {'C','R','Y','P','T','E','D','\x03','\x07'}; +#define IOSTREAM_CRYPT_VERSION 2 +#define IOSTREAM_TAG_SIZE 16 + +enum io_stream_encrypt_flags { + IO_STREAM_ENC_INTEGRITY_HMAC = 0x1, + IO_STREAM_ENC_INTEGRITY_AEAD = 0x2, + IO_STREAM_ENC_INTEGRITY_NONE = 0x4, + IO_STREAM_ENC_VERSION_1 = 0x8, +}; + +#endif diff --git a/src/lib-dcrypt/dcrypt-openssl.c b/src/lib-dcrypt/dcrypt-openssl.c new file mode 100644 index 0000000..1cbe352 --- /dev/null +++ b/src/lib-dcrypt/dcrypt-openssl.c @@ -0,0 +1,3807 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "base64.h" +#include "str.h" +#include "hex-binary.h" +#include "safe-memset.h" +#include "randgen.h" +#include "array.h" +#include "module-dir.h" +#include "istream.h" +#include "json-tree.h" +#include "dovecot-openssl-common.h" +#include <openssl/evp.h> +#include <openssl/sha.h> +#include <openssl/err.h> +#include <openssl/rsa.h> +#include <openssl/ec.h> +#include <openssl/bio.h> +#include <openssl/pem.h> +#include <openssl/x509.h> +#include <openssl/engine.h> +#include <openssl/hmac.h> +#include <openssl/objects.h> +#include <openssl/bn.h> +#include "dcrypt.h" +#include "dcrypt-private.h" + +/** + + key format documentation: + ========================= + + v1 key + ------ + algo id = openssl NID + enctype = 0 = none, 1 = ecdhe, 2 = password + key id = sha256(hex encoded public point) + + public key + ---------- + 1<tab>algo id<tab>public point + + private key + ----------- + - enctype none + 1<tab>algo id<tab>0<tab>private point<tab>key id + + - enctype ecdh (algorithm AES-256-CTR, key = SHA256(shared secret), IV = \0\0\0...) + 1<tab>algo id<tab>1<tab>private point<tab>ephemeral public key<tab>encryption key id<tab>key id + + - enctype password (algorithm AES-256-CTR, key = PBKDF2(SHA1, 16, password, salt), IV = \0\0\0...) + 1<tab>algo id<tab>2<tab>private point<tab>salt<tab>key id + + v2 key + ------ + algo oid = ASN1 OID of key algorithm (RSA or EC curve) + enctype = 0 = none, 1 = ecdhe, 2 = password + key id = SHA256(i2d_PUBKEY) + + public key + ---------- + 2<tab>HEX(i2d_PUBKEY)<tab>key id + + - enctype none + 2<tab>key algo oid<tab>0<tab>(RSA = i2d_PrivateKey, EC=Private Point)<tab>key id + + - enctype ecdh, key,iv = PBKDF2(hash algo, rounds, shared secret, salt) + 2<tab>key algo oid<tab>1<tab>symmetric algo name<tab>salt<tab>hash algo<tab>rounds<tab>E(RSA = i2d_PrivateKey, EC=Private Point)<tab>ephemeral public key<tab>encryption key id<tab>key id + + - enctype password, key,iv = PBKDF2(hash algo, rounds, password, salt) + 2<tab>key algo oid<tab>1<tab>symmetric algo name<tab>salt<tab>hash algo<tab>rounds<tab>E(RSA = i2d_PrivateKey, EC=Private Point)<tab>key id +**/ + +#ifndef HAVE_EVP_PKEY_get0 +#define EVP_PKEY_get0_EC_KEY(x) x->pkey.ec +#define EVP_PKEY_get0_RSA(x) x->pkey.rsa +#endif + +#ifndef HAVE_OBJ_LENGTH +#define OBJ_length(o) ((o)->length) +#endif + +#ifndef HAVE_EVP_MD_CTX_NEW +# define EVP_MD_CTX_new() EVP_MD_CTX_create() +# define EVP_MD_CTX_free(ctx) EVP_MD_CTX_destroy(ctx) +#endif + +#ifndef HAVE_HMAC_CTX_NEW +# define HMAC_Init_ex(ctx, key, key_len, md, impl) \ + HMAC_Init_ex(&(ctx), key, key_len, md, impl) +# define HMAC_Update(ctx, data, len) HMAC_Update(&(ctx), data, len) +# define HMAC_Final(ctx, md, len) HMAC_Final(&(ctx), md, len) +# define HMAC_CTX_free(ctx) HMAC_cleanup(&(ctx)) +#else +# define HMAC_CTX_free(ctx) \ + STMT_START { HMAC_CTX_free(ctx); (ctx) = NULL; } STMT_END +#endif + +/* Not always present */ +#ifndef HAVE_BN_SECURE_NEW +# define BN_secure_new BN_new +#endif + +/* openssl manual says this is OK */ +#define OID_TEXT_MAX_LEN 80 + +#define t_base64url_decode_str(x) t_base64url_decode_str(BASE64_DECODE_FLAG_IGNORE_PADDING, (x)) + +struct dcrypt_context_symmetric { + pool_t pool; + const EVP_CIPHER *cipher; + EVP_CIPHER_CTX *ctx; + unsigned char *key; + unsigned char *iv; + unsigned char *aad; + size_t aad_len; + unsigned char *tag; + size_t tag_len; + int padding; + int mode; +}; + +struct dcrypt_context_hmac { + pool_t pool; + const EVP_MD *md; +#ifdef HAVE_HMAC_CTX_NEW + HMAC_CTX *ctx; +#else + HMAC_CTX ctx; +#endif + unsigned char *key; + size_t klen; +}; + +struct dcrypt_public_key { + EVP_PKEY *key; + unsigned int ref; + enum dcrypt_key_usage usage; + char *key_id; +}; + +struct dcrypt_private_key { + EVP_PKEY *key; + unsigned int ref; + enum dcrypt_key_usage usage; + char *key_id; +}; + +#define DCRYPT_SET_ERROR(error) STMT_START { if (error_r != NULL) *error_r = (error); } STMT_END + +static bool +dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, + const char *algorithm, buffer_t *result, + const char **error_r); +static bool +dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, + buffer_t *result, const char **error_r); +static bool +dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, + const char *algorithm, buffer_t *result, + const char **error_r); +static bool +dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, + buffer_t *result, const char **error_r); +static void +dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key **pub_key_r); +static void +dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key); +static void +dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key); +static bool +dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r); +static bool +dcrypt_openssl_key_string_get_info(const char *key_data, + enum dcrypt_key_format *format_r, enum dcrypt_key_version *version_r, + enum dcrypt_key_kind *kind_r, + enum dcrypt_key_encryption_type *encryption_type_r, + const char **encryption_key_hash_r, const char **key_hash_r, + const char **error_r); + +#ifndef HAVE_EC_GROUP_order_bits +static int EC_GROUP_order_bits(const EC_GROUP *grp) +{ + int bits; + BIGNUM *bn = BN_new(); + (void)EC_GROUP_get_order(grp, bn, NULL); + bits = BN_num_bits(bn); + BN_free(bn); + return bits; +} +#endif + +static bool dcrypt_openssl_error(const char **error_r) +{ + unsigned long ec; + + if (error_r == NULL) { + /* caller is not really interested */ + return FALSE; + } + + ec = ERR_get_error(); + DCRYPT_SET_ERROR(t_strdup_printf("%s", ERR_error_string(ec, NULL))); + return FALSE; +} + +static int +dcrypt_openssl_padding_mode(enum dcrypt_padding padding, + bool sig, const char **error_r) +{ + switch (padding) { + case DCRYPT_PADDING_DEFAULT: + if (sig) return RSA_PKCS1_PSS_PADDING; + else return RSA_PKCS1_OAEP_PADDING; + case DCRYPT_PADDING_RSA_PKCS1_OAEP: + return RSA_PKCS1_OAEP_PADDING; + case DCRYPT_PADDING_RSA_PKCS1_PSS: + return RSA_PKCS1_PSS_PADDING; + case DCRYPT_PADDING_RSA_PKCS1: + return RSA_PKCS1_PADDING; + case DCRYPT_PADDING_RSA_NO: + return RSA_NO_PADDING; + default: + DCRYPT_SET_ERROR("Unsupported padding mode"); + return -1; + } + i_unreached(); +} + +static bool dcrypt_openssl_initialize(const struct dcrypt_settings *set, + const char **error_r) +{ + if (set->crypto_device != NULL && set->crypto_device[0] != '\0') { + if (dovecot_openssl_common_global_set_engine( + set->crypto_device, error_r) <= 0) + return FALSE; + } + return TRUE; +} + +/* legacy function for old formats that generates + hex encoded point from EC public key + */ +static char *ec_key_get_pub_point_hex(const EC_KEY *key) +{ + const EC_POINT *p; + const EC_GROUP *g; + + p = EC_KEY_get0_public_key(key); + g = EC_KEY_get0_group(key); + return EC_POINT_point2hex(g, p, POINT_CONVERSION_COMPRESSED, NULL); +} + +static bool +dcrypt_openssl_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, + struct dcrypt_context_symmetric **ctx_r, + const char **error_r) +{ + struct dcrypt_context_symmetric *ctx; + pool_t pool; + const EVP_CIPHER *cipher; + + cipher = EVP_get_cipherbyname(algorithm); + if (cipher == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Invalid cipher %s", + algorithm)); + return FALSE; + } + + /* allocate context */ + pool = pool_alloconly_create("dcrypt openssl", 1024); + ctx = p_new(pool, struct dcrypt_context_symmetric, 1); + ctx->pool = pool; + ctx->cipher = cipher; + ctx->padding = 1; + ctx->mode = (mode == DCRYPT_MODE_ENCRYPT ? 1 : 0); + *ctx_r = ctx; + return TRUE; +} + +static void +dcrypt_openssl_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) +{ + pool_t pool = (*ctx)->pool; + + if ((*ctx)->ctx != NULL) + EVP_CIPHER_CTX_free((*ctx)->ctx); + pool_unref(&pool); + *ctx = NULL; +} + +static void +dcrypt_openssl_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, + const unsigned char *key, size_t key_len) +{ + if (ctx->key != NULL) + p_free(ctx->pool, ctx->key); + ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); + memcpy(ctx->key, key, I_MIN(key_len, + (size_t)EVP_CIPHER_key_length(ctx->cipher))); +} + +static void +dcrypt_openssl_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, + const unsigned char *iv, size_t iv_len) +{ + if(ctx->iv != NULL) + p_free(ctx->pool, ctx->iv); + + ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); + memcpy(ctx->iv, iv, I_MIN(iv_len, + (size_t)EVP_CIPHER_iv_length(ctx->cipher))); +} + +static void +dcrypt_openssl_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) +{ + if(ctx->key != NULL) + p_free(ctx->pool, ctx->key); + if(ctx->iv != NULL) + p_free(ctx->pool, ctx->iv); + + ctx->key = p_malloc(ctx->pool, EVP_CIPHER_key_length(ctx->cipher)); + random_fill(ctx->key, EVP_CIPHER_key_length(ctx->cipher)); + ctx->iv = p_malloc(ctx->pool, EVP_CIPHER_iv_length(ctx->cipher)); + random_fill(ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); +} + +static void +dcrypt_openssl_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, + bool padding) +{ + ctx->padding = (padding?1:0); +} + +static bool +dcrypt_openssl_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, + buffer_t *key) +{ + if(ctx->key == NULL) + return FALSE; + + buffer_append(key, ctx->key, EVP_CIPHER_key_length(ctx->cipher)); + return TRUE; +} + +static bool +dcrypt_openssl_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, + buffer_t *iv) +{ + if(ctx->iv == NULL) + return FALSE; + + buffer_append(iv, ctx->iv, EVP_CIPHER_iv_length(ctx->cipher)); + return TRUE; +} + +static void +dcrypt_openssl_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, + const unsigned char *aad, size_t aad_len) +{ + if (ctx->aad != NULL) + p_free(ctx->pool, ctx->aad); + + /* allow empty aad */ + ctx->aad = p_malloc(ctx->pool, I_MAX(1,aad_len)); + memcpy(ctx->aad, aad, aad_len); + ctx->aad_len = aad_len; +} + +static bool +dcrypt_openssl_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, + buffer_t *aad) +{ + if (ctx->aad == NULL) + return FALSE; + + buffer_append(aad, ctx->aad, ctx->aad_len); + return TRUE; +} + +static void +dcrypt_openssl_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, + const unsigned char *tag, size_t tag_len) +{ + if (ctx->tag != NULL) + p_free(ctx->pool, ctx->tag); + + /* unlike aad, tag cannot be empty */ + ctx->tag = p_malloc(ctx->pool, tag_len); + memcpy(ctx->tag, tag, tag_len); + ctx->tag_len = tag_len; +} + +static bool +dcrypt_openssl_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, + buffer_t *tag) +{ + if (ctx->tag == NULL) + return FALSE; + + buffer_append(tag, ctx->tag, ctx->tag_len); + return TRUE; +} + +static unsigned int +dcrypt_openssl_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) +{ + return EVP_CIPHER_key_length(ctx->cipher); +} + +static unsigned int +dcrypt_openssl_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) +{ + return EVP_CIPHER_iv_length(ctx->cipher); +} + +static unsigned int +dcrypt_openssl_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) +{ + return EVP_CIPHER_block_size(ctx->cipher); +} + +static bool +dcrypt_openssl_ctx_sym_init(struct dcrypt_context_symmetric *ctx, + const char **error_r) +{ + int ec; + int len; + + i_assert(ctx->key != NULL); + i_assert(ctx->iv != NULL); + i_assert(ctx->ctx == NULL); + + if((ctx->ctx = EVP_CIPHER_CTX_new()) == NULL) + return dcrypt_openssl_error(error_r); + + ec = EVP_CipherInit_ex(ctx->ctx, ctx->cipher, NULL, + ctx->key, ctx->iv, ctx->mode); + if (ec != 1) + return dcrypt_openssl_error(error_r); + + EVP_CIPHER_CTX_set_padding(ctx->ctx, ctx->padding); + len = 0; + if (ctx->aad != NULL) { + ec = EVP_CipherUpdate(ctx->ctx, NULL, &len, + ctx->aad, ctx->aad_len); + } + if (ec != 1) + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_ctx_sym_update(struct dcrypt_context_symmetric *ctx, + const unsigned char *data, size_t data_len, + buffer_t *result, const char **error_r) +{ + const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); + size_t buf_used = result->used; + unsigned char *buf; + int outl; + + i_assert(ctx->ctx != NULL); + + /* From `man 3 evp_cipherupdate`: + + EVP_EncryptUpdate() encrypts inl bytes from the buffer in and writes + the encrypted version to out. This function can be called multiple + times to encrypt successive blocks of data. The amount of data + written depends on the block alignment of the encrypted data: as a + result the amount of data written may be anything from zero bytes to + (inl + cipher_block_size - 1) so out should contain sufficient room. + The actual number of bytes written is placed in outl. + */ + + buf = buffer_append_space_unsafe(result, data_len + block_size); + outl = 0; + if (EVP_CipherUpdate + (ctx->ctx, buf, &outl, data, data_len) != 1) + return dcrypt_openssl_error(error_r); + buffer_set_used_size(result, buf_used + outl); + return TRUE; +} + +static bool +dcrypt_openssl_ctx_sym_final(struct dcrypt_context_symmetric *ctx, + buffer_t *result, const char **error_r) +{ + const size_t block_size = (size_t)EVP_CIPHER_block_size(ctx->cipher); + size_t buf_used = result->used; + unsigned char *buf; + int outl; + int ec; + + i_assert(ctx->ctx != NULL); + + /* From `man 3 evp_cipherupdate`: + + If padding is enabled (the default) then EVP_EncryptFinal_ex() + encrypts the "final" data, that is any data that remains in a partial + block. It uses standard block padding (aka PKCS padding). The + encrypted final data is written to out which should have sufficient + space for one cipher block. The number of bytes written is placed in + outl. After this function is called the encryption operation is + finished and no further calls to EVP_EncryptUpdate() should be made. + */ + + buf = buffer_append_space_unsafe(result, block_size); + outl = 0; + + /* when **DECRYPTING** set expected tag */ + if (ctx->mode == 0 && ctx->tag != NULL) { + ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_SET_TAG, + ctx->tag_len, ctx->tag); + } else { + ec = 1; + } + + if (ec == 1) + ec = EVP_CipherFinal_ex(ctx->ctx, buf, &outl); + + if (ec == 1) { + buffer_set_used_size(result, buf_used + outl); + /* when **ENCRYPTING** recover tag */ + if (ctx->mode == 1 && ctx->aad != NULL) { + /* tag should be NULL here */ + i_assert(ctx->tag == NULL); + /* openssl claims taglen is always 16, go figure .. */ + ctx->tag = p_malloc(ctx->pool, EVP_GCM_TLS_TAG_LEN); + ec = EVP_CIPHER_CTX_ctrl(ctx->ctx, EVP_CTRL_GCM_GET_TAG, + EVP_GCM_TLS_TAG_LEN, ctx->tag); + ctx->tag_len = EVP_GCM_TLS_TAG_LEN; + } + } + + if (ec == 0) + DCRYPT_SET_ERROR("data authentication failed"); + else if (ec < 0) + dcrypt_openssl_error(error_r); + + EVP_CIPHER_CTX_free(ctx->ctx); + ctx->ctx = NULL; + + return (ec == 1); +} + +static bool +dcrypt_openssl_ctx_hmac_create(const char *algorithm, + struct dcrypt_context_hmac **ctx_r, + const char **error_r) +{ + struct dcrypt_context_hmac *ctx; + pool_t pool; + const EVP_MD *md; + + md = EVP_get_digestbyname(algorithm); + if(md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Invalid digest %s", + algorithm)); + return FALSE; + } + + /* allocate context */ + pool = pool_alloconly_create("dcrypt openssl", 1024); + ctx = p_new(pool, struct dcrypt_context_hmac, 1); + ctx->pool = pool; + ctx->md = md; + *ctx_r = ctx; + return TRUE; +} + +static void +dcrypt_openssl_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) +{ + pool_t pool = (*ctx)->pool; + HMAC_CTX_free((*ctx)->ctx); + pool_unref(&pool); + *ctx = NULL; +} + +static void +dcrypt_openssl_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, + const unsigned char *key, size_t key_len) +{ + if (ctx->key != NULL) + p_free(ctx->pool, ctx->key); + + ctx->klen = I_MIN(key_len, HMAC_MAX_MD_CBLOCK); + ctx->key = p_malloc(ctx->pool, ctx->klen); + memcpy(ctx->key, key, ctx->klen); +} + +static bool +dcrypt_openssl_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) +{ + if (ctx->key == NULL) + return FALSE; + buffer_append(key, ctx->key, ctx->klen); + return TRUE; +} + +static void +dcrypt_openssl_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) +{ + ctx->klen = HMAC_MAX_MD_CBLOCK; + ctx->key = p_malloc(ctx->pool, ctx->klen); + random_fill(ctx->key, ctx->klen); +} + +static unsigned int +dcrypt_openssl_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) +{ + return EVP_MD_size(ctx->md); +} + +static bool +dcrypt_openssl_ctx_hmac_init(struct dcrypt_context_hmac *ctx, + const char **error_r) +{ + int ec; + + i_assert(ctx->md != NULL); +#ifdef HAVE_HMAC_CTX_NEW + ctx->ctx = HMAC_CTX_new(); + if (ctx->ctx == NULL) + return dcrypt_openssl_error(error_r); +#endif + ec = HMAC_Init_ex(ctx->ctx, ctx->key, ctx->klen, ctx->md, NULL); + if (ec != 1) + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_ctx_hmac_update(struct dcrypt_context_hmac *ctx, + const unsigned char *data, size_t data_len, + const char **error_r) +{ + int ec; + + ec = HMAC_Update(ctx->ctx, data, data_len); + if (ec != 1) + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, + const char **error_r) +{ + int ec; + unsigned char buf[HMAC_MAX_MD_CBLOCK]; + unsigned int outl; + + ec = HMAC_Final(ctx->ctx, buf, &outl); + HMAC_CTX_free(ctx->ctx); + if (ec == 1) + buffer_append(result, buf, outl); + else + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_generate_ec_key(int nid, EVP_PKEY **key, const char **error_r) +{ + EVP_PKEY_CTX *pctx; + EVP_PKEY_CTX *ctx; + EVP_PKEY *params = NULL; + + /* generate parameters for EC */ + pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); + if (pctx == NULL || + EVP_PKEY_paramgen_init(pctx) < 1 || + EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, nid) < 1 || + EVP_PKEY_paramgen(pctx, ¶ms) < 1) + { + dcrypt_openssl_error(error_r); + EVP_PKEY_CTX_free(pctx); + return FALSE; + } + + /* generate key from parameters */ + ctx = EVP_PKEY_CTX_new(params, NULL); + if (ctx == NULL || + EVP_PKEY_keygen_init(ctx) < 1 || + EVP_PKEY_keygen(ctx, key) < 1) + { + dcrypt_openssl_error(error_r); + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(ctx); + return FALSE; + } + + EVP_PKEY_free(params); + EVP_PKEY_CTX_free(pctx); + EVP_PKEY_CTX_free(ctx); + EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY((*key)), + OPENSSL_EC_NAMED_CURVE); + return TRUE; +} + +static bool +dcrypt_openssl_generate_rsa_key(int bits, EVP_PKEY **key, const char **error_r) +{ + i_assert(bits >= 256); + int ec = 0; + + EVP_PKEY_CTX *ctx; + ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); + if (ctx == NULL || + EVP_PKEY_keygen_init(ctx) < 1 || + EVP_PKEY_CTX_set_rsa_keygen_bits(ctx, bits) < 1 || + EVP_PKEY_keygen(ctx, key) < 1) { + dcrypt_openssl_error(error_r); + ec = -1; + } + + EVP_PKEY_CTX_free(ctx); + return ec == 0; +} + +static bool +dcrypt_openssl_ecdh_derive_secret(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key *pub_key, + buffer_t *shared_secret, + const char **error_r) +{ + /* initialize */ + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new(priv_key->key, NULL); + if (pctx == NULL || + EVP_PKEY_derive_init(pctx) != 1 || + EVP_PKEY_derive_set_peer(pctx, pub_key->key) != 1) { + EVP_PKEY_CTX_free(pctx); + return dcrypt_openssl_error(error_r); + } + + /* derive */ + size_t len; + if (EVP_PKEY_derive(pctx, NULL, &len) != 1) { + EVP_PKEY_CTX_free(pctx); + return dcrypt_openssl_error(error_r); + } + unsigned char buf[len]; + if (EVP_PKEY_derive(pctx, buf, &len) != 1) { + EVP_PKEY_CTX_free(pctx); + return dcrypt_openssl_error(error_r); + } + + EVP_PKEY_CTX_free(pctx); + buffer_append(shared_secret, buf, len); + + return TRUE; +} + +static bool +dcrypt_openssl_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, + buffer_t *R, buffer_t *S, + const char **error_r) +{ + bool ret; + i_assert(local_key != NULL && local_key->key != NULL); + + EVP_PKEY *local = local_key->key; + BN_CTX *bn_ctx = BN_CTX_new(); + if (bn_ctx == NULL) + return dcrypt_openssl_error(error_r); + + const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); + EC_POINT *pub = EC_POINT_new(grp); + + /* convert ephemeral key data EC point */ + if (pub == NULL || + EC_POINT_oct2point(grp, pub, R->data, R->used, bn_ctx) != 1) + { + EC_POINT_free(pub); + BN_CTX_free(bn_ctx); + return dcrypt_openssl_error(error_r); + } + EC_KEY *ec_key = EC_KEY_new(); + + /* convert point to public key */ + int ec = 0; + if (ec_key == NULL || + EC_KEY_set_group(ec_key, grp) != 1 || + EC_KEY_set_public_key(ec_key, pub) != 1) + ec = -1; + else + EC_POINT_free(pub); + BN_CTX_free(bn_ctx); + + /* make sure it looks like a valid key */ + if (ec == -1 || EC_KEY_check_key(ec_key) != 1) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + + EVP_PKEY *peer = EVP_PKEY_new(); + if (peer == NULL) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + EVP_PKEY_set1_EC_KEY(peer, ec_key); + EC_KEY_free(ec_key); + + struct dcrypt_public_key pub_key; + i_zero(&pub_key); + pub_key.key = peer; + + ret = dcrypt_openssl_ecdh_derive_secret(local_key, &pub_key, S, error_r); + + EVP_PKEY_free(peer); + return ret; +} + +static bool +dcrypt_openssl_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, + buffer_t *R, buffer_t *S, + const char **error_r) +{ + i_assert(peer_key != NULL && peer_key->key != NULL); + bool ret; + + /* ensure peer_key is EC key */ + EVP_PKEY *local = NULL; + EVP_PKEY *peer = peer_key->key; + if (EVP_PKEY_base_id(peer) != EVP_PKEY_EC) { + DCRYPT_SET_ERROR("Only ECC key can be used"); + return FALSE; + } + + /* generate another key from same group */ + int nid = EC_GROUP_get_curve_name( + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(peer))); + if (!dcrypt_openssl_generate_ec_key(nid, &local, error_r)) + return FALSE; + + struct dcrypt_private_key priv_key; + i_zero(&priv_key); + priv_key.key = local; + + if (!(ret = dcrypt_openssl_ecdh_derive_secret(&priv_key, peer_key, S, + error_r))) { + EVP_PKEY_free(local); + return FALSE; + } + + /* get ephemeral key (=R) */ + BN_CTX *bn_ctx = BN_CTX_new(); + const EC_POINT *pub = EC_KEY_get0_public_key(EVP_PKEY_get0_EC_KEY(local)); + const EC_GROUP *grp = EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(local)); + size_t len = EC_POINT_point2oct(grp, pub, POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, bn_ctx); + unsigned char R_buf[len]; + EC_POINT_point2oct(grp, pub, POINT_CONVERSION_UNCOMPRESSED, + R_buf, len, bn_ctx); + BN_CTX_free(bn_ctx); + buffer_append(R, R_buf, len); + EVP_PKEY_free(local); + + return ret; +} + +static bool +dcrypt_openssl_pbkdf2(const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + const char *hash, unsigned int rounds, + buffer_t *result, unsigned int result_len, + const char **error_r) +{ + int ret; + i_assert(rounds > 0); + i_assert(result_len > 0); + i_assert(result != NULL); + /* determine MD */ + const EVP_MD* md = EVP_get_digestbyname(hash); + if (md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Invalid digest %s", hash)); + return FALSE; + } + + unsigned char buffer[result_len]; + if ((ret = PKCS5_PBKDF2_HMAC((const char*)password, password_len, + salt, salt_len, rounds, + md, result_len, buffer)) == 1) { + buffer_append(result, buffer, result_len); + } + if (ret != 1) + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_generate_keypair(struct dcrypt_keypair *pair_r, + enum dcrypt_key_type kind, unsigned int bits, + const char *curve, const char **error_r) +{ + EVP_PKEY *pkey = NULL; + + i_assert(pair_r != NULL); + i_zero(pair_r); + if (kind == DCRYPT_KEY_RSA) { + if (dcrypt_openssl_generate_rsa_key(bits, &pkey, error_r)) { + pair_r->priv = i_new(struct dcrypt_private_key, 1); + pair_r->priv->key = pkey; + pair_r->priv->ref++; + pair_r->pub = NULL; + dcrypt_openssl_private_to_public_key(pair_r->priv, + &pair_r->pub); + return TRUE; + } else { + return dcrypt_openssl_error(error_r); + } + } else if (kind == DCRYPT_KEY_EC) { + int nid = OBJ_sn2nid(curve); + if (nid == NID_undef) { + DCRYPT_SET_ERROR(t_strdup_printf("Unknown EC curve %s", + curve)); + return FALSE; + } + if (dcrypt_openssl_generate_ec_key(nid, &pkey, error_r)) { + pair_r->priv = i_new(struct dcrypt_private_key, 1); + pair_r->priv->key = pkey; + pair_r->priv->ref++; + pair_r->pub = NULL; + dcrypt_openssl_private_to_public_key(pair_r->priv, + &pair_r->pub); + return TRUE; + } else { + return dcrypt_openssl_error(error_r); + } + } + DCRYPT_SET_ERROR("Key type not supported in this build"); + return FALSE; +} + +static bool +dcrypt_openssl_decrypt_point_v1(buffer_t *data, buffer_t *key, BIGNUM **point_r, + const char **error_r) +{ + struct dcrypt_context_symmetric *dctx; + buffer_t *tmp = t_buffer_create(64); + + if (!dcrypt_openssl_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, + &dctx, error_r)) { + return FALSE; + } + + /* v1 KEYS have all-zero IV - have to use it ourselves too */ + dcrypt_openssl_ctx_sym_set_iv(dctx, (const unsigned char*) + "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); + dcrypt_openssl_ctx_sym_set_key(dctx, key->data, key->used); + + if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || + !dcrypt_openssl_ctx_sym_update(dctx, data->data, data->used, + tmp, error_r) || + !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { + dcrypt_openssl_ctx_sym_destroy(&dctx); + return FALSE; + } + + dcrypt_openssl_ctx_sym_destroy(&dctx); + + *point_r = BN_bin2bn(tmp->data, tmp->used, NULL); + safe_memset(buffer_get_modifiable_data(tmp, NULL), 0,tmp->used); + buffer_set_used_size(key, 0); + + if (*point_r == NULL) + return dcrypt_openssl_error(error_r); + return TRUE; +} + +static bool +dcrypt_openssl_decrypt_point_ec_v1(struct dcrypt_private_key *dec_key, + const char *data_hex, + const char *peer_key_hex, BIGNUM **point_r, + const char **error_r) +{ + buffer_t *peer_key, *data, key, *secret; + bool res; + + data = t_buffer_create(128); + peer_key = t_buffer_create(64); + + hex_to_binary(data_hex, data); + hex_to_binary(peer_key_hex, peer_key); + + secret = t_buffer_create(64); + + if (!dcrypt_openssl_ecdh_derive_secret_local(dec_key, peer_key, + secret, error_r)) + return FALSE; + + /* run it thru SHA256 once */ + unsigned char digest[SHA256_DIGEST_LENGTH]; + SHA256(secret->data, secret->used, digest); + safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); + buffer_set_used_size(secret, 0); + buffer_create_from_const_data(&key, digest, SHA256_DIGEST_LENGTH); + + /* then use this as key */ + res = dcrypt_openssl_decrypt_point_v1(data, &key, point_r, error_r); + memset(digest, 0, sizeof(digest)); + safe_memset(digest, 0, SHA256_DIGEST_LENGTH); + + return res; +} + +static bool +dcrypt_openssl_decrypt_point_password_v1(const char *data_hex, + const char *password_hex, + const char *salt_hex, BIGNUM **point_r, + const char **error_r) +{ + buffer_t *salt, *data, *password, *key; + + data = t_buffer_create(128); + salt = t_buffer_create(16); + password = t_buffer_create(32); + key = t_buffer_create(32); + + hex_to_binary(data_hex, data); + hex_to_binary(salt_hex, salt); + hex_to_binary(password_hex, password); + + /* aes-256-ctr uses 32 byte key, and v1 uses all-zero IV */ + if (!dcrypt_openssl_pbkdf2(password->data, password->used, + salt->data, salt->used, + "sha256", 16, key, 32, error_r)) + return FALSE; + + return dcrypt_openssl_decrypt_point_v1(data, key, point_r, error_r); +} + +static bool +dcrypt_openssl_load_private_key_dovecot_v1(struct dcrypt_private_key **key_r, + int len, const char **input, + const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r) +{ + int nid, ec, enctype; + BIGNUM *point = NULL; + + if (str_to_int(input[1], &nid) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + if (str_to_int(input[2], &enctype) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + /* decode and optionally decipher private key value */ + if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { + point = BN_secure_new(); + if (point == NULL || BN_hex2bn(&point, input[3]) < 1) { + BN_free(point); + return dcrypt_openssl_error(error_r); + } + } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { + /* by password */ + if (password == NULL) { + DCRYPT_SET_ERROR("password missing"); + return FALSE; + } + const char *enc_priv_pt = input[3]; + const char *salt = input[4]; + if (!dcrypt_openssl_decrypt_point_password_v1( + enc_priv_pt, password, salt, &point, error_r)) { + return FALSE; + } + } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { + /* by key */ + if (dec_key == NULL) { + DCRYPT_SET_ERROR("decrypt key missing"); + return FALSE; + } + const char *enc_priv_pt = input[3]; + const char *peer_key = input[4]; + if (!dcrypt_openssl_decrypt_point_ec_v1( + dec_key, enc_priv_pt, peer_key, &point, error_r)) { + return FALSE; + } + } else { + DCRYPT_SET_ERROR("Invalid key data"); + return FALSE; + } + + EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); + if (eckey == NULL) return dcrypt_openssl_error(error_r); + + /* assign private key */ + BN_CTX *bnctx = BN_CTX_new(); + if (bnctx == NULL) { + EC_KEY_free(eckey); + return dcrypt_openssl_error(error_r); + } + EC_KEY_set_private_key(eckey, point); + EC_KEY_precompute_mult(eckey, bnctx); + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); + if (pub == NULL) { + EC_KEY_free(eckey); + BN_CTX_free(bnctx); + return dcrypt_openssl_error(error_r); + } + /* calculate public key */ + ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, + NULL, NULL, bnctx); + EC_KEY_set_public_key(eckey, pub); + BN_free(point); + EC_POINT_free(pub); + BN_CTX_free(bnctx); + + /* make sure it looks OK and is correct */ + if (ec == 1 && EC_KEY_check_key(eckey) == 1) { + unsigned char digest[SHA256_DIGEST_LENGTH]; + /* validate that the key was loaded correctly */ + char *id = ec_key_get_pub_point_hex(eckey); + if (id == NULL) { + EC_KEY_free(eckey); + return dcrypt_openssl_error(error_r); + } + SHA256((unsigned char*)id, strlen(id), digest); + OPENSSL_free(id); + const char *digest_hex = + binary_to_hex(digest, SHA256_DIGEST_LENGTH); + if (strcmp(digest_hex, input[len-1]) != 0) { + DCRYPT_SET_ERROR("Key id mismatch after load"); + EC_KEY_free(eckey); + return FALSE; + } + EVP_PKEY *key = EVP_PKEY_new(); + if (key == NULL) { + EC_KEY_free(eckey); + return dcrypt_openssl_error(error_r); + } + EVP_PKEY_set1_EC_KEY(key, eckey); + EC_KEY_free(eckey); + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = key; + (*key_r)->ref++; + return TRUE; + } + + EC_KEY_free(eckey); + + return dcrypt_openssl_error(error_r); +} + +/* encrypt/decrypt private keys */ +static bool +dcrypt_openssl_cipher_key_dovecot_v2(const char *cipher, + enum dcrypt_sym_mode mode, + buffer_t *input, buffer_t *secret, + buffer_t *salt, const char *digalgo, + unsigned int rounds, buffer_t *result_r, + const char **error_r) +{ + struct dcrypt_context_symmetric *dctx; + bool res; + + if (!dcrypt_openssl_ctx_sym_create(cipher, mode, &dctx, error_r)) { + return FALSE; + } + + /* generate encryption key/iv based on secret/salt */ + buffer_t *key_data = t_buffer_create(128); + res = dcrypt_openssl_pbkdf2(secret->data, secret->used, + salt->data, salt->used, digalgo, rounds, key_data, + dcrypt_openssl_ctx_sym_get_key_length(dctx) + + dcrypt_openssl_ctx_sym_get_iv_length(dctx), + error_r); + + if (!res) { + dcrypt_openssl_ctx_sym_destroy(&dctx); + return FALSE; + } + + buffer_t *tmp = t_buffer_create(128); + const unsigned char *kd = buffer_free_without_data(&key_data); + + /* perform ciphering */ + dcrypt_openssl_ctx_sym_set_key(dctx, kd, + dcrypt_openssl_ctx_sym_get_key_length(dctx)); + dcrypt_openssl_ctx_sym_set_iv(dctx, + kd + dcrypt_openssl_ctx_sym_get_key_length(dctx), + dcrypt_openssl_ctx_sym_get_iv_length(dctx)); + + if (!dcrypt_openssl_ctx_sym_init(dctx, error_r) || + !dcrypt_openssl_ctx_sym_update(dctx, input->data, + input->used, tmp, error_r) || + !dcrypt_openssl_ctx_sym_final(dctx, tmp, error_r)) { + res = FALSE; + } else { + /* provide result if succeeded */ + buffer_append_buf(result_r, tmp, 0, SIZE_MAX); + res = TRUE; + } + /* and ensure no data leaks */ + safe_memset(buffer_get_modifiable_data(tmp, NULL), 0, tmp->used); + + dcrypt_openssl_ctx_sym_destroy(&dctx); + return res; +} + +static bool +dcrypt_openssl_load_private_key_dovecot_v2(struct dcrypt_private_key **key_r, + int len, const char **input, + const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r) +{ + int enctype; + buffer_t *key_data = t_buffer_create(256); + + /* check for encryption type */ + if (str_to_int(input[2], &enctype) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + if (enctype < 0 || enctype > 2) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + /* match encryption type to field counts */ + if ((enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE && len != 5) || + (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD && len != 9) || + (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK && len != 11)) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + /* get key type */ + int nid = OBJ_txt2nid(input[1]); + + if (nid == NID_undef) + return dcrypt_openssl_error(error_r); + + /* decode and possibly decipher private key value */ + if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_NONE) { + if (hex_to_binary(input[3], key_data) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + } + } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { + if (dec_key == NULL) { + DCRYPT_SET_ERROR("decrypt key missing"); + return FALSE; + } + unsigned int rounds; + struct dcrypt_public_key *pubkey = NULL; + if (str_to_uint(input[6], &rounds) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + buffer_t *data = t_buffer_create(128); + + /* check that we have correct decryption key */ + dcrypt_openssl_private_to_public_key(dec_key, &pubkey); + if (!dcrypt_openssl_public_key_id(pubkey, "sha256", + data, error_r)) { + dcrypt_openssl_unref_public_key(&pubkey); + return FALSE; + } + + dcrypt_openssl_unref_public_key(&pubkey); + + if (strcmp(binary_to_hex(data->data, data->used), + input[9]) != 0) { + DCRYPT_SET_ERROR("No private key available"); + return FALSE; + } + + + buffer_t *salt, *peer_key, *secret; + salt = t_buffer_create(strlen(input[4])/2); + peer_key = t_buffer_create(strlen(input[8])/2); + secret = t_buffer_create(128); + + buffer_set_used_size(data, 0); + hex_to_binary(input[4], salt); + hex_to_binary(input[8], peer_key); + hex_to_binary(input[7], data); + + /* get us secret value to use for key/iv generation */ + if (EVP_PKEY_base_id((EVP_PKEY*)dec_key) == EVP_PKEY_RSA) { + if (!dcrypt_openssl_rsa_decrypt(dec_key, + peer_key->data, peer_key->used, secret, + DCRYPT_PADDING_RSA_PKCS1_OAEP, error_r)) + return FALSE; + } else { + /* perform ECDH */ + if (!dcrypt_openssl_ecdh_derive_secret_local( + dec_key, peer_key, secret, error_r)) + return FALSE; + } + /* decrypt key */ + if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], + DCRYPT_MODE_DECRYPT, data, secret, salt, + input[5], rounds, key_data, error_r)) { + return FALSE; + } + } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { + if (password == NULL) { + DCRYPT_SET_ERROR("password missing"); + return FALSE; + } + unsigned int rounds; + if (str_to_uint(input[6], &rounds) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + buffer_t *salt, secret, *data; + salt = t_buffer_create(strlen(input[4])/2); + buffer_create_from_const_data(&secret, password, strlen(password)); + data = t_buffer_create(strlen(input[7])/2); + if (hex_to_binary(input[4], salt) != 0 || + hex_to_binary(input[7], data) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + if (!dcrypt_openssl_cipher_key_dovecot_v2(input[3], + DCRYPT_MODE_DECRYPT, data, &secret, salt, + input[5], rounds, key_data, error_r)) { + return FALSE; + } + } + + /* decode actual key */ + if (EVP_PKEY_type(nid) == EVP_PKEY_RSA) { + RSA *rsa = RSA_new(); + const unsigned char *ptr = buffer_get_data(key_data, NULL); + if (rsa == NULL || + d2i_RSAPrivateKey(&rsa, &ptr, key_data->used) == NULL || + RSA_check_key(rsa) != 1) { + safe_memset(buffer_get_modifiable_data(key_data, NULL), + 0, key_data->used); + RSA_free(rsa); + return dcrypt_openssl_error(error_r); + } + safe_memset(buffer_get_modifiable_data(key_data, NULL), + 0, key_data->used); + buffer_set_used_size(key_data, 0); + EVP_PKEY *pkey = EVP_PKEY_new(); + if (pkey == NULL) { + RSA_free(rsa); + return dcrypt_openssl_error(error_r); + } + EVP_PKEY_set1_RSA(pkey, rsa); + RSA_free(rsa); + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + } else { + int ec; + BIGNUM *point = BN_secure_new(); + if (point == NULL || + BN_mpi2bn(key_data->data, key_data->used, point) == NULL) { + safe_memset(buffer_get_modifiable_data(key_data, NULL), + 0, key_data->used); + BN_free(point); + return dcrypt_openssl_error(error_r); + } + EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); + safe_memset(buffer_get_modifiable_data(key_data, NULL), + 0, key_data->used); + buffer_set_used_size(key_data, 0); + BN_CTX *bnctx = BN_CTX_new(); + if (eckey == NULL || bnctx == NULL) { + BN_free(point); + EC_KEY_free(eckey); + BN_CTX_free(bnctx); + return dcrypt_openssl_error(error_r); + } + EC_KEY_set_private_key(eckey, point); + EC_KEY_precompute_mult(eckey, bnctx); + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(eckey)); + if (pub == NULL) + ec = -1; + else { + /* calculate public key */ + ec = EC_POINT_mul(EC_KEY_get0_group(eckey), pub, point, + NULL, NULL, bnctx); + EC_KEY_set_public_key(eckey, pub); + EC_POINT_free(pub); + } + BN_free(point); + BN_CTX_free(bnctx); + /* make sure the EC key is valid */ + EVP_PKEY *key = EVP_PKEY_new(); + if (ec == 1 && key != NULL && EC_KEY_check_key(eckey) == 1) { + EVP_PKEY_set1_EC_KEY(key, eckey); + EC_KEY_free(eckey); + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = key; + (*key_r)->ref++; + } else { + EVP_PKEY_free(key); + EC_KEY_free(eckey); + return dcrypt_openssl_error(error_r); + } + } + + /* finally compare key to key id */ + dcrypt_openssl_private_key_id(*key_r, "sha256", key_data, NULL); + + if (strcmp(binary_to_hex(key_data->data, key_data->used), + input[len-1]) != 0) { + dcrypt_openssl_unref_private_key(key_r); + DCRYPT_SET_ERROR("Key id mismatch after load"); + return FALSE; + } + + return TRUE; +} + +/* JWK Parameter names defined at https://www.iana.org/assignments/jose/jose.xhtml */ + +static const struct jwk_to_ssl_map_entry { + const char *jwk_curve; + int nid; +} jwk_to_ssl_curves[] = +{ + /* See https://tools.ietf.org/search/rfc8422#appendix-A */ + { .jwk_curve = "P-256", .nid = NID_X9_62_prime256v1 }, + { .jwk_curve = "P-384", .nid = NID_secp384r1 }, + { .jwk_curve = "P-521", .nid = NID_secp521r1 }, + { .jwk_curve = NULL, .nid = 0 } +}; + +static const char *key_usage_to_jwk_use(enum dcrypt_key_usage usage) +{ + switch(usage) { + case DCRYPT_KEY_USAGE_NONE: + return NULL; + case DCRYPT_KEY_USAGE_ENCRYPT: + return "enc"; + case DCRYPT_KEY_USAGE_SIGN: + return "sig"; + }; + i_unreached(); +} + +static enum dcrypt_key_usage jwk_use_to_key_usage(const char *use) +{ + if (strcmp(use, "enc") == 0) + return DCRYPT_KEY_USAGE_ENCRYPT; + if (strcmp(use, "sig") == 0) + return DCRYPT_KEY_USAGE_SIGN; + return DCRYPT_KEY_USAGE_NONE; +} + +static int jwk_curve_to_nid(const char *curve) +{ + /* use static mapping table to get correct input for OpenSSL */ + const struct jwk_to_ssl_map_entry *entry = jwk_to_ssl_curves; + for (;entry->jwk_curve != NULL;entry++) + if (strcmp(curve, entry->jwk_curve) == 0) + return entry->nid; + return 0; +} + +static const char *nid_to_jwk_curve(int nid) +{ + const struct jwk_to_ssl_map_entry *entry = jwk_to_ssl_curves; + for (;entry->jwk_curve != NULL;entry++) + if (nid == entry->nid) + return entry->jwk_curve; + return NULL; +} + +/* Loads both public and private key */ +static bool load_jwk_ec_key(EVP_PKEY **key_r, bool want_private_key, + const struct json_tree_node *root, + const char *password ATTR_UNUSED, + struct dcrypt_private_key *dec_key ATTR_UNUSED, + const char **error_r) +{ + i_assert(password == NULL && dec_key == NULL); + const char *crv, *x, *y, *d; + const struct json_tree_node *node; + + if ((node = json_tree_find_key(root, "crv")) == NULL || + (crv = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing crv parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "x")) == NULL || + (x = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing x parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "y")) == NULL || + (y = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing y parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "d")) == NULL || + (d = json_tree_get_value_str(node)) == NULL) { + if (want_private_key) { + DCRYPT_SET_ERROR("Missing d parameter"); + return FALSE; + } + } + + /* base64 decode x and y */ + buffer_t *bx = t_base64url_decode_str(x); + buffer_t *by = t_base64url_decode_str(y); + + /* determine NID */ + int nid = jwk_curve_to_nid(crv); + if (nid == 0) { + DCRYPT_SET_ERROR(t_strdup_printf("Unsupported curve: %s", crv)); + return FALSE; + } + /* create key */ + EC_KEY *ec_key = EC_KEY_new_by_curve_name(nid); + if (ec_key == NULL) { + DCRYPT_SET_ERROR("Cannot allocate memory"); + return FALSE; + } + + BIGNUM *px = BN_new(); + BIGNUM *py = BN_new(); + + if (BN_bin2bn(bx->data, bx->used, px) == NULL || + BN_bin2bn(by->data, by->used, py) == NULL) { + EC_KEY_free(ec_key); + BN_free(px); + BN_free(py); + return dcrypt_openssl_error(error_r); + } + + int ret = EC_KEY_set_public_key_affine_coordinates(ec_key, px, py); + BN_free(px); + BN_free(py); + + if (ret != 1) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + + /* FIXME: Support decryption */ + if (want_private_key) { + buffer_t *bd = t_base64url_decode_str(d); + BIGNUM *pd = BN_secure_new(); + if (BN_bin2bn(bd->data, bd->used, pd) == NULL) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + ret = EC_KEY_set_private_key(ec_key, pd); + BN_free(pd); + if (ret != 1) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + } + + if (EC_KEY_check_key(ec_key) != 1) { + EC_KEY_free(ec_key); + return dcrypt_openssl_error(error_r); + } + + EC_KEY_precompute_mult(ec_key, NULL); + EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE); + + /* return as EVP_PKEY */ + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_set1_EC_KEY(pkey, ec_key); + EC_KEY_free(ec_key); + *key_r = pkey; + + return TRUE; +} + +/* RSA helpers */ +#if !defined(HAVE_RSA_SET0_KEY) +static int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + if (n == NULL || e == NULL) { + RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + BN_free(r->n); + r->n = n; + BN_free(r->e); + r->e = e; + BN_free(r->d); + r->d = d; + return 1; +} +#endif +#if !defined(HAVE_RSA_SET0_FACTORS) +static int RSA_set0_factors(RSA *r, BIGNUM *p, BIGNUM *q) +{ + if (p == NULL || q == NULL) { + RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + BN_free(r->p); + r->p = p; + BN_free(r->q); + r->q = q; + return 1; +} +#endif +#if !defined(HAVE_RSA_SET0_CRT_PARAMS) +static int RSA_set0_crt_params(RSA *r, BIGNUM *dmp1, BIGNUM *dmq1, BIGNUM *iqmp) +{ + if (dmp1 == NULL || dmq1 == NULL || iqmp == NULL) { + RSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + BN_free(r->dmp1); + r->dmp1 = dmp1; + BN_free(r->dmq1); + r->dmq1 = dmq1; + BN_free(r->iqmp); + r->iqmp = iqmp; + return 1; +} +#endif + +/* Loads both public and private key */ +static bool load_jwk_rsa_key(EVP_PKEY **key_r, bool want_private_key, + const struct json_tree_node *root, + const char *password ATTR_UNUSED, + struct dcrypt_private_key *dec_key ATTR_UNUSED, + const char **error_r) +{ + const char *n, *e, *d = NULL, *p = NULL, *q = NULL, *dp = NULL; + const char *dq = NULL, *qi = NULL; + const struct json_tree_node *node; + + /* n and e must be present */ + if ((node = json_tree_find_key(root, "n")) == NULL || + (n = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing n parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "e")) == NULL || + (e = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing e parameter"); + return FALSE; + } + + if (want_private_key) { + if ((node = json_tree_find_key(root, "d")) == NULL || + (d = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing d parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "p")) == NULL || + (p = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing p parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "q")) == NULL || + (q = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing q parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "dp")) == NULL || + (dp = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing dp parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "dq")) == NULL || + (dq = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing dq parameter"); + return FALSE; + } + + if ((node = json_tree_find_key(root, "qi")) == NULL || + (qi = json_tree_get_value_str(node)) == NULL) { + DCRYPT_SET_ERROR("Missing qi parameter"); + return FALSE; + } + } + + /* convert into BIGNUMs */ + BIGNUM *pn, *pe, *pd, *pp, *pq, *pdp, *pdq, *pqi; + buffer_t *bn = t_base64url_decode_str(n); + buffer_t *be = t_base64url_decode_str(e); + if (want_private_key) { + pd = BN_secure_new(); + buffer_t *bd = t_base64url_decode_str(d); + if (BN_bin2bn(bd->data, bd->used, pd) == NULL) { + BN_free(pd); + return dcrypt_openssl_error(error_r); + } + } else { + pd = NULL; + } + + pn = BN_new(); + pe = BN_new(); + + if (BN_bin2bn(bn->data, bn->used, pn) == NULL || + BN_bin2bn(be->data, be->used, pe) == NULL) { + if (pd != NULL) + BN_free(pd); + BN_free(pn); + BN_free(pe); + return dcrypt_openssl_error(error_r); + } + + RSA *rsa_key = RSA_new(); + if (rsa_key == NULL) { + if (pd != NULL) + BN_free(pd); + BN_free(pn); + BN_free(pe); + return dcrypt_openssl_error(error_r); + } + + if (RSA_set0_key(rsa_key, pn, pe, pd) != 1) { + if (pd != NULL) + BN_free(pd); + BN_free(pn); + BN_free(pe); + RSA_free(rsa_key); + return dcrypt_openssl_error(error_r); + } + + if (want_private_key) { + pp = BN_secure_new(); + pq = BN_secure_new(); + pdp = BN_secure_new(); + pdq = BN_secure_new(); + pqi = BN_secure_new(); + + buffer_t *bp = t_base64url_decode_str(p); + buffer_t *bq = t_base64url_decode_str(q); + buffer_t *bdp = t_base64url_decode_str(dp); + buffer_t *bdq = t_base64url_decode_str(dq); + buffer_t *bqi = t_base64url_decode_str(qi); + + if (BN_bin2bn(bp->data, bp->used, pp) == NULL || + BN_bin2bn(bq->data, bq->used, pq) == NULL || + BN_bin2bn(bdp->data, bdp->used, pdp) == NULL || + BN_bin2bn(bdq->data, bdq->used, pdq) == NULL || + BN_bin2bn(bqi->data, bqi->used, pqi) == NULL || + RSA_set0_factors(rsa_key, pp, pq) != 1) { + RSA_free(rsa_key); + BN_free(pp); + BN_free(pq); + BN_free(pdp); + BN_free(pdq); + BN_free(pqi); + return dcrypt_openssl_error(error_r); + } else if (RSA_set0_crt_params(rsa_key, pdp, pdq, pqi) != 1) { + RSA_free(rsa_key); + BN_free(pdp); + BN_free(pdq); + BN_free(pqi); + return dcrypt_openssl_error(error_r); + } + } + + /* return as EVP_PKEY */ + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_set1_RSA(pkey, rsa_key); + RSA_free(rsa_key); + *key_r = pkey; + + return TRUE; +} + + +static bool +dcrypt_openssl_load_private_key_jwk(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r) +{ + const char *kty; + const char *error; + const struct json_tree_node *root, *node; + struct json_tree *key_tree; + EVP_PKEY *pkey; + bool ret; + + if (parse_jwk_key(data, &key_tree, &error) != 0) { + DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK private key: %s", + error)); + return FALSE; + } + + root = json_tree_root(key_tree); + + /* check key type */ + if ((node = json_tree_find_key(root, "kty")) == NULL) { + DCRYPT_SET_ERROR("Cannot load JWK private key: no kty parameter"); + json_tree_deinit(&key_tree); + return FALSE; + } + + kty = json_tree_get_value_str(node); + + if (null_strcmp(kty, "EC") == 0) { + ret = load_jwk_ec_key(&pkey, TRUE, root, password, dec_key, &error); + } else if (strcmp(kty, "RSA") == 0) { + ret = load_jwk_rsa_key(&pkey, TRUE, root, password, dec_key, &error); + } else { + error = "Unsupported key type"; + ret = FALSE; + } + + i_assert(ret || error != NULL); + + if (!ret) + DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK private key: %s", error)); + else if (ret) { + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + /* check if kid is present */ + if ((node = json_tree_find_key(root, "kid")) != NULL) + (*key_r)->key_id = i_strdup_empty(json_tree_get_value_str(node)); + /* check if use is present */ + if ((node = json_tree_find_key(root, "use")) != NULL) + (*key_r)->usage = jwk_use_to_key_usage(json_tree_get_value_str(node)); + } + + json_tree_deinit(&key_tree); + + return ret; +} + +static bool +dcrypt_openssl_load_public_key_jwk(struct dcrypt_public_key **key_r, + const char *data, const char **error_r) +{ + const char *kty; + const char *error; + const struct json_tree_node *root, *node; + struct json_tree *key_tree; + EVP_PKEY *pkey; + bool ret; + + if (parse_jwk_key(data, &key_tree, &error) != 0) { + DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK public key: %s", + error)); + return FALSE; + } + + root = json_tree_root(key_tree); + + /* check key type */ + if ((node = json_tree_find_key(root, "kty")) == NULL) { + DCRYPT_SET_ERROR("Cannot load JWK public key: no kty parameter"); + json_tree_deinit(&key_tree); + return FALSE; + } + + kty = json_tree_get_value_str(node); + + if (null_strcmp(kty, "EC") == 0) { + ret = load_jwk_ec_key(&pkey, FALSE, root, NULL, NULL, &error); + } else if (strcmp(kty, "RSA") == 0) { + ret = load_jwk_rsa_key(&pkey, FALSE, root, NULL, NULL, &error); + } else { + error = "Unsupported key type"; + ret = FALSE; + } + + i_assert(ret || error != NULL); + + if (!ret) + DCRYPT_SET_ERROR(t_strdup_printf("Cannot load JWK public key: %s", error)); + else if (ret) { + *key_r = i_new(struct dcrypt_public_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + /* check if kid is present */ + if ((node = json_tree_find_key(root, "kid")) != NULL) + (*key_r)->key_id = i_strdup_empty(json_tree_get_value_str(node)); + /* check if use is present */ + if ((node = json_tree_find_key(root, "use")) != NULL) + (*key_r)->usage = jwk_use_to_key_usage(json_tree_get_value_str(node)); + } + + json_tree_deinit(&key_tree); + + return ret; +} + + +static int bn2base64url(const BIGNUM *bn, string_t *dest) +{ + int len = BN_num_bytes(bn); + unsigned char *data = t_malloc_no0(len); + if (BN_bn2bin(bn, data) != len) + return -1; + base64url_encode(BASE64_ENCODE_FLAG_NO_PADDING, SIZE_MAX, data, len, dest); + return 0; +} + +/* FIXME: Add encryption support */ +/* FIXME: Add support for 'algo' field */ +static bool store_jwk_ec_key(EVP_PKEY *pkey, bool is_private_key, + enum dcrypt_key_usage usage, + const char *key_id, + const char *cipher ATTR_UNUSED, + const char *password ATTR_UNUSED, + struct dcrypt_public_key *enc_key ATTR_UNUSED, + string_t *dest, const char **error_r) +{ + i_assert(cipher == NULL && password == NULL && enc_key == NULL); + string_t *temp = t_str_new(256); + const EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + i_assert(ec_key != NULL); + + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(ec_key)); + const EC_POINT *public_point = EC_KEY_get0_public_key(ec_key); + BIGNUM *x, *y; + + x = BN_new(); + y = BN_new(); + if (EC_POINT_get_affine_coordinates_GFp(EC_KEY_get0_group(ec_key), public_point, + x, y, NULL) != 1) { + BN_free(x); + BN_free(y); + return dcrypt_openssl_error(error_r); + } + + const char *curve = nid_to_jwk_curve(nid); + const char *use = key_usage_to_jwk_use(usage); + + str_printfa(temp, "{\"kty\":\"EC\",\"crv\":\"%s\"", curve); + str_append(temp, ",\"x\":\""); + bn2base64url(x, temp); + str_append(temp, "\",\"y\":\""); + bn2base64url(y, temp); + + if (use != NULL) { + str_append(temp, "\",\"use\":\""); + json_append_escaped(temp, use); + } + if (key_id != NULL) { + str_append(temp, "\",\"kid\":\""); + json_append_escaped(temp, key_id); + } + BN_free(x); + BN_free(y); + + if (is_private_key) { + const BIGNUM *d = EC_KEY_get0_private_key(ec_key); + if (d == NULL) { + DCRYPT_SET_ERROR("No private key available"); + return FALSE; + } + str_append(temp, "\",\"d\":\""); + bn2base64url(d, temp); + } + str_append(temp, "\"}"); + str_append_str(dest, temp); + return TRUE; +} + +/* FIXME: Add RSA support */ + +static bool store_jwk_key(EVP_PKEY *pkey, bool is_private_key, + enum dcrypt_key_usage usage, + const char *key_id, + const char *cipher, + const char *password, + struct dcrypt_public_key *enc_key, + string_t *dest, const char **error_r) +{ + i_assert(cipher == NULL && password == NULL && enc_key == NULL); + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { + return store_jwk_ec_key(pkey, is_private_key, usage, key_id, + cipher, password, enc_key, dest, error_r); + } + DCRYPT_SET_ERROR("Unsupported key type"); + return FALSE; +} + +static bool +dcrypt_openssl_load_private_key_dovecot(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *key, + enum dcrypt_key_version version, + const char **error_r) +{ + const char **input = t_strsplit(data, ":\t"); + size_t len = str_array_length(input); + + switch (version) { + case DCRYPT_KEY_VERSION_1: + return dcrypt_openssl_load_private_key_dovecot_v1( + key_r, len, input, password, key, error_r); + case DCRYPT_KEY_VERSION_2: + return dcrypt_openssl_load_private_key_dovecot_v2( + key_r, len, input, password, key, error_r); + case DCRYPT_KEY_VERSION_NA: + i_unreached(); + } + return FALSE; +} + +static bool +dcrypt_openssl_load_public_key_dovecot_v1(struct dcrypt_public_key **key_r, + int len, const char **input, + const char **error_r) +{ + int nid; + if (str_to_int(input[1], &nid) != 0) { + DCRYPT_SET_ERROR("Corrupted data"); + return FALSE; + } + + EC_KEY *eckey = EC_KEY_new_by_curve_name(nid); + if (eckey == NULL) { + dcrypt_openssl_error(error_r); + return FALSE; + } + + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + BN_CTX *bnctx = BN_CTX_new(); + + EC_POINT *point = EC_POINT_new(EC_KEY_get0_group(eckey)); + if (bnctx == NULL || point == NULL || + EC_POINT_hex2point(EC_KEY_get0_group(eckey), + input[2], point, bnctx) == NULL) { + BN_CTX_free(bnctx); + EC_KEY_free(eckey); + EC_POINT_free(point); + dcrypt_openssl_error(error_r); + return FALSE; + } + BN_CTX_free(bnctx); + + EC_KEY_set_public_key(eckey, point); + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + + EC_POINT_free(point); + + if (EC_KEY_check_key(eckey) == 1) { + EVP_PKEY *key = EVP_PKEY_new(); + EVP_PKEY_set1_EC_KEY(key, eckey); + EC_KEY_free(eckey); + /* make sure digest matches */ + buffer_t *dgst = t_buffer_create(32); + struct dcrypt_public_key tmp; + i_zero(&tmp); + tmp.key = key; + dcrypt_openssl_public_key_id_old(&tmp, dgst, NULL); + if (strcmp(binary_to_hex(dgst->data, dgst->used), + input[len-1]) != 0) { + DCRYPT_SET_ERROR("Key id mismatch after load"); + EVP_PKEY_free(key); + return FALSE; + } + *key_r = i_new(struct dcrypt_public_key, 1); + (*key_r)->key = key; + (*key_r)->ref++; + return TRUE; + } + + dcrypt_openssl_error(error_r); + return FALSE; +} + +static bool +dcrypt_openssl_load_public_key_dovecot_v2(struct dcrypt_public_key **key_r, + int len, const char **input, + const char **error_r) +{ + buffer_t tmp; + size_t keylen = strlen(input[1])/2; + unsigned char keybuf[keylen]; + const unsigned char *ptr; + buffer_create_from_data(&tmp, keybuf, keylen); + hex_to_binary(input[1], &tmp); + ptr = keybuf; + + EVP_PKEY *pkey = EVP_PKEY_new(); + if (pkey == NULL || d2i_PUBKEY(&pkey, &ptr, keylen)==NULL) { + EVP_PKEY_free(pkey); + dcrypt_openssl_error(error_r); + return FALSE; + } + + /* make sure digest matches */ + buffer_t *dgst = t_buffer_create(32); + struct dcrypt_public_key tmpkey; + i_zero(&tmpkey); + tmpkey.key = pkey; + dcrypt_openssl_public_key_id(&tmpkey, "sha256", dgst, NULL); + if (strcmp(binary_to_hex(dgst->data, dgst->used), input[len-1]) != 0) { + DCRYPT_SET_ERROR("Key id mismatch after load"); + EVP_PKEY_free(pkey); + return FALSE; + } + + *key_r = i_new(struct dcrypt_public_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + return TRUE; +} + +static bool +dcrypt_openssl_load_public_key_dovecot(struct dcrypt_public_key **key_r, + const char *data, + enum dcrypt_key_version version, + const char **error_r) +{ + const char **input = t_strsplit(data, ":\t"); + size_t len = str_array_length(input); + + switch (version) { + case DCRYPT_KEY_VERSION_1: + return dcrypt_openssl_load_public_key_dovecot_v1( + key_r, len, input, error_r); + break; + case DCRYPT_KEY_VERSION_2: + return dcrypt_openssl_load_public_key_dovecot_v2( + key_r, len, input, error_r); + break; + case DCRYPT_KEY_VERSION_NA: + i_unreached(); + } + return FALSE; +} + +static bool +dcrypt_openssl_encrypt_private_key_dovecot(buffer_t *key, int enctype, + const char *cipher, + const char *password, + struct dcrypt_public_key *enc_key, + buffer_t *destination, + const char **error_r) +{ + bool res; + unsigned char *ptr; + + unsigned char salt[8]; + buffer_t *peer_key = t_buffer_create(128); + buffer_t *secret = t_buffer_create(128); + cipher = t_str_lcase(cipher); + + str_append(destination, cipher); + str_append_c(destination, ':'); + random_fill(salt, sizeof(salt)); + binary_to_hex_append(destination, salt, sizeof(salt)); + buffer_t saltbuf; + buffer_create_from_const_data(&saltbuf, salt, sizeof(salt)); + + /* so we don't have to make new version if we ever upgrade these */ + str_append(destination, t_strdup_printf(":%s:%d:", + DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, + DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS)); + + if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { + if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_RSA) { + size_t used = buffer_get_used_size(secret); + /* peer key, in this case, is encrypted secret, + which is 16 bytes of data */ + ptr = buffer_append_space_unsafe(secret, 16); + random_fill(ptr, 16); + buffer_set_used_size(secret, used+16); + if (!dcrypt_rsa_encrypt(enc_key, secret->data, + secret->used, peer_key, + DCRYPT_PADDING_RSA_PKCS1_OAEP, + error_r)) { + return FALSE; + } + } else if (EVP_PKEY_base_id(enc_key->key) == EVP_PKEY_EC) { + /* generate secret by ECDHE */ + if (!dcrypt_openssl_ecdh_derive_secret_peer( + enc_key, peer_key, secret, error_r)) { + return FALSE; + } + } else { + /* Loading the key should have failed */ + i_unreached(); + } + /* add encryption key id, reuse peer_key buffer */ + } else if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD) { + str_append(secret, password); + } + + /* encrypt key using secret and salt */ + buffer_t *tmp = t_buffer_create(128); + res = dcrypt_openssl_cipher_key_dovecot_v2(cipher, + DCRYPT_MODE_ENCRYPT, key, secret, &saltbuf, + DCRYPT_DOVECOT_KEY_ENCRYPT_HASH, + DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS, tmp, error_r); + safe_memset(buffer_get_modifiable_data(secret, NULL), 0, secret->used); + binary_to_hex_append(destination, tmp->data, tmp->used); + + /* some additional fields or private key version */ + if (enctype == DCRYPT_DOVECOT_KEY_ENCRYPT_PK) { + str_append_c(destination, ':'); + + /* for RSA, this is the actual encrypted secret */ + binary_to_hex_append(destination, + peer_key->data, peer_key->used); + str_append_c(destination, ':'); + + buffer_set_used_size(peer_key, 0); + if (!dcrypt_openssl_public_key_id(enc_key, "sha256", + peer_key, error_r)) + return FALSE; + binary_to_hex_append(destination, + peer_key->data, peer_key->used); + } + return res; +} + +static bool +dcrypt_openssl_store_private_key_dovecot(struct dcrypt_private_key *key, + const char *cipher, + buffer_t *destination, + const char *password, + struct dcrypt_public_key *enc_key, + const char **error_r) +{ + size_t dest_used = buffer_get_used_size(destination); + const char *cipher2 = NULL; + EVP_PKEY *pkey = key->key; + char objtxt[OID_TEXT_MAX_LEN]; + ASN1_OBJECT *obj; + + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { + /* because otherwise we get wrong nid */ + obj = OBJ_nid2obj(EC_GROUP_get_curve_name( + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))); + EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), + POINT_CONVERSION_COMPRESSED); + + } else { + obj = OBJ_nid2obj(EVP_PKEY_id(pkey)); + } + + int enctype = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; + int len = OBJ_obj2txt(objtxt, sizeof(objtxt), obj, 1); + if (len < 1) + return dcrypt_openssl_error(error_r); + if (len > (int)sizeof(objtxt)) { + DCRYPT_SET_ERROR("Object identifier too long"); + return FALSE; + } + + buffer_t *buf = t_buffer_create(256); + + /* convert key to private key value */ + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) { + unsigned char *ptr; + RSA *rsa = EVP_PKEY_get0_RSA(pkey); + int len = i2d_RSAPrivateKey(rsa, &ptr); + if (len < 1) + return dcrypt_openssl_error(error_r); + buffer_append(buf, ptr, len); + } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { + unsigned char *ptr; + EC_KEY *eckey = EVP_PKEY_get0_EC_KEY(pkey); + const BIGNUM *pk = EC_KEY_get0_private_key(eckey); + /* serialize to MPI which is portable */ + int len = BN_bn2mpi(pk, NULL); + ptr = buffer_append_space_unsafe(buf, len); + BN_bn2mpi(pk, ptr); + } else { + /* Loading the key should have failed */ + i_unreached(); + } + + /* see if we want ECDH based or password based encryption */ + if (cipher != NULL && strncasecmp(cipher, "ecdh-", 5) == 0) { + i_assert(enc_key != NULL); + i_assert(password == NULL); + enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PK; + cipher2 = cipher+5; + } else if (cipher != NULL) { + i_assert(enc_key == NULL); + i_assert(password != NULL); + enctype = DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD; + cipher2 = cipher; + } else if (enctype == DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { + i_assert(enc_key == NULL && password == NULL); + } + + /* put in OID and encryption type */ + str_append(destination, t_strdup_printf("2:%s:%d:", + objtxt, enctype)); + + /* perform encryption if desired */ + if (enctype != DCRYPT_KEY_ENCRYPTION_TYPE_NONE) { + if (!dcrypt_openssl_encrypt_private_key_dovecot(buf, + enctype, cipher2, password, enc_key, destination, + error_r)) { + buffer_set_used_size(destination, dest_used); + return FALSE; + } + } else { + binary_to_hex_append(destination, buf->data, buf->used); + } + + /* append public key id */ + str_append_c(destination, ':'); + buffer_set_used_size(buf, 0); + bool res = dcrypt_openssl_private_key_id(key, "sha256", buf, error_r); + binary_to_hex_append(destination, buf->data, buf->used); + + if (!res) { + /* well, that didn't end well */ + buffer_set_used_size(destination, dest_used); + return FALSE; + } + return TRUE; +} + +static bool +dcrypt_openssl_store_public_key_dovecot(struct dcrypt_public_key *key, + buffer_t *destination, + const char **error_r) +{ + EVP_PKEY *pubkey = key->key; + unsigned char *tmp = NULL; + size_t dest_used = buffer_get_used_size(destination); + + if (EVP_PKEY_base_id(pubkey) == EVP_PKEY_EC) + EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pubkey), + POINT_CONVERSION_COMPRESSED); + int rv = i2d_PUBKEY(pubkey, &tmp); + + if (tmp == NULL) + return dcrypt_openssl_error(error_r); + + /* then store it */ + str_append_c(destination, '2'); + str_append_c(destination, ':'); + binary_to_hex_append(destination, tmp, rv); + OPENSSL_free(tmp); + + /* append public key ID */ + str_append_c(destination, ':'); + + buffer_t *buf = t_buffer_create(32); + bool res = dcrypt_openssl_public_key_id(key, "sha256", buf, error_r); + + if (!res) { + buffer_set_used_size(destination, dest_used); + return FALSE; + } + + str_append(destination, binary_to_hex(buf->data, buf->used)); + return TRUE; +} + +static bool +dcrypt_openssl_load_private_key(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r) +{ + i_assert(key_r != NULL); + + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + if (!dcrypt_openssl_key_string_get_info(data, &format, &version, + &kind, NULL, NULL, NULL, error_r)) { + return FALSE; + } + if (kind != DCRYPT_KEY_KIND_PRIVATE) { + DCRYPT_SET_ERROR("key is not private"); + return FALSE; + } + + if (format == DCRYPT_FORMAT_JWK) + return dcrypt_openssl_load_private_key_jwk(key_r, data, password, + dec_key, error_r); + + if (format == DCRYPT_FORMAT_DOVECOT) + return dcrypt_openssl_load_private_key_dovecot(key_r, data, + password, dec_key, version, error_r); + + EVP_PKEY *key = NULL, *key2; + + BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); + + key = EVP_PKEY_new(); + + key2 = PEM_read_bio_PrivateKey(key_in, &key, NULL, (void*)password); + + BIO_vfree(key_in); + + if (key2 == NULL) { + EVP_PKEY_free(key); + return dcrypt_openssl_error(error_r); + } + + if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { + EC_KEY_set_asn1_flag(EVP_PKEY_get0_EC_KEY(key), + OPENSSL_EC_NAMED_CURVE); + } + + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = key; + (*key_r)->ref++; + + return TRUE; +} + +static bool +dcrypt_openssl_load_public_key(struct dcrypt_public_key **key_r, + const char *data, const char **error_r) +{ + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + i_assert(key_r != NULL); + + if (!dcrypt_openssl_key_string_get_info(data, &format, &version, + &kind, NULL, NULL, NULL, + error_r)) { + return FALSE; + } + /* JWK private keys can be loaded as public */ + if (kind != DCRYPT_KEY_KIND_PUBLIC && format != DCRYPT_FORMAT_JWK) { + DCRYPT_SET_ERROR("key is not public"); + return FALSE; + } + + if (format == DCRYPT_FORMAT_JWK) + return dcrypt_openssl_load_public_key_jwk(key_r, data, error_r); + + if (format == DCRYPT_FORMAT_DOVECOT) + return dcrypt_openssl_load_public_key_dovecot(key_r, data, + version, error_r); + + EVP_PKEY *key = NULL; + BIO *key_in = BIO_new_mem_buf((void*)data, strlen(data)); + if (key_in == NULL) + return dcrypt_openssl_error(error_r); + + key = PEM_read_bio_PUBKEY(key_in, &key, NULL, NULL); + if (BIO_reset(key_in) <= 0) + i_unreached(); + if (key == NULL) { /* ec keys are bother */ + /* read the header */ + char buf[27]; /* begin public key */ + if (BIO_gets(key_in, buf, sizeof(buf)) != 1) { + BIO_vfree(key_in); + return dcrypt_openssl_error(error_r); + } + if (strcmp(buf, "-----BEGIN PUBLIC KEY-----") != 0) { + DCRYPT_SET_ERROR("Missing public key header"); + return FALSE; + } + BIO *b64 = BIO_new(BIO_f_base64()); + if (b64 == NULL) { + BIO_vfree(key_in); + return dcrypt_openssl_error(error_r); + } + EC_KEY *eckey = d2i_EC_PUBKEY_bio(b64, NULL); + if (eckey != NULL) { + EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE); + key = EVP_PKEY_new(); + if (key != NULL) + EVP_PKEY_set1_EC_KEY(key, eckey); + EC_KEY_free(eckey); + } + } + + BIO_vfree(key_in); + + if (key == NULL) + return dcrypt_openssl_error(error_r); + + *key_r = i_new(struct dcrypt_public_key, 1); + (*key_r)->key = key; + (*key_r)->ref++; + + return TRUE; +} + +static bool +dcrypt_openssl_store_private_key(struct dcrypt_private_key *key, + enum dcrypt_key_format format, + const char *cipher, buffer_t *destination, + const char *password, + struct dcrypt_public_key *enc_key, + const char **error_r) +{ + i_assert(key != NULL && key->key != NULL); + + int ec; + if (format == DCRYPT_FORMAT_DOVECOT) { + bool ret; + ret = dcrypt_openssl_store_private_key_dovecot( + key, cipher, destination, password, enc_key, error_r); + return ret; + } + + EVP_PKEY *pkey = key->key; + + if (format == DCRYPT_FORMAT_JWK) { + bool ret; + ret = store_jwk_key(pkey, TRUE, key->usage, key->key_id, + cipher, password, enc_key, + destination, error_r); + return ret; + } + + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) + EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), + POINT_CONVERSION_UNCOMPRESSED); + + BIO *key_out = BIO_new(BIO_s_mem()); + if (key_out == NULL) + return dcrypt_openssl_error(error_r); + + const EVP_CIPHER *algo = NULL; + if (cipher != NULL) { + algo = EVP_get_cipherbyname(cipher); + if (algo == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Invalid cipher %s", + cipher)); + return FALSE; + } + } + + ec = PEM_write_bio_PrivateKey(key_out, pkey, algo, + NULL, 0, NULL, (void*)password); + + if (BIO_flush(key_out) <= 0) + ec = -1; + + if (ec != 1) { + BIO_vfree(key_out); + return dcrypt_openssl_error(error_r); + } + + long bs; + char *buf; + bs = BIO_get_mem_data(key_out, &buf); + buffer_append(destination, buf, bs); + BIO_vfree(key_out); + + return TRUE; +} + +static bool +dcrypt_openssl_store_public_key(struct dcrypt_public_key *key, + enum dcrypt_key_format format, + buffer_t *destination, const char **error_r) +{ + int ec; + + i_assert(key != NULL && key->key != NULL); + + if (format == DCRYPT_FORMAT_DOVECOT) { + return dcrypt_openssl_store_public_key_dovecot(key, destination, + error_r); + } + + EVP_PKEY *pkey = key->key; + + if (format == DCRYPT_FORMAT_JWK) { + bool ret; + ret = store_jwk_key(pkey, FALSE, key->usage, key->key_id, + NULL, NULL, NULL, + destination, error_r); + return ret; + } + + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) + EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(pkey), + POINT_CONVERSION_UNCOMPRESSED); + + BIO *key_out = BIO_new(BIO_s_mem()); + if (key_out == NULL) + return dcrypt_openssl_error(error_r); + + BIO *b64; + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) + ec = PEM_write_bio_PUBKEY(key_out, pkey); + else if ((b64 = BIO_new(BIO_f_base64())) == NULL) + ec = -1; + else { + (void)BIO_puts(key_out, "-----BEGIN PUBLIC KEY-----\n"); + (void)BIO_push(b64, key_out); + ec = i2d_EC_PUBKEY_bio(b64, EVP_PKEY_get0_EC_KEY(pkey)); + if (BIO_flush(b64) <= 0) + ec = -1; + (void)BIO_pop(b64); + BIO_vfree(b64); + if (BIO_puts(key_out, "-----END PUBLIC KEY-----") <= 0) + ec = -1; + } + + if (ec != 1) { + BIO_vfree(key_out); + return dcrypt_openssl_error(error_r); + } + + long bs; + char *buf; + bs = BIO_get_mem_data(key_out, &buf); + buffer_append(destination, buf, bs); + BIO_vfree(key_out); + + return TRUE; +} + +static void +dcrypt_openssl_private_to_public_key(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key **pub_key_r) +{ + i_assert(priv_key != NULL && pub_key_r != NULL); + + EVP_PKEY *pkey = priv_key->key; + EVP_PKEY *pk; + + pk = EVP_PKEY_new(); + i_assert(pk != NULL); /* we shouldn't get malloc() failures */ + + if (EVP_PKEY_base_id(pkey) == EVP_PKEY_RSA) + { + RSA *rsa = RSAPublicKey_dup(EVP_PKEY_get0_RSA(pkey)); + EVP_PKEY_set1_RSA(pk, rsa); + RSA_free(rsa); + } else if (EVP_PKEY_base_id(pkey) == EVP_PKEY_EC) { + EC_KEY* eck = EVP_PKEY_get1_EC_KEY(pkey); + EC_KEY_set_asn1_flag(eck, OPENSSL_EC_NAMED_CURVE); + EVP_PKEY_set1_EC_KEY(pk, eck); + EC_KEY_free(eck); + } else { + /* Loading the key should have failed */ + i_unreached(); + } + + *pub_key_r = i_new(struct dcrypt_public_key, 1); + (*pub_key_r)->key = pk; + (*pub_key_r)->ref++; +} + +static bool +dcrypt_openssl_key_string_get_info( + const char *key_data, enum dcrypt_key_format *format_r, + enum dcrypt_key_version *version_r, enum dcrypt_key_kind *kind_r, + enum dcrypt_key_encryption_type *encryption_type_r, + const char **encryption_key_hash_r, const char **key_hash_r, + const char **error_r) +{ + enum dcrypt_key_format format = DCRYPT_FORMAT_PEM; + enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; + enum dcrypt_key_encryption_type encryption_type = + DCRYPT_KEY_ENCRYPTION_TYPE_NONE; + enum dcrypt_key_kind kind = DCRYPT_KEY_KIND_PUBLIC; + char *encryption_key_hash = NULL; + char *key_hash = NULL; + + i_assert(key_data != NULL); + + /* is it PEM key */ + if (str_begins(key_data, "-----BEGIN ")) { + format = DCRYPT_FORMAT_PEM; + version = DCRYPT_KEY_VERSION_NA; + key_data += 11; + if (str_begins(key_data, "RSA ")) { + DCRYPT_SET_ERROR("RSA private key format not supported, convert it to PKEY format with openssl pkey"); + return FALSE; + } + if (str_begins(key_data, "ENCRYPTED ")) { + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; + key_data += 10; + } + if (str_begins(key_data, "PRIVATE KEY-----")) + kind = DCRYPT_KEY_KIND_PRIVATE; + else if (str_begins(key_data, "PUBLIC KEY-----")) + kind = DCRYPT_KEY_KIND_PUBLIC; + else { + DCRYPT_SET_ERROR("Unknown/invalid PEM key type"); + return FALSE; + } + } else if (*key_data == '{') { + /* possibly a JWK key */ + format = DCRYPT_FORMAT_JWK; + version = DCRYPT_KEY_VERSION_NA; + struct json_tree *tree; + const struct json_tree_node *root, *node; + const char *value, *error; + if (parse_jwk_key(key_data, &tree, &error) != 0) { + DCRYPT_SET_ERROR("Unknown/invalid key data"); + return FALSE; + } + + /* determine key type */ + root = json_tree_root(tree); + if ((node = json_tree_find_key(root, "kty")) == NULL || + (value = json_tree_get_value_str(node)) == NULL) { + json_tree_deinit(&tree); + DCRYPT_SET_ERROR("Invalid JWK key: Missing kty parameter"); + return FALSE; + } else if (strcmp(value, "RSA") == 0) { + if (json_tree_find_key(root, "d") != NULL) + kind = DCRYPT_KEY_KIND_PRIVATE; + else + kind = DCRYPT_KEY_KIND_PUBLIC; + } else if (strcmp(value, "EC") == 0) { + if (json_tree_find_key(root, "d") != NULL) + kind = DCRYPT_KEY_KIND_PRIVATE; + else + kind = DCRYPT_KEY_KIND_PUBLIC; + } else { + json_tree_deinit(&tree); + DCRYPT_SET_ERROR("Unsupported JWK key type"); + return FALSE; + } + json_tree_deinit(&tree); + } else { + if (str_begins(key_data, "1:")) { + DCRYPT_SET_ERROR("Dovecot v1 key format uses tab to separate fields"); + return FALSE; + } else if (str_begins(key_data, "2\t")) { + DCRYPT_SET_ERROR("Dovecot v2 key format uses colon to separate fields"); + return FALSE; + } + const char **fields = t_strsplit(key_data, ":\t"); + int nfields = str_array_length(fields); + + if (nfields < 2) { + DCRYPT_SET_ERROR("Unknown key format"); + return FALSE; + } + + format = DCRYPT_FORMAT_DOVECOT; + + /* field 1 - version */ + if (strcmp(fields[0], "1") == 0) { + version = DCRYPT_KEY_VERSION_1; + if (nfields == 4) { + kind = DCRYPT_KEY_KIND_PUBLIC; + } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; + } else if (nfields == 6 && strcmp(fields[2],"2") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; + } else if (nfields == 7 && strcmp(fields[2],"1") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; + if (encryption_key_hash_r != NULL) + encryption_key_hash = i_strdup(fields[nfields-2]); + } else { + DCRYPT_SET_ERROR("Invalid dovecot v1 encoding"); + return FALSE; + } + } else if (strcmp(fields[0], "2") == 0) { + version = DCRYPT_KEY_VERSION_2; + if (nfields == 3) { + kind = DCRYPT_KEY_KIND_PUBLIC; + } else if (nfields == 5 && strcmp(fields[2],"0") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_NONE; + } else if (nfields == 9 && strcmp(fields[2],"2") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD; + } else if (nfields == 11 && strcmp(fields[2],"1") == 0) { + kind = DCRYPT_KEY_KIND_PRIVATE; + encryption_type = DCRYPT_KEY_ENCRYPTION_TYPE_KEY; + if (encryption_key_hash_r != NULL) + encryption_key_hash = i_strdup(fields[nfields-2]); + } else { + DCRYPT_SET_ERROR("Invalid dovecot v2 encoding"); + return FALSE; + } + } else { + DCRYPT_SET_ERROR("Invalid dovecot key version"); + return FALSE; + } + + /* last field is always key hash */ + if (key_hash_r != NULL) + key_hash = i_strdup(fields[nfields-1]); + } + + if (format_r != NULL) *format_r = format; + if (version_r != NULL) *version_r = version; + if (encryption_type_r != NULL) *encryption_type_r = encryption_type; + if (encryption_key_hash_r != NULL) { + *encryption_key_hash_r = t_strdup(encryption_key_hash); + i_free(encryption_key_hash); + } + if (kind_r != NULL) *kind_r = kind; + if (key_hash_r != NULL) { + *key_hash_r = t_strdup(key_hash); + i_free(key_hash); + } + return TRUE; +} + +static void dcrypt_openssl_ref_public_key(struct dcrypt_public_key *key) +{ + i_assert(key != NULL && key->ref > 0); + key->ref++; +} + +static void dcrypt_openssl_ref_private_key(struct dcrypt_private_key *key) +{ + i_assert(key != NULL && key->ref > 0); + key->ref++; +} + +static void dcrypt_openssl_unref_public_key(struct dcrypt_public_key **key) +{ + i_assert(key != NULL); + struct dcrypt_public_key *_key = *key; + if (_key == NULL) + return; + i_assert(_key->ref > 0); + *key = NULL; + if (--_key->ref > 0) return; + EVP_PKEY_free(_key->key); + i_free(_key->key_id); + i_free(_key); +} + +static void dcrypt_openssl_unref_private_key(struct dcrypt_private_key **key) +{ + i_assert(key != NULL); + struct dcrypt_private_key *_key = *key; + if (_key == NULL) + return; + i_assert(_key->ref > 0); + *key = NULL; + if (--_key->ref > 0) return; + EVP_PKEY_free(_key->key); + i_free(_key->key_id); + i_free(_key); +} + +static void dcrypt_openssl_unref_keypair(struct dcrypt_keypair *keypair) +{ + i_assert(keypair != NULL); + dcrypt_openssl_unref_public_key(&keypair->pub); + dcrypt_openssl_unref_private_key(&keypair->priv); +} + +static bool +dcrypt_openssl_rsa_encrypt(struct dcrypt_public_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r) +{ + i_assert(key != NULL && key->key != NULL); + int ec, pad = dcrypt_openssl_padding_mode(padding, FALSE, error_r); + if (pad == -1) + return FALSE; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); + size_t outl = EVP_PKEY_size(key->key); + unsigned char buf[outl]; + + if (ctx == NULL || + EVP_PKEY_encrypt_init(ctx) < 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, pad) < 1 || + EVP_PKEY_encrypt(ctx, buf, &outl, data, data_len) < 1) { + dcrypt_openssl_error(error_r); + ec = -1; + } else { + buffer_append(result, buf, outl); + ec = 0; + } + + EVP_PKEY_CTX_free(ctx); + + return ec == 0; +} + +static bool +dcrypt_openssl_rsa_decrypt(struct dcrypt_private_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r) +{ + i_assert(key != NULL && key->key != NULL); + int ec, pad = dcrypt_openssl_padding_mode(padding, FALSE, error_r); + if (pad == -1) + return FALSE; + EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(key->key, NULL); + size_t outl = EVP_PKEY_size(key->key); + unsigned char buf[outl]; + + if (ctx == NULL || + EVP_PKEY_decrypt_init(ctx) < 1 || + EVP_PKEY_CTX_set_rsa_padding(ctx, pad) < 1 || + EVP_PKEY_decrypt(ctx, buf, &outl, data, data_len) < 1) { + dcrypt_openssl_error(error_r); + ec = -1; + } else { + buffer_append(result, buf, outl); + ec = 0; + } + + EVP_PKEY_CTX_free(ctx); + + return ec == 0; +} + +static const char * +dcrypt_openssl_oid2name(const unsigned char *oid, size_t oid_len, + const char **error_r) +{ + const char *name; + i_assert(oid != NULL); + ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, oid_len); + if (obj == NULL) { + dcrypt_openssl_error(error_r); + return NULL; + } + name = OBJ_nid2sn(OBJ_obj2nid(obj)); + ASN1_OBJECT_free(obj); + return name; +} + +static bool +dcrypt_openssl_name2oid(const char *name, buffer_t *oid, const char **error_r) +{ + i_assert(name != NULL); + ASN1_OBJECT *obj = OBJ_txt2obj(name, 0); + if (obj == NULL) + return dcrypt_openssl_error(error_r); + + size_t len = OBJ_length(obj); + if (len == 0) + { + DCRYPT_SET_ERROR("Object has no OID assigned"); + return FALSE; + } + len = i2d_ASN1_OBJECT(obj, NULL); + unsigned char *bufptr = buffer_append_space_unsafe(oid, len); + i2d_ASN1_OBJECT(obj, &bufptr); + ASN1_OBJECT_free(obj); + if (bufptr != NULL) { + return TRUE; + } + return dcrypt_openssl_error(error_r); +} + +static enum dcrypt_key_type +dcrypt_openssl_private_key_type(struct dcrypt_private_key *key) +{ + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *priv = key->key; + if (EVP_PKEY_base_id(priv) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; + else if (EVP_PKEY_base_id(priv) == EVP_PKEY_EC) return DCRYPT_KEY_EC; + else i_unreached(); +} + +static enum dcrypt_key_type +dcrypt_openssl_public_key_type(struct dcrypt_public_key *key) +{ + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *pub = key->key; + if (EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) return DCRYPT_KEY_RSA; + else if (EVP_PKEY_base_id(pub) == EVP_PKEY_EC) return DCRYPT_KEY_EC; + else i_unreached(); +} + +/** this is the v1 old legacy way of doing key id's **/ +static bool +dcrypt_openssl_public_key_id_old(struct dcrypt_public_key *key, + buffer_t *result, const char **error_r) +{ + unsigned char buf[SHA256_DIGEST_LENGTH]; + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *pub = key->key; + + if (EVP_PKEY_base_id(pub) != EVP_PKEY_EC) { + DCRYPT_SET_ERROR("Only EC key supported"); + return FALSE; + } + + char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(pub)); + if (pub_pt_hex == NULL) + return dcrypt_openssl_error(error_r); + /* digest this */ + SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); + buffer_append(result, buf, SHA256_DIGEST_LENGTH); + OPENSSL_free(pub_pt_hex); + return TRUE; +} + +static bool +dcrypt_openssl_private_key_id_old(struct dcrypt_private_key *key, + buffer_t *result, const char **error_r) +{ + unsigned char buf[SHA256_DIGEST_LENGTH]; + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *priv = key->key; + + if (EVP_PKEY_base_id(priv) != EVP_PKEY_EC) { + DCRYPT_SET_ERROR("Only EC key supported"); + return FALSE; + } + + char *pub_pt_hex = ec_key_get_pub_point_hex(EVP_PKEY_get0_EC_KEY(priv)); + if (pub_pt_hex == NULL) + return dcrypt_openssl_error(error_r); + /* digest this */ + SHA256((const unsigned char*)pub_pt_hex, strlen(pub_pt_hex), buf); + buffer_append(result, buf, SHA256_DIGEST_LENGTH); + OPENSSL_free(pub_pt_hex); + return TRUE; +} + +/** this is the new which uses H(der formatted public key) **/ +static bool +dcrypt_openssl_public_key_id_evp(EVP_PKEY *key, + const EVP_MD *md, buffer_t *result, + const char **error_r) +{ + bool res = FALSE; + unsigned char buf[EVP_MD_size(md)], *ptr; + + if (EVP_PKEY_base_id(key) == EVP_PKEY_EC) { + EC_KEY_set_conv_form(EVP_PKEY_get0_EC_KEY(key), + POINT_CONVERSION_COMPRESSED); + } + BIO *b = BIO_new(BIO_s_mem()); + if (b == NULL || i2d_PUBKEY_bio(b, key) < 1) { + BIO_vfree(b); + return dcrypt_openssl_error(error_r); + } + long len = BIO_get_mem_data(b, &ptr); + unsigned int hlen = sizeof(buf); + /* then hash it */ + EVP_MD_CTX *ctx = EVP_MD_CTX_new(); + if (ctx == NULL || + EVP_DigestInit_ex(ctx, md, NULL) < 1 || + EVP_DigestUpdate(ctx, (const unsigned char*)ptr, len) < 1 || + EVP_DigestFinal_ex(ctx, buf, &hlen) < 1) { + res = dcrypt_openssl_error(error_r); + } else { + buffer_append(result, buf, hlen); + res = TRUE; + } + EVP_MD_CTX_free(ctx); + BIO_vfree(b); + + return res; +} + +static bool +dcrypt_openssl_public_key_id(struct dcrypt_public_key *key, + const char *algorithm, buffer_t *result, + const char **error_r) +{ + const EVP_MD *md = EVP_get_digestbyname(algorithm); + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *pub = key->key; + + if (md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Unknown cipher %s", algorithm)); + return FALSE; + } + + return dcrypt_openssl_public_key_id_evp(pub, md, result, error_r); +} + +static bool +dcrypt_openssl_private_key_id(struct dcrypt_private_key *key, + const char *algorithm, buffer_t *result, + const char **error_r) +{ + const EVP_MD *md = EVP_get_digestbyname(algorithm); + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *priv = key->key; + + if (md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Unknown cipher %s", algorithm)); + return FALSE; + } + + return dcrypt_openssl_public_key_id_evp(priv, md, result, error_r); +} + +static bool +dcrypt_openssl_digest(const char *algorithm, const void *data, size_t data_len, + buffer_t *digest_r, const char **error_r) +{ + bool ret; + EVP_MD_CTX *mdctx; + const EVP_MD *md = EVP_get_digestbyname(algorithm); + if (md == NULL) + return dcrypt_openssl_error(error_r); + unsigned int md_size = EVP_MD_size(md); + if ((mdctx = EVP_MD_CTX_create()) == NULL) + return dcrypt_openssl_error(error_r); + unsigned char *buf = buffer_append_space_unsafe(digest_r, md_size); + if (EVP_DigestInit_ex(mdctx, EVP_sha256(), NULL) != 1 || + EVP_DigestUpdate(mdctx, data, data_len) != 1 || + EVP_DigestFinal_ex(mdctx, buf, &md_size) != 1) { + ret = dcrypt_openssl_error(error_r); + } else { + ret = TRUE; + } + EVP_MD_CTX_free(mdctx); + return ret; +} + +#ifndef HAVE_ECDSA_SIG_GET0 +static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) +{ + i_assert(sig != NULL); + *pr = sig->r; + *ps = sig->s; +} +#endif +#ifndef HAVE_ECDSA_SIG_SET0 +static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (sig == NULL || r == NULL || s == NULL) { + ECDSAerr(0, ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + BN_free(sig->r); + sig->r = r; + BN_free(sig->s); + sig->s = s; + + return 1; +} +#endif + +static bool +dcrypt_openssl_sign_ecdsa(struct dcrypt_private_key *key, const char *algorithm, + const void *data, size_t data_len, buffer_t *signature_r, + const char **error_r) +{ + EVP_PKEY *pkey = key->key; + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + bool ret; + int rs_len = EC_GROUP_order_bits(EC_KEY_get0_group(ec_key)) / 8; + + /* digest data */ + buffer_t *digest = t_buffer_create(64); + if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r)) + return FALSE; + + /* sign data */ + ECDSA_SIG *ec_sig; + if ((ec_sig = ECDSA_do_sign(digest->data, digest->used, ec_key)) == NULL) + return dcrypt_openssl_error(error_r); + + /* export signature */ + const BIGNUM *r; + const BIGNUM *s; + + ECDSA_SIG_get0(ec_sig, &r, &s); + + int r_len = BN_num_bytes(r); + i_assert(rs_len >= r_len); + + /* write r */ + unsigned char *buf = buffer_append_space_unsafe(signature_r, rs_len); + if (BN_bn2bin(r, buf + (rs_len - r_len)) != r_len) { + ret = dcrypt_openssl_error(error_r); + } else { + buf = buffer_append_space_unsafe(signature_r, rs_len); + int s_len = BN_num_bytes(s); + i_assert(rs_len >= s_len); + if (BN_bn2bin(s, buf + (rs_len - s_len)) != s_len) { + ret = dcrypt_openssl_error(error_r); + } else { + ret = TRUE; + } + } + + ECDSA_SIG_free(ec_sig); + + return ret; +} + +static bool +dcrypt_openssl_sign(struct dcrypt_private_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, buffer_t *signature_r, + enum dcrypt_padding padding, const char **error_r) +{ + switch (format) { + case DCRYPT_SIGNATURE_FORMAT_DSS: + break; + case DCRYPT_SIGNATURE_FORMAT_X962: + if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) { + DCRYPT_SET_ERROR("Format does not support RSA"); + return FALSE; + } + return dcrypt_openssl_sign_ecdsa(key, algorithm, + data, data_len, signature_r, error_r); + default: + i_unreached(); + } + + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *dctx; + bool ret; + const EVP_MD *md = EVP_get_digestbyname(algorithm); + size_t siglen; + int pad = dcrypt_openssl_padding_mode(padding, TRUE, error_r); + + if (pad == -1) + return FALSE; + + if (md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Unknown digest %s", algorithm)); + return FALSE; + } + + dctx = EVP_MD_CTX_create(); + + /* NB! Padding is set only on RSA signatures + ECDSA signatures use whatever is default */ + if (EVP_DigestSignInit(dctx, &pctx, md, NULL, key->key) != 1 || + (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA && + EVP_PKEY_CTX_set_rsa_padding(pctx, pad) != 1) || + EVP_DigestSignUpdate(dctx, data, data_len) != 1 || + EVP_DigestSignFinal(dctx, NULL, &siglen) != 1) { + ret = dcrypt_openssl_error(error_r); + } else { + i_assert(siglen > 0); + /* @UNSAFE */ + unsigned char *buf = + buffer_append_space_unsafe(signature_r, siglen); + if (EVP_DigestSignFinal(dctx, buf, &siglen) != 1) { + ret = dcrypt_openssl_error(error_r); + } else { + buffer_set_used_size(signature_r, siglen); + ret = TRUE; + } + } + + EVP_MD_CTX_destroy(dctx); + + return ret; +} + +static bool +dcrypt_openssl_verify_ecdsa(struct dcrypt_public_key *key, const char *algorithm, + const void *data, size_t data_len, + const unsigned char *signature, size_t signature_len, + bool *valid_r, const char **error_r) +{ + if ((signature_len % 2) != 0) { + DCRYPT_SET_ERROR("Truncated signature"); + return FALSE; + } + + EVP_PKEY *pkey = key->key; + EC_KEY *ec_key = EVP_PKEY_get0_EC_KEY(pkey); + int ec; + + /* digest data */ + buffer_t *digest = t_buffer_create(64); + if (!dcrypt_openssl_digest(algorithm, data, data_len, digest, error_r)) + return FALSE; + + BIGNUM *r = BN_new(); + BIGNUM *s = BN_new(); + /* attempt to decode BIGNUMs */ + if (BN_bin2bn(signature, signature_len / 2, r) == NULL) { + BN_free(r); + BN_free(s); + return dcrypt_openssl_error(error_r); + } + /* then next */ + if (BN_bin2bn(CONST_PTR_OFFSET(signature, signature_len / 2), + signature_len / 2, s) == NULL) { + BN_free(r); + BN_free(s); + return dcrypt_openssl_error(error_r); + } + + /* reconstruct signature */ + ECDSA_SIG *ec_sig = ECDSA_SIG_new(); + ECDSA_SIG_set0(ec_sig, r, s); + + /* verify it */ + ec = ECDSA_do_verify(digest->data, digest->used, ec_sig, ec_key); + ECDSA_SIG_free(ec_sig); + + if (ec == 1) { + *valid_r = TRUE; + } else if (ec == 0) { + *valid_r = FALSE; + } else { + return dcrypt_openssl_error(error_r); + } + return TRUE; +} + +static bool +dcrypt_openssl_verify(struct dcrypt_public_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, + const unsigned char *signature, size_t signature_len, + bool *valid_r, enum dcrypt_padding padding, + const char **error_r) +{ + switch (format) { + case DCRYPT_SIGNATURE_FORMAT_DSS: + break; + case DCRYPT_SIGNATURE_FORMAT_X962: + if (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA) { + DCRYPT_SET_ERROR("Format does not support RSA"); + return FALSE; + } + return dcrypt_openssl_verify_ecdsa(key, algorithm, + data, data_len, signature, signature_len, + valid_r, error_r); + default: + i_unreached(); + } + + EVP_PKEY_CTX *pctx = NULL; + EVP_MD_CTX *dctx; + bool ret; + const EVP_MD *md = EVP_get_digestbyname(algorithm); + int rc, pad = dcrypt_openssl_padding_mode(padding, TRUE, error_r); + + if (pad == -1) + return FALSE; + + if (md == NULL) { + DCRYPT_SET_ERROR(t_strdup_printf("Unknown digest %s", algorithm)); + return FALSE; + } + + dctx = EVP_MD_CTX_create(); + + /* NB! Padding is set only on RSA signatures + ECDSA signatures use whatever is default */ + if (EVP_DigestVerifyInit(dctx, &pctx, md, NULL, key->key) != 1 || + (EVP_PKEY_base_id(key->key) == EVP_PKEY_RSA && + EVP_PKEY_CTX_set_rsa_padding(pctx, pad) != 1) || + EVP_DigestVerifyUpdate(dctx, data, data_len) != 1 || + (rc = EVP_DigestVerifyFinal(dctx, signature, signature_len)) < 0) { + ret = dcrypt_openssl_error(error_r); + } else { + /* return code 1 means valid signature, otherwise invalid */ + *valid_r = (rc == 1); + ret = TRUE; + } + + EVP_MD_CTX_destroy(dctx); + + return ret; +} + +static bool +dcrypt_openssl_key_store_private_raw(struct dcrypt_private_key *key, + pool_t pool, + enum dcrypt_key_type *type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r) +{ + i_assert(key != NULL && key->key != NULL); + i_assert(array_is_created(keys_r)); + EVP_PKEY *priv = key->key; + ARRAY_TYPE(dcrypt_raw_key) keys; + t_array_init(&keys, 2); + + if (EVP_PKEY_base_id(priv) == EVP_PKEY_RSA) { + DCRYPT_SET_ERROR("Not implemented"); + return FALSE; + } else if (EVP_PKEY_base_id(priv) == EVP_PKEY_EC) { + /* store OID */ + EC_KEY *key = EVP_PKEY_get0_EC_KEY(priv); + EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); + ASN1_OBJECT *obj = OBJ_nid2obj(nid); + int len = OBJ_length(obj); + if (len == 0) { + DCRYPT_SET_ERROR("Object has no OID assigned"); + return FALSE; + } + len = i2d_ASN1_OBJECT(obj, NULL); + unsigned char *bufptr = p_malloc(pool, len); + struct dcrypt_raw_key *item = array_append_space(&keys); + item->parameter = bufptr; + item->len = i2d_ASN1_OBJECT(obj, &bufptr); + ASN1_OBJECT_free(obj); + /* store private key */ + const BIGNUM *b = EC_KEY_get0_private_key(key); + len = BN_num_bytes(b); + item = array_append_space(&keys); + bufptr = p_malloc(pool, len); + if (BN_bn2bin(b, bufptr) < len) + return dcrypt_openssl_error(error_r); + item->parameter = bufptr; + item->len = len; + *type_r = DCRYPT_KEY_EC; + } else { + DCRYPT_SET_ERROR("Key type unsupported"); + return FALSE; + } + + array_append_array(keys_r, &keys); + return TRUE; +} + +static bool +dcrypt_openssl_key_store_public_raw(struct dcrypt_public_key *key, + pool_t pool, + enum dcrypt_key_type *type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r) +{ + i_assert(key != NULL && key->key != NULL); + EVP_PKEY *pub = key->key; + ARRAY_TYPE(dcrypt_raw_key) keys; + t_array_init(&keys, 2); + + if (EVP_PKEY_base_id(pub) == EVP_PKEY_RSA) { + DCRYPT_SET_ERROR("Not implemented"); + return FALSE; + } else if (EVP_PKEY_base_id(pub) == EVP_PKEY_EC) { + /* store OID */ + EC_KEY *key = EVP_PKEY_get0_EC_KEY(pub); + EC_KEY_set_conv_form(key, POINT_CONVERSION_UNCOMPRESSED); + int nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(key)); + ASN1_OBJECT *obj = OBJ_nid2obj(nid); + int len = OBJ_length(obj); + if (len == 0) { + DCRYPT_SET_ERROR("Object has no OID assigned"); + return FALSE; + } + len = i2d_ASN1_OBJECT(obj, NULL); + unsigned char *bufptr = p_malloc(pool, len); + struct dcrypt_raw_key *item = array_append_space(&keys); + item->parameter = bufptr; + item->len = i2d_ASN1_OBJECT(obj, &bufptr); + ASN1_OBJECT_free(obj); + + /* store public key */ + const EC_POINT *point = EC_KEY_get0_public_key(key); + len = EC_POINT_point2oct(EC_KEY_get0_group(key), point, + POINT_CONVERSION_UNCOMPRESSED, + NULL, 0, NULL); + bufptr = p_malloc(pool, len); + item = array_append_space(&keys); + item->parameter = bufptr; + item->len = len; + if (EC_POINT_point2oct(EC_KEY_get0_group(key), point, + POINT_CONVERSION_UNCOMPRESSED, + bufptr, len, NULL) < (unsigned int)len) + return dcrypt_openssl_error(error_r); + *type_r = DCRYPT_KEY_EC; + } else { + DCRYPT_SET_ERROR("Key type unsupported"); + return FALSE; + } + + array_append_array(keys_r, &keys); + + return TRUE; +} + +static bool +dcrypt_openssl_key_load_private_raw(struct dcrypt_private_key **key_r, + enum dcrypt_key_type type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r) +{ + int ec; + i_assert(keys != NULL && array_is_created(keys) && array_count(keys) > 1); + const struct dcrypt_raw_key *item; + + if (type == DCRYPT_KEY_RSA) { + DCRYPT_SET_ERROR("Not implemented"); + return FALSE; + } else if (type == DCRYPT_KEY_EC) { + /* get curve */ + if (array_count(keys) < 2) { + DCRYPT_SET_ERROR("Invalid parameters"); + return FALSE; + } + item = array_idx(keys, 0); + const unsigned char *oid = item->parameter; + ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, item->len); + if (obj == NULL) + return dcrypt_openssl_error(error_r); + int nid = OBJ_obj2nid(obj); + ASN1_OBJECT_free(obj); + + /* load private point */ + item = array_idx(keys, 1); + BIGNUM *bn = BN_secure_new(); + if (BN_bin2bn(item->parameter, item->len, bn) == NULL) { + BN_free(bn); + return dcrypt_openssl_error(error_r); + } + + /* setup a key */ + EC_KEY *key = EC_KEY_new_by_curve_name(nid); + ec = EC_KEY_set_private_key(key, bn); + BN_free(bn); + + if (ec != 1) { + EC_KEY_free(key); + return dcrypt_openssl_error(error_r); + } + + /* calculate & assign public key */ + EC_POINT *pub = EC_POINT_new(EC_KEY_get0_group(key)); + if (pub == NULL) { + EC_KEY_free(key); + return dcrypt_openssl_error(error_r); + } + /* calculate public key */ + ec = EC_POINT_mul(EC_KEY_get0_group(key), pub, + EC_KEY_get0_private_key(key), + NULL, NULL, NULL); + if (ec == 1) + ec = EC_KEY_set_public_key(key, pub); + EC_POINT_free(pub); + + /* check the key */ + if (ec != 1 || EC_KEY_check_key(key) != 1) { + EC_KEY_free(key); + return dcrypt_openssl_error(error_r); + } + EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); + + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_set1_EC_KEY(pkey, key); + EC_KEY_free(key); + *key_r = i_new(struct dcrypt_private_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + return TRUE; + } else { + DCRYPT_SET_ERROR("Key type unsupported"); + } + + return FALSE; +} + +static bool +dcrypt_openssl_key_load_public_raw(struct dcrypt_public_key **key_r, + enum dcrypt_key_type type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r) +{ + int ec; + i_assert(keys != NULL && array_is_created(keys) && array_count(keys) > 1); + const struct dcrypt_raw_key *item; + + if (type == DCRYPT_KEY_RSA) { + DCRYPT_SET_ERROR("Not implemented"); + return FALSE; + } else if (type == DCRYPT_KEY_EC) { + /* get curve */ + if (array_count(keys) < 2) { + DCRYPT_SET_ERROR("Invalid parameters"); + return FALSE; + } + item = array_idx(keys, 0); + const unsigned char *oid = item->parameter; + ASN1_OBJECT *obj = d2i_ASN1_OBJECT(NULL, &oid, item->len); + if (obj == NULL) { + dcrypt_openssl_error(error_r); + return FALSE; + } + int nid = OBJ_obj2nid(obj); + ASN1_OBJECT_free(obj); + + /* set group */ + EC_GROUP *group = EC_GROUP_new_by_curve_name(nid); + if (group == NULL) { + dcrypt_openssl_error(error_r); + return FALSE; + } + + /* load point */ + item = array_idx(keys, 1); + EC_POINT *point = EC_POINT_new(group); + if (EC_POINT_oct2point(group, point, item->parameter, + item->len, NULL) != 1) { + EC_POINT_free(point); + EC_GROUP_free(group); + return dcrypt_openssl_error(error_r); + } + + EC_KEY *key = EC_KEY_new(); + ec = EC_KEY_set_group(key, group); + if (ec == 1) + ec = EC_KEY_set_public_key(key, point); + EC_POINT_free(point); + EC_GROUP_free(group); + + if (ec != 1 || EC_KEY_check_key(key) != 1) { + EC_KEY_free(key); + return dcrypt_openssl_error(error_r); + } + + EC_KEY_precompute_mult(key, NULL); + EC_KEY_set_asn1_flag(key, OPENSSL_EC_NAMED_CURVE); + EVP_PKEY *pkey = EVP_PKEY_new(); + EVP_PKEY_set1_EC_KEY(pkey, key); + EC_KEY_free(key); + *key_r = i_new(struct dcrypt_public_key, 1); + (*key_r)->key = pkey; + (*key_r)->ref++; + return TRUE; + } else { + DCRYPT_SET_ERROR("Key type unsupported"); + } + + return FALSE; +} + +static bool +dcrypt_openssl_key_get_curve_public(struct dcrypt_public_key *key, + const char **curve_r, const char **error_r) +{ + EVP_PKEY *pkey = key->key; + char objtxt[OID_TEXT_MAX_LEN]; + + if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC) { + DCRYPT_SET_ERROR("Unsupported key type"); + return FALSE; + } + + ASN1_OBJECT *obj = OBJ_nid2obj(EC_GROUP_get_curve_name( + EC_KEY_get0_group(EVP_PKEY_get0_EC_KEY(pkey)))); + + int len = OBJ_obj2txt(objtxt, sizeof(objtxt), obj, 1); + ASN1_OBJECT_free(obj); + + if (len < 1) { + return dcrypt_openssl_error(error_r); + } else if ((unsigned int)len > sizeof(objtxt)) { + DCRYPT_SET_ERROR("Object name too long"); + return FALSE; + } + + *curve_r = t_strndup(objtxt, len); + return TRUE; +} + +static const char * +dcrypt_openssl_key_get_id_public(struct dcrypt_public_key *key) +{ + return key->key_id; +} + +static const char * +dcrypt_openssl_key_get_id_private(struct dcrypt_private_key *key) +{ + return key->key_id; +} + +static void +dcrypt_openssl_key_set_id_public(struct dcrypt_public_key *key, const char *id) +{ + i_free(key->key_id); + key->key_id = i_strdup_empty(id); +} + +static void +dcrypt_openssl_key_set_id_private(struct dcrypt_private_key *key, const char *id) +{ + i_free(key->key_id); + key->key_id = i_strdup_empty(id); +} + +static enum dcrypt_key_usage +dcrypt_openssl_key_get_usage_public(struct dcrypt_public_key *key) +{ + return key->usage; +} + +static enum dcrypt_key_usage +dcrypt_openssl_key_get_usage_private(struct dcrypt_private_key *key) +{ + return key->usage; +} + +static void +dcrypt_openssl_key_set_usage_public(struct dcrypt_public_key *key, + enum dcrypt_key_usage usage) +{ + key->usage = usage; +} + +static void +dcrypt_openssl_key_set_usage_private(struct dcrypt_private_key *key, + enum dcrypt_key_usage usage) +{ + key->usage = usage; +} + + +static struct dcrypt_vfs dcrypt_openssl_vfs = { + .initialize = dcrypt_openssl_initialize, + .ctx_sym_create = dcrypt_openssl_ctx_sym_create, + .ctx_sym_destroy = dcrypt_openssl_ctx_sym_destroy, + .ctx_sym_set_key = dcrypt_openssl_ctx_sym_set_key, + .ctx_sym_set_iv = dcrypt_openssl_ctx_sym_set_iv, + .ctx_sym_set_key_iv_random = dcrypt_openssl_ctx_sym_set_key_iv_random, + .ctx_sym_set_padding = dcrypt_openssl_ctx_sym_set_padding, + .ctx_sym_get_key = dcrypt_openssl_ctx_sym_get_key, + .ctx_sym_get_iv = dcrypt_openssl_ctx_sym_get_iv, + .ctx_sym_set_aad = dcrypt_openssl_ctx_sym_set_aad, + .ctx_sym_get_aad = dcrypt_openssl_ctx_sym_get_aad, + .ctx_sym_set_tag = dcrypt_openssl_ctx_sym_set_tag, + .ctx_sym_get_tag = dcrypt_openssl_ctx_sym_get_tag, + .ctx_sym_get_key_length = dcrypt_openssl_ctx_sym_get_key_length, + .ctx_sym_get_iv_length = dcrypt_openssl_ctx_sym_get_iv_length, + .ctx_sym_get_block_size = dcrypt_openssl_ctx_sym_get_block_size, + .ctx_sym_init = dcrypt_openssl_ctx_sym_init, + .ctx_sym_update = dcrypt_openssl_ctx_sym_update, + .ctx_sym_final = dcrypt_openssl_ctx_sym_final, + .ctx_hmac_create = dcrypt_openssl_ctx_hmac_create, + .ctx_hmac_destroy = dcrypt_openssl_ctx_hmac_destroy, + .ctx_hmac_set_key = dcrypt_openssl_ctx_hmac_set_key, + .ctx_hmac_set_key_random = dcrypt_openssl_ctx_hmac_set_key_random, + .ctx_hmac_get_digest_length = dcrypt_openssl_ctx_hmac_get_digest_length, + .ctx_hmac_get_key = dcrypt_openssl_ctx_hmac_get_key, + .ctx_hmac_init = dcrypt_openssl_ctx_hmac_init, + .ctx_hmac_update = dcrypt_openssl_ctx_hmac_update, + .ctx_hmac_final = dcrypt_openssl_ctx_hmac_final, + .ecdh_derive_secret_local = dcrypt_openssl_ecdh_derive_secret_local, + .ecdh_derive_secret_peer = dcrypt_openssl_ecdh_derive_secret_peer, + .pbkdf2 = dcrypt_openssl_pbkdf2, + .generate_keypair = dcrypt_openssl_generate_keypair, + .load_private_key = dcrypt_openssl_load_private_key, + .load_public_key = dcrypt_openssl_load_public_key, + .store_private_key = dcrypt_openssl_store_private_key, + .store_public_key = dcrypt_openssl_store_public_key, + .private_to_public_key = dcrypt_openssl_private_to_public_key, + .key_string_get_info = dcrypt_openssl_key_string_get_info, + .unref_keypair = dcrypt_openssl_unref_keypair, + .unref_public_key = dcrypt_openssl_unref_public_key, + .unref_private_key = dcrypt_openssl_unref_private_key, + .ref_public_key = dcrypt_openssl_ref_public_key, + .ref_private_key = dcrypt_openssl_ref_private_key, + .rsa_encrypt = dcrypt_openssl_rsa_encrypt, + .rsa_decrypt = dcrypt_openssl_rsa_decrypt, + .oid2name = dcrypt_openssl_oid2name, + .name2oid = dcrypt_openssl_name2oid, + .private_key_type = dcrypt_openssl_private_key_type, + .public_key_type = dcrypt_openssl_public_key_type, + .public_key_id = dcrypt_openssl_public_key_id, + .public_key_id_old = dcrypt_openssl_public_key_id_old, + .private_key_id = dcrypt_openssl_private_key_id, + .private_key_id_old = dcrypt_openssl_private_key_id_old, + .key_store_private_raw = dcrypt_openssl_key_store_private_raw, + .key_store_public_raw = dcrypt_openssl_key_store_public_raw, + .key_load_private_raw = dcrypt_openssl_key_load_private_raw, + .key_load_public_raw = dcrypt_openssl_key_load_public_raw, + .key_get_curve_public = dcrypt_openssl_key_get_curve_public, + .key_get_id_public = dcrypt_openssl_key_get_id_public, + .key_get_id_private = dcrypt_openssl_key_get_id_private, + .key_set_id_public = dcrypt_openssl_key_set_id_public, + .key_set_id_private = dcrypt_openssl_key_set_id_private, + .key_get_usage_public = dcrypt_openssl_key_get_usage_public, + .key_get_usage_private = dcrypt_openssl_key_get_usage_private, + .key_set_usage_public = dcrypt_openssl_key_set_usage_public, + .key_set_usage_private = dcrypt_openssl_key_set_usage_private, + .sign = dcrypt_openssl_sign, + .verify = dcrypt_openssl_verify, + .ecdh_derive_secret = dcrypt_openssl_ecdh_derive_secret, +}; + +void dcrypt_openssl_init(struct module *module ATTR_UNUSED) +{ + dovecot_openssl_common_global_ref(); + dcrypt_set_vfs(&dcrypt_openssl_vfs); +} + +void dcrypt_openssl_deinit(void) +{ + dovecot_openssl_common_global_unref(); +} diff --git a/src/lib-dcrypt/dcrypt-private.h b/src/lib-dcrypt/dcrypt-private.h new file mode 100644 index 0000000..13da99e --- /dev/null +++ b/src/lib-dcrypt/dcrypt-private.h @@ -0,0 +1,211 @@ +#ifndef DCRYPT_PRIVATE_H +#define DCRYPT_PRIVATE_H + +#define DCRYPT_DOVECOT_KEY_ENCRYPT_HASH "sha256" +#define DCRYPT_DOVECOT_KEY_ENCRYPT_ROUNDS 2048 + +#define DCRYPT_DOVECOT_KEY_ENCRYPT_NONE 0 +#define DCRYPT_DOVECOT_KEY_ENCRYPT_PK 1 +#define DCRYPT_DOVECOT_KEY_ENCRYPT_PASSWORD 2 + +struct dcrypt_vfs { + bool (*initialize)(const struct dcrypt_settings *set, + const char **error_r); + + bool (*ctx_sym_create)(const char *algorithm, + enum dcrypt_sym_mode mode, + struct dcrypt_context_symmetric **ctx_r, + const char **error_r); + void (*ctx_sym_destroy)(struct dcrypt_context_symmetric **ctx); + + void (*ctx_sym_set_key)(struct dcrypt_context_symmetric *ctx, + const unsigned char *key, size_t key_len); + void (*ctx_sym_set_iv)(struct dcrypt_context_symmetric *ctx, + const unsigned char *iv, size_t iv_len); + void (*ctx_sym_set_key_iv_random)(struct dcrypt_context_symmetric *ctx); + + void (*ctx_sym_set_padding)(struct dcrypt_context_symmetric *ctx, + bool padding); + + bool (*ctx_sym_get_key)(struct dcrypt_context_symmetric *ctx, + buffer_t *key); + bool (*ctx_sym_get_iv)(struct dcrypt_context_symmetric *ctx, + buffer_t *iv); + + void (*ctx_sym_set_aad)(struct dcrypt_context_symmetric *ctx, + const unsigned char *aad, size_t aad_len); + bool (*ctx_sym_get_aad)(struct dcrypt_context_symmetric *ctx, + buffer_t *aad); + void (*ctx_sym_set_tag)(struct dcrypt_context_symmetric *ctx, + const unsigned char *tag, size_t tag_len); + bool (*ctx_sym_get_tag)(struct dcrypt_context_symmetric *ctx, + buffer_t *tag); + + unsigned int (*ctx_sym_get_key_length)( + struct dcrypt_context_symmetric *ctx); + unsigned int (*ctx_sym_get_iv_length)( + struct dcrypt_context_symmetric *ctx); + unsigned int (*ctx_sym_get_block_size)( + struct dcrypt_context_symmetric *ctx); + + bool (*ctx_sym_init)(struct dcrypt_context_symmetric *ctx, + const char **error_r); + bool (*ctx_sym_update)(struct dcrypt_context_symmetric *ctx, + const unsigned char *data, size_t data_len, + buffer_t *result, const char **error_r); + bool (*ctx_sym_final)(struct dcrypt_context_symmetric *ctx, + buffer_t *result, const char **error_r); + + bool (*ctx_hmac_create)(const char *algorithm, + struct dcrypt_context_hmac **ctx_r, + const char **error_r); + void (*ctx_hmac_destroy)(struct dcrypt_context_hmac **ctx); + + void (*ctx_hmac_set_key)(struct dcrypt_context_hmac *ctx, + const unsigned char *key, size_t key_len); + bool (*ctx_hmac_get_key)(struct dcrypt_context_hmac *ctx, + buffer_t *key); + unsigned int (*ctx_hmac_get_digest_length)( + struct dcrypt_context_hmac *ctx); + void (*ctx_hmac_set_key_random)(struct dcrypt_context_hmac *ctx); + + bool (*ctx_hmac_init)(struct dcrypt_context_hmac *ctx, + const char **error_r); + bool (*ctx_hmac_update)(struct dcrypt_context_hmac *ctx, + const unsigned char *data, size_t data_len, + const char **error_r); + bool (*ctx_hmac_final)(struct dcrypt_context_hmac *ctx, + buffer_t *result, const char **error_r); + + bool (*ecdh_derive_secret_local)(struct dcrypt_private_key *local_key, + buffer_t *R, buffer_t *S, + const char **error_r); + bool (*ecdh_derive_secret_peer)(struct dcrypt_public_key *peer_key, + buffer_t *R, buffer_t *S, + const char **error_r); + bool (*pbkdf2)(const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + const char *hash, unsigned int rounds, + buffer_t *result, unsigned int result_len, + const char **error_r); + + bool (*generate_keypair)(struct dcrypt_keypair *pair_r, + enum dcrypt_key_type kind, unsigned int bits, + const char *curve, const char **error_r); + + bool (*load_private_key)(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r); + bool (*load_public_key)(struct dcrypt_public_key **key_r, + const char *data, const char **error_r); + + bool (*store_private_key)(struct dcrypt_private_key *key, + enum dcrypt_key_format format, + const char *cipher, buffer_t *destination, + const char *password, + struct dcrypt_public_key *enc_key, + const char **error_r); + bool (*store_public_key)(struct dcrypt_public_key *key, + enum dcrypt_key_format format, + buffer_t *destination, const char **error_r); + + void (*private_to_public_key)(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key **pub_key_r); + + bool (*key_string_get_info)( + const char *key_data, enum dcrypt_key_format *format_r, + enum dcrypt_key_version *version_r, + enum dcrypt_key_kind *kind_r, + enum dcrypt_key_encryption_type *encryption_type_r, + const char **encryption_key_hash_r, const char **key_hash_r, + const char **error_r); + + void (*unref_keypair)(struct dcrypt_keypair *keypair); + void (*unref_public_key)(struct dcrypt_public_key **key); + void (*unref_private_key)(struct dcrypt_private_key **key); + void (*ref_public_key)(struct dcrypt_public_key *key); + void (*ref_private_key)(struct dcrypt_private_key *key); + + bool (*rsa_encrypt)(struct dcrypt_public_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r); + bool (*rsa_decrypt)(struct dcrypt_private_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r); + + const char *(*oid2name)(const unsigned char *oid, + size_t oid_len, const char **error_r); + bool (*name2oid)(const char *name, buffer_t *oid, + const char **error_r); + + enum dcrypt_key_type (*private_key_type)(struct dcrypt_private_key *key); + enum dcrypt_key_type (*public_key_type)(struct dcrypt_public_key *key); + bool (*public_key_id)(struct dcrypt_public_key *key, + const char *algorithm, buffer_t *result, + const char **error_r); + bool (*public_key_id_old)(struct dcrypt_public_key *key, + buffer_t *result, const char **error_r); + bool (*private_key_id)(struct dcrypt_private_key *key, + const char *algorithm, buffer_t *result, + const char **error_r); + bool (*private_key_id_old)(struct dcrypt_private_key *key, + buffer_t *result, const char **error_r); + bool (*key_store_private_raw)(struct dcrypt_private_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r); + bool (*key_store_public_raw)(struct dcrypt_public_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r); + bool (*key_load_private_raw)(struct dcrypt_private_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r); + bool (*key_load_public_raw)(struct dcrypt_public_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r); + bool (*key_get_curve_public)(struct dcrypt_public_key *key, + const char **curve_r, const char **error_r); + const char *(*key_get_id_public)(struct dcrypt_public_key *key); + const char *(*key_get_id_private)(struct dcrypt_private_key *key); + void (*key_set_id_public)(struct dcrypt_public_key *key, const char *id); + void (*key_set_id_private)(struct dcrypt_private_key *key, const char *id); + enum dcrypt_key_usage (*key_get_usage_public)(struct dcrypt_public_key *key); + enum dcrypt_key_usage (*key_get_usage_private)(struct dcrypt_private_key *key); + void (*key_set_usage_public)(struct dcrypt_public_key *key, + enum dcrypt_key_usage usage); + void (*key_set_usage_private)(struct dcrypt_private_key *key, + enum dcrypt_key_usage usage); + bool (*sign)(struct dcrypt_private_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, buffer_t *signature_r, + enum dcrypt_padding padding, const char **error_r); + bool (*verify)(struct dcrypt_public_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, + const unsigned char *signature, size_t signature_len, + bool *valid_r, enum dcrypt_padding padding, + const char **error_r); + bool (*ecdh_derive_secret)(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key *pub_key, + buffer_t *shared_secret, const char **error_r); +}; + +void dcrypt_set_vfs(struct dcrypt_vfs *vfs); + +void dcrypt_openssl_init(struct module *module ATTR_UNUSED); +void dcrypt_gnutls_init(struct module *module ATTR_UNUSED); +void dcrypt_openssl_deinit(void); +void dcrypt_gnutls_deinit(void); + +int parse_jwk_key(const char *key_data, struct json_tree **tree_r, + const char **error_r); + +#endif diff --git a/src/lib-dcrypt/dcrypt.c b/src/lib-dcrypt/dcrypt.c new file mode 100644 index 0000000..9916754 --- /dev/null +++ b/src/lib-dcrypt/dcrypt.c @@ -0,0 +1,660 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "module-dir.h" +#include "dcrypt.h" +#include "istream.h" +#include "json-tree.h" +#include "dcrypt-private.h" + +static struct module *dcrypt_module = NULL; +static struct dcrypt_vfs *dcrypt_vfs = NULL; +static const struct dcrypt_settings dcrypt_default_set = { + .module_dir = DCRYPT_MODULE_DIR, +}; + +bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, + const char **error_r) +{ + struct module_dir_load_settings mod_set; + const char *error; + + if (dcrypt_vfs != NULL) { + return TRUE; + } + if (backend == NULL) backend = "openssl"; /* default for now */ + if (set == NULL) + set = &dcrypt_default_set; + + const char *implementation = t_strconcat("dcrypt_",backend,NULL); + + i_zero(&mod_set); + mod_set.abi_version = DOVECOT_ABI_VERSION; + mod_set.require_init_funcs = TRUE; + if (module_dir_try_load_missing(&dcrypt_module, set->module_dir, + implementation, &mod_set, &error) < 0) { + if (error_r != NULL) + *error_r = error; + return FALSE; + } + module_dir_init(dcrypt_module); + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->initialize != NULL) { + if (!dcrypt_vfs->initialize(set, error_r)) { + dcrypt_deinitialize(); + return FALSE; + } + } + /* Destroy SSL module after(most of) the others. Especially lib-fs + backends may still want to access SSL module in their own + atexit-callbacks. */ + lib_atexit_priority(dcrypt_deinitialize, LIB_ATEXIT_PRIORITY_LOW); + return TRUE; +} + +void dcrypt_deinitialize(void) +{ + module_dir_unload(&dcrypt_module); + dcrypt_vfs = NULL; +} + +void dcrypt_set_vfs(struct dcrypt_vfs *vfs) +{ + dcrypt_vfs = vfs; +} + +bool dcrypt_is_initialized(void) +{ + return dcrypt_vfs != NULL; +} + +bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, + struct dcrypt_context_symmetric **ctx_r, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_create(algorithm, mode, ctx_r, error_r); +} + +void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_destroy(ctx); +} + +void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, + const unsigned char *key, size_t key_len) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_key(ctx, key, key_len); +} + +void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, + const unsigned char *iv, size_t iv_len) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_iv(ctx, iv, iv_len); +} + +void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_key_iv_random(ctx); +} + +bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_key(ctx, key); +} +bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_iv(ctx, iv); +} + +unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_key_length(ctx); +} + +unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_iv_length(ctx); +} + +void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, + const unsigned char *aad, size_t aad_len) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_aad(ctx, aad, aad_len); +} + +bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, buffer_t *aad) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_aad(ctx, aad); +} + +void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, + const unsigned char *tag, size_t tag_len) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_tag(ctx, tag, tag_len); +} + +bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, buffer_t *tag) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_tag(ctx, tag); +} + +unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_get_block_size(ctx); +} + +bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_init(ctx, error_r); +} + +bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, + const unsigned char *data, + size_t data_len, buffer_t *result, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_update(ctx, data, data_len, result, error_r); +} + +bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, + buffer_t *result, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_sym_final(ctx, result, error_r); +} + +void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, + bool padding) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_sym_set_padding(ctx, padding); +} + +bool dcrypt_ctx_hmac_create(const char *algorithm, + struct dcrypt_context_hmac **ctx_r, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_create(algorithm, ctx_r, error_r); +} + +void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_hmac_destroy(ctx); +} + +void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, + const unsigned char *key, size_t key_len) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_hmac_set_key(ctx, key, key_len); +} + +bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_get_key(ctx, key); +} + +void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ctx_hmac_set_key_random(ctx); +} + +unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_get_digest_length(ctx); +} + +bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_init(ctx, error_r); +} + +bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, + const unsigned char *data, size_t data_len, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_update(ctx, data, data_len, error_r); +} + +bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ctx_hmac_final(ctx, result, error_r); +} + +bool dcrypt_ecdh_derive_secret(struct dcrypt_private_key *local_key, + struct dcrypt_public_key *pub_key, + buffer_t *shared_secret, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->ecdh_derive_secret == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->ecdh_derive_secret(local_key, pub_key, shared_secret, + error_r); +} + +bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, + buffer_t *R, buffer_t *S, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ecdh_derive_secret_local(local_key, R, S, error_r); +} +bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, + buffer_t *R, buffer_t *S, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->ecdh_derive_secret_peer(peer_key, R, S, error_r); +} + +bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + const char *hash, unsigned int rounds, buffer_t *result, + unsigned int result_len, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->pbkdf2(password, password_len, salt, salt_len, + hash, rounds, result, result_len, error_r); +} + +bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, + enum dcrypt_key_type kind, unsigned int bits, + const char *curve, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + i_zero(pair_r); + return dcrypt_vfs->generate_keypair(pair_r, kind, bits, curve, error_r); +} + +bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->load_private_key(key_r, data, password, + dec_key, error_r); +} + +bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, + const char *data, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->load_public_key(key_r, data, error_r); +} + +bool dcrypt_key_store_private(struct dcrypt_private_key *key, + enum dcrypt_key_format format, + const char *cipher, buffer_t *destination, + const char *password, + struct dcrypt_public_key *enc_key, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->store_private_key(key, format, cipher, + destination, password, enc_key, + error_r); +} +bool dcrypt_key_store_public(struct dcrypt_public_key *key, + enum dcrypt_key_format format, + buffer_t *destination, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->store_public_key(key, format, destination, error_r); +} + +void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key **pub_key_r) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->private_to_public_key(priv_key, pub_key_r); +} + +bool dcrypt_key_string_get_info(const char *key_data, + enum dcrypt_key_format *format_r, + enum dcrypt_key_version *version_r, + enum dcrypt_key_kind *kind_r, + enum dcrypt_key_encryption_type *encryption_type_r, + const char **encryption_key_hash_r, + const char **key_hash_r, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs-> + key_string_get_info(key_data, format_r, version_r, kind_r, + encryption_type_r, encryption_key_hash_r, + key_hash_r, error_r); +} + +enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->private_key_type(key); +} + +enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->public_key_type(key); +} + +bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, + buffer_t *result, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->public_key_id(key, algorithm, result, error_r); +} + +bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->public_key_id_old(key, result, error_r); +} + +bool dcrypt_key_id_private(struct dcrypt_private_key *key, + const char *algorithm, buffer_t *result, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->private_key_id(key, algorithm, result, error_r); +} + +bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, buffer_t *result, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->private_key_id_old(key, result, error_r); +} + +void dcrypt_keypair_unref(struct dcrypt_keypair *keypair) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->unref_keypair(keypair); +} + +void dcrypt_key_ref_public(struct dcrypt_public_key *key) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ref_public_key(key); +} + +void dcrypt_key_ref_private(struct dcrypt_private_key *key) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->ref_private_key(key); +} + +void dcrypt_key_unref_public(struct dcrypt_public_key **key) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->unref_public_key(key); +} + +void dcrypt_key_unref_private(struct dcrypt_private_key **key) +{ + i_assert(dcrypt_vfs != NULL); + dcrypt_vfs->unref_private_key(key); +} + +bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->rsa_encrypt(key, data, data_len, result, + padding, error_r); +} + +bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->rsa_decrypt(key, data, data_len, result, + padding, error_r); +} + +const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->oid2name(oid, oid_len, error_r); +} + +bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + return dcrypt_vfs->name2oid(name, oid, error_r); +} + +bool dcrypt_key_store_private_raw(struct dcrypt_private_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_store_private_raw == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->key_store_private_raw(key, pool, key_type_r, keys_r, + error_r); +} + +bool dcrypt_key_store_public_raw(struct dcrypt_public_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_store_public_raw == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->key_store_public_raw(key, pool, key_type_r, keys_r, + error_r); +} + +bool dcrypt_key_load_private_raw(struct dcrypt_private_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_load_private_raw == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->key_load_private_raw(key_r, key_type, keys, + error_r); +} + +bool dcrypt_key_load_public_raw(struct dcrypt_public_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_load_public_raw == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->key_load_public_raw(key_r, key_type, keys, + error_r); +} + +bool dcrypt_key_get_curve_public(struct dcrypt_public_key *key, + const char **curve_r, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_get_curve_public == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + return dcrypt_vfs->key_get_curve_public(key, curve_r, error_r); +} + +const char *dcrypt_key_get_id_public(struct dcrypt_public_key *key) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_get_id_public == NULL) + return NULL; + return dcrypt_vfs->key_get_id_public(key); +} + +const char *dcrypt_key_get_id_private(struct dcrypt_private_key *key) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_get_id_private == NULL) + return NULL; + return dcrypt_vfs->key_get_id_private(key); +} + +void dcrypt_key_set_id_public(struct dcrypt_public_key *key, const char *id) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_set_id_public == NULL) + return; + dcrypt_vfs->key_set_id_public(key, id); +} + +void dcrypt_key_set_id_private(struct dcrypt_private_key *key, const char *id) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_set_id_private == NULL) + return; + dcrypt_vfs->key_set_id_private(key, id); +} + +enum dcrypt_key_usage dcrypt_key_get_usage_public(struct dcrypt_public_key *key) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_get_usage_public == NULL) + return DCRYPT_KEY_USAGE_NONE; + return dcrypt_vfs->key_get_usage_public(key); +} + +enum dcrypt_key_usage dcrypt_key_get_usage_private(struct dcrypt_private_key *key) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_get_usage_private == NULL) + return DCRYPT_KEY_USAGE_NONE; + return dcrypt_vfs->key_get_usage_private(key); +} + +void dcrypt_key_set_usage_public(struct dcrypt_public_key *key, + enum dcrypt_key_usage usage) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_set_usage_public == NULL) + return; + dcrypt_vfs->key_set_usage_public(key, usage); +} + +void dcrypt_key_set_usage_private(struct dcrypt_private_key *key, + enum dcrypt_key_usage usage) +{ + i_assert(dcrypt_vfs != NULL); + if (dcrypt_vfs->key_set_usage_private == NULL) + return; + dcrypt_vfs->key_set_usage_private(key, usage); +} + +bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, buffer_t *signature_r, + enum dcrypt_padding padding, const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + + if (dcrypt_vfs->sign == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + + return dcrypt_vfs->sign(key, algorithm, format, data, data_len, + signature_r, padding, error_r); +} + +bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, + const unsigned char *signature, size_t signature_len, + bool *valid_r, enum dcrypt_padding padding, + const char **error_r) +{ + i_assert(dcrypt_vfs != NULL); + + if (dcrypt_vfs->verify == NULL) { + *error_r = "Not implemented"; + return FALSE; + } + + return dcrypt_vfs->verify(key, algorithm, format, data, data_len, + signature, signature_len, + valid_r, padding, error_r); +} + +int parse_jwk_key(const char *key_data, struct json_tree **tree_r, + const char **error_r) +{ + struct istream *is = i_stream_create_from_data(key_data, strlen(key_data)); + struct json_parser *parser = json_parser_init(is); + struct json_tree *tree = json_tree_init(); + const char *error; + enum json_type type; + const char *value; + int ret; + + i_stream_unref(&is); + + while ((ret = json_parse_next(parser, &type, &value)) == 1) + json_tree_append(tree, type, value); + + i_assert(ret == -1); + + if (json_parser_deinit(&parser, &error) != 0) { + json_tree_deinit(&tree); + *error_r = error; + if (error == NULL) + *error_r = "Truncated JSON"; + return -1; + } + + *tree_r = tree; + + return 0; +} diff --git a/src/lib-dcrypt/dcrypt.h b/src/lib-dcrypt/dcrypt.h new file mode 100644 index 0000000..0e8a9aa --- /dev/null +++ b/src/lib-dcrypt/dcrypt.h @@ -0,0 +1,409 @@ +#ifndef DCRYPT_H +#define DCRYPT_H 1 +#include "array.h" + +struct dcrypt_context_symmetric; +struct dcrypt_context_hmac; +struct dcrypt_public_key; +struct dcrypt_private_key; + +struct dcrypt_keypair { + struct dcrypt_public_key *pub; + struct dcrypt_private_key *priv; +}; + +enum dcrypt_sym_mode { + DCRYPT_MODE_ENCRYPT, + DCRYPT_MODE_DECRYPT +}; + +enum dcrypt_key_type { + DCRYPT_KEY_RSA = 0x1, + DCRYPT_KEY_EC = 0x2 +}; + +/** + * dovecot key format: + * version version-specific data + * v1: version tab nid tab raw ec private key (in hex) + * v2: version colon algorithm oid colon private-or-public-key-only (in hex) + */ +enum dcrypt_key_format { + DCRYPT_FORMAT_PEM, + DCRYPT_FORMAT_DOVECOT, + DCRYPT_FORMAT_JWK, /* JSON Web Key (JWK) [RFC7517] */ +}; + +enum dcrypt_key_encryption_type { + DCRYPT_KEY_ENCRYPTION_TYPE_NONE, + DCRYPT_KEY_ENCRYPTION_TYPE_KEY, + DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD +}; + +enum dcrypt_key_version { + DCRYPT_KEY_VERSION_1, + DCRYPT_KEY_VERSION_2, + DCRYPT_KEY_VERSION_NA /* not applicable, PEM key */ +}; + +enum dcrypt_key_kind { + DCRYPT_KEY_KIND_PUBLIC, + DCRYPT_KEY_KIND_PRIVATE +}; + +enum dcrypt_key_usage { + DCRYPT_KEY_USAGE_NONE, + DCRYPT_KEY_USAGE_ENCRYPT, + DCRYPT_KEY_USAGE_SIGN, +}; + +enum dcrypt_signature_format { + DCRYPT_SIGNATURE_FORMAT_DSS, + DCRYPT_SIGNATURE_FORMAT_X962, +}; + +/* this parameter makes sense with RSA only + default for RSA means either PSS (sign/verify) + or OAEP (encrypt/decrypt). + for ECDSA default can be used. +*/ +enum dcrypt_padding { + DCRYPT_PADDING_DEFAULT, + DCRYPT_PADDING_RSA_PKCS1_PSS, + DCRYPT_PADDING_RSA_PKCS1_OAEP, + DCRYPT_PADDING_RSA_PKCS1, /* for compatibility use only */ + DCRYPT_PADDING_RSA_NO, +}; + +struct dcrypt_settings { + /* OpenSSL engine to use */ + const char *crypto_device; + /* Look for backends in this directory */ + const char *module_dir; +}; + +struct dcrypt_raw_key { + const void *parameter; + size_t len; +}; + +ARRAY_DEFINE_TYPE(dcrypt_raw_key, struct dcrypt_raw_key); + +/** + * load and initialize dcrypt backend, use either openssl or gnutls + */ +bool dcrypt_initialize(const char *backend, const struct dcrypt_settings *set, + const char **error_r); + +/** + * Returns TRUE if dcrypt has been initialized. + */ +bool dcrypt_is_initialized(void); + +/** + * deinitialize dcrypt. + * + * NOTE: Do not call this function if you are going to use dcrypt later on. + * Deinitializing the library using this will not allow it to be reinitialized + * when using OpenSSL. + */ +void dcrypt_deinitialize(void); + +/** + * create symmetric context + */ +bool dcrypt_ctx_sym_create(const char *algorithm, enum dcrypt_sym_mode mode, + struct dcrypt_context_symmetric **ctx_r, + const char **error_r); + +/** + * destroy symmetric context and free memory + */ +void dcrypt_ctx_sym_destroy(struct dcrypt_context_symmetric **ctx); + +/** + * key and IV manipulation functions + */ +void dcrypt_ctx_sym_set_key(struct dcrypt_context_symmetric *ctx, + const unsigned char *key, size_t key_len); +void dcrypt_ctx_sym_set_iv(struct dcrypt_context_symmetric *ctx, + const unsigned char *iv, size_t iv_len); +void dcrypt_ctx_sym_set_key_iv_random(struct dcrypt_context_symmetric *ctx); +bool dcrypt_ctx_sym_get_key(struct dcrypt_context_symmetric *ctx, buffer_t *key); +bool dcrypt_ctx_sym_get_iv(struct dcrypt_context_symmetric *ctx, buffer_t *iv); + +/** + * turn padding on/off (default: on) + */ +void dcrypt_ctx_sym_set_padding(struct dcrypt_context_symmetric *ctx, + bool padding); + +/** + * authentication data manipulation (use with GCM only) + */ +void dcrypt_ctx_sym_set_aad(struct dcrypt_context_symmetric *ctx, + const unsigned char *aad, size_t aad_len); +bool dcrypt_ctx_sym_get_aad(struct dcrypt_context_symmetric *ctx, + buffer_t *aad); +/** + * result tag from aead (use with GCM only) + */ +void dcrypt_ctx_sym_set_tag(struct dcrypt_context_symmetric *ctx, + const unsigned char *tag, size_t tag_len); +bool dcrypt_ctx_sym_get_tag(struct dcrypt_context_symmetric *ctx, + buffer_t *tag); + +/* get various lengths */ +unsigned int dcrypt_ctx_sym_get_key_length(struct dcrypt_context_symmetric *ctx); +unsigned int dcrypt_ctx_sym_get_iv_length(struct dcrypt_context_symmetric *ctx); +unsigned int dcrypt_ctx_sym_get_block_size(struct dcrypt_context_symmetric *ctx); + +/** + * initialize crypto + */ +bool dcrypt_ctx_sym_init(struct dcrypt_context_symmetric *ctx, + const char **error_r); + +/** + * update with data + */ +bool dcrypt_ctx_sym_update(struct dcrypt_context_symmetric *ctx, + const unsigned char *data, size_t data_len, + buffer_t *result, const char **error_r); + +/** + * perform final step (may or may not emit data) + */ +bool dcrypt_ctx_sym_final(struct dcrypt_context_symmetric *ctx, + buffer_t *result, const char **error_r); + +/** + * create HMAC context, algorithm is digest algorithm + */ +bool dcrypt_ctx_hmac_create(const char *algorithm, + struct dcrypt_context_hmac **ctx_r, + const char **error_r); +/** + * destroy HMAC context and free memory + */ +void dcrypt_ctx_hmac_destroy(struct dcrypt_context_hmac **ctx); + +/** + * hmac key manipulation + */ +void dcrypt_ctx_hmac_set_key(struct dcrypt_context_hmac *ctx, + const unsigned char *key, size_t key_len); +bool dcrypt_ctx_hmac_get_key(struct dcrypt_context_hmac *ctx, buffer_t *key); +void dcrypt_ctx_hmac_set_key_random(struct dcrypt_context_hmac *ctx); + +/** + * get digest length for HMAC + */ +unsigned int dcrypt_ctx_hmac_get_digest_length(struct dcrypt_context_hmac *ctx); + +/** + * initialize hmac + */ +bool dcrypt_ctx_hmac_init(struct dcrypt_context_hmac *ctx, + const char **error_r); + +/** + * update hmac context with data + */ +bool dcrypt_ctx_hmac_update(struct dcrypt_context_hmac *ctx, + const unsigned char *data, size_t data_len, + const char **error_r); + +/** + * perform final rounds and retrieve result + */ +bool dcrypt_ctx_hmac_final(struct dcrypt_context_hmac *ctx, buffer_t *result, + const char **error_r); + +/** + * Elliptic Curve based Diffie-Heffman shared secret derivation */ +bool dcrypt_ecdh_derive_secret(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key *pub_key, + buffer_t *shared_secret, + const char **error_r); +/** + * Helpers for DCRYPT file format */ +bool dcrypt_ecdh_derive_secret_local(struct dcrypt_private_key *local_key, + buffer_t *R, buffer_t *S, + const char **error_r); +bool dcrypt_ecdh_derive_secret_peer(struct dcrypt_public_key *peer_key, + buffer_t *R, buffer_t *S, + const char **error_r); + +/** Signature functions + algorithm is name of digest algorithm to use, such as SHA256. + + both RSA and EC keys are supported. +*/ + +/* returns false on error, true on success */ +bool dcrypt_sign(struct dcrypt_private_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, buffer_t *signature_r, + enum dcrypt_padding padding, const char **error_r); + +/* check valid_r for signature validity + false return means it wasn't able to verify it for other reasons */ +bool dcrypt_verify(struct dcrypt_public_key *key, const char *algorithm, + enum dcrypt_signature_format format, + const void *data, size_t data_len, + const unsigned char *signature, size_t signature_len, + bool *valid_r, enum dcrypt_padding padding, + const char **error_r); + +/** + * generate cryptographic data from password and salt. Use 1000-10000 for rounds. + */ +bool dcrypt_pbkdf2(const unsigned char *password, size_t password_len, + const unsigned char *salt, size_t salt_len, + const char *hash, unsigned int rounds, + buffer_t *result, unsigned int result_len, + const char **error_r); + +bool dcrypt_keypair_generate(struct dcrypt_keypair *pair_r, + enum dcrypt_key_type kind, unsigned int bits, + const char *curve, const char **error_r); + +/** + * load loads key structure from external format. + * store stores key structure into external format. + * + * you can provide either PASSWORD or ENC_KEY, not both. + */ +bool dcrypt_key_load_private(struct dcrypt_private_key **key_r, + const char *data, const char *password, + struct dcrypt_private_key *dec_key, + const char **error_r); + +bool dcrypt_key_load_public(struct dcrypt_public_key **key_r, + const char *data, const char **error_r); + +/** + * When encrypting with public key, the cipher parameter here must begin with + * ecdh-, for example ecdh-aes-256-ctr. An example of a valid cipher for + * encrypting with password would be aes-256-ctr. + */ +bool dcrypt_key_store_private(struct dcrypt_private_key *key, + enum dcrypt_key_format format, const char *cipher, + buffer_t *destination, const char *password, + struct dcrypt_public_key *enc_key, + const char **error_r); + +bool dcrypt_key_store_public(struct dcrypt_public_key *key, + enum dcrypt_key_format format, + buffer_t *destination, const char **error_r); + +void dcrypt_key_convert_private_to_public(struct dcrypt_private_key *priv_key, + struct dcrypt_public_key **pub_key_r); + +void dcrypt_keypair_unref(struct dcrypt_keypair *keypair); +void dcrypt_key_ref_public(struct dcrypt_public_key *key); +void dcrypt_key_ref_private(struct dcrypt_private_key *key); +void dcrypt_key_unref_public(struct dcrypt_public_key **key); +void dcrypt_key_unref_private(struct dcrypt_private_key **key); + +enum dcrypt_key_type dcrypt_key_type_private(struct dcrypt_private_key *key); +enum dcrypt_key_type dcrypt_key_type_public(struct dcrypt_public_key *key); +/* return digest of key */ +bool dcrypt_key_id_public(struct dcrypt_public_key *key, const char *algorithm, + buffer_t *result, const char **error_r); +/* return SHA1 sum of key */ +bool dcrypt_key_id_public_old(struct dcrypt_public_key *key, buffer_t *result, + const char **error_r); +/* return digest of key */ +bool dcrypt_key_id_private(struct dcrypt_private_key *key, + const char *algorithm, buffer_t *result, + const char **error_r); +/* return SHA1 sum of key */ +bool dcrypt_key_id_private_old(struct dcrypt_private_key *key, + buffer_t *result, const char **error_r); + +/* return raw private key: + Only ECC supported currently + + returns OID bytes and private key in bigendian bytes +*/ +bool dcrypt_key_store_private_raw(struct dcrypt_private_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r); + +/* return raw public key + Only ECC supported currently + + returns OID bytes and public key in compressed form (z||x) +*/ +bool dcrypt_key_store_public_raw(struct dcrypt_public_key *key, + pool_t pool, + enum dcrypt_key_type *key_type_r, + ARRAY_TYPE(dcrypt_raw_key) *keys_r, + const char **error_r); + +/* load raw private key: + Only ECC supported currently + + expects OID bytes and private key in bigendian bytes +*/ +bool dcrypt_key_load_private_raw(struct dcrypt_private_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r); + +/* load raw public key + Only ECC supported currently + + expects OID bytes and public key bytes. +*/ +bool dcrypt_key_load_public_raw(struct dcrypt_public_key **key_r, + enum dcrypt_key_type key_type, + const ARRAY_TYPE(dcrypt_raw_key) *keys, + const char **error_r); + +/* for ECC only - return textual name or OID of used curve */ +bool dcrypt_key_get_curve_public(struct dcrypt_public_key *key, + const char **curve_r, const char **error_r); + +bool dcrypt_key_string_get_info(const char *key_data, + enum dcrypt_key_format *format_r, + enum dcrypt_key_version *version_r, + enum dcrypt_key_kind *kind_r, + enum dcrypt_key_encryption_type *encryption_type_r, + const char **encryption_key_hash_r, + const char **key_hash_r, const char **error_r); + +/* Get/Set key identifier, this is optional opaque string identifying the key. */ +const char *dcrypt_key_get_id_public(struct dcrypt_public_key *key); +const char *dcrypt_key_get_id_private(struct dcrypt_private_key *key); +void dcrypt_key_set_id_public(struct dcrypt_public_key *key, const char *id); +void dcrypt_key_set_id_private(struct dcrypt_private_key *key, const char *id); + +/* Get/Set key usage, optional. Defaults to NONE */ +enum dcrypt_key_usage dcrypt_key_get_usage_public(struct dcrypt_public_key *key); +enum dcrypt_key_usage dcrypt_key_get_usage_private(struct dcrypt_private_key *key); +void dcrypt_key_set_usage_public(struct dcrypt_public_key *key, + enum dcrypt_key_usage usage); +void dcrypt_key_set_usage_private(struct dcrypt_private_key *key, + enum dcrypt_key_usage usage); + +/* RSA stuff */ +bool dcrypt_rsa_encrypt(struct dcrypt_public_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r); +bool dcrypt_rsa_decrypt(struct dcrypt_private_key *key, + const unsigned char *data, size_t data_len, + buffer_t *result, enum dcrypt_padding padding, + const char **error_r); + +/* OID stuff */ +const char *dcrypt_oid2name(const unsigned char *oid, size_t oid_len, + const char **error_r); +bool dcrypt_name2oid(const char *name, buffer_t *oid, const char **error_r); + +#endif diff --git a/src/lib-dcrypt/istream-decrypt.c b/src/lib-dcrypt/istream-decrypt.c new file mode 100644 index 0000000..be550fb --- /dev/null +++ b/src/lib-dcrypt/istream-decrypt.c @@ -0,0 +1,1146 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "randgen.h" +#include "safe-memset.h" +#include "hash-method.h" +#include "sha2.h" +#include "memarea.h" +#include "dcrypt.h" +#include "istream.h" +#include "istream-decrypt.h" +#include "istream-private.h" +#include "dcrypt-iostream.h" + +#include "hex-binary.h" + +#include <arpa/inet.h> + +#define ISTREAM_DECRYPT_READ_FIRST 15 + +struct decrypt_istream_snapshot { + struct istream_snapshot snapshot; + struct decrypt_istream *dstream; + buffer_t *buf; +}; + +struct decrypt_istream { + struct istream_private istream; + buffer_t *buf; + bool symmetric; + + i_stream_decrypt_get_key_callback_t *key_callback; + void *key_context; + + struct dcrypt_private_key *priv_key; + bool initialized; + bool finalized; + bool use_mac; + bool snapshot_pending; + + uoff_t ftr, pos; + enum io_stream_encrypt_flags flags; + + /* original iv, in case seeking is done, future feature */ + unsigned char *iv; + + struct dcrypt_context_symmetric *ctx_sym; + struct dcrypt_context_hmac *ctx_mac; + + enum decrypt_istream_format format; +}; + +static void i_stream_decrypt_reset(struct decrypt_istream *dstream) +{ + dstream->finalized = FALSE; + dstream->use_mac = FALSE; + + dstream->ftr = 0; + dstream->pos = 0; + dstream->flags = 0; + + if (!dstream->symmetric) { + dstream->initialized = FALSE; + if (dstream->ctx_sym != NULL) + dcrypt_ctx_sym_destroy(&dstream->ctx_sym); + if (dstream->ctx_mac != NULL) + dcrypt_ctx_hmac_destroy(&dstream->ctx_mac); + } + i_free(dstream->iv); + dstream->format = DECRYPT_FORMAT_V1; +} + +enum decrypt_istream_format +i_stream_encrypt_get_format(const struct istream *input) +{ + return ((const struct decrypt_istream*)input->real_stream)->format; +} + +enum io_stream_encrypt_flags +i_stream_encrypt_get_flags(const struct istream *input) +{ + return ((const struct decrypt_istream*)input->real_stream)->flags; +} + +static ssize_t +i_stream_decrypt_read_header_v1(struct decrypt_istream *stream, + const unsigned char *data, size_t mlen) +{ + const char *error = NULL; + size_t keydata_len = 0; + uint16_t len; + int ec, i = 0; + + const unsigned char *digest_pos = NULL, *key_digest_pos = NULL, + *key_ct_pos = NULL; + size_t digest_len = 0, key_ct_len = 0, key_digest_size = 0; + + buffer_t ephemeral_key; + buffer_t *secret = t_buffer_create(256); + buffer_t *key = t_buffer_create(256); + + if (mlen < 2) + return 0; + keydata_len = be16_to_cpu_unaligned(data); + if (mlen-2 < keydata_len) { + /* try to read more */ + return 0; + } + + data+=2; + mlen-=2; + + while (i < 4 && mlen > 2) { + memcpy(&len, data, 2); + len = ntohs(len); + if (len == 0 || len > mlen-2) + break; + data += 2; + mlen -= 2; + + switch(i++) { + case 0: + buffer_create_from_const_data(&ephemeral_key, + data, len); + break; + case 1: + /* public key id */ + digest_pos = data; + digest_len = len; + break; + case 2: + /* encryption key digest */ + key_digest_pos = data; + key_digest_size = len; + break; + case 3: + /* encrypted key data */ + key_ct_pos = data; + key_ct_len = len; + break; + } + data += len; + mlen -= len; + } + + if (i < 4) { + io_stream_set_error(&stream->istream.iostream, + "Invalid or corrupted header"); + /* was it consumed? */ + stream->istream.istream.stream_errno = + mlen > 2 ? EINVAL : EPIPE; + return -1; + } + + /* we don't have a private key */ + if (stream->priv_key == NULL) { + /* see if we can get one */ + if (stream->key_callback != NULL) { + const char *key_id = + binary_to_hex(digest_pos, digest_len); + int ret = stream->key_callback(key_id, + &stream->priv_key, &error, stream->key_context); + if (ret < 0) { + io_stream_set_error(&stream->istream.iostream, + "Private key not available: %s", + error); + return -1; + } + if (ret == 0) { + io_stream_set_error(&stream->istream.iostream, + "Private key not available"); + return -1; + } + } else { + io_stream_set_error(&stream->istream.iostream, + "Private key not available"); + return -1; + } + } + + buffer_t *check = t_buffer_create(32); + + if (!dcrypt_key_id_private_old(stream->priv_key, check, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Cannot get public key hash: %s", error); + return -1; + } else { + if (memcmp(digest_pos, check->data, + I_MIN(digest_len,check->used)) != 0) { + io_stream_set_error(&stream->istream.iostream, + "Private key not available"); + return -1; + } + } + + /* derive shared secret */ + if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, + &ephemeral_key, secret, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Cannot perform ECDH: %s", error); + return -1; + } + + /* run it thru SHA256 once */ + const struct hash_method *hash = &hash_method_sha256; + unsigned char hctx[hash->context_size]; + unsigned char hres[hash->digest_size]; + hash->init(hctx); + hash->loop(hctx, secret->data, secret->used); + hash->result(hctx, hres); + safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); + + /* NB! The old code was broken and used this kind of IV - it is not + correct, but we need to stay compatible with old data */ + + /* use it to decrypt the actual encryption key */ + struct dcrypt_context_symmetric *dctx; + if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, + &dctx, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: %s", error); + return -1; + } + + ec = 0; + dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*) + "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); + dcrypt_ctx_sym_set_key(dctx, hres, hash->digest_size); + if (!dcrypt_ctx_sym_init(dctx, &error) || + !dcrypt_ctx_sym_update(dctx, key_ct_pos, key_ct_len, key, &error) || + !dcrypt_ctx_sym_final(dctx, key, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: %s", error); + ec = -1; + } + dcrypt_ctx_sym_destroy(&dctx); + + if (ec != 0) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: %s", error); + return -1; + } + + /* see if we got the correct key */ + hash->init(hctx); + hash->loop(hctx, key->data, key->used); + hash->result(hctx, hres); + + if (key_digest_size != sizeof(hres)) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: " + "invalid digest length"); + return -1; + } + if (memcmp(hres, key_digest_pos, sizeof(hres)) != 0) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: " + "decrypted key is invalid"); + return -1; + } + + /* prime context with key */ + if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_DECRYPT, + &stream->ctx_sym, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption context create error: %s", + error); + return -1; + } + + /* Again, old code used this IV, so we have to use it too */ + dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*) + "\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0", 16); + dcrypt_ctx_sym_set_key(stream->ctx_sym, key->data, key->used); + + safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); + + if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption init error: %s", error); + return -1; + } + + stream->use_mac = FALSE; + stream->initialized = TRUE; + /* now we are ready to decrypt stream */ + + return sizeof(IOSTREAM_CRYPT_MAGIC) + 1 + 2 + keydata_len; +} + +static bool +get_msb32(const unsigned char **_data, const unsigned char *end, + uint32_t *num_r) +{ + const unsigned char *data = *_data; + if (end-data < 4) + return FALSE; + *num_r = be32_to_cpu_unaligned(data); + *_data += 4; + return TRUE; +} + +static bool +i_stream_decrypt_der(const unsigned char **_data, const unsigned char *end, + const char **str_r) +{ + const unsigned char *data = *_data; + unsigned int len; + + if (end-data < 2) + return FALSE; + /* get us DER encoded length */ + if ((data[1] & 0x80) != 0) { + /* two byte length */ + if (end-data < 3) + return FALSE; + len = ((data[1] & 0x7f) << 8) + data[2] + 3; + } else { + len = data[1] + 2; + } + if ((size_t)(end-data) < len) + return FALSE; + *str_r = dcrypt_oid2name(data, len, NULL); + *_data += len; + return TRUE; +} + +static ssize_t +i_stream_decrypt_key(struct decrypt_istream *stream, const char *malg, + unsigned int rounds, const unsigned char *data, + const unsigned char *end, buffer_t *key, size_t key_len) +{ + const char *error; + enum dcrypt_key_type ktype; + int keys; + bool have_key = FALSE; + unsigned char dgst[32]; + uint32_t val; + buffer_t buf; + + if (data == end) + return 0; + + keys = *data++; + + /* if we have a key, prefab the digest */ + if (stream->priv_key != NULL) { + buffer_create_from_data(&buf, dgst, sizeof(dgst)); + if (!dcrypt_key_id_private(stream->priv_key, "sha256", &buf, + &error)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "dcrypt_key_id_private failed: %s", + error); + return -1; + } + } else if (stream->key_callback == NULL) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "no private key available"); + return -1; + } + + /* for each key */ + for(;keys>0;keys--) { + if ((size_t)(end-data) < 1 + (ssize_t)sizeof(dgst)) + return 0; + ktype = *data++; + + if (stream->priv_key != NULL) { + /* see if key matches to the one we have */ + if (memcmp(dgst, data, sizeof(dgst)) == 0) { + have_key = TRUE; + break; + } + } else if (stream->key_callback != NULL) { + const char *hexdgst = /* digest length */ + binary_to_hex(data, sizeof(dgst)); + if (stream->priv_key != NULL) + dcrypt_key_unref_private(&stream->priv_key); + /* hope you going to give us right key.. */ + int ret = stream->key_callback(hexdgst, + &stream->priv_key, &error, stream->key_context); + if (ret < 0) { + io_stream_set_error(&stream->istream.iostream, + "Private key not available: " + "%s", error); + return -1; + } + if (ret > 0) { + have_key = TRUE; + break; + } + } + data += sizeof(dgst); + + /* wasn't correct key, skip over some data */ + if (!get_msb32(&data, end, &val) || + !get_msb32(&data, end, &val)) + return 0; + } + + /* didn't find matching key */ + if (!have_key) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "no private key available"); + return -1; + } + + data += sizeof(dgst); + + const unsigned char *ephemeral_key; + uint32_t ep_key_len; + const unsigned char *encrypted_key; + uint32_t eklen; + const unsigned char *ekhash; + uint32_t ekhash_len; + + /* read ephemeral key (can be missing for RSA) */ + if (!get_msb32(&data, end, &ep_key_len) || + (size_t)(end-data) < ep_key_len) + return 0; + ephemeral_key = data; + data += ep_key_len; + + /* read encrypted key */ + if (!get_msb32(&data, end, &eklen) || (size_t)(end-data) < eklen) + return 0; + encrypted_key = data; + data += eklen; + + /* read key data hash */ + if (!get_msb32(&data, end, &ekhash_len) || + (size_t)(end-data) < ekhash_len) + return 0; + ekhash = data; + data += ekhash_len; + + /* decrypt the seed */ + if (ktype == DCRYPT_KEY_RSA) { + if (!dcrypt_rsa_decrypt(stream->priv_key, encrypted_key, eklen, + key, DCRYPT_PADDING_RSA_PKCS1_OAEP, + &error)) { + io_stream_set_error(&stream->istream.iostream, + "key decryption error: %s", error); + return -1; + } + } else if (ktype == DCRYPT_KEY_EC) { + /* perform ECDHE */ + buffer_t *temp_key = t_buffer_create(256); + buffer_t *secret = t_buffer_create(256); + buffer_t peer_key; + buffer_create_from_const_data(&peer_key, + ephemeral_key, ep_key_len); + if (!dcrypt_ecdh_derive_secret_local(stream->priv_key, + &peer_key, secret, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: corrupted header"); + return -1; + } + + /* use shared secret and peer key to generate decryption key, + AES-256-CBC has 32 byte key and 16 byte IV */ + if (!dcrypt_pbkdf2(secret->data, secret->used, + peer_key.data, peer_key.used, + malg, rounds, temp_key, 32+16, &error)) { + safe_memset(buffer_get_modifiable_data(secret, 0), + 0, secret->used); + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: %s", error); + return -1; + } + + safe_memset(buffer_get_modifiable_data(secret, 0), + 0, secret->used); + if (temp_key->used != 32+16) { + safe_memset(buffer_get_modifiable_data(temp_key, 0), + 0, temp_key->used); + io_stream_set_error(&stream->istream.iostream, + "Cannot perform key decryption: " + "invalid temporary key"); + return -1; + } + struct dcrypt_context_symmetric *dctx; + if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_DECRYPT, + &dctx, &error)) { + safe_memset(buffer_get_modifiable_data(temp_key, 0), + 0, temp_key->used); + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: %s", error); + return -1; + } + const unsigned char *ptr = temp_key->data; + + /* we use ephemeral_key for IV */ + dcrypt_ctx_sym_set_key(dctx, ptr, 32); + dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); + safe_memset(buffer_get_modifiable_data(temp_key, 0), + 0, temp_key->used); + + int ec = 0; + if (!dcrypt_ctx_sym_init(dctx, &error) || + !dcrypt_ctx_sym_update(dctx, encrypted_key, eklen, + key, &error) || + !dcrypt_ctx_sym_final(dctx, key, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Cannot perform key decryption: %s", + error); + ec = -1; + } + + if (key->used != key_len) { + io_stream_set_error(&stream->istream.iostream, + "Cannot perform key decryption: " + "invalid key length"); + ec = -1; + } + + dcrypt_ctx_sym_destroy(&dctx); + if (ec != 0) return ec; + } else { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "unsupported key type 0x%02x", ktype); + return -1; + } + + /* make sure we were able to decrypt the encrypted key correctly */ + const struct hash_method *hash = hash_method_lookup(t_str_lcase(malg)); + if (hash == NULL) { + safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "unsupported hash algorithm: %s", malg); + return -1; + } + unsigned char hctx[hash->context_size]; + unsigned char hres[hash->digest_size]; + hash->init(hctx); + hash->loop(hctx, key->data, key->used); + hash->result(hctx, hres); + + for(int i = 1; i < 2049; i++) { + uint32_t i_msb = cpu32_to_be(i); + + hash->init(hctx); + hash->loop(hctx, hres, sizeof(hres)); + hash->loop(hctx, &i_msb, sizeof(i_msb)); + hash->result(hctx, hres); + } + + /* do the comparison */ + if (memcmp(ekhash, hres, I_MIN(ekhash_len, sizeof(hres))) != 0) { + safe_memset(buffer_get_modifiable_data(key, 0), 0, key->used); + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "corrupted header ekhash"); + return -1; + } + return 1; +} + +static int +i_stream_decrypt_header_contents(struct decrypt_istream *stream, + const unsigned char *data, size_t size) +{ + const unsigned char *end = data + size; + bool failed = FALSE; + + /* read cipher OID */ + const char *calg; + if (!i_stream_decrypt_der(&data, end, &calg)) + return 0; + if (calg == NULL || + !dcrypt_ctx_sym_create(calg, DCRYPT_MODE_DECRYPT, + &stream->ctx_sym, NULL)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "unsupported/invalid cipher: %s", calg); + return -1; + } + + /* read MAC oid (MAC is used for PBKDF2 and key data digest, too) */ + const char *malg; + if (!i_stream_decrypt_der(&data, end, &malg)) + return 0; + if (malg == NULL || !dcrypt_ctx_hmac_create(malg, &stream->ctx_mac, NULL)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: " + "unsupported/invalid MAC algorithm: %s", + malg); + return -1; + } + + /* read rounds (for PBKDF2) */ + uint32_t rounds; + if (!get_msb32(&data, end, &rounds)) + return 0; + /* read key data length */ + uint32_t kdlen; + if (!get_msb32(&data, end, &kdlen)) + return 0; + + size_t tagsize; + + if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + tagsize = IOSTREAM_TAG_SIZE; + } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + tagsize = IOSTREAM_TAG_SIZE; + } else { + tagsize = 0; + } + + /* how much key data we should be getting */ + size_t kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; + buffer_t *keydata = t_buffer_create(kl); + + /* try to decrypt the keydata with a private key */ + int ret; + if ((ret = i_stream_decrypt_key(stream, malg, rounds, data, + end, keydata, kl)) <= 0) + return ret; + + /* oh, it worked! */ + const unsigned char *ptr = keydata->data; + if (keydata->used != kl) { + /* but returned wrong amount of data */ + io_stream_set_error(&stream->istream.iostream, + "Key decryption error: " + "Key data length mismatch"); + return -1; + } + + /* prime contexts */ + dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, + dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); + ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); + dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); + stream->iv = i_malloc(dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); + memcpy(stream->iv, ptr, dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); + ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); + + /* based on the chosen MAC, initialize HMAC or AEAD */ + if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + const char *error; + dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); + if (!dcrypt_ctx_hmac_init(stream->ctx_mac, &error)) { + io_stream_set_error(&stream->istream.iostream, + "MAC error: %s", error); + stream->istream.istream.stream_errno = EINVAL; + failed = TRUE; + } + stream->ftr = dcrypt_ctx_hmac_get_digest_length(stream->ctx_mac); + stream->use_mac = TRUE; + } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); + stream->ftr = tagsize; + stream->use_mac = TRUE; + } else { + stream->use_mac = FALSE; + } + /* destroy private key data */ + safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); + buffer_set_used_size(keydata, 0); + return failed ? -1 : 1; +} + +static ssize_t +i_stream_decrypt_read_header(struct decrypt_istream *stream, + const unsigned char *data, size_t mlen) +{ + const char *error; + const unsigned char *end = data + mlen; + + /* check magic */ + if (mlen < sizeof(IOSTREAM_CRYPT_MAGIC)) + return 0; + if (memcmp(data, IOSTREAM_CRYPT_MAGIC, sizeof(IOSTREAM_CRYPT_MAGIC)) != 0) { + io_stream_set_error(&stream->istream.iostream, + "Stream is not encrypted (invalid magic)"); + stream->istream.istream.stream_errno = EINVAL; + return -1; + } + data += sizeof(IOSTREAM_CRYPT_MAGIC); + + if (data >= end) + return 0; /* read more? */ + + /* check version */ + if (*data == '\x01') { + stream->format = DECRYPT_FORMAT_V1; + return i_stream_decrypt_read_header_v1(stream, data+1, + end - (data+1)); + } else if (*data != '\x02') { + io_stream_set_error(&stream->istream.iostream, + "Unsupported encrypted data 0x%02x", *data); + return -1; + } + + stream->format = DECRYPT_FORMAT_V2; + + data++; + + /* read flags */ + uint32_t flags; + if (!get_msb32(&data, end, &flags)) + return 0; + stream->flags = flags; + + /* get the total length of header */ + uint32_t hdr_len; + if (!get_msb32(&data, end, &hdr_len)) + return 0; + /* do not forget stream format */ + if ((size_t)(end-data)+1 < hdr_len) + return 0; + + int ret; + if ((ret = i_stream_decrypt_header_contents(stream, data, hdr_len)) < 0) + return -1; + else if (ret == 0) { + io_stream_set_error(&stream->istream.iostream, + "Decryption error: truncate header length"); + stream->istream.istream.stream_errno = EPIPE; + return -1; + } + stream->initialized = TRUE; + + /* if it all went well, try to initialize decryption context */ + if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { + io_stream_set_error(&stream->istream.iostream, + "Decryption init error: %s", error); + return -1; + } + return hdr_len; +} + +static void +i_stream_decrypt_realloc_buf_if_needed(struct decrypt_istream *dstream) +{ + if (!dstream->snapshot_pending) + return; + + /* buf exists in a snapshot. Leave it be and create a copy of it + that we modify. */ + buffer_t *old_buf = dstream->buf; + dstream->buf = buffer_create_dynamic(default_pool, + I_MAX(512, old_buf->used)); + buffer_append(dstream->buf, old_buf->data, old_buf->used); + dstream->snapshot_pending = FALSE; + + dstream->istream.buffer = dstream->buf->data; +} + +static ssize_t +i_stream_decrypt_read(struct istream_private *stream) +{ + struct decrypt_istream *dstream = + (struct decrypt_istream *)stream; + const unsigned char *data; + size_t size, decrypt_size; + const char *error = NULL; + int ret; + bool check_mac = FALSE; + + /* not if it's broken */ + if (stream->istream.stream_errno != 0) + return -1; + + i_stream_decrypt_realloc_buf_if_needed(dstream); + for (;;) { + /* remove skipped data from buffer */ + if (stream->skip > 0) { + i_assert(stream->skip <= dstream->buf->used); + buffer_delete(dstream->buf, 0, stream->skip); + stream->pos -= stream->skip; + stream->skip = 0; + } + + stream->buffer = dstream->buf->data; + + i_assert(stream->pos <= dstream->buf->used); + if (stream->pos >= dstream->istream.max_buffer_size) { + /* stream buffer still at maximum */ + return -2; + } + + /* if something is already decrypted, return as much of it as + we can */ + if (dstream->initialized && dstream->buf->used > 0) { + size_t new_pos, bytes; + + /* only return up to max_buffer_size bytes, even when + buffer actually has more, as not to confuse the + caller */ + if (dstream->buf->used <= + dstream->istream.max_buffer_size) { + new_pos = dstream->buf->used; + if (dstream->finalized) + stream->istream.eof = TRUE; + } else { + new_pos = dstream->istream.max_buffer_size; + } + + bytes = new_pos - stream->pos; + stream->pos = new_pos; + if (bytes > 0) + return (ssize_t)bytes; + } + if (dstream->finalized) { + /* all data decrypted */ + stream->istream.eof = TRUE; + return -1; + } + + /* need to read more input */ + ret = i_stream_read_memarea(stream->parent); + if (ret == 0) + return ret; + + data = i_stream_get_data(stream->parent, &size); + + if (ret == -1 && + (size == 0 || stream->parent->stream_errno != 0)) { + stream->istream.stream_errno = + stream->parent->stream_errno; + + /* file was empty */ + if (!dstream->initialized && + size == 0 && stream->parent->eof) { + stream->istream.eof = TRUE; + return -1; + } + + if (stream->istream.stream_errno != 0) + return -1; + + if (!dstream->initialized) { + io_stream_set_error(&stream->iostream, + "Decryption error: %s", + "Input truncated in decryption header"); + stream->istream.stream_errno = EPIPE; + return -1; + } + + /* final block */ + if (dcrypt_ctx_sym_final(dstream->ctx_sym, + dstream->buf, &error)) { + dstream->finalized = TRUE; + continue; + } + io_stream_set_error(&stream->iostream, + "MAC error: %s", error); + stream->istream.stream_errno = EINVAL; + return -1; + } + + if (!dstream->initialized) { + ssize_t hret; + + if ((hret=i_stream_decrypt_read_header( + dstream, data, size)) <= 0) { + if (hret < 0) { + if (stream->istream.stream_errno == 0) + /* assume temporary failure */ + stream->istream.stream_errno = EIO; + return -1; + } + + if (hret == 0 && stream->parent->eof) { + /* not encrypted by us */ + stream->istream.stream_errno = EPIPE; + io_stream_set_error(&stream->iostream, + "Truncated header"); + return -1; + } + } + + if (hret == 0) { + /* see if we can get more data */ + if (ret == -2) { + stream->istream.stream_errno = EINVAL; + io_stream_set_error(&stream->iostream, + "Header too large " + "(more than %zu bytes)", + size); + return -1; + } + continue; + } else { + /* clean up buffer */ + safe_memset(buffer_get_modifiable_data(dstream->buf, 0), + 0, dstream->buf->used); + buffer_set_used_size(dstream->buf, 0); + i_stream_skip(stream->parent, hret); + } + + data = i_stream_get_data(stream->parent, &size); + } + decrypt_size = size; + + if (dstream->use_mac) { + if (stream->parent->eof) { + if (decrypt_size < dstream->ftr) { + io_stream_set_error(&stream->iostream, + "Decryption error: " + "footer is longer than data"); + stream->istream.stream_errno = EINVAL; + return -1; + } + check_mac = TRUE; + } else { + /* ignore footer's length of data until we + reach EOF */ + size -= dstream->ftr; + } + decrypt_size -= dstream->ftr; + if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + if (!dcrypt_ctx_hmac_update(dstream->ctx_mac, + data, decrypt_size, &error)) { + io_stream_set_error(&stream->iostream, + "MAC error: %s", error); + stream->istream.stream_errno = EINVAL; + return -1; + } + } + } + + if (check_mac) { + if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + unsigned char dgst[dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)]; + buffer_t db; + buffer_create_from_data(&db, dgst, sizeof(dgst)); + if (!dcrypt_ctx_hmac_final(dstream->ctx_mac, &db, &error)) { + io_stream_set_error(&stream->iostream, + "Cannot verify MAC: %s", error); + stream->istream.stream_errno = EINVAL; + return -1; + } + if (memcmp(dgst, data + decrypt_size, + dcrypt_ctx_hmac_get_digest_length(dstream->ctx_mac)) != 0) { + io_stream_set_error(&stream->iostream, + "Cannot verify MAC: mismatch"); + stream->istream.stream_errno = EINVAL; + return -1; + } + } else if ((dstream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + dcrypt_ctx_sym_set_tag(dstream->ctx_sym, + data + decrypt_size, + dstream->ftr); + } + } + + if (!dcrypt_ctx_sym_update(dstream->ctx_sym, + data, decrypt_size, dstream->buf, &error)) { + io_stream_set_error(&stream->iostream, + "Decryption error: %s", error); + stream->istream.stream_errno = EINVAL; + return -1; + } + i_stream_skip(stream->parent, size); + } +} + +static void +i_stream_decrypt_seek(struct istream_private *stream, uoff_t v_offset, + bool mark ATTR_UNUSED) +{ + struct decrypt_istream *dstream = + (struct decrypt_istream *)stream; + + i_stream_decrypt_realloc_buf_if_needed(dstream); + + if (i_stream_nonseekable_try_seek(stream, v_offset)) + return; + + /* have to seek backwards - reset crypt state and retry */ + i_stream_decrypt_reset(dstream); + if (!i_stream_nonseekable_try_seek(stream, v_offset)) + i_unreached(); +} + +static void i_stream_decrypt_close(struct iostream_private *stream, + bool close_parent) +{ + struct decrypt_istream *dstream = + (struct decrypt_istream *)stream; + + if (close_parent) + i_stream_close(dstream->istream.parent); +} + +static void +i_stream_decrypt_snapshot_free(struct istream_snapshot *_snapshot) +{ + struct decrypt_istream_snapshot *snapshot = + container_of(_snapshot, struct decrypt_istream_snapshot, + snapshot); + + if (snapshot->dstream->buf != snapshot->buf) + buffer_free(&snapshot->buf); + else { + i_assert(snapshot->dstream->snapshot_pending); + snapshot->dstream->snapshot_pending = FALSE; + } + i_free(snapshot); +} + +static struct istream_snapshot * +i_stream_decrypt_snapshot(struct istream_private *stream, + struct istream_snapshot *prev_snapshot) +{ + struct decrypt_istream *dstream = + (struct decrypt_istream *)stream; + struct decrypt_istream_snapshot *snapshot; + + if (stream->buffer != dstream->buf->data) { + /* reading body */ + return i_stream_default_snapshot(stream, prev_snapshot); + } + + /* snapshot the header buffer */ + snapshot = i_new(struct decrypt_istream_snapshot, 1); + snapshot->dstream = dstream; + snapshot->buf = dstream->buf; + snapshot->snapshot.free = i_stream_decrypt_snapshot_free; + snapshot->snapshot.prev_snapshot = prev_snapshot; + dstream->snapshot_pending = TRUE; + return &snapshot->snapshot; +} + +static void i_stream_decrypt_destroy(struct iostream_private *stream) +{ + struct decrypt_istream *dstream = + (struct decrypt_istream *)stream; + + if (!dstream->snapshot_pending) + buffer_free(&dstream->buf); + else { + /* Clear buf to make sure i_stream_decrypt_snapshot_free() + frees it. */ + dstream->buf = NULL; + } + + if (dstream->iv != NULL) + i_free_and_null(dstream->iv); + if (dstream->ctx_sym != NULL) + dcrypt_ctx_sym_destroy(&dstream->ctx_sym); + if (dstream->ctx_mac != NULL) + dcrypt_ctx_hmac_destroy(&dstream->ctx_mac); + if (dstream->priv_key != NULL) + dcrypt_key_unref_private(&dstream->priv_key); + + i_stream_unref(&dstream->istream.parent); +} + +static struct decrypt_istream * +i_stream_create_decrypt_common(struct istream *input) +{ + struct decrypt_istream *dstream; + + i_assert(input->real_stream->max_buffer_size > 0); + + dstream = i_new(struct decrypt_istream, 1); + dstream->istream.max_buffer_size = input->real_stream->max_buffer_size; + dstream->istream.read = i_stream_decrypt_read; + dstream->istream.snapshot = i_stream_decrypt_snapshot; + if (input->seekable) + dstream->istream.seek = i_stream_decrypt_seek; + dstream->istream.iostream.close = i_stream_decrypt_close; + dstream->istream.iostream.destroy = i_stream_decrypt_destroy; + + dstream->istream.istream.readable_fd = FALSE; + dstream->istream.istream.blocking = input->blocking; + dstream->istream.istream.seekable = input->seekable; + + dstream->buf = buffer_create_dynamic(default_pool, 512); + + (void)i_stream_create(&dstream->istream, input, + i_stream_get_fd(input), 0); + return dstream; +} + +struct istream * +i_stream_create_decrypt(struct istream *input, + struct dcrypt_private_key *priv_key) +{ + struct decrypt_istream *dstream; + + dstream = i_stream_create_decrypt_common(input); + dcrypt_key_ref_private(priv_key); + dstream->priv_key = priv_key; + return &dstream->istream.istream; +} + +struct istream * +i_stream_create_sym_decrypt(struct istream *input, + struct dcrypt_context_symmetric *ctx) +{ + const char *error; + int ec; + struct decrypt_istream *dstream; + dstream = i_stream_create_decrypt_common(input); + dstream->use_mac = FALSE; + dstream->initialized = TRUE; + dstream->symmetric = TRUE; + + if (!dcrypt_ctx_sym_init(ctx, &error)) ec = -1; + else ec = 0; + + dstream->ctx_sym = ctx; + + if (ec != 0) { + io_stream_set_error(&dstream->istream.iostream, + "Cannot initialize decryption: %s", error); + dstream->istream.istream.stream_errno = EIO; + }; + + return &dstream->istream.istream; +} + +struct istream * +i_stream_create_decrypt_callback(struct istream *input, + i_stream_decrypt_get_key_callback_t *callback, + void *context) +{ + struct decrypt_istream *dstream; + + i_assert(callback != NULL); + + dstream = i_stream_create_decrypt_common(input); + dstream->key_callback = callback; + dstream->key_context = context; + return &dstream->istream.istream; +} diff --git a/src/lib-dcrypt/istream-decrypt.h b/src/lib-dcrypt/istream-decrypt.h new file mode 100644 index 0000000..d531c00 --- /dev/null +++ b/src/lib-dcrypt/istream-decrypt.h @@ -0,0 +1,48 @@ +#ifndef ISTREAM_DECRYPT_H +#define ISTREAM_DECRYPT_H + +struct dcrypt_private_key; +struct dcrypt_context_symmetric; + +enum decrypt_istream_format { + DECRYPT_FORMAT_V1, + DECRYPT_FORMAT_V2 +}; + +/* Look for a private key for a specified public key digest and set it to + priv_key_r. Returns 1 if ok, 0 if key doesn't exist, -1 on internal error. + + Note that the private key will be unreferenced when the istream is + destroyed. If the callback is returning a persistent key, it must reference + the key first. (This is required, because otherwise a key newly created by + the callback couldn't be automatically freed.) */ +typedef int +i_stream_decrypt_get_key_callback_t(const char *pubkey_digest, + struct dcrypt_private_key **priv_key_r, + const char **error_r, void *context); + +struct istream * +i_stream_create_decrypt(struct istream *input, + struct dcrypt_private_key *priv_key); + +/* create stream for reading plain encrypted data with no header or MAC. + do not call dcrypt_ctx_sym_init + */ +struct istream * +i_stream_create_sym_decrypt(struct istream *input, + struct dcrypt_context_symmetric *ctx); + + +/* Decrypt the istream. When a private key is needed, the callback will be + called. This allows using multiple private keys for different mails. */ +struct istream * +i_stream_create_decrypt_callback(struct istream *input, + i_stream_decrypt_get_key_callback_t *callback, + void *context); + +enum decrypt_istream_format +i_stream_encrypt_get_format(const struct istream *input); +enum io_stream_encrypt_flags +i_stream_encrypt_get_flags(const struct istream *input); + +#endif diff --git a/src/lib-dcrypt/ostream-encrypt.c b/src/lib-dcrypt/ostream-encrypt.c new file mode 100644 index 0000000..c6e67f5 --- /dev/null +++ b/src/lib-dcrypt/ostream-encrypt.c @@ -0,0 +1,800 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "randgen.h" +#include "dcrypt-iostream.h" +#include "ostream-encrypt.h" +#include "ostream-private.h" +#include "hash-method.h" +#include "sha2.h" +#include "safe-memset.h" +#include "dcrypt.h" + +#include <arpa/inet.h> + +/* file struct dcrypt_public_key syntax + * magic (14 bytes) + * version (1 bytes) + * flags (4 bytes) + * size of header (4 bytes) + * sha1 of key id (20 bytes) + * cipher oid + * mac oid + * rounds (4 bytes) + * key data size (4 bytes) + * key data + * cipher data + * mac data (mac specific bytes) + */ + +#define IO_STREAM_ENCRYPT_SEED_SIZE 32 +#define IO_STREAM_ENCRYPT_ROUNDS 2048 + +struct encrypt_ostream { + struct ostream_private ostream; + + struct dcrypt_context_symmetric *ctx_sym; + struct dcrypt_context_hmac *ctx_mac; + + enum io_stream_encrypt_flags flags; + struct dcrypt_public_key *pub; + + unsigned char *key_data; + size_t key_data_len; + + buffer_t *cipher_oid; + buffer_t *mac_oid; + size_t block_size; + + bool finalized; + bool failed; + bool prefix_written; +}; + +static int +o_stream_encrypt_send(struct encrypt_ostream *stream, + const unsigned char *data, size_t size) +{ + ssize_t ec; + + ec = o_stream_send(stream->ostream.parent, data, size); + if (ec == (ssize_t)size) + return 0; + else if (ec < 0) { + o_stream_copy_error_from_parent(&stream->ostream); + return -1; + } else { + io_stream_set_error(&stream->ostream.iostream, + "ostream-encrypt: " + "Unexpectedly short write to parent stream"); + stream->ostream.ostream.stream_errno = EINVAL; + return -1; + } +} + +static int +o_stream_encrypt_send_header_v1(struct encrypt_ostream *stream) +{ + unsigned char c; + unsigned short s; + + i_assert(!stream->prefix_written); + stream->prefix_written = TRUE; + + buffer_t *values = t_buffer_create(256); + buffer_append(values, IOSTREAM_CRYPT_MAGIC, + sizeof(IOSTREAM_CRYPT_MAGIC)); + /* version */ + c = 1; + buffer_append(values, &c, 1); + /* key data length */ + s = htons(stream->key_data_len); + buffer_append(values, &s, 2); + /* then write key data */ + buffer_append(values, stream->key_data, stream->key_data_len); + i_free_and_null(stream->key_data); + + /* then send it to stream */ + return o_stream_encrypt_send(stream, values->data, values->used); +} + +static int +o_stream_encrypt_send_header_v2(struct encrypt_ostream *stream) +{ + unsigned char c; + unsigned int i; + + i_assert(!stream->prefix_written); + stream->prefix_written = TRUE; + + buffer_t *values = t_buffer_create(256); + buffer_append(values, IOSTREAM_CRYPT_MAGIC, + sizeof(IOSTREAM_CRYPT_MAGIC)); + c = 2; + buffer_append(values, &c, 1); + i = cpu32_to_be(stream->flags); + buffer_append(values, &i, 4); + /* store total length of header + 9 = version + flags + length + 8 = rounds + key data length + */ + i = cpu32_to_be(sizeof(IOSTREAM_CRYPT_MAGIC) + 9 + + stream->cipher_oid->used + stream->mac_oid->used + + 8 + stream->key_data_len); + buffer_append(values, &i, 4); + + buffer_append_buf(values, stream->cipher_oid, 0, SIZE_MAX); + buffer_append_buf(values, stream->mac_oid, 0, SIZE_MAX); + i = cpu32_to_be(IO_STREAM_ENCRYPT_ROUNDS); + buffer_append(values, &i, 4); + i = cpu32_to_be(stream->key_data_len); + buffer_append(values, &i, 4); + buffer_append(values, stream->key_data, stream->key_data_len); + i_free_and_null(stream->key_data); + + return o_stream_encrypt_send(stream, values->data, values->used); +} + +static int +o_stream_encrypt_keydata_create_v1(struct encrypt_ostream *stream) +{ + buffer_t *encrypted_key, *ephemeral_key, *secret, *res, buf; + const char *error = NULL; + const struct hash_method *hash = &hash_method_sha256; + + /* various temporary buffers */ + unsigned char seed[IO_STREAM_ENCRYPT_SEED_SIZE]; + unsigned char pkhash[hash->digest_size]; + unsigned char ekhash[hash->digest_size]; + unsigned char hres[hash->digest_size]; + + unsigned char hctx[hash->context_size]; + + /* hash the public key first */ + buffer_create_from_data(&buf, pkhash, sizeof(pkhash)); + if (!dcrypt_key_id_public_old(stream->pub, &buf, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Key hash failed: %s", error); + return -1; + } + + /* hash the key base */ + hash->init(hctx); + hash->loop(hctx, seed, sizeof(seed)); + hash->result(hctx, ekhash); + + ephemeral_key = t_buffer_create(256); + encrypted_key = t_buffer_create(256); + secret = t_buffer_create(256); + + if (!dcrypt_ecdh_derive_secret_peer(stream->pub, ephemeral_key, + secret, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Cannot perform ECDH: %s", error); + return -1; + } + + /* hash the secret data */ + hash->init(hctx); + hash->loop(hctx, secret->data, secret->used); + hash->result(hctx, hres); + safe_memset(buffer_get_modifiable_data(secret, 0), 0, secret->used); + + /* use it to encrypt the actual encryption key */ + struct dcrypt_context_symmetric *dctx; + if (!dcrypt_ctx_sym_create("aes-256-ctr", DCRYPT_MODE_ENCRYPT, + &dctx, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Key encryption error: %s", error); + return -1; + } + + random_fill(seed, sizeof(seed)); + hash->init(hctx); + hash->loop(hctx, seed, sizeof(seed)); + hash->result(hctx, ekhash); + + int ec = 0; + + /* NB! The old code was broken and used this kind of IV - it is not + correct, but we need to stay compatible with old data */ + dcrypt_ctx_sym_set_iv(dctx, (const unsigned char*) + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", 16); + dcrypt_ctx_sym_set_key(dctx, hres, sizeof(hres)); + + if (!dcrypt_ctx_sym_init(dctx, &error) || + !dcrypt_ctx_sym_update(dctx, seed, sizeof(seed), + encrypted_key, &error) || + !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { + ec = -1; + } + dcrypt_ctx_sym_destroy(&dctx); + + if (ec != 0) { + safe_memset(seed, 0, sizeof(seed)); + io_stream_set_error(&stream->ostream.iostream, + "Key encryption error: %s", error); + return -1; + } + + /* same as above */ + dcrypt_ctx_sym_set_iv(stream->ctx_sym, (const unsigned char*) + "\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00\x00", 16); + dcrypt_ctx_sym_set_key(stream->ctx_sym, seed, sizeof(seed)); + safe_memset(seed, 0, sizeof(seed)); + + if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Encryption init error: %s", error); + return -1; + } + + res = buffer_create_dynamic(default_pool, 256); + + /* ephemeral key */ + unsigned short s; + s = htons(ephemeral_key->used); + buffer_append(res, &s, 2); + buffer_append(res, ephemeral_key->data, ephemeral_key->used); + /* public key hash */ + s = htons(sizeof(pkhash)); + buffer_append(res, &s, 2); + buffer_append(res, pkhash, sizeof(pkhash)); + /* encrypted key hash */ + s = htons(sizeof(ekhash)); + buffer_append(res, &s, 2); + buffer_append(res, ekhash, sizeof(ekhash)); + /* encrypted key */ + s = htons(encrypted_key->used); + buffer_append(res, &s, 2); + buffer_append(res, encrypted_key->data, encrypted_key->used); + + stream->key_data_len = res->used; + stream->key_data = buffer_free_without_data(&res); + + return 0; +} + +static int +o_stream_encrypt_key_for_pubkey_v2(struct encrypt_ostream *stream, + const char *malg, const unsigned char *key, + size_t key_len, + struct dcrypt_public_key *pubkey, + buffer_t *res) +{ + enum dcrypt_key_type ktype; + const char *error; + buffer_t *encrypted_key, *ephemeral_key, *temp_key; + + ephemeral_key = t_buffer_create(256); + encrypted_key = t_buffer_create(256); + temp_key = t_buffer_create(48); + + ktype = dcrypt_key_type_public(pubkey); + + if (ktype == DCRYPT_KEY_RSA) { + /* encrypt key as R (as we don't need DH with RSA)*/ + if (!dcrypt_rsa_encrypt(pubkey, key, key_len, encrypted_key, + DCRYPT_PADDING_RSA_PKCS1_OAEP, + &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Cannot encrypt key data: %s", + error); + return -1; + } + } else if (ktype == DCRYPT_KEY_EC) { + /* R = our ephemeral public key */ + buffer_t *secret = t_buffer_create(256); + + /* derive ephemeral key and shared secret */ + if (!dcrypt_ecdh_derive_secret_peer(pubkey, ephemeral_key, + secret, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Cannot perform ECDH: %s", error); + return -1; + } + + /* use shared secret and ephemeral key to generate encryption + key/iv */ + if (!dcrypt_pbkdf2(secret->data, secret->used, + ephemeral_key->data, ephemeral_key->used, + malg, IO_STREAM_ENCRYPT_ROUNDS, temp_key, + 48, &error)) { + safe_memset(buffer_get_modifiable_data(secret, 0), + 0, secret->used); + io_stream_set_error(&stream->ostream.iostream, + "Cannot perform key encryption: %s", + error); + } + safe_memset(buffer_get_modifiable_data(secret, 0), + 0, secret->used); + + /* encrypt key with shared secret */ + struct dcrypt_context_symmetric *dctx; + if (!dcrypt_ctx_sym_create("AES-256-CBC", DCRYPT_MODE_ENCRYPT, + &dctx, &error)) { + safe_memset(buffer_get_modifiable_data(temp_key, 0), + 0, temp_key->used); + io_stream_set_error(&stream->ostream.iostream, + "Cannot perform key encryption: %s", + error); + return -1; + } + + const unsigned char *ptr = temp_key->data; + i_assert(temp_key->used == 48); + + dcrypt_ctx_sym_set_key(dctx, ptr, 32); + dcrypt_ctx_sym_set_iv(dctx, ptr+32, 16); + safe_memset(buffer_get_modifiable_data(temp_key, 0), + 0, temp_key->used); + + int ec = 0; + if (!dcrypt_ctx_sym_init(dctx, &error) || + !dcrypt_ctx_sym_update(dctx, key, key_len, + encrypted_key, &error) || + !dcrypt_ctx_sym_final(dctx, encrypted_key, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Cannot perform key encryption: %s", + error); + ec = -1; + } + + dcrypt_ctx_sym_destroy(&dctx); + if (ec != 0) return ec; + } else { + io_stream_set_error(&stream->ostream.iostream, + "Unsupported key type"); + return -1; + } + + /* store key type */ + char kt = ktype; + buffer_append(res, &kt, 1); + /* store hash of public key as ID */ + dcrypt_key_id_public(stream->pub, "sha256", res, NULL); + /* store ephemeral key (if present) */ + unsigned int val = cpu32_to_be(ephemeral_key->used); + buffer_append(res, &val, 4); + buffer_append_buf(res, ephemeral_key, 0, SIZE_MAX); + /* store encrypted key */ + val = cpu32_to_be(encrypted_key->used); + buffer_append(res, &val, 4); + buffer_append_buf(res, encrypted_key, 0, SIZE_MAX); + + return 0; +} + +static int +o_stream_encrypt_keydata_create_v2(struct encrypt_ostream *stream, + const char *malg) +{ + const struct hash_method *hash = hash_method_lookup(malg); + const char *error; + size_t tagsize; + const unsigned char *ptr; + size_t kl; + unsigned int val; + + buffer_t *keydata, *res; + + if (hash == NULL) { + io_stream_set_error(&stream->ostream.iostream, + "Encryption init error: " + "Hash algorithm '%s' not supported", malg); + return -1; + } + + /* key data length for internal use */ + if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + tagsize = IOSTREAM_TAG_SIZE; + } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + tagsize = IOSTREAM_TAG_SIZE; + } else { + /* do not include MAC */ + tagsize = 0; + } + + /* generate keydata length of random data for key/iv/mac */ + kl = dcrypt_ctx_sym_get_key_length(stream->ctx_sym) + + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym) + tagsize; + keydata = t_buffer_create(kl); + random_fill(buffer_append_space_unsafe(keydata, kl), kl); + buffer_set_used_size(keydata, kl); + ptr = keydata->data; + + res = buffer_create_dynamic(default_pool, 256); + + /* store number of public key(s) */ + buffer_append(res, "\1", 1); /* one key for now */ + + /* we can do multiple keys at this point, but do it only once now */ + if (o_stream_encrypt_key_for_pubkey_v2(stream, malg, ptr, kl, + stream->pub, res) != 0) { + buffer_free(&res); + return -1; + } + + /* create hash of the key data */ + unsigned char hctx[hash->context_size]; + unsigned char hres[hash->digest_size]; + hash->init(hctx); + hash->loop(hctx, ptr, kl); + hash->result(hctx, hres); + + for(int i = 1; i < 2049; i++) { + uint32_t i_msb = cpu32_to_be(i); + + hash->init(hctx); + hash->loop(hctx, hres, sizeof(hres)); + hash->loop(hctx, &i_msb, sizeof(i_msb)); + hash->result(hctx, hres); + } + + /* store key data hash */ + val = cpu32_to_be(sizeof(hres)); + buffer_append(res, &val, 4); + buffer_append(res, hres, sizeof(hres)); + + /* pick up key data that goes into stream */ + stream->key_data_len = res->used; + stream->key_data = buffer_free_without_data(&res); + + /* prime contexts */ + dcrypt_ctx_sym_set_key(stream->ctx_sym, ptr, + dcrypt_ctx_sym_get_key_length(stream->ctx_sym)); + ptr += dcrypt_ctx_sym_get_key_length(stream->ctx_sym); + dcrypt_ctx_sym_set_iv(stream->ctx_sym, ptr, + dcrypt_ctx_sym_get_iv_length(stream->ctx_sym)); + ptr += dcrypt_ctx_sym_get_iv_length(stream->ctx_sym); + + if ((stream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + dcrypt_ctx_hmac_set_key(stream->ctx_mac, ptr, tagsize); + dcrypt_ctx_hmac_init(stream->ctx_mac, &error); + } else if ((stream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + dcrypt_ctx_sym_set_aad(stream->ctx_sym, ptr, tagsize); + } + + /* clear out private key data */ + safe_memset(buffer_get_modifiable_data(keydata, 0), 0, keydata->used); + + if (!dcrypt_ctx_sym_init(stream->ctx_sym, &error)) { + io_stream_set_error(&stream->ostream.iostream, + "Encryption init error: %s", error); + return -1; + } + return 0; +} + +static ssize_t +o_stream_encrypt_sendv(struct ostream_private *stream, + const struct const_iovec *iov, unsigned int iov_count) +{ + struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; + const char *error; + ssize_t ec,total = 0; + + /* not if finalized */ + i_assert(!estream->finalized); + + /* write prefix */ + if (!estream->prefix_written) { + T_BEGIN { + if ((estream->flags & IO_STREAM_ENC_VERSION_1) == + IO_STREAM_ENC_VERSION_1) + ec = o_stream_encrypt_send_header_v1(estream); + else + ec = o_stream_encrypt_send_header_v2(estream); + } T_END; + if (ec < 0) { + return -1; + } + } + + /* buffer for encrypted data */ + unsigned char ciphertext[IO_BLOCK_SIZE]; + buffer_t buf; + buffer_create_from_data(&buf, ciphertext, sizeof(ciphertext)); + + /* encrypt & send all blocks of data at max ciphertext buffer's + length */ + for(unsigned int i = 0; i < iov_count; i++) { + size_t bl, off = 0, len = iov[i].iov_len; + const unsigned char *ptr = iov[i].iov_base; + while(len > 0) { + buffer_set_used_size(&buf, 0); + /* update can emite twice the size of input */ + bl = I_MIN(sizeof(ciphertext)/2, len); + + if (!dcrypt_ctx_sym_update(estream->ctx_sym, ptr + off, + bl, &buf, &error)) { + io_stream_set_error(&stream->iostream, + "Encryption failure: %s", + error); + return -1; + } + if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + /* update mac */ + if (!dcrypt_ctx_hmac_update(estream->ctx_mac, + buf.data, buf.used, &error)) { + io_stream_set_error(&stream->iostream, + "MAC failure: %s", error); + return -1; + } + } + + /* hopefully upstream can accommodate */ + if (o_stream_encrypt_send(estream, buf.data, buf.used) < 0) { + return -1; + } + + len -= bl; + off += bl; + total += bl; + } + } + + stream->ostream.offset += total; + return total; +} + +static int +o_stream_encrypt_finalize(struct ostream_private *stream) +{ + const char *error; + struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; + + if (estream->finalized) { + /* we've already flushed the encrypted output. */ + return 0; + } + estream->finalized = TRUE; + + /* if nothing was written, we are done */ + if (!estream->prefix_written) return 0; + + /* acquire last block */ + buffer_t *buf = t_buffer_create( + dcrypt_ctx_sym_get_block_size(estream->ctx_sym)); + if (!dcrypt_ctx_sym_final(estream->ctx_sym, buf, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Encryption failure: %s", error); + return -1; + } + /* sometimes final does not emit anything */ + if (buf->used > 0) { + /* update mac */ + if (((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC)) { + if (!dcrypt_ctx_hmac_update(estream->ctx_mac, buf->data, + buf->used, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "MAC failure: %s", error); + return -1; + } + } + if (o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { + return -1; + } + } + + /* write last mac bytes */ + buffer_set_used_size(buf, 0); + if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + if (!dcrypt_ctx_hmac_final(estream->ctx_mac, buf, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "MAC failure: %s", error); + return -1; + } + } else if ((estream->flags & IO_STREAM_ENC_INTEGRITY_AEAD) == + IO_STREAM_ENC_INTEGRITY_AEAD) { + dcrypt_ctx_sym_get_tag(estream->ctx_sym, buf); + i_assert(buf->used > 0); + } + if (buf->used > 0 && + o_stream_encrypt_send(estream, buf->data, buf->used) < 0) { + return -1; + } + + return 0; +} + +static int +o_stream_encrypt_flush(struct ostream_private *stream) +{ + struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; + + if (stream->finished && estream->ctx_sym != NULL && + !estream->finalized) { + if (o_stream_encrypt_finalize(&estream->ostream) < 0) + return -1; + } + + return o_stream_flush_parent(stream); +} + +static void +o_stream_encrypt_close(struct iostream_private *stream, + bool close_parent) +{ + struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; + + i_assert(estream->finalized || estream->ctx_sym == NULL || + estream->ostream.ostream.stream_errno != 0); + if (close_parent) + o_stream_close(estream->ostream.parent); +} + +static void +o_stream_encrypt_destroy(struct iostream_private *stream) +{ + struct encrypt_ostream *estream = (struct encrypt_ostream *)stream; + + /* release resources */ + if (estream->ctx_sym != NULL) + dcrypt_ctx_sym_destroy(&estream->ctx_sym); + if (estream->ctx_mac != NULL) + dcrypt_ctx_hmac_destroy(&estream->ctx_mac); + if (estream->key_data != NULL) + i_free(estream->key_data); + if (estream->cipher_oid != NULL) + buffer_free(&estream->cipher_oid); + if (estream->mac_oid != NULL) + buffer_free(&estream->mac_oid); + if (estream->pub != NULL) + dcrypt_key_unref_public(&estream->pub); + o_stream_unref(&estream->ostream.parent); +} + +static int +o_stream_encrypt_init(struct encrypt_ostream *estream, const char *algorithm) +{ + const char *error; + char *calg, *malg; + + if ((estream->flags & IO_STREAM_ENC_VERSION_1) == + IO_STREAM_ENC_VERSION_1) { + if (!dcrypt_ctx_sym_create("AES-256-CTR", DCRYPT_MODE_ENCRYPT, + &estream->ctx_sym, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Cannot create ostream-encrypt: %s", + error); + return -1; + } + /* disable MAC */ + estream->flags |= IO_STREAM_ENC_INTEGRITY_NONE; + /* then do keying */ + return o_stream_encrypt_keydata_create_v1(estream); + } else { + calg = t_strdup_noconst(algorithm); + malg = strrchr(calg, '-'); + + if (malg == NULL) { + io_stream_set_error(&estream->ostream.iostream, + "Invalid algorithm " + "(must be cipher-mac)"); + return -1; + } + (*malg++) = '\0'; + + if (!dcrypt_ctx_sym_create(calg, DCRYPT_MODE_ENCRYPT, + &estream->ctx_sym, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Cannot create ostream-encrypt: %s", + error); + return -1; + } + + /* create cipher and mac context, take note of OIDs */ + estream->cipher_oid = buffer_create_dynamic(default_pool, 12); + estream->block_size = + dcrypt_ctx_sym_get_block_size(estream->ctx_sym); + if (!dcrypt_name2oid(calg, estream->cipher_oid, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Cannot create ostream-encrypt: %s", + error); + return -1; + } + + /* mac context is optional */ + if ((estream->flags & IO_STREAM_ENC_INTEGRITY_HMAC) == + IO_STREAM_ENC_INTEGRITY_HMAC) { + if (!dcrypt_ctx_hmac_create(malg, &estream->ctx_mac, + &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Cannot create ostream-encrypt: %s", + error); + return -1; + } + } + + estream->mac_oid = buffer_create_dynamic(default_pool, 12); + if (!dcrypt_name2oid(malg, estream->mac_oid, &error)) { + io_stream_set_error(&estream->ostream.iostream, + "Cannot create ostream-encrypt: %s", error); + return -1; + } + + /* MAC algorithm is used for PBKDF2 and keydata hashing */ + return o_stream_encrypt_keydata_create_v2(estream, malg); + } +} + +static struct encrypt_ostream * +o_stream_create_encrypt_common(enum io_stream_encrypt_flags flags) +{ + struct encrypt_ostream *estream; + + estream = i_new(struct encrypt_ostream, 1); + estream->ostream.sendv = o_stream_encrypt_sendv; + estream->ostream.flush = o_stream_encrypt_flush; + estream->ostream.iostream.close = o_stream_encrypt_close; + estream->ostream.iostream.destroy = o_stream_encrypt_destroy; + + estream->flags = flags; + + return estream; +} + +struct ostream * +o_stream_create_encrypt(struct ostream *output, const char *algorithm, + struct dcrypt_public_key *box_pub, enum io_stream_encrypt_flags flags) +{ + struct encrypt_ostream *estream = o_stream_create_encrypt_common(flags); + int ec; + + dcrypt_key_ref_public(box_pub); + estream->pub = box_pub; + + T_BEGIN { + ec = o_stream_encrypt_init(estream, algorithm); + } T_END; + + struct ostream *os = o_stream_create(&estream->ostream, output, + o_stream_get_fd(output)); + + if (ec != 0) { + os->stream_errno = EINVAL; + } + + return os; +} + +struct ostream * +o_stream_create_sym_encrypt(struct ostream *output, + struct dcrypt_context_symmetric *ctx) +{ + struct encrypt_ostream *estream = + o_stream_create_encrypt_common(IO_STREAM_ENC_INTEGRITY_NONE); + const char *error; + int ec; + + estream->prefix_written = TRUE; + + if (!dcrypt_ctx_sym_init(estream->ctx_sym, &error)) + ec = -1; + else + ec = 0; + + estream->ctx_sym = ctx; + + struct ostream *os = o_stream_create(&estream->ostream, output, + o_stream_get_fd(output)); + if (ec != 0) { + io_stream_set_error(&estream->ostream.iostream, + "Could not initialize stream: %s", + error); + os->stream_errno = EINVAL; + } + + return os; +} diff --git a/src/lib-dcrypt/ostream-encrypt.h b/src/lib-dcrypt/ostream-encrypt.h new file mode 100644 index 0000000..0b28547 --- /dev/null +++ b/src/lib-dcrypt/ostream-encrypt.h @@ -0,0 +1,30 @@ +#ifndef OSTREAM_ENCRYPT_H +#define OSTREAM_ENCRYPT_H + +struct dcrypt_public_key; +struct dcrypt_context_symmetric; + +/** + * algorithm is in form AES-256-CBC-SHA1, recommended + * AES-256-GCM-SHA256 + * + * Algorithms (both crypto and digest) *MUST* have OID to use it. + * + */ + +struct ostream * +o_stream_create_encrypt(struct ostream *output, const char *algorithm, + struct dcrypt_public_key *box_pub, + enum io_stream_encrypt_flags flags); + +/* create context for performing encryption with + preset crypto context. do not call ctx_sym_init. + + no header or mac is written, just plain crypto + data. + */ +struct ostream * +o_stream_create_sym_encrypt(struct ostream *output, + struct dcrypt_context_symmetric *ctx); + +#endif diff --git a/src/lib-dcrypt/sample-v1.asc b/src/lib-dcrypt/sample-v1.asc new file mode 100644 index 0000000..c69fb50 --- /dev/null +++ b/src/lib-dcrypt/sample-v1.asc @@ -0,0 +1,48 @@ +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 +d/l7J6LsH25I/zsIV/0OHcrvMUEf2WzU3Q== diff --git a/src/lib-dcrypt/sample-v1_short.asc b/src/lib-dcrypt/sample-v1_short.asc new file mode 100644 index 0000000..4bc4c06 --- /dev/null +++ b/src/lib-dcrypt/sample-v1_short.asc @@ -0,0 +1,4 @@ +Q1JZUFRFRAMHAQCtAEMDAA+5INzRNr5OAicv3XI4jh//ufjZN9yYr7mElHKNGY+D +D2ziqHhPKVra6JzBzZvfySntDnDvdLAomafpDVlESMlkACB8mhA56i5P7XPoHdP/ +w/oi6kooNSk5rd57+OqFiwD6TwAgu4C6eZWV+Spojlk8eOAw784ySgMHK8gDrXhA +Jwg34GcAIPX4RmqXh+7QTAQWtGNHQvjqSygBxSUYkQ3KfPLMymKvAACMLy6/ diff --git a/src/lib-dcrypt/sample-v2.asc b/src/lib-dcrypt/sample-v2.asc new file mode 100644 index 0000000..f9b2be3 --- /dev/null +++ b/src/lib-dcrypt/sample-v2.asc @@ -0,0 +1,287 @@ +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 diff --git a/src/lib-dcrypt/test-crypto.c b/src/lib-dcrypt/test-crypto.c new file mode 100644 index 0000000..ce43791 --- /dev/null +++ b/src/lib-dcrypt/test-crypto.c @@ -0,0 +1,1314 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "buffer.h" +#include "str.h" +#include "dcrypt.h" +#include "dcrypt-iostream.h" +#include "ostream.h" +#include "ostream-encrypt.h" +#include "istream.h" +#include "iostream-temp.h" +#include "randgen.h" +#include "test-common.h" +#include "hex-binary.h" +#include "json-parser.h" +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> + +static void test_cipher_test_vectors(void) +{ + static const struct { + const char *key; + const char *iv; + const char *pt; + const char *ct; + } vectors[] = { + { + "2b7e151628aed2a6abf7158809cf4f3c", + "000102030405060708090a0b0c0d0e0f", + "6bc1bee22e409f96e93d7e117393172a", + "7649abac8119b246cee98e9b12e9197d" + }, { + "2b7e151628aed2a6abf7158809cf4f3c", + "7649ABAC8119B246CEE98E9B12E9197D", + "ae2d8a571e03ac9c9eb76fac45af8e51", + "5086cb9b507219ee95db113a917678b2" + } + }; + + + test_begin("test_cipher_test_vectors"); + + buffer_t *key,*iv,*pt,*ct,*res_enc,*res_dec; + + key = t_buffer_create(16); + iv = t_buffer_create(16); + pt = t_buffer_create(16); + ct = t_buffer_create(16); + + res_enc = t_buffer_create(32); + res_dec = t_buffer_create(32); + + for(size_t i = 0; i < N_ELEMENTS(vectors); i++) { + struct dcrypt_context_symmetric *ctx; + + buffer_set_used_size(key, 0); + buffer_set_used_size(iv, 0); + buffer_set_used_size(pt, 0); + buffer_set_used_size(ct, 0); + buffer_set_used_size(res_enc, 0); + buffer_set_used_size(res_dec, 0); + + hex_to_binary(vectors[i].key, key); + hex_to_binary(vectors[i].iv, iv); + hex_to_binary(vectors[i].pt, pt); + hex_to_binary(vectors[i].ct, ct); + + if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_ENCRYPT, + &ctx, NULL)) { + test_assert_failed("dcrypt_ctx_sym_create", + __FILE__, __LINE__-1); + continue; + } + + dcrypt_ctx_sym_set_padding(ctx, FALSE); + + dcrypt_ctx_sym_set_key(ctx, key->data, key->used); + dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); + + test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); + + test_assert_idx(dcrypt_ctx_sym_update(ctx, + pt->data, pt->used, res_enc, NULL), i); + test_assert_idx(dcrypt_ctx_sym_final(ctx, res_enc, NULL), i); + + test_assert_idx(buffer_cmp(ct, res_enc), i); + + dcrypt_ctx_sym_destroy(&ctx); + + if (!dcrypt_ctx_sym_create("AES-128-CBC", DCRYPT_MODE_DECRYPT, + &ctx, NULL)) { + test_assert_failed("dcrypt_ctx_sym_create", + __FILE__, __LINE__-1); + continue; + } + + dcrypt_ctx_sym_set_padding(ctx, FALSE); + + dcrypt_ctx_sym_set_key(ctx, key->data, key->used); + dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); + + test_assert_idx(dcrypt_ctx_sym_init(ctx, NULL), i); + test_assert_idx(dcrypt_ctx_sym_update(ctx, + res_enc->data, res_enc->used, res_dec, NULL), i); + test_assert_idx(dcrypt_ctx_sym_final(ctx, res_dec, NULL), i); + + test_assert_idx(buffer_cmp(pt, res_dec), i); + + dcrypt_ctx_sym_destroy(&ctx); + } + + test_end(); +} + +static void test_cipher_aead_test_vectors(void) +{ + struct dcrypt_context_symmetric *ctx; + const char *error = NULL; + + test_begin("test_cipher_aead_test_vectors"); + + if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_ENCRYPT, + &ctx, &error)) { + test_assert_failed("dcrypt_ctx_sym_create", + __FILE__, __LINE__-1); + return; + } + + buffer_t *key, *iv, *aad, *pt, *ct, *tag, *tag_res, *res; + + key = t_buffer_create(16); + iv = t_buffer_create(16); + aad = t_buffer_create(16); + pt = t_buffer_create(16); + ct = t_buffer_create(16); + tag = t_buffer_create(16); + res = t_buffer_create(16); + tag_res = t_buffer_create(16); + + hex_to_binary("feffe9928665731c6d6a8f9467308308", key); + hex_to_binary("cafebabefacedbaddecaf888", iv); + hex_to_binary("d9313225f88406e5a55909c5aff5269a" + "86a7a9531534f7da2e4c303d8a318a72" + "1c3c0c95956809532fcf0e2449a6b525" + "b16aedf5aa0de657ba637b391aafd255", pt); + hex_to_binary("42831ec2217774244b7221b784d0d49c" + "e3aa212f2c02a4e035c17e2329aca12e" + "21d514b25466931c7d8f6a5aac84aa05" + "1ba30b396a0aac973d58e091473f5985", ct); + hex_to_binary("4d5c2af327cd64a62cf35abd2ba6fab4", tag); + + dcrypt_ctx_sym_set_key(ctx, key->data, key->used); + dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); + dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); + test_assert(dcrypt_ctx_sym_init(ctx, &error)); + test_assert(dcrypt_ctx_sym_update(ctx, pt->data, pt->used, res, &error)); + test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); + test_assert(dcrypt_ctx_sym_get_tag(ctx, tag_res)); + + test_assert(buffer_cmp(ct, res) == TRUE); + test_assert(buffer_cmp(tag, tag_res) == TRUE); + + dcrypt_ctx_sym_destroy(&ctx); + + if (!dcrypt_ctx_sym_create("aes-128-gcm", DCRYPT_MODE_DECRYPT, + &ctx, &error)) { + test_assert_failed("dcrypt_ctx_sym_create", + __FILE__, __LINE__-1); + } else { + + buffer_set_used_size(res, 0); + + dcrypt_ctx_sym_set_key(ctx, key->data, key->used); + dcrypt_ctx_sym_set_iv(ctx, iv->data, iv->used); + dcrypt_ctx_sym_set_aad(ctx, aad->data, aad->used); + dcrypt_ctx_sym_set_tag(ctx, tag->data, tag->used); + test_assert(dcrypt_ctx_sym_init(ctx, &error)); + test_assert(dcrypt_ctx_sym_update(ctx, + ct->data, ct->used, res, &error)); + test_assert(dcrypt_ctx_sym_final(ctx, res, &error)); + + test_assert(buffer_cmp(pt, res) == TRUE); + + dcrypt_ctx_sym_destroy(&ctx); + } + + test_end(); +} + +static void test_hmac_test_vectors(void) +{ + test_begin("test_hmac_test_vectors"); + + buffer_t *pt, *ct, *key, *res; + pt = t_buffer_create(50); + key = t_buffer_create(20); + ct = t_buffer_create(32); + res = t_buffer_create(32); + + hex_to_binary("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", key); + hex_to_binary("dddddddddddddddddddddddddddddddddddddddddddddddddd" + "dddddddddddddddddddddddddddddddddddddddddddddddddd", pt); + hex_to_binary("773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe", res); + + struct dcrypt_context_hmac *hctx; + if (!dcrypt_ctx_hmac_create("sha256", &hctx, NULL)) { + test_assert_failed("dcrypt_ctx_hmac_create", + __FILE__, __LINE__-1); + } else { + dcrypt_ctx_hmac_set_key(hctx, key->data, key->used); + test_assert(dcrypt_ctx_hmac_init(hctx, NULL)); + test_assert(dcrypt_ctx_hmac_update(hctx, + pt->data, pt->used, NULL)); + test_assert(dcrypt_ctx_hmac_final(hctx, ct, NULL)); + test_assert(buffer_cmp(ct, res)); + dcrypt_ctx_hmac_destroy(&hctx); + } + + test_end(); +} + +static void test_load_v1_keys(void) +{ + test_begin("test_load_v1_keys"); + + const char *error = NULL; + const char *data1 = + "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14bda24" + "749303923de9a9bb9370e0026f995901a57e63113eeb2baf0c" + "940e978d00686cbb52bd5014bc318563375876255\t0300E46" + "DA2125427BE968EB3B649910CDC4C405E5FFDE18D433A97CAB" + "FEE28CEEFAE9EE356C792004FFB80981D67E741B8CC036A342" + "35A8D2E1F98D1658CFC963D07EB\td0cfaca5d335f9edc41c8" + "4bb47465184cb0e2ec3931bebfcea4dd433615e77a0\t7c9a1" + "039ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea8" + "58b00fa4f"; + + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type encryption_type; + const char *encryption_key_hash = NULL; + const char *key_hash = NULL; + + bool ret = dcrypt_key_string_get_info(data1, &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + + test_assert(ret == TRUE); + test_assert(error == NULL); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_1); + test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); + test_assert(strcmp(encryption_key_hash, + "d0cfaca5d335f9edc41c84bb47465184" + "cb0e2ec3931bebfcea4dd433615e77a0") == 0); + test_assert(strcmp(key_hash, + "7c9a1039ea2e4fed73e81dd3ffc3fa22" + "ea4a28352939adde7bf8ea858b00fa4f") == 0); + + const char* data2 = + "1\t716\t0301EB00973C4EFC8FCECA4EA33E941F50B561199A" + "5159BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71" + "EE2AD264CD16B863FA094A8F6F69A56B62E8918040\t7c9a10" + "39ea2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea85" + "8b00fa4f"; + + error = NULL; + encryption_key_hash = NULL; + key_hash = NULL; + + ret = dcrypt_key_string_get_info(data2, &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + + test_assert(ret == TRUE); + test_assert(error == NULL); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_1); + test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(encryption_key_hash == NULL); + test_assert(strcmp(key_hash, + "7c9a1039ea2e4fed73e81dd3ffc3fa22" + "ea4a28352939adde7bf8ea858b00fa4f") == 0); + + /* This is the key that should be able to decrypt key1 */ + const char *data3 = + "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD" + "2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E" + "133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca" + "5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd4336" + "15e77a0"; + + error = NULL; + encryption_key_hash = NULL; + key_hash = NULL; + + ret = dcrypt_key_string_get_info(data3, &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + test_assert(ret == TRUE); + test_assert(error == NULL); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_1); + test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(encryption_key_hash == NULL); + test_assert(strcmp(key_hash, + "d0cfaca5d335f9edc41c84bb47465184" + "cb0e2ec3931bebfcea4dd433615e77a0") == 0); + + /* key3's key_hash should and does match key1's encryption_key_hash */ + struct dcrypt_private_key *pkey = NULL; + struct dcrypt_private_key *pkey2 = NULL; + pkey = NULL; + error = NULL; + + ret = dcrypt_key_load_private(&pkey2, data3, NULL, NULL, &error); + test_assert(ret == TRUE); + test_assert(error == NULL); + + ret = dcrypt_key_load_private(&pkey, data1, NULL, pkey2, &error); + test_assert(ret == TRUE); + test_assert(error == NULL); + + dcrypt_key_unref_private(&pkey2); + dcrypt_key_unref_private(&pkey); + + test_end(); +} + +static void test_load_v1_key(void) +{ + test_begin("test_load_v1_key"); + + buffer_t *key_1 = t_buffer_create(128); + + struct dcrypt_private_key *pkey = NULL, *pkey2 = NULL; + const char *error = NULL; + + test_assert(dcrypt_key_load_private(&pkey, + "1\t716\t0\t048FD04FD3612B22D32790C592CF21CEF417EFD" + "2EA34AE5F688FA5B51BED29E05A308B68DA78E16E90B47A11E" + "133BD9A208A2894FD01B0BEE865CE339EA3FB17AC\td0cfaca" + "5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd4336" + "15e77a0", NULL, NULL, &error)); + if (pkey != NULL) { + buffer_set_used_size(key_1, 0); + /* check that key_id matches */ + struct dcrypt_public_key *pubkey = NULL; + dcrypt_key_convert_private_to_public(pkey, &pubkey); + test_assert(dcrypt_key_store_public(pubkey, + DCRYPT_FORMAT_DOVECOT, key_1, NULL)); + buffer_set_used_size(key_1, 0); + dcrypt_key_id_public(pubkey, "sha256", key_1, &error); + test_assert(strcmp("792caad4d38c9eb2134a0cbc844eae38" + "6116de096a0ccafc98479825fc99b6a1", + binary_to_hex(key_1->data, key_1->used)) + == 0); + + dcrypt_key_unref_public(&pubkey); + pkey2 = NULL; + + test_assert(dcrypt_key_load_private(&pkey2, + "1\t716\t1\t0567e6bf9579813ae967314423b0fceb14" + "bda24749303923de9a9bb9370e0026f995901a57e6311" + "3eeb2baf0c940e978d00686cbb52bd5014bc318563375" + "876255\t0300E46DA2125427BE968EB3B649910CDC4C4" + "05E5FFDE18D433A97CABFEE28CEEFAE9EE356C792004F" + "FB80981D67E741B8CC036A34235A8D2E1F98D1658CFC9" + "63D07EB\td0cfaca5d335f9edc41c84bb47465184cb0e" + "2ec3931bebfcea4dd433615e77a0\t7c9a1039ea2e4fe" + "d73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b00" + "fa4f", NULL, pkey, &error)); + if (pkey2 != NULL) { + buffer_set_used_size(key_1, 0); + /* check that key_id matches */ + struct dcrypt_public_key *pubkey = NULL; + dcrypt_key_convert_private_to_public(pkey2, &pubkey); + test_assert(dcrypt_key_store_public(pubkey, + DCRYPT_FORMAT_DOVECOT, key_1, NULL)); + buffer_set_used_size(key_1, 0); + test_assert(dcrypt_key_id_public_old(pubkey, + key_1, &error)); + test_assert(strcmp( + "7c9a1039ea2e4fed73e81dd3ffc3fa22" + "ea4a28352939adde7bf8ea858b00fa4f", + binary_to_hex(key_1->data, key_1->used)) == 0); + + dcrypt_key_unref_public(&pubkey); + dcrypt_key_unref_private(&pkey2); + } + dcrypt_key_unref_private(&pkey); + } + + test_end(); +} + +static void test_load_v1_public_key(void) +{ + test_begin("test_load_v1_public_key"); + + const char* data1 = + "1\t716\t030131D8A5FD5167947A0AE9CB112ADED652665463" + "5AA5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BB" + "BCB44BBFC0D662A4287A848BA570D4E5E45A11FE0F\td0cfac" + "a5d335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433" + "615e77a0"; + + const char* error = NULL; + const char* key_hash = NULL; + const char* encryption_key_hash = NULL; + + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type encryption_type; + + bool ret = dcrypt_key_string_get_info(data1, &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + + test_assert(ret == TRUE); + test_assert(error == NULL); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_1); + test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(key_hash != NULL && + strcmp(key_hash, "d0cfaca5d335f9edc41c84bb47465184" + "cb0e2ec3931bebfcea4dd433615e77a0") == 0); + test_assert(encryption_key_hash == NULL); + + struct dcrypt_public_key *pub_key = NULL; + ret = dcrypt_key_load_public(&pub_key, data1, &error); + test_assert(ret == TRUE); + test_assert(error == NULL); + + test_assert(dcrypt_key_type_public(pub_key) == DCRYPT_KEY_EC); + + dcrypt_key_unref_public(&pub_key); + test_assert(pub_key == NULL); + + test_end(); +} + +static void test_load_v2_key(void) +{ + const char *keys[] = { + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtu" + "QJA+uboZWVwgHc\n" + "DciyVdrovAPwlMqshDK3s78IDDuhRANCAAQm0VEdzLB9PtD0HA" + "8JK1zifWnj8M00\n" + "FQzedfp9SQsWyA8dzs5/NFR5MTe6Xbh/ndKEs1zZH3vZ4FlNri" + "lZc0st\n" + "-----END PRIVATE KEY-----\n", + "2:1.2.840.10045.3.1.7:0:0000002100b6e40903eb9ba195" + "95c201dc0dc8b255dae8bc03f094caac8432b7b3bf080c3b:a" + "b13d251976dedab546b67354e7678821740dd534b749c2857f" + "66bf62bbaddfd", + "2:1.2.840.10045.3.1.7:2:aes-256-ctr:483bd74fd3d917" + "63:sha256:2048:d44ae35d3af7a2febcb15cde0c3693e7ed9" + "8595665ed655a97fa918d346d5c661a6e2339f4:ab13d25197" + "6dedab546b67354e7678821740dd534b749c2857f66bf62bba" + "ddfd", + "2:1.2.840.10045.3.1.7:1:aes-256-ctr:2574c10be28a4c" + "09:sha256:2048:a750ec9dea91999f108f943485a20f273f4" + "0f75c37fc9bcccdedda514c8243e550d69ce1bd:02237a199d" + "7d945aa6492275a02881071eceec5749caf2485da8c64fb601" + "229098:ab13d251976dedab546b67354e7678821740dd534b7" + "49c2857f66bf62bbaddfd:ab13d251976dedab546b67354e76" + "78821740dd534b749c2857f66bf62bbaddfd" + }; + + test_begin("test_load_v2_key"); + const char *error = NULL; + buffer_t *tmp = buffer_create_dynamic(default_pool, 256); + + struct dcrypt_private_key *priv,*priv2; + + test_assert_idx(dcrypt_key_load_private(&priv2, + keys[0], NULL, NULL, &error), 0); + test_assert_idx(dcrypt_key_store_private(priv2, + DCRYPT_FORMAT_PEM, NULL, tmp, NULL, NULL, &error), 0); + test_assert_idx(strcmp(str_c(tmp), keys[0])==0, 0); + buffer_set_used_size(tmp, 0); + + test_assert_idx(dcrypt_key_load_private(&priv, + keys[1], NULL, NULL, &error), 1); + test_assert_idx(dcrypt_key_store_private(priv, + DCRYPT_FORMAT_DOVECOT, NULL, tmp, NULL, NULL, &error), 1); + test_assert_idx(strcmp(str_c(tmp), keys[1])==0, 1); + buffer_set_used_size(tmp, 0); + dcrypt_key_unref_private(&priv); + + test_assert_idx(dcrypt_key_load_private(&priv, + keys[2], "This Is Sparta", NULL, &error), 2); + test_assert_idx(dcrypt_key_store_private(priv, + DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", tmp, + "This Is Sparta", NULL, &error), 2); + buffer_set_used_size(tmp, 0); + dcrypt_key_unref_private(&priv); + + struct dcrypt_public_key *pub = NULL; + dcrypt_key_convert_private_to_public(priv2, &pub); + test_assert_idx(dcrypt_key_load_private(&priv, + keys[3], NULL, priv2, &error), 3); + test_assert_idx(dcrypt_key_store_private(priv, + DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", tmp, + NULL, pub, &error), 3); + buffer_set_used_size(tmp, 0); + dcrypt_key_unref_private(&priv2); + dcrypt_key_unref_private(&priv); + dcrypt_key_unref_public(&pub); + + buffer_free(&tmp); + + if (error != NULL) error = NULL; + + test_end(); +} + +static void test_load_v2_public_key(void) +{ + struct dcrypt_public_key *pub = NULL; + const char *error; + + test_begin("test_load_v2_public_key"); + const char *key = + "2:3058301006072a8648ce3d020106052b810400230344000" + "301c50954e734dd8b410a607764a7057065a45510da52f2c6" + "e28e0cb353b9c389fa8cb786943ae991fce9befed78fb162f" + "bbc615415f06af06c8cc80c37f4e94ff6c7:185a721254278" + "2e239111f9c19d126ad55b18ddaf4883d66afe8d9627c3607" + "d8"; + + test_assert(dcrypt_key_load_public(&pub, key, &error)); + + buffer_t *tmp = buffer_create_dynamic(default_pool, 256); + + if (pub != NULL) { + test_assert(dcrypt_key_store_public(pub, + DCRYPT_FORMAT_DOVECOT, tmp, &error)); + test_assert(strcmp(key, str_c(tmp))==0); + buffer_free(&tmp); + dcrypt_key_unref_public(&pub); + } + + test_end(); +} + +static void test_get_info_v2_key(void) +{ + test_begin("test_get_info_v2_key"); + + const char *key = + "2:305e301006072a8648ce3d020106052b81040026034a0002" + "03fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360" + "cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945" + "ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706" + "b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d9" + "0966e84dc"; + enum dcrypt_key_format format; + enum dcrypt_key_version version = DCRYPT_KEY_VERSION_NA; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type encryption_type; + const char *encryption_key_hash = NULL; + const char *key_hash = NULL; + const char *error = NULL; + + test_assert(dcrypt_key_string_get_info(key, &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error)); + test_assert(error == NULL); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_2); + + test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(encryption_key_hash == NULL); + test_assert(key_hash != NULL && strcmp(key_hash, + "86706b69d1f640011a65d26a42f2ba20" + "a619173644e1cc7475eb1d90966e84dc") == 0); + + test_end(); +} + +static void test_gen_and_get_info_rsa_pem(void) +{ + test_begin("test_gen_and_get_info_rsa_pem"); + + const char *error = NULL; + bool ret = FALSE; + struct dcrypt_keypair pair; + string_t* buf = str_new(default_pool, 4096); + + ret = dcrypt_keypair_generate(&pair, DCRYPT_KEY_RSA, 1024, NULL, NULL); + test_assert(ret == TRUE); + + /* test public key */ + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type encryption_type; + const char *encryption_key_hash; + const char *key_hash; + + ret = dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, buf, + &error); + test_assert(ret == TRUE); + + ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + test_assert(ret == TRUE); + test_assert(format == DCRYPT_FORMAT_PEM); + test_assert(version == DCRYPT_KEY_VERSION_NA); + + test_assert(kind == DCRYPT_KEY_KIND_PUBLIC); + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(encryption_key_hash == NULL); + test_assert(key_hash == NULL); + + /* test private key */ + buffer_set_used_size(buf, 0); + ret = dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_PEM, NULL, + buf, NULL, NULL, &error); + + test_assert(ret == TRUE); + + ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, + &kind, &encryption_type, &encryption_key_hash, + &key_hash, &error); + + test_assert(ret == TRUE); + test_assert(format == DCRYPT_FORMAT_PEM); + test_assert(version == DCRYPT_KEY_VERSION_NA); + + test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); + + test_assert(encryption_type == DCRYPT_KEY_ENCRYPTION_TYPE_NONE); + test_assert(encryption_key_hash == NULL); + test_assert(key_hash == NULL); + + dcrypt_keypair_unref(&pair); + buffer_free(&buf); + + test_end(); +} + +static void test_get_info_rsa_private_key(void) +{ + test_begin("test_get_info_rsa_private_key"); + + const char *key = "-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQC89q02I9NezBLQ+otn5XLYE7S+GsKUz59ogr45DA/6MI9jey0W\n" +"56SeWQ1FJD1vDhAx/TRBMfOmhcIPsBjc5sakYOawPdoiqLjOIlO+iHwnbbmLuMsq\n" +"ue09vgvZsKjuTr2F5DOFQY43Bq/Nd+4bjHJItdOM58+xwA2I/8vDbtI8jwIDAQAB\n" +"AoGBAJCUrTMfdjqyKjN7f+6ewKBTc5eBIiB6O53ba3B6qj7jqNKVDIrZ8jq2KFEe\n" +"yWKPgBS/h5vafHKNJU6bjmp2qMUJPB7PTA876eDo0cq9PplUqihiTlXJFwNQYtF+\n" +"o27To5t25+5qdSAj657+lQfFT9Xn9fzYHDmotURxH10FgFkBAkEA+7Ny6lBTeb3W\n" +"LnP0UPfPzQLilEr8u81PLWe69RGtsEaMQHGpHOl4e+bvvVYbG1cgxwxI1m01uR9r\n" +"qpD3qLUdrQJBAMAw6UvN8R+opYTZzwqK7Nliil2QZMPmXM04SV1iFq26NM60w2Fm\n" +"HqOOh0EbpSWsFtIgxJFWoZOtrguxqCJuUqsCQF3EoXf3StHczhDqM8eCOpD2lTCH\n" +"qxXPy8JvlW+9EUbNUWykq0rRE4idJQ0VKe4KjHR6+Buh/dSkhvi5Hvpj1tUCQHRv\n" +"LWeXZLVhXqWVrzEb6VHpuRnmGKX2MdLCfu/sNQEbBlMUgCnJzFYaSybOsMaZ81lq\n" +"MKw8Z7coSYEcKFhzrfECQQD7l+4Bhy8Zuz6VoGGIZwIhxkJrImBFmaUwx8N6jg20\n" +"sgDRYwCoGkGd7B8uIHZLJoWzSSutHiu5i5PYUy5VT1yT\n" +"-----END RSA PRIVATE KEY-----\n"; + + const char *error = NULL; + + test_assert(!dcrypt_key_string_get_info(key, NULL, NULL, + NULL, NULL, NULL, NULL, &error)); + test_assert(error != NULL && strstr(error, "pkey") != NULL); + + test_end(); +} + +static void test_get_info_invalid_keys(void) +{ + test_begin("test_get_info_invalid_keys"); + + const char *key = + "1:716:030131D8A5FD5167947A0AE9CB112ADED6526654635A" + "A5887051EE2364414B60FF32EBA8FA0BBE9485DBDE8794BBBC" + "B44BBFC0D662A4287A848BA570D4E5E45A11FE0F:d0cfaca5d" + "335f9edc41c84bb47465184cb0e2ec3931bebfcea4dd433615" + "e77a0"; + const char *error = NULL; + + test_assert(dcrypt_key_string_get_info(key, NULL, NULL, + NULL, NULL, NULL, NULL, &error) == FALSE); + test_assert(error != NULL && strstr(error, "tab") != NULL); + + key = + "2\t305e301006072a8648ce3d020106052b81040026034a000" + "203fcc90034fa03d6fb79a0fc8b3b43c3398f68e7602930736" + "0cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b794" + "5ed9d182f3156550e9ee30b237a0217dbf79d28975f31\t867" + "06b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1" + "d90966e84dc"; + error = NULL; + + test_assert(dcrypt_key_string_get_info(key, NULL, NULL, + NULL, NULL, NULL, NULL, &error) == FALSE); + test_assert(error != NULL && strstr(error, "colon") != NULL); + + key = "2"; + error = NULL; + + test_assert(dcrypt_key_string_get_info(key, NULL, NULL, + NULL, NULL, NULL, NULL, &error) == FALSE); + test_assert(error != NULL && strstr(error, "Unknown") != NULL); + + test_end(); +} + +static void test_get_info_key_encrypted(void) +{ + test_begin("test_get_info_key_encrypted"); + + struct dcrypt_keypair p1, p2; + const char *error = NULL; + bool ret = dcrypt_keypair_generate(&p1, + DCRYPT_KEY_EC, 0, "secp521r1", &error); + test_assert(ret == TRUE); + ret = dcrypt_keypair_generate(&p2, + DCRYPT_KEY_EC, 0, "secp521r1", &error); + test_assert(ret == TRUE); + + string_t* buf = t_str_new(4096); + + buffer_set_used_size(buf, 0); + ret = dcrypt_key_store_private(p1.priv, + DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf, + NULL, p2.pub, &error); + test_assert(ret == TRUE); + + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type enc_type; + const char *enc_hash; + const char *key_hash; + + ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, + &kind, &enc_type, &enc_hash, &key_hash, &error); + test_assert(ret == TRUE); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_2); + test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); + test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_KEY); + test_assert(enc_hash != NULL); + test_assert(key_hash != NULL); + + dcrypt_keypair_unref(&p1); + dcrypt_keypair_unref(&p2); + + test_end(); +} + +static void test_get_info_pw_encrypted(void) +{ + test_begin("test_get_info_pw_encrypted"); + + struct dcrypt_keypair p1; + i_zero(&p1); + const char *error; + bool ret = dcrypt_keypair_generate(&p1, + DCRYPT_KEY_EC, 0, "secp521r1", &error); + test_assert(ret == TRUE); + + string_t* buf = t_str_new(4096); + ret = dcrypt_key_store_private(p1.priv, + DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, "pw", NULL, &error); + test_assert(ret == TRUE); + + enum dcrypt_key_format format; + enum dcrypt_key_version version; + enum dcrypt_key_kind kind; + enum dcrypt_key_encryption_type enc_type; + const char *enc_hash; + const char *key_hash; + + ret = dcrypt_key_string_get_info(str_c(buf), &format, &version, + &kind, &enc_type, &enc_hash, &key_hash, &error); + test_assert(ret == TRUE); + test_assert(format == DCRYPT_FORMAT_DOVECOT); + test_assert(version == DCRYPT_KEY_VERSION_2); + test_assert(kind == DCRYPT_KEY_KIND_PRIVATE); + test_assert(enc_type == DCRYPT_KEY_ENCRYPTION_TYPE_PASSWORD); + test_assert(enc_hash == NULL); + test_assert(key_hash != NULL); + + dcrypt_keypair_unref(&p1); + + test_end(); +} + +static void test_password_change(void) +{ + test_begin("test_password_change"); + + const char *pw1 = "first password"; + struct dcrypt_keypair orig; + const char *error = NULL; + + bool ret = dcrypt_keypair_generate(&orig, + DCRYPT_KEY_EC, 0, "secp521r1", &error); + test_assert(ret == TRUE); + + string_t *buf = t_str_new(4096); + ret = dcrypt_key_store_private(orig.priv, + DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf, pw1, NULL, &error); + test_assert(ret == TRUE); + + /* load the pw-encrypted key */ + struct dcrypt_private_key *k1_priv = NULL; + ret = dcrypt_key_load_private(&k1_priv, str_c(buf), pw1, NULL, &error); + test_assert(ret == TRUE); + + /* encrypt a key with the pw-encrypted key k1 */ + struct dcrypt_keypair k2; + ret = dcrypt_keypair_generate(&k2, + DCRYPT_KEY_EC, 0, "secp521r1", &error); + test_assert(ret == TRUE); + + string_t *buf2 = t_str_new(4096); + struct dcrypt_public_key *k1_pub = NULL; + dcrypt_key_convert_private_to_public(k1_priv, &k1_pub); + ret = dcrypt_key_store_private(k2.priv, + DCRYPT_FORMAT_DOVECOT, "ecdh-aes-256-ctr", buf2, + NULL, k1_pub, &error); + test_assert(ret == TRUE); + + /* change the password */ + const char *pw2 = "second password"; + string_t *buf3 = t_str_new(4096); + + /* encrypt k1 with pw2 */ + ret = dcrypt_key_store_private(k1_priv, + DCRYPT_FORMAT_DOVECOT, "aes-256-ctr", buf3, pw2, NULL, &error); + test_assert(ret == TRUE); + + /* load the pw2 encrypted key */ + struct dcrypt_private_key *k2_priv = NULL; + ret = dcrypt_key_load_private(&k2_priv, str_c(buf3), pw2, NULL, &error); + test_assert(ret == TRUE); + + /* load the key that was encrypted with pw1 using the pw2 encrypted key */ + struct dcrypt_private_key *k3_priv = NULL; + ret = dcrypt_key_load_private(&k3_priv, + str_c(buf2), NULL, k2_priv, &error); + test_assert(ret == TRUE); + + dcrypt_key_unref_private(&k1_priv); + dcrypt_key_unref_public(&k1_pub); + dcrypt_key_unref_private(&k2_priv); + dcrypt_key_unref_private(&k3_priv); + dcrypt_keypair_unref(&orig); + dcrypt_keypair_unref(&k2); + + test_end(); +} + +static void test_load_invalid_keys(void) +{ + test_begin("test_load_invalid_keys"); + + const char *error = NULL; + const char *key = + "1:716:0301EB00973C4EFC8FCECA4EA33E941F50B561199A51" + "59BCB6C2EED9DD1D62D65E38A254979D89E28F0C28883E71EE" + "2AD264CD16B863FA094A8F6F69A56B62E8918040:7c9a1039e" + "a2e4fed73e81dd3ffc3fa22ea4a28352939adde7bf8ea858b0" + "0fa4f"; + struct dcrypt_public_key *pub_key = NULL; + + bool ret = dcrypt_key_load_public(&pub_key, key, &error); + test_assert(ret == FALSE); + test_assert(error != NULL); + + error = NULL; + key = + "2:305e301006072a8648ce3d020106052b81040026034a0002" + "03fcc90034fa03d6fb79a0fc8b3b43c3398f68e76029307360" + "cdcb9e27bb7e84b3c19dfb7244763bc4d442d216f09b7b7945" + "ed9d182f3156550e9ee30b237a0217dbf79d28975f31:86706" + "b69d1f640011a65d26a42f2ba20a619173644e1cc7475eb1d9" + "0966e84dc"; + struct dcrypt_private_key *priv_key = NULL; + + ret = dcrypt_key_load_private(&priv_key, key, NULL, NULL, &error); + test_assert(ret == FALSE); + test_assert(error != NULL); + + test_end(); +} + +static void test_raw_keys(void) +{ + + test_begin("test_raw_keys"); + + ARRAY_TYPE(dcrypt_raw_key) priv_key; + ARRAY_TYPE(dcrypt_raw_key) pub_key; + pool_t pool = pool_datastack_create(); + + enum dcrypt_key_type t; + + p_array_init(&priv_key, pool, 2); + p_array_init(&pub_key, pool, 2); + + /* generate ECC key */ + struct dcrypt_keypair pair; + i_assert(dcrypt_keypair_generate(&pair, DCRYPT_KEY_EC, 0, "prime256v1", NULL)); + + /* store it */ + test_assert(dcrypt_key_store_private_raw(pair.priv, pool, &t, &priv_key, + NULL)); + test_assert(dcrypt_key_store_public_raw(pair.pub, pool, &t, &pub_key, + NULL)); + dcrypt_keypair_unref(&pair); + + /* load it */ + test_assert(dcrypt_key_load_private_raw(&pair.priv, t, &priv_key, + NULL)); + test_assert(dcrypt_key_load_public_raw(&pair.pub, t, &pub_key, + NULL)); + + dcrypt_keypair_unref(&pair); + + /* test load known raw private key */ + const char *curve = "prime256v1"; + const unsigned char priv_key_data[] = { + 0x16, 0x9e, 0x62, 0x36, 0xaf, 0x9c, 0xae, 0x0e, 0x71, 0xda, + 0xf2, 0x63, 0xe2, 0xe0, 0x5d, 0xf1, 0xd5, 0x35, 0x8c, 0x2b, + 0x68, 0xf0, 0x2a, 0x69, 0xc4, 0x5d, 0x3d, 0x1c, 0xde, 0xa1, + 0x9b, 0xd3 + }; + + /* create buffers */ + struct dcrypt_raw_key *item; + ARRAY_TYPE(dcrypt_raw_key) static_key; + t_array_init(&static_key, 2); + + /* Add OID */ + buffer_t *buf = t_buffer_create(32); + test_assert(dcrypt_name2oid(curve, buf, NULL)); + item = array_append_space(&static_key); + item->parameter = buf->data; + item->len = buf->used; + + /* Add key data */ + item = array_append_space(&static_key); + item->parameter = priv_key_data; + item->len = sizeof(priv_key_data); + + /* Try load it */ + test_assert(dcrypt_key_load_private_raw(&pair.priv, t, + &static_key, NULL)); + + /* See what we got */ + buf = t_buffer_create(128); + test_assert(dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_DOVECOT, + NULL, buf, NULL, NULL, NULL)); + test_assert_strcmp(str_c(buf), + "2:1.2.840.10045.3.1.7:0:00000020169e6236af9cae0e71d" + "af263e2e05df1d5358c2b68f02a69c45d3d1cdea19bd3:21d11" + "6b7b3e5c52e81f0437a10b0116cfafc467fb1b96e48926d0216" + "68fc1bea"); + + /* try to load public key, too */ + const unsigned char pub_key_data[] = { + 0x04, 0xe8, 0x7c, 0x6d, 0xa0, 0x29, 0xfe, 0x5d, 0x16, 0x1a, + 0xd6, 0x6a, 0xc6, 0x1c, 0x78, 0x8a, 0x36, 0x0f, 0xfb, 0x64, + 0xe7, 0x7f, 0x58, 0x13, 0xb3, 0x80, 0x1f, 0x99, 0x45, 0xee, + 0xa9, 0x4a, 0xe2, 0xde, 0xf3, 0x88, 0xc6, 0x37, 0x72, 0x7f, + 0xbe, 0x97, 0x02, 0x94, 0xb2, 0x21, 0x60, 0xa4, 0x98, 0x4e, + 0xfb, 0x46, 0x19, 0x61, 0x4c, 0xc5, 0xe1, 0x9f, 0xe9, 0xb2, + 0xd2, 0x4d, 0xae, 0x83, 0x4b + }; + + array_clear(&static_key); + + /* Add OID */ + buf = t_buffer_create(32); + test_assert(dcrypt_name2oid(curve, buf, NULL)); + item = array_append_space(&static_key); + item->parameter = buf->data; + item->len = buf->used; + + /* Add key data */ + item = array_append_space(&static_key); + item->parameter = pub_key_data; + item->len = sizeof(pub_key_data); + + /* See what we got */ + test_assert(dcrypt_key_load_public_raw(&pair.pub, t, + &static_key, NULL)); + buf = t_buffer_create(128); + test_assert(dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_DOVECOT, + buf, NULL)); + test_assert_strcmp(str_c(buf), + "2:3039301306072a8648ce3d020106082a8648ce3d03010703220003e87c6d" + "a029fe5d161ad66ac61c788a360ffb64e77f5813b3801f9945eea94ae2:21d" + "116b7b3e5c52e81f0437a10b0116cfafc467fb1b96e48926d021668fc1bea"); + dcrypt_keypair_unref(&pair); + + test_end(); +} + +static void test_sign_verify_rsa(void) +{ + const char *error = NULL; + bool valid; + struct dcrypt_private_key *priv_key = NULL; + struct dcrypt_public_key *pub_key = NULL; + + buffer_t *signature = + buffer_create_dynamic(pool_datastack_create(), 128); + const char *data = "signed data"; + + test_begin("sign and verify (rsa)"); + const char *key = "-----BEGIN PRIVATE KEY-----\n" +"MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALz2rTYj017MEtD6\n" +"i2flctgTtL4awpTPn2iCvjkMD/owj2N7LRbnpJ5ZDUUkPW8OEDH9NEEx86aFwg+w\n" +"GNzmxqRg5rA92iKouM4iU76IfCdtuYu4yyq57T2+C9mwqO5OvYXkM4VBjjcGr813\n" +"7huMcki104znz7HADYj/y8Nu0jyPAgMBAAECgYEAkJStMx92OrIqM3t/7p7AoFNz\n" +"l4EiIHo7ndtrcHqqPuOo0pUMitnyOrYoUR7JYo+AFL+Hm9p8co0lTpuOanaoxQk8\n" +"Hs9MDzvp4OjRyr0+mVSqKGJOVckXA1Bi0X6jbtOjm3bn7mp1ICPrnv6VB8VP1ef1\n" +"/NgcOai1RHEfXQWAWQECQQD7s3LqUFN5vdYuc/RQ98/NAuKUSvy7zU8tZ7r1Ea2w\n" +"RoxAcakc6Xh75u+9VhsbVyDHDEjWbTW5H2uqkPeotR2tAkEAwDDpS83xH6ilhNnP\n" +"Cors2WKKXZBkw+ZczThJXWIWrbo0zrTDYWYeo46HQRulJawW0iDEkVahk62uC7Go\n" +"Im5SqwJAXcShd/dK0dzOEOozx4I6kPaVMIerFc/Lwm+Vb70RRs1RbKSrStETiJ0l\n" +"DRUp7gqMdHr4G6H91KSG+Lke+mPW1QJAdG8tZ5dktWFepZWvMRvpUem5GeYYpfYx\n" +"0sJ+7+w1ARsGUxSAKcnMVhpLJs6wxpnzWWowrDxntyhJgRwoWHOt8QJBAPuX7gGH\n" +"Lxm7PpWgYYhnAiHGQmsiYEWZpTDHw3qODbSyANFjAKgaQZ3sHy4gdksmhbNJK60e\n" +"K7mLk9hTLlVPXJM=\n" +"-----END PRIVATE KEY-----"; + + test_assert(dcrypt_key_load_private(&priv_key, + key, NULL, NULL, &error)); + if (priv_key == NULL) + i_fatal("%s", error); + dcrypt_key_convert_private_to_public(priv_key, &pub_key); + test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + data, strlen(data), signature, 0, &error)); + /* verify signature */ + test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + data, strlen(data), + signature->data, signature->used, &valid, 0, &error) && valid); + + dcrypt_key_unref_public(&pub_key); + dcrypt_key_unref_private(&priv_key); + + test_end(); +} + +static void test_sign_verify_ecdsa(void) +{ + const char *error = NULL; + bool valid; + struct dcrypt_private_key *priv_key = NULL; + struct dcrypt_public_key *pub_key = NULL; + + buffer_t *signature = + buffer_create_dynamic(pool_datastack_create(), 128); + const char *data = "signed data"; + + test_begin("sign and verify (ecdsa)"); + const char *key = "-----BEGIN PRIVATE KEY-----\n" +"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgZ4AMMyJ9XDl5lKM2\n" +"vusbT1OQ6VzBWBkB3/4syovaKtyhRANCAAQHTR+6L2qMh5fdcMZF+Y1rctBsq8Oy\n" +"7jZ4uV+MiuaoGNQ5sTxlcv6ETX/XrEDq4S/DUhFKzQ6u9VXYZImvRCT1\n" +"-----END PRIVATE KEY-----"; + + test_assert(dcrypt_key_load_private(&priv_key, + key, NULL, NULL, &error)); + if (priv_key == NULL) + i_fatal("%s", error); + dcrypt_key_convert_private_to_public(priv_key, &pub_key); + test_assert(dcrypt_sign(priv_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + data, strlen(data), signature, 0, &error)); + /* verify signature */ + test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + data, strlen(data), signature->data, + signature->used, &valid, 0, &error) && valid); + + dcrypt_key_unref_public(&pub_key); + dcrypt_key_unref_private(&priv_key); + + test_end(); +} + +static void test_static_verify_ecdsa(void) +{ + test_begin("static verify (ecdsa)"); + const char *input = "hello, world"; + const char *priv_key_pem = + "-----BEGIN PRIVATE KEY-----\n" + "MGcCAQAwEwYHKoZIzj0CAQYIKoZIzj0DAQcETTBLAgEBBCC25AkD65uhlZXCAdwN\n" + "yLJV2ui8A/CUyqyEMrezvwgMO6EkAyIAAybRUR3MsH0+0PQcDwkrXOJ9aePwzTQV\n" + "DN51+n1JCxbI\n" + "-----END PRIVATE KEY-----"; + const char *pub_key_pem = + "-----BEGIN PUBLIC KEY-----\n" + "MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADJtFRHcywfT7Q9BwPCStc4n1p4/DN\n" + "NBUM3nX6fUkLFsg=\n" + "-----END PUBLIC KEY-----"; + + const unsigned char sig[] = { + 0x30,0x45,0x02,0x20,0x2c,0x76,0x20,0x5e,0xfc,0xa6,0x9e,0x16, + 0x44,0xb3,0xbc,0xbf,0xcc,0x43,0xc1,0x08,0x76,0x4a,0xe8,0x60, + 0xc5,0x9b,0x99,0x20,0x5b,0x44,0x33,0x5c,0x38,0x84,0x63,0xcb, + 0x02,0x21,0x00,0xa3,0x67,0xed,0x57,0xbf,0x59,0x46,0xb7,0x0c, + 0x7b,0xec,0x4f,0x78,0x14,0xec,0xfa,0x8d,0xa2,0x85,0x48,0xea, + 0xe1,0xaf,0x9e,0xbf,0x04,0xac,0x0e,0x41,0xfe,0x84,0x0e + }; + + struct dcrypt_keypair pair; + bool valid; + const char *error; + + i_zero(&pair); + /* static key test */ + test_assert(dcrypt_key_load_public(&pair.pub, pub_key_pem, NULL)); + test_assert(dcrypt_key_load_private(&pair.priv, priv_key_pem, NULL, NULL, NULL)); + /* validate signature */ + test_assert(dcrypt_verify(pair.pub, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + input, strlen(input), + sig, sizeof(sig), &valid, 0, &error) && + valid == TRUE); + + dcrypt_keypair_unref(&pair); + + test_end(); +} + +static void test_jwk_keys(void) +{ + /* Make sure this matches what comes out from store private */ + const char *jwk_key_json = "{\"kty\":\"EC\"," + "\"crv\":\"P-256\"," + "\"x\":\"Kp0Y4-Wpt-D9t_2XenFIj0LmvaZByLG69yOisek4aMI\"," + "\"y\":\"wjEPB5BhH5SRPw1cCN5grWrLCphrW19fCFR8p7c9O5o\"," + "\"use\":\"sig\"," + "\"kid\":\"123\"," + "\"d\":\"Po2z9rs86J2Qb_xWprr4idsWNPlgKf3G8-mftnE2ync\"}"; + /* Acquired using another tool */ + const char *pem_key = + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEKp0Y4+Wpt+D9t/2XenFIj0LmvaZB\n" + "yLG69yOisek4aMLCMQ8HkGEflJE/DVwI3mCtassKmGtbX18IVHyntz07mg==\n" + "-----END PUBLIC KEY-----"; + test_begin("test_jwk_keys"); + struct dcrypt_keypair pair; + buffer_t *pem = t_buffer_create(256); + i_zero(&pair); + + test_assert(dcrypt_key_load_public(&pair.pub, jwk_key_json, NULL)); + test_assert(dcrypt_key_load_private(&pair.priv, jwk_key_json, NULL, NULL, NULL)); + + /* test accessors */ + test_assert_strcmp(dcrypt_key_get_id_public(pair.pub), "123"); + test_assert(dcrypt_key_get_usage_public(pair.pub) == DCRYPT_KEY_USAGE_SIGN); + + /* make sure we got the right key */ + test_assert(dcrypt_key_store_public(pair.pub, DCRYPT_FORMAT_PEM, pem, NULL)); + test_assert_strcmp(str_c(pem), pem_key); + + str_truncate(pem, 0); + test_assert(dcrypt_key_store_private(pair.priv, DCRYPT_FORMAT_JWK, NULL, pem, NULL, NULL, NULL)); + test_assert_strcmp(str_c(pem), jwk_key_json); + + dcrypt_keypair_unref(&pair); + + test_end(); +} + +static void test_static_verify_rsa(void) +{ + const char *error = NULL; + bool valid; + struct dcrypt_public_key *pub_key = NULL; + + test_begin("static verify (rsa)"); + const char *data = "test signature input\n"; + const unsigned char sig[] = { + 0x6f,0x1b,0xfb,0xdd,0xdb,0xb1,0xcd,0x6f,0xf1,0x1b, + 0xb8,0xad,0x71,0x75,0x6c,0x87,0x22,0x11,0xe4,0xc3, + 0xe7,0xca,0x15,0x04,0xda,0x98,0xab,0x07,0x27,0xcc, + 0x5a,0x4d,0xab,0xac,0x37,0x7a,0xff,0xd2,0xdf,0x37, + 0x58,0x37,0x53,0x46,0xd5,0x6d,0x9d,0x73,0x83,0x90, + 0xea,0x5e,0x2c,0xc7,0x51,0x9e,0xc4,0xda,0xc5,0x7d, + 0xa5,0xcd,0xb7,0xd7,0x41,0x23,0x6d,0xb9,0x6d,0xe0, + 0x99,0xa1,0x63,0x6b,0x60,0x5f,0x15,0x5b,0xda,0x21, + 0x17,0x4c,0x37,0x68,0x67,0x7f,0x8e,0x02,0x93,0xd2, + 0x86,0xdd,0xe5,0xa7,0xc3,0xd9,0x93,0x8b,0x0c,0x56, + 0x1d,0x5c,0x60,0x63,0x3e,0x8b,0xbe,0x1f,0xb2,0xe7, + 0x7f,0xe5,0x66,0x6f,0xcd,0x2b,0x0c,0x02,0x2a,0x12, + 0x96,0x86,0x66,0x00,0xff,0x12,0x8a,0x79 + }; + const char *key = "-----BEGIN PUBLIC KEY-----\n" +"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC89q02I9NezBLQ+otn5XLYE7S+\n" +"GsKUz59ogr45DA/6MI9jey0W56SeWQ1FJD1vDhAx/TRBMfOmhcIPsBjc5sakYOaw\n" +"PdoiqLjOIlO+iHwnbbmLuMsque09vgvZsKjuTr2F5DOFQY43Bq/Nd+4bjHJItdOM\n" +"58+xwA2I/8vDbtI8jwIDAQAB\n" +"-----END PUBLIC KEY-----"; + + test_assert(dcrypt_key_load_public(&pub_key, key, &error)); + if (pub_key == NULL) + i_fatal("%s", error); + test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_DSS, + data, strlen(data), + sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) && + valid); + dcrypt_key_unref_public(&pub_key); + + test_end(); +} + +/* Sample values from RFC8292 */ +static void test_static_verify_ecdsa_x962(void) +{ + const char *error = NULL; + bool valid; + struct dcrypt_public_key *pub_key = NULL; + + test_begin("static verify (ecdsa x9.62)"); + const char *data = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c" + "2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzp" + "wdXNoQGV4YW1wbGUuY29tIn0"; + const unsigned char sig[] = { + 0x8b,0x70,0x98,0x6f,0xbb,0x78,0xc5,0xfc,0x42,0x0e,0xab, + 0xa9,0xb4,0x53,0x9e,0xa4,0x2f,0x46,0x02,0xef,0xc7,0x2c, + 0x69,0x0c,0x94,0xcb,0x82,0x19,0x22,0xb6,0xae,0x98,0x94, + 0x7e,0x72,0xbd,0xa2,0x31,0x70,0x0d,0x76,0xf5,0x26,0xb1, + 0x2b,0xb6,0x6c,0xac,0x6b,0x33,0x63,0x8e,0xf5,0xb6,0x2f, + 0xd3,0xa4,0x49,0x21,0xf3,0xbe,0x80,0xf5,0xa0 + }; + const char *key = +"-----BEGIN PUBLIC KEY-----\n" +"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUfHPKLVFQzVvnCPGyfucbECzPDa\n" +"7rWbXriLcysAjEcXpgrmHhINiJz51G5T9EI8J8Dlqr2iNLCTljYSYKUE+w==\n" +"-----END PUBLIC KEY-----"; + + test_assert(dcrypt_key_load_public(&pub_key, key, &error)); + if (pub_key == NULL) + i_fatal("%s", error); + test_assert(dcrypt_verify(pub_key, "sha256", DCRYPT_SIGNATURE_FORMAT_X962, + data, strlen(data), + sig, sizeof(sig), &valid, DCRYPT_PADDING_RSA_PKCS1, &error) && + valid); + dcrypt_key_unref_public(&pub_key); + + test_end(); +} + + +int main(void) +{ + struct dcrypt_settings set = { + .module_dir = ".libs" + }; + const char *error; + + if (!dcrypt_initialize(NULL, &set, &error)) { + i_error("No functional dcrypt backend found - " + "skipping tests: %s", error); + return 0; + } + + static void (*const test_functions[])(void) = { + test_cipher_test_vectors, + test_cipher_aead_test_vectors, + test_hmac_test_vectors, + test_load_v1_keys, + test_load_v1_key, + test_load_v1_public_key, + test_load_v2_key, + test_load_v2_public_key, + test_get_info_v2_key, + test_gen_and_get_info_rsa_pem, + test_get_info_rsa_private_key, + test_get_info_invalid_keys, + test_get_info_key_encrypted, + test_get_info_pw_encrypted, + test_password_change, + test_load_invalid_keys, + test_raw_keys, + test_jwk_keys, + test_sign_verify_rsa, + test_sign_verify_ecdsa, + test_static_verify_ecdsa, + test_static_verify_rsa, + test_static_verify_ecdsa_x962, + NULL + }; + + int ret = test_run(test_functions); + + dcrypt_deinitialize(); + + return ret; +} diff --git a/src/lib-dcrypt/test-stream.c b/src/lib-dcrypt/test-stream.c new file mode 100644 index 0000000..8c1a985 --- /dev/null +++ b/src/lib-dcrypt/test-stream.c @@ -0,0 +1,639 @@ +/* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "str.h" +#include "dcrypt.h" +#include "dcrypt-iostream.h" +#include "ostream.h" +#include "ostream-encrypt.h" +#include "istream.h" +#include "istream-decrypt.h" +#include "istream-hash.h" +#include "istream-base64.h" +#include "randgen.h" +#include "hash-method.h" +#include "test-common.h" +#include "hex-binary.h" + +#include <fcntl.h> +#include <sys/stat.h> +#include <stdio.h> + +static const char key_v1_priv[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIGpAgEAMBAGByqGSM49AgEGBSuBBAAjBIGRMIGOAgEBBEGz2V2VMi/5s+Z+GJh7\n" + "4WfqZjZUpqqm+NJWojm6BbrZMY+9ZComlTGVcUZ007acFxV93oMmrfmtRUb5ynrb\n" + "MRFskKFGA0QAAwHrAJc8TvyPzspOoz6UH1C1YRmaUVm8tsLu2d0dYtZeOKJUl52J\n" + "4o8MKIg+ce4q0mTNFrhj+glKj29ppWti6JGAQA==\n" + "-----END PRIVATE KEY-----"; + +static const char key_v1_pub[] = + "-----BEGIN PUBLIC KEY-----\n" + "MFgwEAYHKoZIzj0CAQYFK4EEACMDRAADAesAlzxO/I/Oyk6jPpQfULVhGZpRWby2\n" + "wu7Z3R1i1l44olSXnYnijwwoiD5x7irSZM0WuGP6CUqPb2mla2LokYBA\n" + "-----END PUBLIC KEY-----"; + +static const char key_v2_priv[] = + "-----BEGIN PRIVATE KEY-----\n" + "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgtuQJA+uboZWVwgHc\n" + "DciyVdrovAPwlMqshDK3s78IDDuhRANCAAQm0VEdzLB9PtD0HA8JK1zifWnj8M00\n" + "FQzedfp9SQsWyA8dzs5/NFR5MTe6Xbh/ndKEs1zZH3vZ4FlNrilZc0st\n" + "-----END PRIVATE KEY-----"; + +static const char key_v2_pub[] = + "-----BEGIN PUBLIC KEY-----\n" + "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJtFRHcywfT7Q9BwPCStc4n1p4/DN\n" + "NBUM3nX6fUkLFsgPHc7OfzRUeTE3ul24f53ShLNc2R972eBZTa4pWXNLLQ==\n" + "-----END PUBLIC KEY-----"; + +static const char test_sample_v1_hash[] = + "1d7cc2cc1f1983f76241cc42389911e88590ad58cf9d54cafeb5b198d3723dd1"; +static const char test_sample_v1_short_hash[] = + "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c"; +static const char test_sample_v2_hash[] = + "2e31218656dd34db65b321688bf418dee4ee785e99eb9c21e0d29b4af27a863e"; + +static struct dcrypt_keypair test_v1_kp; +static struct dcrypt_keypair test_v2_kp; + +static void test_static_v1_input(void) +{ + ssize_t siz; + const struct hash_method *hash = hash_method_lookup("sha256"); + unsigned char hash_ctx[hash->context_size]; + unsigned char hash_dgst[hash->digest_size]; + hash->init(hash_ctx); + + test_begin("test_static_v1_input"); + + struct istream *is_1 = + i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1.asc", + IO_BLOCK_SIZE); + struct istream *is_2 = i_stream_create_base64_decoder(is_1); + i_stream_unref(&is_1); + struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); + i_stream_unref(&is_2); + struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); + i_stream_unref(&is_3); + + while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } + + if (is_4->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_4)); + + test_assert(is_4->stream_errno == 0); + + i_stream_unref(&is_4); + + hash->result(hash_ctx, hash_dgst); + + test_assert(strcmp(test_sample_v1_hash, + binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); + + test_end(); +} + +static void test_static_v1_input_short(void) +{ + ssize_t siz; + const struct hash_method *hash = hash_method_lookup("sha256"); + unsigned char hash_ctx[hash->context_size]; + unsigned char hash_dgst[hash->digest_size]; + hash->init(hash_ctx); + + test_begin("test_static_v1_input_short"); + + struct istream *is_1 = + i_stream_create_file(DCRYPT_SRC_DIR"/sample-v1_short.asc", + IO_BLOCK_SIZE); + struct istream *is_2 = i_stream_create_base64_decoder(is_1); + i_stream_unref(&is_1); + struct istream *is_3 = i_stream_create_decrypt(is_2, test_v1_kp.priv); + i_stream_unref(&is_2); + struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); + i_stream_unref(&is_3); + + while((siz = i_stream_read(is_4))>0) { i_stream_skip(is_4, siz); } + + if (is_4->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_4)); + + test_assert(is_4->stream_errno == 0); + + i_stream_unref(&is_4); + + hash->result(hash_ctx, hash_dgst); + + test_assert(strcmp(test_sample_v1_short_hash, + binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); + + test_end(); +} + +static void test_static_v2_input(void) +{ + test_begin("test_static_v2_input"); + + ssize_t amt; + const struct hash_method *hash = hash_method_lookup("sha256"); + unsigned char hash_ctx[hash->context_size]; + unsigned char hash_dgst[hash->digest_size]; + hash->init(hash_ctx); + + struct istream *is_1 = + i_stream_create_file(DCRYPT_SRC_DIR"/sample-v2.asc", + IO_BLOCK_SIZE); + struct istream *is_2 = i_stream_create_base64_decoder(is_1); + i_stream_unref(&is_1); + struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); + i_stream_unref(&is_2); + struct istream *is_4 = i_stream_create_hash(is_3, hash, hash_ctx); + i_stream_unref(&is_3); + + while((amt = i_stream_read(is_4))>0) { i_stream_skip(is_4, amt); } + + if (is_4->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_4)); + + test_assert(is_4->stream_errno == 0); + + i_stream_unref(&is_4); + + hash->result(hash_ctx, hash_dgst); + + test_assert(strcmp(test_sample_v2_hash, + binary_to_hex(hash_dgst, sizeof(hash_dgst))) == 0); + + test_end(); + +/** this code is left here to show how the sample file is created + struct istream *is = + i_stream_create_file("../lib-fts/udhr_fra.txt", 8192); + struct istream *is_2 = i_stream_create_hash(is, hash, hash_ctx); + int fd = open("sample-v2.bin", O_CREAT|O_TRUNC|O_WRONLY, S_IRWXU); + struct ostream *os = o_stream_create_fd_file(fd, 0, TRUE); + struct ostream *os_2 = o_stream_create_encrypt(os, + "aes-256-gcm-sha256", test_v2_kp.pub, + IO_STREAM_ENC_INTEGRITY_AEAD); + const unsigned char *ptr; + size_t siz; + + while(i_stream_read_data(is_2, &ptr, &siz, 0)>0) { + o_stream_nsend(os_2, ptr, siz); + i_stream_skip(is_2, siz); + } + + i_assert(o_stream_finish(os_2) > 0); + + o_stream_close(os_2); + i_stream_close(is_2); + + hash->result(hash_ctx, hash_dgst); + printf("%s\n", binary_to_hex(hash_dgst, sizeof(hash_dgst))); +*/ +} + +static void test_write_read_v1(void) +{ + test_begin("test_write_read_v1"); + unsigned char payload[IO_BLOCK_SIZE]; + const unsigned char *ptr; + size_t pos = 0, siz; + random_fill(payload, IO_BLOCK_SIZE); + + buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "<unused>", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); + o_stream_nsend(os_2, payload, sizeof(payload)); + + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + test_assert(os_2->stream_errno == 0); + test_assert(o_stream_finish(os_2) > 0); + test_assert(os_2->stream_errno == 0); + + o_stream_unref(&os); + o_stream_unref(&os_2); + + struct istream *is = test_istream_create_data(buf->data, buf->used); + struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); + + size_t offset = 0; + test_istream_set_allow_eof(is, FALSE); + test_istream_set_size(is, 0); + while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + else + test_istream_set_size(is, ++offset); + + test_assert_idx(pos + siz <= sizeof(payload), pos); + if (pos + siz > sizeof(payload)) + break; + test_assert_idx(siz == 0 || + memcmp(ptr, payload + pos, siz) == 0, pos); + i_stream_skip(is_2, siz); pos += siz; + } + + test_assert(is_2->stream_errno == 0); + + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + + test_end(); +} + +static void test_write_read_v1_short(void) +{ + test_begin("test_write_read_v1_short"); + unsigned char payload[1]; + const unsigned char *ptr; + size_t pos = 0, siz; + random_fill(payload, 1); + + buffer_t *buf = buffer_create_dynamic(default_pool, 64); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "<unused>", test_v2_kp.pub, IO_STREAM_ENC_VERSION_1); + o_stream_nsend(os_2, payload, sizeof(payload)); + + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + test_assert(os_2->stream_errno == 0); + test_assert(o_stream_finish(os_2) > 0); + test_assert(os_2->stream_errno == 0); + + o_stream_unref(&os); + o_stream_unref(&os_2); + + struct istream *is = test_istream_create_data(buf->data, buf->used); + struct istream *is_2 = i_stream_create_decrypt(is, test_v2_kp.priv); + + size_t offset = 0; + test_istream_set_allow_eof(is, FALSE); + test_istream_set_size(is, 0); + while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + else + test_istream_set_size(is, ++offset); + + test_assert_idx(pos + siz <= sizeof(payload), pos); + if (siz > sizeof(payload) || pos + siz > sizeof(payload)) + break; + test_assert_idx(siz == 0 || + memcmp(ptr, payload + pos, siz) == 0, pos); + i_stream_skip(is_2, siz); pos += siz; + } + + test_assert(is_2->stream_errno == 0); + + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + + test_end(); +} + +static void test_write_read_v1_empty(void) +{ + const unsigned char *ptr; + size_t siz; + test_begin("test_write_read_v1_empty"); + buffer_t *buf = buffer_create_dynamic(default_pool, 64); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "<unused>", test_v1_kp.pub, IO_STREAM_ENC_VERSION_1); + test_assert(o_stream_finish(os_2) > 0); + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + o_stream_unref(&os); + o_stream_unref(&os_2); + /* this should've been enough */ + + struct istream *is = test_istream_create_data(buf->data, buf->used); + struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); + + /* read should not fail */ + test_istream_set_allow_eof(is, FALSE); + test_istream_set_size(is, 0); + size_t offset = 0; + ssize_t ret; + while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { + test_assert(ret == 0); + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + else + test_istream_set_size(is, ++offset); + }; + + test_assert(is_2->stream_errno == 0); + if (is_2->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_2)); + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + test_end(); +} + +static void test_write_read_v2(void) +{ + test_begin("test_write_read_v2"); + unsigned char payload[IO_BLOCK_SIZE*10]; + const unsigned char *ptr; + size_t pos = 0, siz; + random_fill(payload, IO_BLOCK_SIZE*10); + + buffer_t *buf = buffer_create_dynamic(default_pool, sizeof(payload)); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "aes-256-gcm-sha256", test_v1_kp.pub, + IO_STREAM_ENC_INTEGRITY_AEAD); + o_stream_nsend(os_2, payload, sizeof(payload)); + test_assert(o_stream_finish(os_2) > 0); + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + o_stream_unref(&os); + o_stream_unref(&os_2); + + struct istream *is = test_istream_create_data(buf->data, buf->used); + /* test regression where read fails due to incorrect behaviour + when buffer is full before going to decrypt code */ + i_stream_set_max_buffer_size(is, 8192); + test_assert(i_stream_read(is) > 0); + struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); + + size_t offset = 0; + test_istream_set_size(is, 0); + test_istream_set_allow_eof(is, FALSE); + while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + else + test_istream_set_size(is, ++offset); + + test_assert_idx(pos + siz <= sizeof(payload), pos); + if (pos + siz > sizeof(payload)) break; + test_assert_idx(siz == 0 || + memcmp(ptr, payload + pos, siz) == 0, pos); + i_stream_skip(is_2, siz); pos += siz; + } + + test_assert(is_2->stream_errno == 0); + if (is_2->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_2)); + + /* test seeking */ + for (size_t i = sizeof(payload)-100; i > 100; i -= 100) { + i_stream_seek(is_2, i); + test_assert_idx(i_stream_read_data(is_2, &ptr, &siz, 0) == 1, i); + test_assert_idx(memcmp(ptr, payload + i, siz) == 0, i); + } + i_stream_seek(is_2, 0); + test_assert(i_stream_read_data(is_2, &ptr, &siz, 0) == 1); + test_assert(memcmp(ptr, payload, siz) == 0); + + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + + test_end(); +} + +static void test_write_read_v2_short(void) +{ + test_begin("test_write_read_v2_short"); + unsigned char payload[1]; + const unsigned char *ptr; + size_t pos = 0, siz; + random_fill(payload, 1); + + buffer_t *buf = buffer_create_dynamic(default_pool, 64); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "aes-256-gcm-sha256", test_v1_kp.pub, + IO_STREAM_ENC_INTEGRITY_AEAD); + o_stream_nsend(os_2, payload, sizeof(payload)); + test_assert(o_stream_finish(os_2) > 0); + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + o_stream_unref(&os); + o_stream_unref(&os_2); + + struct istream *is = test_istream_create_data(buf->data, buf->used); + struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); + + size_t offset = 0; + test_istream_set_allow_eof(is, FALSE); + test_istream_set_size(is, 0); + while(i_stream_read_data(is_2, &ptr, &siz, 0)>=0) { + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + test_istream_set_size(is, ++offset); + + test_assert_idx(pos + siz <= sizeof(payload), pos); + if (siz > sizeof(payload) || pos + siz > sizeof(payload)) + break; + test_assert_idx(siz == 0 || + memcmp(ptr, payload + pos, siz) == 0, pos); + i_stream_skip(is_2, siz); pos += siz; + } + + test_assert(is_2->stream_errno == 0); + if (is_2->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_2)); + + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + + test_end(); +} + +static void test_write_read_v2_empty(void) +{ + const unsigned char *ptr; + size_t siz; + test_begin("test_write_read_v2_empty"); + buffer_t *buf = buffer_create_dynamic(default_pool, 64); + struct ostream *os = o_stream_create_buffer(buf); + struct ostream *os_2 = o_stream_create_encrypt(os, + "aes-256-gcm-sha256", test_v1_kp.pub, + IO_STREAM_ENC_INTEGRITY_AEAD); + test_assert(o_stream_finish(os_2) > 0); + if (os_2->stream_errno != 0) + i_debug("error: %s", o_stream_get_error(os_2)); + + o_stream_unref(&os); + o_stream_unref(&os_2); + /* this should've been enough */ + + struct istream *is = test_istream_create_data(buf->data, buf->used); + struct istream *is_2 = i_stream_create_decrypt(is, test_v1_kp.priv); + + /* read should not fail */ + size_t offset = 0; + test_istream_set_allow_eof(is, FALSE); + test_istream_set_size(is, 0); + ssize_t ret; + while ((ret = i_stream_read_data(is_2, &ptr, &siz, 0)) >= 0) { + test_assert(ret == 0); + if (offset == buf->used) + test_istream_set_allow_eof(is, TRUE); + test_istream_set_size(is, ++offset); + }; + + test_assert(is_2->stream_errno == 0); + if (is_2->stream_errno != 0) + i_debug("error: %s", i_stream_get_error(is_2)); + i_stream_unref(&is); + i_stream_unref(&is_2); + buffer_free(&buf); + test_end(); +} + +static int +no_op_cb(const char *digest ATTR_UNUSED, + struct dcrypt_private_key **priv_key_r ATTR_UNUSED, + const char **error_r ATTR_UNUSED, + void *context ATTR_UNUSED) +{ + return 0; +} + +static void test_read_0_to_400_byte_garbage(void) +{ + test_begin("test_read_0_to_100_byte_garbage"); + + char data[512]; + memset(data, 0, sizeof(data)); + + for (size_t s = 0; s <= 400; ++s) { + struct istream *is = test_istream_create_data(data, s); + struct istream *ds = i_stream_create_decrypt_callback(is, + no_op_cb, NULL); + test_istream_set_size(is, 0); + test_istream_set_allow_eof(is, FALSE); + ssize_t siz = 0; + for (size_t offset = 0; offset <= s && siz == 0; offset++) { + if (offset == s) + test_istream_set_allow_eof(is, TRUE); + test_istream_set_size(is, offset); + siz = i_stream_read(ds); + } + test_assert_idx(siz < 0, s); + i_stream_unref(&ds); + i_stream_unref(&is); + } + + test_end(); +} + +static void test_read_large_header(void) +{ + test_begin("test_read_large_header"); + + struct istream *is = + test_istream_create_data(IOSTREAM_CRYPT_MAGIC, + sizeof(IOSTREAM_CRYPT_MAGIC)); + struct istream *ds = + i_stream_create_decrypt_callback(is, no_op_cb, NULL); + test_istream_set_allow_eof(is, FALSE); + test_istream_set_max_buffer_size(is, sizeof(IOSTREAM_CRYPT_MAGIC)); + + test_assert(i_stream_read(ds) == -1); + i_stream_unref(&ds); + i_stream_unref(&is); + + test_end(); +} + +static void test_read_increment(void) +{ + test_begin("test_read_increment"); + + ssize_t amt, total, i; + + struct istream *is_1 = i_stream_create_file( + DCRYPT_SRC_DIR"/sample-v2.asc", IO_BLOCK_SIZE); + struct istream *is_2 = i_stream_create_base64_decoder(is_1); + i_stream_unref(&is_1); + struct istream *is_3 = i_stream_create_decrypt(is_2, test_v2_kp.priv); + i_stream_unref(&is_2); + total = 0; + i = 500; + + i_stream_set_max_buffer_size(is_3, i); + while((amt = i_stream_read(is_3)) > 0) { + total += amt; + i_stream_set_max_buffer_size(is_3, ++i); + } + + /* make sure snapshotting works: */ + i_stream_skip(is_3, 1); + test_assert(i_stream_read(is_3) == -1); + + test_assert(total == 13534); + test_assert(is_3->stream_errno == 0); + test_assert(is_3->eof); + + i_stream_unref(&is_3); + + test_end(); +} + +static void test_free_keys() +{ + dcrypt_key_unref_private(&test_v1_kp.priv); + dcrypt_key_unref_public(&test_v1_kp.pub); + dcrypt_key_unref_private(&test_v2_kp.priv); + dcrypt_key_unref_public(&test_v2_kp.pub); +} + +int main(void) +{ + struct dcrypt_settings set = { + .module_dir = ".libs" + }; + const char *error; + + if (!dcrypt_initialize(NULL, &set, &error)) { + i_error("No functional dcrypt backend found - " + "skipping tests: %s", error); + return 0; + } + + test_assert(dcrypt_key_load_private(&test_v1_kp.priv, key_v1_priv, + NULL, NULL, NULL)); + test_assert(dcrypt_key_load_public(&test_v1_kp.pub, key_v1_pub, NULL)); + test_assert(dcrypt_key_load_private(&test_v2_kp.priv, key_v2_priv, + NULL, NULL, NULL)); + test_assert(dcrypt_key_load_public(&test_v2_kp.pub, key_v2_pub, NULL)); + + static void (*const test_functions[])(void) = { + test_static_v1_input, + test_static_v1_input_short, + test_static_v2_input, + test_read_increment, + test_write_read_v1, + test_write_read_v1_short, + test_write_read_v1_empty, + test_write_read_v2, + test_write_read_v2_short, + test_write_read_v2_empty, + test_free_keys, + test_read_0_to_400_byte_garbage, + test_read_large_header, + NULL + }; + + return test_run(test_functions); +} |