summaryrefslogtreecommitdiffstats
path: root/src/lib-dcrypt
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-dcrypt')
-rw-r--r--src/lib-dcrypt/Makefile.am75
-rw-r--r--src/lib-dcrypt/Makefile.in1142
-rw-r--r--src/lib-dcrypt/dcrypt-iostream.h16
-rw-r--r--src/lib-dcrypt/dcrypt-openssl.c3807
-rw-r--r--src/lib-dcrypt/dcrypt-private.h211
-rw-r--r--src/lib-dcrypt/dcrypt.c660
-rw-r--r--src/lib-dcrypt/dcrypt.h409
-rw-r--r--src/lib-dcrypt/istream-decrypt.c1146
-rw-r--r--src/lib-dcrypt/istream-decrypt.h48
-rw-r--r--src/lib-dcrypt/ostream-encrypt.c800
-rw-r--r--src/lib-dcrypt/ostream-encrypt.h30
-rw-r--r--src/lib-dcrypt/sample-v1.asc48
-rw-r--r--src/lib-dcrypt/sample-v1_short.asc4
-rw-r--r--src/lib-dcrypt/sample-v2.asc287
-rw-r--r--src/lib-dcrypt/test-crypto.c1314
-rw-r--r--src/lib-dcrypt/test-stream.c639
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, &params) < 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 @@
+Q1JZUFRFRAMHAQCtAEMCAMyKuGO/j3TPoXzRJ39THa1oGDChmueqRVFlR3qnIWcd
+Mt15zp+juTJzwfxKDNsgdIfFleIbuuo1AX1TgimaVfb8ACB8mhA56i5P7XPoHdP/
+w/oi6kooNSk5rd57+OqFiwD6TwAgWi/IHZ3tFmaohetUkFowgcrYwMh9HR9iOXg6
+QdIDnqMAIIrC9OcLyuMzUp18LpVKZLg6QaJEsjrepBatgkqRDgBKAAD7tnI+Rjjg
+rNZ5UHYvjA1xsrEhfbyx8X6Vb+em++p+aE+I92pBrqV/XIeR1er1oNX3nxZwEnL4
+UwhavZOMw7Qna0o4bop4PfK65HqnFTmgaiNDBMdE/CFaxSRlI0PLc0jhqxoEYU/d
+0hrtPcpQMq0sCxBbHqcnDF1xAK2hAZU12BH+JoV4bI1k1MMn1xAcXxiVdtSO5NE8
+E5fyaMfvTq2zIZtqQY09arrd0DaQ8o/L2dIV6jQVNZLxbRFWVayoWloT/YVSlHhi
+w5YHJetffXO02mllj3Mxr5aIfCmpZbrcWMfrF88ksI6HyQOrTHKS+Y95+VFLbxy4
++BWFGKV4zUGUwrfEDOpxIAZbBHsABjV82NS2TEZltu/ki3EhlwC8Hy+oyqn+LZN5
+LCmbt/maMI0EJU6cNCUCQM8Rq2Xv7xP/DrC3A0y7gj3pT44dY0dMj76gBApKO4Qw
+rLcBgY9qycXHtUwvtg/QZJrb2n2AB7h0+B3LgVm8P12l1KAFS2ykugBWUVJSuUjK
+5fjTk4EDIaCm0rhs2hNty9OtBkBuQBolkzxHtqp9/+QhIWJEtNtQEdnwN8DtdlNH
+/p69sZvkDhmyqSuByCodwVhkPZf5d/oGVUvE73btlAJcl8NZMXDqgKHfT4U5OF6Y
+eSXUIz9oiM2Wy1CVqrA2JdFGQ4Hcbf76IP462+gGefOd62atFfvjGGIb+Okyrmab
+jNxn/7wtUw42MXIzoAk+GQsOgo77rH075mILWqp0OuAyRTKmsZTP3FaDb4SxQk3K
+pw3N7HiNIiDW1wd5BLJE73qxKr+JC8GLs/s+JpfVb+lxzXAKXpEZTGFd/zphF1Kf
+J+aBCF+UB5Lq+QYoHfKSJzRb24PScAVs1VrV8nJlQvxMZW6Rd4ofNcsMjY+b1pBx
+bv74+oACEXC/F1jpp3tMTTLOLN8/hNq0J1mE3uOYRAaNK1jaQQAoa4rNxeY/2vBw
+mod8/Erc9M0M4B3VHVfRJ4F8MAx3b8if3oiicLu0OJ16snGHM7BGp2pOEPRVAg1Q
++70zDctuSV6HwPYSAXaIw2LPrbsQmq1Lq/JivHYRwjUDBFAaXkPv2WFEfUsXEtmw
+UzUPLgWxZM8MsPDEI510SzRd3OlBUjuS96+/9n/Qv1D5DBnrDH9gUwUiPr9V+rMM
+kmNaAQpTKpCNpevCH/F18rQnUi/PJKY37q8pF/lO0OW7mzS+CxCvFr+aWxZ2kV84
+GwUrUuSdHa1Z/0BY/UTZn1BAi6bOudiWHH5loJBldreSJ2K2/iwMpLUUEuQ+TJJ8
+BvsWeOLMqEpDIxuuYERCUqHO8EmtvsSPKcIeS7ZZpvkRlRIWCuKgnaHSymT87Eq1
+SJskTkAWd1iGvQI78tM8KwT2KDmf7Qs3RUCiSynT/H1OmVQqwVn/5c9wFjV+0PRA
+KFZZDEMvwy5tYpJ1Y0nYuUUMOlA8l11rpKqAIcRj0V256uXoj5cBnTsGZAOFDBqx
+pBLmGFgz1havj8RsgqtJfCkkh+Y8l9xszAC98/FGYOtmKpP4sXXM3LASkhi2FU4C
+OH/4tUsoh3tMCendx36s1UmliE94BiNuJMUAqSh9cLCnW/Uiw0bzqV8GOJLDk/A3
+XMWrERuA2Jn7qQ9e9qmYarc8r6JjWUuxECZ04d12wNCcDF7hWxFYLbwTk/ZUIM5D
+1ZsdOPUQDf7gjEW4gQoOK7pQWifJa56ZSSFurLoL5ae+3dPwUu8HNQLvwmx4paSe
+01shQd36MXcRZ7BRVp0GqNrviuAtXacSxx1GIO9rh7RtyGGs1grsQ07P6Evpk0k/
+1WY48cE+xWU5SH4JwxMZ3vbDguMY/cnp2VhuzZguJ4iIFKg5RMVShrSkZQcWwH7e
+7JVu7hOe3bWp1KeVG41IsOFpo0Jfpegtgf4r1hYih02Q54UNIFf5G3IRsbC1pjtj
+ALYteCLe9oa+7lIAVDWgmq/NqWLsi4dtlz97TG8XApIFZ6Prr7KG8N+RFTmouXYT
+QH6FuF5XvJ0TqIgIkdSzbuaNmN47E6PQoDAuRJ4X9ahYpF1xC4ecnAaI+rlaparF
+Z1OOtwYVQ1Hthc7wp+205aK6ujZyU6a9MrGXGZklRQdigCd+EOs0kWy8t+bcmk6K
+9aXEJyGMBtcgQDGWZZJer5w8aUKj6SDp9y7X1kyAuhh8DFvNKMgR3vs4wnvsjr1W
+cYxH8RvAVXj76Xjvvgeg3jcJbcr0mPwB0SrcpQ+88mF64x+nrKsXmz9E8wBgOMY6
+4qXXb27YUehAGN81jTZRN8lShl20fnPZKd5U1sqIhevXNUrTVjxVNff0fHRnOOb9
+sVD3vdQmfbXvB3nyQLKQ+Wcw/WPqa0But5KEXFjnaXPORcEWEpYvWOgGPegmrTgh
+P4ZVscRdxozvd0sKvpLNMd/EiNLBaYft+4Yo38WZYkZzNJT7LhW41pbQyK1cmZiU
+COisSP2rrus6iKCNwaGPMZJNCCdQN862DSTaa0IZSEeSRpfBfA2UevSsfX0uTgyb
+B4Az1u4QJv9fQp8oAnnpH7YHhW43V/YTVuwLLF7pqSl2J2gNcLFfGuJVftRp6xJg
+mkfXOxoJ0y5CQU7pwQZ/F6WxuWBX1m7MNE/OfI9l9ODj0uAgKn1e+DzH1VXZi8ls
+lenQuepkt7zqXG5dRRhW+FSlJy1oo9oPcf2bZhAYMty3mh9F4Ils3SSCE6T6cU0A
+yYCNrq8iKyq5V1jdC9haN5NmF9yNiIxYWdbigcBTzR5+AZuNe7aJSXu0qvQJR9s0
+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);
+}