diff options
Diffstat (limited to 'sm')
-rw-r--r-- | sm/Makefile.am | 33 | ||||
-rw-r--r-- | sm/Makefile.in | 265 | ||||
-rw-r--r-- | sm/call-agent.c | 70 | ||||
-rw-r--r-- | sm/call-dirmngr.c | 55 | ||||
-rw-r--r-- | sm/certchain.c | 220 | ||||
-rw-r--r-- | sm/certcheck.c | 31 | ||||
-rw-r--r-- | sm/certlist.c | 71 | ||||
-rw-r--r-- | sm/certreqgen-ui.c | 4 | ||||
-rw-r--r-- | sm/certreqgen.c | 240 | ||||
-rw-r--r-- | sm/decrypt.c | 396 | ||||
-rw-r--r-- | sm/delete.c | 4 | ||||
-rw-r--r-- | sm/encrypt.c | 413 | ||||
-rw-r--r-- | sm/export.c | 4 | ||||
-rw-r--r-- | sm/fingerprint.c | 35 | ||||
-rw-r--r-- | sm/gpgsm.c | 48 | ||||
-rw-r--r-- | sm/gpgsm.h | 33 | ||||
-rw-r--r-- | sm/gpgsm.w32-manifest.in | 9 | ||||
-rw-r--r-- | sm/import.c | 112 | ||||
-rw-r--r-- | sm/keydb.c | 21 | ||||
-rw-r--r-- | sm/keylist.c | 53 | ||||
-rw-r--r-- | sm/minip12.c | 2482 | ||||
-rw-r--r-- | sm/minip12.h | 2 | ||||
-rw-r--r-- | sm/misc.c | 109 | ||||
-rw-r--r-- | sm/server.c | 75 | ||||
-rw-r--r-- | sm/sign.c | 478 | ||||
-rw-r--r-- | sm/t-minip12.c | 800 | ||||
-rw-r--r-- | sm/verify.c | 38 |
27 files changed, 4700 insertions, 1401 deletions
diff --git a/sm/Makefile.am b/sm/Makefile.am index 0bc7640..db8b35e 100644 --- a/sm/Makefile.am +++ b/sm/Makefile.am @@ -17,9 +17,17 @@ ## Process this file with automake to produce Makefile.in -EXTRA_DIST = ChangeLog-2011 gpgsm-w32info.rc gpgsm.w32-manifest.in +EXTRA_DIST = ChangeLog-2011 \ + gpgsm-w32info.rc gpgsm.w32-manifest.in bin_PROGRAMS = gpgsm +noinst_PROGRAMS = $(module_tests) $(module_maint_tests) + +if DISABLE_TESTS +TESTS = +else +TESTS = $(module_tests) +endif AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) @@ -27,10 +35,8 @@ AM_CPPFLAGS = -DKEYBOX_WITH_X509=1 include $(top_srcdir)/am/cmacros.am if HAVE_W32_SYSTEM -gpgsm_robjs = $(resource_objs) gpgsm-w32info.o -gpgsm-w32info.o : gpgsm.w32-manifest -else -gpgsm_robjs = +gpgsm_rc_objs = $(resource_objs) gpgsm-w32info.o +gpgsm-w32info.o : gpgsm.w32-manifest ../common/w32info-rc.h endif gpgsm_SOURCES = \ @@ -65,8 +71,23 @@ common_libs = ../kbx/libkeybox509.a $(libcommon) gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ $(GPG_ERROR_LIBS) $(LIBREADLINE) $(LIBINTL) \ - $(LIBICONV) $(gpgsm_robjs) $(extra_sys_libs) $(NETLIBS) + $(LIBICONV) $(gpgsm_rc_objs) $(extra_sys_libs) $(NETLIBS) gpgsm_LDFLAGS = $(extra_bin_ldflags) +gpgsm_DEPENDENCIES = $(gpgsm_rc_objs) + +module_tests = t-minip12 +module_maint_tests = + +t_common_src = +t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) + + +t_minip12_CFLAGS = -DWITHOUT_NPTH=1 \ + $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(GPG_ERROR_CFLAGS) +t_minip12_SOURCES = $(t_common_src) t-minip12.c minip12.c +t_minip12_LDADD = $(t_common_ldadd) $(NETLIBS) + # Make sure that all libs are build before we use them. This is # important for things like make -j2. diff --git a/sm/Makefile.in b/sm/Makefile.in index 286b652..23fbac3 100644 --- a/sm/Makefile.in +++ b/sm/Makefile.in @@ -124,6 +124,8 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ bin_PROGRAMS = gpgsm$(EXEEXT) +noinst_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) +@DISABLE_TESTS_FALSE@TESTS = $(am__EXEEXT_1) @HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \ @HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBEXECDIR="\"$(libexecdir)\"" \ @HAVE_DOSISH_SYSTEM_FALSE@ -DGNUPG_LIBDIR="\"$(libdir)/@PACKAGE@\"" \ @@ -146,17 +148,16 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ - $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ - $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ - $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ksba.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/ldap.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libassuan.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/npth.m4 \ + $(top_srcdir)/m4/ntbtls.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/socklen.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 $(top_srcdir)/m4/tar-ustar.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -165,7 +166,9 @@ CONFIG_HEADER = $(top_builddir)/config.h CONFIG_CLEAN_FILES = gpgsm.w32-manifest CONFIG_CLEAN_VPATH_FILES = am__installdirs = "$(DESTDIR)$(bindir)" -PROGRAMS = $(bin_PROGRAMS) +am__EXEEXT_1 = t-minip12$(EXEEXT) +am__EXEEXT_2 = +PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am_gpgsm_OBJECTS = gpgsm.$(OBJEXT) misc.$(OBJEXT) keydb.$(OBJEXT) \ server.$(OBJEXT) call-agent.$(OBJEXT) call-dirmngr.$(OBJEXT) \ fingerprint.$(OBJEXT) certlist.$(OBJEXT) certdump.$(OBJEXT) \ @@ -178,13 +181,17 @@ gpgsm_OBJECTS = $(am_gpgsm_OBJECTS) am__DEPENDENCIES_1 = @HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ @HAVE_W32_SYSTEM_TRUE@ gpgsm-w32info.o -gpgsm_DEPENDENCIES = $(common_libs) ../common/libgpgrl.a \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ +gpgsm_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(gpgsm_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__objects_1 = +am_t_minip12_OBJECTS = $(am__objects_1) t_minip12-t-minip12.$(OBJEXT) \ + t_minip12-minip12.$(OBJEXT) +t_minip12_OBJECTS = $(am_t_minip12_OBJECTS) +am__DEPENDENCIES_3 = $(libcommon) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -gpgsm_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(gpgsm_LDFLAGS) \ +t_minip12_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) +t_minip12_LINK = $(CCLD) $(t_minip12_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ $(LDFLAGS) -o $@ AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) @@ -213,8 +220,13 @@ am__depfiles_remade = ./$(DEPDIR)/call-agent.Po \ ./$(DEPDIR)/minip12.Po ./$(DEPDIR)/misc.Po \ ./$(DEPDIR)/passphrase.Po ./$(DEPDIR)/qualified.Po \ ./$(DEPDIR)/server.Po ./$(DEPDIR)/sign.Po \ - ./$(DEPDIR)/verify.Po + ./$(DEPDIR)/t_minip12-minip12.Po \ + ./$(DEPDIR)/t_minip12-t-minip12.Po ./$(DEPDIR)/verify.Po am__mv = mv -f +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 = COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) AM_V_CC = $(am__v_CC_@AM_V@) @@ -227,8 +239,8 @@ 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 = $(gpgsm_SOURCES) -DIST_SOURCES = $(gpgsm_SOURCES) +SOURCES = $(gpgsm_SOURCES) $(t_minip12_SOURCES) +DIST_SOURCES = $(gpgsm_SOURCES) $(t_minip12_SOURCES) am__can_run_installinfo = \ case $$AM_UPDATE_INFO_DIR in \ n|no|NO) false;; \ @@ -253,6 +265,28 @@ am__define_uniq_tagged_files = \ done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red='[0;31m'; \ + grn='[0;32m'; \ + lgn='[1;32m'; \ + blu='[1;34m'; \ + mgn='[0;35m'; \ + brg='[1m'; \ + std='[m'; \ + fi; \ +} am__DIST_COMMON = $(srcdir)/Makefile.in \ $(srcdir)/gpgsm.w32-manifest.in $(top_srcdir)/am/cmacros.am \ $(top_srcdir)/build-aux/depcomp \ @@ -443,7 +477,9 @@ target_alias = @target_alias@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ -EXTRA_DIST = ChangeLog-2011 gpgsm-w32info.rc gpgsm.w32-manifest.in +EXTRA_DIST = ChangeLog-2011 \ + gpgsm-w32info.rc gpgsm.w32-manifest.in + AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) # NB: AM_CFLAGS may also be used by tools running on the build @@ -469,8 +505,7 @@ libcommon = ../common/libcommon.a libcommonpth = ../common/libcommonpth.a libcommontls = ../common/libcommontls.a libcommontlsnpth = ../common/libcommontlsnpth.a -@HAVE_W32_SYSTEM_FALSE@gpgsm_robjs = -@HAVE_W32_SYSTEM_TRUE@gpgsm_robjs = $(resource_objs) gpgsm-w32info.o +@HAVE_W32_SYSTEM_TRUE@gpgsm_rc_objs = $(resource_objs) gpgsm-w32info.o gpgsm_SOURCES = \ gpgsm.c gpgsm.h \ misc.c \ @@ -501,9 +536,21 @@ common_libs = ../kbx/libkeybox509.a $(libcommon) gpgsm_LDADD = $(common_libs) ../common/libgpgrl.a \ $(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) \ $(GPG_ERROR_LIBS) $(LIBREADLINE) $(LIBINTL) \ - $(LIBICONV) $(gpgsm_robjs) $(extra_sys_libs) $(NETLIBS) + $(LIBICONV) $(gpgsm_rc_objs) $(extra_sys_libs) $(NETLIBS) gpgsm_LDFLAGS = $(extra_bin_ldflags) +gpgsm_DEPENDENCIES = $(gpgsm_rc_objs) +module_tests = t-minip12 +module_maint_tests = +t_common_src = +t_common_ldadd = $(libcommon) $(LIBGCRYPT_LIBS) $(KSBA_LIBS) \ + $(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) + +t_minip12_CFLAGS = -DWITHOUT_NPTH=1 \ + $(LIBGCRYPT_CFLAGS) $(KSBA_CFLAGS) $(GPG_ERROR_CFLAGS) + +t_minip12_SOURCES = $(t_common_src) t-minip12.c minip12.c +t_minip12_LDADD = $(t_common_ldadd) $(NETLIBS) all: all-am .SUFFIXES: @@ -583,10 +630,17 @@ uninstall-binPROGRAMS: clean-binPROGRAMS: -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + gpgsm$(EXEEXT): $(gpgsm_OBJECTS) $(gpgsm_DEPENDENCIES) $(EXTRA_gpgsm_DEPENDENCIES) @rm -f gpgsm$(EXEEXT) $(AM_V_CCLD)$(gpgsm_LINK) $(gpgsm_OBJECTS) $(gpgsm_LDADD) $(LIBS) +t-minip12$(EXEEXT): $(t_minip12_OBJECTS) $(t_minip12_DEPENDENCIES) $(EXTRA_t_minip12_DEPENDENCIES) + @rm -f t-minip12$(EXEEXT) + $(AM_V_CCLD)$(t_minip12_LINK) $(t_minip12_OBJECTS) $(t_minip12_LDADD) $(LIBS) + mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -616,6 +670,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/qualified.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_minip12-minip12.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/t_minip12-t-minip12.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Po@am__quote@ # am--include-marker $(am__depfiles_remade): @@ -638,6 +694,34 @@ am--depfiles: $(am__depfiles_remade) @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` +t_minip12-t-minip12.o: t-minip12.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_minip12_CFLAGS) $(CFLAGS) -MT t_minip12-t-minip12.o -MD -MP -MF $(DEPDIR)/t_minip12-t-minip12.Tpo -c -o t_minip12-t-minip12.o `test -f 't-minip12.c' || echo '$(srcdir)/'`t-minip12.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_minip12-t-minip12.Tpo $(DEPDIR)/t_minip12-t-minip12.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-minip12.c' object='t_minip12-t-minip12.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) $(t_minip12_CFLAGS) $(CFLAGS) -c -o t_minip12-t-minip12.o `test -f 't-minip12.c' || echo '$(srcdir)/'`t-minip12.c + +t_minip12-t-minip12.obj: t-minip12.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_minip12_CFLAGS) $(CFLAGS) -MT t_minip12-t-minip12.obj -MD -MP -MF $(DEPDIR)/t_minip12-t-minip12.Tpo -c -o t_minip12-t-minip12.obj `if test -f 't-minip12.c'; then $(CYGPATH_W) 't-minip12.c'; else $(CYGPATH_W) '$(srcdir)/t-minip12.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_minip12-t-minip12.Tpo $(DEPDIR)/t_minip12-t-minip12.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='t-minip12.c' object='t_minip12-t-minip12.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) $(t_minip12_CFLAGS) $(CFLAGS) -c -o t_minip12-t-minip12.obj `if test -f 't-minip12.c'; then $(CYGPATH_W) 't-minip12.c'; else $(CYGPATH_W) '$(srcdir)/t-minip12.c'; fi` + +t_minip12-minip12.o: minip12.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_minip12_CFLAGS) $(CFLAGS) -MT t_minip12-minip12.o -MD -MP -MF $(DEPDIR)/t_minip12-minip12.Tpo -c -o t_minip12-minip12.o `test -f 'minip12.c' || echo '$(srcdir)/'`minip12.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_minip12-minip12.Tpo $(DEPDIR)/t_minip12-minip12.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='minip12.c' object='t_minip12-minip12.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) $(t_minip12_CFLAGS) $(CFLAGS) -c -o t_minip12-minip12.o `test -f 'minip12.c' || echo '$(srcdir)/'`minip12.c + +t_minip12-minip12.obj: minip12.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(t_minip12_CFLAGS) $(CFLAGS) -MT t_minip12-minip12.obj -MD -MP -MF $(DEPDIR)/t_minip12-minip12.Tpo -c -o t_minip12-minip12.obj `if test -f 'minip12.c'; then $(CYGPATH_W) 'minip12.c'; else $(CYGPATH_W) '$(srcdir)/minip12.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/t_minip12-minip12.Tpo $(DEPDIR)/t_minip12-minip12.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='minip12.c' object='t_minip12-minip12.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) $(t_minip12_CFLAGS) $(CFLAGS) -c -o t_minip12-minip12.obj `if test -f 'minip12.c'; then $(CYGPATH_W) 'minip12.c'; else $(CYGPATH_W) '$(srcdir)/minip12.c'; fi` + ID: $(am__tagged_files) $(am__define_uniq_tagged_files); mkid -fID $$unique tags: tags-am @@ -690,6 +774,99 @@ cscopelist-am: $(am__tagged_files) distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags +check-TESTS: $(TESTS) + @failed=0; all=0; xfail=0; xpass=0; skip=0; \ + srcdir=$(srcdir); export srcdir; \ + list=' $(TESTS) '; \ + $(am__tty_colors); \ + if test -n "$$list"; then \ + for tst in $$list; do \ + if test -f ./$$tst; then dir=./; \ + elif test -f $$tst; then dir=; \ + else dir="$(srcdir)/"; fi; \ + if $(TESTS_ENVIRONMENT) $${dir}$$tst $(AM_TESTS_FD_REDIRECT); then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xpass=`expr $$xpass + 1`; \ + failed=`expr $$failed + 1`; \ + col=$$red; res=XPASS; \ + ;; \ + *) \ + col=$$grn; res=PASS; \ + ;; \ + esac; \ + elif test $$? -ne 77; then \ + all=`expr $$all + 1`; \ + case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$tst[\ \ ]*) \ + xfail=`expr $$xfail + 1`; \ + col=$$lgn; res=XFAIL; \ + ;; \ + *) \ + failed=`expr $$failed + 1`; \ + col=$$red; res=FAIL; \ + ;; \ + esac; \ + else \ + skip=`expr $$skip + 1`; \ + col=$$blu; res=SKIP; \ + fi; \ + echo "$${col}$$res$${std}: $$tst"; \ + done; \ + if test "$$all" -eq 1; then \ + tests="test"; \ + All=""; \ + else \ + tests="tests"; \ + All="All "; \ + fi; \ + if test "$$failed" -eq 0; then \ + if test "$$xfail" -eq 0; then \ + banner="$$All$$all $$tests passed"; \ + else \ + if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \ + banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \ + fi; \ + else \ + if test "$$xpass" -eq 0; then \ + banner="$$failed of $$all $$tests failed"; \ + else \ + if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \ + banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \ + fi; \ + fi; \ + dashes="$$banner"; \ + skipped=""; \ + if test "$$skip" -ne 0; then \ + if test "$$skip" -eq 1; then \ + skipped="($$skip test was not run)"; \ + else \ + skipped="($$skip tests were not run)"; \ + fi; \ + test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$skipped"; \ + fi; \ + report=""; \ + if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \ + report="Please report to $(PACKAGE_BUGREPORT)"; \ + test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \ + dashes="$$report"; \ + fi; \ + dashes=`echo "$$dashes" | sed s/./=/g`; \ + if test "$$failed" -eq 0; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + fi; \ + echo "$${col}$$dashes$${std}"; \ + echo "$${col}$$banner$${std}"; \ + test -z "$$skipped" || echo "$${col}$$skipped$${std}"; \ + test -z "$$report" || echo "$${col}$$report$${std}"; \ + echo "$${col}$$dashes$${std}"; \ + test "$$failed" -eq 0; \ + else :; fi + distdir: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) distdir-am @@ -724,6 +901,7 @@ distdir-am: $(DISTFILES) fi; \ done check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-TESTS check: check-am all-am: Makefile $(PROGRAMS) installdirs: @@ -762,7 +940,8 @@ maintainer-clean-generic: @echo "it deletes files that may require special tools to rebuild." clean: clean-am -clean-am: clean-binPROGRAMS clean-generic mostlyclean-am +clean-am: clean-binPROGRAMS clean-generic clean-noinstPROGRAMS \ + mostlyclean-am distclean: distclean-am -rm -f ./$(DEPDIR)/call-agent.Po @@ -788,6 +967,8 @@ distclean: distclean-am -rm -f ./$(DEPDIR)/qualified.Po -rm -f ./$(DEPDIR)/server.Po -rm -f ./$(DEPDIR)/sign.Po + -rm -f ./$(DEPDIR)/t_minip12-minip12.Po + -rm -f ./$(DEPDIR)/t_minip12-t-minip12.Po -rm -f ./$(DEPDIR)/verify.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ @@ -857,6 +1038,8 @@ maintainer-clean: maintainer-clean-am -rm -f ./$(DEPDIR)/qualified.Po -rm -f ./$(DEPDIR)/server.Po -rm -f ./$(DEPDIR)/sign.Po + -rm -f ./$(DEPDIR)/t_minip12-minip12.Po + -rm -f ./$(DEPDIR)/t_minip12-t-minip12.Po -rm -f ./$(DEPDIR)/verify.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -875,28 +1058,28 @@ ps-am: uninstall-am: uninstall-binPROGRAMS -.MAKE: install-am install-strip - -.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ - clean-binPROGRAMS clean-generic cscopelist-am ctags ctags-am \ - distclean distclean-compile distclean-generic distclean-tags \ - distdir dvi dvi-am html html-am info info-am install \ - install-am install-binPROGRAMS install-data install-data-am \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-html install-html-am install-info install-info-am \ - install-man install-pdf install-pdf-am install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \ - ps ps-am tags tags-am uninstall uninstall-am \ - uninstall-binPROGRAMS +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-TESTS \ + check-am clean clean-binPROGRAMS clean-generic \ + clean-noinstPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-tags distdir dvi \ + dvi-am html html-am info info-am install install-am \ + install-binPROGRAMS install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS .PRECIOUS: Makefile @HAVE_W32_SYSTEM_TRUE@.rc.o: @HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" -@HAVE_W32_SYSTEM_TRUE@gpgsm-w32info.o : gpgsm.w32-manifest +@HAVE_W32_SYSTEM_TRUE@gpgsm-w32info.o : gpgsm.w32-manifest ../common/w32info-rc.h # Make sure that all libs are build before we use them. This is # important for things like make -j2. diff --git a/sm/call-agent.c b/sm/call-agent.c index 0c271d9..438da51 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -246,7 +246,9 @@ default_inq_cb (void *opaque, const char *line) && have_static_passphrase ()) { const char *s = get_static_passphrase (); + assuan_begin_confidential (parm->ctx); err = assuan_send_data (parm->ctx, s, strlen (s)); + assuan_end_confidential (parm->ctx); } else log_error ("ignoring gpg-agent inquiry '%s'\n", line); @@ -334,7 +336,7 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *digest, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen ) { - int rc, i; + int rc, i, pkalgo; char *p, line[ASSUAN_LINELENGTH]; membuf_t data; size_t len; @@ -342,6 +344,7 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, unsigned char *sigbuf; size_t sigbuflen; struct default_inq_parm_s inq_parm; + gcry_sexp_t sig; (void)desc; @@ -353,6 +356,8 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break; case GCRY_MD_MD5: hashopt = "--hash=md5"; break; case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break; + case GCRY_MD_SHA384:hashopt = "--hash=sha384"; break; + case GCRY_MD_SHA512:hashopt = "--hash=sha512"; break; default: return gpg_error (GPG_ERR_DIGEST_ALGO); } @@ -366,6 +371,23 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, if (digestlen*2 + 50 > DIM(line)) return gpg_error (GPG_ERR_GENERAL); + /* Get the key type from the scdaemon. */ + snprintf (line, DIM(line), "SCD READKEY %s", keyid); + init_membuf (&data, 1024); + rc = assuan_transact (agent_ctx, line, + put_membuf_cb, &data, NULL, NULL, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return rc; + } + + p = get_membuf (&data, &len); + pkalgo = get_pk_algo_from_canon_sexp (p, len); + xfree (p); + if (!pkalgo) + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + p = stpcpy (line, "SCD SETDATA " ); for (i=0; i < digestlen ; i++, p += 2 ) sprintf (p, "%02X", digest[i]); @@ -386,24 +408,31 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, } sigbuf = get_membuf (&data, &sigbuflen); - /* Create an S-expression from it which is formatted like this: - "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever - creates non-RSA keys we need to change things. */ - *r_buflen = 21 + 11 + sigbuflen + 4; - p = xtrymalloc (*r_buflen); - *r_buf = (unsigned char*)p; - if (!p) + switch(pkalgo) { - xfree (sigbuf); - return 0; + case GCRY_PK_RSA: + rc = gcry_sexp_build (&sig, NULL, "(sig-val(rsa(s%b)))", + sigbuflen, sigbuf); + break; + + case GCRY_PK_ECC: + rc = gcry_sexp_build (&sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))", + sigbuflen/2, sigbuf, + sigbuflen/2, sigbuf + sigbuflen/2); + break; + + default: + rc = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + break; } - p = stpcpy (p, "(7:sig-val(3:rsa(1:s" ); - sprintf (p, "%u:", (unsigned int)sigbuflen); - p += strlen (p); - memcpy (p, sigbuf, sigbuflen); - p += sigbuflen; - strcpy (p, ")))"); xfree (sigbuf); + if (rc) + return rc; + + rc = make_canon_sexp (sig, r_buf, r_buflen); + gcry_sexp_release (sig); + if (rc) + return rc; assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); return 0; @@ -845,6 +874,10 @@ istrusted_status_cb (void *opaque, const char *line) flags->relax = 1; else if (has_leading_keyword (line, "cm")) flags->chain_model = 1; + else if (has_leading_keyword (line, "qual")) + flags->qualified = 1; + else if (has_leading_keyword (line, "de-vs")) + flags->de_vs = 1; } return 0; } @@ -1273,6 +1306,7 @@ gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, char *arg4 = NULL; membuf_t data; struct default_inq_parm_s inq_parm; + int wasconf; *r_passphrase = NULL; @@ -1291,9 +1325,13 @@ gpgsm_agent_ask_passphrase (ctrl_t ctrl, const char *desc_msg, int repeat, xfree (arg4); init_membuf_secure (&data, 64); + wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (agent_ctx); err = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &inq_parm, NULL, NULL); + if (!wasconf) + assuan_end_confidential (agent_ctx); if (err) xfree (get_membuf (&data, NULL)); diff --git a/sm/call-dirmngr.c b/sm/call-dirmngr.c index 1a411f2..0bd805e 100644 --- a/sm/call-dirmngr.c +++ b/sm/call-dirmngr.c @@ -415,8 +415,8 @@ inq_certificate (void *opaque, const char *line) int err; ksba_cert_t cert; - - err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, + FIND_CERT_ALLOW_AMBIG|FIND_CERT_WITH_EPHEM); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); @@ -605,7 +605,8 @@ gpgsm_dirmngr_isvalid (ctrl_t ctrl, { /* Note the no_dirmngr flag: This avoids checking this certificate over and over again. */ - rc = gpgsm_validate_chain (ctrl, rspcert, "", NULL, 0, NULL, + rc = gpgsm_validate_chain (ctrl, rspcert, GNUPG_ISOTIME_NONE, + NULL, 0, NULL, VALIDATE_FLAG_NO_DIRMNGR, NULL); if (rc) { @@ -940,21 +941,48 @@ static gpg_error_t run_command_inq_cb (void *opaque, const char *line) { struct run_command_parm_s *parm = opaque; + gpg_error_t err; const char *s; int rc = 0; + ksba_cert_t cert = NULL; + ksba_sexp_t ski = NULL; + const unsigned char *der; + size_t derlen, n; if ((s = has_leading_keyword (line, "SENDCERT"))) - { /* send the given certificate */ - int err; - ksba_cert_t cert; - const unsigned char *der; - size_t derlen; - + { + /* Send the given certificate. */ line = s; if (!*line) return gpg_error (GPG_ERR_ASS_PARAMETER); - err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, 1); + err = gpgsm_find_cert (parm->ctrl, line, NULL, &cert, + FIND_CERT_ALLOW_AMBIG); + if (err) + { + log_error ("certificate not found: %s\n", gpg_strerror (err)); + rc = gpg_error (GPG_ERR_NOT_FOUND); + } + else + { + der = ksba_cert_get_image (cert, &derlen); + if (!der) + rc = gpg_error (GPG_ERR_INV_CERT_OBJ); + else + rc = assuan_send_data (parm->ctx, der, derlen); + } + } + else if ((s = has_leading_keyword (line, "SENDCERT_SKI"))) + { + /* Send a certificate where a sourceKeyIdentifier is included. */ + line = s; + ski = make_simple_sexp_from_hexstr (line, &n); + line += n; + while (*line == ' ') + line++; + + err = gpgsm_find_cert (parm->ctrl, line, ski, &cert, + FIND_CERT_ALLOW_AMBIG|FIND_CERT_WITH_EPHEM); if (err) { log_error ("certificate not found: %s\n", gpg_strerror (err)); @@ -967,11 +995,11 @@ run_command_inq_cb (void *opaque, const char *line) rc = gpg_error (GPG_ERR_INV_CERT_OBJ); else rc = assuan_send_data (parm->ctx, der, derlen); - ksba_cert_release (cert); } } else if ((s = has_leading_keyword (line, "PRINTINFO"))) - { /* Simply show the message given in the argument. */ + { + /* Simply show the message given in the argument. */ line = s; log_info ("dirmngr: %s\n", line); } @@ -981,7 +1009,6 @@ run_command_inq_cb (void *opaque, const char *line) root certificate. */ char fpr[41]; struct rootca_flags_s rootca_flags; - int n; line = s; @@ -1005,6 +1032,8 @@ run_command_inq_cb (void *opaque, const char *line) rc = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE); } + ksba_cert_release (cert); + xfree (ski); return rc; } diff --git a/sm/certchain.c b/sm/certchain.c index d2a1800..57de483 100644 --- a/sm/certchain.c +++ b/sm/certchain.c @@ -306,6 +306,7 @@ allowed_ca (ctrl_t ctrl, static int check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) { + static int no_policy_file; gpg_error_t err; char *policies; estream_t fp; @@ -340,17 +341,29 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) return 0; } - fp = es_fopen (opt.policy_file, "r"); + if (no_policy_file) + { + /* Avoid trying to open the policy file if we already know that + * it does not exist. */ + fp = NULL; + gpg_err_set_errno (ENOENT); + } + else + fp = es_fopen (opt.policy_file, "r"); if (!fp) { - if (opt.verbose || errno != ENOENT) + if ((opt.verbose || errno != ENOENT) && !no_policy_file) log_info (_("failed to open '%s': %s\n"), opt.policy_file, strerror (errno)); + + if (errno == ENOENT) + no_policy_file = 1; + xfree (policies); /* With no critical policies this is only a warning */ if (!any_critical) { - if (!opt.quiet) + if (opt.verbose) do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; @@ -360,6 +373,8 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) return gpg_error (GPG_ERR_NO_POLICY_MATCH); } + /* FIXME: Cache the policy file content. */ + for (;;) { int c; @@ -380,7 +395,8 @@ check_cert_policy (ksba_cert_t cert, int listmode, estream_t fplist) /* With no critical policies this is only a warning */ if (!any_critical) { - do_list (0, listmode, fplist, + if (opt.verbose) + do_list (0, listmode, fplist, _("Note: non-critical certificate policy not allowed")); return 0; } @@ -460,7 +476,7 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc) { log_error ("keydb_get_cert() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } xfree (subj); @@ -475,7 +491,7 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } @@ -544,7 +560,7 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc) { log_error ("keydb_get_validity() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } if (*not_after && strcmp (ctrl->current_time, not_after) > 0 ) @@ -558,7 +574,7 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc) { log_error ("keydb_search_fpr() failed: rc=%d\n", rc); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); goto leave; } /* Ready. The NE_FOUND_CERT is availabale via keydb_get_cert. */ @@ -569,7 +585,7 @@ find_up_search_by_keyid (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_release (ne_found_cert); ksba_cert_release (cert); xfree (subj); - return rc? -1:0; + return rc? gpg_error (GPG_ERR_NOT_FOUND) : 0; } @@ -646,10 +662,10 @@ find_up_external (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc) { log_error ("external key lookup failed: %s\n", gpg_strerror (rc)); - rc = -1; + rc = gpg_error (GPG_ERR_NOT_FOUND); } else if (!find_up_store_certs_parm.count) - rc = -1; + rc = gpg_err_code (rc) == GPG_ERR_NOT_FOUND; else { int old; @@ -818,7 +834,8 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh, if (rc && !opt.quiet) log_info (_("dirmngr cache-only key lookup failed: %s\n"), gpg_strerror (rc)); - return (!rc && find_up_store_certs_parm.count)? 0 : -1; + return ((!rc && find_up_store_certs_parm.count) + ? 0 : gpg_error (GPG_ERR_NOT_FOUND)); } @@ -828,15 +845,15 @@ find_up_dirmngr (ctrl_t ctrl, KEYDB_HANDLE kh, FIND_NEXT is true, the function shall return the next possible issuer. The certificate itself is not directly returned but a keydb_get_cert on the keydb context KH will return it. Returns 0 - on success, -1 if not found or an error code. */ -static int + on success, GPG_ERR_NOT_FOUND if not found or another error code. */ +static gpg_error_t find_up (ctrl_t ctrl, KEYDB_HANDLE kh, ksba_cert_t cert, const char *issuer, int find_next) { ksba_name_t authid; ksba_sexp_t authidno; ksba_sexp_t keyid; - int rc = -1; + gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND); if (DBG_X509) log_debug ("looking for parent certificate\n"); @@ -845,90 +862,91 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, const char *s = ksba_name_enum (authid, 0); if (s && *authidno) { - rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); - if (rc) + err = keydb_search_issuer_sn (ctrl, kh, s, authidno); + if (err) keydb_search_reset (kh); - if (!rc && DBG_X509) + if (!err && DBG_X509) log_debug (" found via authid and sn+issuer\n"); /* In case of an error, try to get the certificate from the - dirmngr. That is done by trying to put that certifcate + dirmngr. That is done by trying to put that certificate into the ephemeral DB and let the code below do the actual retrieve. Thus there is no error checking. Skipped in find_next mode as usual. */ - if (rc == -1 && !find_next) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next) find_up_dirmngr (ctrl, kh, authidno, s, 0); /* In case of an error try the ephemeral DB. We can't do that in find_next mode because we can't keep the search state then. */ - if (rc == -1 && !find_next) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next) { int old = keydb_set_ephemeral (kh, 1); if (!old) { - rc = keydb_search_issuer_sn (ctrl, kh, s, authidno); - if (rc) + err = keydb_search_issuer_sn (ctrl, kh, s, authidno); + if (err) keydb_search_reset (kh); - if (!rc && DBG_X509) + if (!err && DBG_X509) log_debug (" found via authid and sn+issuer (ephem)\n"); } keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (err) /* Need to make sure to have this error code. */ + err = gpg_error (GPG_ERR_NOT_FOUND); } - if (rc == -1 && keyid && !find_next) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && keyid && !find_next) { /* Not found by AKI.issuer_sn. Lets try the AKI.ki instead. Loop over all certificates with that issuer as subject and stop for the one with a matching subjectKeyIdentifier. */ /* Fixme: Should we also search in the dirmngr? */ - rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); - if (!rc && DBG_X509) + err = find_up_search_by_keyid (ctrl, kh, issuer, keyid); + if (!err && DBG_X509) log_debug (" found via authid and keyid\n"); - if (rc) + if (err) { int old = keydb_set_ephemeral (kh, 1); if (!old) - rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); - if (!rc && DBG_X509) + err = find_up_search_by_keyid (ctrl, kh, issuer, keyid); + if (!err && DBG_X509) log_debug (" found via authid and keyid (ephem)\n"); keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (err) /* Need to make sure to have this error code. */ + err = gpg_error (GPG_ERR_NOT_FOUND); } /* If we still didn't found it, try to find it via the subject from the dirmngr-cache. */ - if (rc == -1 && !find_next) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next) { if (!find_up_dirmngr (ctrl, kh, NULL, issuer, 1)) { int old = keydb_set_ephemeral (kh, 1); if (keyid) - rc = find_up_search_by_keyid (ctrl, kh, issuer, keyid); + err = find_up_search_by_keyid (ctrl, kh, issuer, keyid); else { keydb_search_reset (kh); - rc = keydb_search_subject (ctrl, kh, issuer); + err = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); } - if (rc) - rc = -1; /* Need to make sure to have this error code. */ + if (err) /* Need to make sure to have this error code. */ + err = gpg_error (GPG_ERR_NOT_FOUND); - if (!rc && DBG_X509) + if (!err && DBG_X509) log_debug (" found via authid and issuer from dirmngr cache\n"); } /* If we still didn't found it, try an external lookup. */ - if (rc == -1 && !find_next && !ctrl->offline) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND + && !find_next && !ctrl->offline) { /* We allow AIA also if CRLs are enabled; both can be used * as a web bug so it does not make sense to not use AIA if @@ -938,12 +956,12 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); - rc = 0; + err = 0; } else if (opt.auto_issuer_key_retrieve) { - rc = find_up_external (ctrl, kh, issuer, keyid); - if (!rc && DBG_X509) + err = find_up_external (ctrl, kh, issuer, keyid); + if (!err && DBG_X509) log_debug (" found via authid and external lookup\n"); } } @@ -952,9 +970,9 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, /* Print a note so that the user does not feel too helpless when an issuer certificate was found and gpgsm prints BAD signature because it is not the correct one. */ - if (rc == -1 && opt.quiet) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && opt.quiet) ; - else if (rc == -1) + else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { log_info ("%sissuer certificate ", find_next?"next ":""); if (keyid) @@ -973,16 +991,16 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, } log_printf ("not found using authorityKeyIdentifier\n"); } - else if (rc) - log_error ("failed to find authorityKeyIdentifier: rc=%d\n", rc); + else if (err) + log_error ("failed to find authorityKeyIdentifier: err=%d\n", err); xfree (keyid); ksba_name_release (authid); xfree (authidno); } - if (rc) /* Not found via authorithyKeyIdentifier, try regular issuer name. */ - rc = keydb_search_subject (ctrl, kh, issuer); - if (rc == -1 && !find_next) + if (err) /* Not found via authorithyKeyIdentifier, try regular issuer name. */ + err = keydb_search_subject (ctrl, kh, issuer); + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next) { int old; @@ -995,33 +1013,33 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, if (!old) { keydb_search_reset (kh); - rc = keydb_search_subject (ctrl, kh, issuer); + err = keydb_search_subject (ctrl, kh, issuer); } keydb_set_ephemeral (kh, old); - if (!rc && DBG_X509) + if (!err && DBG_X509) log_debug (" found via issuer\n"); } /* Still not found. If enabled, try an external lookup. */ - if (rc == -1 && !find_next && !ctrl->offline) + if (gpg_err_code (err) == GPG_ERR_NOT_FOUND && !find_next && !ctrl->offline) { if ((opt.auto_issuer_key_retrieve || !opt.no_crl_check) && !find_up_via_auth_info_access (ctrl, kh, cert)) { if (DBG_X509) log_debug (" found via authorityInfoAccess.caIssuers\n"); - rc = 0; + err = 0; } else if (opt.auto_issuer_key_retrieve) { - rc = find_up_external (ctrl, kh, issuer, NULL); - if (!rc && DBG_X509) + err = find_up_external (ctrl, kh, issuer, NULL); + if (!err && DBG_X509) log_debug (" found via issuer and external lookup\n"); } } - return rc; + return err; } @@ -1030,7 +1048,7 @@ find_up (ctrl_t ctrl, KEYDB_HANDLE kh, int gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) { - int rc = 0; + gpg_error_t err = 0; char *issuer = NULL; char *subject = NULL; KEYDB_HANDLE kh = keydb_new (); @@ -1039,7 +1057,7 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) if (!kh) { log_error (_("failed to allocate keyDB handle\n")); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } @@ -1048,45 +1066,47 @@ gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next) if (!issuer) { log_error ("no issuer found in certificate\n"); - rc = gpg_error (GPG_ERR_BAD_CERT); + err = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (!subject) { log_error ("no subject found in certificate\n"); - rc = gpg_error (GPG_ERR_BAD_CERT); + err = gpg_error (GPG_ERR_BAD_CERT); goto leave; } if (is_root_cert (start, issuer, subject)) { - rc = -1; /* we are at the root */ + err = gpg_error (GPG_ERR_NOT_FOUND); /* we are at the root */ goto leave; } - rc = find_up (ctrl, kh, start, issuer, 0); - if (rc) + err = find_up (ctrl, kh, start, issuer, 0); + if (err) { /* It is quite common not to have a certificate, so better don't print an error here. */ - if (rc != -1 && opt.verbose > 1) - log_error ("failed to find issuer's certificate: rc=%d\n", rc); - rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); + if (gpg_err_code (err) != GPG_ERR_NOT_FOUND && opt.verbose > 1) + log_error ("failed to find issuer's certificate: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + err = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } - rc = keydb_get_cert (kh, r_next); - if (rc) + err = keydb_get_cert (kh, r_next); + if (err) { - log_error ("keydb_get_cert() failed: rc=%d\n", rc); - rc = gpg_error (GPG_ERR_GENERAL); + log_error ("keydb_get_cert() failed: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + err = gpg_error (GPG_ERR_GENERAL); } leave: xfree (issuer); xfree (subject); keydb_release (kh); - return rc; + return err; } @@ -1115,7 +1135,7 @@ is_root_cert (ksba_cert_t cert, const char *issuerdn, const char *subjectdn) { if (gpg_err_code (err) == GPG_ERR_NO_DATA) return 1; /* Yes. Without a authorityKeyIdentifier this needs - to be the Root certifcate (our trust anchor). */ + to be the Root certificate (our trust anchor). */ log_error ("error getting authorityKeyIdentifier: %s\n", gpg_strerror (err)); return 0; /* Well, it is broken anyway. Return No. */ @@ -1328,7 +1348,7 @@ check_validity_period (ksba_isotime_t current_time, } /* This is a variant of check_validity_period used with the chain - model. The dextra contraint here is that notBefore and notAfter + model. The extra contraint here is that notBefore and notAfter must exists and if the additional argument CHECK_TIME is given this time is used to check the validity period of SUBJECT_CERT. */ static gpg_error_t @@ -1396,7 +1416,7 @@ check_validity_period_cm (ksba_isotime_t current_time, || strcmp (check_time, not_after) > 0)) { /* Note that we don't need a case for the root certificate - because its own consitency has already been checked. */ + because its own consistency has already been checked. */ do_list(opt.ignore_expiration?0:1, listmode, listfp, depth == 0 ? _("signature not created during lifetime of certificate") : @@ -1567,7 +1587,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, for (;;) { int is_root; - gpg_error_t istrusted_rc = -1; + gpg_error_t istrusted_rc = gpg_error (GPG_ERR_NOT_TRUSTED); /* Put the certificate on our list. */ { @@ -1707,11 +1727,15 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, else { /* Need to consult the list of root certificates for - qualified signatures. */ - err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL); + qualified signatures. But first we check the + modern way by looking at the root ca flag. */ + if (rootca_flags->qualified) + err = 0; + else + err = gpgsm_is_in_qualified_list (ctrl, subject_cert, NULL); if (!err) is_qualified = 1; - else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND) + else if ( gpg_err_code (err) == GPG_ERR_NOT_FOUND ) is_qualified = 0; else log_error ("checking the list of qualified " @@ -1779,7 +1803,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, if (rc) goto leave; - break; /* Okay: a self-signed certicate is an end-point. */ + break; /* Okay: a self-signed certificate is an end-point. */ } /* End is_root. */ @@ -1796,7 +1820,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, rc = find_up (ctrl, kh, subject_cert, issuer, 0); if (rc) { - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { do_list (0, listmode, listfp, _("issuer certificate not found")); if (!listmode) @@ -1807,7 +1831,8 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, } } else - log_error ("failed to find issuer's certificate: rc=%d\n", rc); + log_error ("failed to find issuer's certificate: %s <%s>\n", + gpg_strerror (rc), gpg_strsource (rc)); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } @@ -1879,7 +1904,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, } is_root = gpgsm_is_root_cert (issuer_cert); - istrusted_rc = -1; + istrusted_rc = gpg_error (GPG_ERR_NOT_TRUSTED); /* Check that a CA is allowed to issue certificates. */ @@ -2103,7 +2128,7 @@ do_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime_arg, do_validate_chain. This function is a wrapper to handle a root certificate with the chain_model flag set. If RETFLAGS is not NULL, flags indicating now the verification was done are stored - there. The only defined vits for RETFLAGS are + there. The only defined bits for RETFLAGS are VALIDATE_FLAG_CHAIN_MODEL and VALIDATE_FLAG_STEED. If you are verifying a signature you should set CHECKTIME to the @@ -2137,9 +2162,15 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, memset (&rootca_flags, 0, sizeof rootca_flags); - rc = do_validate_chain (ctrl, cert, checktime, - r_exptime, listmode, listfp, flags, - &rootca_flags); + if ((flags & VALIDATE_FLAG_BYPASS)) + { + *retflags |= VALIDATE_FLAG_BYPASS; + rc = 0; + } + else + rc = do_validate_chain (ctrl, cert, checktime, + r_exptime, listmode, listfp, flags, + &rootca_flags); if (!rc && (flags & VALIDATE_FLAG_STEED)) { *retflags |= VALIDATE_FLAG_STEED; @@ -2148,7 +2179,11 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, && !(flags & VALIDATE_FLAG_CHAIN_MODEL) && (rootca_flags.valid && rootca_flags.chain_model)) { - do_list (0, listmode, listfp, _("switching to chain model")); + /* The root CA indicated that the chain model is to be used but + * we have not yet used it. Thus do the validation again using + * the chain model. */ + if (opt.verbose) + do_list (0, listmode, listfp, _("switching to chain model")); rc = do_validate_chain (ctrl, cert, checktime, r_exptime, listmode, listfp, (flags |= VALIDATE_FLAG_CHAIN_MODEL), @@ -2158,6 +2193,8 @@ gpgsm_validate_chain (ctrl_t ctrl, ksba_cert_t cert, ksba_isotime_t checktime, if (opt.verbose) do_list (0, listmode, listfp, _("validation model used: %s"), + (*retflags & VALIDATE_FLAG_BYPASS)? + "bypass" : (*retflags & VALIDATE_FLAG_STEED)? "steed" : (*retflags & VALIDATE_FLAG_CHAIN_MODEL)? @@ -2225,14 +2262,15 @@ gpgsm_basic_cert_check (ctrl_t ctrl, ksba_cert_t cert) rc = find_up (ctrl, kh, cert, issuer, 0); if (rc) { - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { log_info ("issuer certificate (#/"); gpgsm_dump_string (issuer); log_printf (") not found\n"); } else - log_error ("failed to find issuer's certificate: rc=%d\n", rc); + log_error ("failed to find issuer's certificate: %s <%s>\n", + gpg_strerror (rc), gpg_strsource (rc)); rc = gpg_error (GPG_ERR_MISSING_ISSUER_CERT); goto leave; } diff --git a/sm/certcheck.c b/sm/certcheck.c index d6b967c..534f47c 100644 --- a/sm/certcheck.c +++ b/sm/certcheck.c @@ -74,14 +74,17 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, size_t nframe; unsigned char *frame; - if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECDSA) + if (pkalgo == GCRY_PK_DSA || pkalgo == GCRY_PK_ECC) { - unsigned int qbits; + unsigned int qbits0, qbits; - if ( pkalgo == GCRY_PK_ECDSA ) - qbits = gcry_pk_get_nbits (pkey); + if ( pkalgo == GCRY_PK_ECC ) + { + qbits0 = gcry_pk_get_nbits (pkey); + qbits = qbits0 == 521? 512 : qbits0; + } else - qbits = get_dsa_qbits (pkey); + qbits0 = qbits = get_dsa_qbits (pkey); if ( (qbits%8) ) { @@ -98,7 +101,7 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, if (qbits < 160) { log_error (_("%s key uses an unsafe (%u bit) hash\n"), - gcry_pk_algo_name (pkalgo), qbits); + gcry_pk_algo_name (pkalgo), qbits0); return gpg_error (GPG_ERR_INTERNAL); } @@ -109,7 +112,7 @@ do_encode_md (gcry_md_hd_t md, int algo, int pkalgo, unsigned int nbits, { log_error (_("a %u bit hash is not valid for a %u bit %s key\n"), (unsigned int)nframe*8, - gcry_pk_get_nbits (pkey), + qbits0, gcry_pk_algo_name (pkalgo)); /* FIXME: we need to check the requirements for ECDSA. */ if (nframe < 20 || pkalgo == GCRY_PK_DSA ) @@ -210,10 +213,8 @@ pk_algo_from_sexp (gcry_sexp_t pkey) algo = GCRY_PK_RSA; else if (n==3 && !memcmp (name, "dsa", 3)) algo = GCRY_PK_DSA; - /* Because this function is called only for verification we can - assume that ECC actually means ECDSA. */ else if (n==3 && !memcmp (name, "ecc", 3)) - algo = GCRY_PK_ECDSA; + algo = GCRY_PK_ECC; else if (n==13 && !memcmp (name, "ambiguous-rsa", 13)) algo = GCRY_PK_RSA; else @@ -357,9 +358,19 @@ gpgsm_check_cert_sig (ksba_cert_t issuer_cert, ksba_cert_t cert) int use_pss = 0; unsigned int saltlen; + /* Note that we map the 4 algos which current Libgcrypt versions are + * not aware of the OID. */ algo = gcry_md_map_name ( (algoid=ksba_cert_get_digest_algo (cert))); if (!algo && algoid && !strcmp (algoid, "1.2.840.113549.1.1.10")) use_pss = 1; + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.1")) + algo = GCRY_MD_SHA224; /* ecdsa-with-sha224 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.2")) + algo = GCRY_MD_SHA256; /* ecdsa-with-sha256 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.3")) + algo = GCRY_MD_SHA384; /* ecdsa-with-sha384 */ + else if (!algo && algoid && !strcmp (algoid, "1.2.840.10045.4.3.4")) + algo = GCRY_MD_SHA512; /* ecdsa-with-sha512 */ else if (!algo) { log_error ("unknown digest algorithm '%s' used certificate\n", diff --git a/sm/certlist.c b/sm/certlist.c index b1ae58c..f404629 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -34,7 +34,16 @@ #include "keydb.h" #include "../common/i18n.h" - +/* Mode values for cert_usage_p. + * Take care: the values have a semantic. */ +#define USE_MODE_SIGN 0 +#define USE_MODE_ENCR 1 +#define USE_MODE_VRFY 2 +#define USE_MODE_DECR 3 +#define USE_MODE_CERT 4 +#define USE_MODE_OCSP 5 + +/* OIDs we use here. */ static const char oid_kp_serverAuth[] = "1.3.6.1.5.5.7.3.1"; static const char oid_kp_clientAuth[] = "1.3.6.1.5.5.7.3.2"; static const char oid_kp_codeSigning[] = "1.3.6.1.5.5.7.3.3"; @@ -42,6 +51,7 @@ static const char oid_kp_emailProtection[]= "1.3.6.1.5.5.7.3.4"; static const char oid_kp_timeStamping[] = "1.3.6.1.5.5.7.3.8"; static const char oid_kp_ocspSigning[] = "1.3.6.1.5.5.7.3.9"; + /* Return 0 if the cert is usable for encryption. A MODE of 0 checks for signing a MODE of 1 checks for encryption, a MODE of 2 checks for verification and a MODE of 3 for decryption (just for @@ -120,7 +130,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) if (gpg_err_code (err) == GPG_ERR_NO_DATA) { err = 0; - if (opt.verbose && mode < 2 && !silent) + if (opt.verbose && mode < USE_MODE_VRFY && !silent) log_info (_("no key usage specified - assuming all usages\n")); use = ~0; } @@ -137,7 +147,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) return err; } - if (mode == 4) + if (mode == USE_MODE_CERT) { if ((use & (KSBA_KEYUSAGE_KEY_CERT_SIGN))) return 0; @@ -147,7 +157,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } - if (mode == 5) + if (mode == USE_MODE_OCSP) { if (use != ~0 && (have_ocsp_signing @@ -161,7 +171,8 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) } encr_bits = (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT); - if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)) + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) + || gpgsm_is_ecc_key (cert)) encr_bits |= KSBA_KEYUSAGE_KEY_AGREEMENT; sign_bits = (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION); @@ -170,11 +181,13 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) return 0; if (!silent) - log_info - (mode==3? _("certificate should not have been used for encryption\n"): - mode==2? _("certificate should not have been used for signing\n"): - mode==1? _("certificate is not usable for encryption\n"): - /**/ _("certificate is not usable for signing\n")); + log_info (mode == USE_MODE_DECR? + _("certificate should not have been used for encryption\n") : + mode == USE_MODE_VRFY? + _("certificate should not have been used for signing\n") : + mode == USE_MODE_ENCR? + _("certificate is not usable for encryption\n") : + _("certificate is not usable for signing\n")); return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } @@ -184,7 +197,7 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) int gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) { - return cert_usage_p (cert, 0, silent); + return cert_usage_p (cert, USE_MODE_SIGN, silent); } @@ -192,31 +205,31 @@ gpgsm_cert_use_sign_p (ksba_cert_t cert, int silent) int gpgsm_cert_use_encrypt_p (ksba_cert_t cert) { - return cert_usage_p (cert, 1, 0); + return cert_usage_p (cert, USE_MODE_ENCR, 0); } int gpgsm_cert_use_verify_p (ksba_cert_t cert) { - return cert_usage_p (cert, 2, 0); + return cert_usage_p (cert, USE_MODE_VRFY, 0); } int gpgsm_cert_use_decrypt_p (ksba_cert_t cert) { - return cert_usage_p (cert, 3, 0); + return cert_usage_p (cert, USE_MODE_DECR, 0); } int gpgsm_cert_use_cert_p (ksba_cert_t cert) { - return cert_usage_p (cert, 4, 0); + return cert_usage_p (cert, USE_MODE_CERT, 0); } int gpgsm_cert_use_ocsp_p (ksba_cert_t cert) { - return cert_usage_p (cert, 5, 0); + return cert_usage_p (cert, USE_MODE_OCSP, 0); } @@ -387,7 +400,7 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else if (!rc) { @@ -441,6 +454,11 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, if (!rc && !is_cert_in_certlist (cert, *listaddr)) { + unsigned int valflags = 0; + + if (!secret && (opt.always_trust || ctrl->always_trust)) + valflags |= VALIDATE_FLAG_BYPASS; + if (!rc && secret) { char *p; @@ -454,9 +472,10 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, xfree (p); } } + if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, "", NULL, - 0, NULL, 0, NULL); + rc = gpgsm_validate_chain (ctrl, cert, GNUPG_ISOTIME_NONE, NULL, + 0, NULL, valflags, NULL); if (!rc) { certlist_t cl = xtrycalloc (1, sizeof *cl); @@ -476,7 +495,8 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, keydb_release (kh); ksba_cert_release (cert); - return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; + return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND + ? gpg_error (GPG_ERR_NO_PUBKEY): rc); } @@ -500,11 +520,12 @@ gpgsm_release_certlist (certlist_t list) int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert, - int allow_ambiguous) + unsigned int flags) { int rc; KEYDB_SEARCH_DESC desc; KEYDB_HANDLE kh = NULL; + int allow_ambiguous = (flags & FIND_CERT_ALLOW_AMBIG); *r_cert = NULL; rc = classify_user_id (name, &desc, 0); @@ -515,6 +536,9 @@ gpgsm_find_cert (ctrl_t ctrl, rc = gpg_error (GPG_ERR_ENOMEM); else { + if ((flags & FIND_CERT_WITH_EPHEM)) + keydb_set_ephemeral (kh, 1); + nextone: rc = keydb_search (ctrl, kh, &desc, 1); if (!rc) @@ -560,7 +584,7 @@ gpgsm_find_cert (ctrl_t ctrl, } next_ambiguous: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else { @@ -614,5 +638,6 @@ gpgsm_find_cert (ctrl_t ctrl, } keydb_release (kh); - return rc == -1? gpg_error (GPG_ERR_NO_PUBKEY): rc; + return (gpg_err_code (rc) == GPG_ERR_NOT_FOUND? + gpg_error (GPG_ERR_NO_PUBKEY): rc); } diff --git a/sm/certreqgen-ui.c b/sm/certreqgen-ui.c index d75b017..fbfaa8a 100644 --- a/sm/certreqgen-ui.c +++ b/sm/certreqgen-ui.c @@ -113,7 +113,9 @@ check_keygrip (ctrl_t ctrl, const char *hexgrip) case GCRY_PK_RSA: return "RSA"; case GCRY_PK_DSA: return "DSA"; case GCRY_PK_ELG: return "ELG"; - case GCRY_PK_EDDSA: return "ECDSA"; + case GCRY_PK_ECC: return "ECC"; + case GCRY_PK_ECDSA: return "ECDSA"; + case GCRY_PK_EDDSA: return "EdDSA"; default: return NULL; } } diff --git a/sm/certreqgen.c b/sm/certreqgen.c index 92d6ffe..63c35a2 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -67,6 +67,7 @@ #include "keydb.h" #include "../common/i18n.h" +#include "../common/membuf.h" enum para_name @@ -74,6 +75,7 @@ enum para_name pKEYTYPE, pKEYLENGTH, pKEYGRIP, + pKEYCURVE, pKEYUSAGE, pNAMEDN, pNAMEEMAIL, @@ -236,6 +238,7 @@ read_parameters (ctrl_t ctrl, estream_t fp, estream_t out_fp) { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Grip", pKEYGRIP }, + { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Name-DN", pNAMEDN }, { "Name-Email", pNAMEEMAIL, 1 }, @@ -433,6 +436,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para, struct para_data_s *r; const char *s, *string; int i; + int algo; unsigned int nbits; char numbuf[20]; unsigned char keyparms[100]; @@ -446,30 +450,30 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para, /* Check that we have all required parameters; */ assert (get_parameter (para, pKEYTYPE, 0)); - /* We can only use RSA for now. There is a problem with pkcs-10 on - how to use ElGamal because it is expected that a PK algorithm can - always be used for signing. Another problem is that on-card - generated encryption keys may not be used for signing. */ - i = get_parameter_algo (para, pKEYTYPE); - if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s) + /* There is a problem with pkcs-10 on how to use ElGamal because it + is expected that a PK algorithm can always be used for + signing. Another problem is that on-card generated encryption + keys may not be used for signing. */ + algo = get_parameter_algo (para, pKEYTYPE); + if (!algo && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s) { /* Hack to allow creation of certificates directly from a smart card. For example: "Key-Type: card:OPENPGP.3". */ if (!strncmp (s, "card:", 5) && s[5]) cardkeyid = xtrystrdup (s+5); } - if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid ) + if (algo < 1 && !cardkeyid) { r = get_parameter (para, pKEYTYPE, 0); if (r) - log_error (_("line %d: invalid algorithm\n"), r?r->lnr:0); + log_error (_("line %d: invalid algorithm\n"), r->lnr); else log_error ("No Key-Type specified\n"); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the keylength. NOTE: If you change this make sure that it - macthes the gpgconflist item in gpgsm.c */ + matches the gpgconflist item in gpgsm.c */ if (!get_parameter (para, pKEYLENGTH, 0)) nbits = 3072; else @@ -626,7 +630,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para, /* Check the optional AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); - if (string) + if (string && strcmp (string, "none")) { for (s=string, i=0; hexdigitp (s); s++, i++) ; @@ -641,7 +645,7 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para, /* Check the optional SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); - if (string) + if (string && strcmp (string, "none")) { for (s=string, i=0; hexdigitp (s); s++, i++) ; @@ -721,10 +725,38 @@ proc_parameters (ctrl_t ctrl, struct para_data_s *para, } else if (!outctrl->dryrun) /* Generate new key. */ { - sprintf (numbuf, "%u", nbits); - snprintf ((char*)keyparms, DIM (keyparms), - "(6:genkey(3:rsa(5:nbits%d:%s)))", - (int)strlen (numbuf), numbuf); + if (algo == GCRY_PK_RSA) + { + sprintf (numbuf, "%u", nbits); + snprintf ((char*)keyparms, DIM (keyparms), + "(6:genkey(3:rsa(5:nbits%d:%s)))", + (int)strlen (numbuf), numbuf); + } + else if ((opt.compat_flags & COMPAT_ALLOW_ECC_ENCR) + && (algo == GCRY_PK_ECC || algo == GCRY_PK_EDDSA)) + { + const char *curve = get_parameter_value (para, pKEYCURVE, 0); + const char *flags; + + if (algo == GCRY_PK_EDDSA) + flags = "(flags eddsa)"; + else if (!strcmp (curve, "Curve25519")) + flags = "(flags djb-tweak)"; + else + flags = ""; + + snprintf ((char*)keyparms, DIM (keyparms), + "(genkey(ecc(curve %zu:%s)%s))", + strlen (curve), curve, flags); + } + else + { + r = get_parameter (para, pKEYTYPE, 0); + log_error (_("line %d: invalid algorithm\n"), r->lnr); + xfree (sigkey); + xfree (cardkeyid); + return gpg_error (GPG_ERR_INV_PARAMETER); + } rc = gpgsm_agent_genkey (ctrl, keyparms, &public); if (rc) { @@ -805,16 +837,40 @@ create_request (ctrl_t ctrl, ksba_isotime_t atime; int certmode = 0; int mdalgo; + membuf_t tbsbuffer; + membuf_t *tbsmb = NULL; + size_t publiclen; + size_t sigkeylen; + int publicpkalgo; /* The gcrypt public key algo of the public key. */ + int sigkeypkalgo; /* The gcrypt public key algo of the signing key. */ err = ksba_certreq_new (&cr); if (err) return err; - string = get_parameter_value (para, pHASHALGO, 0); - if (string) - mdalgo = gcry_md_map_name (string); + publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); + sigkeylen = sigkey? gcry_sexp_canon_len (sigkey, 0, NULL, NULL) : 0; + + publicpkalgo = get_pk_algo_from_canon_sexp (public, publiclen); + sigkeypkalgo = sigkey? get_pk_algo_from_canon_sexp (public, publiclen) : 0; + + if (publicpkalgo == GCRY_PK_EDDSA) + { + mdalgo = GCRY_MD_SHA512; + md = NULL; /* We sign the data and not a hash. */ + init_membuf (&tbsbuffer, 2048); + tbsmb = &tbsbuffer; + ksba_certreq_set_hash_function + (cr, (void (*)(void *, const void*,size_t))put_membuf, tbsmb); + } else - mdalgo = GCRY_MD_SHA256; + { + string = get_parameter_value (para, pHASHALGO, 0); + if (string) + mdalgo = gcry_md_map_name (string); + else + mdalgo = GCRY_MD_SHA256; + } rc = gcry_md_open (&md, mdalgo, 0); if (rc) { @@ -912,12 +968,10 @@ create_request (ctrl_t ctrl, } } - err = ksba_certreq_set_public_key (cr, public); if (err) { - log_error ("error setting the public key: %s\n", - gpg_strerror (err)); + log_error ("error setting the public key: %s\n", gpg_strerror (err)); rc = err; goto leave; } @@ -1113,14 +1167,17 @@ create_request (ctrl_t ctrl, given we set it to the public key to create a self-signed certificate. */ if (!sigkey) - sigkey = public; + { + sigkey = public; + sigkeylen = publiclen; + sigkeypkalgo = publicpkalgo; + } + /* Set the the digestinfo aka siginfo. */ { unsigned char *siginfo; - err = transform_sigval (sigkey, - gcry_sexp_canon_len (sigkey, 0, NULL, NULL), - mdalgo, &siginfo, NULL); + err = transform_sigval (sigkey, sigkeylen, mdalgo, &siginfo, NULL); if (!err) { err = ksba_certreq_set_siginfo (cr, siginfo); @@ -1135,9 +1192,12 @@ create_request (ctrl_t ctrl, } } + /* Insert the AuthorityKeyId. */ string = get_parameter_value (para, pAUTHKEYID, 0); - if (string) + if (string && !strcmp (string, "none")) + ; /* Do not issue an AKI. */ + else if (string) { char *hexbuf; @@ -1163,20 +1223,69 @@ create_request (ctrl_t ctrl, hexbuf[2] = 0x80; /* Context tag for an implicit Octet string. */ hexbuf[3] = len; err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier, - 0, - hexbuf, 4+len); + 0, hexbuf, 4+len); xfree (hexbuf); if (err) { - log_error ("error setting the authority-key-id: %s\n", + log_error ("error setting the AKI: %s\n", gpg_strerror (err)); + goto leave; + } + } + else if (publicpkalgo == GCRY_PK_EDDSA || publicpkalgo == GCRY_PK_ECC) + { + /* For EdDSA and ECC we add the public key as default identifier. */ + const unsigned char *q; + size_t qlen, derlen; + unsigned char *der; + + err = get_ecc_q_from_canon_sexp (public, publiclen, &q, &qlen); + if (err) + { + log_error ("error getting Q from public key: %s\n", gpg_strerror (err)); goto leave; } + if (publicpkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40) + { + /* Skip our optional native encoding octet. */ + q++; + qlen--; + } + /* FIXME: For plain ECC we should better use a compressed + * point. That requires an updated Libgcrypt. Without that + * using nistp521 won't work due to the length check below. */ + if (qlen > 125) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + derlen = 4 + qlen; + der = xtrymalloc (derlen); + if (!der) + { + err = gpg_error_from_syserror (); + goto leave; + } + der[0] = 0x30; /* Sequence */ + der[1] = qlen + 2; + der[2] = 0x80; /* Context tag for an implict Octet String. */ + der[3] = qlen; + memcpy (der+4, q, qlen); + err = ksba_certreq_add_extension (cr, oidstr_authorityKeyIdentifier, + 0, der, derlen); + xfree (der); + if (err) + { + log_error ("error setting the AKI: %s\n", gpg_strerror (err)); + goto leave; + } } /* Insert the SubjectKeyId. */ string = get_parameter_value (para, pSUBJKEYID, 0); - if (string) + if (string && !strcmp (string, "none")) + ; /* Do not issue an SKI. */ + else if (string) { char *hexbuf; @@ -1204,10 +1313,53 @@ create_request (ctrl_t ctrl, xfree (hexbuf); if (err) { - log_error ("error setting the subject-key-id: %s\n", + log_error ("error setting SKI: %s\n", gpg_strerror (err)); + goto leave; + } + } + else if (sigkeypkalgo == GCRY_PK_EDDSA || sigkeypkalgo == GCRY_PK_ECC) + { + /* For EdDSA and ECC we add the public key as default identifier. */ + const unsigned char *q; + size_t qlen, derlen; + unsigned char *der; + + err = get_ecc_q_from_canon_sexp (sigkey, sigkeylen, &q, &qlen); + if (err) + { + log_error ("error getting Q from signature key: %s\n", gpg_strerror (err)); goto leave; } + if (sigkeypkalgo == GCRY_PK_EDDSA && qlen>32 && (qlen&1) && *q==0x40) + { + /* Skip our optional native encoding octet. */ + q++; + qlen--; + } + if (qlen > 127) + { + err = gpg_error (GPG_ERR_TOO_LARGE); + goto leave; + } + derlen = 2 + qlen; + der = xtrymalloc (derlen); + if (!der) + { + err = gpg_error_from_syserror (); + goto leave; + } + der[0] = 0x04; /* Octet String */ + der[1] = qlen; + memcpy (der+2, q, qlen); + err = ksba_certreq_add_extension (cr, oidstr_subjectKeyIdentifier, 0, + der, derlen); + xfree (der); + if (err) + { + log_error ("error setting the SKI: %s\n", gpg_strerror (err)); + goto leave; + } } /* Insert additional extensions. */ @@ -1269,7 +1421,11 @@ create_request (ctrl_t ctrl, } } else - sigkey = public; + { + sigkey = public; + sigkeylen = publiclen; + sigkeypkalgo = publicpkalgo; + } do { @@ -1283,20 +1439,12 @@ create_request (ctrl_t ctrl, if (stopreason == KSBA_SR_NEED_SIG) { gcry_sexp_t s_pkey; - size_t n; unsigned char grip[20]; char hexgrip[41]; unsigned char *sigval, *newsigval; size_t siglen; - n = gcry_sexp_canon_len (sigkey, 0, NULL, NULL); - if (!n) - { - log_error ("libksba did not return a proper S-Exp\n"); - rc = gpg_error (GPG_ERR_BUG); - goto leave; - } - rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, n); + rc = gcry_sexp_sscan (&s_pkey, NULL, (const char*)sigkey, sigkeylen); if (rc) { log_error ("gcry_sexp_scan failed: %s\n", gpg_strerror (rc)); @@ -1312,8 +1460,9 @@ create_request (ctrl_t ctrl, gcry_sexp_release (s_pkey); bin2hex (grip, 20, hexgrip); - log_info ("about to sign the %s for key: &%s\n", - certmode? "certificate":"CSR", hexgrip); + if (!opt.quiet) + log_info ("about to sign the %s for key: &%s\n", + certmode? "certificate":"CSR", hexgrip); if (carddirect && !certmode) rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, @@ -1345,8 +1494,7 @@ create_request (ctrl_t ctrl, goto leave; } - err = transform_sigval (sigval, siglen, mdalgo, - &newsigval, NULL); + err = transform_sigval (sigval, siglen, mdalgo, &newsigval, NULL); xfree (sigval); if (!err) { diff --git a/sm/decrypt.c b/sm/decrypt.c index 2aa716f..742653a 100644 --- a/sm/decrypt.c +++ b/sm/decrypt.c @@ -1,7 +1,7 @@ /* decrypt.c - Decrypt a message * Copyright (C) 2001, 2003, 2010 Free Software Foundation, Inc. * Copyright (C) 2001-2019 Werner Koch - * Copyright (C) 2015-2021 g10 Code GmbH + * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -82,6 +82,335 @@ string_from_gcry_buffer (gcry_buffer_t *buffer) } +/* Helper to construct and hash the + * ECC-CMS-SharedInfo ::= SEQUENCE { + * keyInfo AlgorithmIdentifier, + * entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, + * suppPubInfo [2] EXPLICIT OCTET STRING } + * as described in RFC-5753, 7.2. */ +gpg_error_t +hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, const char *wrap_algo_str, + unsigned int keylen, + const void *ukm, unsigned int ukmlen) +{ + gpg_error_t err; + void *p; + unsigned char *oid; + size_t n, oidlen, toidlen, tkeyinfo, tukmlen, tsupppubinfo; + unsigned char keylenbuf[6]; + membuf_t mb = MEMBUF_ZERO; + + err = ksba_oid_from_str (wrap_algo_str, &oid, &oidlen); + if (err) + return err; + toidlen = get_tlv_length (CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); + tkeyinfo = get_tlv_length (CLASS_UNIVERSAL, TAG_SEQUENCE, 1, toidlen); + + tukmlen = ukm? get_tlv_length (CLASS_CONTEXT, 0, 1, ukmlen) : 0; + + keylen *= 8; + keylenbuf[0] = TAG_OCTET_STRING; + keylenbuf[1] = 4; + keylenbuf[2] = (keylen >> 24); + keylenbuf[3] = (keylen >> 16); + keylenbuf[4] = (keylen >> 8); + keylenbuf[5] = keylen; + + tsupppubinfo = get_tlv_length (CLASS_CONTEXT, 2, 1, sizeof keylenbuf); + + put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, + tkeyinfo + tukmlen + tsupppubinfo); + put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_SEQUENCE, 1, + toidlen); + put_tlv_to_membuf (&mb, CLASS_UNIVERSAL, TAG_OBJECT_ID, 0, oidlen); + put_membuf (&mb, oid, oidlen); + ksba_free (oid); + + if (ukm) + { + put_tlv_to_membuf (&mb, CLASS_CONTEXT, 0, 1, ukmlen); + put_membuf (&mb, ukm, ukmlen); + } + + put_tlv_to_membuf (&mb, CLASS_CONTEXT, 2, 1, sizeof keylenbuf); + put_membuf (&mb, keylenbuf, sizeof keylenbuf); + + p = get_membuf (&mb, &n); + if (!p) + return gpg_error_from_syserror (); + + gcry_md_write (hash_hd, p, n); + xfree (p); + return 0; +} + + + +/* Derive a KEK (key wrapping key) using (SECRET,SECRETLEN), an + * optional (UKM,ULMLEN), the wrap algorithm WRAP_ALGO_STR in decimal + * dotted form, and the hash algorithm HASH_ALGO. On success a key of + * length KEYLEN is stored at KEY. */ +gpg_error_t +ecdh_derive_kek (unsigned char *key, unsigned int keylen, + int hash_algo, const char *wrap_algo_str, + const void *secret, unsigned int secretlen, + const void *ukm, unsigned int ukmlen) +{ + gpg_error_t err = 0; + unsigned int hashlen; + gcry_md_hd_t hash_hd; + unsigned char counter; + unsigned int n, ncopy; + + hashlen = gcry_md_get_algo_dlen (hash_algo); + if (!hashlen) + return gpg_error (GPG_ERR_INV_ARG); + + err = gcry_md_open (&hash_hd, hash_algo, 0); + if (err) + return err; + + /* According to SEC1 3.6.1 we should check that + * SECRETLEN + UKMLEN + 4 < maxhashlen + * However, we have no practical limit on the hash length and thus + * there is no point in checking this. The second check that + * KEYLEN < hashlen*(2^32-1) + * is obviously also not needed. + */ + for (n=0, counter=1; n < keylen; counter++) + { + if (counter > 1) + gcry_md_reset (hash_hd); + gcry_md_write (hash_hd, secret, secretlen); + gcry_md_write (hash_hd, "\x00\x00\x00", 3); /* MSBs of counter */ + gcry_md_write (hash_hd, &counter, 1); + err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, + ukm, ukmlen); + if (err) + break; + gcry_md_final (hash_hd); + if (n + hashlen > keylen) + ncopy = keylen - n; + else + ncopy = hashlen; + memcpy (key+n, gcry_md_read (hash_hd, 0), ncopy); + n += ncopy; + } + + gcry_md_close (hash_hd); + return err; +} + + +/* This function will modify SECRET. NBITS is the size of the curve + * which which we took from the certificate. */ +static gpg_error_t +ecdh_decrypt (unsigned char *secret, size_t secretlen, + unsigned int nbits, gcry_sexp_t enc_val, + unsigned char **r_result, unsigned int *r_resultlen) +{ + gpg_error_t err; + gcry_buffer_t ioarray[4] = { {0}, {0}, {0}, {0} }; + char *encr_algo_str = NULL; + char *wrap_algo_str = NULL; + int hash_algo, cipher_algo; + const unsigned char *ukm; /* Alias for ioarray[2]. */ + unsigned int ukmlen; + const unsigned char *data; /* Alias for ioarray[3]. */ + unsigned int datalen; + unsigned int keylen; + unsigned char key[32]; + gcry_cipher_hd_t cipher_hd = NULL; + unsigned char *result = NULL; + unsigned int resultlen; + + *r_resultlen = 0; + *r_result = NULL; + + /* Extract X from SECRET; this is the actual secret. Unless a + * smartcard diretcly returns X, it must be in the format of: + * + * 04 || X || Y + * 40 || X + * 41 || X + */ + if (secretlen < 2) + return gpg_error (GPG_ERR_BAD_DATA); + if (secretlen == (nbits+7)/8) + ; /* Matches curve length - this is already the X coordinate. */ + else if (*secret == 0x04) + { + secretlen--; + memmove (secret, secret+1, secretlen); + if ((secretlen & 1)) + return gpg_error (GPG_ERR_BAD_DATA); + secretlen /= 2; + } + else if (*secret == 0x40 || *secret == 0x41) + { + secretlen--; + memmove (secret, secret+1, secretlen); + } + else + return gpg_error (GPG_ERR_BAD_DATA); + if (!secretlen) + return gpg_error (GPG_ERR_BAD_DATA); + + if (DBG_CRYPTO) + log_printhex (secret, secretlen, "ECDH X ..:"); + + /* We have now the shared secret bytes in (SECRET,SECRETLEN). Now + * we will compute the KEK using a value dervied from the secret + * bytes. */ + err = gcry_sexp_extract_param (enc_val, "enc-val", + "&'encr-algo''wrap-algo''ukm'?s", + ioarray+0, ioarray+1, + ioarray+2, ioarray+3, NULL); + if (err) + { + log_error ("extracting ECDH parameter failed: %s\n", gpg_strerror (err)); + goto leave; + } + encr_algo_str = string_from_gcry_buffer (ioarray); + if (!encr_algo_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + wrap_algo_str = string_from_gcry_buffer (ioarray+1); + if (!wrap_algo_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + ukm = ioarray[2].data; + ukmlen = ioarray[2].len; + data = ioarray[3].data; + datalen = ioarray[3].len; + + /* Check parameters. */ + if (DBG_CRYPTO) + { + log_debug ("encr_algo: %s\n", encr_algo_str); + log_debug ("wrap_algo: %s\n", wrap_algo_str); + log_printhex (ukm, ukmlen, "ukm .....:"); + log_printhex (data, datalen, "data ....:"); + } + + if (!strcmp (encr_algo_str, "1.3.132.1.11.1")) + { + /* dhSinglePass-stdDH-sha256kdf-scheme */ + hash_algo = GCRY_MD_SHA256; + } + else if (!strcmp (encr_algo_str, "1.3.132.1.11.2")) + { + /* dhSinglePass-stdDH-sha384kdf-scheme */ + hash_algo = GCRY_MD_SHA384; + } + else if (!strcmp (encr_algo_str, "1.3.132.1.11.3")) + { + /* dhSinglePass-stdDH-sha512kdf-scheme */ + hash_algo = GCRY_MD_SHA512; + } + else if (!strcmp (encr_algo_str, "1.3.133.16.840.63.0.2")) + { + /* dhSinglePass-stdDH-sha1kdf-scheme */ + hash_algo = GCRY_MD_SHA1; + } + else + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + + if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.5")) + { + cipher_algo = GCRY_CIPHER_AES128; + keylen = 16; + } + else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.25")) + { + cipher_algo = GCRY_CIPHER_AES192; + keylen = 24; + } + else if (!strcmp (wrap_algo_str, "2.16.840.1.101.3.4.1.45")) + { + cipher_algo = GCRY_CIPHER_AES256; + keylen = 32; + } + else + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } + + err = ecdh_derive_kek (key, keylen, hash_algo, wrap_algo_str, + secret, secretlen, ukm, ukmlen); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (key, keylen, "KEK .....:"); + + /* Unwrap the key. */ + if ((datalen % 8) || datalen < 16) + { + log_error ("can't use a shared secret of %u bytes for ecdh\n", datalen); + err = gpg_error (GPG_ERR_BAD_DATA); + goto leave; + } + + resultlen = datalen - 8; + result = xtrymalloc_secure (resultlen); + if (!result) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + { + log_error ("ecdh failed to initialize AESWRAP: %s\n", gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_setkey (cipher_hd, key, keylen); + wipememory (key, sizeof key); + if (err) + { + log_error ("ecdh failed in gcry_cipher_setkey: %s\n", gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_decrypt (cipher_hd, result, resultlen, data, datalen); + if (err) + { + log_error ("ecdh failed in gcry_cipher_decrypt: %s\n",gpg_strerror (err)); + goto leave; + } + + *r_resultlen = resultlen; + *r_result = result; + result = NULL; + + leave: + if (result) + { + wipememory (result, resultlen); + xfree (result); + } + gcry_cipher_close (cipher_hd); + xfree (encr_algo_str); + xfree (wrap_algo_str); + xfree (ioarray[0].data); + xfree (ioarray[1].data); + xfree (ioarray[2].data); + xfree (ioarray[3].data); + return err; +} + + /* Helper for pwri_decrypt to parse the derive info. * Example data for (DER,DERLEN): * SEQUENCE { @@ -100,7 +429,7 @@ static gpg_error_t pwri_parse_pbkdf2 (const unsigned char *der, size_t derlen, unsigned char const **r_salt, unsigned int *r_saltlen, unsigned long *r_iterations, - enum gcry_md_algos *r_digest) + int *r_digest) { gpg_error_t err; size_t objlen, hdrlen; @@ -212,7 +541,7 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, unsigned int ekeylen; unsigned char kek[32]; unsigned int keklen; - enum gcry_cipher_algos encr_algo; + int encr_algo; enum gcry_cipher_modes encr_mode; gcry_cipher_hd_t encr_hd = NULL; unsigned char *result = NULL; @@ -221,7 +550,7 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, const unsigned char *salt; /* Points int dparm. */ unsigned int saltlen; unsigned long iterations; - enum gcry_md_algos digest_algo; + int digest_algo; char *passphrase = NULL; @@ -447,13 +776,14 @@ pwri_decrypt (ctrl_t ctrl, gcry_sexp_t enc_val, /* Decrypt the session key and fill in the parm structure. The algo and the IV is expected to be already in PARM. */ static int -prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, +prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, + int pk_algo, unsigned int nbits, const char *desc, ksba_const_sexp_t enc_val, struct decrypt_filter_parm_s *parm) { char *seskey = NULL; size_t n, seskeylen; - int pwri = !hexkeygrip; + int pwri = !hexkeygrip && !pk_algo; int rc; if (DBG_CRYPTO) @@ -468,6 +798,9 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, log_error ("error decrypting session key: %s\n", gpg_strerror (rc)); goto leave; } + + if (DBG_CRYPTO) + log_printhex (seskey, seskeylen, "DEK frame:"); } n=0; @@ -490,6 +823,27 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, seskey = decrypted; seskeylen = decryptedlen; } + else if (pk_algo == GCRY_PK_ECC) + { + gcry_sexp_t s_enc_val; + unsigned char *decrypted; + unsigned int decryptedlen; + + rc = gcry_sexp_sscan (&s_enc_val, NULL, enc_val, + gcry_sexp_canon_len (enc_val, 0, NULL, NULL)); + if (rc) + goto leave; + + rc = ecdh_decrypt (seskey, seskeylen, nbits, s_enc_val, + &decrypted, &decryptedlen); + gcry_sexp_release (s_enc_val); + if (rc) + goto leave; + xfree (seskey); + seskey = decrypted; + seskeylen = decryptedlen; + + } else if (seskeylen == 32 || seskeylen == 24 || seskeylen == 16) { /* Smells like an AES-128, 3-DES, or AES-256 key. This might @@ -530,7 +884,10 @@ prepare_decryption (ctrl_t ctrl, const char *hexkeygrip, const char *desc, } if (DBG_CRYPTO) - log_printhex (seskey+n, seskeylen-n, "session key:"); + { + log_printhex (seskey+n, seskeylen-n, "CEK .......:"); + log_printhex (parm->iv, parm->ivlen, "IV ........:"); + } if (opt.verbose) log_info (_("%s.%s encrypted data\n"), @@ -717,6 +1074,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) int recp; estream_t in_fp = NULL; struct decrypt_filter_parm_s dfparm; + char *curve = NULL; memset (&dfparm, 0, sizeof dfparm); @@ -759,6 +1117,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + if (ctrl->input_size_hint) + gnupg_ksba_set_total (b64writer, ctrl->input_size_hint); + rc = ksba_cms_new (&cms); if (rc) goto leave; @@ -839,7 +1201,10 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) dfparm.mode = mode; dfparm.blklen = gcry_cipher_get_algo_blklen (algo); if (dfparm.blklen > sizeof (dfparm.helpblock)) - return gpg_error (GPG_ERR_BUG); + { + rc = gpg_error (GPG_ERR_BUG); + goto leave; + } rc = ksba_cms_get_content_enc_iv (cms, dfparm.iv, @@ -951,16 +1316,19 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) hexkeygrip = gpgsm_get_keygrip_hexstring (cert); desc = gpgsm_format_keydesc (cert); + pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); - pk_algo = gpgsm_get_key_algo_info (cert, &nbits); + xfree (curve); + pk_algo = gpgsm_get_key_algo_info (cert, &nbits, &curve); if (!opt.quiet) log_info (_("encrypted to %s key %s\n"), pkalgostr, pkfpr); /* Check compliance. */ if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_DECRYPTION, - pk_algo, 0, NULL, nbits, NULL)) + pk_algo, PK_ALGO_FLAG_ECC18, + NULL, nbits, curve)) { char kidstr[10+1]; @@ -978,7 +1346,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) dfparm.is_de_vs = (dfparm.is_de_vs && gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, - NULL, nbits, NULL)); + NULL, nbits, curve)); oops: if (rc) @@ -1006,7 +1374,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) if (maybe_pwri && opt.verbose) log_info ("recp %d - KEKRI or PWRI\n", recp); - rc = prepare_decryption (ctrl, hexkeygrip, + rc = prepare_decryption (ctrl, hexkeygrip, pk_algo, nbits, desc, enc_val, &dfparm); xfree (enc_val); if (rc) @@ -1076,7 +1444,8 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) if (!any_key) { - rc = gpg_error (GPG_ERR_NO_SECKEY); + if (!rc) + rc = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } } @@ -1155,6 +1524,7 @@ gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp) log_error ("message decryption failed: %s <%s>\n", gpg_strerror (rc), gpg_strsource (rc)); } + xfree (curve); ksba_cms_release (cms); gnupg_ksba_destroy_reader (b64reader); gnupg_ksba_destroy_writer (b64writer); diff --git a/sm/delete.c b/sm/delete.c index 56d5b1f..b68a084 100644 --- a/sm/delete.c +++ b/sm/delete.c @@ -83,7 +83,7 @@ delete_one (ctrl_t ctrl, const char *username) next_ambigious: rc = keydb_search (ctrl, kh, &desc, 1); - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; else if (!rc) { @@ -107,7 +107,7 @@ delete_one (ctrl_t ctrl, const char *username) } if (rc) { - if (rc == -1) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = gpg_error (GPG_ERR_NO_PUBKEY); log_error (_("certificate '%s' not found: %s\n"), username, gpg_strerror (rc)); diff --git a/sm/encrypt.c b/sm/encrypt.c index 587b568..7c8a964 100644 --- a/sm/encrypt.c +++ b/sm/encrypt.c @@ -1,6 +1,8 @@ /* encrypt.c - Encrypt a message * Copyright (C) 2001, 2003, 2004, 2007, 2008, * 2010 Free Software Foundation, Inc. + * Copyright (C) 2001-2019 Werner Koch + * Copyright (C) 2015-2020 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -144,7 +147,7 @@ init_dek (DEK dek) return 0; } - +/* Encrypt an RSA session key. */ static int encode_session_key (DEK dek, gcry_sexp_t * r_data) { @@ -165,10 +168,282 @@ encode_session_key (DEK dek, gcry_sexp_t * r_data) } +/* Encrypt DEK using ECDH. S_PKEY is the public key. On success the + * result is stored at R_ENCVAL. Example of a public key: + * + * (public-key (ecc (curve "1.3.132.0.34") (q #04B0[...]B8#))) + * + */ +static gpg_error_t +ecdh_encrypt (DEK dek, gcry_sexp_t s_pkey, gcry_sexp_t *r_encval) +{ + gpg_error_t err; + gcry_sexp_t l1; + char *curvebuf = NULL; + const char *curve; + unsigned int curvebits; + const char *encr_algo_str; + const char *wrap_algo_str; + int hash_algo, cipher_algo; + unsigned int keylen, hashlen; + unsigned char key[32]; + gcry_sexp_t s_data = NULL; + gcry_sexp_t s_encr = NULL; + gcry_buffer_t ioarray[2] = { {0}, {0} }; + unsigned char *secret; /* Alias for ioarray[0]. */ + unsigned int secretlen; + unsigned char *pubkey; /* Alias for ioarray[1]. */ + unsigned int pubkeylen; + gcry_cipher_hd_t cipher_hd = NULL; + unsigned char *result = NULL; + unsigned int resultlen; + + *r_encval = NULL; + + /* Figure out the encryption and wrap algo OIDs. */ + /* Get the curve name if any, */ + l1 = gcry_sexp_find_token (s_pkey, "curve", 0); + if (l1) + { + curvebuf = gcry_sexp_nth_string (l1, 1); + gcry_sexp_release (l1); + } + if (!curvebuf) + { + err = gpg_error (GPG_ERR_INV_CURVE); + log_error ("%s: invalid public key: no curve\n", __func__); + goto leave; + } + + /* We need to use our OpenPGP mapping to turn a curve name into its + * canonical numerical OID. We also use this to get the size of the + * curve which we need to figure out a suitable hash algo. We + * should have a Libgcrypt function to do this; see bug report #4926. */ + curve = openpgp_curve_to_oid (curvebuf, &curvebits, NULL); + if (!curve) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s: invalid public key: %s\n", __func__, gpg_strerror (err)); + goto leave; + } + xfree (curvebuf); + curvebuf = NULL; + + /* Our mapping matches the recommended algorithms from RFC-5753 but + * not supporing the short curves which would require 3DES. */ + if (curvebits < 255) + { + err = gpg_error (GPG_ERR_UNKNOWN_CURVE); + log_error ("%s: curve '%s' is not supported\n", __func__, curve); + goto leave; + } + else if (curvebits <= 256) + { + /* dhSinglePass-stdDH-sha256kdf-scheme */ + encr_algo_str = "1.3.132.1.11.1"; + wrap_algo_str = "2.16.840.1.101.3.4.1.5"; + hash_algo = GCRY_MD_SHA256; + hashlen = 32; + cipher_algo = GCRY_CIPHER_AES128; + keylen = 16; + } + else if (curvebits <= 384) + { + /* dhSinglePass-stdDH-sha384kdf-scheme */ + encr_algo_str = "1.3.132.1.11.2"; + wrap_algo_str = "2.16.840.1.101.3.4.1.25"; + hash_algo = GCRY_MD_SHA384; + hashlen = 48; + cipher_algo = GCRY_CIPHER_AES256; + keylen = 24; + } + else + { + /* dhSinglePass-stdDH-sha512kdf-scheme*/ + encr_algo_str = "1.3.132.1.11.3"; + wrap_algo_str = "2.16.840.1.101.3.4.1.45"; + hash_algo = GCRY_MD_SHA512; + hashlen = 64; + cipher_algo = GCRY_CIPHER_AES256; + keylen = 32; + } + + + /* Create a secret and an ephemeral key. */ + { + char *k; + k = gcry_random_bytes_secure ((curvebits+7)/8, GCRY_STRONG_RANDOM); + if (DBG_CRYPTO) + log_printhex (k, (curvebits+7)/8, "ephm. k .:"); + err = gcry_sexp_build (&s_data, NULL, "%b", (int)(curvebits+7)/8, k); + xfree (k); + } + if (err) + { + log_error ("%s: error building ephemeral secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_pk_encrypt (&s_encr, s_data, s_pkey); + if (err) + { + log_error ("%s: error encrypting ephemeral secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + err = gcry_sexp_extract_param (s_encr, NULL, "&se", + &ioarray+0, ioarray+1, NULL); + if (err) + { + log_error ("%s: error extracting ephemeral key and secret: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + secret = ioarray[0].data; + secretlen = ioarray[0].len; + pubkey = ioarray[1].data; + pubkeylen = ioarray[1].len; + + if (DBG_CRYPTO) + { + log_printhex (pubkey, pubkeylen, "pubkey ..:"); + log_printhex (secret, secretlen, "secret ..:"); + } + + /* Extract X coordinate from SECRET. */ + if (secretlen < 5) /* 5 because N could be reduced to (n-1)/2. */ + err = gpg_error (GPG_ERR_BAD_DATA); + else if (*secret == 0x04) + { + secretlen--; + memmove (secret, secret+1, secretlen); + if ((secretlen & 1)) + { + err = gpg_error (GPG_ERR_BAD_DATA); + goto leave; + } + secretlen /= 2; + } + else if (*secret == 0x40 || *secret == 0x41) + { + secretlen--; + memmove (secret, secret+1, secretlen); + } + else + err = gpg_error (GPG_ERR_BAD_DATA); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (secret, secretlen, "ECDH X ..:"); + + /* Derive a KEK (key wrapping key) using MESSAGE and SECRET_X. + * According to SEC1 3.6.1 we should check that + * SECRETLEN + UKMLEN + 4 < maxhashlen + * However, we have no practical limit on the hash length and thus + * there is no point in checking this. The second check that + * KEYLEN < hashlen*(2^32-1) + * is obviously also not needed. Because with our allowed + * parameters KEYLEN is always less or equal to HASHLEN so that we + * do not need to iterate at all. + */ + log_assert (gcry_md_get_algo_dlen (hash_algo) == hashlen); + { + gcry_md_hd_t hash_hd; + err = gcry_md_open (&hash_hd, hash_algo, 0); + if (err) + goto leave; + gcry_md_write(hash_hd, secret, secretlen); + gcry_md_write(hash_hd, "\x00\x00\x00\x01", 4); /* counter */ + err = hash_ecc_cms_shared_info (hash_hd, wrap_algo_str, keylen, NULL, 0); + gcry_md_final (hash_hd); + log_assert (keylen <= sizeof key && keylen <= hashlen); + memcpy (key, gcry_md_read (hash_hd, 0), keylen); + gcry_md_close (hash_hd); + if (err) + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (key, keylen, "KEK .....:"); + + /* Wrap the key. */ + if ((dek->keylen % 8) || dek->keylen < 16) + { + log_error ("%s: can't use a session key of %u bytes\n", + __func__, dek->keylen); + err = gpg_error (GPG_ERR_BAD_DATA); + goto leave; + } + + resultlen = dek->keylen + 8; + result = xtrymalloc_secure (resultlen); + if (!result) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = gcry_cipher_open (&cipher_hd, cipher_algo, GCRY_CIPHER_MODE_AESWRAP, 0); + if (err) + { + log_error ("%s: failed to initialize AESWRAP: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_setkey (cipher_hd, key, keylen); + wipememory (key, sizeof key); + if (err) + { + log_error ("%s: failed in gcry_cipher_setkey: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + err = gcry_cipher_encrypt (cipher_hd, result, resultlen, + dek->key, dek->keylen); + if (err) + { + log_error ("%s: failed in gcry_cipher_encrypt: %s\n", + __func__, gpg_strerror (err)); + goto leave; + } + + if (DBG_CRYPTO) + log_printhex (result, resultlen, "w(CEK) ..:"); + + err = gcry_sexp_build (r_encval, NULL, + "(enc-val(ecdh(e%b)(s%b)(encr-algo%s)(wrap-algo%s)))", + (int)pubkeylen, pubkey, + (int)resultlen, result, + encr_algo_str, + wrap_algo_str, + NULL); + if (err) + log_error ("%s: failed building final S-exp: %s\n", + __func__, gpg_strerror (err)); + + leave: + gcry_cipher_close (cipher_hd); + wipememory (key, sizeof key); + xfree (result); + xfree (ioarray[0].data); + xfree (ioarray[1].data); + gcry_sexp_release (s_data); + gcry_sexp_release (s_encr); + xfree (curvebuf); + return err; +} + + /* Encrypt the DEK under the key contained in CERT and return it as a - canonical S-Exp in encval. */ + * canonical S-expressions at ENCVAL. PK_ALGO is the public key + * algorithm which the caller has already retrieved from CERT. */ static int -encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval) +encrypt_dek (const DEK dek, ksba_cert_t cert, int pk_algo, + unsigned char **encval) { gcry_sexp_t s_ciph, s_data, s_pkey; int rc; @@ -198,21 +473,41 @@ encrypt_dek (const DEK dek, ksba_cert_t cert, unsigned char **encval) return rc; } + if (DBG_CRYPTO) + { + log_printsexp (" pubkey:", s_pkey); + log_printhex (dek->key, dek->keylen, "CEK .....:"); + } + /* Put the encoded cleartext into a simple list. */ s_data = NULL; /* (avoid compiler warning) */ - rc = encode_session_key (dek, &s_data); - if (rc) + if (pk_algo == GCRY_PK_ECC) { - gcry_sexp_release (s_pkey); - log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); - return rc; + if (!(opt.compat_flags & COMPAT_ALLOW_ECC_ENCR)) + rc = gpg_error (GPG_ERR_NOT_SUPPORTED); + else + rc = ecdh_encrypt (dek, s_pkey, &s_ciph); } + else + { + rc = encode_session_key (dek, &s_data); + if (rc) + { + log_error ("encode_session_key failed: %s\n", gpg_strerror (rc)); + return rc; + } + if (DBG_CRYPTO) + log_printsexp (" data:", s_data); - /* pass it to libgcrypt */ - rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + /* pass it to libgcrypt */ + rc = gcry_pk_encrypt (&s_ciph, s_data, s_pkey); + } gcry_sexp_release (s_data); gcry_sexp_release (s_pkey); + if (DBG_CRYPTO) + log_printsexp ("enc-val:", s_ciph); + /* Reformat it. */ if (!rc) { @@ -300,9 +595,8 @@ encrypt_cb (void *cb_value, char *buffer, size_t count, size_t *nread) int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { - int rc = 0; + gpg_error_t err = 0; gnupg_ksba_io_t b64writer = NULL; - gpg_error_t err; ksba_writer_t writer; ksba_reader_t reader = NULL; ksba_cms_t cms = NULL; @@ -331,7 +625,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) log_error(_("no valid recipients given\n")); gpgsm_status (ctrl, STATUS_NO_RECP, "0"); audit_log_i (ctrl->audit, AUDIT_GOT_RECIPIENTS, 0); - rc = gpg_error (GPG_ERR_NO_PUBKEY); + err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } @@ -343,7 +637,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) if (!kh) { log_error (_("failed to allocate keyDB handle\n")); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } @@ -351,45 +645,43 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) data_fp = es_fdopen_nc (data_fd, "rb"); if (!data_fp) { - rc = gpg_error_from_syserror (); - log_error ("fdopen() failed: %s\n", strerror (errno)); + err = gpg_error_from_syserror (); + log_error ("fdopen() failed: %s\n", gpg_strerror (err)); goto leave; } err = ksba_reader_new (&reader); + if (!err) + err = ksba_reader_set_cb (reader, encrypt_cb, &encparm); if (err) - rc = err; - if (!rc) - rc = ksba_reader_set_cb (reader, encrypt_cb, &encparm); - if (rc) - goto leave; + goto leave; encparm.fp = data_fp; ctrl->pem_name = "ENCRYPTED MESSAGE"; - rc = gnupg_ksba_create_writer + err = gnupg_ksba_create_writer (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), ctrl->pem_name, out_fp, &writer); - if (rc) + if (err) { - log_error ("can't create writer: %s\n", gpg_strerror (rc)); + log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + if (ctrl->input_size_hint) + gnupg_ksba_set_total (b64writer, ctrl->input_size_hint); + err = ksba_cms_new (&cms); if (err) - { - rc = err; - goto leave; - } + goto leave; err = ksba_cms_set_reader_writer (cms, reader, writer); if (err) { - log_debug ("ksba_cms_set_reader_writer failed: %s\n", + log_error ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -402,9 +694,8 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) err = ksba_cms_set_content_type (cms, 1, KSBA_CT_DATA); if (err) { - log_debug ("ksba_cms_set_content_type failed: %s\n", + log_error ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -416,34 +707,34 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), opt.def_cipher_algoid, gnupg_compliance_option_string (opt.compliance)); - rc = gpg_error (GPG_ERR_CIPHER_ALGO); + err = gpg_error (GPG_ERR_CIPHER_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, - "random-compliance", rc); + "random-compliance", err); goto leave; } /* Create a session key */ dek = xtrycalloc_secure (1, sizeof *dek); if (!dek) - rc = out_of_core (); + err = gpg_error_from_syserror (); else - { - dek->algoid = opt.def_cipher_algoid; - rc = init_dek (dek); - } - if (rc) + { + dek->algoid = opt.def_cipher_algoid; + err = init_dek (dek); + } + if (err) { log_error ("failed to create the session key: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); goto leave; } @@ -452,7 +743,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { log_error ("ksba_cms_set_content_enc_algo failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -462,7 +752,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) encparm.buffer = xtrymalloc (encparm.bufsize); if (!encparm.buffer) { - rc = out_of_core (); + err = gpg_error_from_syserror (); goto leave; } @@ -478,11 +768,12 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) unsigned char *encval; unsigned int nbits; int pk_algo; + char *curve = NULL; /* Check compliance. */ - pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); + pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits, &curve); if (!gnupg_pk_is_compliant (opt.compliance, pk_algo, 0, - NULL, nbits, NULL)) + NULL, nbits, curve)) { char kidstr[10+1]; @@ -497,15 +788,18 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) /* Fixme: When adding ECC we need to provide the curvename and * the key to gnupg_pk_is_compliant. */ if (compliant - && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, NULL)) + && !gnupg_pk_is_compliant (CO_DE_VS, pk_algo, 0, NULL, nbits, curve)) compliant = 0; - rc = encrypt_dek (dek, cl->cert, &encval); - if (rc) + xfree (curve); + curve = NULL; + + err = encrypt_dek (dek, cl->cert, pk_algo, &encval); + if (err) { - audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, rc); + audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("encryption failed for recipient no. %d: %s\n", - recpno, gpg_strerror (rc)); + recpno, gpg_strerror (err)); goto leave; } @@ -515,7 +809,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) audit_log_cert (ctrl->audit, AUDIT_ENCRYPTED_TO, cl->cert, err); log_error ("ksba_cms_add_recipient failed: %s\n", gpg_strerror (err)); - rc = err; xfree (encval); goto leave; } @@ -527,7 +820,6 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) { log_error ("ksba_cms_set_enc_val failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -541,7 +833,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) log_error (_("operation forced to fail due to" " unfulfilled compliance rules\n")); gpgsm_errors_seen = 1; - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); goto leave; } @@ -552,8 +844,7 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) err = ksba_cms_build (cms, &stopreason); if (err) { - log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); - rc = err; + log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); goto leave; } } @@ -562,15 +853,15 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) if (encparm.readerror) { log_error ("error reading input: %s\n", strerror (encparm.readerror)); - rc = gpg_error (gpg_err_code_from_errno (encparm.readerror)); + err = gpg_error (gpg_err_code_from_errno (encparm.readerror)); goto leave; } - rc = gnupg_ksba_finish_writer (b64writer); - if (rc) + err = gnupg_ksba_finish_writer (b64writer); + if (err) { - log_error ("write failed: %s\n", gpg_strerror (rc)); + log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } audit_log (ctrl->audit, AUDIT_ENCRYPTION_DONE); @@ -585,5 +876,5 @@ gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int data_fd, estream_t out_fp) xfree (dek); es_fclose (data_fp); xfree (encparm.buffer); - return rc; + return err; } diff --git a/sm/export.c b/sm/export.c index 3da06d7..dcfdab4 100644 --- a/sm/export.c +++ b/sm/export.c @@ -298,7 +298,7 @@ gpgsm_export (ctrl_t ctrl, strlist_t names, estream_t stream) ksba_cert_release (cert); cert = NULL; } - if (rc && rc != -1) + if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); else if (b64writer) { @@ -392,7 +392,7 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, estream_t stream, int rawmode) } err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); } - else if (err == -1 || gpg_err_code (err) == GPG_ERR_EOF) + else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; if (err) { diff --git a/sm/fingerprint.c b/sm/fingerprint.c index 2e01cf1..ab898fd 100644 --- a/sm/fingerprint.c +++ b/sm/fingerprint.c @@ -219,20 +219,25 @@ gpgsm_get_keygrip_hexstring (ksba_cert_t cert) /* Return the PK algorithm used by CERT as well as the length in bits - of the public key at NBITS. */ + * of the public key at NBITS. If R_CURVE is not NULL and an ECC + * algorithm is used the name or OID of the curve is stored there; the + * caller needs to free this value. */ int -gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits) +gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits, char **r_curve) { gcry_sexp_t s_pkey; int rc; ksba_sexp_t p; size_t n; gcry_sexp_t l1, l2; + const char *curve; const char *name; char namebuf[128]; if (nbits) *nbits = 0; + if (r_curve) + *r_curve = NULL; p = ksba_cert_get_public_key (cert); if (!p) @@ -258,6 +263,24 @@ gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits) gcry_sexp_release (s_pkey); return 0; } + + if (r_curve) + { + curve = gcry_pk_get_curve (l1, 0, NULL); + if (curve) + { + name = openpgp_oid_to_curve (openpgp_curve_to_oid (curve, + NULL, NULL), 0); + *r_curve = xtrystrdup (name? name : curve); + if (!*r_curve) + { + gcry_sexp_release (l1); + gcry_sexp_release (s_pkey); + return 0; /* Out of core. */ + } + } + } + l2 = gcry_sexp_cadr (l1); gcry_sexp_release (l1); l1 = l2; @@ -277,6 +300,14 @@ gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits) } +/* Return true if CERT is an ECC key. */ +int +gpgsm_is_ecc_key (ksba_cert_t cert) +{ + return GCRY_PK_ECC == gpgsm_get_key_algo_info (cert, NULL, NULL); +} + + /* This is a wrapper around pubkey_algo_string which takes a KSBA * certificate instead of a Gcrypt public key. Note that this * function may return NULL on error. */ @@ -134,6 +134,7 @@ enum cmd_and_opt_values { oAssumeArmor, oAssumeBase64, oAssumeBinary, + oInputSizeHint, oBase64, oNoArmor, @@ -201,6 +202,8 @@ enum cmd_and_opt_values { oIgnoreCertWithOID, oRequireCompliance, oCompatibilityFlags, + oKbxBufferSize, + oAlwaysTrust, oNoAutostart }; @@ -309,6 +312,7 @@ static ARGPARSE_OPTS opts[] = { N_("assume input is in base-64 format")), ARGPARSE_s_n (oAssumeBinary, "assume-binary", N_("assume input is in binary format")), + ARGPARSE_s_s (oInputSizeHint, "input-size-hint", "@"), ARGPARSE_header ("Output", N_("Options controlling the output")), @@ -391,6 +395,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"), + ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"), ARGPARSE_header (NULL, N_("Options for unattended use")), @@ -424,6 +429,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), ARGPARSE_s_s (oXauthority, "xauthority", "@"), ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"), + ARGPARSE_p_u (oKbxBufferSize, "kbx-buffer-size", "@"), ARGPARSE_header (NULL, ""), /* Stop the header group. */ @@ -463,6 +469,7 @@ static struct debug_flags_s debug_flags [] = static struct compatibility_flags_s compatibility_flags [] = { { COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" }, + { COMPAT_ALLOW_ECC_ENCR, "allow-ecc-encr" }, { 0, NULL } }; @@ -494,7 +501,7 @@ static int default_include_certs = DEFAULT_INCLUDE_CERTS; static int default_validation_model; /* The default cipher algo. */ -#define DEFAULT_CIPHER_ALGO "AES" +#define DEFAULT_CIPHER_ALGO "AES256" static char *build_list (const char *text, @@ -515,6 +522,7 @@ our_pk_test_algo (int algo) { case GCRY_PK_RSA: case GCRY_PK_ECDSA: + case GCRY_PK_EDDSA: return gcry_pk_test_algo (algo); default: return 1; @@ -767,7 +775,7 @@ set_debug (void) /* minip12.c may be used outside of GnuPG, thus we don't have the * opt structure over there. */ - p12_set_verbosity (opt.verbose); + p12_set_verbosity (opt.verbose, opt.debug); } @@ -1144,6 +1152,10 @@ main ( int argc, char **argv) ctrl.is_base64 = 0; break; + case oInputSizeHint: + ctrl.input_size_hint = string_to_u64 (pargs.r.ret_str); + break; + case oDisableCRLChecks: opt.no_crl_check = 1; break; @@ -1431,6 +1443,11 @@ main ( int argc, char **argv) case oMinRSALength: opt.min_rsa_length = pargs.r.ret_ulong; break; case oRequireCompliance: opt.require_compliance = 1; break; + case oAlwaysTrust: opt.always_trust = 1; break; + + case oKbxBufferSize: + keybox_set_buffersize (pargs.r.ret_ulong, 0); + break; default: if (configname) @@ -1491,6 +1508,15 @@ main ( int argc, char **argv) if (may_coredump && !opt.quiet) log_info (_("WARNING: program may create a core file!\n")); + if (opt.require_compliance && opt.always_trust) + { + opt.always_trust = 0; + if (opt.quiet) + log_info (_("WARNING: %s overrides %s\n"), + "--require-compliance","--always-trust"); + } + + /* if (opt.qualsig_approval && !opt.quiet) */ /* log_info (_("This software has officially been approved to " */ /* "create and verify\n" */ @@ -1530,7 +1556,7 @@ main ( int argc, char **argv) set_debug (); if (opt.verbose) /* Print the compatibility flags. */ parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags); - gnupg_set_compliance_extra_info (opt.min_rsa_length); + gnupg_set_compliance_extra_info (CO_EXTRA_INFO_MIN_RSA, opt.min_rsa_length); /* Although we always use gpgsm_exit, we better install a regualr exit handler so that at least the secure memory gets wiped @@ -1829,11 +1855,17 @@ main ( int argc, char **argv) break; case aSignEncr: /* sign and encrypt the given file */ - log_error ("this command has not yet been implemented\n"); + log_error ("the command '%s' has not yet been implemented\n", + "--sign --encrypt"); + gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", + gpg_error (GPG_ERR_NOT_IMPLEMENTED)); break; case aClearsign: /* make a clearsig */ - log_error ("this command has not yet been implemented\n"); + log_error ("the command '%s' has not yet been implemented\n", + "--clearsign"); + gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", + gpg_error (GPG_ERR_NOT_IMPLEMENTED)); break; case aVerify: @@ -2060,8 +2092,10 @@ main ( int argc, char **argv) default: - log_error (_("invalid command (there is no implicit command)\n")); - break; + log_error (_("invalid command (there is no implicit command)\n")); + gpgsm_status_with_error (&ctrl, STATUS_FAILURE, "option-parser", + gpg_error (GPG_ERR_MISSING_ACTION)); + break; } /* Print the audit result if needed. */ @@ -102,8 +102,6 @@ struct int extra_digest_algo; /* A digest algorithm also used for verification of signatures. */ - int always_trust; /* Trust the given keys even if there is no - valid certification chain */ int skip_verify; /* do not check signatures on data */ int lock_once; /* Keep lock once they are set */ @@ -150,6 +148,10 @@ struct * mode. */ int require_compliance; + /* Enable always-trust mode - note that there is also server option + * for this. */ + int always_trust; + /* Compatibility flags (COMPAT_FLAG_xxxx). */ unsigned int compat_flags; } opt; @@ -181,6 +183,7 @@ struct * policies: 1.3.6.1.4.1.7924.1.1:N: */ #define COMPAT_ALLOW_KA_TO_ENCR 1 +#define COMPAT_ALLOW_ECC_ENCR 2 /* Forward declaration for an object defined in server.c */ @@ -210,6 +213,11 @@ struct server_control_s int is_pem; /* Is in PEM format */ int is_base64; /* is in plain base-64 format */ + /* If > 0 a hint with the expected number of input data bytes. This + * is not necessary an exact number but intended to be used for + * progress info and to decide on how to allocate buffers. */ + uint64_t input_size_hint; + int create_base64; /* Create base64 encoded output */ int create_pem; /* create PEM output */ const char *pem_name; /* PEM name to use */ @@ -224,6 +232,9 @@ struct server_control_s 2 := STEED model. */ int offline; /* If true gpgsm won't do any network access. */ + int always_trust; /* True in always-trust mode; see also + * opt.always-trust. */ + /* The current time. Used as a helper in certchain.c. */ ksba_isotime_t current_time; }; @@ -236,6 +247,7 @@ struct certlist_s ksba_cert_t cert; int is_encrypt_to; /* True if the certificate has been set through the --encrypto-to option. */ + int pk_algo; /* The PK_ALGO from CERT or 0 if not yet known. */ int hash_algo; /* Used to track the hash algorithm to use. */ const char *hash_algo_oid; /* And the corresponding OID. */ }; @@ -249,6 +261,8 @@ struct rootca_flags_s information. */ unsigned int relax:1; /* Relax checking of root certificates. */ unsigned int chain_model:1; /* Root requires the use of the chain model. */ + unsigned int qualified:1; /* Root CA used for qualfied signatures. */ + unsigned int de_vs:1; /* Root CA is de-vs compliant. */ }; @@ -268,6 +282,7 @@ gpg_error_t gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, gpg_err_code_t ec); gpg_error_t gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, gpg_error_t err); +gpg_error_t gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total); gpg_error_t gpgsm_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); @@ -280,7 +295,9 @@ unsigned long gpgsm_get_short_fingerprint (ksba_cert_t cert, unsigned long *r_high); unsigned char *gpgsm_get_keygrip (ksba_cert_t cert, unsigned char *array); char *gpgsm_get_keygrip_hexstring (ksba_cert_t cert); -int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits); +int gpgsm_get_key_algo_info (ksba_cert_t cert, unsigned int *nbits, + char **r_curve); +int gpgsm_is_ecc_key (ksba_cert_t cert); char *gpgsm_pubkey_algo_string (ksba_cert_t cert, int *r_algoid); char *gpgsm_get_certid (ksba_cert_t cert); @@ -329,6 +346,7 @@ int gpgsm_create_cms_signature (ctrl_t ctrl, #define VALIDATE_FLAG_NO_DIRMNGR 1 #define VALIDATE_FLAG_CHAIN_MODEL 2 #define VALIDATE_FLAG_STEED 4 +#define VALIDATE_FLAG_BYPASS 8 /* No actual validation. */ int gpgsm_walk_cert_chain (ctrl_t ctrl, ksba_cert_t start, ksba_cert_t *r_next); @@ -354,8 +372,11 @@ int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert, int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret, certlist_t *listaddr, int is_encrypt_to); void gpgsm_release_certlist (certlist_t list); + +#define FIND_CERT_ALLOW_AMBIG 1 +#define FIND_CERT_WITH_EPHEM 2 int gpgsm_find_cert (ctrl_t ctrl, const char *name, ksba_sexp_t keyid, - ksba_cert_t *r_cert, int allow_ambiguous); + ksba_cert_t *r_cert, unsigned int flags); /*-- keylist.c --*/ gpg_error_t gpgsm_list_keys (ctrl_t ctrl, strlist_t names, @@ -387,6 +408,10 @@ int gpgsm_encrypt (ctrl_t ctrl, certlist_t recplist, int in_fd, estream_t out_fp); /*-- decrypt.c --*/ +gpg_error_t hash_ecc_cms_shared_info (gcry_md_hd_t hash_hd, + const char *wrap_algo_str, + unsigned int keylen, + const void *ukm, unsigned int ukmlen); int gpgsm_decrypt (ctrl_t ctrl, int in_fd, estream_t out_fp); /*-- certreqgen.c --*/ diff --git a/sm/gpgsm.w32-manifest.in b/sm/gpgsm.w32-manifest.in index 3055788..413b3e3 100644 --- a/sm/gpgsm.w32-manifest.in +++ b/sm/gpgsm.w32-manifest.in @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8" standalone="yes" ?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> -<description>GNU Privacy Guard (X409/CMS tool)</description> +<description>GNU Privacy Guard (X.509/CMS Tool)</description> <assemblyIdentity type="win32" name="GnuPG.gpgsm" @@ -15,4 +15,11 @@ <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/><!-- Vista --> </application> </compatibility> +<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> + <security> + <requestedPrivileges> + <requestedExecutionLevel level="asInvoker"/> + </requestedPrivileges> + </security> +</trustInfo> </assembly> diff --git a/sm/import.c b/sm/import.c index 8a75a2b..ab8d883 100644 --- a/sm/import.c +++ b/sm/import.c @@ -192,7 +192,8 @@ check_and_store (ctrl_t ctrl, struct stats_s *stats, */ rc = gpgsm_basic_cert_check (ctrl, cert); if (!rc && ctrl->with_validation) - rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); + rc = gpgsm_validate_chain (ctrl, cert, + GNUPG_ISOTIME_NONE, NULL, 0, NULL, 0, NULL); if (!rc || (!ctrl->with_validation && (gpg_err_code (rc) == GPG_ERR_MISSING_CERT || gpg_err_code (rc) == GPG_ERR_MISSING_ISSUER_CERT))) @@ -551,7 +552,7 @@ gpgsm_import_files (ctrl_t ctrl, int nfiles, char **files, int fd = of (*files); rc = import_one (ctrl, &stats, fd); close (fd); - if (rc == -1) + if (rc == -1/*legacy*/ || gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; } } @@ -717,6 +718,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) gcry_sexp_t s_key = NULL; unsigned char grip[20]; int bad_pass = 0; + char *curve = NULL; int i; struct store_cert_parm_s store_cert_parm; @@ -778,8 +780,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) kparms = p12_parse (p12buffer + p12bufoff, p12buflen - p12bufoff, passphrase, store_cert_cb, &store_cert_parm, - &bad_pass, NULL); - + &bad_pass, &curve); xfree (passphrase); passphrase = NULL; @@ -790,35 +791,79 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) goto leave; } -/* print_mpi (" n", kparms[0]); */ -/* print_mpi (" e", kparms[1]); */ -/* print_mpi (" d", kparms[2]); */ -/* print_mpi (" p", kparms[3]); */ -/* print_mpi (" q", kparms[4]); */ -/* print_mpi ("dmp1", kparms[5]); */ -/* print_mpi ("dmq1", kparms[6]); */ -/* print_mpi (" u", kparms[7]); */ - - sk.n = kparms[0]; - sk.e = kparms[1]; - sk.d = kparms[2]; - sk.q = kparms[3]; - sk.p = kparms[4]; - sk.u = kparms[7]; - err = rsa_key_check (&sk); - if (err) - goto leave; -/* print_mpi (" n", sk.n); */ -/* print_mpi (" e", sk.e); */ -/* print_mpi (" d", sk.d); */ -/* print_mpi (" p", sk.p); */ -/* print_mpi (" q", sk.q); */ -/* print_mpi (" u", sk.u); */ - - /* Create an S-expression from the parameters. */ - err = gcry_sexp_build (&s_key, NULL, - "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", - sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); + if (curve) + { + gcry_ctx_t ecctx = NULL; + + /* log_debug ("curve: %s\n", curve); */ + /* gcry_log_debugmpi ("MPI[0]", kparms[0]); */ + + /* We need to get the public key. */ + err = gcry_mpi_ec_new (&ecctx, NULL, curve); + if (err) + { + log_error ("error creating context for curve '%s': %s\n", + curve, gpg_strerror (err)); + goto leave; + } + err = gcry_mpi_ec_set_mpi ("d", kparms[0], ecctx); + if (err) + { + log_error ("error setting 'd' into context of curve '%s': %s\n", + curve, gpg_strerror (err)); + gcry_ctx_release (ecctx); + goto leave; + } + + kparms[1] = gcry_mpi_ec_get_mpi ("q", ecctx, 1); + if (!kparms[1]) + { + log_error ("error computing 'q' from 'd' for curve '%s'\n", curve); + gcry_ctx_release (ecctx); + goto leave; + } + + gcry_ctx_release (ecctx); + + err = gcry_sexp_build (&s_key, NULL, + "(private-key(ecc(curve %s)(q%m)(d%m)))", + curve, kparms[1], kparms[0], NULL); + } + else /* RSA */ + { + /* print_mpi (" n", kparms[0]); */ + /* print_mpi (" e", kparms[1]); */ + /* print_mpi (" d", kparms[2]); */ + /* print_mpi (" p", kparms[3]); */ + /* print_mpi (" q", kparms[4]); */ + /* print_mpi ("dmp1", kparms[5]); */ + /* print_mpi ("dmq1", kparms[6]); */ + /* print_mpi (" u", kparms[7]); */ + + sk.n = kparms[0]; + sk.e = kparms[1]; + sk.d = kparms[2]; + sk.q = kparms[3]; + sk.p = kparms[4]; + sk.u = kparms[7]; + err = rsa_key_check (&sk); + if (err) + goto leave; + /* print_mpi (" n", sk.n); */ + /* print_mpi (" e", sk.e); */ + /* print_mpi (" d", sk.d); */ + /* print_mpi (" p", sk.p); */ + /* print_mpi (" q", sk.q); */ + /* print_mpi (" u", sk.u); */ + + /* Create an S-expression from the parameters. */ + err = gcry_sexp_build (&s_key, NULL, + "(private-key(rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))", + sk.n, sk.e, sk.d, sk.p, sk.q, sk.u, NULL); + } + + /* The next is very ugly - we really should not rely on our + * knowledge of p12_parse internals. */ for (i=0; i < 8; i++) gcry_mpi_release (kparms[i]); gcry_free (kparms); @@ -925,6 +970,7 @@ parse_p12 (ctrl_t ctrl, ksba_reader_t reader, struct stats_s *stats) xfree (kek); xfree (get_membuf (&p12mbuf, NULL)); xfree (p12buffer); + xfree (curve); if (bad_pass) { @@ -941,9 +941,10 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, rc = lock_all (hd); if (rc) return rc; - rc = -1; + rc = gpg_error (GPG_ERR_EOF); - while (rc == -1 && hd->current >= 0 && hd->current < hd->used) + while (gpg_err_code (rc) == GPG_ERR_EOF + && hd->current >= 0 && hd->current < hd->used) { switch (hd->active[hd->current].type) { @@ -954,9 +955,11 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc, KEYBOX_BLOBTYPE_X509, NULL, &skipped); + if (rc == -1) /* Map legacy code. */ + rc = gpg_error (GPG_ERR_EOF); break; } - if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) + if (gpg_err_code (rc) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; } @@ -964,6 +967,10 @@ keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd, hd->found = hd->current; } + /* The NOTHING_FOUND error is triggered by a NEXT command. */ + if (gpg_err_code (rc) == GPG_ERR_EOF + || gpg_err_code (rc) == GPG_ERR_NOTHING_FOUND) + rc = gpg_error (GPG_ERR_NOT_FOUND); return rc; } @@ -1102,7 +1109,7 @@ keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed) return rc; rc = keydb_search_fpr (ctrl, kh, fpr); - if (rc != -1) + if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) { keydb_release (kh); if (!rc) @@ -1194,9 +1201,7 @@ keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, err = keydb_search_fpr (ctrl, kh, fpr); if (err) { - if (err == -1) - err = gpg_error (GPG_ERR_NOT_FOUND); - else + if (gpg_err_code (err) != gpg_error (GPG_ERR_NOT_FOUND)) log_error (_("problem re-searching certificate: %s\n"), gpg_strerror (err)); keydb_release (kh); @@ -1313,7 +1318,7 @@ keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names) } } } - if (rc && rc != -1) + if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("%s failed: %s\n", __func__, gpg_strerror (rc)); leave: diff --git a/sm/keylist.c b/sm/keylist.c index 2d51aa7..4e2d999 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -247,7 +247,7 @@ print_key_data (ksba_cert_t cert, estream_t fp) } static void -print_capabilities (ksba_cert_t cert, estream_t fp) +print_capabilities (ksba_cert_t cert, int algo, estream_t fp) { gpg_error_t err; unsigned int use; @@ -299,7 +299,7 @@ print_capabilities (ksba_cert_t cert, estream_t fp) /* We need to returned the faked key usage to frontends so that they * can select the right key. Note that we don't do this for the * human readable keyUsage. */ - if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) + if ((algo == GCRY_PK_ECC || (opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)) && (use & KSBA_KEYUSAGE_KEY_AGREEMENT)) is_encr = 1; @@ -375,14 +375,14 @@ email_kludge (const char *name) * number. NBITS is the length of the key in bits. */ static void print_compliance_flags (ksba_cert_t cert, int algo, unsigned int nbits, - estream_t fp) + const char *curvename, estream_t fp) { int hashalgo; /* Note that we do not need to test for PK_ALGO_FLAG_RSAPSS because * that is not a property of the key but one of the created * signature. */ - if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, NULL)) + if (gnupg_pk_is_compliant (CO_DE_VS, algo, 0, NULL, nbits, curvename)) { hashalgo = gcry_md_map_name (ksba_cert_get_digest_algo (cert)); if (gnupg_digest_is_compliant (CO_DE_VS, hashalgo)) @@ -408,13 +408,15 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, gpg_error_t valerr; int algo; unsigned int nbits; + char *curve = NULL; const char *chain_id; char *chain_id_buffer = NULL; int is_root = 0; char *kludge_uid; if (ctrl->with_validation) - valerr = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, NULL, 0, NULL); + valerr = gpgsm_validate_chain (ctrl, cert, + GNUPG_ISOTIME_NONE, NULL, 1, NULL, 0, NULL); else valerr = 0; @@ -432,8 +434,9 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, chain_id = chain_id_buffer; ksba_cert_release (next); } - else if (rc == -1) /* We have reached the root certificate. */ + else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { + /* We have reached the root certificate. */ chain_id = fpr; is_root = 1; } @@ -469,6 +472,8 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, { if (gpgsm_cert_has_well_known_private_key (cert)) *truststring = 'w'; /* Well, this is dummy CA. */ + else if (gpg_err_code (valerr) == GPG_ERR_NOT_TRUSTED) + *truststring = 'n'; /* Likely the root cert is not trusted. */ else *truststring = 'i'; } @@ -499,7 +504,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, if (*truststring) es_fputs (truststring, fp); - algo = gpgsm_get_key_algo_info (cert, &nbits); + algo = gpgsm_get_key_algo_info (cert, &nbits, &curve); es_fprintf (fp, ":%u:%d:%s:", nbits, algo, fpr+24); ksba_cert_get_validity (cert, 0, t); @@ -538,7 +543,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, /* Field 11, signature class - not used */ es_putc (':', fp); /* Field 12, capabilities: */ - print_capabilities (cert, fp); + print_capabilities (cert, algo, fp); es_putc (':', fp); /* Field 13, not used: */ es_putc (':', fp); @@ -563,8 +568,10 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, } es_putc (':', fp); /* End of field 15. */ es_putc (':', fp); /* End of field 16. */ + if (curve) + es_fputs (curve, fp); es_putc (':', fp); /* End of field 17. */ - print_compliance_flags (cert, algo, nbits, fp); + print_compliance_flags (cert, algo, nbits, curve, fp); es_putc (':', fp); /* End of field 18. */ es_putc ('\n', fp); @@ -626,6 +633,7 @@ list_cert_colon (ctrl_t ctrl, ksba_cert_t cert, unsigned int validity, xfree (p); } xfree (kludge_uid); + xfree (curve); } @@ -829,12 +837,11 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, es_fprintf (fp, " hashAlgo: %s%s%s%s\n", oid, s?" (":"",s?s:"",s?")":""); { - const char *algoname; - unsigned int nbits; + char *algostr; - algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits)); - es_fprintf (fp, " keyType: %u bit %s\n", - nbits, algoname? algoname:"?"); + algostr = gpgsm_pubkey_algo_string (cert, NULL); + es_fprintf (fp, " keyType: %s\n", algostr? algostr : "[error]"); + xfree (algostr); } /* subjectKeyIdentifier */ @@ -1103,7 +1110,8 @@ list_cert_raw (ctrl_t ctrl, KEYDB_HANDLE hd, if (with_validation) { - err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); + err = gpgsm_validate_chain (ctrl, cert, + GNUPG_ISOTIME_NONE, NULL, 1, fp, 0, NULL); if (!err) es_fprintf (fp, " [certificate is good]\n"); else @@ -1192,15 +1200,13 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, { - const char *algoname; - unsigned int nbits; + char *algostr; - algoname = gcry_pk_algo_name (gpgsm_get_key_algo_info (cert, &nbits)); - es_fprintf (fp, " key type: %u bit %s\n", - nbits, algoname? algoname:"?"); + algostr = gpgsm_pubkey_algo_string (cert, NULL); + es_fprintf (fp, " key type: %s\n", algostr? algostr : "[error]"); + xfree (algostr); } - err = ksba_cert_get_key_usage (cert, &kusage); if (gpg_err_code (err) != GPG_ERR_NO_DATA) { @@ -1352,7 +1358,8 @@ list_cert_std (ctrl_t ctrl, ksba_cert_t cert, estream_t fp, int have_secret, size_t buflen; char buffer[1]; - err = gpgsm_validate_chain (ctrl, cert, "", NULL, 1, fp, 0, NULL); + err = gpgsm_validate_chain (ctrl, cert, + GNUPG_ISOTIME_NONE, NULL, 1, fp, 0, NULL); tmperr = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!tmperr && buflen) @@ -1586,7 +1593,7 @@ list_internal_keys (ctrl_t ctrl, strlist_t names, estream_t fp, lastcert = cert; cert = NULL; } - if (gpg_err_code (rc) == GPG_ERR_EOF || rc == -1 ) + if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; if (rc) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); diff --git a/sm/minip12.c b/sm/minip12.c index 29b4898..2e60edf 100644 --- a/sm/minip12.c +++ b/sm/minip12.c @@ -1,7 +1,7 @@ /* minip12.c - A minimal pkcs-12 implementation. * Copyright (C) 2002, 2003, 2004, 2006, 2011 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch - * Copyright (C) 2022 g10 Code GmbH + * Copyright (C) 2022, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -50,6 +50,8 @@ #define DIM(v) (sizeof(v)/sizeof((v)[0])) #endif +/* Enable the next macro to dump stuff for debugging. */ +#undef ENABLE_DER_STRUCT_DUMPING static unsigned char const oid_data[9] = { @@ -78,6 +80,19 @@ static unsigned char const oid_pkcs5PBES2[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x05, 0x0D }; static unsigned char const oid_aes128_CBC[9] = { 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x02 }; +static unsigned char const oid_aes256_CBC[9] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, 0x2A }; + +static unsigned char const oid_hmacWithSHA1[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x07 }; +static unsigned char const oid_hmacWithSHA224[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x08 }; +static unsigned char const oid_hmacWithSHA256[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x09 }; +static unsigned char const oid_hmacWithSHA384[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0A }; +static unsigned char const oid_hmacWithSHA512[8] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x0B }; static unsigned char const oid_rsaEncryption[9] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01 }; @@ -109,6 +124,8 @@ static unsigned char const data_mactemplate[51] = { #define DATA_MACTEMPLATE_MAC_OFF 17 #define DATA_MACTEMPLATE_SALT_OFF 39 +/* Note that the BMP String in this template reads: + * "GnuPG exported certificate ffffffff" */ static unsigned char const data_attrtemplate[106] = { 0x31, 0x7c, 0x30, 0x55, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x14, 0x31, @@ -138,11 +155,52 @@ struct tag_info int class; int is_constructed; unsigned long tag; - unsigned long length; /* length part of the TLV */ - int nhdr; + size_t length; /* length part of the TLV */ + size_t nhdr; int ndef; /* It is an indefinite length */ }; + +#define TLV_MAX_DEPTH 20 + + +struct bufferlist_s +{ + struct bufferlist_s *next; + char *buffer; +}; + + +/* An object to control the ASN.1 parsing. */ +struct tlv_ctx_s +{ + /* The orginal buffer with the entire pkcs#12 object and its length. */ + const unsigned char *origbuffer; + size_t origbufsize; + + /* The current buffer we are working on and its length. */ + const unsigned char *buffer; + size_t bufsize; + + int in_ndef; /* Flag indicating that we are in a NDEF. */ + int pending; /* The last tlv_next has not yet been processed. */ + + struct tag_info ti; /* The current tag. */ + gpg_error_t lasterr; /* Last error from tlv function. */ + const char *lastfunc;/* Name of last called function. */ + + struct bufferlist_s *bufferlist; /* To keep track of malloced buffers. */ + + unsigned int stacklen; /* Used size of the stack. */ + struct { + const unsigned char *buffer; /* Saved value of BUFFER. */ + size_t bufsize; /* Saved value of BUFSIZE. */ + size_t length; /* Length of the container (ti.length). */ + int in_ndef; /* Saved IN_NDEF flag (ti.ndef). */ + } stack[TLV_MAX_DEPTH]; +}; + + /* Parser communication object. */ struct p12_parse_ctx_s { @@ -167,21 +225,85 @@ struct p12_parse_ctx_s static int opt_verbose; +static unsigned char *cram_octet_string (const unsigned char *input, + size_t length, size_t *r_newlength); +static int need_octet_string_cramming (const unsigned char *input, + size_t length); + + + + void -p12_set_verbosity (int verbose) +p12_set_verbosity (int verbose, int debug) +{ + opt_verbose = !!verbose; + if (debug) + opt_verbose = 2; +} + + +#define dump_tag_info(a,b) _dump_tag_info ((a),__LINE__,(b)) +static void +_dump_tag_info (const char *text, int lno, struct tlv_ctx_s *tlv) +{ + struct tag_info *ti; + + if (opt_verbose < 2) + return; + + ti = &tlv->ti; + + log_debug ("p12_parse:%s:%d: @%04zu class=%d tag=%lu len=%zu nhdr=%zu %s%s\n", + text, lno, + (size_t)(tlv->buffer - tlv->origbuffer) - ti->nhdr, + ti->class, ti->tag, ti->length, ti->nhdr, + ti->is_constructed?" cons":"", + ti->ndef?" ndef":""); +} + + +#define dump_tlv_ctx(a,b,c) _dump_tlv_ctx ((a),(b),__LINE__,(c)) +static void +_dump_tlv_ctx (const char *text, const char *text2, + int lno, struct tlv_ctx_s *tlv) { - opt_verbose = verbose; + if (opt_verbose < 2) + return; + + log_debug ("p12_parse:%s%s%s:%d: @%04zu lvl=%u %s\n", + text, + text2? "/":"", text2? text2:"", + lno, + (size_t)(tlv->buffer - tlv->origbuffer), + tlv->stacklen, + tlv->in_ndef? " in-ndef":""); } -/* static void */ -/* dump_tag_info (struct tag_info *ti) */ -/* { */ -/* log_debug ("p12_parse: ti.class=%d tag=%lu len=%lu nhdr=%d %s%s\n", */ -/* ti->class, ti->tag, ti->length, ti->nhdr, */ -/* ti->is_constructed?" cons":"", */ -/* ti->ndef?" ndef":""); */ -/* } */ +static int +digest_algo_from_oid (unsigned char const *oid, size_t oidlen) +{ + int algo; + + if (oidlen == DIM(oid_hmacWithSHA1) && + !memcmp (oid, oid_hmacWithSHA1, oidlen)) + algo = GCRY_MD_SHA1; + else if (oidlen == DIM(oid_hmacWithSHA224) && + !memcmp (oid, oid_hmacWithSHA224, oidlen)) + algo = GCRY_MD_SHA224; + else if (oidlen == DIM(oid_hmacWithSHA256) && + !memcmp (oid, oid_hmacWithSHA256, oidlen)) + algo = GCRY_MD_SHA256; + else if (oidlen == DIM(oid_hmacWithSHA384) && + !memcmp (oid, oid_hmacWithSHA384, oidlen)) + algo = GCRY_MD_SHA384; + else if (oidlen == DIM(oid_hmacWithSHA512) && + !memcmp (oid, oid_hmacWithSHA512, oidlen)) + algo = GCRY_MD_SHA512; + else + algo = 0; + return algo; +} /* Wrapper around tlv_builder_add_ptr to add an OID. When we @@ -276,121 +398,574 @@ builder_add_mpi (tlv_builder_t tb, int class, int tag, gcry_mpi_t mpi, } + /* Parse the buffer at the address BUFFER which is of SIZE and return - the tag and the length part from the TLV triplet. Update BUFFER - and SIZE on success. Checks that the encoded length does not - exhaust the length of the provided buffer. */ + * the tag and the length part from the TLV triplet. Update BUFFER + * and SIZE on success. Checks that the encoded length does not + * exhaust the length of the provided buffer. */ static int parse_tag (unsigned char const **buffer, size_t *size, struct tag_info *ti) { - int c; - unsigned long tag; - const unsigned char *buf = *buffer; - size_t length = *size; + gpg_error_t err; + int tag; - ti->length = 0; - ti->ndef = 0; - ti->nhdr = 0; + err = parse_ber_header (buffer, size, + &ti->class, &tag, + &ti->is_constructed, &ti->ndef, + &ti->length, &ti->nhdr); + if (err) + return err; + if (tag < 0) + return gpg_error (GPG_ERR_EOVERFLOW); + ti->tag = tag; - /* Get the tag */ - if (!length) - return -1; /* premature eof */ - c = *buf++; length--; - ti->nhdr++; + if (ti->length > *size) + return gpg_error (GPG_ERR_BUFFER_TOO_SHORT); /* data larger than buffer. */ + + return 0; +} - ti->class = (c & 0xc0) >> 6; - ti->is_constructed = !!(c & 0x20); - tag = c & 0x1f; - if (tag == 0x1f) +/* Create a new TLV object. */ +static struct tlv_ctx_s * +tlv_new (const unsigned char *buffer, size_t bufsize) +{ + struct tlv_ctx_s *tlv; + tlv = xtrycalloc (1, sizeof *tlv); + if (tlv) { - tag = 0; - do + tlv->origbuffer = buffer; + tlv->origbufsize = bufsize; + tlv->buffer = buffer; + tlv->bufsize = bufsize; + } + return tlv; +} + + +/* This function can be used to store a malloced buffer into the TLV + * object. Ownership of BUFFER is thus transferred to TLV. This + * buffer will then only be released by tlv_release. */ +static gpg_error_t +tlv_register_buffer (struct tlv_ctx_s *tlv, char *buffer) +{ + struct bufferlist_s *item; + + item = xtrycalloc (1, sizeof *item); + if (!item) + return gpg_error_from_syserror (); + item->buffer = buffer; + item->next = tlv->bufferlist; + tlv->bufferlist = item; + return 0; +} + + +static void +tlv_release (struct tlv_ctx_s *tlv) +{ + if (!tlv) + return; + while (tlv->bufferlist) + { + struct bufferlist_s *save = tlv->bufferlist->next; + xfree (tlv->bufferlist->buffer); + xfree (tlv->bufferlist); + tlv->bufferlist = save; + } + xfree (tlv); +} + + +/* Helper for the tlv_peek functions. */ +static gpg_error_t +_tlv_peek (struct tlv_ctx_s *tlv, struct tag_info *ti) +{ + const unsigned char *p; + size_t n; + + /* Note that we want to peek ahead of any current container but of + * course not beyond our entire buffer. */ + p = tlv->buffer; + if ((p - tlv->origbuffer) > tlv->origbufsize) + return gpg_error (GPG_ERR_BUG); + n = tlv->origbufsize - (p - tlv->origbuffer); + return parse_tag (&p, &n, ti); +} + + +/* Look for the next tag and return true if it matches CLASS and TAG. + * Otherwise return false. No state is changed. */ +static int +tlv_peek (struct tlv_ctx_s *tlv, int class, int tag) +{ + struct tag_info ti; + + return (!_tlv_peek (tlv, &ti) + && ti.class == class && ti.tag == tag); +} + + +/* Look for the next tag and return true if it is the Null tag. + * Otherwise return false. No state is changed. */ +static int +tlv_peek_null (struct tlv_ctx_s *tlv) +{ + struct tag_info ti; + + return (!_tlv_peek (tlv, &ti) + && ti.class == CLASS_UNIVERSAL && ti.tag == TAG_NULL + && !ti.is_constructed && !ti.length); +} + + +/* Helper for tlv_expect_sequence and tlv_expect_context_tag. */ +static gpg_error_t +_tlv_push (struct tlv_ctx_s *tlv) +{ + /* Right now our pointer is at the value of the current container. + * We push that info onto the stack. */ + if (tlv->stacklen >= TLV_MAX_DEPTH) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_MANY)); + tlv->stack[tlv->stacklen].buffer = tlv->buffer; + tlv->stack[tlv->stacklen].bufsize = tlv->bufsize; + tlv->stack[tlv->stacklen].in_ndef = tlv->in_ndef; + tlv->stack[tlv->stacklen].length = tlv->ti.length; + tlv->stacklen++; + + tlv->in_ndef = tlv->ti.ndef; + + /* We set the size of the buffer to the TLV length if it is known or + * else to the size of the remaining entire buffer. */ + if (tlv->in_ndef) + { + if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) + return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); + tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); + } + else + tlv->bufsize = tlv->ti.length; + + dump_tlv_ctx (__func__, NULL, tlv); + return 0; +} + + +/* Helper for tlv_next. */ +static gpg_error_t +_tlv_pop (struct tlv_ctx_s *tlv) +{ + size_t lastlen; + + /* We reached the end of a container, either due to the size limit + * or due to an end tag. Now we pop the last container so that we + * are positioned at the value of the last container. */ + if (!tlv->stacklen) + return gpg_error (GPG_ERR_EOF); + + tlv->stacklen--; + tlv->in_ndef = tlv->stack[tlv->stacklen].in_ndef; + if (tlv->in_ndef) + { + /* We keep buffer but adjust bufsize to the end of the origbuffer. */ + if ((tlv->buffer - tlv->origbuffer) > tlv->origbufsize) + return (tlv->lasterr = gpg_error (GPG_ERR_BUG)); + tlv->bufsize = tlv->origbufsize - (tlv->buffer - tlv->origbuffer); + } + else + { + lastlen = tlv->stack[tlv->stacklen].length; + tlv->buffer = tlv->stack[tlv->stacklen].buffer; + tlv->bufsize = tlv->stack[tlv->stacklen].bufsize; + if (lastlen > tlv->bufsize) { - tag <<= 7; - if (!length) - return -1; /* premature eof */ - c = *buf++; length--; - ti->nhdr++; - tag |= c & 0x7f; + log_debug ("%s: container length larger than buffer (%zu/%zu)\n", + __func__, lastlen, tlv->bufsize); + return gpg_error (GPG_ERR_INV_BER); } - while (c & 0x80); + tlv->buffer += lastlen; + tlv->bufsize -= lastlen; } - ti->tag = tag; - /* Get the length */ - if (!length) - return -1; /* prematureeof */ - c = *buf++; length--; - ti->nhdr++; - - if ( !(c & 0x80) ) - ti->length = c; - else if (c == 0x80) - ti->ndef = 1; - else if (c == 0xff) - return -1; /* forbidden length value */ + dump_tlv_ctx (__func__, NULL, tlv); + return 0; +} + + +/* Parse the next tag and value. Also detect the end of a + * container. */ +#define tlv_next(a) _tlv_next ((a), __LINE__) +static gpg_error_t +_tlv_next (struct tlv_ctx_s *tlv, int lno) +{ + gpg_error_t err; + + tlv->lasterr = 0; + tlv->lastfunc = __func__; + + if (tlv->pending) + { + tlv->pending = 0; + if (opt_verbose > 1) + log_debug ("%s: tlv_next skipped\n", __func__); + return 0; + } + + if (opt_verbose > 1) + log_debug ("%s: tlv_next called\n", __func__); + /* If we are at the end of an ndef container pop the stack. */ + if (!tlv->in_ndef && !tlv->bufsize) + { + do + err = _tlv_pop (tlv); + while (!err && !tlv->in_ndef && !tlv->bufsize); + if (err) + return (tlv->lasterr = err); + if (opt_verbose > 1) + log_debug ("%s: container(s) closed due to size\n", __func__); + } + + again: + /* Get the next tag. */ + err = parse_tag (&tlv->buffer, &tlv->bufsize, &tlv->ti); + if (err) + { + if (opt_verbose > 1) + log_debug ("%s: reading tag returned err=%d\n", __func__, err); + return err; + } + + /* If there is an end tag in an ndef container pop the stack. Also + * pop other containers which are fully consumed. */ + if (tlv->in_ndef && (tlv->ti.class == CLASS_UNIVERSAL + && !tlv->ti.tag && !tlv->ti.is_constructed)) + { + do + err = _tlv_pop (tlv); + while (!err && !tlv->in_ndef && !tlv->bufsize); + if (err) + return (tlv->lasterr = err); + if (opt_verbose > 1) + log_debug ("%s: container(s) closed due to end tag\n", __func__); + goto again; + } + + _dump_tag_info (__func__, lno, tlv); + return 0; +} + + +/* Return the current neting level of the TLV object. */ +static unsigned int +tlv_level (struct tlv_ctx_s *tlv) +{ + return tlv->stacklen; +} + + +/* Set a flag to indicate that the last tlv_next has not yet been + * consumed. */ +static void +tlv_set_pending (struct tlv_ctx_s *tlv) +{ + tlv->pending = 1; +} + + +/* Skip over the value of the current tag. Does not yet work for ndef + * containers. */ +static void +tlv_skip (struct tlv_ctx_s *tlv) +{ + tlv->lastfunc = __func__; + log_assert (tlv->bufsize >= tlv->ti.length); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; +} + + +/* Expect that the current tag is a sequence and setup the context for + * processing. */ +static gpg_error_t +tlv_expect_sequence (struct tlv_ctx_s *tlv) +{ + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SEQUENCE + && tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + return _tlv_push (tlv); +} + + +/* Expect that the current tag is a context tag and setup the context + * for processing. The tag of the context is returned at R_TAG. */ +static gpg_error_t +tlv_expect_context_tag (struct tlv_ctx_s *tlv, int *r_tag) +{ + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_CONTEXT && tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + *r_tag = tlv->ti.tag; + return _tlv_push (tlv); +} + + +/* Expect that the current tag is a SET and setup the context for + * processing. */ +static gpg_error_t +tlv_expect_set (struct tlv_ctx_s *tlv) +{ + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_SET + && tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + return _tlv_push (tlv); +} + + +/* Expect an object of CLASS with TAG and store its value at + * (R_DATA,R_DATALEN). Then skip over its value to the next tag. + * Note that the stored value is not allocated but points into + * TLV. */ +static gpg_error_t +tlv_expect_object (struct tlv_ctx_s *tlv, int class, int tag, + unsigned char const **r_data, size_t *r_datalen) +{ + gpg_error_t err; + const unsigned char *p; + size_t n; + int needpush = 0; + + tlv->lastfunc = __func__; + if (!(tlv->ti.class == class && tlv->ti.tag == tag)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + p = tlv->buffer; + n = tlv->ti.length; + if (!n && tlv->ti.ndef) + { + n = tlv->bufsize; + needpush = 1; + } + else if (!tlv->ti.length) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + + if (class == CLASS_CONTEXT && tag == 0 && tlv->ti.is_constructed + && need_octet_string_cramming (p, n)) + { + char *newbuffer; + + newbuffer = cram_octet_string (p, n, r_datalen); + if (!newbuffer) + return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); + err = tlv_register_buffer (tlv, newbuffer); + if (err) + { + xfree (newbuffer); + return (tlv->lasterr = err); + } + *r_data = newbuffer; + } else { - unsigned long len = 0; - int count = c & 0x7f; + *r_data = p; + *r_datalen = n; + } + if (needpush) + return _tlv_push (tlv); + + if (!(tlv->bufsize >= tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; + return 0; +} + + +/* Expect that the current tag is an object string and store its value + * at (R_DATA,R_DATALEN). Then skip over its value to the next tag. + * Note that the stored value are not allocated but point into TLV. + * If ENCAPSULATES is set the octet string is used as a new + * container. R_DATA and R_DATALEN are optional. */ +static gpg_error_t +tlv_expect_octet_string (struct tlv_ctx_s *tlv, int encapsulates, + unsigned char const **r_data, size_t *r_datalen) +{ + gpg_error_t err; + const unsigned char *p; + size_t n; + + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OCTET_STRING + && (!tlv->ti.is_constructed || encapsulates))) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + p = tlv->buffer; + if (!(n=tlv->ti.length) && !tlv->ti.ndef) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + + if (encapsulates && tlv->ti.is_constructed + && need_octet_string_cramming (p, n)) + { + char *newbuffer; - for (; count; count--) + newbuffer = cram_octet_string (p, n, r_datalen); + if (!newbuffer) + return (tlv->lasterr = gpg_error (GPG_ERR_BAD_BER)); + err = tlv_register_buffer (tlv, newbuffer); + if (err) { - len <<= 8; - if (!length) - return -1; /* premature_eof */ - c = *buf++; length--; - ti->nhdr++; - len |= c & 0xff; + xfree (newbuffer); + return (tlv->lasterr = err); } - ti->length = len; + *r_data = newbuffer; } + else + { + if (r_data) + *r_data = p; + if (r_datalen) + *r_datalen = tlv->ti.length; + } + if (encapsulates) + return _tlv_push (tlv); - if (ti->class == CLASS_UNIVERSAL && !ti->tag) - ti->length = 0; + if (!(tlv->bufsize >= tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; + return 0; +} - if (ti->length > length) - return -1; /* data larger than buffer. */ - *buffer = buf; - *size = length; +/* Expect that the current tag is an integer and return its value at + * R_VALUE. Then skip over its value to the next tag. */ +static gpg_error_t +tlv_expect_integer (struct tlv_ctx_s *tlv, int *r_value) +{ + const unsigned char *p; + size_t n; + int value; + + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER + && !tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + p = tlv->buffer; + if (!(n=tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + + /* We currently support only positive values. */ + if ((*p & 0x80)) + return (tlv->lasterr = gpg_error (GPG_ERR_ERANGE)); + + for (value = 0; n; n--) + { + value <<= 8; + value |= (*p++) & 0xff; + if (value < 0) + return (tlv->lasterr = gpg_error (GPG_ERR_EOVERFLOW)); + } + *r_value = value; + if (!(tlv->bufsize >= tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; return 0; } -/* Given an ASN.1 chunk of a structure like: +/* Variant of tlv_expect_integer which returns an MPI. If IGNORE_ZERO + * is set a value of 0 is ignored and R_VALUE not changed and the + * function returns GPG_ERR_FALSE. No check for negative encoded + * integers is doe because the old code here worked the same and we + * can't foreclose invalid encoded PKCS#12 stuff - after all it is + * PKCS#12 see https://www.cs.auckland.ac.nz/~pgut001/pubs/pfx.html */ +static gpg_error_t +tlv_expect_mpinteger (struct tlv_ctx_s *tlv, int ignore_zero, + gcry_mpi_t *r_value) +{ + const unsigned char *p; + size_t n; + + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_INTEGER + && !tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + p = tlv->buffer; + if (!(n=tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + + if (!(tlv->bufsize >= tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; + if (ignore_zero && n == 1 && !*p) + return gpg_error (GPG_ERR_FALSE); + + return gcry_mpi_scan (r_value, GCRYMPI_FMT_USG, p, n, NULL); +} + + +/* Expect that the current tag is an object id and store its value at + * (R_OID,R_OIDLEN). Then skip over its value to the next tag. Note + * that the stored value is not allocated but points into TLV. */ +static gpg_error_t +tlv_expect_object_id (struct tlv_ctx_s *tlv, + unsigned char const **r_oid, size_t *r_oidlen) +{ + const unsigned char *p; + size_t n; + + tlv->lastfunc = __func__; + if (!(tlv->ti.class == CLASS_UNIVERSAL && tlv->ti.tag == TAG_OBJECT_ID + && !tlv->ti.is_constructed)) + return (tlv->lasterr = gpg_error (GPG_ERR_INV_OBJ)); + p = tlv->buffer; + if (!(n=tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + + *r_oid = p; + *r_oidlen = tlv->ti.length; + if (!(tlv->bufsize >= tlv->ti.length)) + return (tlv->lasterr = gpg_error (GPG_ERR_TOO_SHORT)); + tlv->buffer += tlv->ti.length; + tlv->bufsize -= tlv->ti.length; + return 0; +} + - 24 NDEF: OCTET STRING -- This is not passed to us - 04 1: OCTET STRING -- INPUT point s to here - : 30 - 04 1: OCTET STRING - : 80 - [...] - 04 2: OCTET STRING - : 00 00 - : } -- This denotes a Null tag and are the last - -- two bytes in INPUT. - - Create a new buffer with the content of that octet string. INPUT - is the original buffer with a length as stored at LENGTH. Returns - NULL on error or a new malloced buffer with the length of this new - buffer stored at LENGTH and the number of bytes parsed from input - are added to the value stored at INPUT_CONSUMED. INPUT_CONSUMED is - allowed to be passed as NULL if the caller is not interested in - this value. */ + +/* Given an ASN.1 chunk of a structure like: + * + * 24 NDEF: OCTET STRING -- This is not passed to us + * 04 1: OCTET STRING -- INPUT point s to here + * : 30 + * 04 1: OCTET STRING + * : 80 + * [...] + * 04 2: OCTET STRING + * : 00 00 + * : } -- This denotes a Null tag and are the last + * -- two bytes in INPUT. + * + * The example is from Mozilla Firefox 1.0.4 which actually exports + * certs as single byte chunks of octet strings. + * + * Create a new buffer with the content of that octet string. INPUT + * is the original buffer with a LENGTH. Returns + * NULL on error or a new malloced buffer with its actual used length + * stored at R_NEWLENGTH. */ static unsigned char * -cram_octet_string (const unsigned char *input, size_t *length, - size_t *input_consumed) +cram_octet_string (const unsigned char *input, size_t length, + size_t *r_newlength) { const unsigned char *s = input; - size_t n = *length; + size_t n = length; unsigned char *output, *d; struct tag_info ti; /* Allocate output buf. We know that it won't be longer than the input buffer. */ - d = output = gcry_malloc (n); + d = output = gcry_malloc (length); if (!output) goto bailout; @@ -413,19 +988,47 @@ cram_octet_string (const unsigned char *input, size_t *length, } - *length = d - output; - if (input_consumed) - *input_consumed += s - input; + *r_newlength = d - output; return output; bailout: - if (input_consumed) - *input_consumed += s - input; gcry_free (output); return NULL; } +/* Return true if (INPUT,LENGTH) is a structure which should be passed + * to cram_octet_string. This is basically the same loop as in + * cram_octet_string but without any actual copying. */ +static int +need_octet_string_cramming (const unsigned char *input, size_t length) +{ + const unsigned char *s = input; + size_t n = length; + struct tag_info ti; + + if (!length) + return 0; + + while (n) + { + if (parse_tag (&s, &n, &ti)) + return 0; + if (ti.class == CLASS_UNIVERSAL && ti.tag == TAG_OCTET_STRING + && !ti.ndef && !ti.is_constructed) + { + s += ti.length; + n -= ti.length; + } + else if (ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed) + break; /* Ready */ + else + return 0; + } + + return 1; +} + static int string_to_key (int id, char *salt, size_t saltlen, int iter, const char *pw, @@ -556,13 +1159,14 @@ set_key_iv (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, static int set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, - const void *iv, size_t ivlen, const char *pw, int algo) + const void *iv, size_t ivlen, const char *pw, + int cipher_algo, int digest_algo) { unsigned char *keybuf; size_t keylen; int rc; - keylen = gcry_cipher_get_algo_keylen (algo); + keylen = gcry_cipher_get_algo_keylen (cipher_algo); if (!keylen) return -1; keybuf = gcry_malloc_secure (keylen); @@ -570,7 +1174,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, return -1; rc = gcry_kdf_derive (pw, strlen (pw), - GCRY_KDF_PBKDF2, GCRY_MD_SHA1, + GCRY_KDF_PBKDF2, digest_algo, salt, saltlen, iter, keylen, keybuf); if (rc) { @@ -601,7 +1205,7 @@ set_key_iv_pbes2 (gcry_cipher_hd_t chd, char *salt, size_t saltlen, int iter, static void crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, - const char *pw, int cipher_algo, int encrypt) + const char *pw, int cipher_algo, int digest_algo, int encrypt) { gcry_cipher_hd_t chd; int rc; @@ -614,8 +1218,9 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, return; } - if (cipher_algo == GCRY_CIPHER_AES128 - ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, cipher_algo) + if ((cipher_algo == GCRY_CIPHER_AES128 || cipher_algo == GCRY_CIPHER_AES256) + ? set_key_iv_pbes2 (chd, salt, saltlen, iter, iv, ivlen, pw, + cipher_algo, digest_algo) : set_key_iv (chd, salt, saltlen, iter, pw, cipher_algo == GCRY_CIPHER_RFC2268_40? 5:24)) { @@ -646,13 +1251,13 @@ crypt_block (unsigned char *buffer, size_t length, char *salt, size_t saltlen, and CIPHER_ALGO is the algorithm id to use. CHECK_FNC is a function called with the plaintext and used to check whether the decryption succeeded; i.e. that a correct passphrase has been - given. That function shall return true if the decryption has likely - succeeded. */ -static void + given. The function returns the length of the unpadded plaintext + or 0 on error. */ +static size_t decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, char *salt, size_t saltlen, int iter, const void *iv, size_t ivlen, - const char *pw, int cipher_algo, + const char *pw, int cipher_algo, int digest_algo, int (*check_fnc) (const void *, size_t)) { static const char * const charsets[] = { @@ -677,6 +1282,7 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, int charsetidx = 0; char *convertedpw = NULL; /* Malloced and converted password or NULL. */ size_t convertedpwsize = 0; /* Allocated length. */ + size_t plainlen = 0; for (charsetidx=0; charsets[charsetidx]; charsetidx++) { @@ -724,11 +1330,33 @@ decrypt_block (const void *ciphertext, unsigned char *plaintext, size_t length, } memcpy (plaintext, ciphertext, length); crypt_block (plaintext, length, salt, saltlen, iter, iv, ivlen, - convertedpw? convertedpw:pw, cipher_algo, 0); + convertedpw? convertedpw:pw, cipher_algo, digest_algo, 0); if (check_fnc (plaintext, length)) - break; /* Decryption succeeded. */ + { + /* Strip the pkcs#7 padding. */ + if (length) + { + int n, i; + + n = plaintext[length-1]; + if (n >= length || n > 16) + log_info ("decryption failed; invalid padding size\n"); + else + { + for (i=1; i < n; i++) + if (plaintext[length-i-1] != n) + break; + if (i < n) + log_info ("decryption failed; invalid padding octet\n"); + else + plainlen = length - n; + } + } + break; /* Decryption probably succeeded. */ + } } gcry_free (convertedpw); + return plainlen; } @@ -741,13 +1369,15 @@ bag_decrypted_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; - /* { */ - /* # warning debug code is enabled */ - /* FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); */ - /* if (!fp || fwrite (p, n, 1, fp) != 1) */ - /* exit (2); */ - /* fclose (fp); */ - /* } */ +#ifdef ENABLE_DER_STRUCT_DUMPING + { + # warning debug code is enabled + FILE *fp = fopen ("tmp-minip12-plain-data.der", "wb"); + if (!fp || fwrite (p, n, 1, fp) != 1) + exit (2); + fclose (fp); + } +#endif /*ENABLE_DER_STRUCT_DUMPING*/ if (parse_tag (&p, &n, &ti)) return 0; @@ -761,482 +1391,518 @@ bag_decrypted_data_p (const void *plaintext, size_t length) static int -parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, - const unsigned char *buffer, size_t length, - int startoffset, size_t *r_consumed) +parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { - struct tag_info ti; - const unsigned char *p = buffer; - const unsigned char *p_start = buffer; - size_t n = length; + gpg_error_t err = 0; const char *where; - char salt[20]; + const unsigned char *oid; + size_t oidlen; + const unsigned char *data; + size_t datalen; + int intval; + char salt[32]; size_t saltlen; char iv[16]; unsigned int iter; unsigned char *plain = NULL; - unsigned char *cram_buffer = NULL; - size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_3des = 0; int is_pbes2 = 0; + int is_aes256 = 0; int keyelem_count; + int renewed_tlv = 0; + int loopcount; + unsigned int startlevel, startlevel2; + int digest_algo = GCRY_MD_SHA1; - where = "start"; - if (parse_tag (&p, &n, &ti)) + where = "bag.encryptedData"; + if (opt_verbose) + log_info ("processing %s\n", where); + + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; where = "bag.encryptedData.version"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 0) + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - p++; n--; - if (parse_tag (&p, &n, &ti)) + if (intval) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; where = "bag.encryptedData.data"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) - || memcmp (p, oid_data, DIM(oid_data))) + if (oidlen != DIM(oid_data) || memcmp (oid, oid_data, DIM(oid_data))) goto bailout; - p += DIM(oid_data); - n -= DIM(oid_data); where = "bag.encryptedData.keyinfo"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pbeWithSHAAnd40BitRC2_CBC) - && !memcmp (p, oid_pbeWithSHAAnd40BitRC2_CBC, + if (oidlen == DIM(oid_pbeWithSHAAnd40BitRC2_CBC) + && !memcmp (oid, oid_pbeWithSHAAnd40BitRC2_CBC, DIM(oid_pbeWithSHAAnd40BitRC2_CBC))) + ; + else if (oidlen == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) + && !memcmp (oid, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, + DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) + is_3des = 1; + else if (oidlen == DIM(oid_pkcs5PBES2) + && !memcmp (oid, oid_pkcs5PBES2, oidlen)) + is_pbes2 = 1; + else { - p += DIM(oid_pbeWithSHAAnd40BitRC2_CBC); - n -= DIM(oid_pbeWithSHAAnd40BitRC2_CBC); - } - else if (!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) - && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, - DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) - { - p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); - n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); - is_3des = 1; - } - else if (!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pkcs5PBES2) - && !memcmp (p, oid_pkcs5PBES2, ti.length)) - { - p += ti.length; - n -= ti.length; - is_pbes2 = 1; + err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); + goto bailout; } - else - goto bailout; + /*FIXME: This code is duplicated in parse_shrouded_key_bag. */ if (is_pbes2) { + size_t parmlen; /* Remaining length of the parameter sequence. */ + where = "pkcs5PBES2-params"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pkcs5PBKDF2) - && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) - goto bailout; /* Not PBKDF2. */ - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (oidlen != DIM(oid_pkcs5PBKDF2) + || memcmp (oid, oid_pkcs5PBKDF2, oidlen)) + { + err = gpg_error (GPG_ERR_INV_BER); /* Not PBKDF2. */ + goto bailout; + } + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + parmlen = tlv->ti.length; + + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OCTET_STRING - && ti.length >= 8 && ti.length < sizeof salt)) - goto bailout; /* No salt or unsupported length. */ - saltlen = ti.length; - memcpy (salt, p, saltlen); - p += saltlen; - n -= saltlen; + parmlen -= tlv->ti.length + tlv->ti.nhdr; + if (datalen < 8 || datalen > sizeof salt) + { + log_info ("bad length of salt (%zu)\n", datalen); + err = gpg_error (GPG_ERR_INV_LENGTH); + goto bailout; + } + saltlen = datalen; + memcpy (salt, data, saltlen); - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) - goto bailout; /* No valid iteration count. */ - for (iter=0; ti.length; ti.length--) + if ((err = tlv_expect_integer (tlv, &intval))) + goto bailout; + parmlen -= tlv->ti.length + tlv->ti.nhdr; + if (!intval) /* Not a valid iteration count. */ { - iter <<= 8; - iter |= (*p++) & 0xff; - n--; + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; } - /* Note: We don't support the optional parameters but assume - that the algorithmIdentifier follows. */ - if (parse_tag (&p, &n, &ti)) + iter = intval; + + if (parmlen > 2) /* There is the optional prf. */ + { + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + digest_algo = digest_algo_from_oid (oid, oidlen); + if (!digest_algo) + { + gpgrt_log_printhex (oid, oidlen, "kdf digest algo:"); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto bailout; + } + if (opt_verbose > 1) + log_debug ("kdf digest algo = %d\n", digest_algo); + + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } + } + else + digest_algo = GCRY_MD_SHA1; + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_aes128_CBC) - && !memcmp (p, oid_aes128_CBC, ti.length))) - goto bailout; /* Not AES-128. */ - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) - goto bailout; /* Bad IV. */ - memcpy (iv, p, sizeof iv); - p += sizeof iv; - n -= sizeof iv; + + if (oidlen == DIM(oid_aes128_CBC) + && !memcmp (oid, oid_aes128_CBC, oidlen)) + ; + else if (oidlen == DIM(oid_aes256_CBC) + && !memcmp (oid, oid_aes256_CBC, oidlen)) + is_aes256 = 1; + else + { + gpgrt_log_printhex (oid, oidlen, "cipher algo:"); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto bailout; + } + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) + goto bailout; + if (datalen != sizeof iv) + { + err = gpg_error (GPG_ERR_INV_LENGTH); + goto bailout; /* Bad IV. */ + } + memcpy (iv, data, datalen); } else { where = "rc2or3des-params"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING - || ti.length < 8 || ti.length > 20 ) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - saltlen = ti.length; - memcpy (salt, p, saltlen); - p += saltlen; - n -= saltlen; - if (parse_tag (&p, &n, &ti)) + if (datalen < 8 || datalen > 20) + { + log_info ("bad length of salt (%zu) for 3DES\n", datalen); + err = gpg_error (GPG_ERR_INV_LENGTH); + goto bailout; + } + saltlen = datalen; + memcpy (salt, data, saltlen); + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - for (iter=0; ti.length; ti.length--) + if (!intval) { - iter <<= 8; - iter |= (*p++) & 0xff; - n--; + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; } + iter = intval; } where = "rc2or3desoraes-ciphertext"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - consumed = p - p_start; - if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed && ti.ndef) - { - /* Mozilla exported certs now come with single byte chunks of - octet strings. (Mozilla Firefox 1.0.4). Arghh. */ - where = "cram-rc2or3des-ciphertext"; - cram_buffer = cram_octet_string ( p, &n, &consumed); - if (!cram_buffer) - goto bailout; - p = p_start = cram_buffer; - if (r_consumed) - *r_consumed = consumed; - r_consumed = NULL; /* Donot update that value on return. */ - ti.length = n; - } - else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.is_constructed) - { - where = "octets-rc2or3des-ciphertext"; - n = ti.length; - cram_buffer = cram_octet_string ( p, &n, &consumed); - if (!cram_buffer) - goto bailout; - p = p_start = cram_buffer; - if (r_consumed) - *r_consumed = consumed; - r_consumed = NULL; /* Do not update that value on return. */ - ti.length = n; - } - else if (ti.class == CLASS_CONTEXT && ti.tag == 0 && ti.length ) - ; - else + if (tlv_expect_object (tlv, CLASS_CONTEXT, 0, &data, &datalen)) goto bailout; if (opt_verbose) - log_info ("%lu bytes of %s encrypted text\n",ti.length, - is_pbes2?"AES128":is_3des?"3DES":"RC2"); + log_info ("%zu bytes of %s encrypted text\n", datalen, + is_pbes2?(is_aes256?"AES256":"AES128"):is_3des?"3DES":"RC2"); - plain = gcry_malloc_secure (ti.length); + plain = gcry_malloc_secure (datalen); if (!plain) { + err = gpg_error_from_syserror (); log_error ("error allocating decryption buffer\n"); goto bailout; } - decrypt_block (p, plain, ti.length, salt, saltlen, iter, + datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter, iv, is_pbes2?16:0, ctx->password, - is_pbes2 ? GCRY_CIPHER_AES128 : + is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) : is_3des ? GCRY_CIPHER_3DES : GCRY_CIPHER_RFC2268_40, + digest_algo, bag_decrypted_data_p); - n = ti.length; - startoffset = 0; - p_start = p = plain; + if (!datalen) + { + err = gpg_error (GPG_ERR_DECRYPT_FAILED); + ctx->badpass = 1; /* This is the most likley reason. */ + goto bailout; + } - where = "outer.outer.seq"; - if (parse_tag (&p, &n, &ti)) + /* We do not need the TLV anymore and allocated a new one. */ + where = "bag.encryptedData.decrypted-text"; + tlv = tlv_new (plain, datalen); + if (!tlv) { - ctx->badpass = 1; + err = gpg_error_from_syserror (); goto bailout; } - if (ti.class || ti.tag != TAG_SEQUENCE) + renewed_tlv = 1; + + if (tlv_next (tlv)) { ctx->badpass = 1; goto bailout; } - - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_sequence (tlv)) { ctx->badpass = 1; goto bailout; } /* Loop over all certificates inside the bag. */ - while (n) + loopcount = 0; + startlevel = tlv_level (tlv); + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { int iscrlbag = 0; int iskeybag = 0; + loopcount++; where = "certbag.nextcert"; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - where = "certbag.objectidentifier"; - if (parse_tag (&p, &n, &ti)) + where = "certbag.oid"; + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OBJECT_ID) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if ( ti.length == DIM(oid_pkcs_12_CertBag) - && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) - { - p += DIM(oid_pkcs_12_CertBag); - n -= DIM(oid_pkcs_12_CertBag); - } - else if ( ti.length == DIM(oid_pkcs_12_CrlBag) - && !memcmp (p, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag))) - { - p += DIM(oid_pkcs_12_CrlBag); - n -= DIM(oid_pkcs_12_CrlBag); - iscrlbag = 1; - } - else if ( ti.length == DIM(oid_pkcs_12_keyBag) - && !memcmp (p, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag))) + if (oidlen == DIM(oid_pkcs_12_CertBag) + && !memcmp (oid, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) + ; + else if (oidlen == DIM(oid_pkcs_12_CrlBag) + && !memcmp (oid, oid_pkcs_12_CrlBag, DIM(oid_pkcs_12_CrlBag))) + iscrlbag = 1; + else if (oidlen == DIM(oid_pkcs_12_keyBag) + && !memcmp (oid, oid_pkcs_12_keyBag, DIM(oid_pkcs_12_keyBag))) { /* The TrustedMIME plugin for MS Outlook started to create files with just one outer 3DES encrypted container and inside the certificates as well as the key. */ - p += DIM(oid_pkcs_12_keyBag); - n -= DIM(oid_pkcs_12_keyBag); iskeybag = 1; } else - goto bailout; + { + gpgrt_log_printhex (oid, oidlen, "cert bag type OID:"); + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + goto bailout; + } where = "certbag.before.certheader"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; + if (iscrlbag) { log_info ("skipping unsupported crlBag\n"); - p += ti.length; - n -= ti.length; } else if (iskeybag && ctx->privatekey) { log_info ("one keyBag already processed; skipping this one\n"); - p += ti.length; - n -= ti.length; } else if (iskeybag) { - int len; - if (opt_verbose) log_info ("processing simple keyBag\n"); - /* Fixme: This code is duplicated from parse_bag_data. */ - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER - || ti.length != 1 || *p) + if (tlv_expect_sequence (tlv)) goto bailout; - p++; n--; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + + if (tlv_next (tlv)) goto bailout; - len = ti.length; - if (parse_tag (&p, &n, &ti)) + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - if (len < ti.nhdr) + if (intval) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - len -= ti.nhdr; - if (ti.class || ti.tag != TAG_OBJECT_ID - || ti.length != DIM(oid_rsaEncryption) - || memcmp (p, oid_rsaEncryption, - DIM(oid_rsaEncryption))) + if (tlv_expect_sequence (tlv)) goto bailout; - p += DIM (oid_rsaEncryption); - n -= DIM (oid_rsaEncryption); - if (len < ti.length) + + if (tlv_next (tlv)) goto bailout; - len -= ti.length; - if (n < len) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - p += len; - n -= len; - if ( parse_tag (&p, &n, &ti) - || ti.class || ti.tag != TAG_OCTET_STRING) + if (oidlen != DIM(oid_rsaEncryption) + || memcmp (oid, oid_rsaEncryption, oidlen)) + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto bailout; + } + + /* We ignore the next octet string. */ + if (tlv_next (tlv)) goto bailout; - if ( parse_tag (&p, &n, &ti) - || ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - len = ti.length; - log_assert (!ctx->privatekey); + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + + if (ctx->privatekey) + { + err = gpg_error (GPG_ERR_DUP_VALUE); + log_error ("a private key has already been received\n"); + goto bailout; + } ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey); if (!ctx->privatekey) { + err = gpg_error_from_syserror (); log_error ("error allocating private key element array\n"); goto bailout; } - keyelem_count = 0; where = "reading.keybag.key-parameters"; - for (keyelem_count = 0; len && keyelem_count < 9;) + keyelem_count = 0; + startlevel2 = tlv_level (tlv); + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel2) { - if ( parse_tag (&p, &n, &ti) - || ti.class || ti.tag != TAG_INTEGER) - goto bailout; - if (len < ti.nhdr) - goto bailout; - len -= ti.nhdr; - if (len < ti.length) - goto bailout; - len -= ti.length; - if (!keyelem_count && ti.length == 1 && !*p) - ; /* ignore the very first one if it is a 0 */ - else + if (keyelem_count >= 9) + { + err = gpg_error (GPG_ERR_TOO_MANY); + goto bailout; + } + + err = tlv_expect_mpinteger (tlv, !keyelem_count, + ctx->privatekey+keyelem_count); + if (!keyelem_count && gpg_err_code (err) == GPG_ERR_FALSE) + ; /* Ignore the first value iff it is zero. */ + else if (err) { - int rc; - - rc = gcry_mpi_scan (ctx->privatekey+keyelem_count, - GCRYMPI_FMT_USG, p, - ti.length, NULL); - if (rc) - { - log_error ("error parsing key parameter: %s\n", - gpg_strerror (rc)); - goto bailout; - } - keyelem_count++; + log_error ("error parsing RSA key parameter %d: %s\n", + keyelem_count, gpg_strerror (err)); + goto bailout; } - p += ti.length; - n -= ti.length; + if (opt_verbose > 1) + log_debug ("RSA key parameter %d found\n", keyelem_count); + keyelem_count++; } - if (len) + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; + err = 0; } else { if (opt_verbose) log_info ("processing certBag\n"); - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OBJECT_ID - || ti.length != DIM(oid_x509Certificate_for_pkcs_12) - || memcmp (p, oid_x509Certificate_for_pkcs_12, - DIM(oid_x509Certificate_for_pkcs_12))) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - p += DIM(oid_x509Certificate_for_pkcs_12); - n -= DIM(oid_x509Certificate_for_pkcs_12); + if (oidlen != DIM(oid_x509Certificate_for_pkcs_12) + || memcmp (oid, oid_x509Certificate_for_pkcs_12, + DIM(oid_x509Certificate_for_pkcs_12))) + { + err = gpg_error (GPG_ERR_UNSUPPORTED_CERT); + goto bailout; + } where = "certbag.before.octetstring"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (intval) + { + err = gpg_error (GPG_ERR_BAD_BER); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; /* Return the certificate. */ if (ctx->certcb) - ctx->certcb (ctx->certcbarg, p, ti.length); - - p += ti.length; - n -= ti.length; + ctx->certcb (ctx->certcbarg, data, datalen); } - /* Ugly hack to cope with the padding: Forget about the rest if - that is less or equal to the cipher's block length. We can - reasonable assume that all valid data will be longer than - just one block. */ - if (n <= (is_pbes2? 16:8)) - n = 0; - /* Skip the optional SET with the pkcs12 cert attributes. */ - if (n) + where = "bag.attribute_set"; + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) { - where = "bag.attributes"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (!ti.class && ti.tag == TAG_SEQUENCE) - ; /* No attributes. */ - else if (!ti.class && ti.tag == TAG_SET && !ti.ndef) - { /* The optional SET. */ - p += ti.length; - n -= ti.length; - if (n <= (is_pbes2?16:8)) - n = 0; - if (n && parse_tag (&p, &n, &ti)) - goto bailout; - } - else + err = tlv_expect_set (tlv); + if (err) goto bailout; + tlv_skip (tlv); + if (opt_verbose) + log_info ("skipping %s\n", where); } } + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) + { + if (!loopcount) /* The first while(tlv_next) failed. */ + ctx->badpass = 1; + goto bailout; + } + err = 0; - if (r_consumed) - *r_consumed = consumed; - gcry_free (plain); - gcry_free (cram_buffer); - return 0; - - bailout: - if (r_consumed) - *r_consumed = consumed; + leave: + if (renewed_tlv) + tlv_release (tlv); gcry_free (plain); - gcry_free (cram_buffer); - log_error ("encryptedData error at \"%s\", offset %u\n", - where, (unsigned int)((p - p_start)+startoffset)); if (ctx->badpass) { /* Note, that the following string might be used by other programs @@ -1244,7 +1910,18 @@ parse_bag_encrypted_data (struct p12_parse_ctx_s *ctx, translated or changed. */ log_error ("possibly bad passphrase given\n"); } - return -1; + return err; + + bailout: + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", + __func__, where, + tlv? tlv->stacklen : 0, + tlv? tlv->lastfunc : "", + tlv ? gpg_strerror (tlv->lasterr) : "init failed", + gpg_strerror (err)); + goto leave; } @@ -1257,13 +1934,15 @@ bag_data_p (const void *plaintext, size_t length) const unsigned char *p = plaintext; size_t n = length; -/* { */ -/* # warning debug code is enabled */ -/* FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); */ -/* if (!fp || fwrite (p, n, 1, fp) != 1) */ -/* exit (2); */ -/* fclose (fp); */ -/* } */ +#ifdef ENABLE_DER_STRUCT_DUMPING + { +# warning debug code is enabled + FILE *fp = fopen ("tmp-minip12-plain-key.der", "wb"); + if (!fp || fwrite (p, n, 1, fp) != 1) + exit (2); + fclose (fp); + } +#endif /*ENABLE_DER_STRUCT_DUMPING*/ if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) return 0; @@ -1276,363 +1955,478 @@ bag_data_p (const void *plaintext, size_t length) static gpg_error_t -parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, - const unsigned char *buffer, size_t length, - int startoffset, - size_t *r_consumed) +parse_shrouded_key_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { gpg_error_t err = 0; - struct tag_info ti; - const unsigned char *p = buffer; - const unsigned char *p_start = buffer; - size_t n = length; const char *where; + const unsigned char *oid; + size_t oidlen; + const unsigned char *data; + size_t datalen; + int intval; char salt[20]; size_t saltlen; char iv[16]; unsigned int iter; - int len; + struct tlv_ctx_s *saved_tlv = NULL; + int renewed_tlv = 0; /* True if the TLV must be released. */ unsigned char *plain = NULL; - unsigned char *cram_buffer = NULL; - size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ int is_pbes2 = 0; - int keyelem_count = 0; + int is_aes256 = 0; + int digest_algo = GCRY_MD_SHA1; where = "shrouded_key_bag"; - if (parse_tag (&p, &n, &ti)) + if (opt_verbose) + log_info ("processing %s\n", where); + + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + where = "shrouded_key_bag.cipherinfo"; + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class == 0 && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) - && !memcmp (p, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + + if (oidlen == DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC) + && !memcmp (oid, oid_pbeWithSHAAnd3_KeyTripleDES_CBC, DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC))) + ; /* Standard cipher. */ + else if (oidlen == DIM(oid_pkcs5PBES2) + && !memcmp (oid, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2))) + is_pbes2 = 1; + else { - p += DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); - n -= DIM(oid_pbeWithSHAAnd3_KeyTripleDES_CBC); - } - else if (ti.class == 0 && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pkcs5PBES2) - && !memcmp (p, oid_pkcs5PBES2, DIM(oid_pkcs5PBES2))) - { - p += DIM(oid_pkcs5PBES2); - n -= DIM(oid_pkcs5PBES2); - is_pbes2 = 1; + err = gpg_error (GPG_ERR_UNKNOWN_ALGORITHM); + goto bailout; } - else - goto bailout; if (is_pbes2) { + size_t parmlen; /* Remaining length of the parameter sequence. */ + where = "shrouded_key_bag.pkcs5PBES2-params"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) + goto bailout; + + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_sequence (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_pkcs5PBKDF2) - && !memcmp (p, oid_pkcs5PBKDF2, ti.length))) + if (!(oidlen == DIM(oid_pkcs5PBKDF2) + && !memcmp (oid, oid_pkcs5PBKDF2, oidlen))) goto bailout; /* Not PBKDF2. */ - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + parmlen = tlv->ti.length; + + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OCTET_STRING - && ti.length >= 8 && ti.length < sizeof salt)) - goto bailout; /* No salt or unsupported length. */ - saltlen = ti.length; - memcpy (salt, p, saltlen); - p += saltlen; - n -= saltlen; + parmlen -= tlv->ti.length + tlv->ti.nhdr; + if (datalen < 8 || datalen > sizeof salt) + { + log_info ("bad length of salt (%zu) for AES\n", datalen); + err = gpg_error (GPG_ERR_INV_LENGTH); + goto bailout; + } + saltlen = datalen; + memcpy (salt, data, saltlen); - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) + goto bailout; + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - if (!(!ti.class && ti.tag == TAG_INTEGER && ti.length)) - goto bailout; /* No valid iteration count. */ - for (iter=0; ti.length; ti.length--) + parmlen -= tlv->ti.length + tlv->ti.nhdr; + if (!intval) /* Not a valid iteration count. */ + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; + } + iter = intval; + + if (parmlen > 2) /* There is the optional prf. */ { - iter <<= 8; - iter |= (*p++) & 0xff; - n--; + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) + goto bailout; + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + digest_algo = digest_algo_from_oid (oid, oidlen); + if (!digest_algo) + { + gpgrt_log_printhex (oid, oidlen, "kdf digest algo:"); + err = gpg_error (GPG_ERR_DIGEST_ALGO); + goto bailout; + } + if (opt_verbose > 1) + log_debug ("kdf digest algo = %d\n", digest_algo); + + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } } - /* Note: We don't support the optional parameters but assume - that the algorithmIdentifier follows. */ - if (parse_tag (&p, &n, &ti)) + else + digest_algo = GCRY_MD_SHA1; + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_sequence (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (oidlen == DIM(oid_aes128_CBC) + && !memcmp (oid, oid_aes128_CBC, oidlen)) + ; + else if (oidlen == DIM(oid_aes256_CBC) + && !memcmp (oid, oid_aes256_CBC, oidlen)) + is_aes256 = 1; + else + { + gpgrt_log_printhex (oid, oidlen, "cipher is:"); + err = gpg_error (GPG_ERR_CIPHER_ALGO); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OBJECT_ID - && ti.length == DIM(oid_aes128_CBC) - && !memcmp (p, oid_aes128_CBC, ti.length))) - goto bailout; /* Not AES-128. */ - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - if (!(!ti.class && ti.tag == TAG_OCTET_STRING && ti.length == sizeof iv)) + if (datalen != sizeof iv) goto bailout; /* Bad IV. */ - memcpy (iv, p, sizeof iv); - p += sizeof iv; - n -= sizeof iv; + memcpy (iv, data, datalen); } else { where = "shrouded_key_bag.3des-params"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING - || ti.length < 8 || ti.length > 20) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - saltlen = ti.length; - memcpy (salt, p, saltlen); - p += saltlen; - n -= saltlen; - if (parse_tag (&p, &n, &ti)) + if (datalen < 8 || datalen > 20) + { + log_info ("bad length of salt (%zu) for 3DES\n", datalen); + err = gpg_error (GPG_ERR_INV_LENGTH); + goto bailout; + } + saltlen = datalen; + memcpy (salt, data, saltlen); + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_INTEGER || !ti.length ) + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - for (iter=0; ti.length; ti.length--) + if (!intval) { - iter <<= 8; - iter |= (*p++) & 0xff; - n--; + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; } + iter = intval; } where = "shrouded_key_bag.3desoraes-ciphertext"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING || !ti.length ) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; if (opt_verbose) - log_info ("%lu bytes of %s encrypted text\n", - ti.length, is_pbes2? "AES128":"3DES"); + log_info ("%zu bytes of %s encrypted text\n", + datalen, is_pbes2? (is_aes256?"AES256":"AES128"):"3DES"); + + plain = gcry_malloc_secure (datalen); - plain = gcry_malloc_secure (ti.length); if (!plain) { + err = gpg_error_from_syserror (); log_error ("error allocating decryption buffer\n"); goto bailout; } - consumed += p - p_start + ti.length; - decrypt_block (p, plain, ti.length, salt, saltlen, iter, + datalen = decrypt_block (data, plain, datalen, salt, saltlen, iter, iv, is_pbes2? 16:0, ctx->password, - is_pbes2? GCRY_CIPHER_AES128 : GCRY_CIPHER_3DES, + is_pbes2 ? (is_aes256?GCRY_CIPHER_AES256:GCRY_CIPHER_AES128) + : GCRY_CIPHER_3DES, + digest_algo, bag_data_p); - n = ti.length; - startoffset = 0; - p_start = p = plain; + if (!datalen) + { + err = gpg_error (GPG_ERR_DECRYPT_FAILED); + ctx->badpass = 1; + goto bailout; + } + /* We do not need the TLV anymore and allocated a new one. */ where = "shrouded_key_bag.decrypted-text"; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) - goto bailout; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER - || ti.length != 1 || *p) - goto bailout; - p++; n--; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + saved_tlv = tlv; + tlv = tlv_new (plain, datalen); + if (!tlv) + { + err = gpg_error_from_syserror (); + goto bailout; + } + renewed_tlv = 1; + if (opt_verbose > 1) + log_debug ("new parser context\n"); + + if (tlv_next (tlv)) + { + ctx->badpass = 1; + goto bailout; + } + if (tlv_expect_sequence (tlv)) + { + ctx->badpass = 1; + goto bailout; + } + + if (tlv_next (tlv)) + { + ctx->badpass = 1; + goto bailout; + } + if ((err = tlv_expect_integer (tlv, &intval))) + { + ctx->badpass = 1; + goto bailout; + } + if (intval) + { + ctx->badpass = 1; + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - len = ti.length; - if (parse_tag (&p, &n, &ti)) + if (tlv_expect_sequence (tlv)) goto bailout; - if (len < ti.nhdr) + + if (tlv_next (tlv)) goto bailout; - len -= ti.nhdr; - if (ti.class || ti.tag != TAG_OBJECT_ID) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - /* gpgrt_log_printhex (p, ti.length, "OID:"); */ - if (ti.length == DIM(oid_rsaEncryption) - && !memcmp (p, oid_rsaEncryption, DIM(oid_rsaEncryption))) + if (oidlen == DIM(oid_rsaEncryption) + && !memcmp (oid, oid_rsaEncryption, oidlen)) { - p += DIM (oid_rsaEncryption); - n -= DIM (oid_rsaEncryption); + if (opt_verbose > 1) + log_debug ("RSA parameters\n"); + + if (tlv_peek_null (tlv)) + { + /* Read the optional Null tag. */ + if (tlv_next (tlv)) + goto bailout; + } } - else if (ti.length == DIM(oid_pcPublicKey) - && !memcmp (p, oid_pcPublicKey, DIM(oid_pcPublicKey))) + else if (oidlen == DIM(oid_pcPublicKey) + && !memcmp (oid, oid_pcPublicKey, oidlen)) { /* See RFC-5915 for the format. */ - p += DIM (oid_pcPublicKey); - n -= DIM (oid_pcPublicKey); - if (len < ti.length) - goto bailout; - len -= ti.length; - if (n < len) - goto bailout; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - /* gpgrt_log_debug ("ti=%d/%lu len=%lu\n",ti.class,ti.tag,ti.length); */ - if (len < ti.nhdr) - goto bailout; - len -= ti.nhdr; - if (ti.class || ti.tag != TAG_OBJECT_ID) + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; ksba_free (ctx->curve); - ctx->curve = ksba_oid_to_str (p, ti.length); + ctx->curve = ksba_oid_to_str (oid, oidlen); if (!ctx->curve) - goto bailout; - /* log_debug ("OID of curve is: %s\n", curve); */ - p += ti.length; - n -= ti.length; + { + err = gpg_error (GPG_ERR_INV_OID_STRING); + goto bailout; + } + if (opt_verbose > 1) + log_debug ("OID of curve is: %s\n", ctx->curve); } - else - goto bailout; - if (len < ti.length) + else /* Unknown key format */ + { + gpgrt_log_printhex (oid, oidlen, "key format OID:"); + err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); + goto bailout; + } + + /* An octet string to encapsulate the key elements. */ + if (tlv_next (tlv)) goto bailout; - len -= ti.length; - if (n < len) + if (tlv_expect_octet_string (tlv, 1, &data, &datalen)) goto bailout; - p += len; - n -= len; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) + + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - len = ti.length; if (ctx->privatekey) { - log_error ("a key has already been received\n"); + err = gpg_error (GPG_ERR_DUP_VALUE); + log_error ("a private key has already been received\n"); goto bailout; } ctx->privatekey = gcry_calloc (10, sizeof *ctx->privatekey); if (!ctx->privatekey) { - + err = gpg_error_from_syserror (); log_error ("error allocating privatekey element array\n"); goto bailout; } - keyelem_count = 0; where = "shrouded_key_bag.reading.key-parameters"; if (ctx->curve) /* ECC case. */ { - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) - goto bailout; - if (len < ti.nhdr) + if (tlv_next (tlv)) goto bailout; - len -= ti.nhdr; - if (len < ti.length) + if ((err = tlv_expect_integer (tlv, &intval))) goto bailout; - len -= ti.length; - if (ti.length != 1 && *p != 1) + if (intval != 1) { + err = gpg_error (GPG_ERR_INV_VALUE); log_error ("error parsing private ecPublicKey parameter: %s\n", "bad version"); goto bailout; } - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_OCTET_STRING) - goto bailout; - if (len < ti.nhdr) + + if (tlv_next (tlv)) goto bailout; - len -= ti.nhdr; - if (len < ti.length) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; - len -= ti.length; - /* log_printhex (p, ti.length, "ecc q="); */ + if (opt_verbose > 1) + log_printhex (data, datalen, "ecc q="); err = gcry_mpi_scan (ctx->privatekey, GCRYMPI_FMT_USG, - p, ti.length, NULL); + data, datalen, NULL); if (err) { log_error ("error parsing key parameter: %s\n", gpg_strerror (err)); goto bailout; } - p += ti.length; - n -= ti.length; - - len = 0; /* Skip the rest. */ } else /* RSA case */ { - for (keyelem_count=0; len && keyelem_count < 9;) + int keyelem_count = 0; + int firstparam = 1; + unsigned int startlevel = tlv_level (tlv); + + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { - if (parse_tag (&p, &n, &ti) || ti.class || ti.tag != TAG_INTEGER) - goto bailout; - if (len < ti.nhdr) - goto bailout; - len -= ti.nhdr; - if (len < ti.length) - goto bailout; - len -= ti.length; - if (!keyelem_count && ti.length == 1 && !*p) - ; /* ignore the very first one if it is a 0 */ + if (keyelem_count >= 9) + { + err = gpg_error (GPG_ERR_TOO_MANY); + goto bailout; + } + + err = tlv_expect_mpinteger (tlv, firstparam, + ctx->privatekey+keyelem_count); + if (firstparam && gpg_err_code (err) == GPG_ERR_FALSE) + ; /* Ignore the first value iff it is zero. */ + else if (err) + { + log_error ("error parsing RSA key parameter %d: %s\n", + keyelem_count, gpg_strerror (err)); + goto bailout; + } else { - err = gcry_mpi_scan (ctx->privatekey+keyelem_count, - GCRYMPI_FMT_USG, p, ti.length, NULL); - if (err) - { - log_error ("error parsing key parameter: %s\n", - gpg_strerror (err)); - goto bailout; - } + if (opt_verbose > 1) + log_debug ("RSA key parameter %d found\n", keyelem_count); keyelem_count++; } - p += ti.length; - n -= ti.length; + firstparam = 0; } + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) + goto bailout; + err = 0; } - if (len) - goto bailout; - goto leave; + if (opt_verbose > 1) + log_debug ("restoring parser context\n"); + tlv_release (tlv); + renewed_tlv = 0; + tlv = saved_tlv; + + where = "shrouded_key_bag.attribute_set"; + /* Check for an optional set of attributes. */ + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + { + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_set (tlv); + if (err) + goto bailout; + tlv_skip (tlv); + if (opt_verbose) + log_info ("skipping %s\n", where); + } - bailout: - gcry_free (plain); - log_error ("data error at \"%s\", offset %zu\n", - where, (size_t)((p - p_start) + startoffset)); - if (!err) - err = gpg_error (GPG_ERR_GENERAL); leave: - gcry_free (cram_buffer); - if (r_consumed) - *r_consumed = consumed; + gcry_free (plain); + if (renewed_tlv) + { + tlv_release (tlv); + if (opt_verbose > 1) + log_debug ("parser context released\n"); + } return err; + + bailout: + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + log_error ("%s(%s): lvl=%d (%s): %s - %s\n", + __func__, where, + tlv? tlv->stacklen : 0, + tlv? tlv->lastfunc : "", + tlv ? gpg_strerror (tlv->lasterr) : "init failed", + gpg_strerror (err)); + goto leave; } static gpg_error_t -parse_cert_bag (struct p12_parse_ctx_s *ctx, - const unsigned char *buffer, size_t length, - int startoffset, - size_t *r_consumed) +parse_cert_bag (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { gpg_error_t err = 0; - struct tag_info ti; - const unsigned char *p = buffer; - const unsigned char *p_start = buffer; - size_t n = length; const char *where; - size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ + int intval; + const unsigned char *oid; + size_t oidlen; + const unsigned char *data; + size_t datalen; if (opt_verbose) log_info ("processing certBag\n"); @@ -1643,181 +2437,190 @@ parse_cert_bag (struct p12_parse_ctx_s *ctx, * OBJECT IDENTIFIER pkcs-12-certBag */ where = "certbag.before.certheader"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval)) goto bailout; - if (parse_tag (&p, &n, &ti)) + if (intval) + { + err = gpg_error (GPG_ERR_INV_VALUE); + goto bailout; + } + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (ti.class || ti.tag != TAG_OBJECT_ID - || ti.length != DIM(oid_x509Certificate_for_pkcs_12) - || memcmp (p, oid_x509Certificate_for_pkcs_12, - DIM(oid_x509Certificate_for_pkcs_12))) + if (oidlen != DIM(oid_x509Certificate_for_pkcs_12) + || memcmp (oid, oid_x509Certificate_for_pkcs_12, oidlen)) goto bailout; - p += DIM(oid_x509Certificate_for_pkcs_12); - n -= DIM(oid_x509Certificate_for_pkcs_12); + /* Expect: * [0] * OCTET STRING encapsulates -- the certificates */ where = "certbag.before.octetstring"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING || ti.ndef) + if (tlv_expect_octet_string (tlv, 0, &data, &datalen)) goto bailout; /* Return the certificate from the octet string. */ if (ctx->certcb) - ctx->certcb (ctx->certcbarg, p, ti.length); - - p += ti.length; - n -= ti.length; - - if (!n) - goto leave; /* ready. */ + ctx->certcb (ctx->certcbarg, data, datalen); - /* Expect: + /* Expect optional: * SET * SEQUENCE -- we actually ignore this. */ where = "certbag.attribute_set"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (!ti.class && ti.tag == TAG_SET && !ti.ndef) - { /* Comsume the optional SET. */ - p += ti.length; - n -= ti.length; - if (parse_tag (&p, &n, &ti)) + /* Check for an optional set of attributes. */ + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_SET)) + { + if (tlv_next (tlv)) goto bailout; + err = tlv_expect_set (tlv); + if (err) + goto bailout; + tlv_skip (tlv); + if (opt_verbose) + log_info ("skipping %s\n", where); } - goto leave; - - bailout: - log_error ( "data error at \"%s\", offset %u\n", - where, (unsigned int)((p - p_start) + startoffset)); - err = gpg_error (GPG_ERR_GENERAL); leave: - if (r_consumed) - *r_consumed = consumed; return err; + + bailout: + log_error ("%s(%s): lvl=%u (%s): %s - %s\n", + __func__, where, + tlv? tlv->stacklen : 0, + tlv? tlv->lastfunc : "", + tlv ? gpg_strerror (tlv->lasterr) : "init failed", + gpg_strerror (err)); + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + goto leave; } static gpg_error_t -parse_bag_data (struct p12_parse_ctx_s *ctx, - const unsigned char *buffer, size_t length, int startoffset, - size_t *r_consumed) +parse_bag_data (struct p12_parse_ctx_s *ctx, struct tlv_ctx_s *tlv) { gpg_error_t err = 0; - struct tag_info ti; - const unsigned char *p = buffer; - const unsigned char *p_start = buffer; - size_t n = length; const char *where; - unsigned char *cram_buffer = NULL; - size_t consumed = 0; /* Number of bytes consumed from the original buffer. */ + int intval; + const unsigned char *oid; + size_t oidlen; + unsigned int startlevel; + + if (opt_verbose) + log_info ("processing bag data\n"); /* Expect: * [0] * OCTET STRING, encapsulates */ where = "data"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class || ti.tag != TAG_OCTET_STRING) + if (tlv_expect_octet_string (tlv, 1, NULL, NULL)) goto bailout; - - consumed = p - p_start; - if (ti.is_constructed && ti.ndef) + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) { - /* Mozilla exported certs now come with single byte chunks of - octet strings. (Mozilla Firefox 1.0.4). Arghh. */ - where = "data.cram_os"; - cram_buffer = cram_octet_string ( p, &n, &consumed); - if (!cram_buffer) + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_octet_string (tlv, 1, NULL, NULL); + if (err) goto bailout; - p = p_start = cram_buffer; - if (r_consumed) - *r_consumed = consumed; - r_consumed = NULL; /* Ugly hack to not update that value on return. */ } /* Expect: * SEQUENCE - * SEQUENCE */ - where = "data.2seqs"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + where = "data.outerseqs"; + if (tlv_next (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - /* Expect: - * OBJECT IDENTIFIER - */ - where = "data.oid"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (ti.class || ti.tag != TAG_OBJECT_ID) - goto bailout; - - /* Now divert to the actual parser. */ - if (ti.length == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) - && !memcmp (p, oid_pkcs_12_pkcs_8ShroudedKeyBag, - DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) + startlevel = tlv_level (tlv); + dump_tlv_ctx ("data.outerseqs", "beginloop", tlv); + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { - p += DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); - n -= DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag); - - if (parse_shrouded_key_bag (ctx, p, n, - startoffset + (p - p_start), r_consumed)) + /* Expect: + * SEQUENCE + */ + where = "data.innerseqs"; + if (tlv_expect_sequence (tlv)) goto bailout; - } - else if ( ti.length == DIM(oid_pkcs_12_CertBag) - && !memcmp (p, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) - { - p += DIM(oid_pkcs_12_CertBag); - n -= DIM(oid_pkcs_12_CertBag); - if (parse_cert_bag (ctx, p, n, - startoffset + (p - p_start), r_consumed)) + /* Expect: + * OBJECT IDENTIFIER + */ + where = "data.oid"; + if (tlv_next (tlv)) goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) + goto bailout; + + /* Divert to the actual parser. */ + if (oidlen == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) + && !memcmp (oid, oid_pkcs_12_pkcs_8ShroudedKeyBag, + DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) + { + if ((err = parse_shrouded_key_bag (ctx, tlv))) + goto bailout; + } + else if (oidlen == DIM(oid_pkcs_12_CertBag) + && !memcmp (oid, oid_pkcs_12_CertBag, DIM(oid_pkcs_12_CertBag))) + { + if ((err = parse_cert_bag (ctx, tlv))) + goto bailout; + } + else + { + tlv_skip (tlv); + log_info ("unknown inner data type - skipped\n"); + } } - else + dump_tlv_ctx ("data.outerseqs", "endloop", tlv); + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) goto bailout; - - goto leave; - - bailout: - log_error ( "data error at \"%s\", offset %u\n", - where, (unsigned int)((p - p_start) + startoffset)); - err = gpg_error (GPG_ERR_GENERAL); + err = 0; leave: - gcry_free (cram_buffer); - if (r_consumed) /* Store the number of consumed bytes unless already done. */ - *r_consumed = consumed; return err; + + bailout: + if (!err) + err = gpg_error (GPG_ERR_GENERAL); + log_error ("%s(%s): lvl=%d (%s): %s - %s\n", + __func__, where, + tlv? tlv->stacklen : 0, + tlv? tlv->lastfunc : "", + tlv ? gpg_strerror (tlv->lasterr) : "init failed", + gpg_strerror (err)); + goto leave; } @@ -1825,7 +2628,7 @@ parse_bag_data (struct p12_parse_ctx_s *ctx, secret key parameters. This is a very limited implementation in that it is only able to look for 3DES encoded encryptedData and tries to extract the first private key object it finds. In case of - an error NULL is returned. CERTCB and CERRTCBARG are used to pass + an error NULL is returned. CERTCB and CERTCBARG are used to pass X.509 certificates back to the caller. If R_CURVE is not NULL and an ECC key was found the OID of the curve is stored there. */ gcry_mpi_t * @@ -1833,16 +2636,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, void (*certcb)(void*, const unsigned char*, size_t), void *certcbarg, int *r_badpass, char **r_curve) { - struct tag_info ti; - const unsigned char *p = buffer; - const unsigned char *p_start = buffer; - size_t n = length; - const char *where; - int bagseqlength, len; - int bagseqndef, lenndef; - unsigned char *cram_buffer = NULL; - size_t consumed; + gpg_error_t err = 0; + const char *where = ""; + struct tlv_ctx_s *tlv; struct p12_parse_ctx_s ctx = { NULL }; + const unsigned char *oid; + size_t oidlen; + int intval; + unsigned int startlevel; *r_badpass = 0; @@ -1850,146 +2651,113 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, ctx.certcbarg = certcbarg; ctx.password = pw; + tlv = tlv_new (buffer, length); + if (!tlv) + { + err = gpg_error_from_syserror (); + goto bailout; + } where = "pfx"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; where = "pfxVersion"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_INTEGER || ti.length != 1 || *p != 3) + if (tlv_expect_integer (tlv, &intval) || intval != 3) goto bailout; - p++; n--; where = "authSave"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (ti.tag != TAG_OBJECT_ID || ti.length != DIM(oid_data) - || memcmp (p, oid_data, DIM(oid_data))) + if (oidlen != DIM(oid_data) || memcmp (oid, oid_data, DIM(oid_data))) goto bailout; - p += DIM(oid_data); - n -= DIM(oid_data); - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_CONTEXT || ti.tag) + if (tlv_expect_context_tag (tlv, &intval) || intval != 0 ) goto bailout; - if (parse_tag (&p, &n, &ti)) + + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_OCTET_STRING) + if (tlv_expect_octet_string (tlv, 1, NULL, NULL)) goto bailout; - if (ti.is_constructed && ti.ndef) + if (tlv_peek (tlv, CLASS_UNIVERSAL, TAG_OCTET_STRING)) { - /* Mozilla exported certs now come with single byte chunks of - octet strings. (Mozilla Firefox 1.0.4). Arghh. */ - where = "cram-bags"; - cram_buffer = cram_octet_string ( p, &n, NULL); - if (!cram_buffer) + if (tlv_next (tlv)) + goto bailout; + err = tlv_expect_octet_string (tlv, 1, NULL, NULL); + if (err) goto bailout; - p = p_start = cram_buffer; } where = "bags"; - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) goto bailout; - if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE) + if (tlv_expect_sequence (tlv)) goto bailout; - bagseqndef = ti.ndef; - bagseqlength = ti.length; - while (bagseqlength || bagseqndef) + + startlevel = tlv_level (tlv); + dump_tlv_ctx ("bags", "beginloop", tlv); + while (!(err = tlv_next (tlv)) && tlv_level (tlv) == startlevel) { - /* log_debug ("p12_parse: at offset %ld\n", (p - p_start)); */ where = "bag-sequence"; - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (bagseqndef && ti.class == CLASS_UNIVERSAL - && !ti.tag && !ti.is_constructed) - break; /* Ready */ - if (ti.class != CLASS_UNIVERSAL || ti.tag != TAG_SEQUENCE) + dump_tlv_ctx (where, NULL, tlv); + if (tlv_expect_sequence (tlv)) goto bailout; - if (!bagseqndef) - { - if (bagseqlength < ti.nhdr) - goto bailout; - bagseqlength -= ti.nhdr; - if (bagseqlength < ti.length) - goto bailout; - bagseqlength -= ti.length; - } - lenndef = ti.ndef; - len = ti.length; - - if (parse_tag (&p, &n, &ti)) + if (tlv_next (tlv)) + goto bailout; + if (tlv_expect_object_id (tlv, &oid, &oidlen)) goto bailout; - if (lenndef) - len = ti.nhdr; - else - len -= ti.nhdr; - if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_encryptedData) - && !memcmp (p, oid_encryptedData, DIM(oid_encryptedData))) + if (oidlen == DIM(oid_encryptedData) + && !memcmp (oid, oid_encryptedData, DIM(oid_encryptedData))) { - - p += DIM(oid_encryptedData); - n -= DIM(oid_encryptedData); - if (!lenndef) - len -= DIM(oid_encryptedData); where = "bag.encryptedData"; - consumed = 0; - if (parse_bag_encrypted_data (&ctx, p, n, (p - p_start), &consumed)) - { - *r_badpass = ctx.badpass; - goto bailout; - } - if (lenndef) - len += consumed; + if ((err=parse_bag_encrypted_data (&ctx, tlv))) + goto bailout; } - else if (ti.tag == TAG_OBJECT_ID && ti.length == DIM(oid_data) - && !memcmp (p, oid_data, DIM(oid_data))) + else if (oidlen == DIM(oid_data) + && !memcmp (oid, oid_data, DIM(oid_data))) { - p += DIM(oid_data); - n -= DIM(oid_data); - if (!lenndef) - len -= DIM(oid_data); - where = "bag.data"; - consumed = 0; - if (parse_bag_data (&ctx, p, n, (p - p_start), &consumed)) + if ((err=parse_bag_data (&ctx, tlv))) goto bailout; - if (lenndef) - len += consumed; } - else + else if (oidlen == DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag) + && !memcmp (oid, oid_pkcs_12_pkcs_8ShroudedKeyBag, + DIM(oid_pkcs_12_pkcs_8ShroudedKeyBag))) { - log_info ("unknown outer bag type - skipped\n"); - p += ti.length; - n -= ti.length; + where = "bag.shroudedkeybag"; + if ((err = parse_shrouded_key_bag (&ctx, tlv))) + goto bailout; } - - if (len < 0 || len > n) - goto bailout; - p += len; - n -= len; - if (lenndef) + else { - /* Need to skip the Null Tag. */ - if (parse_tag (&p, &n, &ti)) - goto bailout; - if (!(ti.class == CLASS_UNIVERSAL && !ti.tag && !ti.is_constructed)) - goto bailout; + tlv_skip (tlv); + log_info ("unknown outer bag type - skipped\n"); } } + dump_tlv_ctx ("bags", "endloop", tlv); + if (!err) + tlv_set_pending (tlv); + else if (err && gpg_err_code (err) != GPG_ERR_EOF) + goto bailout; + err = 0; - gcry_free (cram_buffer); + tlv_release (tlv); if (r_curve) *r_curve = ctx.curve; else @@ -1998,8 +2766,14 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, return ctx.privatekey; bailout: - log_error ("error at \"%s\", offset %u\n", - where, (unsigned int)(p - p_start)); + *r_badpass = ctx.badpass; + log_error ("%s(%s): @%04zu lvl=%u %s: %s - %s\n", + __func__, where, + tlv? (size_t)(tlv->buffer - tlv->origbuffer):0, + tlv? tlv->stacklen : 0, + tlv? tlv->lastfunc : "", + tlv? gpg_strerror (tlv->lasterr) : "init failed", + gpg_strerror (err)); if (ctx.privatekey) { int i; @@ -2009,7 +2783,7 @@ p12_parse (const unsigned char *buffer, size_t length, const char *pw, gcry_free (ctx.privatekey); ctx.privatekey = NULL; } - gcry_free (cram_buffer); + tlv_release (tlv); gcry_free (ctx.curve); if (r_curve) *r_curve = NULL; @@ -2951,7 +3725,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, pw, - GCRY_CIPHER_RFC2268_40, 1); + GCRY_CIPHER_RFC2268_40, GCRY_MD_SHA1, 1); /* Encode the encrypted stuff into a bag. */ seqlist[seqlistidx].buffer = build_cert_bag (buffer, buflen, salt, &n); @@ -2983,7 +3757,7 @@ p12_build (gcry_mpi_t *kparms, const void *cert, size_t certlen, /* Encrypt it. */ gcry_randomize (salt, 8, GCRY_STRONG_RANDOM); crypt_block (buffer, buflen, salt, 8, 2048, NULL, 0, - pw, GCRY_CIPHER_3DES, 1); + pw, GCRY_CIPHER_3DES, GCRY_MD_SHA1, 1); /* Encode the encrypted stuff into a bag. */ if (cert && certlen) diff --git a/sm/minip12.h b/sm/minip12.h index 84c5f5f..654cab0 100644 --- a/sm/minip12.h +++ b/sm/minip12.h @@ -23,7 +23,7 @@ #include <gcrypt.h> -void p12_set_verbosity (int verbose); +void p12_set_verbosity (int verbose, int debug); gcry_mpi_t *p12_parse (const unsigned char *buffer, size_t length, const char *pw, @@ -101,7 +101,7 @@ setup_pinentry_env (void) function ignores missing parameters so that it can also be used to create an siginfo value as expected by ksba_certreq_set_siginfo. To create a siginfo s-expression a public-key s-expression may be - used instead of a sig-val. We only support RSA for now. */ + used instead of a sig-val. */ gpg_error_t transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, unsigned char **r_newsigval, size_t *r_newsigvallen) @@ -111,11 +111,15 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, size_t buflen, toklen; int depth, last_depth1, last_depth2; int is_pubkey = 0; - const unsigned char *rsa_s = NULL; - size_t rsa_s_len = 0; + int pkalgo; + const unsigned char *rsa_s, *ecc_r, *ecc_s; + size_t rsa_s_len, ecc_r_len, ecc_s_len; const char *oid; gcry_sexp_t sexp; + rsa_s = ecc_r = ecc_s = NULL; + rsa_s_len = ecc_r_len = ecc_s_len = 0; + *r_newsigval = NULL; if (r_newsigvallen) *r_newsigvallen = 0; @@ -137,7 +141,15 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, return err; if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; - if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen)) + if (!tok) + return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); + if (toklen == 3 && !memcmp ("rsa", tok, 3)) + pkalgo = GCRY_PK_RSA; + else if (toklen == 3 && !memcmp ("ecc", tok, 3)) + pkalgo = GCRY_PK_ECC; + else if (toklen == 5 && !memcmp ("ecdsa", tok, 5)) + pkalgo = GCRY_PK_ECC; + else return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); last_depth1 = depth; @@ -150,8 +162,8 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, return err; if (tok && toklen == 1) { - const unsigned char **mpi; - size_t *mpi_len; + const unsigned char **mpi = NULL; + size_t *mpi_len = NULL; switch (*tok) { @@ -161,6 +173,25 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, if (mpi && *mpi) return gpg_error (GPG_ERR_DUP_VALUE); + switch (*tok) + { + case 's': + if (pkalgo == GCRY_PK_RSA) + { + mpi = &rsa_s; + mpi_len = &rsa_s_len; + } + else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA) + { + mpi = &ecc_s; + mpi_len = &ecc_s_len; + } + break; + + case 'r': mpi = &ecc_r; mpi_len = &ecc_r_len; break; + default: mpi = NULL; mpi_len = NULL; break; + } + if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))) return err; if (tok && mpi) @@ -181,34 +212,62 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo, if (err) return err; - /* Map the hash algorithm to an OID. */ - switch (mdalgo) + if (0) + ; /* Just to align it with the code in 2.3. */ + else { - case GCRY_MD_SHA1: - oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */ - break; + /* Map the hash algorithm to an OID. */ + if (mdalgo < 0 || mdalgo > (1<<15) || pkalgo < 0 || pkalgo > (1<<15)) + return gpg_error (GPG_ERR_DIGEST_ALGO); + + switch (mdalgo | (pkalgo << 16)) + { + case GCRY_MD_SHA1 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.5"; /* sha1WithRSAEncryption */ + break; + + case GCRY_MD_SHA256 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */ + break; + + case GCRY_MD_SHA384 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */ + break; + + case GCRY_MD_SHA512 | (GCRY_PK_RSA << 16): + oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */ + break; + + case GCRY_MD_SHA224 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.1"; /* ecdsa-with-sha224 */ + break; - case GCRY_MD_SHA256: - oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */ - break; + case GCRY_MD_SHA256 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-sha256 */ + break; - case GCRY_MD_SHA384: - oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */ - break; + case GCRY_MD_SHA384 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-sha384 */ + break; - case GCRY_MD_SHA512: - oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */ - break; + case GCRY_MD_SHA512 | (GCRY_PK_ECC << 16): + oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-sha512 */ + break; - default: - return gpg_error (GPG_ERR_DIGEST_ALGO); + default: + return gpg_error (GPG_ERR_DIGEST_ALGO); + } } - if (rsa_s && !is_pubkey) + if (is_pubkey) + err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid); + else if (pkalgo == GCRY_PK_RSA) err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))", oid, (int)rsa_s_len, rsa_s); - else - err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid); + else if (pkalgo == GCRY_PK_ECC || pkalgo == GCRY_PK_EDDSA) + err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(r%b)(s%b)))", oid, + (int)ecc_r_len, ecc_r, (int)ecc_s_len, ecc_s); + if (err) return err; err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen); diff --git a/sm/server.c b/sm/server.c index 5341d31..31abe37 100644 --- a/sm/server.c +++ b/sm/server.c @@ -290,6 +290,17 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) ctrl->offline = i; } } + else if (!strcmp (key, "always-trust")) + { + /* We ignore this option if gpgsm has been started with + --always-trust (which also sets offline) and if + --require-compliance is active */ + if (!opt.always_trust && !opt.require_compliance) + { + int i = *value? !!atoi (value) : 1; + ctrl->always_trust = i; + } + } else if (!strcmp (key, "request-origin")) { if (!opt.request_origin) @@ -301,6 +312,10 @@ option_handler (assuan_context_t ctx, const char *key, const char *value) opt.request_origin = i; } } + else if (!strcmp (key, "input-size-hint")) + { + ctrl->input_size_hint = string_to_u64 (value); + } else err = gpg_error (GPG_ERR_UNKNOWN_OPTION); @@ -319,6 +334,7 @@ reset_notify (assuan_context_t ctx, char *line) gpgsm_release_certlist (ctrl->server_local->signerlist); ctrl->server_local->recplist = NULL; ctrl->server_local->signerlist = NULL; + ctrl->always_trust = 0; close_message_fd (ctrl); assuan_close_input_fd (ctx); assuan_close_output_fd (ctx); @@ -487,6 +503,7 @@ cmd_encrypt (assuan_context_t ctx, char *line) gpgsm_release_certlist (ctrl->server_local->recplist); ctrl->server_local->recplist = NULL; + ctrl->always_trust = 0; /* Close and reset the fd */ close_message_fd (ctrl); assuan_close_input_fd (ctx); @@ -1127,7 +1144,8 @@ static const char hlp_getinfo[] = " agent-check - Return success if the agent is running.\n" " cmd_has_option CMD OPT\n" " - Returns OK if the command CMD implements the option OPT.\n" - " offline - Returns OK if the connection is in offline mode."; + " offline - Returns OK if the connection is in offline mode." + " always-trust- Returns OK if the connection is in always-trust mode."; static gpg_error_t cmd_getinfo (assuan_context_t ctx, char *line) { @@ -1186,6 +1204,11 @@ cmd_getinfo (assuan_context_t ctx, char *line) { rc = ctrl->offline? 0 : gpg_error (GPG_ERR_FALSE); } + else if (!strcmp (line, "always-trust")) + { + rc = (ctrl->always_trust || opt.always_trust)? 0 + /**/ : gpg_error (GPG_ERR_FALSE); + } else rc = set_error (GPG_ERR_ASS_PARAMETER, "unknown value for WHAT"); @@ -1450,7 +1473,14 @@ gpgsm_status2 (ctrl_t ctrl, int no, ...) } } putc ('\n', statusfp); - fflush (statusfp); + if (ferror (statusfp)) + err = gpg_error_from_syserror (); + else + { + fflush (statusfp); + if (ferror (statusfp)) + err = gpg_error_from_syserror (); + } } else { @@ -1474,7 +1504,7 @@ gpgsm_status_with_err_code (ctrl_t ctrl, int no, const char *text, { char buf[30]; - sprintf (buf, "%u", (unsigned int)ec); + snprintf (buf, sizeof buf, "%u", (unsigned int)ec); if (text) return gpgsm_status2 (ctrl, no, text, buf, NULL); else @@ -1495,6 +1525,45 @@ gpgsm_status_with_error (ctrl_t ctrl, int no, const char *text, } +/* This callback is used to emit progress status lines. */ +gpg_error_t +gpgsm_progress_cb (ctrl_t ctrl, uint64_t current, uint64_t total) +{ + char buffer[60]; + char units[] = "BKMGTPEZY?"; + int unitidx = 0; + + if (total) + { + if (current > total) + current = total; + + while (total > 1024*1024) + { + total /= 1024; + current /= 1024; + unitidx++; + } + } + else + { + while (current > 1024*1024) + { + current /= 1024; + unitidx++; + } + } + + if (unitidx > 9) + unitidx = 9; + + snprintf (buffer, sizeof buffer, "? %lu %lu %c%s", + (unsigned long)current, (unsigned long)total, + units[unitidx], unitidx? "iB" : ""); + return gpgsm_status2 (ctrl, STATUS_PROGRESS, "?", buffer, NULL); +} + + /* Helper to notify the client about Pinentry events. Because that might disturb some older clients, this is only done when enabled via an option. Returns an gpg error code. */ @@ -1,6 +1,8 @@ /* sign.c - Sign a message * Copyright (C) 2001, 2002, 2003, 2008, * 2010 Free Software Foundation, Inc. + * Copyright (C) 2003-2012, 2016-2017, 2019, + * 2020, 2022-2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +18,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -33,6 +36,7 @@ #include "keydb.h" #include "../common/i18n.h" +#include "../common/tlv.h" /* Hash the data and return if something was hashed. Return -1 on error. */ @@ -293,7 +297,7 @@ add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) } ksba_cert_release (cert); - return rc == -1? 0: rc; + return gpg_err_code (rc) == GPG_ERR_NOT_FOUND? 0 : rc; ksba_failure: ksba_cert_release (cert); @@ -304,6 +308,215 @@ add_certificate_list (ctrl_t ctrl, ksba_cms_t cms, ksba_cert_t cert) +/* This function takes a binary detached signature in (BLOB,BLOBLEN) + * and writes it to OUT_FP. The core of the function is to replace + * NDEF length sequences in the input to those with fixed inputs. + * This helps certain other implementations to properly verify + * detached signature. Moreover, it allows our own trailing zero + * stripping code - which we need for PDF signatures - to work + * correctly. + * + * Example start of a detached signature as created by us: + * 0 NDEF: SEQUENCE { -- 1st sequence + * 2 9: OBJECT IDENTIFIER signedData (1 2 840 113549 1 7 2) + * 13 NDEF: [0] { -- 2nd sequence + * 15 NDEF: SEQUENCE { -- 3rd sequence + * 17 1: INTEGER 1 -- version + * 20 15: SET { -- set of algorithms + * 22 13: SEQUENCE { + * 24 9: OBJECT IDENTIFIER sha-256 (2 16 840 1 101 3 4 2 1) + * 35 0: NULL + * : } + * : } + * 37 NDEF: SEQUENCE { -- 4th pretty short sequence + * 39 9: OBJECT IDENTIFIER data (1 2 840 113549 1 7 1) + * : } + * 52 869: [0] { + * Our goal is to replace the NDEF by fixed length tags. + */ +static gpg_error_t +write_detached_signature (const void *blob, size_t bloblen, estream_t out_fp) +{ + gpg_error_t err; + const unsigned char *p; + size_t n, objlen, hdrlen; + int class, tag, cons, ndef; + const unsigned char *p_ctoid, *p_version, *p_algoset, *p_dataoid; + size_t n_ctoid, n_version, n_algoset, n_dataoid; + const unsigned char *p_certset, *p_signerinfos; + size_t n_certset, n_signerinfos; + int i; + ksba_der_t dbld; + unsigned char *finalder = NULL; + size_t finalderlen; + + p = blob; + n = bloblen; + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 1st sequence. */ + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No signedData OID. */ + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + p_ctoid = p; + n_ctoid = objlen; + p += objlen; + n -= objlen; + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_CONTEXT && tag == 0 && cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 2nd sequence. */ + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 3rd sequence. */ + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_INTEGER)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No version. */ + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + p_version = p; + n_version = objlen; + p += objlen; + n -= objlen; + + p_algoset = p; + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_SET && cons && !ndef)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No set of algorithms. */ + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + n_algoset = hdrlen + objlen; + p += objlen; + n -= objlen; + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No 4th sequence. */ + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !cons)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No data OID. */ + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + p_dataoid = p; + n_dataoid = objlen; + p += objlen; + n -= objlen; + + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_NONE && !cons && !objlen)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No End tag. */ + + /* certificates [0] IMPLICIT CertificateSet OPTIONAL, + * Note: We ignore the following + * crls [1] IMPLICIT CertificateRevocationLists OPTIONAL + * because gpgsm does not create them. */ + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef,&objlen,&hdrlen))) + return err; + if (class == CLASS_CONTEXT && tag == 0 && cons) + { + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + p_certset = p; + n_certset = objlen; + p += objlen; + n -= objlen; + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef, + &objlen,&hdrlen))) + return err; + } + else + { + p_certset = NULL; + n_certset = 0; + } + + /* SignerInfos ::= SET OF SignerInfo */ + if (!(class == CLASS_UNIVERSAL && tag == TAG_SET && cons && !ndef)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No set of signerInfos. */ + if (objlen > n) + return gpg_error (GPG_ERR_BAD_BER); /* Object larger than data. */ + p_signerinfos = p; + n_signerinfos = objlen; + p += objlen; + n -= objlen; + + /* For the fun of it check the 3 end tags. */ + for (i=0; i < 3; i++) + { + if ((err=parse_ber_header (&p,&n,&class,&tag,&cons,&ndef, + &objlen,&hdrlen))) + return err; + if (!(class == CLASS_UNIVERSAL && tag == TAG_NONE && !cons && !objlen)) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* No End tag. */ + } + if (n) + return gpg_error (GPG_ERR_INV_CMS_OBJ); /* Garbage */ + + /*---- From here on we jump to leave on error. ----*/ + + /* Now create a new object from the collected data. */ + dbld = ksba_der_builder_new (16); /* (pre-allocate 16 items) */ + if (!dbld) + { + err = gpg_error_from_syserror (); + goto leave; + } + ksba_der_add_tag (dbld, 0, KSBA_TYPE_SEQUENCE); + ksba_der_add_val ( dbld, 0, KSBA_TYPE_OBJECT_ID, p_ctoid, n_ctoid); + ksba_der_add_tag ( dbld, KSBA_CLASS_CONTEXT, 0); + ksba_der_add_tag ( dbld, 0, KSBA_TYPE_SEQUENCE); + ksba_der_add_val ( dbld, 0, KSBA_TYPE_INTEGER, p_version, n_version); + ksba_der_add_der ( dbld, p_algoset, n_algoset); + ksba_der_add_tag ( dbld, 0, KSBA_TYPE_SEQUENCE); + ksba_der_add_val ( dbld, 0, KSBA_TYPE_OBJECT_ID, p_dataoid, n_dataoid); + ksba_der_add_end ( dbld); + if (p_certset) + { + ksba_der_add_tag ( dbld, KSBA_CLASS_CONTEXT, 0); + ksba_der_add_der ( dbld, p_certset, n_certset); + ksba_der_add_end ( dbld); + } + ksba_der_add_tag ( dbld, 0, KSBA_TYPE_SET); + ksba_der_add_der ( dbld, p_signerinfos, n_signerinfos); + ksba_der_add_end ( dbld); + ksba_der_add_end ( dbld); + ksba_der_add_end ( dbld); + ksba_der_add_end (dbld); + + err = ksba_der_builder_get (dbld, &finalder, &finalderlen); + if (err) + goto leave; + + if (es_fwrite (finalder, finalderlen, 1, out_fp) != 1) + { + err = gpg_error_from_syserror (); + goto leave; + } + + + leave: + ksba_der_release (dbld); + ksba_free (finalder); + return err; +} + + + /* Perform a sign operation. Sign the data received on DATA-FD in embedded mode or in detached @@ -314,10 +527,11 @@ int gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, int data_fd, int detached, estream_t out_fp) { - int i, rc; gpg_error_t err; + int i; gnupg_ksba_io_t b64writer = NULL; ksba_writer_t writer; + estream_t sig_fp = NULL; /* Used for detached signatures. */ ksba_cms_t cms = NULL; ksba_stop_reason_t stopreason; KEYDB_HANDLE kh = NULL; @@ -328,6 +542,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, ksba_isotime_t signed_at; certlist_t cl; int release_signerlist = 0; + int binary_detached = detached && !ctrl->create_pem && !ctrl->create_base64; + char *curve = NULL; audit_set_type (ctrl->audit, AUDIT_TYPE_SIGN); @@ -335,45 +551,59 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (!kh) { log_error (_("failed to allocate keyDB handle\n")); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { - rc = gpg_error (GPG_ERR_FORBIDDEN); + err = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); gpgsm_status_with_error (ctrl, STATUS_ERROR, - "random-compliance", rc); + "random-compliance", err); goto leave; } + /* Note that in detached mode the b64 write is actually a binary + * writer because we need to fixup the created signature later. + * Note that we do this only for binary output because we have no + * PEM writer interface outside of the ksba create writer code. */ ctrl->pem_name = "SIGNED MESSAGE"; - rc = gnupg_ksba_create_writer - (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) - | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), - ctrl->pem_name, out_fp, &writer); - if (rc) + if (binary_detached) { - log_error ("can't create writer: %s\n", gpg_strerror (rc)); - goto leave; + sig_fp = es_fopenmem (0, "w+"); + err = sig_fp? 0 : gpg_error_from_syserror (); + if (!err) + err = gnupg_ksba_create_writer (&b64writer, 0, NULL, sig_fp, &writer); + } + else + { + err = gnupg_ksba_create_writer + (&b64writer, ((ctrl->create_pem? GNUPG_KSBA_IO_PEM : 0) + | (ctrl->create_base64? GNUPG_KSBA_IO_BASE64 : 0)), + ctrl->pem_name, out_fp, &writer); } - - err = ksba_cms_new (&cms); if (err) { - rc = err; + log_error ("can't create writer: %s\n", gpg_strerror (err)); goto leave; } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + if (ctrl->input_size_hint) + gnupg_ksba_set_total (b64writer, ctrl->input_size_hint); + + err = ksba_cms_new (&cms); + if (err) + goto leave; + err = ksba_cms_set_reader_writer (cms, NULL, writer); if (err) { log_debug ("ksba_cms_set_reader_writer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -385,7 +615,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_debug ("ksba_cms_set_content_type failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } @@ -398,22 +627,23 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, log_error ("no default signer found\n"); gpgsm_status2 (ctrl, STATUS_INV_SGNR, get_inv_recpsgnr_code (GPG_ERR_NO_SECKEY), NULL); - rc = gpg_error (GPG_ERR_GENERAL); + err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Although we don't check for ambiguous specification we will check that the signer's certificate is usable and valid. */ - rc = gpgsm_cert_use_sign_p (cert, 0); - if (!rc) - rc = gpgsm_validate_chain (ctrl, cert, "", NULL, 0, NULL, 0, NULL); - if (rc) + err = gpgsm_cert_use_sign_p (cert, 0); + if (!err) + err = gpgsm_validate_chain (ctrl, cert, + GNUPG_ISOTIME_NONE, NULL, 0, NULL, 0, NULL); + if (err) { char *tmpfpr; tmpfpr = gpgsm_get_fingerprint_hexstring (cert, 0); gpgsm_status2 (ctrl, STATUS_INV_SGNR, - get_inv_recpsgnr_code (rc), tmpfpr, NULL); + get_inv_recpsgnr_code (err), tmpfpr, NULL); xfree (tmpfpr); goto leave; } @@ -422,7 +652,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, signerlist = xtrycalloc (1, sizeof *signerlist); if (!signerlist) { - rc = out_of_core (); + err = gpg_error_from_syserror (); ksba_cert_release (cert); goto leave; } @@ -430,6 +660,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, release_signerlist = 1; } + /* Figure out the hash algorithm to use. We do not want to use the one for the certificate but if possible an OID for the plain algorithm. */ @@ -438,6 +669,12 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, for (i=0, cl=signerlist; cl; cl = cl->next, i++) { const char *oid; + unsigned int nbits; + int pk_algo; + + xfree (curve); + pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits, &curve); + cl->pk_algo = pk_algo; if (opt.forced_digest_algo) { @@ -446,7 +683,21 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } else { - oid = ksba_cert_get_digest_algo (cl->cert); + if (pk_algo == GCRY_PK_ECC) + { + /* Map the Curve to a corresponding hash algo. */ + if (nbits <= 256) + oid = "2.16.840.1.101.3.4.2.1"; /* sha256 */ + else if (nbits <= 384) + oid = "2.16.840.1.101.3.4.2.2"; /* sha384 */ + else + oid = "2.16.840.1.101.3.4.2.3"; /* sha512 */ + } + else + { + /* For RSA we reuse the hash algo used by the certificate. */ + oid = ksba_cert_get_digest_algo (cl->cert); + } cl->hash_algo = oid ? gcry_md_map_name (oid) : 0; } switch (cl->hash_algo) @@ -457,7 +708,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, case GCRY_MD_SHA256: oid = "2.16.840.1.101.3.4.2.1"; break; case GCRY_MD_SHA384: oid = "2.16.840.1.101.3.4.2.2"; break; case GCRY_MD_SHA512: oid = "2.16.840.1.101.3.4.2.3"; break; -/* case GCRY_MD_WHIRLPOOL: oid = "No OID yet"; break; */ case GCRY_MD_MD5: /* We don't want to use MD5. */ case 0: /* No algorithm found in cert. */ @@ -482,27 +732,22 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, goto leave; } - { - unsigned int nbits; - int pk_algo = gpgsm_get_key_algo_info (cl->cert, &nbits); - - if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, 0, - NULL, nbits, NULL)) - { - char kidstr[10+1]; - - snprintf (kidstr, sizeof kidstr, "0x%08lX", - gpgsm_get_short_fingerprint (cl->cert, NULL)); - log_error (_("key %s may not be used for signing in %s mode\n"), - kidstr, - gnupg_compliance_option_string (opt.compliance)); - err = gpg_error (GPG_ERR_PUBKEY_ALGO); - goto leave; - } - } + if (!gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pk_algo, + PK_ALGO_FLAG_ECC18, NULL, nbits, curve)) + { + char kidstr[10+1]; + + snprintf (kidstr, sizeof kidstr, "0x%08lX", + gpgsm_get_short_fingerprint (cl->cert, NULL)); + log_error (_("key %s may not be used for signing in %s mode\n"), + kidstr, + gnupg_compliance_option_string (opt.compliance)); + err = gpg_error (GPG_ERR_PUBKEY_ALGO); + goto leave; + } } - if (opt.verbose) + if (opt.verbose > 1 || opt.debug) { for (i=0, cl=signerlist; cl; cl = cl->next, i++) log_info (_("hash algorithm used for signer %d: %s (%s)\n"), @@ -513,22 +758,21 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Gather certificates of signers and store them in the CMS object. */ for (cl=signerlist; cl; cl = cl->next) { - rc = gpgsm_cert_use_sign_p (cl->cert, 0); - if (rc) + err = gpgsm_cert_use_sign_p (cl->cert, 0); + if (err) goto leave; err = ksba_cms_add_signer (cms, cl->cert); if (err) { log_error ("ksba_cms_add_signer failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } - rc = add_certificate_list (ctrl, cms, cl->cert); - if (rc) + err = add_certificate_list (ctrl, cms, cl->cert); + if (err) { log_error ("failed to store list of certificates: %s\n", - gpg_strerror(rc)); + gpg_strerror (err)); goto leave; } /* Set the hash algorithm we are going to use */ @@ -537,7 +781,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_debug ("ksba_cms_add_digest_algo failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -559,7 +802,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error (_("checking for qualified certificate failed: %s\n"), gpg_strerror (err)); - rc = err; goto leave; } if (*buffer) @@ -567,19 +809,16 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, else err = gpgsm_not_qualified_warning (ctrl, cl->cert); if (err) - { - rc = err; - goto leave; - } + goto leave; } } /* Prepare hashing (actually we are figuring out what we have set above). */ - rc = gcry_md_open (&data_md, 0, 0); - if (rc) + err = gcry_md_open (&data_md, 0, 0); + if (err) { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); + log_error ("md_open failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_HASHING) @@ -591,7 +830,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if (!algo) { log_error ("unknown hash algorithm '%s'\n", algoid? algoid:"?"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } gcry_md_enable (data_md, algo); @@ -616,7 +855,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, digest, digest_len); @@ -624,7 +863,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -638,19 +876,21 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_signing_time failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } /* We need to write at least a minimal list of our capabilities to - try to convince some MUAs to use 3DES and not the crippled - RC2. Our list is: - - aes128-CBC - des-EDE3-CBC - */ - err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL, 0); + * try to convince some MUAs to use 3DES and not the crippled + * RC2. Our list is: + * + * aes256-CBC + * aes128-CBC + * des-EDE3-CBC + */ + err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.42", NULL,0); + if (!err) + err = ksba_cms_add_smime_capability (cms, "2.16.840.1.101.3.4.1.2", NULL,0); if (!err) err = ksba_cms_add_smime_capability (cms, "1.2.840.113549.3.7", NULL, 0); if (err) @@ -667,8 +907,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, err = ksba_cms_build (cms, &stopreason); if (err) { - log_debug ("ksba_cms_build failed: %s\n", gpg_strerror (err)); - rc = err; + log_error ("creating CMS object failed: %s\n", gpg_strerror (err)); goto leave; } @@ -680,8 +919,8 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, assert (!detached); - rc = hash_and_copy_data (data_fd, data_md, writer); - if (rc) + err = hash_and_copy_data (data_fd, data_md, writer); + if (err) goto leave; audit_log (ctrl->audit, AUDIT_GOT_DATA); for (cl=signerlist,signer=0; cl; cl = cl->next, signer++) @@ -691,7 +930,7 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, if ( !digest || !digest_len ) { log_error ("problem getting the hash of the data\n"); - rc = gpg_error (GPG_ERR_BUG); + err = gpg_error (GPG_ERR_BUG); goto leave; } err = ksba_cms_set_message_digest (cms, signer, @@ -700,7 +939,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, { log_error ("ksba_cms_set_message_digest failed: %s\n", gpg_strerror (err)); - rc = err; goto leave; } } @@ -710,10 +948,10 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, /* Compute the signature for all signers. */ gcry_md_hd_t md; - rc = gcry_md_open (&md, 0, 0); - if (rc) + err = gcry_md_open (&md, 0, 0); + if (err) { - log_error ("md_open failed: %s\n", gpg_strerror (rc)); + log_error ("md_open failed: %s\n", gpg_strerror (err)); goto leave; } if (DBG_HASHING) @@ -738,20 +976,20 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } } - rc = ksba_cms_hash_signed_attrs (cms, signer); - if (rc) + err = ksba_cms_hash_signed_attrs (cms, signer); + if (err) { log_debug ("hashing signed attrs failed: %s\n", - gpg_strerror (rc)); + gpg_strerror (err)); gcry_md_close (md); goto leave; } - rc = gpgsm_create_cms_signature (ctrl, cl->cert, - md, cl->hash_algo, &sigval); - if (rc) + err = gpgsm_create_cms_signature (ctrl, cl->cert, + md, cl->hash_algo, &sigval); + if (err) { - audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, rc); + audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); gcry_md_close (md); goto leave; } @@ -763,7 +1001,6 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, audit_log_cert (ctrl->audit, AUDIT_SIGNED_BY, cl->cert, err); log_error ("failed to store the signature: %s\n", gpg_strerror (err)); - rc = err; gcry_md_close (md); goto leave; } @@ -772,24 +1009,29 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, fpr = gpgsm_get_fingerprint_hexstring (cl->cert, GCRY_MD_SHA1); if (!fpr) { - rc = gpg_error (GPG_ERR_ENOMEM); + err = gpg_error (GPG_ERR_ENOMEM); gcry_md_close (md); goto leave; } - rc = 0; - { - int pkalgo = gpgsm_get_key_algo_info (cl->cert, NULL); - buf = xtryasprintf ("%c %d %d 00 %s %s", - detached? 'D':'S', - pkalgo, - cl->hash_algo, - signed_at, - fpr); - if (!buf) - rc = gpg_error_from_syserror (); - } + if (opt.verbose) + { + char *pkalgostr = gpgsm_pubkey_algo_string (cl->cert, NULL); + log_info (_("%s/%s signature using %s key %s\n"), + pubkey_algo_to_string (cl->pk_algo), + gcry_md_algo_name (cl->hash_algo), + pkalgostr, fpr); + xfree (pkalgostr); + } + buf = xtryasprintf ("%c %d %d 00 %s %s", + detached? 'D':'S', + cl->pk_algo, + cl->hash_algo, + signed_at, + fpr); + if (!buf) + err = gpg_error_from_syserror (); xfree (fpr); - if (rc) + if (err) { gcry_md_close (md); goto leave; @@ -803,26 +1045,44 @@ gpgsm_sign (ctrl_t ctrl, certlist_t signerlist, } while (stopreason != KSBA_SR_READY); - rc = gnupg_ksba_finish_writer (b64writer); - if (rc) + err = gnupg_ksba_finish_writer (b64writer); + if (err) { - log_error ("write failed: %s\n", gpg_strerror (rc)); + log_error ("write failed: %s\n", gpg_strerror (err)); goto leave; } + if (binary_detached) + { + void *blob = NULL; + size_t bloblen; + + err = (es_fclose_snatch (sig_fp, &blob, &bloblen)? + gpg_error_from_syserror () : 0); + sig_fp = NULL; + if (err) + goto leave; + err = write_detached_signature (blob, bloblen, out_fp); + xfree (blob); + if (err) + goto leave; + } + + audit_log (ctrl->audit, AUDIT_SIGNING_DONE); log_info ("signature created\n"); - leave: - if (rc) + if (err) log_error ("error creating signature: %s <%s>\n", - gpg_strerror (rc), gpg_strsource (rc) ); + gpg_strerror (err), gpg_strsource (err) ); if (release_signerlist) gpgsm_release_certlist (signerlist); + xfree (curve); ksba_cms_release (cms); gnupg_ksba_destroy_writer (b64writer); keydb_release (kh); gcry_md_close (data_md); - return rc; + es_fclose (sig_fp); + return err; } diff --git a/sm/t-minip12.c b/sm/t-minip12.c new file mode 100644 index 0000000..c9adced --- /dev/null +++ b/sm/t-minip12.c @@ -0,0 +1,800 @@ +/* t-minip12.c - Test driver for minip12.c + * Copyright (C) 2020, 2023 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <unistd.h> +#include <stdarg.h> +#include <ctype.h> + +#include "../common/util.h" +#include "minip12.h" + + +#define PGM "t-minip12" + +static int verbose; +static int debug; +static int any_error; + +static void die (const char *format, ...) GPGRT_ATTR_NR_PRINTF(1,2); +static void err (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); +static void inf (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); +/* static void dbg (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); */ +static void printresult (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); +static char *my_xstrconcat (const char *s1, ...) GPGRT_ATTR_SENTINEL(0); + +#define xstrconcat my_xstrconcat +#define trim_spaces(a) my_trim_spaces ((a)) +#define my_isascii(c) (!((c) & 0x80)) + + + + + +/* Print diagnostic message and exit with failure. */ +static void +die (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + if (!*format || format[strlen(format)-1] != '\n') + putc ('\n', stderr); + + exit (1); +} + + +/* Print diagnostic message. */ +static void +err (const char *format, ...) +{ + va_list arg_ptr; + + any_error = 1; + + fflush (stdout); + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + if (!*format || format[strlen(format)-1] != '\n') + putc ('\n', stderr); +} + + +/* Print an info message. */ +static void +inf (const char *format, ...) +{ + va_list arg_ptr; + + if (verbose) + { + fprintf (stderr, "%s: ", PGM); + + va_start (arg_ptr, format); + vfprintf (stderr, format, arg_ptr); + va_end (arg_ptr); + if (!*format || format[strlen(format)-1] != '\n') + putc ('\n', stderr); + } +} + + +/* Print a debug message. */ +/* static void */ +/* dbg (const char *format, ...) */ +/* { */ +/* va_list arg_ptr; */ + +/* if (debug) */ +/* { */ +/* fprintf (stderr, "%s: DBG: ", PGM); */ + +/* va_start (arg_ptr, format); */ +/* vfprintf (stderr, format, arg_ptr); */ +/* va_end (arg_ptr); */ +/* if (!*format || format[strlen(format)-1] != '\n') */ +/* putc ('\n', stderr); */ +/* } */ +/* } */ + + +/* Print a result line to stdout. */ +static void +printresult (const char *format, ...) +{ + va_list arg_ptr; + + fflush (stdout); +#ifdef HAVE_FLOCKFILE + flockfile (stdout); +#endif + va_start (arg_ptr, format); + vfprintf (stdout, format, arg_ptr); + if (*format && format[strlen(format)-1] != '\n') + putc ('\n', stdout); + va_end (arg_ptr); + fflush (stdout); +#ifdef HAVE_FLOCKFILE + funlockfile (stdout); +#endif +} + + +/* Helper for xstrconcat and strconcat. */ +static char * +do_strconcat (int xmode, const char *s1, va_list arg_ptr) +{ + const char *argv[48]; + size_t argc; + size_t needed; + char *buffer, *p; + + argc = 0; + argv[argc++] = s1; + needed = strlen (s1); + while (((argv[argc] = va_arg (arg_ptr, const char *)))) + { + needed += strlen (argv[argc]); + if (argc >= DIM (argv)-1) + die ("too may args for strconcat\n"); + argc++; + } + needed++; + buffer = xmode? xmalloc (needed) : malloc (needed); + for (p = buffer, argc=0; argv[argc]; argc++) + p = stpcpy (p, argv[argc]); + + return buffer; +} + + +/* Concatenate the string S1 with all the following strings up to a + NULL. Returns a malloced buffer with the new string or dies on error. */ +static char * +my_xstrconcat (const char *s1, ...) +{ + va_list arg_ptr; + char *result; + + if (!s1) + result = xstrdup (""); + else + { + va_start (arg_ptr, s1); + result = do_strconcat (1, s1, arg_ptr); + va_end (arg_ptr); + } + return result; +} + + +static char * +my_trim_spaces (char *str ) +{ + char *string, *p, *mark; + + string = str; + for (p=string; *p && isspace (*(unsigned char *)p) ; p++) + ; + for (mark=NULL; (*string = *p); string++, p++ ) + if (isspace (*(unsigned char *)p)) + { + if (!mark) + mark = string; + } + else + mark = NULL; + if (mark) + *mark = '\0'; + + return str ; +} + + +/* Prepend FNAME with the srcdir environment variable's value and + * return an allocated filename. */ +static char * +prepend_srcdir (const char *fname) +{ + static const char *srcdir; + + if (!srcdir && !(srcdir = getenv ("srcdir"))) + return xstrdup (fname); + else + return xstrconcat (srcdir, "/", fname, NULL); +} + + +/* (BUFFER,BUFLEN) and return a malloced hexstring. */ +static char * +hash_buffer (const void *buffer, size_t buflen) +{ + unsigned char hash[20]; + char *result; + int i; + + gcry_md_hash_buffer (GCRY_MD_SHA1, hash, buffer, buflen); + result = xmalloc (41); + for (i=0; i < 20; i++) + snprintf (result + 2*i, 3, "%02x", hash[i]); + return result; +} + + +/* Read next line but skip over empty and comment lines. Caller must + xfree the result. */ +static char * +read_textline (FILE *fp, int *lineno) +{ + char line[4096]; + char *p; + + do + { + if (!fgets (line, sizeof line, fp)) + { + if (feof (fp)) + return NULL; + die ("error reading input line: %s\n", strerror (errno)); + } + ++*lineno; + p = strchr (line, '\n'); + if (!p) + die ("input line %d not terminated or too long\n", *lineno); + *p = 0; + for (p--;p > line && my_isascii (*p) && isspace (*p); p--) + *p = 0; + } + while (!*line || *line == '#'); + return xstrdup (line); +} + + +/* Copy the data after the tag to BUFFER. BUFFER will be allocated as + needed. */ +static void +copy_data (char **buffer, const char *line, int lineno) +{ + const char *s; + + xfree (*buffer); + *buffer = NULL; + + s = strchr (line, ':'); + if (!s) + { + err ("syntax error at input line %d", lineno); + return; + } + for (s++; my_isascii (*s) && isspace (*s); s++) + ; + *buffer = xstrdup (s); +} + + +static void +hexdowncase (char *string) +{ + char *p; + + if (string) + for (p=string; *p; p++) + if (my_isascii (*p)) + *p = tolower (*p); +} + + +/* Return the value of the variable VARNAME from ~/.gnupg-autogen.rc + * or NULL if it does not exists or is empty. */ +static char * +value_from_gnupg_autogen_rc (const char *varname) +{ + const char *home; + char *fname; + FILE *fp; + char *line = NULL; + char *p; + int lineno = 0; + + if (!(home = getenv ("HOME"))) + home = ""; + fname = xstrconcat (home, "/.gnupg-autogen.rc", NULL); + fp = fopen (fname, "r"); + if (!fp) + goto leave; + + while ((line = read_textline (fp, &lineno))) + { + p = strchr (line, '='); + if (p) + { + *p++ = 0; + trim_spaces (line); + if (!strcmp (line, varname)) + { + trim_spaces (p); + if (*p) + { + memmove (line, p, strlen (p)+1); + if (*line == '~' && line[1] == '/') + { + p = xstrconcat (home, line+1, NULL); + xfree (line); + line = p; + } + break; /* found. */ + } + } + } + xfree (line); + } + + leave: + if (fp) + fclose (fp); + xfree (fname); + return line; +} + + +static void +cert_cb (void *opaque, const unsigned char *cert, size_t certlen) +{ + (void)opaque; + (void)cert; + + if (verbose) + log_info ("got a certificate of %zu bytes length\n", certlen); +} + + +/* Parse one PKCS#12 file. Returns zero on success. */ +static int +one_file (const char *name, const char *pass) +{ + FILE *fp; + struct stat st; + unsigned char *buf; + size_t buflen; + gcry_mpi_t *result; + int badpass; + char *curve = NULL; + + fp = fopen (name, "rb"); + if (!fp) + { + fprintf (stderr, PGM": can't open '%s': %s\n", name, strerror (errno)); + return 1; + } + + if (fstat (fileno(fp), &st)) + { + fprintf (stderr, PGM": can't stat '%s': %s\n", name, strerror (errno)); + return 1; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + fprintf (stderr, "error reading '%s': %s\n", name, strerror (errno)); + return 1; + } + fclose (fp); + + result = p12_parse (buf, buflen, pass, cert_cb, NULL, &badpass, &curve); + if (result) + { + int i, rc; + unsigned char *tmpbuf; + + if (curve) + log_info ("curve: %s\n", curve); + for (i=0; result[i]; i++) + { + rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]); + if (rc) + log_error ("%d: [error printing number: %s]\n", + i, gpg_strerror (rc)); + else + { + log_info ("%d: %s\n", i, tmpbuf); + gcry_free (tmpbuf); + } + } + } + if (badpass) + log_error ("Bad password given?\n"); + + xfree (buf); + return 0; +} + + +static void +cert_collect_cb (void *opaque, const unsigned char *cert, size_t certlen) +{ + char **certstr = opaque; + char *hash, *save; + + hash = hash_buffer (cert, certlen); + if (*certstr) + { + save = *certstr; + *certstr = xstrconcat (save, ",", hash, NULL); + xfree (save); + xfree (hash); + } + else + *certstr = hash; +} + + +static int +run_one_test (const char *name, const char *desc, const char *pass, + const char *certexpected, const char *keyexpected) +{ + FILE *fp; + struct stat st; + unsigned char *buf; + size_t buflen; + gcry_mpi_t *result; + int badpass; + char *curve = NULL; + char *resulthash = NULL; + char *p; + char *certstr = NULL; + int ret; + + inf ("testing '%s' (%s)", name , desc? desc:""); + fp = fopen (name, "rb"); + if (!fp) + { + err ("can't open '%s': %s\n", name, strerror (errno)); + printresult ("FAIL: %s - test file not found\n", name); + return 1; + } + + if (fstat (fileno (fp), &st)) + { + err ("can't stat '%s': %s\n", name, strerror (errno)); + printresult ("FAIL: %s - error stating test file\n", name); + fclose (fp); + return 1; + } + + buflen = st.st_size; + buf = xmalloc (buflen+1); + if (fread (buf, buflen, 1, fp) != 1) + { + err ("error reading '%s': %s\n", name, strerror (errno)); + printresult ("FAIL: %s - error reading test file\n", name); + fclose (fp); + xfree (buf); + return 1; + } + fclose (fp); + + result = p12_parse (buf, buflen, pass? pass:"", cert_collect_cb, &certstr, + &badpass, &curve); + if (result) + { + int i, rc; + char *tmpstring; + unsigned char *tmpbuf; + char numbuf[20]; + + if (curve) + { + if (verbose > 1) + inf ("curve: %s\n", curve); + tmpstring = xstrconcat ("curve:", curve, "\n", NULL); + } + else + tmpstring = xstrdup ("\n"); + for (i=0; result[i]; i++) + { + rc = gcry_mpi_aprint (GCRYMPI_FMT_HEX, &tmpbuf, NULL, result[i]); + if (rc) + die ("result %d: [error printing number: %s]\n", + i, gpg_strerror (rc)); + else + { + if (verbose > 1) + inf ("result %d: %s\n", i, tmpbuf); + snprintf (numbuf, sizeof numbuf, "%d:", i); + p = xstrconcat (tmpstring, numbuf, tmpbuf, "\n", NULL); + xfree (tmpstring); + tmpstring = p; + gcry_free (tmpbuf); + } + } + + /* Hash only if we have at least one parameter; i.e. the curve + * alone is not sufficient. */ + if (result[0]) + resulthash = hash_buffer (tmpstring, strlen (tmpstring)); + xfree (tmpstring); + } + + if (verbose > 1) + { + inf ("cert(exp)=%s", certexpected); + inf ("cert(got)=%s", certstr? certstr:"[null]"); + inf ("key(exp)=%s", keyexpected); + inf ("key(got)=%s", resulthash? resulthash:"[null]"); + } + + ret = 1; + if (!result) + printresult ("FAIL: %s - error from parser\n", name); + else if (certexpected && !certstr) + printresult ("FAIL: %s - expected certs but got none\n", name); + else if (!certexpected && certstr) + printresult ("FAIL: %s - no certs expected but got one\n", name); + else if (certexpected && certstr && strcmp (certexpected, certstr)) + { + printresult ("FAIL: %s - certs not as expected\n", name); + inf ("cert(exp)=%s", certexpected); + inf ("cert(got)=%s", certstr? certstr:"[null]"); + } + else if (keyexpected && !resulthash) + printresult ("FAIL: %s - expected key but got none\n", name); + else if (!keyexpected && resulthash) + printresult ("FAIL: %s - key not expected but got one\n", name); + else if (keyexpected && resulthash && strcmp (keyexpected, resulthash)) + { + printresult ("FAIL: %s - keys not as expected\n", name); + inf ("key(exp)=%s", keyexpected); + inf ("key(got)=%s", resulthash? resulthash:"[null]"); + } + else + { + printresult ("PASS: %s\n", name); + ret = 0; + } + + if (result) + { + int i; + for (i=0; result[i]; i++) + gcry_mpi_release (result[i]); + gcry_free (result); + } + xfree (certstr); + xfree (resulthash); + xfree (curve); + xfree (buf); + return ret; +} + + +/* Run a regression test using the Info take from DESCFNAME. */ +static int +run_tests_from_file (const char *descfname) +{ + FILE *fp; + char *descdir; + int lineno, ntests; + char *line; + char *name = NULL; + char *desc = NULL; + char *pass = NULL; + char *cert = NULL; + char *key = NULL; + int ret = 0; + char *p; + + inf ("Running tests from '%s'", descfname); + descdir = xstrdup (descfname); + p = strrchr (descdir, '/'); + if (p) + *p = 0; + else + { + xfree (descdir); + descdir = xstrdup ("."); + } + + fp = fopen (descfname, "r"); + if (!fp) + die ("error opening '%s': %s\n", descfname, strerror (errno)); + + lineno = ntests = 0; + while ((line = read_textline (fp, &lineno))) + { + if (!strncmp (line, "Name:", 5)) + { + if (name) + ret |= run_one_test (name, desc, pass, cert, key); + xfree (cert); cert = NULL; + xfree (desc); desc = NULL; + xfree (pass); pass = NULL; + xfree (key); key = NULL; + copy_data (&name, line, lineno); + if (name) + { + p = xstrconcat (descdir, "/", name, NULL); + xfree (name); + name = p; + } + } + else if (!strncmp (line, "Desc:", 5)) + copy_data (&desc, line, lineno); + else if (!strncmp (line, "Pass:", 5)) + copy_data (&pass, line, lineno); + else if (!strncmp (line, "Cert:", 5)) + { + p = NULL; + copy_data (&p, line, lineno); + hexdowncase (p); + if (p && cert) + { + char *save = cert; + cert = xstrconcat (save, ",", p, NULL); + xfree (save); + xfree (p); + } + else + cert = p; + } + else if (!strncmp (line, "Key:", 4)) + { + copy_data (&key, line, lineno); + hexdowncase (key); + } + else + inf ("%s:%d: unknown tag ignored", descfname, lineno); + + xfree (line); + } + if (name) + ret |= run_one_test (name, desc, pass, cert, key); + xfree (name); + xfree (desc); + xfree (pass); + xfree (cert); + xfree (key); + + fclose (fp); + xfree (descdir); + return ret; +} + + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + char const *name = NULL; + char const *pass = NULL; + int ret; + int no_extra = 0; + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " <pkcs12file> [<passphrase>]\n" + "Without <pkcs12file> a regression test is run\n" + "Options:\n" + " --no-extra do not run extra tests\n" + " --verbose print timings etc.\n" + " given twice shows more\n" + " --debug flyswatter\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--no-extra")) + { + no_extra = 1; + argc--; argv++; + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + debug++; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + if (!argc) + { + name = NULL; + pass = NULL; + } + else if (argc == 1) + { + name = argv[0]; + pass = ""; + } + else if (argc == 2) + { + name = argv[0]; + pass = argv[1]; + } + else + { + fprintf (stderr, "usage: " PGM " [<file> [<passphrase>]]\n"); + exit (1); + } + + gcry_control (GCRYCTL_DISABLE_SECMEM, NULL); + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, NULL); + + if (name) + { + p12_set_verbosity (verbose, debug); + ret = one_file (name, pass); + } + else + { + char *descfname, *p; + + if (verbose > 1) + p12_set_verbosity (verbose > 1? (verbose - 1):0, debug); + descfname = prepend_srcdir ("../tests/samplekeys/Description-p12"); + ret = run_tests_from_file (descfname); + xfree (descfname); + + /* Check whether we have non-public regression test cases. */ + p = no_extra? NULL:value_from_gnupg_autogen_rc ("GNUPG_EXTRA_TESTS_DIR"); + if (p) + { + descfname = xstrconcat (p, "/pkcs12/Description", NULL); + xfree (p); + ret |= run_tests_from_file (descfname); + xfree (descfname); + } + } + + return ret; +} diff --git a/sm/verify.c b/sm/verify.c index 5510f42..58911bb 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -106,12 +106,17 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) int signer; const char *algoid; int algo; - int is_detached; + int is_detached, maybe_detached; estream_t in_fp = NULL; char *p; audit_set_type (ctrl->audit, AUDIT_TYPE_VERIFY); + /* Although we detect detached signatures during the parsing phase, + * we need to know it earlier and thus accept the caller's idea of + * what to verify. */ + maybe_detached = (data_fd != -1); + kh = keydb_new (); if (!kh) { @@ -132,7 +137,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) rc = gnupg_ksba_create_reader (&b64reader, ((ctrl->is_pem? GNUPG_KSBA_IO_PEM : 0) | (ctrl->is_base64? GNUPG_KSBA_IO_BASE64 : 0) - | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0)), + | (ctrl->autodetect_encoding? GNUPG_KSBA_IO_AUTODETECT : 0) + | (maybe_detached? GNUPG_KSBA_IO_STRIP : 0)), in_fp, &reader); if (rc) { @@ -153,6 +159,10 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) } } + gnupg_ksba_set_progress_cb (b64writer, gpgsm_progress_cb, ctrl); + if (ctrl->input_size_hint) + gnupg_ksba_set_total (b64writer, ctrl->input_size_hint); + rc = ksba_cms_new (&cms); if (rc) goto leave; @@ -300,6 +310,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) unsigned int nbits; int pkalgo; char *pkalgostr = NULL; + char *pkcurve = NULL; char *pkfpr = NULL; unsigned int pkalgoflags, verifyflags; @@ -458,7 +469,11 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) pkfpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); pkalgostr = gpgsm_pubkey_algo_string (cert, NULL); - pkalgo = gpgsm_get_key_algo_info (cert, &nbits); + pkalgo = gpgsm_get_key_algo_info (cert, &nbits, &pkcurve); + /* Remap the ECC algo to the algo we use. Note that EdDSA has + * already been mapped. */ + if (pkalgo == GCRY_PK_ECC) + pkalgo = GCRY_PK_ECDSA; log_info (_("Signature made ")); if (*sigtime) @@ -488,8 +503,9 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) audit_log_i (ctrl->audit, AUDIT_DATA_HASH_ALGO, algo); /* Check compliance. */ + pkalgoflags |= PK_ALGO_FLAG_ECC18; if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_VERIFICATION, - pkalgo, pkalgoflags, NULL, nbits, NULL)) + pkalgo, pkalgoflags, NULL, nbits, pkcurve)) { char kidstr[10+1]; @@ -509,9 +525,19 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) goto next_signer; } + /* Print compliance warning for the key. */ + if (!opt.quiet + && !gnupg_pk_is_compliant (opt.compliance, pkalgo, pkalgoflags, + NULL, nbits, pkcurve)) + { + log_info (_("WARNING: This key is not suitable for signing" + " in %s mode\n"), + gnupg_compliance_option_string (opt.compliance)); + } + /* Check compliance with CO_DE_VS. */ if (gnupg_pk_is_compliant (CO_DE_VS, pkalgo, pkalgoflags, - NULL, nbits, NULL) + NULL, nbits, pkcurve) && gnupg_gcrypt_is_compliant (CO_DE_VS) && gnupg_digest_is_compliant (CO_DE_VS, sigval_hash_algo)) gpgsm_status (ctrl, STATUS_VERIFICATION_COMPLIANCE_MODE, @@ -524,7 +550,6 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) gpgsm_errors_seen = 1; } - /* Now we can check the signature. */ if (msgdigest) { /* Signed attributes are available. */ @@ -705,6 +730,7 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) gcry_sexp_release (sigval); xfree (msgdigest); xfree (pkalgostr); + xfree (pkcurve); xfree (pkfpr); ksba_cert_release (cert); cert = NULL; |