diff options
Diffstat (limited to 'g10')
47 files changed, 2709 insertions, 976 deletions
diff --git a/g10/ChangeLog-2011 b/g10/ChangeLog-2011 index 37da37b..4737b1f 100644 --- a/g10/ChangeLog-2011 +++ b/g10/ChangeLog-2011 @@ -4798,7 +4798,7 @@ * g10.c (main): Try to create the trustdb even for non-colon-mode list-key operations. This is required because getkey needs to - know whether a a key is ultimately trusted. From Werner on stable + know whether a key is ultimately trusted. From Werner on stable branch. * exec.c [__CYGWIN32__]: Keep cygwin separate from Mingw32; diff --git a/g10/Makefile.am b/g10/Makefile.am index f885673..e275aaa 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -1,3 +1,4 @@ +# Makefile.am - g10 # Copyright (C) 1998, 1999, 2000, 2001, 2002, # 2003, 2006, 2010 Free Software Foundation, Inc. # @@ -19,10 +20,9 @@ ## Process this file with automake to produce Makefile.in EXTRA_DIST = distsigkey.gpg \ - ChangeLog-2011 \ - gpg-w32info.rc gpg.w32-manifest.in \ - gpgv-w32info.rc gpgv.w32-manifest.in \ - test.c t-keydb-keyring.kbx \ + gpg-w32info.rc gpg.w32-manifest.in \ + gpgv-w32info.rc gpgv.w32-manifest.in \ + ChangeLog-2011 test.c t-keydb-keyring.kbx \ t-keydb-get-keyblock.gpg t-stutter-data.asc \ all-tests.scm @@ -90,13 +90,11 @@ endif if HAVE_W32_SYSTEM -gpg_robjs = $(resource_objs) gpg-w32info.o -gpgv_robjs = $(resource_objs) gpgv-w32info.o -gpg-w32info.o : gpg.w32-manifest -gpgv-w32info.o : gpgv.w32-manifest -else -gpg_robjs = -gpgv_robjs = +gpg_rc_objs = gpg-w32info.o +gpgv_rc_objs = gpgv-w32info.o + +gpg-w32info.o : gpg.w32-manifest ../common/w32info-rc.h +gpgv-w32info.o : gpgv.w32-manifest ../common/w32info-rc.h endif common_source = \ @@ -180,18 +178,18 @@ gpgv_SOURCES = gpgv.c \ # $(common_source) LDADD = $(needed_libs) ../common/libgpgrl.a \ - $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) + $(ZLIBS) $(LIBINTL) $(CAPLIBS) gpg_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(SQLITE3_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(gpg_robjs) $(extra_sys_libs) + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(LIBICONV) $(gpg_rc_objs) $(extra_sys_libs) gpg_LDFLAGS = $(extra_bin_ldflags) gpgv_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ - $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(gpgv_robjs) $(extra_sys_libs) + $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(LIBICONV) $(gpgv_rc_objs) $(extra_sys_libs) gpgv_LDFLAGS = $(extra_bin_ldflags) gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(extra_sys_libs) gpgcompose_LDFLAGS = $(extra_bin_ldflags) @@ -200,15 +198,15 @@ module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter t_rmd160_SOURCES = t-rmd160.c rmd160.c t_rmd160_LDADD = $(t_common_ldadd) t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source) -t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(t_common_ldadd) t_keydb_get_keyblock_SOURCES = t-keydb-get-keyblock.c test-stubs.c \ $(common_source) t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(t_common_ldadd) + $(NETLIBS) $(LIBICONV) $(t_common_ldadd) t_stutter_SOURCES = t-stutter.c test-stubs.c \ $(common_source) -t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(t_common_ldadd) diff --git a/g10/Makefile.in b/g10/Makefile.in index 1774106..e7b449e 100644 --- a/g10/Makefile.in +++ b/g10/Makefile.in @@ -14,6 +14,7 @@ @SET_MAKE@ +# Makefile.am - g10 # Copyright (C) 1998, 1999, 2000, 2001, 2002, # 2003, 2006, 2010 Free Software Foundation, Inc. # @@ -151,17 +152,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) @@ -218,14 +218,11 @@ am_gpg_OBJECTS = gpg.$(OBJEXT) keyedit.$(OBJEXT) $(am__objects_6) gpg_OBJECTS = $(am_gpg_OBJECTS) am__DEPENDENCIES_1 = am__DEPENDENCIES_2 = $(needed_libs) ../common/libgpgrl.a \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) -@HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) \ -@HAVE_W32_SYSTEM_TRUE@ gpg-w32info.o + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) gpg_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_3) \ + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(gpg_rc_objs) \ $(am__DEPENDENCIES_1) gpg_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(gpg_LDFLAGS) $(LDFLAGS) -o \ $@ @@ -249,7 +246,8 @@ gpgcompose_OBJECTS = $(am_gpgcompose_OBJECTS) gpgcompose_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) gpgcompose_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(gpgcompose_LDFLAGS) \ $(LDFLAGS) -o $@ am__gpgv_SOURCES_DIST = gpgv.c gpg.h dek.h build-packet.c compress.c \ @@ -261,11 +259,9 @@ am__gpgv_SOURCES_DIST = gpgv.c gpg.h dek.h build-packet.c compress.c \ ecdh.c verify.c am_gpgv_OBJECTS = gpgv.$(OBJEXT) $(am__objects_2) verify.$(OBJEXT) gpgv_OBJECTS = $(am_gpgv_OBJECTS) -@HAVE_W32_SYSTEM_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) \ -@HAVE_W32_SYSTEM_TRUE@ gpgv-w32info.o gpgv_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_4) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(gpgv_rc_objs) $(am__DEPENDENCIES_1) gpgv_LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(gpgv_LDFLAGS) $(LDFLAGS) \ -o $@ am__t_keydb_SOURCES_DIST = t-keydb.c test-stubs.c gpg.h dek.h \ @@ -280,7 +276,7 @@ am_t_keydb_OBJECTS = t-keydb.$(OBJEXT) test-stubs.$(OBJEXT) \ t_keydb_OBJECTS = $(am_t_keydb_OBJECTS) t_keydb_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) am__t_keydb_get_keyblock_SOURCES_DIST = t-keydb-get-keyblock.c \ test-stubs.c gpg.h dek.h build-packet.c compress.c \ compress-bz2.c filter.h free-packet.c getkey.c keydb.c keydb.h \ @@ -294,7 +290,8 @@ am_t_keydb_get_keyblock_OBJECTS = t-keydb-get-keyblock.$(OBJEXT) \ t_keydb_get_keyblock_OBJECTS = $(am_t_keydb_get_keyblock_OBJECTS) t_keydb_get_keyblock_DEPENDENCIES = $(am__DEPENDENCIES_2) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_1) am_t_rmd160_OBJECTS = t-rmd160.$(OBJEXT) rmd160.$(OBJEXT) t_rmd160_OBJECTS = $(am_t_rmd160_OBJECTS) t_rmd160_DEPENDENCIES = $(am__DEPENDENCIES_1) @@ -310,7 +307,7 @@ am_t_stutter_OBJECTS = t-stutter.$(OBJEXT) test-stubs.$(OBJEXT) \ t_stutter_OBJECTS = $(am_t_stutter_OBJECTS) t_stutter_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_1) + $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) AM_V_P = $(am__v_P_@AM_V@) am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) am__v_P_0 = false @@ -617,10 +614,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ EXTRA_DIST = distsigkey.gpg \ - ChangeLog-2011 \ - gpg-w32info.rc gpg.w32-manifest.in \ - gpgv-w32info.rc gpgv.w32-manifest.in \ - test.c t-keydb-keyring.kbx \ + gpg-w32info.rc gpg.w32-manifest.in \ + gpgv-w32info.rc gpgv.w32-manifest.in \ + ChangeLog-2011 test.c t-keydb-keyring.kbx \ t-keydb-get-keyblock.gpg t-stutter-data.asc \ all-tests.scm @@ -670,10 +666,8 @@ TESTS_ENVIRONMENT = \ @NO_TRUST_MODELS_TRUE@trust_source = @USE_TOFU_FALSE@tofu_source = @USE_TOFU_TRUE@tofu_source = tofu.h tofu.c gpgsql.c gpgsql.h -@HAVE_W32_SYSTEM_FALSE@gpg_robjs = -@HAVE_W32_SYSTEM_TRUE@gpg_robjs = $(resource_objs) gpg-w32info.o -@HAVE_W32_SYSTEM_FALSE@gpgv_robjs = -@HAVE_W32_SYSTEM_TRUE@gpgv_robjs = $(resource_objs) gpgv-w32info.o +@HAVE_W32_SYSTEM_TRUE@gpg_rc_objs = gpg-w32info.o +@HAVE_W32_SYSTEM_TRUE@gpgv_rc_objs = gpgv-w32info.o common_source = \ gpg.h \ dek.h \ @@ -755,20 +749,20 @@ gpgv_SOURCES = gpgv.c \ # ks-db.h \ # $(common_source) LDADD = $(needed_libs) ../common/libgpgrl.a \ - $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) + $(ZLIBS) $(LIBINTL) $(CAPLIBS) gpg_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(SQLITE3_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(gpg_robjs) $(extra_sys_libs) + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(LIBICONV) $(gpg_rc_objs) $(extra_sys_libs) gpg_LDFLAGS = $(extra_bin_ldflags) gpgv_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ - $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(gpgv_robjs) $(extra_sys_libs) + $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(LIBICONV) $(gpgv_rc_objs) $(extra_sys_libs) gpgv_LDFLAGS = $(extra_bin_ldflags) gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(extra_sys_libs) gpgcompose_LDFLAGS = $(extra_bin_ldflags) @@ -777,19 +771,19 @@ module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter t_rmd160_SOURCES = t-rmd160.c rmd160.c t_rmd160_LDADD = $(t_common_ldadd) t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source) -t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(t_common_ldadd) t_keydb_get_keyblock_SOURCES = t-keydb-get-keyblock.c test-stubs.c \ $(common_source) t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(t_common_ldadd) + $(NETLIBS) $(LIBICONV) $(t_common_ldadd) t_stutter_SOURCES = t-stutter.c test-stubs.c \ $(common_source) -t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(t_common_ldadd) all: all-am @@ -1392,8 +1386,9 @@ uninstall-am: uninstall-local @HAVE_W32_SYSTEM_TRUE@.rc.o: @HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" -@HAVE_W32_SYSTEM_TRUE@gpg-w32info.o : gpg.w32-manifest -@HAVE_W32_SYSTEM_TRUE@gpgv-w32info.o : gpgv.w32-manifest + +@HAVE_W32_SYSTEM_TRUE@gpg-w32info.o : gpg.w32-manifest ../common/w32info-rc.h +@HAVE_W32_SYSTEM_TRUE@gpgv-w32info.o : gpgv.w32-manifest ../common/w32info-rc.h $(PROGRAMS): $(needed_libs) ../common/libgpgrl.a diff --git a/g10/armor.c b/g10/armor.c index 36215a3..8fcb74a 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -233,6 +233,7 @@ is_armored (const byte *buf) case PKT_COMPRESSED: case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: case PKT_PLAINTEXT: case PKT_OLD_COMMENT: case PKT_COMMENT: diff --git a/g10/build-packet.c b/g10/build-packet.c index a40ed0d..5de5114 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -42,6 +42,7 @@ static u32 calc_plaintext( PKT_plaintext *pt ); static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed); static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); @@ -106,6 +107,7 @@ build_packet (IOBUF out, PACKET *pkt) break; case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: + case PKT_ENCRYPTED_AEAD: new_ctb = pkt->pkt.encrypted->new_ctb; break; case PKT_COMPRESSED: @@ -158,6 +160,9 @@ build_packet (IOBUF out, PACKET *pkt) case PKT_ENCRYPTED_MDC: rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); break; + case PKT_ENCRYPTED_AEAD: + rc = do_encrypted_aead (out, ctb, pkt->pkt.encrypted); + break; case PKT_COMPRESSED: rc = do_compressed (out, ctb, pkt->pkt.compressed); break; @@ -618,9 +623,7 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) IOBUF a = iobuf_temp(); log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC); - - /* The only acceptable version. */ - log_assert( enc->version == 4 ); + log_assert (enc->version == 4 || enc->version == 5); /* RFC 4880, Section 3.7. */ switch (enc->s2k.mode) @@ -635,6 +638,8 @@ do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) } iobuf_put( a, enc->version ); iobuf_put( a, enc->cipher_algo ); + if (enc->version == 5) + iobuf_put (a, enc->aead_algo); iobuf_put( a, enc->s2k.mode ); iobuf_put( a, enc->s2k.hash_algo ); if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { @@ -756,12 +761,20 @@ do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) if (nbytes == (size_t)(-1) && (iobuf_error (out) || iobuf_error (pt->buf))) return iobuf_error (out)? iobuf_error (out):iobuf_error (pt->buf); + /* Always get the error to catch write errors because + * iobuf_copy does not reliable return (-1) in that case. */ + rc = iobuf_error (out); if(ctb_new_format_p (ctb) && !pt->len) /* Turn off partial body length mode. */ iobuf_set_partial_body_length_mode (out, 0); - if( pt->len && nbytes != pt->len ) - log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", - (ulong)nbytes, (ulong)pt->len ); + if (pt->len && nbytes != pt->len) + { + log_error ("do_plaintext(): wrote %lu bytes" + " but expected %lu bytes\n", + (ulong)nbytes, (ulong)pt->len ); + if (!rc) /* Just in case no error was set */ + rc = gpg_error (GPG_ERR_EIO); + } } return rc; @@ -821,6 +834,32 @@ do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) } +/* Serialize the symmetrically AEAD encrypted data packet + * (rfc4880bis-03, Section 5.16) described by ED and write it to OUT. + * + * Note: this only writes only packet's header. The caller must then + * follow up and write the actual encrypted data. This should be done + * by pushing the the cipher_filter_aead. */ +static int +do_encrypted_aead (iobuf_t out, int ctb, PKT_encrypted *ed) +{ + u32 n; + + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_AEAD); + + n = ed->len ? (ed->len + ed->extralen + 4) : 0; + write_header (out, ctb, n ); + iobuf_writebyte (out, 1); /* Version. */ + iobuf_writebyte (out, ed->cipher_algo); + iobuf_writebyte (out, ed->aead_algo); + iobuf_writebyte (out, ed->chunkbyte); + + /* This is all. The caller has to write the encrypted data */ + + return 0; +} + + /* Serialize the compressed packet (RFC 4880, Section 5.6) described by CD and write it to OUT. diff --git a/g10/call-agent.c b/g10/call-agent.c index 7e60542..486b7d9 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -149,6 +149,7 @@ default_inq_cb (void *opaque, const char *line) || has_leading_keyword (line, "NEW_PASSPHRASE")) && opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) { + assuan_begin_confidential (parm->ctx); if (have_static_passphrase ()) { const char *s = get_static_passphrase (); @@ -175,6 +176,7 @@ default_inq_cb (void *opaque, const char *line) err = assuan_send_data (parm->ctx, pw, strlen (pw)); xfree (pw); } + assuan_end_confidential (parm->ctx); } else log_debug ("ignoring gpg-agent inquiry '%s'\n", line); @@ -525,7 +527,8 @@ learn_status_cb (void *opaque, const char *line) xfree (parm->serialno); parm->serialno = store_serialno (line); parm->is_v2 = (strlen (parm->serialno) >= 16 - && xtoi_2 (parm->serialno+12) >= 2 ); + && (xtoi_2 (parm->serialno+12) == 0 /* Yubikey */ + || xtoi_2 (parm->serialno+12) >= 2)); } else if (keywordlen == 7 && !memcmp (keyword, "APPTYPE", keywordlen)) { @@ -742,6 +745,11 @@ learn_status_cb (void *opaque, const char *line) * card-util.c * keyedit_menu * card_store_key_with_backup (Woth force to remove secret key data) + * + * If force has the value 2 the --reallyforce option is also used. + * This is to make sure the sshadow key overwrites the private key. + * Note that this option is gnupg 2.2 specific because since 2.4.4 an + * ephemeral private key store is used instead. */ int agent_scd_learn (struct agent_card_info_s *info, int force) @@ -761,6 +769,7 @@ agent_scd_learn (struct agent_card_info_s *info, int force) parm.ctx = agent_ctx; rc = assuan_transact (agent_ctx, + force == 2? "LEARN --sendinfo --force --reallyforce" : force ? "LEARN --sendinfo --force" : "LEARN --sendinfo", dummy_data_cb, NULL, default_inq_cb, &parm, learn_status_cb, info); @@ -956,7 +965,8 @@ agent_scd_apdu (const char *hexapdu, unsigned int *r_sw) */ int agent_keytocard (const char *hexgrip, int keyno, int force, - const char *serialno, const char *timestamp) + const char *serialno, const char *timestamp, + const char *ecdh_param_str) { int rc; char line[ASSUAN_LINELENGTH]; @@ -964,8 +974,9 @@ agent_keytocard (const char *hexgrip, int keyno, int force, memset (&parm, 0, sizeof parm); - snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s", - force?"--force ": "", hexgrip, serialno, keyno, timestamp); + snprintf (line, DIM(line), "KEYTOCARD %s%s %s OPENPGP.%d %s%s%s", + force?"--force ": "", hexgrip, serialno, keyno, timestamp, + ecdh_param_str? " ":"", ecdh_param_str? ecdh_param_str:""); rc = start_agent (NULL, 1); if (rc) @@ -1611,7 +1622,7 @@ agent_get_passphrase (const char *cache_id, char *arg4 = NULL; membuf_t data; struct default_inq_parm_s dfltparm; - int have_newsymkey; + int have_newsymkey, wasconf; memset (&dfltparm, 0, sizeof dfltparm); @@ -1663,10 +1674,14 @@ agent_get_passphrase (const char *cache_id, xfree (arg4); init_membuf_secure (&data, 64); + wasconf = assuan_get_flag (agent_ctx, ASSUAN_CONFIDENTIAL); + assuan_begin_confidential (agent_ctx); rc = assuan_transact (agent_ctx, line, put_membuf_cb, &data, default_inq_cb, &dfltparm, NULL, NULL); + if (!wasconf) + assuan_end_confidential (agent_ctx); if (rc) xfree (get_membuf (&data, NULL)); @@ -1781,15 +1796,60 @@ agent_get_s2k_count (unsigned long *r_count) +struct keyinfo_data_parm_s +{ + char *serialno; + int is_smartcard; + int passphrase_cached; + int cleartext; +}; + + +static gpg_error_t +keyinfo_status_cb (void *opaque, const char *line) +{ + struct keyinfo_data_parm_s *data = opaque; + char *s; + + if ((s = has_leading_keyword (line, "KEYINFO")) && data) + { + /* Parse the arguments: + * 0 1 2 3 4 5 + * <keygrip> <type> <serialno> <idstr> <cached> <protection> + * + * 6 7 8 + * <sshfpr> <ttl> <flags> + */ + char *fields[9]; + + if (split_fields (s, fields, DIM (fields)) == 9) + { + data->is_smartcard = (fields[1][0] == 'T'); + if (data->is_smartcard && !data->serialno && strcmp (fields[2], "-")) + data->serialno = xtrystrdup (fields[2]); + /* '1' for cached */ + data->passphrase_cached = (fields[4][0] == '1'); + /* 'P' for protected, 'C' for clear */ + data->cleartext = (fields[5][0] == 'C'); + } + } + return 0; +} + + /* Ask the agent whether a secret key for the given public key is - available. Returns 0 if available. */ -gpg_error_t + available. Returns 0 if not available. Bigger value is preferred. */ +int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; char line[ASSUAN_LINELENGTH]; char *hexgrip; + struct keyinfo_data_parm_s keyinfo; + + memset (&keyinfo, 0, sizeof keyinfo); + err = start_agent (ctrl, 0); if (err) return err; @@ -1798,11 +1858,22 @@ agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) if (err) return err; - snprintf (line, sizeof line, "HAVEKEY %s", hexgrip); + snprintf (line, sizeof line, "KEYINFO %s", hexgrip); xfree (hexgrip); - err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); - return err; + err = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, + keyinfo_status_cb, &keyinfo); + xfree (keyinfo.serialno); + if (err) + return 0; + + if (keyinfo.passphrase_cached) + return 3; + + if (keyinfo.is_smartcard) + return 2; + + return 1; } /* Ask the agent whether a secret key is available for any of the @@ -1858,41 +1929,6 @@ agent_probe_any_secret_key (ctrl_t ctrl, kbnode_t keyblock) -struct keyinfo_data_parm_s -{ - char *serialno; - int cleartext; -}; - - -static gpg_error_t -keyinfo_status_cb (void *opaque, const char *line) -{ - struct keyinfo_data_parm_s *data = opaque; - int is_smartcard; - char *s; - - if ((s = has_leading_keyword (line, "KEYINFO")) && data) - { - /* Parse the arguments: - * 0 1 2 3 4 5 - * <keygrip> <type> <serialno> <idstr> <cached> <protection> - */ - char *fields[6]; - - if (split_fields (s, fields, DIM (fields)) == 6) - { - is_smartcard = (fields[1][0] == 'T'); - if (is_smartcard && !data->serialno && strcmp (fields[2], "-")) - data->serialno = xtrystrdup (fields[2]); - /* 'P' for protected, 'C' for clear */ - data->cleartext = (fields[5][0] == 'C'); - } - } - return 0; -} - - /* Return the serial number for a secret key. If the returned serial number is NULL, the key is not stored on a smartcard. Caller needs to free R_SERIALNO. diff --git a/g10/call-agent.h b/g10/call-agent.h index dbc6e2f..4b23287 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -104,7 +104,8 @@ int agent_scd_getattr (const char *name, struct agent_card_info_s *info); /* Send the KEYTOCARD command. */ int agent_keytocard (const char *hexgrip, int keyno, int force, - const char *serialno, const char *timestamp); + const char *serialno, const char *timestamp, + const char *ecdh_param_str); /* Send a SETATTR command to the SCdaemon. */ gpg_error_t agent_scd_setattr (const char *name, @@ -153,8 +154,8 @@ gpg_error_t gpg_agent_get_confirmation (const char *desc); gpg_error_t agent_get_s2k_count (unsigned long *r_count); /* Check whether a secret key for public key PK is available. Returns - 0 if the secret key is available. */ -gpg_error_t agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk); + 0 if not available, positive value if the secret key is available. */ +int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk); /* Ask the agent whether a secret key is availabale for any of the keys (primary or sub) in KEYBLOCK. Returns 0 if available. */ diff --git a/g10/card-util.c b/g10/card-util.c index 25c284e..2c977c2 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -801,7 +801,6 @@ change_name (void) { tty_printf (_("Error: Combined name too long " "(limit is %d characters).\n"), 39); - xfree (isoname); rc = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } @@ -1735,12 +1734,13 @@ card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock) } -/* Store the key at NODE into the smartcard and modify NODE to - carry the serialno stuff instead of the actual secret key - parameters. USE is the usage for that key; 0 means any - usage. */ +/* Store the key at NODE into the smartcard and modify NODE to carry + the serialno stuff instead of the actual secret key parameters. + USE is the usage for that key; 0 means any usage. If + PROCESSED_KEYS is not NULL it is a poiter to an strlist which will + be filled with the keygrips of successfully stored keys. */ int -card_store_subkey (KBNODE node, int use) +card_store_subkey (KBNODE node, int use, strlist_t *processed_keys) { struct agent_card_info_s info; int okay = 0; @@ -1749,8 +1749,9 @@ card_store_subkey (KBNODE node, int use) int keyno; PKT_public_key *pk; gpg_error_t err; - char *hexgrip; + char *hexgrip = NULL; int rc; + char *ecdh_param_str = NULL; gnupg_isotime_t timebuf; log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY @@ -1824,15 +1825,29 @@ card_store_subkey (KBNODE node, int use) goto leave; epoch2isotime (timebuf, (time_t)pk->timestamp); - rc = agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf); - + if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + ecdh_param_str = ecdh_param_str_from_pk (pk); + if (!ecdh_param_str) + { + err = gpg_error_from_syserror (); + goto leave; + } + } + rc = agent_keytocard (hexgrip, keyno, rc, info.serialno, + timebuf, ecdh_param_str); if (rc) log_error (_("KEYTOCARD failed: %s\n"), gpg_strerror (rc)); else - okay = 1; - xfree (hexgrip); + { + okay = 1; + if (processed_keys) + add_to_strlist (processed_keys, hexgrip); + } leave: + xfree (hexgrip); + xfree (ecdh_param_str); agent_release_card_info (&info); return okay; } diff --git a/g10/cipher.c b/g10/cipher.c index f577c97..7509915 100644 --- a/g10/cipher.c +++ b/g10/cipher.c @@ -37,11 +37,29 @@ #include "../common/status.h" -#define MIN_PARTIAL_SIZE 512 +/* The size of the buffer we allocate to encrypt the data. This must + * be a multiple of the OCB blocksize (16 byte). */ +#define AEAD_ENC_BUFFER_SIZE (64*1024) + + +/* Wrapper around iobuf_write to make sure that a proper error code is + * always returned. */ +static gpg_error_t +my_iobuf_write (iobuf_t a, const void *buffer, size_t buflen) +{ + if (iobuf_write (a, buffer, buflen)) + { + gpg_error_t err = iobuf_error (a); + if (!err || !gpg_err_code (err)) /* (The latter should never happen) */ + err = gpg_error (GPG_ERR_EIO); + return err; + } + return 0; +} static void -write_header (cipher_filter_context_t *cfx, iobuf_t a) +write_cfb_header (cipher_filter_context_t *cfx, iobuf_t a) { gcry_error_t err; PACKET pkt; @@ -116,7 +134,7 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) /* - * This filter is used to en/de-cipher data with a symmetric algorithm + * This filter is used to encrypt with a symmetric algorithm in CFB mode. */ int cipher_filter_cfb (void *opaque, int control, @@ -128,13 +146,13 @@ cipher_filter_cfb (void *opaque, int control, if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ { - rc = -1; /* not yet used */ + rc = -1; /* not used */ } else if (control == IOBUFCTRL_FLUSH) /* encrypt */ { log_assert (a); if (!cfx->wrote_header) - write_header (cfx, a); + write_cfb_header (cfx, a); if (cfx->mdc_hash) gcry_md_write (cfx->mdc_hash, buf, size); gcry_cipher_encrypt (cfx->cipher_hd, buf, size, NULL, 0); @@ -185,3 +203,432 @@ cipher_filter_cfb (void *opaque, int control, return rc; } + + + +/* Set the nonce and the additional data for the current chunk. If + * FINAL is set the final AEAD chunk is processed. This also reset + * the encryption machinery so that the handle can be used for a new + * chunk. */ +static gpg_error_t +set_ocb_nonce_and_ad (cipher_filter_context_t *cfx, int final) +{ + gpg_error_t err; + unsigned char nonce[16]; + unsigned char ad[21]; + int i; + + log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB); + memcpy (nonce, cfx->startiv, 15); + i = 7; + + nonce[i++] ^= cfx->chunkindex >> 56; + nonce[i++] ^= cfx->chunkindex >> 48; + nonce[i++] ^= cfx->chunkindex >> 40; + nonce[i++] ^= cfx->chunkindex >> 32; + nonce[i++] ^= cfx->chunkindex >> 24; + nonce[i++] ^= cfx->chunkindex >> 16; + nonce[i++] ^= cfx->chunkindex >> 8; + nonce[i++] ^= cfx->chunkindex; + + if (DBG_CRYPTO) + log_printhex (nonce, 15, "nonce:"); + err = gcry_cipher_setiv (cfx->cipher_hd, nonce, i); + if (err) + return err; + + ad[0] = (0xc0 | PKT_ENCRYPTED_AEAD); + ad[1] = 1; + ad[2] = cfx->dek->algo; + ad[3] = AEAD_ALGO_OCB; + ad[4] = cfx->chunkbyte; + ad[5] = cfx->chunkindex >> 56; + ad[6] = cfx->chunkindex >> 48; + ad[7] = cfx->chunkindex >> 40; + ad[8] = cfx->chunkindex >> 32; + ad[9] = cfx->chunkindex >> 24; + ad[10]= cfx->chunkindex >> 16; + ad[11]= cfx->chunkindex >> 8; + ad[12]= cfx->chunkindex; + if (final) + { + ad[13] = cfx->total >> 56; + ad[14] = cfx->total >> 48; + ad[15] = cfx->total >> 40; + ad[16] = cfx->total >> 32; + ad[17] = cfx->total >> 24; + ad[18] = cfx->total >> 16; + ad[19] = cfx->total >> 8; + ad[20] = cfx->total; + } + if (DBG_CRYPTO) + log_printhex (ad, final? 21 : 13, "authdata:"); + return gcry_cipher_authenticate (cfx->cipher_hd, ad, final? 21 : 13); +} + + +static gpg_error_t +write_ocb_header (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + PACKET pkt; + PKT_encrypted ed; + unsigned int blocksize; + unsigned int startivlen; + enum gcry_cipher_modes ciphermode; + + log_assert (cfx->dek->use_aead == AEAD_ALGO_OCB); + + blocksize = openpgp_cipher_get_algo_blklen (cfx->dek->algo); + if (blocksize != 16 ) + log_fatal ("unsupported blocksize %u for AEAD\n", blocksize); + + err = openpgp_aead_algo_info (cfx->dek->use_aead, &ciphermode, &startivlen); + if (err) + goto leave; + + cfx->chunkbyte = 22 - 6; /* Default to the suggested max of 4 MiB. */ + cfx->chunksize = (uint64_t)1 << (cfx->chunkbyte + 6); + cfx->chunklen = 0; + cfx->bufsize = AEAD_ENC_BUFFER_SIZE; + cfx->buflen = 0; + cfx->buffer = xtrymalloc (cfx->bufsize); + if (!cfx->buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + memset (&ed, 0, sizeof ed); + ed.new_ctb = 1; /* (Is anyway required for the packet type). */ + ed.len = 0; /* fixme: cfx->datalen */ + ed.extralen = startivlen + 16; /* (16 is the taglen) */ + ed.cipher_algo = cfx->dek->algo; + ed.aead_algo = cfx->dek->use_aead; + ed.chunkbyte = cfx->chunkbyte; + + init_packet (&pkt); + pkt.pkttype = PKT_ENCRYPTED_AEAD; + pkt.pkt.encrypted = &ed; + + if (DBG_FILTER) + log_debug ("aead packet: len=%lu extralen=%d\n", + (unsigned long)ed.len, ed.extralen); + + write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d", + cfx->dek->algo, ed.aead_algo); + print_cipher_algo_note (cfx->dek->algo); + + if (build_packet( a, &pkt)) + log_bug ("build_packet(ENCRYPTED_AEAD) failed\n"); + + log_assert (sizeof cfx->startiv >= startivlen); + gcry_randomize (cfx->startiv, startivlen, GCRY_STRONG_RANDOM); + err = my_iobuf_write (a, cfx->startiv, startivlen); + if (err) + goto leave; + + err = openpgp_cipher_open (&cfx->cipher_hd, + cfx->dek->algo, + ciphermode, + GCRY_CIPHER_SECURE); + if (err) + goto leave; + + if (DBG_CRYPTO) + log_printhex (cfx->dek->key, cfx->dek->keylen, "thekey:"); + err = gcry_cipher_setkey (cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen); + if (err) + return err; + + cfx->wrote_header = 1; + + leave: + return err; +} + + +/* Get and write the auth tag to stream A. */ +static gpg_error_t +write_ocb_auth_tag (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char tag[16]; + + err = gcry_cipher_gettag (cfx->cipher_hd, tag, 16); + if (err) + goto leave; + err = my_iobuf_write (a, tag, 16); + if (err) + goto leave; + + leave: + if (err) + log_error ("write_auth_tag failed: %s\n", gpg_strerror (err)); + return err; +} + + +/* Write the final chunk to stream A. */ +static gpg_error_t +write_ocb_final_chunk (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err; + char dummy[1]; + + err = set_ocb_nonce_and_ad (cfx, 1); + if (err) + goto leave; + + gcry_cipher_final (cfx->cipher_hd); + + /* Encrypt an empty string. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, dummy, 0, NULL, 0); + if (err) + goto leave; + + err = write_ocb_auth_tag (cfx, a); + + leave: + return err; +} + + +/* The core of the flush sub-function of cipher_filter_ocb. */ +static gpg_error_t +do_ocb_flush (cipher_filter_context_t *cfx, iobuf_t a, byte *buf, size_t size) +{ + gpg_error_t err = 0; + int finalize = 0; + size_t n; + + /* Put the data into a buffer, flush and encrypt as needed. */ + if (DBG_FILTER) + log_debug ("flushing %zu bytes (cur buflen=%zu)\n", size, cfx->buflen); + do + { + const unsigned fast_threshold = 512; + const byte *src_buf = NULL; + int enc_now = 0; + + if (cfx->buflen + size < cfx->bufsize) + n = size; + else + n = cfx->bufsize - cfx->buflen; + + if (cfx->buflen % fast_threshold != 0) + { + /* Attempt to align cfx->buflen to fast threshold size first. */ + size_t nalign = fast_threshold - (cfx->buflen % fast_threshold); + if (nalign < n) + { + n = nalign; + } + } + else if (cfx->buflen == 0 && n >= fast_threshold) + { + /* Handle large input buffers as multiple of cipher blocksize. */ + n = (n / 16) * 16; + } + + if (cfx->chunklen + cfx->buflen + n >= cfx->chunksize) + { + size_t n1 = cfx->chunksize - (cfx->chunklen + cfx->buflen); + finalize = 1; + if (DBG_FILTER) + log_debug ("chunksize %zu reached;" + " cur buflen=%zu using %zu of %zu\n", + (size_t)cfx->chunksize, cfx->buflen, + n1, n); + n = n1; + } + + if (!finalize && cfx->buflen % 16 == 0 && cfx->buflen > 0 + && size >= fast_threshold) + { + /* If cfx->buffer is aligned and remaining input buffer length + * is long, encrypt cfx->buffer inplace now to allow fast path + * handling on next loop iteration. */ + src_buf = cfx->buffer; + enc_now = 1; + n = 0; + } + else if (cfx->buflen == 0 && n >= fast_threshold) + { + /* Fast path for large input buffer. This avoids memcpy and + * instead encrypts directly from input to cfx->buffer. */ + log_assert (n % 16 == 0 || finalize); + src_buf = buf; + cfx->buflen = n; + buf += n; + size -= n; + enc_now = 1; + } + else if (n > 0) + { + memcpy (cfx->buffer + cfx->buflen, buf, n); + src_buf = cfx->buffer; + cfx->buflen += n; + buf += n; + size -= n; + } + + if (cfx->buflen == cfx->bufsize || enc_now || finalize) + { + if (DBG_FILTER) + log_debug ("encrypting: size=%zu buflen=%zu %s%s n=%zu\n", + size, cfx->buflen, finalize?"(finalize)":"", + enc_now?"(now)":"", n); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_ocb_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + if (finalize) + gcry_cipher_final (cfx->cipher_hd); + if (DBG_FILTER) + { + if (finalize) + log_printhex (src_buf, cfx->buflen, "plain(1):"); + else if (cfx->buflen > 32) + log_printhex (src_buf + cfx->buflen - 32, 32, + "plain(last32):"); + } + + /* Take care: even with a buflen of zero an encrypt needs to + * be called after gcry_cipher_final and before + * gcry_cipher_gettag - at least with libgcrypt 1.8 and OCB + * mode. */ + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, + cfx->buflen, src_buf, cfx->buflen); + if (err) + goto leave; + if (finalize && DBG_FILTER) + log_printhex (cfx->buffer, cfx->buflen, "ciphr(1):"); + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + cfx->buflen = 0; + + if (finalize) + { + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_ocb_auth_tag (cfx, a); + if (err) + goto leave; + + cfx->chunkindex++; + cfx->chunklen = 0; + finalize = 0; + } + } + } + while (size); + + leave: + return err; +} + + +/* The core of the free sub-function of cipher_filter_aead. */ +static gpg_error_t +do_ocb_free (cipher_filter_context_t *cfx, iobuf_t a) +{ + gpg_error_t err = 0; + + if (DBG_FILTER) + log_debug ("do_free: buflen=%zu\n", cfx->buflen); + + if (cfx->chunklen || cfx->buflen) + { + if (DBG_FILTER) + log_debug ("encrypting last %zu bytes of the last chunk\n",cfx->buflen); + + if (!cfx->chunklen) + { + if (DBG_FILTER) + log_debug ("start encrypting a new chunk\n"); + err = set_ocb_nonce_and_ad (cfx, 0); + if (err) + goto leave; + } + + gcry_cipher_final (cfx->cipher_hd); + err = gcry_cipher_encrypt (cfx->cipher_hd, cfx->buffer, cfx->buflen, + NULL, 0); + if (err) + goto leave; + err = my_iobuf_write (a, cfx->buffer, cfx->buflen); + if (err) + goto leave; + /* log_printhex (cfx->buffer, cfx->buflen, "wrote:"); */ + cfx->chunklen += cfx->buflen; + cfx->total += cfx->buflen; + + /* Get and write the authentication tag. */ + if (DBG_FILTER) + log_debug ("writing tag: chunklen=%ju total=%ju\n", + (uintmax_t)cfx->chunklen, (uintmax_t)cfx->total); + err = write_ocb_auth_tag (cfx, a); + if (err) + goto leave; + cfx->chunkindex++; + cfx->chunklen = 0; + } + + /* Write the final chunk. */ + if (DBG_FILTER) + log_debug ("creating final chunk\n"); + err = write_ocb_final_chunk (cfx, a); + + leave: + xfree (cfx->buffer); + cfx->buffer = NULL; + gcry_cipher_close (cfx->cipher_hd); + cfx->cipher_hd = NULL; + return err; +} + + +/* + * This filter is used to encrypt with a symmetric algorithm in OCB mode. + */ +int +cipher_filter_ocb (void *opaque, int control, + iobuf_t a, byte *buf, size_t *ret_len) +{ + cipher_filter_context_t *cfx = opaque; + size_t size = *ret_len; + int rc = 0; + + if (control == IOBUFCTRL_UNDERFLOW) /* decrypt */ + { + rc = -1; /* not used */ + } + else if (control == IOBUFCTRL_FLUSH) /* encrypt */ + { + if (!cfx->wrote_header && (rc=write_ocb_header (cfx, a))) + ; + else + rc = do_ocb_flush (cfx, a, buf, size); + } + else if (control == IOBUFCTRL_FREE) + { + rc = do_ocb_free (cfx, a); + } + else if (control == IOBUFCTRL_DESC) + { + mem2str (buf, "cipher_filter_ocb", *ret_len); + } + + return rc; +} diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 0046c35..d372cbb 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -206,6 +206,7 @@ aead_checktag (decode_filter_ctx_t dfx, int final, const void *tagbuf) { log_error ("gcry_cipher_checktag%s failed: %s\n", final? " (final)":"", gpg_strerror (err)); + write_status_error ("aead_checktag", err); return err; } if (DBG_FILTER) @@ -31,7 +31,9 @@ typedef struct * verbose mode. */ unsigned int algo_info_printed : 1; - /* AEAD shall be used. The value is the AEAD algo. */ + /* AEAD shall be used. The value is the AEAD algo. Note that in + * practise only AEAD_ALGO_OCB, AEAD_ALGO_EAX is only used for + * decryption. */ int use_aead : 4; /* MDC shall be used. */ diff --git a/g10/delkey.c b/g10/delkey.c index 13dbcf0..83dbeb0 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -239,7 +239,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, if (thiskeyonly && targetnode != node) continue; - if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) + if (!agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) continue; /* No secret key for that public (sub)key. */ prompt = gpg_format_keydesc (ctrl, diff --git a/g10/encrypt.c b/g10/encrypt.c index 5f9480f..a4863fa 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -1,7 +1,7 @@ /* encrypt.c - Main encryption driver * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 2009 Free Software Foundation, Inc. - * Copyright (C) 2016, 2022 g10 Code GmbH + * Copyright (C) 2016, 2022, 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -17,6 +17,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> @@ -52,7 +53,7 @@ static int write_pubkey_enc_from_list (ctrl_t ctrl, int encrypt_symmetric (const char *filename) { - return encrypt_simple( filename, 1, 0 ); + return encrypt_simple( filename, 1, opt.force_ocb); } @@ -126,45 +127,169 @@ create_dek_with_warnings (int fallback_to_3des, pk_list_t pk_list) } -/* *SESKEY contains the unencrypted session key ((*SESKEY)->KEY) and - the algorithm that will be used to encrypt the contents of the SED - packet ((*SESKEY)->ALGO). If *SESKEY is NULL, then a random - session key that is appropriate for DEK->ALGO is generated and - stored there. - - Encrypt that session key using DEK and store the result in ENCKEY, - which must be large enough to hold (*SESKEY)->KEYLEN + 1 bytes. */ -void -encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) +/* Encrypt a session key using DEK and store a pointer to the result + * at R_ENCKEY and its length at R_ENCKEYLEN. + * + * R_SESKEY points to the unencrypted session key (.KEY, .KEYLEN) and + * the algorithm that will be used to encrypt the contents of the + * SKESK packet (.ALGO). If R_SESKEY points to NULL, then a random + * session key that is appropriate for DEK->ALGO is generated and + * stored at R_SESKEY. If AEAD_ALGO is not 0 the given AEAD algorithm + * is used for encryption. + */ +static gpg_error_t +encrypt_seskey (DEK *dek, aead_algo_t aead_algo, + DEK **r_seskey, void **r_enckey, size_t *r_enckeylen) { - gcry_cipher_hd_t hd; - byte buf[33]; + gpg_error_t err; + gcry_cipher_hd_t hd = NULL; + byte *buf = NULL; + DEK *seskey; - log_assert ( dek->keylen <= 32 ); - if (!*seskey) + *r_enckey = NULL; + *r_enckeylen = 0; + + if (*r_seskey) + seskey = *r_seskey; + else { - *seskey=xmalloc_clear(sizeof(DEK)); - (*seskey)->algo=dek->algo; - make_session_key(*seskey); + seskey = xtrycalloc (1, sizeof(DEK)); + if (!seskey) + { + err = gpg_error_from_syserror (); + goto leave; + } + seskey->algo = dek->algo; + make_session_key (seskey); /*log_hexdump( "thekey", c->key, c->keylen );*/ } - /* The encrypted session key is prefixed with a one-octet algorithm id. */ - buf[0] = (*seskey)->algo; - memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); - - /* We only pass already checked values to the following function, - thus we consider any failure as fatal. */ - if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG (); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG (); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + + if (aead_algo) + { + unsigned int noncelen; + enum gcry_cipher_modes ciphermode; + byte ad[4]; + + err = openpgp_aead_algo_info (aead_algo, &ciphermode, &noncelen); + if (err) + goto leave; + + /* Allocate space for the nonce, the key, and the authentication + * tag (16). */ + buf = xtrymalloc_secure (noncelen + seskey->keylen + 16); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + + gcry_randomize (buf, noncelen, GCRY_STRONG_RANDOM); + + err = openpgp_cipher_open (&hd, dek->algo, + ciphermode, GCRY_CIPHER_SECURE); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, buf, noncelen); + if (err) + goto leave; + + ad[0] = (0xc0 | PKT_SYMKEY_ENC); + ad[1] = 5; + ad[2] = dek->algo; + ad[3] = aead_algo; + err = gcry_cipher_authenticate (hd, ad, 4); + if (err) + goto leave; + + memcpy (buf + noncelen, seskey->key, seskey->keylen); + gcry_cipher_final (hd); + err = gcry_cipher_encrypt (hd, buf + noncelen, seskey->keylen, NULL,0); + if (err) + goto leave; + err = gcry_cipher_gettag (hd, buf + noncelen + seskey->keylen, 16); + if (err) + goto leave; + *r_enckeylen = noncelen + seskey->keylen + 16; + *r_enckey = buf; + buf = NULL; + } + else + { + /* In the old version 4 SKESK the encrypted session key is + * prefixed with a one-octet algorithm id. */ + buf = xtrymalloc_secure (1 + seskey->keylen); + if (!buf) + { + err = gpg_error_from_syserror (); + goto leave; + } + buf[0] = seskey->algo; + memcpy (buf + 1, seskey->key, seskey->keylen ); + + err = openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1); + if (!err) + err = gcry_cipher_setkey (hd, dek->key, dek->keylen); + if (!err) + err = gcry_cipher_setiv (hd, NULL, 0); + if (!err) + err = gcry_cipher_encrypt (hd, buf, 1 + seskey->keylen, NULL, 0); + if (err) + goto leave; + *r_enckeylen = 1 + seskey->keylen; + *r_enckey = buf; + buf = NULL; + } + + /* Return the session key in case we allocated it. */ + *r_seskey = seskey; + seskey = NULL; + + leave: gcry_cipher_close (hd); + if (seskey != *r_seskey) + xfree (seskey); + xfree (buf); + return err; +} + + +/* Return the AEAD algo if we shall use AEAD mode. Returns 0 if AEAD + * shall not be used. */ +aead_algo_t +use_aead (pk_list_t pk_list, int algo) +{ + int can_use; + + if (!(opt.compat_flags & COMPAT_VSD_ALLOW_OCB) + && opt.compliance == CO_DE_VS) + return 0; /* Not yet allowed. */ + + can_use = openpgp_cipher_get_algo_blklen (algo) == 16; + + /* With --force-aead we want AEAD. */ + if (opt.force_ocb) + { + if (!can_use) + { + log_info ("Warning: request to use OCB ignored for cipher '%s'\n", + openpgp_cipher_algo_name (algo)); + return 0; + } + return AEAD_ALGO_OCB; + } - memcpy( enckey, buf, (*seskey)->keylen + 1 ); - wipememory( buf, sizeof buf ); /* burn key */ + /* AEAD does only work with 128 bit cipher blocklength. */ + if (!can_use) + return 0; + + /* Note the user which keys have no AEAD feature flag set. */ + if (opt.verbose) + warn_missing_aead_from_pklist (pk_list); + + /* If all keys support AEAD we can use it. */ + return select_aead_from_pklist (pk_list); } @@ -196,9 +321,9 @@ encrypt_simple (const char *filename, int mode, int use_seskey) PACKET pkt; PKT_plaintext *pt = NULL; STRING2KEY *s2k = NULL; - byte enckey[33]; + void *enckey = NULL; + size_t enckeylen = 0; int rc = 0; - int seskeylen = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; @@ -206,6 +331,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey) text_filter_context_t tfx; progress_filter_context_t *pfx; int do_compress = !!default_compress_algo(); + char peekbuf[32]; + int peekbuflen; if (!gnupg_rng_is_compliant (opt.compliance)) { @@ -242,6 +369,14 @@ encrypt_simple (const char *filename, int mode, int use_seskey) return rc; } + peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf); + if (peekbuflen < 0) + { + peekbuflen = 0; + if (DBG_FILTER) + log_debug ("peeking at input failed\n"); + } + handle_progress (pfx, inp, filename); if (opt.textmode) @@ -250,6 +385,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey) cfx.dek = NULL; if ( mode ) { + aead_algo_t aead_algo; + rc = setup_symkey (&s2k, &cfx.dek); if (rc) { @@ -265,31 +402,50 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if (use_seskey && s2k->mode != 1 && s2k->mode != 3) { use_seskey = 0; - log_info (_("can't use a symmetric ESK packet " - "due to the S2K mode\n")); + log_info (_("can't use a SKESK packet due to the S2K mode\n")); } + /* See whether we want to use OCB. */ + aead_algo = use_aead (NULL, cfx.dek->algo); + if ( use_seskey ) { - DEK *dek = NULL; /* Dummy. */ + DEK *dek = NULL; - seskeylen = openpgp_cipher_get_algo_keylen (default_cipher_algo ()); - encrypt_seskey( cfx.dek, &dek, enckey ); - xfree( cfx.dek ); cfx.dek = dek; + rc = encrypt_seskey (cfx.dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + { + xfree (cfx.dek); + xfree (s2k); + iobuf_close (inp); + release_progress_context (pfx); + return rc; + } + /* Replace key in DEK. */ + xfree (cfx.dek); + cfx.dek = dek; } - if (opt.verbose) - log_info(_("using cipher %s\n"), - openpgp_cipher_algo_name (cfx.dek->algo)); + if (aead_algo) + cfx.dek->use_aead = aead_algo; + else + cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); - cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo); + if (opt.verbose) + log_info(_("using cipher %s.%s\n"), + openpgp_cipher_algo_name (cfx.dek->algo), + cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) + /**/ : "CFB"); } - if (do_compress && cfx.dek && cfx.dek->use_mdc - && is_file_compressed(filename, &rc)) + if (do_compress + && cfx.dek + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && !opt.explicit_compress_option + && is_file_compressed (peekbuf, peekbuflen)) { if (opt.verbose) - log_info(_("'%s' already compressed\n"), filename); + log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); do_compress = 0; } @@ -310,20 +466,23 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( s2k ) { - PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 ); - enc->version = 4; + PKT_symkey_enc *enc = xmalloc_clear (sizeof *enc + enckeylen); + enc->version = cfx.dek->use_aead ? 5 : 4; enc->cipher_algo = cfx.dek->algo; + enc->aead_algo = cfx.dek->use_aead; enc->s2k = *s2k; - if ( use_seskey && seskeylen ) + if (enckeylen) { - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy (enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet( out, &pkt ))) log_error("build symkey packet failed: %s\n", gpg_strerror (rc) ); xfree (enc); + xfree (enckey); + enckey = NULL; } if (!opt.no_literal) @@ -341,12 +500,12 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { - off_t tmpsize; - int overflow; + uint64_t tmpsize; - if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) - && !overflow && opt.verbose) + tmpsize = iobuf_get_filelength(inp); + if (!tmpsize && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); + /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the size of a file is larger than 2^32 minus some bytes for @@ -380,12 +539,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /* Register the cipher filter. */ if (mode) - iobuf_push_filter ( out, cipher_filter_cfb, &cfx ); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &cfx ); /* Register the compress filter. */ if ( do_compress ) { - if (cfx.dek && cfx.dek->use_mdc) + if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx, default_compress_algo()); } @@ -400,15 +562,15 @@ encrypt_simple (const char *filename, int mode, int use_seskey) { /* User requested not to create a literal packet, so we copy the plain data. */ - byte copy_buffer[4096]; - int bytes_copied; - while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) - if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { - log_error ("copying input to output failed: %s\n", - gpg_strerror (rc) ); - break; - } - wipememory (copy_buffer, 4096); /* burn buffer */ + byte copy_buffer[4096]; + int bytes_copied; + while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) + if ( (rc=iobuf_write(out, copy_buffer, bytes_copied)) ) { + log_error ("copying input to output failed: %s\n", + gpg_strerror (rc) ); + break; + } + wipememory (copy_buffer, 4096); /* burn buffer */ } /* Finish the stuff. */ @@ -424,6 +586,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) if (pt) pt->buf = NULL; free_packet (&pkt, NULL); + xfree (enckey); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); @@ -476,23 +639,33 @@ setup_symkey (STRING2KEY **symkey_s2k, DEK **symkey_dek) static int -write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, - iobuf_t out) +write_symkey_enc (STRING2KEY *symkey_s2k, aead_algo_t aead_algo, + DEK *symkey_dek, DEK *dek, iobuf_t out) { - int rc, seskeylen = openpgp_cipher_get_algo_keylen (dek->algo); - + int rc; + void *enckey; + size_t enckeylen; PKT_symkey_enc *enc; - byte enckey[33]; PACKET pkt; - enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1); - encrypt_seskey(symkey_dek,&dek,enckey); + rc = encrypt_seskey (symkey_dek, aead_algo, &dek, &enckey, &enckeylen); + if (rc) + return rc; + enc = xtrycalloc (1, sizeof (PKT_symkey_enc) + enckeylen); + if (!enc) + { + rc = gpg_error_from_syserror (); + xfree (enckey); + return rc; + } - enc->version = 4; + enc->version = aead_algo? 5 : 4; enc->cipher_algo = opt.s2k_cipher_algo; + enc->aead_algo = aead_algo; enc->s2k = *symkey_s2k; - enc->seskeylen = seskeylen + 1; /* algo id */ - memcpy( enc->seskey, enckey, seskeylen + 1 ); + enc->seskeylen = enckeylen; + memcpy (enc->seskey, enckey, enckeylen); + xfree (enckey); pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; @@ -500,7 +673,7 @@ write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek, if ((rc=build_packet(out,&pkt))) log_error("build symkey_enc packet failed: %s\n",gpg_strerror (rc)); - xfree(enc); + xfree (enc); return rc; } @@ -611,6 +784,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, progress_filter_context_t *pfx; PK_LIST pk_list; int do_compress; + char peekbuf[32]; + int peekbuflen; if (filefd != -1 && filename) return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ @@ -683,6 +858,14 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (opt.verbose) log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp)); + peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf); + if (peekbuflen < 0) + { + peekbuflen = 0; + if (DBG_FILTER) + log_debug ("peeking at input failed\n"); + } + handle_progress (pfx, inp, filename); if (opt.textmode) @@ -706,17 +889,22 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (rc) goto leave; - cfx.dek->use_mdc = use_mdc (pk_list,cfx.dek->algo); + cfx.dek->use_aead = use_aead (pk_list, cfx.dek->algo); + if (!cfx.dek->use_aead) + cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ - if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2)) + if (do_compress + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && !opt.explicit_compress_option + && is_file_compressed (peekbuf, peekbuflen)) { if (opt.verbose) - log_info(_("'%s' already compressed\n"), filename); + log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); do_compress = 0; } if (rc2) @@ -737,7 +925,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ - if(use_symkey && (rc = write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) + if(use_symkey && (rc = write_symkey_enc (symkey_s2k, cfx.dek->use_aead, + symkey_dek, cfx.dek, out))) goto leave; if (!opt.no_literal) @@ -747,11 +936,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (filename && *filename && !iobuf_is_pipe_filename (filename) && !opt.textmode ) { - off_t tmpsize; - int overflow; + uint64_t tmpsize; - if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) - && !overflow && opt.verbose) + tmpsize = iobuf_get_filelength (inp); + if (!tmpsize && opt.verbose) log_info(_("WARNING: '%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the size @@ -780,7 +968,10 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, cfx.datalen = filesize && !do_compress ? filesize : 0; /* Register the cipher filter. */ - iobuf_push_filter (out, cipher_filter_cfb, &cfx); + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &cfx); /* Register the compress filter. */ if (do_compress) @@ -889,7 +1080,9 @@ encrypt_filter (void *opaque, int control, if (rc) return rc; - efx->cfx.dek->use_mdc = use_mdc (efx->pk_list,efx->cfx.dek->algo); + efx->cfx.dek->use_aead = use_aead (efx->pk_list, efx->cfx.dek->algo); + if (!efx->cfx.dek->use_aead) + efx->cfx.dek->use_mdc = !!use_mdc (efx->pk_list,efx->cfx.dek->algo); make_session_key ( efx->cfx.dek ); if (DBG_CRYPTO) @@ -902,13 +1095,16 @@ encrypt_filter (void *opaque, int control, if(efx->symkey_s2k && efx->symkey_dek) { - rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek, - efx->cfx.dek,a); + rc = write_symkey_enc (efx->symkey_s2k, efx->cfx.dek->use_aead, + efx->symkey_dek, efx->cfx.dek, a); if(rc) return rc; } - iobuf_push_filter (a, cipher_filter_cfb, &efx->cfx); + iobuf_push_filter (a, + efx->cfx.dek->use_aead? cipher_filter_ocb + /**/ : cipher_filter_cfb, + &efx->cfx); } rc = iobuf_write (a, buf, size); @@ -967,9 +1163,17 @@ write_pubkey_enc (ctrl_t ctrl, if ( opt.verbose ) { char *ustr = get_user_id_string_native (ctrl, enc->keyid); - log_info (_("%s/%s encrypted for: \"%s\"\n"), + if ((pk->pubkey_usage & PUBKEY_USAGE_RENC)) + { + char *tmpustr = xstrconcat (ustr, " [ADSK]", NULL); + xfree (ustr); + ustr = tmpustr; + } + log_info (_("%s/%s.%s encrypted for: \"%s\"\n"), openpgp_pk_algo_name (enc->pubkey_algo), openpgp_cipher_algo_name (dek->algo), + dek->use_aead? openpgp_aead_algo_name (dek->use_aead) + /**/ : "CFB", ustr ); xfree (ustr); } diff --git a/g10/export.c b/g10/export.c index e98af59..398b130 100644 --- a/g10/export.c +++ b/g10/export.c @@ -62,15 +62,17 @@ struct export_stats_s }; -/* A global variable to store the selector created from +/* Global variables to store the selectors created from * --export-filter keep-uid=EXPR. * --export-filter drop-subkey=EXPR. + * --export-filter select=EXPR. * * FIXME: We should put this into the CTRL object but that requires a * lot more changes right now. */ static recsel_expr_t export_keep_uid; static recsel_expr_t export_drop_subkey; +static recsel_expr_t export_select_filter; /* An object used for a linked list to implement the @@ -80,6 +82,7 @@ struct export_filter_attic_s struct export_filter_attic_s *next; recsel_expr_t export_keep_uid; recsel_expr_t export_drop_subkey; + recsel_expr_t export_select_filter; }; static struct export_filter_attic_s *export_filter_attic; @@ -105,6 +108,8 @@ cleanup_export_globals (void) export_keep_uid = NULL; recsel_release (export_drop_subkey); export_drop_subkey = NULL; + recsel_release (export_select_filter); + export_select_filter = NULL; } @@ -129,6 +134,9 @@ parse_export_options(char *str,unsigned int *options,int noisy) {"export-pka", EXPORT_PKA_FORMAT, NULL, NULL }, {"export-dane", EXPORT_DANE_FORMAT, NULL, NULL }, + {"export-revocs", EXPORT_REVOCS, NULL, + N_("export only revocation certificates") }, + {"backup", EXPORT_BACKUP, NULL, N_("use the GnuPG key backup format")}, {"export-backup", EXPORT_BACKUP, NULL, NULL }, @@ -181,6 +189,8 @@ parse_export_options(char *str,unsigned int *options,int noisy) * * - secret :: 1 for a secret subkey, else 0. * - key_algo :: Public key algorithm id + * + * - select :: The key is only exported if the filter returns true. */ gpg_error_t parse_and_set_export_filter (const char *string) @@ -194,6 +204,8 @@ parse_and_set_export_filter (const char *string) err = recsel_parse_expr (&export_keep_uid, string+9); else if (!strncmp (string, "drop-subkey=", 12)) err = recsel_parse_expr (&export_drop_subkey, string+12); + else if (!strncmp (string, "select=", 7)) + err = recsel_parse_expr (&export_select_filter, string+7); else err = gpg_error (GPG_ERR_INV_NAME); @@ -214,6 +226,8 @@ push_export_filters (void) export_keep_uid = NULL; item->export_drop_subkey = export_drop_subkey; export_drop_subkey = NULL; + item->export_select_filter = export_select_filter; + export_select_filter = NULL; item->next = export_filter_attic; export_filter_attic = item; } @@ -232,6 +246,7 @@ pop_export_filters (void) cleanup_export_globals (); export_keep_uid = item->export_keep_uid; export_drop_subkey = item->export_drop_subkey; + export_select_filter = item->export_select_filter; } @@ -1812,8 +1827,18 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, hexgrip, pk); if (err) { - if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) + /* If we receive a fully canceled error we stop + * immediately. If we receive a cancel for a public + * key we also stop immediately because a + * public/secret key is always required first + * (right, we could instead write a stub key but + * that is also kind of surprising). If we receive + * a subkey we skip to the next subkey. */ + if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED + || (node->pkt->pkttype == PKT_PUBLIC_KEY + && gpg_err_code (err) == GPG_ERR_CANCELED)) goto leave; + write_status_error ("export_keys.secret", err); skip_until_subkey = 1; err = 0; } @@ -1884,6 +1909,78 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, } +/* Helper for do_export_stream which writes the own revocations + * certificates (if any) from KEYBLOCK to OUT. */ +static gpg_error_t +do_export_revocs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, + iobuf_t out, unsigned int options, int *any) +{ + gpg_error_t err = 0; + kbnode_t kbctx, node; + PKT_signature *sig; + + (void)ctrl; + + /* NB: walk_kbnode skips packets marked as deleted. */ + for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) + { + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + + /* We are only interested in revocation certifcates. */ + if (!(IS_KEY_REV (sig) || IS_UID_REV (sig) || IS_SUBKEY_REV (sig))) + continue; + + if (!(sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1])) + continue; /* Not a self-signature. */ + + /* Do not export signature packets which are marked as not + * exportable. */ + if (!(options & EXPORT_LOCAL_SIGS) + && !sig->flags.exportable) + continue; /* not exportable */ + + /* Do not export packets with a "sensitive" revocation key + * unless the user wants us to. */ + if (!(options & EXPORT_SENSITIVE_REVKEYS) + && sig->revkey) + { + int i; + + for (i = 0; i < sig->numrevkeys; i++) + if ((sig->revkey[i].class & 0x40)) + break; + if (i < sig->numrevkeys) + continue; + } + + if (!sig->flags.checked) + { + log_info ("signature not marked as checked - ignored\n"); + continue; + } + if (!sig->flags.valid) + { + log_info ("signature not not valid - ignored\n"); + continue; + } + + err = build_packet (out, node->pkt); + if (err) + { + log_error ("build_packet(%d) failed: %s\n", + node->pkt->pkttype, gpg_strerror (err)); + goto leave; + } + *any = 1; + } + + leave: + return err; +} + + /* Export the keys identified by the list of strings in USERS to the stream OUT. If SECRET is false public keys will be exported. With secret true secret keys will be exported; in this case 1 means the @@ -2069,6 +2166,32 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, NULL, NULL); commit_kbnode (&keyblock); } + else if (export_keep_uid || export_drop_subkey || export_select_filter) + { + /* Need to merge so that for example the "usage" property + * has been setup. */ + merge_keys_and_selfsig (ctrl, keyblock); + } + + + if (export_select_filter) + { + int selected = 0; + struct impex_filter_parm_s parm; + parm.ctrl = ctrl; + + for (parm.node = keyblock; parm.node; parm.node = parm.node->next) + { + if (recsel_select (export_select_filter, + impex_filter_getval, &parm)) + { + selected = 1; + break; + } + } + if (!selected) + continue; /* Skip this keyblock. */ + } if (export_keep_uid) { @@ -2085,10 +2208,15 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } /* And write it. */ - err = do_export_one_keyblock (ctrl, keyblock, keyid, - out_help? out_help : out, - secret, options, stats, any, - desc, ndesc, descindex, cipherhd); + if ((options & EXPORT_REVOCS)) + err = do_export_revocs (ctrl, keyblock, keyid, + out_help? out_help : out, + options, any); + else + err = do_export_one_keyblock (ctrl, keyblock, keyid, + out_help? out_help : out, + secret, options, stats, any, + desc, ndesc, descindex, cipherhd); if (err) break; @@ -2131,8 +2259,8 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, keydb_release (kdbhd); if (err || !keyblock_out) release_kbnode( keyblock ); - if( !*any ) - log_info(_("WARNING: nothing exported\n")); + if( !*any && !opt.quiet) + log_info (_("WARNING: nothing exported\n")); return err; } diff --git a/g10/filter.h b/g10/filter.h index d2f6c3f..9a13777 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -88,15 +88,52 @@ struct compress_filter_context_s { typedef struct compress_filter_context_s compress_filter_context_t; -typedef struct { - DEK *dek; - u32 datalen; - gcry_cipher_hd_t cipher_hd; - unsigned int wrote_header : 1; - unsigned int short_blklen_warn : 1; - unsigned long short_blklen_count; - gcry_md_hd_t mdc_hash; - byte enchash[20]; +typedef struct +{ + /* Object with the key and algo */ + DEK *dek; + + /* Length of the data to encrypt if known - 32 bit because OpenPGP + * requires partial encoding for a larger data size. */ + u32 datalen; + + /* The current cipher handle. */ + gcry_cipher_hd_t cipher_hd; + + /* Various processing flags. */ + unsigned int wrote_header : 1; + unsigned int short_blklen_warn : 1; + unsigned long short_blklen_count; + + /* The encoded chunk byte for AEAD. */ + byte chunkbyte; + + /* The decoded CHUNKBYTE. */ + uint64_t chunksize; + + /* The chunk index for AEAD. */ + uint64_t chunkindex; + + /* The number of bytes in the current chunk. */ + uint64_t chunklen; + + /* The total count of encrypted plaintext octets. Note that we + * don't care about encrypting more than 16 Exabyte. */ + uint64_t total; + + /* The hash context and a buffer used for MDC. */ + gcry_md_hd_t mdc_hash; + byte enchash[20]; + + /* The start IV for AEAD encryption. */ + byte startiv[16]; + + /* Using a large buffer for encryption makes processing easier and + * also makes sure the data is well aligned. */ + char *buffer; + size_t bufsize; /* Allocated length. */ + size_t buflen; /* Used length. */ + } cipher_filter_context_t; @@ -118,9 +155,9 @@ typedef struct { typedef struct { char *what; /* description */ u32 last_time; /* last time reported */ - unsigned long last; /* last amount reported */ - unsigned long offset; /* current amount */ - unsigned long total; /* total amount */ + uint64_t last; /* last amount reported */ + uint64_t offset; /* current amount */ + uint64_t total; /* total amount */ int refcount; } progress_filter_context_t; @@ -148,6 +185,8 @@ gpg_error_t push_compress_filter2 (iobuf_t out,compress_filter_context_t *zfx, /*-- cipher.c --*/ int cipher_filter_cfb (void *opaque, int control, iobuf_t chain, byte *buf, size_t *ret_len); +int cipher_filter_ocb (void *opaque, int control, + iobuf_t chain, byte *buf, size_t *ret_len); /*-- textfilter.c --*/ int text_filter( void *opaque, int control, diff --git a/g10/getkey.c b/g10/getkey.c index 4642174..20b717b 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -767,9 +767,11 @@ get_seckey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid) if (!err) { - err = agent_probe_secret_key (/*ctrl*/NULL, pk); - if (err) - release_public_key_parts (pk); + if (!agent_probe_secret_key (/*ctrl*/NULL, pk)) + { + release_public_key_parts (pk); + err = gpg_error (GPG_ERR_NO_SECKEY); + } } return err; @@ -1794,7 +1796,8 @@ get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY is returned if the key - * is not found. + * is not found. If R_KEYBLOCK is not NULL and a key was found the + * keyblock is stored there; otherwiese NULL is stored there. * * The self-signed data has already been merged into the public key * using merge_selfsigs. The caller must release the content of PK by @@ -1802,13 +1805,17 @@ get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, * free_public_key). */ gpg_error_t -get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) +get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname, + kbnode_t *r_keyblock) { gpg_error_t err; kbnode_t keyblock; kbnode_t found_key; unsigned int infoflags; + if (r_keyblock) + *r_keyblock = NULL; + err = read_key_from_file_or_buffer (ctrl, fname, NULL, 0, &keyblock); if (!err) { @@ -1823,7 +1830,10 @@ get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } - release_kbnode (keyblock); + if (!err && r_keyblock) + *r_keyblock = keyblock; + else + release_kbnode (keyblock); return err; } @@ -1885,12 +1895,12 @@ get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf, * returned public key may be a subkey rather than the primary key. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling - * release_public_key_parts (or, if PK was allocated using xfree, you + * release_public_key_parts (or, if PK was allocated using xmalloc, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If PK->REQ_USAGE is set, it is used to filter the search results. - * (Thus, if PK is not NULL, PK->REQ_USAGE must be valid!!!) See the + * Thus, if PK is not NULL, PK->REQ_USAGE must be valid! See the * documentation for finish_lookup to understand exactly how this is * used. * @@ -2149,10 +2159,12 @@ parse_def_secret_key (ctrl_t ctrl) continue; } - err = agent_probe_secret_key (ctrl, pk); - if (! err) - /* This is a valid key. */ - break; + if (agent_probe_secret_key (ctrl, pk)) + { + /* This is a valid key. */ + err = 0; + break; + } } while ((node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY))); @@ -2491,7 +2503,8 @@ merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock) } -static int +/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */ +unsigned int parse_key_usage (PKT_signature * sig) { int key_usage = 0; @@ -2531,11 +2544,29 @@ parse_key_usage (PKT_signature * sig) flags &= ~0x20; } + if ((flags & 0x80)) + { + key_usage |= PUBKEY_USAGE_GROUP; + flags &= ~0x80; + } + if (flags) key_usage |= PUBKEY_USAGE_UNKNOWN; + n--; + p++; + if (n) + { + flags = *p; + if ((flags & 0x04)) + key_usage |= PUBKEY_USAGE_RENC; + if ((flags & 0x08)) + key_usage |= PUBKEY_USAGE_TIME; + } + if (!key_usage) key_usage |= PUBKEY_USAGE_NONE; + } else if (p) /* Key flags of length zero. */ key_usage |= PUBKEY_USAGE_NONE; @@ -2763,7 +2794,7 @@ merge_selfsigs_main (ctrl_t ctrl, kbnode_t keyblock, int *r_revoked, * and there was no way to change it, so we start with the one * from the key packet. We do not support v3 keys anymore but * we keep the code in case a future key versions introduces a - * hadr expire time again. */ + * hard expire time again. */ key_expire = pk->max_expiredate; key_expire_seen = 1; } @@ -3669,21 +3700,31 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* For an exact match mark the primary or subkey that matched the - low-level search criteria. */ - if (want_exact) + * low-level search criteria. Use this loop also to sort our keys + * found using an ADSK fingerprint. */ + for (k = keyblock; k; k = k->next) { - for (k = keyblock; k; k = k->next) - { - if ((k->flag & 1)) - { - log_assert (k->pkt->pkttype == PKT_PUBLIC_KEY - || k->pkt->pkttype == PKT_PUBLIC_SUBKEY); - foundk = k; + if ((k->flag & 1) && (k->pkt->pkttype == PKT_PUBLIC_KEY + || k->pkt->pkttype == PKT_PUBLIC_SUBKEY)) + { + if (want_exact) + { + if (DBG_LOOKUP) + log_debug ("finish_lookup: exact search requested and found\n"); + foundk = k; pk = k->pkt->pkt.public_key; pk->flags.exact = 1; - break; - } - } + break; + } + else if ((k->pkt->pkt.public_key->pubkey_usage == PUBKEY_USAGE_RENC)) + { + if (DBG_LOOKUP) + log_debug ("finish_lookup: found via ADSK - not selected\n"); + if (r_flags) + *r_flags |= LOOKUP_NOT_SELECTED; + return NULL; /* Not found. */ + } + } } /* Get the user id that matched that low-level search criteria. */ @@ -3779,7 +3820,7 @@ finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, continue; } - if (want_secret && agent_probe_secret_key (NULL, pk)) + if (want_secret && !agent_probe_secret_key (NULL, pk)) { if (DBG_LOOKUP) log_debug ("\tno secret key\n"); @@ -4528,7 +4569,7 @@ have_secret_key_with_kid (u32 *keyid) log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); - if (!agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) + if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) result = 1; /* Secret key available. */ else result = 0; @@ -1,7 +1,7 @@ /* gpg.c - The GnuPG utility (main for gpg) * Copyright (C) 1998-2020 Free Software Foundation, Inc. * Copyright (C) 1997-2019 Werner Koch - * Copyright (C) 2015-2021 g10 Code GmbH + * Copyright (C) 2015-2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -62,8 +62,11 @@ #include "tofu.h" #include "../common/init.h" #include "../common/mbox-util.h" +#include "../common/zb32.h" #include "../common/shareddefs.h" #include "../common/compliance.h" +#include "../kbx/keybox.h" + #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY @@ -128,6 +131,7 @@ enum cmd_and_opt_values aQuickRevUid, aQuickSetExpire, aQuickSetPrimaryUid, + aQuickUpdatePref, aListConfig, aListGcryptConfig, aGPGConfList, @@ -248,6 +252,7 @@ enum cmd_and_opt_values oCipherAlgo, oDigestAlgo, oCertDigestAlgo, + oNoCompress, oCompressAlgo, oCompressLevel, oBZ2CompressLevel, @@ -299,6 +304,7 @@ enum cmd_and_opt_values oShowPhotos, oNoShowPhotos, oPhotoViewer, + oForceOCB, oS2KMode, oS2KDigest, oS2KCipher, @@ -348,7 +354,6 @@ enum cmd_and_opt_values oShowSessionKey, oOverrideSessionKey, oOverrideSessionKeyFD, - oOverrideComplianceCheck, oNoRandomSeedFile, oAutoKeyRetrieve, oNoAutoKeyRetrieve, @@ -431,6 +436,8 @@ enum cmd_and_opt_values oForceSignKey, oForbidGenKey, oRequireCompliance, + oCompatibilityFlags, + oAddDesigRevoker, oNoop }; @@ -478,6 +485,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_c (aQuickSetExpire, "quick-set-expire", N_("quickly set a new expiration date")), ARGPARSE_c (aQuickSetPrimaryUid, "quick-set-primary-uid", "@"), + ARGPARSE_c (aQuickUpdatePref, "quick-update-pref", "@"), ARGPARSE_c (aFullKeygen, "full-generate-key" , N_("full featured key pair generation")), ARGPARSE_c (aFullKeygen, "full-gen-key", "@"), @@ -672,6 +680,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oLockOnce, "lock-once", "@"), ARGPARSE_s_n (oLockMultiple, "lock-multiple", "@"), ARGPARSE_s_n (oLockNever, "lock-never", "@"), + ARGPARSE_s_n (oNoCompress, "no-compress", "@"), ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"), ARGPARSE_s_s (oCompressAlgo, "compression-algo", "@"), /* Alias */ ARGPARSE_s_n (oBZ2DecompressLowmem, "bzip2-decompress-lowmem", "@"), @@ -684,6 +693,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoAutoCheckTrustDB, "no-auto-check-trustdb", "@"), ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"), #endif + ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"), ARGPARSE_header ("Input", N_("Options controlling the input")), @@ -834,6 +844,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"), ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"), ARGPARSE_s_i (oS2KCount, "s2k-count", "@"), + ARGPARSE_s_n (oForceOCB, "force-ocb", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"), ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"), @@ -851,7 +862,6 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), - ARGPARSE_s_n (oOverrideComplianceCheck, "override-compliance-check", "@"), /* Options to override new security defaults. */ ARGPARSE_s_n (oAllowWeakKeySignatures, "allow-weak-key-signatures", "@"), ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"), @@ -894,6 +904,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), ARGPARSE_s_n (oForbidGenKey, "forbid-gen-key", "@"), ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"), + ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"), /* Options which can be used in special circumstances. They are not * published and we hope they are never required. */ ARGPARSE_s_n (oUseOnlyOpenPGPCard, "use-only-openpgp-card", "@"), @@ -947,6 +958,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoop, "no-force-mdc", "@"), ARGPARSE_s_n (oNoop, "disable-mdc", "@"), ARGPARSE_s_n (oNoop, "no-disable-mdc", "@"), + ARGPARSE_s_n (oNoop, "override-compliance-check", "@"), ARGPARSE_group (302, N_( @@ -986,6 +998,14 @@ static struct debug_flags_s debug_flags [] = }; +/* The list of compatibility flags. */ +static struct compatibility_flags_s compatibility_flags [] = + { + { COMPAT_VSD_ALLOW_OCB, "vsd-allow-ocb" }, + { 0, NULL } + }; + + #ifdef ENABLE_SELINUX_HACKS #define ALWAYS_ADD_KEYRINGS 1 #else @@ -2005,6 +2025,8 @@ parse_list_options(char *str) char *subpackets=""; /* something that isn't NULL */ struct parse_options lopts[]= { + {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, + NULL}, {"show-photos",LIST_SHOW_PHOTOS,NULL, N_("display photo IDs during key listings")}, {"show-usage",LIST_SHOW_USAGE,NULL, @@ -2031,18 +2053,27 @@ parse_list_options(char *str) N_("show the keyring name in key listings")}, {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL, N_("show expiration dates during signature listings")}, - {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, - NULL}, + {"show-pref", LIST_SHOW_PREF, NULL, + N_("show preferences")}, + {"show-pref-verbose", LIST_SHOW_PREF_VERBOSE, NULL, + N_("show preferences")}, {"show-only-fpr-mbox",LIST_SHOW_ONLY_FPR_MBOX, NULL, NULL}, {NULL,0,NULL,NULL} }; + int i; /* C99 allows for non-constant initializers, but we'd like to compile everywhere, so fill in the show-sig-subpackets argument here. Note that if the parse_options array changes, we'll have - to change the subscript here. */ - lopts[13].value=&subpackets; + to change the subscript here. We use a loop here in case the + list above is reordered. */ + for (i=0; lopts[i].name; i++) + if (lopts[i].bit == LIST_SHOW_SIG_SUBPACKETS) + { + lopts[i].value = &subpackets; + break; + } if(parse_options(str,&opt.list_options,lopts,1)) { @@ -2609,6 +2640,7 @@ main (int argc, char **argv) case aQuickRevUid: case aQuickSetExpire: case aQuickSetPrimaryUid: + case aQuickUpdatePref: case aExportOwnerTrust: case aImportOwnerTrust: case aRebuildKeydbCaches: @@ -2736,6 +2768,15 @@ main (int argc, char **argv) case oDebugIOLBF: break; /* Already set in pre-parse step. */ + case oCompatibilityFlags: + if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags, + compatibility_flags)) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + case oStatusFD: set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) ); break; @@ -2981,6 +3022,8 @@ main (int argc, char **argv) break; case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break; + case oForceOCB: opt.force_ocb = 1; break; + case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break; case oIncludeKeyBlock: opt.flags.include_key_block = 1; break; case oNoIncludeKeyBlock: opt.flags.include_key_block = 0; break; @@ -3096,6 +3139,12 @@ main (int argc, char **argv) case oCompress: /* this is the -z command line option */ opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int; + opt.explicit_compress_option = 1; + break; + case oNoCompress: + /* --no-compress is the same as -z0 */ + opt.compress_level = opt.bz2_compress_level = 0; + opt.explicit_compress_option = 1; break; case oCompressLevel: opt.compress_level = pargs.r.ret_int; break; case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break; @@ -3335,7 +3384,7 @@ main (int argc, char **argv) break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: -#ifdef HAVE_W32_SYSTEM +#ifndef HAVE_W32_SYSTEM utf8_strings = 0; #endif break; @@ -3357,7 +3406,13 @@ main (int argc, char **argv) case oAllowFreeformUID: opt.allow_freeform_uid = 1; break; case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break; case oNoLiteral: opt.no_literal = 1; break; - case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break; + + case oSetFilesize: + /* There are restricts on the value (e.g. < 2^32); you + * need to check the entire code to understand this. */ + opt.set_filesize = pargs.r.ret_ulong; + break; + case oFastListMode: opt.fast_list_mode = 1; break; case oFixedListMode: /* Dummy */ break; case oLegacyListMode: opt.legacy_list_mode = 1; break; @@ -3562,10 +3617,6 @@ main (int argc, char **argv) opt.flags.allow_weak_key_signatures = 1; break; - case oOverrideComplianceCheck: - opt.flags.override_compliance_check = 1; - break; - case oFakedSystemTime: { size_t len = strlen (pargs.r.ret_str); @@ -3604,6 +3655,13 @@ main (int argc, char **argv) opt.flags.require_compliance = 1; break; + case oAddDesigRevoker: + if (!strcmp (pargs.r.ret_str, "clear")) + FREE_STRLIST (opt.desig_revokers); + else + append_to_strlist (&opt.desig_revokers, pargs.r.ret_str); + break; + case oNoop: break; default: @@ -3762,17 +3820,14 @@ main (int argc, char **argv) g10_exit(2); } - /* We allow overriding the compliance check only in non-batch mode - * so that the user has a chance to see the message. */ - if (opt.flags.override_compliance_check && opt.batch) - { - opt.flags.override_compliance_check = 0; - log_info ("Note: '%s' ignored due to batch mode\n", - "--override-compliance-check"); - } - set_debug (debug_level); - gnupg_set_compliance_extra_info (opt.min_rsa_length); + if (opt.verbose) /* Print the compatibility flags. */ + parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags); + + gnupg_set_compliance_extra_info (CO_EXTRA_INFO_MIN_RSA, opt.min_rsa_length); + if ((opt.compat_flags & COMPAT_VSD_ALLOW_OCB)) + gnupg_set_compliance_extra_info (CO_EXTRA_INFO_VSD_ALLOW_OCB, 1); + if (DBG_CLOCK) log_clock ("start"); @@ -4154,6 +4209,7 @@ main (int argc, char **argv) case aQuickAddKey: case aQuickRevUid: case aQuickSetPrimaryUid: + case aQuickUpdatePref: case aFullKeygen: case aKeygen: case aImport: @@ -4654,6 +4710,14 @@ main (int argc, char **argv) } break; + case aQuickUpdatePref: + { + if (argc != 1) + wrong_args ("--quick-update-pref USER-ID"); + keyedit_quick_update_pref (ctrl, *argv); + } + break; + case aFastImport: opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: @@ -4863,42 +4927,74 @@ main (int argc, char **argv) case aGenRandom: { - int level = argc ? atoi(*argv):0; - int count = argc > 1 ? atoi(argv[1]): 0; - int endless = !count; - - if( argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0 ) - wrong_args("--gen-random 0|1|2 [count]"); - - while( endless || count ) { - byte *p; - /* Wee need a multiple of 3, so that in case of - armored output we get a correct string. No - linefolding is done, as it is best to levae this to - other tools */ - size_t n = !endless && count < 99? count : 99; - - p = gcry_random_bytes (n, level); -#ifdef HAVE_DOSISH_SYSTEM - setmode ( fileno(stdout), O_BINARY ); -#endif - if (opt.armor) { - char *tmp = make_radix64_string (p, n); - es_fputs (tmp, es_stdout); - xfree (tmp); - if (n%3 == 1) - es_putc ('=', es_stdout); - if (n%3) - es_putc ('=', es_stdout); - } else { - es_fwrite( p, n, 1, es_stdout ); + int level = argc ? atoi(*argv):0; + int count = argc > 1 ? atoi(argv[1]): 0; + int endless = !count; + int hexhack = (level == 16); + + if (hexhack) + level = 1; + + /* Level 30 uses the same algorithm as our magic wand in + * pinentry/gpg-agent. */ + if (level == 30) + { + unsigned int nbits = 150; + size_t nbytes = (nbits + 7) / 8; + void *rand; + char *generated; + + rand = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM); + if (!rand) + log_fatal ("failed to generate random password\n"); + + generated = zb32_encode (rand, nbits); + gcry_free (rand); + es_fputs (generated, es_stdout); + es_putc ('\n', es_stdout); + xfree (generated); + break; + } + + if (argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0) + wrong_args ("--gen-random 0|1|2|16|30 [count]"); + + while (endless || count) + { + byte *p; + /* We need a multiple of 3, so that in case of armored + * output we get a correct string. No linefolding is + * done, as it is best to leave this to other tools */ + size_t n = !endless && count < 99? count : 99; + size_t nn; + + p = gcry_random_bytes (n, level); + if (hexhack) + { + for (nn = 0; nn < n; nn++) + es_fprintf (es_stdout, "%02x", p[nn]); + } + else if (opt.armor) + { + char *tmp = make_radix64_string (p, n); + es_fputs (tmp, es_stdout); + xfree (tmp); + if (n%3 == 1) + es_putc ('=', es_stdout); + if (n%3) + es_putc ('=', es_stdout); } - xfree(p); - if( !endless ) - count -= n; + else + { + es_set_binary (es_stdout); + es_fwrite( p, n, 1, es_stdout ); + } + xfree(p); + if (!endless) + count -= n; } - if (opt.armor) - es_putc ('\n', es_stdout); + if (opt.armor || hexhack) + es_putc ('\n', es_stdout); } break; diff --git a/g10/gpg.w32-manifest.in b/g10/gpg.w32-manifest.in index 24484db..418fa78 100644 --- a/g10/gpg.w32-manifest.in +++ b/g10/gpg.w32-manifest.in @@ -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/g10/gpgcompose.c b/g10/gpgcompose.c index d82995d..d3b0c14 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -2169,6 +2169,42 @@ static struct option sk_esk_options[] = { " --literal --value foo | " GPG_NAME " --list-packets" } }; + +/* Old version of encrypt_seskey copied from encrypt.c. */ +static void +encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey) +{ + gcry_cipher_hd_t hd; + byte buf[33]; + + log_assert ( dek->keylen <= 32 ); + if (!*seskey) + { + *seskey=xmalloc_clear(sizeof(DEK)); + (*seskey)->algo=dek->algo; + make_session_key(*seskey); + /*log_hexdump( "thekey", c->key, c->keylen );*/ + } + + /* The encrypted session key is prefixed with a one-octet algorithm id. */ + buf[0] = (*seskey)->algo; + memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen ); + + /* We only pass already checked values to the following function, + thus we consider any failure as fatal. */ + if (openpgp_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) + BUG (); + if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) + BUG (); + gcry_cipher_setiv (hd, NULL, 0); + gcry_cipher_encrypt (hd, buf, (*seskey)->keylen + 1, NULL, 0); + gcry_cipher_close (hd); + + memcpy( enckey, buf, (*seskey)->keylen + 1 ); + wipememory( buf, sizeof buf ); /* burn key */ +} + + static int sk_esk (const char *option, int argc, char *argv[], void *cookie) { @@ -2833,8 +2869,7 @@ literal (const char *option, int argc, char *argv[], void *cookie) if (data->file) { iobuf_t in; - int overflow; - off_t off; + uint64_t off; in = iobuf_open (data->filename); if (! in) @@ -2845,10 +2880,10 @@ literal (const char *option, int argc, char *argv[], void *cookie) break; } - off = iobuf_get_filelength (in, &overflow); + off = iobuf_get_filelength (in); iobuf_close (in); - if (overflow || off == 0) + if (off == 0) /* Length is unknown or there was an error (unfortunately, iobuf_get_filelength doesn't distinguish between 0 length files and an error!). diff --git a/g10/gpgv-w32info.rc b/g10/gpgv-w32info.rc index 9182fa4..a6c1b6c 100644 --- a/g10/gpgv-w32info.rc +++ b/g10/gpgv-w32info.rc @@ -1,5 +1,5 @@ -/* gpgv-w32info.rc -*- c -*- - * Copyright (C) 2020 g10 Code GmbH +/* gpgv-w32info.rc -*- c -*- + * Copyright (C) 2013 g10 Code GmbH * * This file is free software; as a special exception the author gives * unlimited permission to copy and/or distribute it, with or without @@ -706,12 +706,12 @@ dotlock_remove_lockfiles (void) { } -gpg_error_t +int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { (void)ctrl; (void)pk; - return gpg_error (GPG_ERR_NO_SECKEY); + return 0; } gpg_error_t diff --git a/g10/gpgv.w32-manifest.in b/g10/gpgv.w32-manifest.in index b7a2120..5dd8408 100644 --- a/g10/gpgv.w32-manifest.in +++ b/g10/gpgv.w32-manifest.in @@ -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/g10/import.c b/g10/import.c index b2d5c1d..f11dedc 100644 --- a/g10/import.c +++ b/g10/import.c @@ -126,7 +126,8 @@ static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self); static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, - u32 *keyid, unsigned int options); + u32 *keyid, unsigned int options, + kbnode_t *r_otherrevsigs); static int any_uid_left (kbnode_t keyblock); static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid); static int merge_blocks (ctrl_t ctrl, unsigned int options, @@ -420,7 +421,7 @@ read_key_from_file_or_buffer (ctrl_t ctrl, const char *fname, goto leave; } - if (!delete_inv_parts (ctrl, keyblock, keyid, 0) ) + if (!delete_inv_parts (ctrl, keyblock, keyid, 0, NULL) ) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; @@ -1830,8 +1831,9 @@ update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) * even most error messages are suppressed. ORIGIN is the origin of * the key (0 for unknown) and URL the corresponding URL. FROM_SK * indicates that the key has been made from a secret key. If R_SAVED - * is not NULL a boolean will be stored indicating whether the keyblock - * has valid parts. + * is not NULL a boolean will be stored indicating whether the + * keyblock has valid parts. Unless OTHERREVSIGS is NULL it is + * updated with encountered new revocation signatures. */ static gpg_error_t import_one_real (ctrl_t ctrl, @@ -1839,7 +1841,8 @@ import_one_real (ctrl_t ctrl, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, - int origin, const char *url, int *r_valid) + int origin, const char *url, int *r_valid, + kbnode_t *otherrevsigs) { gpg_error_t err = 0; PKT_public_key *pk; @@ -1974,7 +1977,8 @@ import_one_real (ctrl_t ctrl, } } - if (!delete_inv_parts (ctrl, keyblock, keyid, options ) ) + /* Delete invalid parts and bail out if there are no user ids left. */ + if (!delete_inv_parts (ctrl, keyblock, keyid, options, otherrevsigs)) { if (!silent) { @@ -2366,10 +2370,12 @@ import_one (ctrl_t ctrl, int origin, const char *url, int *r_valid) { gpg_error_t err; + kbnode_t otherrevsigs = NULL; + kbnode_t node; err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, from_sk, silent, screener, screener_arg, - origin, url, r_valid); + origin, url, r_valid, &otherrevsigs); if (gpg_err_code (err) == GPG_ERR_TOO_LARGE && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX && ((options & (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN)) @@ -2385,8 +2391,17 @@ import_one (ctrl_t ctrl, options |= IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN; err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, from_sk, silent, screener, screener_arg, - origin, url, r_valid); + origin, url, r_valid, &otherrevsigs); + } + + /* Finally try to import other revocation certificates. For example + * those of a former key appended to the current key. */ + if (!err) + { + for (node = otherrevsigs; node; node = node->next) + import_revoke_cert (ctrl, node, options, stats); } + release_kbnode (otherrevsigs); return err; } @@ -2821,9 +2836,19 @@ do_transfer (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *pk, { gpg_error_t err; struct import_stats_s subkey_stats = {0}; + int force = 0; + int already_exist = agent_probe_secret_key (ctrl, pk); + + if (already_exist == 2) + { + if (!opt.quiet) + log_info (_("key %s: card reference is overridden by key material\n"), + keystr_from_pk (pk)); + force = 1; + } err = transfer_secret_keys (ctrl, &subkey_stats, keyblock, - batch, 0, only_marked); + batch, force, only_marked); if (gpg_err_code (err) == GPG_ERR_NOT_PROCESSED) { /* TRANSLATORS: For a smartcard, each private key on host has a @@ -3358,9 +3383,8 @@ list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) } -/**************** - * Import a revocation certificate; this is a single signature packet. - */ +/* Import a revocation certificate; only the first packet in the + * NODE-list is considered. */ static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, struct import_stats_s *stats) @@ -3377,10 +3401,12 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, /* No error output for --show-keys. */ silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN)); - log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); log_assert (IS_KEY_REV (node->pkt->pkt.signature)); + /* FIXME: We can do better here by using the issuer fingerprint if + * available. We should also make use of get_keyblock_byfprint_fast. */ + keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; @@ -3726,12 +3752,15 @@ chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) /* Delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implementation; but * consider RSA as valid, because parse/build_packets knows about it. + * If R_OTHERREVSIGS is not NULL, it is used to return a list of + * revocation certificates which have been deleted from KEYBLOCK but + * should be handled later. * * Returns: True if at least one valid user-id is left over. */ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, - unsigned int options) + unsigned int options, kbnode_t *r_otherrevsigs) { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; @@ -3820,6 +3849,16 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, if(opt.verbose) log_info( _("key %s: revocation certificate" " at wrong place - skipped\n"),keystr(keyid)); + if (r_otherrevsigs) + { + PACKET *pkt; + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = copy_signature + (NULL, node->pkt->pkt.signature); + *r_otherrevsigs = new_kbnode2 (*r_otherrevsigs, pkt); + } delete_kbnode( node ); } else @@ -3842,6 +3881,16 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, delete_kbnode( node ); } } + else if (r_otherrevsigs) + { + PACKET *pkt; + + pkt = xcalloc (1, sizeof *pkt); + pkt->pkttype = PKT_SIGNATURE; + pkt->pkt.signature = copy_signature + (NULL, node->pkt->pkt.signature); + *r_otherrevsigs = new_kbnode2 (*r_otherrevsigs, pkt); + } } } else if (node->pkt->pkttype == PKT_SIGNATURE diff --git a/g10/kbnode.c b/g10/kbnode.c index 9ed6caf..aa1e17c 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -98,6 +98,19 @@ new_kbnode( PACKET *pkt ) } +/* Same as new_kbnode but insert the new node in front of LIST. Returns + * the new list. */ +kbnode_t +new_kbnode2 (kbnode_t list, PACKET *pkt) +{ + kbnode_t n; + + n = new_kbnode (pkt); + n->next = list; + return n; +} + + KBNODE clone_kbnode( KBNODE node ) { diff --git a/g10/keydb.h b/g10/keydb.h index 0f8d711..9c35ccf 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -266,8 +266,8 @@ int algo_available( preftype_t preftype, int algo, const struct pref_hint *hint ); int select_algo_from_prefs( PK_LIST pk_list, int preftype, int request, const struct pref_hint *hint); -int select_mdc_from_pklist (PK_LIST pk_list); -void warn_missing_mdc_from_pklist (PK_LIST pk_list); +aead_algo_t select_aead_from_pklist (PK_LIST pk_list); +void warn_missing_aead_from_pklist (PK_LIST pk_list); void warn_missing_aes_from_pklist (PK_LIST pk_list); /*-- skclist.c --*/ @@ -377,7 +377,8 @@ gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, /* Get a public key directly from file FNAME. */ gpg_error_t get_pubkey_fromfile (ctrl_t ctrl, - PKT_public_key *pk, const char *fname); + PKT_public_key *pk, const char *fname, + kbnode_t *r_keyblock); /* Get a public key from a buffer. */ gpg_error_t get_pubkey_from_buffer (ctrl_t ctrl, PKT_public_key *pkbuf, @@ -453,6 +454,9 @@ void setup_main_keyids (kbnode_t keyblock); data structures. */ void merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock); +/* This function parses the key flags and returns PUBKEY_USAGE_ flags. */ +unsigned int parse_key_usage (PKT_signature *sig); + char *get_user_id_string_native (ctrl_t ctrl, u32 *keyid); char *get_long_user_id_string (ctrl_t ctrl, u32 *keyid); char *get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid); @@ -548,10 +552,12 @@ char *format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen); gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array); gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); +char *ecdh_param_str_from_pk (PKT_public_key *pk); /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); +kbnode_t new_kbnode2 (kbnode_t list, PACKET *pkt); KBNODE clone_kbnode( KBNODE node ); void release_kbnode( KBNODE n ); void delete_kbnode( KBNODE node ); diff --git a/g10/keyedit.c b/g10/keyedit.c index 1cb62de..01de7bb 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,7 +1,7 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch - * Copyright (C) 2015, 2016 g10 Code GmbH + * Copyright (C) 2015, 2016, 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -78,7 +78,8 @@ static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock); -static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock); +static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock, + int unattended); static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock); static int menu_set_notation (ctrl_t ctrl, @@ -1415,6 +1416,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, int sec_shadowing = 0; int run_subkey_warnings = 0; int have_commands = !!commands; + strlist_t delseckey_list = NULL; + int delseckey_list_warn = 0; if (opt.command_fd != -1) ; @@ -1454,7 +1457,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, { have_anyseckey = !agent_probe_any_secret_key (ctrl, keyblock); if (have_anyseckey - && !agent_probe_secret_key (ctrl, keyblock->pkt->pkt.public_key)) + && agent_probe_secret_key (ctrl, keyblock->pkt->pkt.public_key)) { /* The primary key is also available. */ have_seckey = 1; @@ -1491,6 +1494,14 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, subkey_expire_warning (keyblock); } + if (delseckey_list_warn) + { + delseckey_list_warn = 0; + tty_printf + (_("Note: the local copy of the secret key" + " will only be deleted with \"save\".\n")); + } + do { xfree (answer); @@ -1822,10 +1833,12 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, if (node) { PKT_public_key *xxpk = node->pkt->pkt.public_key; - if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0)) + if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0, + &delseckey_list)) { redisplay = 1; sec_shadowing = 1; + delseckey_list_warn = 1; } } } @@ -1902,7 +1915,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, pkt->pkttype = PKT_PUBLIC_KEY; /* Ask gpg-agent to store the secret key to card. */ - if (card_store_subkey (node, 0)) + if (card_store_subkey (node, 0, NULL)) { redisplay = 1; sec_shadowing = 1; @@ -2111,7 +2124,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, " for the selected user IDs? (y/N) ") : _("Really update the preferences? (y/N) "))) { - if (menu_set_preferences (ctrl, keyblock)) + if (menu_set_preferences (ctrl, keyblock, 0)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; @@ -2203,6 +2216,27 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } } + if (delseckey_list) + { + strlist_t sl; + for (err = 0, sl = delseckey_list; sl; sl = sl->next) + { + if (*sl->d) + { + err = agent_delete_key (ctrl, sl->d, NULL, 1/*force*/); + if (err) + break; + *sl->d = 0; /* Mark deleted. */ + } + } + if (err) + { + log_error (_("deleting copy of secret key failed: %s\n"), + gpg_strerror (err)); + break; /* the "save". */ + } + } + if (sec_shadowing) { err = agent_scd_learn (NULL, 1); @@ -2232,6 +2266,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, } /* End of the main command loop. */ leave: + free_strlist (delseckey_list); release_kbnode (keyblock); keydb_release (kdbhd); xfree (answer); @@ -2324,7 +2359,8 @@ quick_find_keyblock (ctrl_t ctrl, const char *username, int want_secret, /* We require the secret primary key to set the primary UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); log_assert (node); - err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key); + if (!agent_probe_secret_key (ctrl, node->pkt->pkt.public_key)) + err = gpg_error (GPG_ERR_NO_SECKEY); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) @@ -2604,6 +2640,45 @@ keyedit_quick_set_primary (ctrl_t ctrl, const char *username, } +/* Unattended updating of the preference tro the standard preferences. + * USERNAME specifies the key. This is basically the same as + * gpg --edit-key <<userif> updpref save + */ +void +keyedit_quick_update_pref (ctrl_t ctrl, const char *username) +{ + gpg_error_t err; + KEYDB_HANDLE kdbhd = NULL; + kbnode_t keyblock = NULL; + +#ifdef HAVE_W32_SYSTEM + /* See keyedit_menu for why we need this. */ + check_trustdb_stale (ctrl); +#endif + + err = quick_find_keyblock (ctrl, username, 1, &kdbhd, &keyblock); + if (err) + goto leave; + + if (menu_set_preferences (ctrl, keyblock, 1)) + { + merge_keys_and_selfsig (ctrl, keyblock); + err = keydb_update_keyblock (ctrl, kdbhd, keyblock); + if (err) + { + log_error (_("update failed: %s\n"), gpg_strerror (err)); + goto leave; + } + } + + leave: + if (err) + write_status_error ("keyedit.updpref", err); + release_kbnode (keyblock); + keydb_release (kdbhd); +} + + /* Find a keyblock by fingerprint because only this uniquely * identifies a key and may thus be used to select a key for * unattended subkey creation os key signing. */ @@ -2684,7 +2759,7 @@ void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local) { - gpg_error_t err; + gpg_error_t err = 0; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int modified = 0; @@ -2722,6 +2797,7 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), _(" Unable to sign.\n")); + err = gpg_error (GPG_ERR_CERT_REVOKED); goto leave; } @@ -2799,6 +2875,7 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, sl->d, gpg_strerror (GPG_ERR_NOT_FOUND)); } log_error ("%s %s", _("No matching user IDs."), _("Nothing to sign.\n")); + err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } @@ -2821,8 +2898,9 @@ keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, if (update_trust) revalidation_mark (ctrl); - leave: + if (err) + write_status_error ("keyedit.sign-key", err); release_kbnode (keyblock); keydb_release (kdbhd); } @@ -2838,7 +2916,7 @@ void keyedit_quick_revsig (ctrl_t ctrl, const char *username, const char *sigtorev, strlist_t affected_uids) { - gpg_error_t err; + gpg_error_t err = 0; int no_signing_key = 0; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; @@ -3316,146 +3394,12 @@ tty_print_notations (int indent, PKT_signature * sig) static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) { - const prefitem_t fake = { 0, 0 }; - const prefitem_t *prefs; - int i; - if (!uid) return; - if (uid->prefs) - prefs = uid->prefs; - else if (verbose) - prefs = &fake; - else - return; - if (verbose) { - int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; - - tty_printf (" "); - tty_printf (_("Cipher: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_SYM) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!openpgp_cipher_test_algo (prefs[i].value) - && prefs[i].value < 100) - tty_printf ("%s", openpgp_cipher_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == CIPHER_ALGO_3DES) - des_seen = 1; - } - } - if (!des_seen) - { - if (any) - tty_printf (", "); - tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); - } - tty_printf ("\n "); - tty_printf (_("AEAD: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_AEAD) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!openpgp_aead_test_algo (prefs[i].value) - && prefs[i].value < 100) - tty_printf ("%s", openpgp_aead_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - } - } - tty_printf ("\n "); - tty_printf (_("Digest: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_HASH) - { - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) - tty_printf ("%s", gcry_md_algo_name (prefs[i].value)); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == DIGEST_ALGO_SHA1) - sha1_seen = 1; - } - } - if (!sha1_seen) - { - if (any) - tty_printf (", "); - tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); - } - tty_printf ("\n "); - tty_printf (_("Compression: ")); - for (i = any = 0; prefs[i].type; i++) - { - if (prefs[i].type == PREFTYPE_ZIP) - { - const char *s = compress_algo_to_string (prefs[i].value); - - if (any) - tty_printf (", "); - any = 1; - /* We don't want to display strings for experimental algos */ - if (s && prefs[i].value < 100) - tty_printf ("%s", s); - else - tty_printf ("[%d]", prefs[i].value); - if (prefs[i].value == COMPRESS_ALGO_NONE) - uncomp_seen = 1; - } - } - if (!uncomp_seen) - { - if (any) - tty_printf (", "); - else - { - tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_ZIP)); - tty_printf (", "); - } - tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); - } - if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) - { - tty_printf ("\n "); - tty_printf (_("Features: ")); - any = 0; - if (uid->flags.mdc) - { - tty_printf ("MDC"); - any = 1; - } - if (!uid->flags.aead) - { - if (any) - tty_printf (", "); - tty_printf ("AEAD"); - } - if (!uid->flags.ks_modify) - { - if (any) - tty_printf (", "); - tty_printf (_("Keyserver no-modify")); - } - } - tty_printf ("\n"); - + show_preferences (uid, 4, -1, 1); if (selfsig) { const byte *pref_ks; @@ -3481,22 +3425,7 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) } else { - tty_printf (" "); - for (i = 0; prefs[i].type; i++) - { - tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : - prefs[i].type == PREFTYPE_AEAD ? 'A' : - prefs[i].type == PREFTYPE_HASH ? 'H' : - prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', - prefs[i].value); - } - if (uid->flags.mdc) - tty_printf (" [mdc]"); - if (uid->flags.aead) - tty_printf (" [aead]"); - if (!uid->flags.ks_modify) - tty_printf (" [no-ks-modify]"); - tty_printf ("\n"); + show_preferences (uid, 4, -1, 0); } } @@ -3532,7 +3461,7 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) } keyid_from_pk (pk, keyid); - have_seckey = !agent_probe_secret_key (ctrl, pk); + have_seckey = agent_probe_secret_key (ctrl, pk); if (node->pkt->pkttype == PKT_PUBLIC_KEY) es_fputs (have_seckey? "sec:" : "pub:", fp); @@ -3573,6 +3502,12 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) es_putc ('c', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) es_putc ('a', fp); + if ((pk->pubkey_usage & PUBKEY_USAGE_RENC)) + es_putc ('r', fp); + if ((pk->pubkey_usage & PUBKEY_USAGE_TIME)) + es_putc ('t', fp); + if ((pk->pubkey_usage & PUBKEY_USAGE_GROUP)) + es_putc ('g', fp); es_putc ('\n', fp); print_fingerprint (ctrl, fp, pk, 0); @@ -5212,10 +5147,11 @@ menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock) /* - * Set preferences to new values for the selected user IDs + * Set preferences to new values for the selected user IDs. + * --quick-update-pred calls this with UNATTENDED set. */ static int -menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) +menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended) { PKT_public_key *main_pk; PKT_user_id *uid; @@ -5224,9 +5160,10 @@ menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) int selected, select_all; int modified = 0; - no_primary_warning (pub_keyblock); + if (!unattended) + no_primary_warning (pub_keyblock); - select_all = !count_selected_uids (pub_keyblock); + select_all = unattended? 1 : !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; diff --git a/g10/keyedit.h b/g10/keyedit.h index 1aa95c1..e397b4a 100644 --- a/g10/keyedit.h +++ b/g10/keyedit.h @@ -54,6 +54,7 @@ void keyedit_quick_set_expire (ctrl_t ctrl, char **subkeyfprs); void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid); +void keyedit_quick_update_pref (ctrl_t ctrl, const char *username); void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock, int print_sec); int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, int rc, kbnode_t keyblock, diff --git a/g10/keygen.c b/g10/keygen.c index 80d65c4..52fd97a 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -54,7 +54,7 @@ /* When generating keys using the streamlined key generation dialog, use this as a default expiration interval. */ -const char *default_expiration_interval = "2y"; +const char *default_expiration_interval = "3y"; /* Flag bits used during key generation. */ #define KEYGEN_FLAG_NO_PROTECTION 1 @@ -121,9 +121,11 @@ struct output_control_s }; -struct opaque_data_usage_and_pk { - unsigned int usage; - PKT_public_key *pk; +struct opaque_data_usage_and_pk +{ + unsigned int usage; + const char *cpl_notation; + PKT_public_key *pk; }; @@ -135,6 +137,8 @@ static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static int mdc_available,ks_modify; +static int aead_available; + static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, @@ -151,6 +155,9 @@ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, u32 expireval); static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max); +static void do_add_notation (PKT_signature *sig, + const char *name, const char *value, + int critical); @@ -301,12 +308,16 @@ keygen_add_key_flags (PKT_signature *sig, void *opaque) } +/* This is only used to write the key binding signature. It is not + * used for the primary key. */ static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; do_add_key_flags (sig, oduap->usage); + if (oduap->cpl_notation) + do_add_notation (sig, "cpl@gnupg.org", oduap->cpl_notation, 0); return keygen_add_key_expire (sig, oduap->pk); } @@ -354,8 +365,12 @@ keygen_set_std_prefs (const char *string,int personal) byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ + int ocb; char dummy_string[20*4+1]; /* Enough for 20 items. */ + /* Use OCB as default in GnuPG and de-vs mode. */ + ocb = GNUPG; + if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) @@ -480,14 +495,24 @@ keygen_set_std_prefs (const char *string,int personal) if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } - else if (ascii_strcasecmp(tok,"mdc")==0) + else if (!ascii_strcasecmp(tok, "mdc") + || !ascii_strcasecmp(tok, "[mdc]")) mdc=1; - else if (ascii_strcasecmp(tok,"no-mdc")==0) + else if (!ascii_strcasecmp(tok, "no-mdc") + || !ascii_strcasecmp(tok, "[no-mdc]")) mdc=0; - else if (ascii_strcasecmp(tok,"ks-modify")==0) + else if (!ascii_strcasecmp(tok, "ks-modify") + || !ascii_strcasecmp(tok, "[ks-modify]")) modify=1; - else if (ascii_strcasecmp(tok,"no-ks-modify")==0) + else if (!ascii_strcasecmp(tok,"no-ks-modify") + || !ascii_strcasecmp(tok,"[no-ks-modify]")) modify=0; + else if (!ascii_strcasecmp(tok,"aead") + || !ascii_strcasecmp(tok,"[aead]")) + ocb = 1; + else if (!ascii_strcasecmp(tok,"no-aead") + || !ascii_strcasecmp(tok,"[no-aead]")) + ocb = 0; else { log_info (_("invalid item '%s' in preference string\n"),tok); @@ -498,6 +523,10 @@ keygen_set_std_prefs (const char *string,int personal) xfree (prefstringbuf); } + /* For now we require a compat flag to set OCB into the preferences. */ + if (!(opt.compat_flags & COMPAT_VSD_ALLOW_OCB)) + ocb = 0; + if(!rc) { if(personal) @@ -578,6 +607,7 @@ keygen_set_std_prefs (const char *string,int personal) memcpy (hash_prefs, hash, (nhash_prefs=nhash)); memcpy (zip_prefs, zip, (nzip_prefs=nzip)); mdc_available = mdc; + aead_available = ocb; ks_modify = modify; prefs_initialized = 1; } @@ -586,6 +616,7 @@ keygen_set_std_prefs (const char *string,int personal) return rc; } + /* Return a fake user ID containing the preferences. Caller must free. */ PKT_user_id * @@ -624,6 +655,7 @@ keygen_get_std_prefs(void) uid->prefs[j].value=0; uid->flags.mdc=mdc_available; + uid->flags.aead=aead_available; uid->flags.ks_modify=ks_modify; return uid; @@ -670,6 +702,49 @@ add_feature_mdc (PKT_signature *sig,int enabled) xfree (buf); } + +static void +add_feature_aead (PKT_signature *sig, int enabled) +{ + const byte *s; + size_t n; + int i; + char *buf; + + s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); + if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02)))) + return; /* Already set or cleared */ + + if (!s || !n) + { /* Create a new one */ + n = 1; + buf = xmalloc_clear (n); + } + else + { + buf = xmalloc (n); + memcpy (buf, s, n); + } + + if (enabled) + buf[0] |= 0x02; /* AEAD supported */ + else + buf[0] &= ~0x02; + + /* Are there any bits set? */ + for (i=0; i < n; i++) + if (buf[i]) + break; + + if (i == n) + delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); + else + build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); + + xfree (buf); +} + + static void add_keyserver_modify (PKT_signature *sig,int enabled) { @@ -731,6 +806,14 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } + if (aead_available) /* The only preference is AEAD_ALGO_OCB. */ + build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, "\x02", 1); + else + { + delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD); + delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD); + } + if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else @@ -747,8 +830,9 @@ keygen_upd_std_prefs (PKT_signature *sig, void *opaque) delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } - /* Make sure that the MDC feature flag is set if needed. */ + /* Make sure that the MDC and AEAD feature flags are set as needed. */ add_feature_mdc (sig,mdc_available); + add_feature_aead (sig, aead_available); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); @@ -789,6 +873,44 @@ keygen_add_keyserver_url(PKT_signature *sig, void *opaque) return 0; } + +/* This function is used to add a notations to a signature. In + * general the caller should have cleared exiting notations before + * adding new ones. For example by calling: + * + * delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); + * delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); + * + * Only human readable notaions may be added. NAME and value are + * expected to be UTF-* strings. + */ +static void +do_add_notation (PKT_signature *sig, const char *name, const char *value, + int critical) +{ + unsigned char *buf; + unsigned int n1,n2; + + n1 = strlen (name); + n2 = strlen (value); + + buf = xmalloc (8 + n1 + n2); + + buf[0] = 0x80; /* human readable. */ + buf[1] = buf[2] = buf[3] = 0; + buf[4] = n1 >> 8; + buf[5] = n1; + buf[6] = n2 >> 8; + buf[7] = n2; + memcpy (buf+8, name, n1); + memcpy (buf+8+n1, value, n2); + build_sig_subpkt (sig, + (SIGSUBPKT_NOTATION|(critical?SIGSUBPKT_FLAG_CRITICAL:0)), + buf, 8+n1+n2 ); + xfree (buf); +} + + int keygen_add_notations(PKT_signature *sig,void *opaque) { @@ -838,6 +960,7 @@ keygen_add_notations(PKT_signature *sig,void *opaque) return 0; } + int keygen_add_revkey (PKT_signature *sig, void *opaque) { @@ -1096,6 +1219,12 @@ write_keybinding (ctrl_t ctrl, kbnode_t root, /* Make the signature. */ oduap.usage = use; + if ((use & PUBKEY_USAGE_ENC) + && opt.compliance == CO_DE_VS + && gnupg_rng_is_compliant (CO_DE_VS)) + oduap.cpl_notation = "de-vs"; + else + oduap.cpl_notation = NULL; oduap.pk = sub_pk; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, 0, timestamp, 0, @@ -1730,6 +1859,9 @@ print_key_flags(int flags) if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); + + if(flags&PUBKEY_USAGE_RENC) + tty_printf("%s ", "RENC"); } @@ -1763,8 +1895,11 @@ ask_key_flags_with_mask (int algo, int subkey, unsigned int current, } /* Mask the possible usage flags. This is for example used for a - * card based key. */ + * card based key. For ECDH we need to allows additional usages if + * they are provided. RENC is not directly poissible here but see + * below for a workaround. */ possible = (openpgp_pk_algo_usage (algo) & mask); + possible &= ~PUBKEY_USAGE_RENC; /* However, only primary keys may certify. */ if (subkey) @@ -1827,6 +1962,12 @@ ask_key_flags_with_mask (int algo, int subkey, unsigned int current, want to experiment with a cert-only primary key. */ current |= PUBKEY_USAGE_CERT; } + else if ((*s == 'r' || *s == 'R') && (possible&PUBKEY_USAGE_ENC)) + { + /* Allow to set RENC or an encryption capable key. + * This is on purpose not shown in the menu. */ + current |= PUBKEY_USAGE_RENC; + } } break; } @@ -2550,26 +2691,39 @@ ask_curve (int *algo, int *subkey_algo, const char *current) * similar. */ u32 -parse_expire_string( const char *string ) +parse_expire_string (const char *string) { int mult; u32 seconds; u32 abs_date = 0; u32 curtime = make_timestamp (); - time_t tt; + uint64_t tt; + uint64_t tmp64; if (!string || !*string || !strcmp (string, "none") || !strcmp (string, "never") || !strcmp (string, "-")) seconds = 0; else if (!strncmp (string, "seconds=", 8)) - seconds = atoi (string+8); + seconds = scan_secondsstr (string+8); else if ((abs_date = scan_isodatestr(string)) && (abs_date+86400/2) > curtime) seconds = (abs_date+86400/2) - curtime; - else if ((tt = isotime2epoch (string)) != (time_t)(-1)) - seconds = (u32)tt - curtime; + else if ((tt = isotime2epoch_u64 (string)) != (uint64_t)(-1)) + { + tmp64 = tt - curtime; + if (tmp64 >= (u32)(-1)) + seconds = (u32)(-1) - 1; /* cap value. */ + else + seconds = (u32)tmp64; + } else if ((mult = check_valid_days (string))) - seconds = atoi (string) * 86400L * mult; + { + tmp64 = scan_secondsstr (string) * 86400L * mult; + if (tmp64 >= (u32)(-1)) + seconds = (u32)(-1) - 1; /* cap value. */ + else + seconds = (u32)tmp64; + } else seconds = (u32)(-1); @@ -2586,11 +2740,16 @@ parse_creation_string (const char *string) if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) - seconds = atoi (string+8); + seconds = scan_secondsstr (string+8); else if ( !(seconds = scan_isodatestr (string))) { - time_t tmp = isotime2epoch (string); - seconds = (tmp == (time_t)(-1))? 0 : tmp; + uint64_t tmp = isotime2epoch_u64 (string); + if (tmp == (uint64_t)(-1)) + seconds = 0; + else if (tmp > (u32)(-1)) + seconds = 0; + else + seconds = tmp; } return seconds; } @@ -3542,14 +3701,29 @@ release_parameter_list (struct para_data_s *r) } } +/* Return the N-th parameter of name KEY from PARA. An IDX of 0 + * returns the first and so on. */ static struct para_data_s * -get_parameter( struct para_data_s *para, enum para_name key ) +get_parameter_idx (struct para_data_s *para, enum para_name key, + unsigned int idx) { - struct para_data_s *r; + struct para_data_s *r; + + for(r = para; r; r = r->next) + if (r->key == key) + { + if (!idx) + return r; + idx--; + } + return NULL; +} - for( r = para; r && r->key != key; r = r->next ) - ; - return r; +/* Return the first parameter of name KEY from PARA. */ +static struct para_data_s * +get_parameter (struct para_data_s *para, enum para_name key) +{ + return get_parameter_idx (para, key, 0); } static const char * @@ -3653,6 +3827,12 @@ parse_usagestr (const char *usagestr) use |= PUBKEY_USAGE_AUTH; else if (!ascii_strcasecmp (s, "cert")) use |= PUBKEY_USAGE_CERT; + else if (!ascii_strcasecmp (s, "renc")) + use |= PUBKEY_USAGE_RENC; + else if (!ascii_strcasecmp (s, "time")) + use |= PUBKEY_USAGE_TIME; + else if (!ascii_strcasecmp (s, "group")) + use |= PUBKEY_USAGE_GROUP; else { xfree (tokens); @@ -3691,6 +3871,68 @@ parse_parameter_usage (const char *fname, } +/* Parse the revocation key specified by NAME, check that the public + * key exists (so that we can get the required public key algorithm), + * and return a parameter wit the revocation key information. On + * error print a diagnostic and return NULL. */ +static struct para_data_s * +prepare_desig_revoker (ctrl_t ctrl, const char *name) +{ + gpg_error_t err; + struct para_data_s *para = NULL; + KEYDB_SEARCH_DESC desc; + int sensitive = 0; + struct revocation_key revkey; + PKT_public_key *revoker_pk = NULL; + size_t fprlen; + + if (!ascii_strncasecmp (name, "sensitive:", 10) && !spacep (name+10)) + { + name += 10; + sensitive = 1; + } + + if (classify_user_id (name, &desc, 1) + || desc.mode != KEYDB_SEARCH_MODE_FPR) + { + log_info (_("\"%s\" is not a fingerprint\n"), name); + err = gpg_error (GPG_ERR_INV_NAME); + goto leave; + } + + revoker_pk = xcalloc (1, sizeof *revoker_pk); + revoker_pk->req_usage = PUBKEY_USAGE_CERT; + err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, + NULL, revoker_pk, name, NULL, NULL, 1); + if (err) + goto leave; + + fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); + if (fprlen != 20) + { + log_info (_("cannot appoint a PGP 2.x style key as a " + "designated revoker\n")); + err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); + goto leave; + } + revkey.class = 0x80; + if (sensitive) + revkey.class |= 0x40; + revkey.algid = revoker_pk->pubkey_algo; + + para = xcalloc (1, sizeof *para); + para->key = pREVOKER; + memcpy (¶->u.revkey, &revkey, sizeof revkey); + + leave: + if (err) + log_error ("invalid revocation key '%s': %s\n", name, gpg_strerror (err)); + free_public_key (revoker_pk); + return para; +} + + +/* Parse a pREVOKER parameter into its dedicated parts. */ static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) @@ -3769,10 +4011,11 @@ get_parameter_uint( struct para_data_s *para, enum para_name key ) } static struct revocation_key * -get_parameter_revkey( struct para_data_s *para, enum para_name key ) +get_parameter_revkey (struct para_data_s *para, enum para_name key, + unsigned int idx) { - struct para_data_s *r = get_parameter( para, key ); - return r? &r->u.revkey : NULL; + struct para_data_s *r = get_parameter_idx (para, key, idx); + return r? &r->u.revkey : NULL; } static int @@ -3783,6 +4026,7 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, const char *s1, *s2, *s3; size_t n; char *p; + strlist_t sl; int is_default = 0; int have_user_id = 0; int err, algo; @@ -3928,10 +4172,20 @@ proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, } } - /* Set revoker, if any. */ + /* Set revoker from parameter file, if any. Must be done first so + * that we don't find a parameter set via prepare_desig_revoker. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; + /* Check and append revokers from the config file. */ + for (sl = opt.desig_revokers; sl; sl = sl->next) + { + r = prepare_desig_revoker (ctrl, sl->d); + if (!r) + return -1; + append_to_parameter (para, r); + } + /* Make KEYCREATIONDATE from Creation-Date. */ r = get_parameter (para, pCREATIONDATE); @@ -4197,14 +4451,17 @@ quickgen_set_para (struct para_data_s *para, int for_subkey, { struct para_data_s *r; - r = xmalloc_clear (sizeof *r + 30); + r = xmalloc_clear (sizeof *r + 50); r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; if (use) - snprintf (r->u.value, 30, "%s%s%s%s", + snprintf (r->u.value, 30, "%s%s%s%s%s%s%s", (use & PUBKEY_USAGE_ENC)? "encr " : "", (use & PUBKEY_USAGE_SIG)? "sign " : "", (use & PUBKEY_USAGE_AUTH)? "auth " : "", - (use & PUBKEY_USAGE_CERT)? "cert " : ""); + (use & PUBKEY_USAGE_CERT)? "cert " : "", + (use & PUBKEY_USAGE_RENC)? "renc " : "", + (use & PUBKEY_USAGE_TIME)? "time " : "", + (use & PUBKEY_USAGE_GROUP)?"group ": ""); else strcpy (r->u.value, for_subkey ? "encr" : "sign"); r->next = para; @@ -4883,22 +5140,41 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, char *cache_nonce = NULL; void *kek = NULL; size_t keklen; + char *ecdh_param_str = NULL; sk = copy_public_key (NULL, sub_psk); if (!sk) return gpg_error_from_syserror (); epoch2isotime (timestamp, (time_t)sk->timestamp); + if (sk->pubkey_algo == PUBKEY_ALGO_ECDH) + { + ecdh_param_str = ecdh_param_str_from_pk (sk); + if (!ecdh_param_str) + { + free_public_key (sk); + return gpg_error_from_syserror (); + } + } err = hexkeygrip_from_pk (sk, &hexgrip); if (err) - return err; + { + xfree (ecdh_param_str); + free_public_key (sk); + return err; + } memset(&info, 0, sizeof (info)); rc = agent_scd_getattr ("SERIALNO", &info); if (rc) - return (gpg_error_t)rc; + { + xfree (ecdh_param_str); + free_public_key (sk); + return (gpg_error_t)rc; + } - rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp); + rc = agent_keytocard (hexgrip, 2, 1, info.serialno, + timestamp, ecdh_param_str); xfree (info.serialno); if (rc) { @@ -4937,10 +5213,14 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, if (err) log_error ("writing card key to backup file: %s\n", gpg_strerror (err)); else - /* Remove secret key data in agent side. */ - agent_scd_learn (NULL, 1); + { + /* Remove secret key data in agent side. We use force 2 here to + * allow overwriting of the temporary private key. */ + agent_scd_learn (NULL, 2); + } leave: + xfree (ecdh_param_str); xfree (cache_nonce); gcry_cipher_close (cipherhd); xfree (kek); @@ -4966,6 +5246,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, int algo; u32 expire; const char *key_from_hexgrip = NULL; + unsigned int idx; if (outctrl->dryrun) { @@ -5067,7 +5348,10 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, keyid_copy (pri_psk->main_keyid, pri_psk->keyid); } - if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) + /* Write all signatures specifying designated revokers. */ + for (idx=0; + !err && (revkey = get_parameter_revkey (para, pREVOKER, idx)); + idx++) err = write_direct_sig (ctrl, pub_root, pri_psk, revkey, timestamp, cache_nonce); diff --git a/g10/keyid.c b/g10/keyid.c index 69d85da..7823f0d 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -88,7 +88,10 @@ pubkey_letter( int algo ) "256E" - ECDSA using a curve with 256 bit The macro PUBKEY_STRING_SIZE may be used to allocate a buffer with - a suitable size.*/ + a suitable size. Note that a more general version of this function + exists as get_keyalgo_string. However, that has no special + treatment for the old and unsupported Elgamal which we here print as + xxxNNNN. */ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { @@ -323,11 +326,12 @@ keyid_copy (u32 *dest, const u32 *src) char * format_keyid (u32 *keyid, int format, char *buffer, int len) { - char tmp[KEYID_STR_SIZE]; if (! buffer) { - buffer = tmp; - len = sizeof (tmp); + len = KEYID_STR_SIZE; + buffer = xtrymalloc (len); + if (!buffer) + return NULL; } if (format == KF_DEFAULT) @@ -362,8 +366,6 @@ format_keyid (u32 *keyid, int format, char *buffer, int len) BUG(); } - if (buffer == tmp) - return xstrdup (buffer); return buffer; } @@ -709,6 +711,13 @@ usagestr_from_pk (PKT_public_key *pk, int fill) if ( (use & PUBKEY_USAGE_AUTH) ) buffer[i++] = 'A'; + if ( (use & PUBKEY_USAGE_RENC) ) + buffer[i++] = 'R'; + if ( (use & PUBKEY_USAGE_TIME) ) + buffer[i++] = 'T'; + if ( (use & PUBKEY_USAGE_GROUP) ) + buffer[i++] = 'G'; + while (fill && i < 4) buffer[i++] = ' '; @@ -985,3 +994,25 @@ hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) } return err; } + + +/* Return a hexfied malloced string of the ECDH parameters for an ECDH + * key from the public key PK. Returns NULL on error. */ +char * +ecdh_param_str_from_pk (PKT_public_key *pk) +{ + const unsigned char *s; + unsigned int n; + + if (!pk + || pk->pubkey_algo != PUBKEY_ALGO_ECDH + || !gcry_mpi_get_flag (pk->pkey[2], GCRYMPI_FLAG_OPAQUE) + || !(s = gcry_mpi_get_opaque (pk->pkey[2], &n)) || !n) + { + gpg_err_set_errno (EINVAL); + return NULL; /* Invalid parameter */ + } + + n = (n+7)/8; + return bin2hex (s, n, NULL); +} diff --git a/g10/keylist.c b/g10/keylist.c index af0ce9d..b99e245 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -287,6 +287,173 @@ print_card_key_info (estream_t fp, kbnode_t keyblock) #endif /*ENABLE_CARD_SUPPORT*/ +/* Print the preferences line. Allowed values for MODE are: + * -1 - print to the TTY + * 0 - print to stdout. + * 1 - use log_info + */ +void +show_preferences (PKT_user_id *uid, int indent, int mode, int verbose) +{ + estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; + const prefitem_t fake = { 0, 0 }; + const prefitem_t *prefs; + int i; + + if (!uid) + return; + + if (uid->prefs) + prefs = uid->prefs; + else if (verbose) + prefs = &fake; + else + return; + + if (verbose) + { + int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; + + tty_fprintf (fp, "%*s %s", indent, "", _("Cipher: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_SYM) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!openpgp_cipher_test_algo (prefs[i].value) + && prefs[i].value < 100) + tty_fprintf (fp, "%s", openpgp_cipher_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == CIPHER_ALGO_3DES) + des_seen = 1; + } + } + if (!des_seen) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("AEAD: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_AEAD) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!openpgp_aead_test_algo (prefs[i].value) + && prefs[i].value < 100) + tty_fprintf (fp, "%s", openpgp_aead_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + } + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("Digest: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_HASH) + { + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) + tty_fprintf (fp, "%s", gcry_md_algo_name (prefs[i].value)); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == DIGEST_ALGO_SHA1) + sha1_seen = 1; + } + } + if (!sha1_seen) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); + } + tty_fprintf (fp, "\n%*s %s", indent, "", _("Compression: ")); + for (i = any = 0; prefs[i].type; i++) + { + if (prefs[i].type == PREFTYPE_ZIP) + { + const char *s = compress_algo_to_string (prefs[i].value); + + if (any) + tty_fprintf (fp, ", "); + any = 1; + /* We don't want to display strings for experimental algos */ + if (s && prefs[i].value < 100) + tty_fprintf (fp, "%s", s); + else + tty_fprintf (fp, "[%d]", prefs[i].value); + if (prefs[i].value == COMPRESS_ALGO_NONE) + uncomp_seen = 1; + } + } + if (!uncomp_seen) + { + if (any) + tty_fprintf (fp, ", "); + else + { + tty_fprintf (fp, "%s", + compress_algo_to_string (COMPRESS_ALGO_ZIP)); + tty_fprintf (fp, ", "); + } + tty_fprintf (fp, "%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); + } + if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) + { + tty_fprintf (fp, "\n%*s %s", indent, "", _("Features: ")); + any = 0; + if (uid->flags.mdc) + { + tty_fprintf (fp, "MDC"); + any = 1; + } + if (uid->flags.aead) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, "AEAD"); + } + if (!uid->flags.ks_modify) + { + if (any) + tty_fprintf (fp, ", "); + tty_fprintf (fp, _("Keyserver no-modify")); + } + } + tty_fprintf (fp, "\n"); + } + else + { + tty_fprintf (fp, "%*s", indent, ""); + for (i = 0; prefs[i].type; i++) + { + tty_fprintf (fp, " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : + prefs[i].type == PREFTYPE_AEAD ? 'A' : + prefs[i].type == PREFTYPE_HASH ? 'H' : + prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', + prefs[i].value); + } + if (uid->flags.mdc) + tty_fprintf (fp, " [mdc]"); + if (uid->flags.aead) + tty_fprintf (fp, " [aead]"); + if (!uid->flags.ks_modify) + tty_fprintf (fp, " [no-ks-modify]"); + tty_fprintf (fp, "\n"); + } +} + + /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, @@ -757,6 +924,13 @@ print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock) if ((use & PUBKEY_USAGE_AUTH)) es_putc ('a', es_stdout); + if (use & PUBKEY_USAGE_RENC) + es_putc ('r', es_stdout); + if ((use & PUBKEY_USAGE_TIME)) + es_putc ('t', es_stdout); + if ((use & PUBKEY_USAGE_GROUP)) + es_putc ('g', es_stdout); + if ((use & PUBKEY_USAGE_UNKNOWN)) es_putc ('?', es_stdout); @@ -1057,6 +1231,11 @@ list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); + if ((opt.list_options & LIST_SHOW_PREF_VERBOSE)) + show_preferences (uid, indent+2, 0, 1); + else if ((opt.list_options & LIST_SHOW_PREF)) + show_preferences (uid, indent+2, 0, 0); + if (opt.with_wkd_hash) { char *mbox, *hash, *p; diff --git a/g10/keyserver.c b/g10/keyserver.c index 1fbe728..5715800 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1234,7 +1234,9 @@ keyserver_refresh (ctrl_t ctrl, strlist_t users) struct keyserver_spec *keyserver=desc[i].skipfncvalue; if (!opt.quiet) - log_info (_("refreshing %d key from %s\n"), 1, keyserver->uri); + log_info (ngettext("refreshing %d key from %s\n", + "refreshing %d keys from %s\n", + 1), 1, keyserver->uri); /* We use the keyserver structure we parsed out before. Note that a preferred keyserver without a scheme:// @@ -233,7 +233,6 @@ void display_online_help( const char *keyword ); /*-- encode.c --*/ gpg_error_t setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); -void encrypt_seskey (DEK *dek, DEK **seskey, byte *enckey); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); @@ -466,6 +465,7 @@ int cmp_signodes (const void *av, const void *bv); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); +void show_preferences (PKT_user_id *uid, int indent, int mode, int verbose); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); @@ -506,7 +506,7 @@ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); -int card_store_subkey (KBNODE node, int use); +int card_store_subkey (KBNODE node, int use, strlist_t *processed_keys); #endif #define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6)) diff --git a/g10/mainproc.c b/g10/mainproc.c index 8e4d848..0d98e0f 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -765,7 +765,7 @@ proc_encrypted (CTX c, PACKET *pkt) result = GPG_ERR_NO_SECKEY; /* Compute compliance with CO_DE_VS. */ - if (!result && is_status_enabled () + if (!result && (is_status_enabled () || opt.flags.require_compliance) /* Overriding session key voids compliance. */ && !opt.override_session_key /* Check symmetric cipher. */ @@ -821,9 +821,13 @@ proc_encrypted (CTX c, PACKET *pkt) compliance_de_vs |= 2; } - /* Trigger the deferred error. */ + /* Trigger the deferred error. The second condition makes sure that a + * log_error printed in the cry_cipher_checktag never gets ignored. */ if (!result && early_plaintext) result = gpg_error (GPG_ERR_BAD_DATA); + else if (!result && pkt->pkt.encrypted->aead_algo + && log_get_errorcount (0)) + result = gpg_error (GPG_ERR_BAD_SIGNATURE); if (result == -1) ; @@ -2612,7 +2616,7 @@ check_sig_and_print (CTX c, kbnode_t node) } /* Compute compliance with CO_DE_VS. */ - if (pk && is_status_enabled () + if (pk && gnupg_gcrypt_is_compliant (CO_DE_VS) && gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, 0, pk->pkey, nbits_from_pk (pk), NULL) @@ -687,7 +687,7 @@ openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, *r_noncelen = 15; break; - case AEAD_ALGO_EAX: + case AEAD_ALGO_EAX: /* Only for decryption of some old data. */ *r_mode = MY_GCRY_CIPHER_MODE_EAX; *r_noncelen = 16; break; @@ -782,21 +782,21 @@ openpgp_pk_algo_usage ( int algo ) switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG - | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); + | PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_ELGAMAL_E: - use = PUBKEY_USAGE_ENC; + use = PUBKEY_USAGE_ENC | PUBKEY_USAGE_RENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; diff --git a/g10/options.h b/g10/options.h index b11e91c..b3cb520 100644 --- a/g10/options.h +++ b/g10/options.h @@ -89,8 +89,10 @@ struct int list_packets; /* Option --list-packets active. */ int def_cipher_algo; int def_digest_algo; + int force_ocb; int cert_digest_algo; int compress_algo; + int explicit_compress_option; /* A compress option was explicitly given. */ int compress_level; int bz2_compress_level; int bz2_decompress_lowmem; @@ -103,6 +105,9 @@ struct * the option --sender. */ strlist_t sender_list; + /* A list of fingerprints added as designated revokers to new keys. */ + strlist_t desig_revokers; + int def_cert_level; int min_cert_level; int ask_cert_level; @@ -236,7 +241,6 @@ struct unsigned int allow_multiple_messages:1; unsigned int allow_weak_digest_algos:1; unsigned int allow_weak_key_signatures:1; - unsigned int override_compliance_check:1; unsigned int large_rsa:1; unsigned int disable_signer_uid:1; unsigned int include_key_block:1; @@ -290,6 +294,9 @@ struct int only_sign_text_ids; int no_symkey_cache; /* Disable the cache used for --symmetric. */ + + /* Compatibility flags (COMPAT_FLAG_xxxx). */ + unsigned int compat_flags; } opt; /* CTRL is used to keep some global variables we currently can't @@ -346,8 +353,11 @@ struct { EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; +/* Compatibility flags */ +#define COMPAT_VSD_ALLOW_OCB 1 + -/* Compatibility flags. */ +/* Compliance test macors. */ #define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS) #define RFC2440 (opt.compliance==CO_RFC2440) #define RFC4880 (opt.compliance==CO_RFC4880) @@ -384,6 +394,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define EXPORT_PKA_FORMAT (1<<6) #define EXPORT_DANE_FORMAT (1<<7) #define EXPORT_BACKUP (1<<10) +#define EXPORT_REVOCS (1<<11) #define LIST_SHOW_PHOTOS (1<<0) #define LIST_SHOW_POLICY_URLS (1<<1) @@ -399,6 +410,8 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define LIST_SHOW_SIG_SUBPACKETS (1<<10) #define LIST_SHOW_USAGE (1<<11) #define LIST_SHOW_ONLY_FPR_MBOX (1<<12) +#define LIST_SHOW_PREF (1<<14) +#define LIST_SHOW_PREF_VERBOSE (1<<15) #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) diff --git a/g10/packet.h b/g10/packet.h index 7f7608c..409d7d4 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -56,6 +56,9 @@ | GCRY_PK_USAGE_AUTH | GCRY_PK_USAGE_UNKN) >= 256 # error Please choose another value for PUBKEY_USAGE_NONE #endif +#define PUBKEY_USAGE_GROUP 512 /* Group flag. */ +#define PUBKEY_USAGE_RENC 1024 /* Restricted encryption. */ +#define PUBKEY_USAGE_TIME 2048 /* Timestamp use. */ /* Helper macros. */ #define is_RSA(a) ((a)==PUBKEY_ALGO_RSA || (a)==PUBKEY_ALGO_RSA_E \ @@ -287,7 +290,7 @@ typedef struct /* The length of ATTRIB_DATA. */ unsigned long attrib_len; byte *namehash; - int help_key_usage; + u16 help_key_usage; u32 help_key_expire; int help_full_count; int help_marginal_count; @@ -385,7 +388,7 @@ typedef struct byte selfsigversion; /* highest version of all of the self-sigs */ /* The public key algorithm. (Serialized.) */ byte pubkey_algo; - byte pubkey_usage; /* for now only used to pass it to getkey() */ + u16 pubkey_usage; /* carries the usage info. */ byte req_usage; /* hack to pass a request to getkey() */ u32 has_expired; /* set to the expiration date if expired */ /* keyid of the primary key. Never access this value directly. diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 5fea1ac..e0fe9c0 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -2189,8 +2189,21 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen, && opt.verbose && !glo_ctrl.silence_parse_warnings) log_info ("signature packet without timestamp\n"); - p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER); - if (p) + /* Set the key id. We first try the issuer fingerprint and if + * this is not found fallback to the issuer. Note that + * only the issuer packet is also searched in the unhashed area. */ + p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_ISSUER_FPR, &len); + if (p && len == 21 && p[0] == 4) + { + sig->keyid[0] = buf32_to_u32 (p + 1 + 12); + sig->keyid[1] = buf32_to_u32 (p + 1 + 16); + } + else if (p && len == 33 && p[0] == 5) + { + sig->keyid[0] = buf32_to_u32 (p + 1 ); + sig->keyid[1] = buf32_to_u32 (p + 1 + 4); + } + else if ((p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER))) { sig->keyid[0] = buf32_to_u32 (p); sig->keyid[1] = buf32_to_u32 (p + 4); diff --git a/g10/photoid.c b/g10/photoid.c index dbef7d7..071a31f 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -52,12 +52,11 @@ generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name) { PKT_user_id *uid; int error=1,i; - unsigned int len; + uint64_t len; char *filename; byte *photo=NULL; byte header[16]; IOBUF file; - int overflow; header[0]=0x10; /* little side of photo header length */ header[1]=0; /* big side of photo header length */ @@ -125,11 +124,18 @@ generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name) } - len=iobuf_get_filelength(file, &overflow); - if(len>6144 || overflow) + len = iobuf_get_filelength(file); + if(len>6144) { - tty_printf( _("This JPEG is really large (%d bytes) !\n"),len); - if(!cpr_get_answer_is_yes("photoid.jpeg.size", + /* We silently skip JPEGs larger than 1MiB because we have a + * 2MiB limit on the user ID packets and we need some limit + * anyway because the returned u64 is larger than the u32 or + * OpenPGP. Note that the diagnostic may print a wrong + * value if the value is really large; we don't fix this to + * avoid a string change. */ + tty_printf( _("This JPEG is really large (%d bytes) !\n"), (int)len); + if(len > 1024*1024 + || !cpr_get_answer_is_yes("photoid.jpeg.size", _("Are you sure you want to use it? (y/N) "))) { iobuf_close(file); diff --git a/g10/pkclist.c b/g10/pkclist.c index fb8b176..3fd7995 100644 --- a/g10/pkclist.c +++ b/g10/pkclist.c @@ -821,7 +821,8 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, { int rc; PKT_public_key *pk; - KBNODE keyblock = NULL; + kbnode_t keyblock = NULL; + kbnode_t node; if (!name || !*name) return gpg_error (GPG_ERR_INV_USER_ID); @@ -832,7 +833,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, pk->req_usage = use; if (from_file) - rc = get_pubkey_fromfile (ctrl, pk, name); + rc = get_pubkey_fromfile (ctrl, pk, name, &keyblock); else rc = get_best_pubkey_byname (ctrl, GET_PUBKEY_NORMAL, NULL, pk, name, &keyblock, 0); @@ -871,10 +872,10 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, int trustlevel; trustlevel = get_validity (ctrl, keyblock, pk, pk->user_id, NULL, 1); - release_kbnode (keyblock); if ( (trustlevel & TRUST_FLAG_DISABLED) ) { /* Key has been disabled. */ + release_kbnode (keyblock); send_status_inv_recp (13, name); log_info (_("%s: skipped: public key is disabled\n"), name); free_public_key (pk); @@ -884,6 +885,7 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, if ( !do_we_trust_pre (ctrl, pk, trustlevel) ) { /* We don't trust this key. */ + release_kbnode (keyblock); send_status_inv_recp (10, name); free_public_key (pk); return GPG_ERR_UNUSABLE_PUBKEY; @@ -902,19 +904,33 @@ find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, { pk_list_t r; - r = xtrymalloc (sizeof *r); - if (!r) - { - rc = gpg_error_from_syserror (); - free_public_key (pk); - return rc; - } + r = xmalloc (sizeof *r); r->pk = pk; r->next = *pk_list_addr; r->flags = mark_hidden? 1:0; *pk_list_addr = r; } + for (node = keyblock; node; node = node->next) + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + && ((pk=node->pkt->pkt.public_key)->pubkey_usage & PUBKEY_USAGE_RENC) + && pk->flags.valid + && !pk->flags.revoked + && !pk->flags.disabled + && !pk->has_expired + && key_present_in_pk_list (*pk_list_addr, pk)) + { + pk_list_t r; + + r = xmalloc (sizeof *r); + r->pk = copy_public_key (NULL, pk); + r->next = *pk_list_addr; + r->flags = mark_hidden? 1:0; /* FIXME: Use PK_LIST_HIDDEN ? */ + *pk_list_addr = r; + } + + + release_kbnode (keyblock); return 0; } @@ -1648,36 +1664,37 @@ select_algo_from_prefs(PK_LIST pk_list, int preftype, return result; } -/* - * Select the MDC flag from the pk_list. We can only use MDC if all - * recipients support this feature. - */ -int -select_mdc_from_pklist (PK_LIST pk_list) + +/* Select the AEAD flag from the pk_list. We can only use AEAD if all + * recipients support this feature. Returns the AEAD to be used or 0 + * if AEAD shall not be used. */ +aead_algo_t +select_aead_from_pklist (PK_LIST pk_list) { - PK_LIST pkr; + pk_list_t pkr; + int aead; - if ( !pk_list ) + if (!pk_list) return 0; for (pkr = pk_list; pkr; pkr = pkr->next) { - int mdc; - if (pkr->pk->user_id) /* selected by user ID */ - mdc = pkr->pk->user_id->flags.mdc; + aead = pkr->pk->user_id->flags.aead; else - mdc = pkr->pk->flags.mdc; - if (!mdc) + aead = pkr->pk->flags.aead; + if (!aead) return 0; /* At least one recipient does not support it. */ } - return 1; /* Can be used. */ + + return AEAD_ALGO_OCB; /* Yes, AEAD can be used. */ } -/* Print a warning for all keys in PK_LIST missing the MDC feature. */ +/* Print a warning for all keys in PK_LIST missing the AEAD feature + * flag or AEAD algorithms. */ void -warn_missing_mdc_from_pklist (PK_LIST pk_list) +warn_missing_aead_from_pklist (PK_LIST pk_list) { PK_LIST pkr; @@ -1686,12 +1703,12 @@ warn_missing_mdc_from_pklist (PK_LIST pk_list) int mdc; if (pkr->pk->user_id) /* selected by user ID */ - mdc = pkr->pk->user_id->flags.mdc; + mdc = pkr->pk->user_id->flags.aead; else - mdc = pkr->pk->flags.mdc; + mdc = pkr->pk->flags.aead; if (!mdc) log_info (_("Note: key %s has no %s feature\n"), - keystr_from_pk (pkr->pk), "MDC"); + keystr_from_pk (pkr->pk), "AEAD"); } } diff --git a/g10/plaintext.c b/g10/plaintext.c index 3bc8696..10d567a 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -584,11 +584,16 @@ do_hash (gcry_md_hd_t md, gcry_md_hd_t md2, IOBUF fp, int textmode) } else { - while ((c = iobuf_get (fp)) != -1) + byte *buffer = xmalloc (32768); + int ret; + + while ((ret = iobuf_read (fp, buffer, 32768)) != -1) { if (md) - gcry_md_putc (md, c); + gcry_md_write (md, buffer, ret); } + + xfree (buffer); } } diff --git a/g10/progress.c b/g10/progress.c index 7e777d4..7ee8b1e 100644 --- a/g10/progress.c +++ b/g10/progress.c @@ -72,13 +72,11 @@ release_progress_context (progress_filter_context_t *pfx) static void -write_status_progress (const char *what, - unsigned long current, unsigned long total_arg) +write_status_progress (const char *what, uint64_t current, uint64_t total) { char buffer[60]; char units[] = "BKMGTPEZY?"; int unitidx = 0; - uint64_t total = total_arg; /* Although we use an unsigned long for the values, 32 bit * applications using GPGME will use an "int" and thus are limited @@ -91,7 +89,10 @@ write_status_progress (const char *what, * to display how many percent of the operation has been done and * thus scaling CURRENT and TOTAL down before they get to large, * should not have a noticeable effect except for rounding - * imprecision. */ + * imprecision. + * Update 2023-06-13: We now use uint64_t but to keep the API stable + * we still do the scaling. + */ if (!total && opt.input_size_hint) total = opt.input_size_hint; @@ -121,7 +122,7 @@ write_status_progress (const char *what, unitidx = 9; snprintf (buffer, sizeof buffer, "%.20s ? %lu %lu %c%s", - what? what : "?", current, (unsigned long)total, + what? what : "?", (unsigned long)current, (unsigned long)total, units[unitidx], unitidx? "iB" : ""); write_status_text (STATUS_PROGRESS, buffer); @@ -181,7 +182,7 @@ progress_filter (void *opaque, int control, void handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name) { - off_t filesize = 0; + uint64_t filesize = 0; if (!pfx) return; @@ -190,7 +191,7 @@ handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name) log_assert (is_status_enabled ()); if ( !iobuf_is_pipe_filename (name) && *name ) - filesize = iobuf_get_filelength (inp, NULL); + filesize = iobuf_get_filelength (inp); else if (opt.set_filesize) filesize = opt.set_filesize; diff --git a/g10/revoke.c b/g10/revoke.c index 035a2e9..7c01149 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -315,8 +315,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) tty_printf(_("(This is a sensitive revocation key)\n")); tty_printf("\n"); - rc = agent_probe_secret_key (ctrl, pk2); - if (rc) + if (!agent_probe_secret_key (ctrl, pk2)) { tty_printf (_("Secret key is not available.\n")); continue; @@ -714,9 +713,9 @@ gen_revoke (ctrl_t ctrl, const char *uname) BUG (); psk = node->pkt->pkt.public_key; - rc = agent_probe_secret_key (NULL, psk); - if (rc) + if (!agent_probe_secret_key (NULL, psk)) { + rc = gpg_error (GPG_ERR_NO_SECKEY); log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; diff --git a/g10/sig-check.c b/g10/sig-check.c index eeaf6f0..09d5a8b 100644 --- a/g10/sig-check.c +++ b/g10/sig-check.c @@ -75,17 +75,10 @@ check_key_verify_compliance (PKT_public_key *pk) NULL)) { /* Compliance failure. */ - log_info (_("key %s may not be used for signing in %s mode\n"), + log_error (_("key %s may not be used for signing in %s mode\n"), keystr_from_pk (pk), gnupg_compliance_option_string (opt.compliance)); - if (opt.flags.override_compliance_check) - log_info (_("continuing verification anyway due to option %s\n"), - "--override-compliance-failure"); - else - { - log_inc_errorcount (); /* We used log info above. */ - err = gpg_error (GPG_ERR_PUBKEY_ALGO); - } + err = gpg_error (GPG_ERR_PUBKEY_ALGO); } return err; @@ -342,7 +335,8 @@ check_signature_metadata_validity (PKT_public_key *pk, PKT_signature *sig, if (r_revoked) *r_revoked = 0; - if (pk->timestamp > sig->timestamp ) + if (pk->timestamp > sig->timestamp + && !(parse_key_usage (sig) & PUBKEY_USAGE_RENC)) { ulong d = pk->timestamp - sig->timestamp; if ( d < 86400 ) @@ -385,8 +385,9 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, goto leave; } - /* Check compliance. */ - if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo)) + /* Check compliance but always allow for key revocations. */ + if (!IS_KEY_REV (sig) + && ! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (mdalgo), @@ -395,9 +396,10 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, goto leave; } - if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, - pksk->pubkey_algo, 0, - pksk->pkey, nbits_from_pk (pksk), NULL)) + if (!IS_KEY_REV (sig) + && ! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, + pksk->pubkey_algo, 0, + pksk->pkey, nbits_from_pk (pksk), NULL)) { log_error (_("key %s may not be used for signing in %s mode\n"), keystr_from_pk (pksk), @@ -749,14 +751,13 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) if (!opt.no_literal) pt=setup_plaintext_name(fname,inp); - /* try to calculate the length of the data */ + /* Try to calculate the length of the data. */ if ( !iobuf_is_pipe_filename (fname) && *fname ) { - off_t tmpsize; - int overflow; + uint64_t tmpsize; - if( !(tmpsize = iobuf_get_filelength(inp, &overflow)) - && !overflow && opt.verbose) + tmpsize = iobuf_get_filelength (inp); + if (!tmpsize && opt.verbose) log_info (_("WARNING: '%s' is an empty file\n"), fname); /* We can't encode the length of very large files because @@ -921,62 +922,68 @@ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int encryptflag, strlist_t remusr, const char *outfile ) { - const char *fname; - armor_filter_context_t *afx; - compress_filter_context_t zfx; - md_filter_context_t mfx; - text_filter_context_t tfx; - progress_filter_context_t *pfx; - encrypt_filter_context_t efx; - IOBUF inp = NULL, out = NULL; - PACKET pkt; - int rc = 0; - PK_LIST pk_list = NULL; - SK_LIST sk_list = NULL; - SK_LIST sk_rover = NULL; - int multifile = 0; - u32 duration=0; - - pfx = new_progress_context (); - afx = new_armor_context (); - memset( &zfx, 0, sizeof zfx); - memset( &mfx, 0, sizeof mfx); - memset( &efx, 0, sizeof efx); - efx.ctrl = ctrl; - init_packet( &pkt ); - - if( filenames ) { - fname = filenames->d; - multifile = !!filenames->next; + const char *fname; + armor_filter_context_t *afx; + compress_filter_context_t zfx; + md_filter_context_t mfx; + text_filter_context_t tfx; + progress_filter_context_t *pfx; + encrypt_filter_context_t efx; + iobuf_t inp = NULL; + iobuf_t out = NULL; + PACKET pkt; + int rc = 0; + PK_LIST pk_list = NULL; + SK_LIST sk_list = NULL; + SK_LIST sk_rover = NULL; + int multifile = 0; + u32 duration=0; + char peekbuf[32]; + int peekbuflen = 0; + + + pfx = new_progress_context (); + afx = new_armor_context (); + memset (&zfx, 0, sizeof zfx); + memset (&mfx, 0, sizeof mfx); + memset (&efx, 0, sizeof efx); + efx.ctrl = ctrl; + init_packet (&pkt); + + if (filenames) + { + fname = filenames->d; + multifile = !!filenames->next; } - else - fname = NULL; + else + fname = NULL; - if( fname && filenames->next && (!detached || encryptflag) ) - log_bug("multiple files can only be detached signed"); + if (fname && filenames->next && (!detached || encryptflag)) + log_bug ("multiple files can only be detached signed"); - if(encryptflag==2 - && (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek))) - goto leave; + if (encryptflag == 2 + && (rc = setup_symkey (&efx.symkey_s2k,&efx.symkey_dek))) + goto leave; - if (opt.ask_sig_expire && !opt.batch) - duration = ask_expire_interval(1,opt.def_sig_expire); - else - duration = parse_expire_string(opt.def_sig_expire); + if (opt.ask_sig_expire && !opt.batch) + duration = ask_expire_interval(1,opt.def_sig_expire); + else + duration = parse_expire_string(opt.def_sig_expire); - /* Note: In the old non-agent version the following call used to - unprotect the secret key. This is now done on demand by the agent. */ - if( (rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) ) - goto leave; + /* Note: In the old non-agent version the following call used to + unprotect the secret key. This is now done on demand by the agent. */ + if ((rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG ))) + goto leave; - if (encryptflag - && (rc=build_pk_list (ctrl, remusr, &pk_list))) - goto leave; + if (encryptflag + && (rc=build_pk_list (ctrl, remusr, &pk_list))) + goto leave; - /* prepare iobufs */ - if( multifile ) /* have list of filenames */ - inp = NULL; /* we do it later */ - else { + /* Prepare iobufs. */ + if (multifile) /* have list of filenames */ + inp = NULL; /* we do it later */ + else + { inp = iobuf_open(fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { @@ -992,276 +999,300 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, goto leave; } - handle_progress (pfx, inp, fname); + peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf); + if (peekbuflen < 0) + { + peekbuflen = 0; + if (DBG_FILTER) + log_debug ("peeking at input failed\n"); + } + + handle_progress (pfx, inp, fname); } - if( outfile ) { - if (is_secured_filename ( outfile )) { - out = NULL; - gpg_err_set_errno (EPERM); + if (outfile) + { + if (is_secured_filename ( outfile )) + { + out = NULL; + gpg_err_set_errno (EPERM); } - else - out = iobuf_create (outfile, 0); - if( !out ) - { - rc = gpg_error_from_syserror (); - log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) ); - goto leave; - } - else if( opt.verbose ) - log_info(_("writing to '%s'\n"), outfile ); + else + out = iobuf_create (outfile, 0); + if (!out) + { + rc = gpg_error_from_syserror (); + log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) ); + goto leave; + } + else if (opt.verbose) + log_info (_("writing to '%s'\n"), outfile); } - else if( (rc = open_outfile (-1, fname, - opt.armor? 1: detached? 2:0, 0, &out))) - goto leave; + else if ((rc = open_outfile (-1, fname, + opt.armor? 1: detached? 2:0, 0, &out))) + goto leave; - /* prepare to calculate the MD over the input */ - if( opt.textmode && !outfile && !multifile ) - { - memset( &tfx, 0, sizeof tfx); - iobuf_push_filter( inp, text_filter, &tfx ); - } + /* Prepare to calculate the MD over the input. */ + if (opt.textmode && !outfile && !multifile) + { + memset( &tfx, 0, sizeof tfx); + iobuf_push_filter( inp, text_filter, &tfx ); + } - if ( gcry_md_open (&mfx.md, 0, 0) ) - BUG (); - if (DBG_HASHING) - gcry_md_debug (mfx.md, "sign"); - - /* If we're encrypting and signing, it is reasonable to pick the - hash algorithm to use out of the recipient key prefs. This is - best effort only, as in a DSA2 and smartcard world there are - cases where we cannot please everyone with a single hash (DSA2 - wants >160 and smartcards want =160). In the future this could - be more complex with different hashes for each sk, but the - current design requires a single hash for all SKs. */ - if(pk_list) - { - if(opt.def_digest_algo) - { - if(!opt.expert && - select_algo_from_prefs(pk_list,PREFTYPE_HASH, - opt.def_digest_algo, - NULL)!=opt.def_digest_algo) - log_info(_("WARNING: forcing digest algorithm %s (%d)" - " violates recipient preferences\n"), - gcry_md_algo_name (opt.def_digest_algo), - opt.def_digest_algo ); - } - else - { - int algo; - int conflict = 0; - struct pref_hint hint = { 0 }; - - /* Of course, if the recipient asks for something - unreasonable (like the wrong hash for a DSA key) then - don't do it. Check all sk's - if any are DSA or live - on a smartcard, then the hash has restrictions and we - may not be able to give the recipient what they want. - For DSA, pass a hint for the largest q we have. Note - that this means that a q>160 key will override a q=160 - key and force the use of truncation for the q=160 key. - The alternative would be to ignore the recipient prefs - completely and get a different hash for each DSA key in - hash_for(). The override behavior here is more or less - reasonable as it is under the control of the user which - keys they sign with for a given message and the fact - that the message with multiple signatures won't be - usable on an implementation that doesn't understand - DSA2 anyway. */ - - for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) - { - if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA - || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) - { - int temp_hashlen = (gcry_mpi_get_nbits - (sk_rover->pk->pkey[1])); - - if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) - { - temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); - if (!temp_hashlen) - conflict = 1; /* Better don't use the prefs. */ - temp_hashlen = (temp_hashlen+7)/8; - /* Fixup for that funny nistp521 (yes, 521) - * were we need to use a 512 bit hash algo. */ - if (temp_hashlen == 66) - temp_hashlen = 64; - } - else + if (gcry_md_open (&mfx.md, 0, 0)) + BUG (); + if (DBG_HASHING) + gcry_md_debug (mfx.md, "sign"); + + /* If we're encrypting and signing, it is reasonable to pick the + * hash algorithm to use out of the recipient key prefs. This is + * best effort only, as in a DSA2 and smartcard world there are + * cases where we cannot please everyone with a single hash (DSA2 + * wants >160 and smartcards want =160). In the future this could + * be more complex with different hashes for each sk, but the + * current design requires a single hash for all SKs. */ + if (pk_list) + { + if (opt.def_digest_algo) + { + if (!opt.expert && + select_algo_from_prefs(pk_list,PREFTYPE_HASH, + opt.def_digest_algo, + NULL)!=opt.def_digest_algo) + log_info (_("WARNING: forcing digest algorithm %s (%d)" + " violates recipient preferences\n"), + gcry_md_algo_name (opt.def_digest_algo), + opt.def_digest_algo ); + } + else + { + int algo; + int conflict = 0; + struct pref_hint hint = { 0 }; + + /* Of course, if the recipient asks for something + * unreasonable (like the wrong hash for a DSA key) then + * don't do it. Check all sk's - if any are DSA or live + * on a smartcard, then the hash has restrictions and we + * may not be able to give the recipient what they want. + * For DSA, pass a hint for the largest q we have. Note + * that this means that a q>160 key will override a q=160 + * key and force the use of truncation for the q=160 key. + * The alternative would be to ignore the recipient prefs + * completely and get a different hash for each DSA key in + * hash_for(). The override behavior here is more or less + * reasonable as it is under the control of the user which + * keys they sign with for a given message and the fact + * that the message with multiple signatures won't be + * usable on an implementation that doesn't understand + * DSA2 anyway. */ + for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) + { + if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA + || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + { + int temp_hashlen = (gcry_mpi_get_nbits + (sk_rover->pk->pkey[1])); + + if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + { + temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); + if (!temp_hashlen) + conflict = 1; /* Better don't use the prefs. */ + temp_hashlen = (temp_hashlen+7)/8; + /* Fixup for that funny nistp521 (yes, 521) + * were we need to use a 512 bit hash algo. */ + if (temp_hashlen == 66) + temp_hashlen = 64; + } + else temp_hashlen = (temp_hashlen+7)/8; - /* Pick a hash that is large enough for our - largest q or matches our Q but if tehreare - several of them we run into a conflict and - don't use the preferences. */ - - if (hint.digest_length < temp_hashlen) - { - if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) - { - if (hint.exact) - conflict = 1; - hint.exact = 1; - } - hint.digest_length = temp_hashlen; - } - } - } - - if (!conflict - && (algo = select_algo_from_prefs (pk_list,PREFTYPE_HASH, - -1,&hint)) > 0) - { - /* Note that we later check that the algo is not weak. */ - recipient_digest_algo = algo; + /* Pick a hash that is large enough for our + * largest q or matches our Q but if tehreare + * several of them we run into a conflict and + * don't use the preferences. */ + if (hint.digest_length < temp_hashlen) + { + if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) + { + if (hint.exact) + conflict = 1; + hint.exact = 1; + } + hint.digest_length = temp_hashlen; + } } - } - } + } - for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) - gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); + if (!conflict + && (algo = select_algo_from_prefs (pk_list,PREFTYPE_HASH, + -1,&hint)) > 0) + { + /* Note that we later check that the algo is not weak. */ + recipient_digest_algo = algo; + } + } + } + + for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) + gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); - if( !multifile ) - iobuf_push_filter( inp, md_filter, &mfx ); + if (!multifile) + iobuf_push_filter (inp, md_filter, &mfx); - if( detached && !encryptflag) - afx->what = 2; + if (detached && !encryptflag) + afx->what = 2; - if( opt.armor && !outfile ) - push_armor_filter (afx, out); + if (opt.armor && !outfile) + push_armor_filter (afx, out); - if( encryptflag ) { - efx.pk_list = pk_list; - /* fixme: set efx.cfx.datalen if known */ - iobuf_push_filter( out, encrypt_filter, &efx ); + if (encryptflag) + { + efx.pk_list = pk_list; + /* fixme: set efx.cfx.datalen if known */ + iobuf_push_filter( out, encrypt_filter, &efx ); } - if (opt.compress_algo && !outfile && !detached) - { - int compr_algo=opt.compress_algo; + if (opt.compress_algo && !outfile && !detached) + { + int compr_algo = opt.compress_algo; - /* If not forced by user */ - if(compr_algo==-1) - { - /* If we're not encrypting, then select_algo_from_prefs - will fail and we'll end up with the default. If we are - encrypting, select_algo_from_prefs cannot fail since - there is an assumed preference for uncompressed data. - Still, if it did fail, we'll also end up with the - default. */ - - if((compr_algo= - select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) - compr_algo=default_compress_algo(); - } - else if(!opt.expert && pk_list - && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, - compr_algo,NULL)!=compr_algo) - log_info(_("WARNING: forcing compression algorithm %s (%d)" - " violates recipient preferences\n"), - compress_algo_to_string(compr_algo),compr_algo); - - /* algo 0 means no compression */ - if( compr_algo ) - push_compress_filter(out,&zfx,compr_algo); - } + if (!opt.explicit_compress_option + && is_file_compressed (peekbuf, peekbuflen)) + { + if (opt.verbose) + log_info(_("'%s' already compressed\n"), fname? fname: "[stdin]"); + compr_algo = 0; + } + else if (compr_algo==-1) + { + /* If we're not encrypting, then select_algo_from_prefs + * will fail and we'll end up with the default. If we are + * encrypting, select_algo_from_prefs cannot fail since + * there is an assumed preference for uncompressed data. + * Still, if it did fail, we'll also end up with the + * default. */ + if ((compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, + -1, NULL)) == -1) + { + compr_algo = default_compress_algo(); + } + } + else if (!opt.expert && pk_list + && select_algo_from_prefs (pk_list, PREFTYPE_ZIP, + compr_algo, NULL) != compr_algo) + { + log_info (_("WARNING: forcing compression algorithm %s (%d)" + " violates recipient preferences\n"), + compress_algo_to_string (compr_algo), compr_algo); + } - /* Write the one-pass signature packets if needed */ - if (!detached) { - rc = write_onepass_sig_packets (sk_list, out, - opt.textmode && !outfile ? 0x01:0x00); - if (rc) - goto leave; + /* Algo 0 means no compression. */ + if (compr_algo) + push_compress_filter (out, &zfx, compr_algo); } - write_status_begin_signing (mfx.md); + /* Write the one-pass signature packets if needed */ + if (!detached) + { + rc = write_onepass_sig_packets (sk_list, out, + opt.textmode && !outfile ? 0x01:0x00); + if (rc) + goto leave; + } + + write_status_begin_signing (mfx.md); - /* Setup the inner packet. */ - if( detached ) { - if( multifile ) { - strlist_t sl; - - if( opt.verbose ) - log_info(_("signing:") ); - /* must walk reverse trough this list */ - for( sl = strlist_last(filenames); sl; - sl = strlist_prev( filenames, sl ) ) { - inp = iobuf_open(sl->d); - if (inp && is_secured_file (iobuf_get_fd (inp))) - { - iobuf_close (inp); - inp = NULL; - gpg_err_set_errno (EPERM); - } - if( !inp ) - { - rc = gpg_error_from_syserror (); - log_error(_("can't open '%s': %s\n"), - sl->d,strerror(errno)); - goto leave; - } - handle_progress (pfx, inp, sl->d); - if( opt.verbose ) - log_printf (" '%s'", sl->d ); - if(opt.textmode) - { - memset( &tfx, 0, sizeof tfx); - iobuf_push_filter( inp, text_filter, &tfx ); - } - iobuf_push_filter( inp, md_filter, &mfx ); - while( iobuf_get(inp) != -1 ) - ; - iobuf_close(inp); inp = NULL; + /* Setup the inner packet. */ + if (detached) + { + if (multifile) + { + strlist_t sl; + + if (opt.verbose) + log_info(_("signing:") ); + /* Must walk reverse trough this list. */ + for (sl = strlist_last (filenames); sl; + sl = strlist_prev (filenames, sl)) + { + inp = iobuf_open(sl->d); + if (inp && is_secured_file (iobuf_get_fd (inp))) + { + iobuf_close (inp); + inp = NULL; + gpg_err_set_errno (EPERM); + } + if (!inp) + { + rc = gpg_error_from_syserror (); + log_error(_("can't open '%s': %s\n"), + sl->d,strerror(errno)); + goto leave; + } + handle_progress (pfx, inp, sl->d); + if (opt.verbose) + log_printf (" '%s'", sl->d ); + if (opt.textmode) + { + memset( &tfx, 0, sizeof tfx); + iobuf_push_filter( inp, text_filter, &tfx ); + } + iobuf_push_filter( inp, md_filter, &mfx ); + while (iobuf_read (inp, NULL, 1<<30) != -1 ) + ; + iobuf_close(inp); inp = NULL; } - if( opt.verbose ) - log_printf ("\n"); + if (opt.verbose) + log_printf ("\n"); } - else { - /* read, so that the filter can calculate the digest */ - while( iobuf_get(inp) != -1 ) - ; + else + { + /* Read, so that the filter can calculate the digest. */ + while (iobuf_read (inp, NULL, 1<<30) != -1 ) + ; } } - else { - rc = write_plaintext_packet (out, inp, fname, - opt.textmode && !outfile ? - (opt.mimemode? 'm':'t'):'b'); + else + { + rc = write_plaintext_packet (out, inp, fname, + opt.textmode && !outfile ? + (opt.mimemode? 'm':'t'):'b'); } - /* catch errors from above */ - if (rc) - goto leave; + /* Catch errors from above. */ + if (rc) + goto leave; - /* write the signatures */ - rc = write_signature_packets (ctrl, sk_list, out, mfx.md, - opt.textmode && !outfile? 0x01 : 0x00, - 0, duration, detached ? 'D':'S', NULL); - if( rc ) - goto leave; + /* Write the signatures. */ + rc = write_signature_packets (ctrl, sk_list, out, mfx.md, + opt.textmode && !outfile? 0x01 : 0x00, + 0, duration, detached ? 'D':'S', NULL); + if (rc) + goto leave; - leave: - if( rc ) - iobuf_cancel(out); - else { - iobuf_close(out); - if (encryptflag) - write_status( STATUS_END_ENCRYPTION ); + leave: + if (rc) + iobuf_cancel (out); + else + { + iobuf_close(out); + if (encryptflag) + write_status( STATUS_END_ENCRYPTION ); } - iobuf_close(inp); - gcry_md_close ( mfx.md ); - release_sk_list( sk_list ); - release_pk_list( pk_list ); - recipient_digest_algo=0; - release_progress_context (pfx); - release_armor_context (afx); - return rc; + iobuf_close(inp); + gcry_md_close ( mfx.md ); + release_sk_list( sk_list ); + release_pk_list( pk_list ); + recipient_digest_algo=0; + release_progress_context (pfx); + release_armor_context (afx); + return rc; } - /**************** * make a clear signature. note that opt.armor is not needed */ diff --git a/g10/test-stubs.c b/g10/test-stubs.c index 2ae4a41..9c45961 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -463,12 +463,12 @@ dotlock_remove_lockfiles (void) { } -gpg_error_t +int agent_probe_secret_key (ctrl_t ctrl, PKT_public_key *pk) { (void)ctrl; (void)pk; - return gpg_error (GPG_ERR_NO_SECKEY); + return 0; } gpg_error_t diff --git a/g10/trust.c b/g10/trust.c index 9749bd7..f11dfb7 100644 --- a/g10/trust.c +++ b/g10/trust.c @@ -59,7 +59,7 @@ register_trusted_key (const char *string) /* Some users have conf files with entries like * trusted-key 0x1234567812345678 # foo * That is obviously wrong. Before fixing bug#1206 trailing garbage - * on a key specification if was ignored. We detect the above use case + * on a key specification was ignored. We detect the above use case * here and cut off the junk-looking-like-a comment. */ if (strchr (string, '#')) { diff --git a/g10/trustdb.c b/g10/trustdb.c index 1b6da96..0a24e41 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -39,16 +39,14 @@ #include "tofu.h" #include "key-clean.h" -static void write_record (ctrl_t ctrl, TRUSTREC *rec); -static void do_sync(void); typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ /* - * Structure to keep track of keys, this is used as an array wherre - * the item right after the last one has a keyblock set to NULL. - * Maybe we can drop this thing and replace it by key_item + * Structure to keep track of keys, this is used as an array where the + * item right after the last one has a keyblock set to NULL. Maybe we + * can drop this thing and replace it by key_item */ struct key_array { @@ -65,12 +63,22 @@ static struct int no_trustdb; } trustdb_args; + /* Some globals. */ -static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */ static struct key_item *utk_list; /* all ultimately trusted keys */ +/* A list used to temporary store trusted keys and a flag indicated + * whether any --trusted-key option has been seen. */ +static struct key_item *trusted_key_list; +static int any_trusted_key_seen; + +/* Flag whether a trustdb check is pending. */ static int pending_check_trustdb; + + +static void write_record (ctrl_t ctrl, TRUSTREC *rec); +static void do_sync (void); static int validate_keys (ctrl_t ctrl, int interactive); @@ -200,11 +208,19 @@ tdb_register_trusted_keyid (u32 *keyid) k = new_key_item (); k->kid[0] = keyid[0]; k->kid[1] = keyid[1]; - k->next = user_utk_list; - user_utk_list = k; + k->next = trusted_key_list; + trusted_key_list = k; } +/* This is called for the option --trusted-key to register these keys + * for later syncing them into the trustdb. The special value "none" + * may be used to indicate that there is a trusted-key option but no + * key shall be inserted for it. This "none" value is helpful to + * distinguish between changing the gpg.conf from a trusted-key to no + * trusted-key options at all. Simply not specify the option would + * not allow to distinguish this case from the --no-options case as + * used for certain calls of gpg for example by gpg-wks-client. */ void tdb_register_trusted_key (const char *string) { @@ -212,6 +228,9 @@ tdb_register_trusted_key (const char *string) KEYDB_SEARCH_DESC desc; u32 kid[2]; + any_trusted_key_seen = 1; + if (!strcmp (string, "none")) + return; err = classify_user_id (string, &desc, 1); if (!err) { @@ -333,13 +352,14 @@ verify_own_keys (ctrl_t ctrl) fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; keyid_from_fingerprint (ctrl, fpr, fprlen, kid); if (!add_utk (kid)) - log_info(_("key %s occurs more than once in the trustdb\n"), - keystr(kid)); - else if ((rec.r.trust.flags & 1)) + log_info (_("key %s occurs more than once in the trustdb\n"), + keystr(kid)); + else if ((rec.r.trust.flags & 1) + && any_trusted_key_seen) { /* Record marked as inserted via --trusted-key. Is this * still the case? */ - for (k2 = user_utk_list; k2; k2 = k2->next) + for (k2 = trusted_key_list; k2; k2 = k2->next) if (k2->kid[0] == kid[0] && k2->kid[1] == kid[1]) break; if (!k2) /* No - clear the flag. */ @@ -363,7 +383,7 @@ verify_own_keys (ctrl_t ctrl) } /* Put any --trusted-key keys into the trustdb */ - for (k = user_utk_list; k; k = k->next) + for (k = trusted_key_list; k; k = k->next) { if ( add_utk (k->kid) ) { /* not yet in trustDB as ultimately trusted */ @@ -388,9 +408,9 @@ verify_own_keys (ctrl_t ctrl) } } - /* release the helper table table */ - release_key_items (user_utk_list); - user_utk_list = NULL; + /* Release the helper table. */ + release_key_items (trusted_key_list); + trusted_key_list = NULL; return; } @@ -687,7 +707,7 @@ tdb_check_or_update (ctrl_t ctrl) if (opt.interactive) update_trustdb (ctrl); else if (!opt.no_auto_check_trustdb) - check_trustdb (ctrl); + check_trustdb (ctrl); } } @@ -944,6 +964,7 @@ update_min_ownertrust (ctrl_t ctrl, u32 *kid, unsigned int new_trust) /* * Clear the ownertrust and min_ownertrust values. + * Also schedule a revalidation if a stale validity record exists. * * Return: True if a change actually happened. */ @@ -977,6 +998,26 @@ tdb_clear_ownertrusts (ctrl_t ctrl, PKT_public_key *pk) do_sync (); return 1; } + else + { + /* Check whether we have a stale RECTYPE_VALID for that key + * and if its validity ist set, schedule a revalidation. */ + ulong recno = rec.r.trust.validlist; + while (recno) + { + read_record (recno, &rec, RECTYPE_VALID); + if (rec.r.valid.validity) + break; + recno = rec.r.valid.next; + } + if (recno) + { + if (DBG_TRUST) + log_debug ("stale validity value detected" + " - scheduling check\n"); + tdb_revalidation_mark (ctrl); + } + } } else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { @@ -1669,38 +1710,50 @@ sanitize_regexp(const char *old) return new; } + /* Used by validate_one_keyblock to confirm a regexp within a trust - signature. Returns 1 for match, and 0 for no match or regex - error. */ + * signature. Returns 1 for match, and 0 for no match or regex + * error. */ static int -check_regexp(const char *expr,const char *string) +check_regexp (const char *expr,const char *string) { int ret; char *regexp; + char *stringbuf = NULL; + regex_t pat; - regexp=sanitize_regexp(expr); - - { - regex_t pat; + regexp = sanitize_regexp (expr); - ret=regcomp(&pat,regexp,REG_ICASE|REG_EXTENDED); - if(ret==0) - { - ret=regexec(&pat,string,0,NULL,0); - regfree(&pat); - } - ret=(ret==0); - } + ret = regcomp (&pat, regexp, (REG_ICASE|REG_EXTENDED)); + if (!ret) + { + if (*regexp == '<' && !strchr (string, '<') + && is_valid_mailbox (string)) + { + /* The R.E. starts with an angle bracket but STRING seems to + * be a plain mailbox (e.g. "foo@example.org"). The + * commonly used R.E. pattern "<[^>]+[@.]example\.org>$" + * won't be able to detect this. Thus we enclose STRING + * into angle brackets for checking. */ + stringbuf = xstrconcat ("<", string, ">", NULL); + string = stringbuf; + } + ret = regexec (&pat, string, 0, NULL, 0); + regfree (&pat); + } - if(DBG_TRUST) - log_debug("regexp '%s' ('%s') on '%s': %s\n", - regexp,expr,string,ret?"YES":"NO"); + ret = !ret; - xfree(regexp); + if (DBG_TRUST) + log_debug ("regexp '%s' ('%s') on '%s'%s: %s\n", + regexp, expr, string, stringbuf? " (fixed)":"", ret? "YES":"NO"); + xfree (regexp); + xfree (stringbuf); return ret; } + /* * Return true if the key is signed by one of the keys in the given * key ID list. User IDs with a valid signature are marked by node |