diff --git a/common/iobuf.c b/common/iobuf.c
index 2d2c8a585..f4766c6a4 100644
--- a/common/iobuf.c
+++ b/common/iobuf.c
@@ -1,7 +1,7 @@
/* iobuf.c - File Handling for OpenPGP.
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
* 2009, 2010, 2011 Free Software Foundation, Inc.
- * Copyright (C) 2015 g10 Code GmbH
+ * Copyright (C) 2015, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@@ -93,6 +93,9 @@ typedef struct
int no_cache;
int eof_seen;
int print_only_name; /* Flags indicating that fname is not a real file. */
+ char peeked[32]; /* Read ahead buffer. */
+ byte npeeked; /* Number of bytes valid in peeked. */
+ byte upeeked; /* Number of bytes used from peeked. */
char fname[1]; /* Name of the file. */
} file_filter_ctx_t;
@@ -454,7 +457,16 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
if (control == IOBUFCTRL_UNDERFLOW)
{
log_assert (size); /* We need a buffer. */
- if (a->eof_seen)
+ if (a->npeeked > a->upeeked)
+ {
+ nbytes = a->npeeked - a->upeeked;
+ if (nbytes > size)
+ nbytes = size;
+ memcpy (buf, a->peeked + a->upeeked, nbytes);
+ a->upeeked += nbytes;
+ *ret_len = nbytes;
+ }
+ else if (a->eof_seen)
{
rc = -1;
*ret_len = 0;
@@ -573,6 +585,68 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
a->eof_seen = 0;
a->keep_open = 0;
a->no_cache = 0;
+ a->npeeked = 0;
+ a->upeeked = 0;
+ }
+ else if (control == IOBUFCTRL_PEEK)
+ {
+ /* Peek on the input. */
+#ifdef HAVE_W32_SYSTEM
+ unsigned long nread;
+
+ nbytes = 0;
+ if (!ReadFile (f, a->peeked, sizeof a->peeked, &nread, NULL))
+ {
+ int ec = (int) GetLastError ();
+ if (ec != ERROR_BROKEN_PIPE)
+ {
+ rc = gpg_error_from_errno (ec);
+ log_error ("%s: read error: ec=%d\n", a->fname, ec);
+ }
+ a->npeeked = 0;
+ }
+ else if (!nread)
+ {
+ a->eof_seen = 1;
+ a->npeeked = 0;
+ }
+ else
+ {
+ a->npeeked = nread;
+ }
+
+#else /* Unix */
+
+ int n;
+
+ peek_more:
+ do
+ {
+ n = read (f, a->peeked + a->npeeked, sizeof a->peeked - a->npeeked);
+ }
+ while (n == -1 && errno == EINTR);
+ if (n > 0)
+ {
+ a->npeeked += n;
+ if (a->npeeked < sizeof a->peeked)
+ goto peek_more;
+ }
+ else if (!n) /* eof */
+ {
+ a->eof_seen = 1;
+ }
+ else /* error */
+ {
+ rc = gpg_error_from_syserror ();
+ if (gpg_err_code (rc) != GPG_ERR_EPIPE)
+ log_error ("%s: read error: %s\n", a->fname, gpg_strerror (rc));
+ }
+#endif /* Unix */
+
+ size = a->npeeked < size? a->npeeked : size;
+ memcpy (buf, a->peeked, size);
+ *ret_len = size;
+ rc = 0; /* Return success - the user needs to check ret_len. */
}
else if (control == IOBUFCTRL_DESC)
{
@@ -1487,6 +1561,25 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
return fd_cache_synchronize (ptrval);
}
}
+ else if (cmd == IOBUF_IOCTL_PEEK)
+ {
+ /* Peek at a justed opened file. Use this only directly after a
+ * file has been opened for reading. Don't use it after you did
+ * a seek. This works only if just file filter has been
+ * pushed. Expects a buffer wit size INTVAL at PTRVAL and returns
+ * the number of bytes put into the buffer. */
+ if (DBG_IOBUF)
+ log_debug ("iobuf-%d.%d: ioctl '%s' peek\n",
+ a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc));
+ if (a->filter == file_filter && ptrval && intval)
+ {
+ file_filter_ctx_t *fcx = a->filter_ov;
+ size_t len = intval;
+
+ if (!file_filter (fcx, IOBUFCTRL_PEEK, NULL, ptrval, &len))
+ return (int)len;
+ }
+ }
return -1;
diff --git a/common/iobuf.h b/common/iobuf.h
index 624e154d8..79d361c18 100644
--- a/common/iobuf.h
+++ b/common/iobuf.h
@@ -106,6 +106,7 @@ enum
IOBUFCTRL_FLUSH = 4,
IOBUFCTRL_DESC = 5,
IOBUFCTRL_CANCEL = 6,
+ IOBUFCTRL_PEEK = 7,
IOBUFCTRL_USER = 16
};
@@ -116,7 +117,8 @@ typedef enum
IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */
IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */
IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */
- IOBUF_IOCTL_FSYNC = 4 /* Uses ptrval. */
+ IOBUF_IOCTL_FSYNC = 4, /* Uses ptrval. */
+ IOBUF_IOCTL_PEEK = 5 /* Uses intval and ptrval. */
} iobuf_ioctl_t;
enum iobuf_use
diff --git a/common/miscellaneous.c b/common/miscellaneous.c
index c3775547b..b73239eaa 100644
--- a/common/miscellaneous.c
+++ b/common/miscellaneous.c
@@ -466,7 +466,7 @@ decode_c_string (const char *src)
/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed
* packet. LEN should be at least 6. */
static int
-is_openpgp_compressed_packet (unsigned char *buf, size_t len)
+is_openpgp_compressed_packet (const unsigned char *buf, size_t len)
{
int c, ctb, pkttype;
int lenbytes;
@@ -508,63 +508,46 @@ is_openpgp_compressed_packet (unsigned char *buf, size_t len)
/*
- * Check if the file is compressed.
+ * Check if the file is compressed. You need to pass the first bytes
+ * of the file as (BUF,BUFLEN). Returns true if the buffer seems to
+ * be compressed.
*/
int
-is_file_compressed (const char *s, int *ret_rc)
+is_file_compressed (const byte *buf, unsigned int buflen)
{
- iobuf_t a;
- byte buf[6];
- int i;
- int rc = 0;
- int overflow;
+ int i;
- struct magic_compress_s {
- size_t len;
- byte magic[4];
- } magic[] = {
- { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
- { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
- { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
- };
+ struct magic_compress_s
+ {
+ byte len;
+ byte magic[5];
+ } magic[] =
+ {
+ { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
+ { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
+ { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
+ { 5, { '%', 'P', 'D', 'F', '-'} } /* PDF */
+ };
- if ( iobuf_is_pipe_filename (s) || !ret_rc )
- return 0; /* We can't check stdin or no file was given */
-
- a = iobuf_open( s );
- if ( a == NULL ) {
- *ret_rc = gpg_error_from_syserror ();
- return 0;
- }
- iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
-
- if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) {
- *ret_rc = 0;
- goto leave;
+ if ( buflen < 6 )
+ {
+ return 0; /* Too short to check - assume uncompressed. */
}
- if ( iobuf_read( a, buf, 6 ) == -1 ) {
- *ret_rc = a->error;
- goto leave;
- }
-
- for ( i = 0; i < DIM( magic ); i++ ) {
- if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) {
- *ret_rc = 0;
- rc = 1;
- goto leave;
+ for ( i = 0; i < DIM (magic); i++ )
+ {
+ if ( !memcmp( buf, magic[i].magic, magic[i].len ))
+ {
+ return 1; /* Is compressed. */
}
}
- if (is_openpgp_compressed_packet (buf, 6))
- {
- *ret_rc = 0;
- rc = 1;
- }
+ if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen))
+ {
+ return 1; /* Already compressed. */
+ }
- leave:
- iobuf_close( a );
- return rc;
+ return 0; /* Not detected as compressed. */
}
diff --git a/common/util.h b/common/util.h
index cd961d212..e4f8d2470 100644
--- a/common/util.h
+++ b/common/util.h
@@ -347,7 +347,7 @@ char *try_make_printable_string (const void *p, size_t n, int delim);
char *make_printable_string (const void *p, size_t n, int delim);
char *decode_c_string (const char *src);
-int is_file_compressed (const char *s, int *ret_rc);
+int is_file_compressed (const byte *buf, unsigned int buflen);
int match_multistr (const char *multistr,const char *match);
diff --git a/doc/gpg.texi b/doc/gpg.texi
index 9491dc83f..d6320f576 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -848,9 +848,10 @@ line.
@opindex keyedit:tsign
Make a trust signature. This is a signature that combines the notions
of certification (like a regular signature), and trust (like the
- "trust" command). It is generally only useful in distinct communities
- or groups. For more information please read the sections
- ``Trust Signature'' and ``Regular Expression'' in RFC-4880.
+ "trust" command). It is generally useful in distinct communities
+ or groups to implement the concept of a Trusted Introducer. For
+ more information please read the sections ``Trust Signature'' and
+ ``Regular Expression'' in RFC-4880.
@end table
@c man:.RS
@@ -1619,6 +1620,16 @@ for the BZIP2 compression algorithm (defaulting to 6 as well). This is a
different option from @option{--compress-level} since BZIP2 uses a
significant amount of memory for each additional compression level.
@option{-z} sets both. A value of 0 for @var{n} disables compression.
+A value of -1 forces compression using the default level.
+
+Except for the @option{--store} command compression is always used
+unless @command{gpg} detects that the input is already compressed. To
+inhibit the use of compression use @option{-z0}; to force compression
+use @option{-z-1} or option @option{z} with another compression level
+than the default as indicated by -1. Note that this overriding of the
+default deection works only with @option{z} and not with the long
+variant of this option.
+
@item --bzip2-decompress-lowmem
@opindex bzip2-decompress-lowmem
diff --git a/g10/encrypt.c b/g10/encrypt.c
index 982a128d0..3d8d9160f 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 .
+ * SPDX-License-Identifier: GPL-3.0-or-later
*/
#include
@@ -326,6 +327,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))
{
@@ -362,6 +365,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)
@@ -426,10 +437,11 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if (do_compress
&& cfx.dek
&& (cfx.dek->use_mdc || cfx.dek->use_aead)
- && is_file_compressed(filename, &rc))
+ && !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;
}
@@ -768,6 +780,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. */
@@ -840,6 +854,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)
@@ -874,10 +896,11 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (do_compress
&& (cfx.dek->use_mdc || cfx.dek->use_aead)
- && is_file_compressed(filename, &rc2))
+ && !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)
diff --git a/g10/gpg.c b/g10/gpg.c
index e7180b818..d7b75a1d8 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -3126,6 +3126,7 @@ 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 oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
diff --git a/g10/options.h b/g10/options.h
index ccf7397d0..020b558aa 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -92,6 +92,7 @@ struct
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;
diff --git a/g10/sign.c b/g10/sign.c
index 17c6bcdf8..eaf9d9ed5 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -921,62 +921,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;
+ 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;
+ 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,274 +998,298 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
goto leave;
}
- handle_progress (pfx, inp, fname);
- }
-
- if( outfile ) {
- if (is_secured_filename ( outfile )) {
- out = NULL;
- gpg_err_set_errno (EPERM);
+ 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");
}
- 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 );
+
+ handle_progress (pfx, inp, fname);
}
- 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 );
- }
+ 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 if ((rc = open_outfile (-1, fname,
+ opt.armor? 1: detached? 2:0, 0, &out)))
+ goto leave;
- if ( gcry_md_open (&mfx.md, 0, 0) )
- BUG ();
- if (DBG_HASHING)
- gcry_md_debug (mfx.md, "sign");
+ /* 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 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 };
+ if (gcry_md_open (&mfx.md, 0, 0))
+ BUG ();
+ if (DBG_HASHING)
+ gcry_md_debug (mfx.md, "sign");
- /* 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. */
+ /* 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 };
- 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]));
+ /* 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 (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( !multifile )
- iobuf_push_filter( inp, md_filter, &mfx );
-
- if( detached && !encryptflag)
- afx->what = 2;
-
- 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 (!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;
+ }
+ }
}
- if (opt.compress_algo && !outfile && !detached)
- {
- int compr_algo=opt.compress_algo;
+ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
+ gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
- /* 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 (!multifile)
+ iobuf_push_filter (inp, md_filter, &mfx);
- 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);
+ if (detached && !encryptflag)
+ afx->what = 2;
- /* algo 0 means no compression */
- if( compr_algo )
- push_compress_filter(out,&zfx,compr_algo);
- }
+ if (opt.armor && !outfile)
+ push_armor_filter (afx, out);
- /* 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;
+ if (encryptflag)
+ {
+ efx.pk_list = pk_list;
+ /* fixme: set efx.cfx.datalen if known */
+ iobuf_push_filter( out, encrypt_filter, &efx );
}
- write_status_begin_signing (mfx.md);
+ if (opt.compress_algo && !outfile && !detached)
+ {
+ int compr_algo = opt.compress_algo;
- /* Setup the inner packet. */
- if( detached ) {
- if( multifile ) {
- strlist_t sl;
+ 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);
+ }
- 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");
- }
- 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');
+ /* Algo 0 means no compression. */
+ if (compr_algo)
+ push_compress_filter (out, &zfx, compr_algo);
}
- /* 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 )
+ /* 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;
-
-
- 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;
-}
+ 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_read (inp, NULL, 1<<30) != -1 )
+ ;
+ iobuf_close(inp); inp = NULL;
+ }
+ if (opt.verbose)
+ log_printf ("\n");
+ }
+ 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');
+ }
+
+ /* 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;
+
+
+ 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;
+}
/****************