* main.h, seskey.c (encode_md_value): Modify to allow a q size greater
[gnupg.git] / g10 / sign.c
index 6a8ce29..925fef4 100644 (file)
@@ -1,5 +1,6 @@
 /* sign.c - sign data
- * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ *               2006 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -15,7 +16,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  */
 
 #include <config.h>
 #include "trustdb.h"
 #include "status.h"
 #include "i18n.h"
+#include "cardglue.h"
 
 
 #ifdef HAVE_DOSISH_SYSTEM
-  #define LF "\r\n"
-  void __stdcall Sleep(ulong);
-  #define sleep(a)  Sleep((a)*1000)
+#define LF "\r\n"
+void __stdcall Sleep(ulong);
+#define sleep(a)  Sleep((a)*1000)
 #else
-  #define LF "\n"
+#define LF "\n"
 #endif
 
 static int recipient_digest_algo=0;
 
 /****************
- * Create a notation.  It is assumed that the stings in STRLIST
- * are already checked to contain only printable data and have a valid
- * NAME=VALUE format.
+ * Create notations and other stuff.  It is assumed that the stings in
+ * STRLIST are already checked to contain only printable data and have
+ * a valid NAME=VALUE format.
  */
 static void
-mk_notation_and_policy( PKT_signature *sig,
+mk_notation_policy_etc( PKT_signature *sig,
                        PKT_public_key *pk, PKT_secret_key *sk )
 {
     const char *string;
     char *s=NULL;
-    byte *buf;
-    unsigned n1, n2;
-    STRLIST nd=NULL,pu=NULL;
+    STRLIST pu=NULL;
+    struct notation *nd=NULL;
     struct expando_args args;
 
     memset(&args,0,sizeof(args));
     args.pk=pk;
     args.sk=sk;
 
+    /* It is actually impossible to get here when making a v3 key
+       signature since keyedit.c:sign_uids will automatically bump a
+       signature with a notation or policy url up to v4, but it is
+       good to do these checks anyway. */
+
     /* notation data */
-    if(IS_SIG(sig) && opt.sig_notation_data)
+    if(IS_SIG(sig) && opt.sig_notations)
       {
        if(sig->version<4)
-         log_info("can't put notation data into v3 signatures\n");
+         log_error(_("can't put notation data into v3 (PGP 2.x style) "
+                     "signatures\n"));
        else
-         nd=opt.sig_notation_data;
+         nd=opt.sig_notations;
       }
-    else if( IS_CERT(sig) && opt.cert_notation_data )
+    else if( IS_CERT(sig) && opt.cert_notations )
       {
        if(sig->version<4)
-         log_info("can't put notation data into v3 key signatures\n");
+         log_error(_("can't put notation data into v3 (PGP 2.x style) "
+                     "key signatures\n"));
        else
-         nd=opt.cert_notation_data;
+         nd=opt.cert_notations;
       }
 
-    for( ; nd; nd = nd->next ) {
-        char *expanded;
-
-        string = nd->d;
-       s = strchr( string, '=' );
-       if( !s )
-         BUG(); /* we have already parsed this */
-       n1 = s - string;
-       s++;
+    if(nd)
+      {
+       struct notation *i;
 
-       expanded=pct_expando(s,&args);
-       if(!expanded)
+       for(i=nd;i;i=i->next)
          {
-           log_error(_("WARNING: unable to %%-expand notation "
-                       "(too large).  Using unexpanded.\n"));
-           expanded=m_strdup(s);
+           i->altvalue=pct_expando(i->value,&args);
+           if(!i->altvalue)
+             log_error(_("WARNING: unable to %%-expand notation "
+                         "(too large).  Using unexpanded.\n"));
          }
 
-       n2 = strlen(expanded);
-       buf = m_alloc( 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, string, n1 );
-       memcpy(buf+8+n1, expanded, n2 );
-       build_sig_subpkt( sig, SIGSUBPKT_NOTATION
-                         | ((nd->flags & 1)? SIGSUBPKT_FLAG_CRITICAL:0),
-                         buf, 8+n1+n2 );
-       m_free(expanded);
-       m_free(buf);
-    }
+       keygen_add_notations(sig,nd);
 
-    if(opt.show_notation)
-      show_notation(sig,0);
+       for(i=nd;i;i=i->next)
+         {
+           xfree(i->altvalue);
+           i->altvalue=NULL;
+         }
+      }
 
     /* set policy URL */
     if( IS_SIG(sig) && opt.sig_policy_url )
       {
        if(sig->version<4)
-         log_info("can't put a policy URL into v3 signatures\n");
+         log_error(_("can't put a policy URL into v3 (PGP 2.x style) "
+                     "signatures\n"));
        else
          pu=opt.sig_policy_url;
       }
     else if( IS_CERT(sig) && opt.cert_policy_url )
       {
        if(sig->version<4)
-         log_info("can't put a policy URL into v3 key signatures\n");
+         log_error(_("can't put a policy URL into v3 key (PGP 2.x style) "
+                     "signatures\n"));
        else
          pu=opt.cert_policy_url;
       }
@@ -148,20 +142,45 @@ mk_notation_and_policy( PKT_signature *sig,
        s=pct_expando(string,&args);
        if(!s)
          {
-           log_error(_("WARNING: unable to %%-expand policy url "
+           log_error(_("WARNING: unable to %%-expand policy URL "
                        "(too large).  Using unexpanded.\n"));
-           s=m_strdup(string);
+           s=xstrdup(string);
          }
 
        build_sig_subpkt(sig,SIGSUBPKT_POLICY|
                         ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
                         s,strlen(s));
 
-       m_free(s);
+       xfree(s);
       }
 
-    if(opt.show_policy_url)
-      show_policy_url(sig,0);
+    /* preferred keyserver URL */
+    if( IS_SIG(sig) && opt.sig_keyserver_url )
+      {
+       if(sig->version<4)
+         log_info("can't put a preferred keyserver URL into v3 signatures\n");
+       else
+         pu=opt.sig_keyserver_url;
+      }
+
+    for(;pu;pu=pu->next)
+      {
+        string = pu->d;
+
+       s=pct_expando(string,&args);
+       if(!s)
+         {
+           log_error(_("WARNING: unable to %%-expand preferred keyserver URL"
+                       " (too large).  Using unexpanded.\n"));
+           s=xstrdup(string);
+         }
+
+       build_sig_subpkt(sig,SIGSUBPKT_PREF_KS|
+                        ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
+                        s,strlen(s));
+
+       xfree(s);
+      }
 }
 
 
@@ -273,30 +292,65 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig,
     sig->digest_algo = digest_algo;
     sig->digest_start[0] = dp[0];
     sig->digest_start[1] = dp[1];
-    frame = encode_md_value( sk->pubkey_algo, md,
-                            digest_algo, mpi_get_nbits(sk->skey[0]), 0 );
-    if (!frame)
-        return G10ERR_GENERAL;
-    rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
-    mpi_free(frame);
+    if (sk->is_protected && sk->protect.s2k.mode == 1002) 
+      { 
+#ifdef ENABLE_CARD_SUPPORT
+        unsigned char *rbuf;
+        size_t rbuflen;
+        char *snbuf;
+        
+        snbuf = serialno_and_fpr_from_sk (sk->protect.iv,
+                                          sk->protect.ivlen, sk);
+        rc = agent_scd_pksign (snbuf, digest_algo,
+                               md_read (md, digest_algo),
+                               md_digest_length (digest_algo),
+                               &rbuf, &rbuflen);
+        xfree (snbuf);
+        if (!rc)
+          {
+            sig->data[0] = mpi_alloc ( (rbuflen+BYTES_PER_MPI_LIMB-1)
+                                       / BYTES_PER_MPI_LIMB );
+            mpi_set_buffer (sig->data[0], rbuf, rbuflen, 0);
+            xfree (rbuf);
+          }
+#else
+        return G10ERR_UNSUPPORTED;
+#endif /* ENABLE_CARD_SUPPORT */
+      }
+    else 
+      {
+       /* TODO: remove this check in the future once all the
+          variable-q DSA stuff makes it into the standard. */
+       if(!opt.expert
+          && sk->pubkey_algo==PUBKEY_ALGO_DSA
+          && md_digest_length(digest_algo)!=20)
+         {
+           log_error(_("DSA requires the use of a 160 bit hash algorithm\n"));
+           return G10ERR_GENERAL;
+         }
+
+        frame = encode_md_value( NULL, sk, md, digest_algo );
+        if (!frame)
+          return G10ERR_GENERAL;
+        rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
+        mpi_free(frame);
+      }
+
     if (!rc && !opt.no_sig_create_check) {
         /* check that the signature verification worked and nothing is
          * fooling us e.g. by a bug in the signature create
          * code or by deliberately introduced faults. */
-        PKT_public_key *pk = m_alloc_clear (sizeof *pk);
+        PKT_public_key *pk = xmalloc_clear (sizeof *pk);
 
         if( get_pubkey( pk, sig->keyid ) )
             rc = G10ERR_NO_PUBKEY;
         else {
-            frame = encode_md_value (pk->pubkey_algo, md,
-                                     sig->digest_algo,
-                                     mpi_get_nbits(pk->pkey[0]), 0);
+           frame = encode_md_value (pk, NULL, md, sig->digest_algo );
             if (!frame)
                 rc = G10ERR_GENERAL;
             else
                 rc = pubkey_verify (pk->pubkey_algo, frame,
-                                    sig->data, pk->pkey,
-                                    NULL, NULL );
+                                    sig->data, pk->pkey );
             mpi_free (frame);
         }
         if (rc)
@@ -308,17 +362,18 @@ do_sign( PKT_secret_key *sk, PKT_signature *sig,
        log_error(_("signing failed: %s\n"), g10_errstr(rc) );
     else {
        if( opt.verbose ) {
-           char *ustr = get_user_id_string( sig->keyid );
-           log_info(_("%s signature from: %s\n"),
-                     pubkey_algo_to_string(sk->pubkey_algo), ustr );
-           m_free(ustr);
+           char *ustr = get_user_id_string_native (sig->keyid);
+           log_info(_("%s/%s signature from: \"%s\"\n"),
+                    pubkey_algo_to_string(sk->pubkey_algo),
+                    digest_algo_to_string(sig->digest_algo),
+                    ustr );
+           xfree(ustr);
        }
     }
     return rc;
 }
 
 
-
 int
 complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md )
 {
@@ -329,17 +384,59 @@ complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md )
     return rc;
 }
 
+/*
+  First try --digest-algo.  If that isn't set, see if the recipient
+  has a preferred algorithm (which is also filtered through
+  --preferred-digest-prefs).  If we're making a signature without a
+  particular recipient (i.e. signing, rather than signing+encrypting)
+  then take the first algorithm in --preferred-digest-prefs that is
+  usable for the pubkey algorithm.  If --preferred-digest-prefs isn't
+  set, then take the OpenPGP default (i.e. SHA-1).
+
+  Possible improvement: Use the highest-ranked usable algorithm from
+  the signing key prefs either before or after using the personal
+  list?
+*/
+
 static int
-hash_for(int pubkey_algo, int packet_version )
+hash_for(PKT_secret_key *sk)
 {
-    if( opt.def_digest_algo )
-       return opt.def_digest_algo;
-    if( recipient_digest_algo )
-        return recipient_digest_algo;
-    if( pubkey_algo == PUBKEY_ALGO_DSA )
-       return DIGEST_ALGO_SHA1;
-    if( pubkey_algo == PUBKEY_ALGO_RSA && packet_version < 4 )
-       return DIGEST_ALGO_MD5;
+  if( opt.def_digest_algo )
+    return opt.def_digest_algo;
+  else if( recipient_digest_algo )
+    return recipient_digest_algo;
+  else if(sk->pubkey_algo==PUBKEY_ALGO_DSA
+         || (sk->is_protected && sk->protect.s2k.mode==1002))
+    {
+      /* The sk lives on a smartcard, or it's a DSA key.  DSA requires
+        a 160-bit hash, and current smartcards only handle SHA-1 and
+        RIPEMD/160 (i.e. 160-bit hashes).  This is correct now, but
+        may need revision as the cards add algorithms and/or DSA is
+        expanded to use larger hashes. */
+
+      if(opt.personal_digest_prefs)
+       {
+         prefitem_t *prefs;
+
+         for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
+           if(md_digest_length(prefs->value)==20)
+             return prefs->value;
+       }
+
+      return DIGEST_ALGO_SHA1;
+    }
+  else if(PGP2 && sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 )
+    {
+      /* Old-style PGP only understands MD5 */
+      return DIGEST_ALGO_MD5;
+    }
+  else if( opt.personal_digest_prefs )
+    {
+      /* It's not DSA, so we can use whatever the first hash algorithm
+        is in the pref list */
+      return opt.personal_digest_prefs[0].value;
+    }
+  else
     return DEFAULT_DIGEST_ALGO;
 }
 
@@ -408,9 +505,9 @@ write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass )
         }
 
         sk = sk_rover->sk;
-        ops = m_alloc_clear (sizeof *ops);
+        ops = xmalloc_clear (sizeof *ops);
         ops->sig_class = sigclass;
-        ops->digest_algo = hash_for (sk->pubkey_algo, sk->version);
+        ops->digest_algo = hash_for (sk);
         ops->pubkey_algo = sk->pubkey_algo;
         keyid_from_sk (sk, ops->keyid);
         ops->last = (skcount == 1);
@@ -443,39 +540,47 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode)
     if (!opt.no_literal) {
         if (fname || opt.set_filename) {
             char *s = make_basename (opt.set_filename? opt.set_filename
-                                                     : fname);
-            pt = m_alloc (sizeof *pt + strlen(s) - 1);
+                                                     : fname,
+                                     iobuf_get_real_fname(inp));
+            pt = xmalloc (sizeof *pt + strlen(s) - 1);
             pt->namelen = strlen (s);
             memcpy (pt->name, s, pt->namelen);
-            m_free (s);
+            xfree (s);
         }
         else { /* no filename */
-            pt = m_alloc (sizeof *pt - 1);
+            pt = xmalloc (sizeof *pt - 1);
             pt->namelen = 0;
         }
     }
 
     /* try to calculate the length of the data */
-    if (fname) {
-        if( !(filesize = iobuf_get_filelength(inp)) )
-            log_info (_("WARNING: `%s' is an empty file\n"), fname);
-
-        /* we can't yet encode the length of very large files,
-         * so we switch to partial length encoding in this case */
-        if (filesize >= IOBUF_FILELENGTH_LIMIT)
-            filesize = 0;
-
-        /* because the text_filter modifies the length of the
+    if ( !iobuf_is_pipe_filename (fname) && *fname )
+      {
+        off_t tmpsize;
+        int overflow;
+
+        if( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+            && !overflow )
+         log_info (_("WARNING: `%s' is an empty file\n"), fname);
+
+        /* 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 packet
+           headers, we switch to partial length encoding. */
+        if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+          filesize = tmpsize;
+        else
+          filesize = 0;
+
+        /* Because the text_filter modifies the length of the
          * data, it is not possible to know the used length
          * without a double read of the file - to avoid that
-         * we simple use partial length packets.
-         */
+         * we simple use partial length packets. */
         if ( ptmode == 't' )
-            filesize = 0;
-    }
-    else {
-        filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
-    }
+         filesize = 0;
+      }
+    else
+      filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
 
     if (!opt.no_literal) {
         PACKET pkt;
@@ -483,7 +588,7 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode)
         pt->timestamp = make_timestamp ();
         pt->mode = ptmode;
         pt->len = filesize;
-        pt->new_ctb = !pt->len && !opt.rfc1991;
+        pt->new_ctb = !pt->len && !RFC1991;
         pt->buf = inp;
         init_packet(&pkt);
         pkt.pkttype = PKT_PLAINTEXT;
@@ -505,7 +610,7 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode)
                            g10_errstr(rc));
                 break;
             }
-        memset(copy_buffer, 0, 4096); /* burn buffer */
+        wipememory(copy_buffer,4096); /* burn buffer */
     }
     /* fixme: it seems that we never freed pt/pkt */
     
@@ -533,15 +638,16 @@ write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash,
        sk = sk_rover->sk;
 
        /* build the signature packet */
-       sig = m_alloc_clear (sizeof *sig);
-       if(opt.force_v3_sigs || opt.rfc1991)
+       sig = xmalloc_clear (sizeof *sig);
+       if(opt.force_v3_sigs || RFC1991)
          sig->version=3;
-       else if(duration || opt.sig_policy_url || opt.sig_notation_data)
+       else if(duration || opt.sig_policy_url
+               || opt.sig_notations || opt.sig_keyserver_url)
          sig->version=4;
        else
          sig->version=sk->version;
        keyid_from_sk (sk, sig->keyid);
-       sig->digest_algo = hash_for (sk->pubkey_algo, sk->version);
+       sig->digest_algo = hash_for(sk);
        sig->pubkey_algo = sk->pubkey_algo;
        if(timestamp)
          sig->timestamp = timestamp;
@@ -555,12 +661,12 @@ write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash,
 
        if (sig->version >= 4)
            build_sig_subpkt_from_sig (sig);
-       mk_notation_and_policy (sig, NULL, sk);
+       mk_notation_policy_etc (sig, NULL, sk);
 
         hash_sigversion_to_magic (md, sig);
        md_final (md);
 
-       rc = do_sign( sk, sig, md, hash_for (sig->pubkey_algo, sk->version) );
+       rc = do_sign( sk, sig, md, hash_for (sk) );
        md_close (md);
 
        if( !rc ) { /* and write it */
@@ -606,6 +712,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
     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;
@@ -614,12 +721,11 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
     int multifile = 0;
-    u32 timestamp=0,duration=0;
+    u32 duration=0;
 
     memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
     memset( &mfx, 0, sizeof mfx);
-    memset( &tfx, 0, sizeof tfx);
     memset( &efx, 0, sizeof efx);
     init_packet( &pkt );
 
@@ -633,18 +739,26 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
     if( fname && filenames->next && (!detached || encryptflag) )
        log_bug("multiple files can only be detached signed");
 
-    if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991)
-      duration=ask_expire_interval(1);
+    if(encryptflag==2
+       && (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek)))
+      goto leave;
+
+    if(!opt.force_v3_sigs && !RFC1991)
+      {
+       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( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
        goto leave;
 
-    if(opt.pgp2 && !only_old_style(sk_list))
+    if(PGP2 && !only_old_style(sk_list))
       {
        log_info(_("you can only detach-sign with PGP 2.x style keys "
                   "while in --pgp2 mode\n"));
-       log_info(_("this message may not be usable by PGP 2.x\n"));
-       opt.pgp2=0;
+       compliance_failure();
       }
 
     if(encryptflag && (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC )))
@@ -653,19 +767,37 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
     /* prepare iobufs */
     if( multifile )  /* have list of filenames */
        inp = NULL; /* we do it later */
-    else if( !(inp = iobuf_open(fname)) ) {
-       log_error("can't open %s: %s\n", fname? fname: "[stdin]",
-                                       strerror(errno) );
-       rc = G10ERR_OPEN_FILE;
-       goto leave;
+    else {
+      inp = iobuf_open(fname);
+      if (inp && is_secured_file (iobuf_get_fd (inp)))
+        {
+          iobuf_close (inp);
+          inp = NULL;
+          errno = EPERM;
+        }
+      if( !inp ) {
+           log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+                     strerror(errno) );
+           rc = G10ERR_OPEN_FILE;
+           goto leave;
+       }
+
+        handle_progress (&pfx, inp, fname);
     }
 
     if( outfile ) {
-       if( !(out = iobuf_create( outfile )) ) {
-           log_error(_("can't create %s: %s\n"), outfile, strerror(errno) );
+        if (is_secured_filename ( outfile )) {
+            out = NULL;
+            errno = EPERM;
+        }
+        else
+            out = iobuf_create( outfile );
+       if( !out )
+         {
+           log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
            rc = G10ERR_CREATE_FILE;
            goto leave;
-       }
+         }
        else if( opt.verbose )
            log_info(_("writing to `%s'\n"), outfile );
     }
@@ -673,9 +805,15 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
        goto leave;
 
     /* prepare to calculate the MD over the input */
-    if( opt.textmode && !outfile )
+    if( opt.textmode && !outfile && !multifile )
+      {
+       memset( &tfx, 0, sizeof tfx);
        iobuf_push_filter( inp, text_filter, &tfx );
+      }
+
     mfx.md = md_open(0, 0);
+    if (DBG_HASHING)
+       md_start_debug (mfx.md, "sign");
 
    /* If we're encrypting and signing, it is reasonable to pick the
        hash algorithm to use out of the recepient key prefs. */
@@ -687,8 +825,8 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
               select_algo_from_prefs(pk_list,PREFTYPE_HASH,
                                      opt.def_digest_algo,
                                      NULL)!=opt.def_digest_algo)
-         log_info(_("forcing digest algorithm %s (%d) "
-                    "violates recipient preferences\n"),
+         log_info(_("WARNING: forcing digest algorithm %s (%d)"
+                    " violates recipient preferences\n"),
                   digest_algo_to_string(opt.def_digest_algo),
                   opt.def_digest_algo);
          }
@@ -704,25 +842,32 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
               sk, but so long as there is only one signing algorithm
               with hash restrictions, this is ok. -dms */
 
+           /* Current smartcards only do 160-bit hashes as well.
+              Note that this may well have to change as the cards add
+              algorithms. */
+
            for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
-             if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA)
+             if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA
+                || (sk_rover->sk->is_protected
+                    && sk_rover->sk->protect.s2k.mode==1002))
                hashlen=20;
 
            if((algo=
-               select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hashlen))>0)
+               select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,
+                                      hashlen?&hashlen:NULL))>0)
              recipient_digest_algo=algo;
          }
       }
 
     for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
        PKT_secret_key *sk = sk_rover->sk;
-       md_enable(mfx.md, hash_for(sk->pubkey_algo, sk->version ));
+       md_enable(mfx.md, hash_for(sk));
     }
 
     if( !multifile )
        iobuf_push_filter( inp, md_filter, &mfx );
 
-    if( detached && !encryptflag && !opt.rfc1991 )
+    if( detached && !encryptflag && !RFC1991 )
        afx.what = 2;
 
     if( opt.armor && !outfile  )
@@ -734,9 +879,9 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
        iobuf_push_filter( out, encrypt_filter, &efx );
     }
 
-    if( opt.compress && !outfile && ( !detached || opt.compress_sigs) )
+    if( opt.compress_algo && !outfile && ( !detached || opt.compress_sigs) )
       {
-        int compr_algo=opt.def_compress_algo;
+        int compr_algo=opt.compress_algo;
 
        /* If not forced by user */
        if(compr_algo==-1)
@@ -750,25 +895,22 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
  
            if((compr_algo=
                select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
-             compr_algo=DEFAULT_COMPRESS_ALGO;
+             compr_algo=default_compress_algo();
          }
-       else if(!opt.expert &&
-               select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
-                                      compr_algo,NULL)!=compr_algo)
-         log_info(_("forcing compression algorithm %s (%d) "
-                    "violates recipient preferences\n"),
+       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 )
-         {
-           zfx.algo = compr_algo;
-           iobuf_push_filter( out, compress_filter, &zfx );
-         }
+         push_compress_filter(out,&zfx,compr_algo);
       }
 
     /* Write the one-pass signature packets if needed */
-    if (!detached && !opt.rfc1991) {
+    if (!detached && !RFC1991) {
         rc = write_onepass_sig_packets (sk_list, out,
                                         opt.textmode && !outfile ? 0x01:0x00);
         if (rc)
@@ -785,14 +927,28 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
            /* must walk reverse trough this list */
            for( sl = strlist_last(filenames); sl;
                        sl = strlist_prev( filenames, sl ) ) {
-               if( !(inp = iobuf_open(sl->d)) ) {
-                   log_error(_("can't open %s: %s\n"),
-                                           sl->d, strerror(errno) );
+                inp = iobuf_open(sl->d);
+                if (inp && is_secured_file (iobuf_get_fd (inp)))
+                  {
+                    iobuf_close (inp);
+                    inp = NULL;
+                    errno = EPERM;
+                  }
+               if( !inp )
+                 {
+                   log_error(_("can't open `%s': %s\n"),
+                             sl->d,strerror(errno));
                    rc = G10ERR_OPEN_FILE;
                    goto leave;
-               }
+                 }
+                handle_progress (&pfx, inp, sl->d);
                if( opt.verbose )
                    fprintf(stderr, " `%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 )
                    ;
@@ -819,7 +975,7 @@ sign_file( STRLIST filenames, int detached, STRLIST locusr,
     /* write the signatures */
     rc = write_signature_packets (sk_list, out, mfx.md,
                                   opt.textmode && !outfile? 0x01 : 0x00,
-                                 timestamp, duration, detached ? 'D':'S');
+                                 0, duration, detached ? 'D':'S');
     if( rc )
         goto leave;
 
@@ -849,21 +1005,27 @@ int
 clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
 {
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     MD_HANDLE textmd = NULL;
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
     int rc = 0;
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
-    int old_style = opt.rfc1991;
+    int old_style = RFC1991;
     int only_md5 = 0;
-    u32 timestamp=0,duration=0;
+    u32 duration=0;
 
     memset( &afx, 0, sizeof afx);
     init_packet( &pkt );
 
-    if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991)
-      duration=ask_expire_interval(1);
+    if(!opt.force_v3_sigs && !RFC1991)
+      {
+       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( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
        goto leave;
@@ -871,28 +1033,42 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
     if( !old_style && !duration )
        old_style = only_old_style( sk_list );
 
-    if(!old_style && opt.pgp2)
+    if(PGP2 && !only_old_style(sk_list))
       {
        log_info(_("you can only clearsign with PGP 2.x style keys "
                   "while in --pgp2 mode\n"));
-       log_info(_("this message may not be usable by PGP 2.x\n"));
-       opt.pgp2=0;
+       compliance_failure();
       }
 
     /* prepare iobufs */
-    if( !(inp = iobuf_open(fname)) ) {
-       log_error("can't open %s: %s\n", fname? fname: "[stdin]",
+    inp = iobuf_open(fname);
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
+    if( !inp ) {
+       log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
                                        strerror(errno) );
        rc = G10ERR_OPEN_FILE;
        goto leave;
     }
+    handle_progress (&pfx, inp, fname);
 
     if( outfile ) {
-       if( !(out = iobuf_create( outfile )) ) {
-           log_error(_("can't create %s: %s\n"), outfile, strerror(errno) );
+        if (is_secured_filename (outfile) ) {
+            outfile = NULL;
+            errno = EPERM;
+        }
+        else 
+            out = iobuf_create( outfile );
+       if( !out )
+         {
+           log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
            rc = G10ERR_CREATE_FILE;
            goto leave;
-       }
+         }
        else if( opt.verbose )
            log_info(_("writing to `%s'\n"), outfile );
     }
@@ -903,7 +1079,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
 
     for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
        PKT_secret_key *sk = sk_rover->sk;
-       if( hash_for(sk->pubkey_algo, sk->version) == DIGEST_ALGO_MD5 )
+       if( hash_for(sk) == DIGEST_ALGO_MD5 )
            only_md5 = 1;
        else {
            only_md5 = 0;
@@ -920,7 +1096,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
        iobuf_writestr(out, "Hash: " );
        for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
            PKT_secret_key *sk = sk_rover->sk;
-           int i = hash_for(sk->pubkey_algo, sk->version);
+           int i = hash_for(sk);
 
            if( !hashs_seen[ i & 0xff ] ) {
                s = digest_algo_to_string( i );
@@ -945,12 +1121,12 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
     textmd = md_open(0, 0);
     for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
        PKT_secret_key *sk = sk_rover->sk;
-       md_enable(textmd, hash_for(sk->pubkey_algo, sk->version));
+       md_enable(textmd, hash_for(sk));
     }
     if ( DBG_HASHING )
        md_start_debug( textmd, "clearsign" );
-    copy_clearsig_text( out, inp, textmd,
-                       !opt.not_dash_escaped, opt.escape_from, old_style );
+    copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped,
+                       opt.escape_from, (old_style && only_md5) );
     /* fixme: check for read errors */
 
     /* now write the armor */
@@ -958,8 +1134,7 @@ clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
     iobuf_push_filter( out, armor_filter, &afx );
 
     /* write the signatures */
-    rc = write_signature_packets (sk_list, out, textmd, 0x01,
-                                 timestamp, duration, 'C');
+    rc=write_signature_packets (sk_list, out, textmd, 0x01, 0, duration, 'C');
     if( rc )
         goto leave;
 
@@ -982,6 +1157,7 @@ int
 sign_symencrypt_file (const char *fname, STRLIST locusr)
 {
     armor_filter_context_t afx;
+    progress_filter_context_t pfx;
     compress_filter_context_t zfx;
     md_filter_context_t mfx;
     text_filter_context_t tfx;
@@ -993,7 +1169,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
     SK_LIST sk_list = NULL;
     SK_LIST sk_rover = NULL;
     int algo;
-    u32 timestamp=0,duration=0;
+    u32 duration=0;
 
     memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
@@ -1002,8 +1178,13 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
     memset( &cfx, 0, sizeof cfx);
     init_packet( &pkt );
 
-    if(opt.ask_sig_expire && !opt.force_v3_sigs && !opt.batch && !opt.rfc1991)
-      duration=ask_expire_interval(1);
+    if(!opt.force_v3_sigs && !RFC1991)
+      {
+       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);
+      }
 
     rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG);
     if (rc) 
@@ -1011,24 +1192,30 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
 
     /* prepare iobufs */
     inp = iobuf_open(fname);
+    if (inp && is_secured_file (iobuf_get_fd (inp)))
+      {
+        iobuf_close (inp);
+        inp = NULL;
+        errno = EPERM;
+      }
     if( !inp ) {
-       log_error("can't open %s: %s\n", fname? fname: "[stdin]",
-                                       strerror(errno) );
+       log_error(_("can't open `%s': %s\n"), 
+                  fname? fname: "[stdin]", strerror(errno) );
        rc = G10ERR_OPEN_FILE;
        goto leave;
     }
+    handle_progress (&pfx, inp, fname);
 
     /* prepare key */
-    s2k = m_alloc_clear( sizeof *s2k );
-    s2k->mode = opt.rfc1991? 0:opt.s2k_mode;
-    s2k->hash_algo = opt.def_digest_algo ? opt.def_digest_algo
-                                        : opt.s2k_digest_algo;
+    s2k = xmalloc_clear( sizeof *s2k );
+    s2k->mode = RFC1991? 0:opt.s2k_mode;
+    s2k->hash_algo = S2K_DIGEST_ALGO;
 
-    algo = opt.def_cipher_algo ? opt.def_cipher_algo : opt.s2k_cipher_algo;
+    algo = default_cipher_algo();
     if (!opt.quiet || !opt.batch)
         log_info (_("%s encryption will be used\n"),
                    cipher_algo_to_string(algo) );
-    cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL );
+    cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL, NULL);
 
     if (!cfx.dek || !cfx.dek->keylen) {
         rc = G10ERR_PASSPHRASE;
@@ -1036,6 +1223,13 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
         goto leave;
     }
 
+    /* We have no way to tell if the recipient can handle messages
+       with an MDC, so this defaults to no.  Perhaps in a few years,
+       this can be defaulted to yes.  Note that like regular
+       encrypting, --force-mdc overrides --disable-mdc. */
+    if(opt.force_mdc)
+      cfx.dek->use_mdc=1;
+
     /* now create the outfile */
     rc = open_outfile (fname, opt.armor? 1:0, &out);
     if (rc)
@@ -1045,10 +1239,12 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
     if (opt.textmode)
        iobuf_push_filter (inp, text_filter, &tfx);
     mfx.md = md_open(0, 0);
+    if ( DBG_HASHING )
+       md_start_debug (mfx.md, "symc-sign");
 
     for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) {
        PKT_secret_key *sk = sk_rover->sk;
-       md_enable (mfx.md, hash_for (sk->pubkey_algo, sk->version ));
+       md_enable (mfx.md, hash_for (sk));
     }
 
     iobuf_push_filter (inp, md_filter, &mfx);
@@ -1059,8 +1255,8 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
 
     /* Write the symmetric key packet */
     /*(current filters: armor)*/
-    if (!opt.rfc1991) {
-       PKT_symkey_enc *enc = m_alloc_clear( sizeof *enc );
+    if (!RFC1991) {
+       PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc );
        enc->version = 4;
        enc->cipher_algo = cfx.dek->algo;
        enc->s2k = *s2k;
@@ -1068,31 +1264,19 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
        pkt.pkt.symkey_enc = enc;
        if( (rc = build_packet( out, &pkt )) )
            log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
-       m_free(enc);
+       xfree(enc);
     }
 
     /* Push the encryption filter */
     iobuf_push_filter( out, cipher_filter, &cfx );
 
-    /* Push the Zip filter */
-    if (opt.compress)
-      {
-       int compr_algo=opt.def_compress_algo;
-
-       /* Default */
-        if(compr_algo==-1)
-         compr_algo=DEFAULT_COMPRESS_ALGO;
-
-       if (compr_algo)
-         {
-           zfx.algo = compr_algo;
-           iobuf_push_filter( out, compress_filter, &zfx );
-         }
-      }
+    /* Push the compress filter */
+    if (default_compress_algo())
+      push_compress_filter(out,&zfx,default_compress_algo());
 
     /* Write the one-pass signature packets */
     /*(current filters: zip - encrypt - armor)*/
-    if (!opt.rfc1991) {
+    if (!RFC1991) {
         rc = write_onepass_sig_packets (sk_list, out,
                                         opt.textmode? 0x01:0x00);
         if (rc)
@@ -1109,7 +1293,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
     /*(current filters: zip - encrypt - armor)*/
     rc = write_signature_packets (sk_list, out, mfx.md,
                                  opt.textmode? 0x01 : 0x00,
-                                 timestamp, duration, 'S');
+                                 0, duration, 'S');
     if( rc )
         goto leave;
 
@@ -1124,8 +1308,8 @@ sign_symencrypt_file (const char *fname, STRLIST locusr)
     iobuf_close(inp);
     release_sk_list( sk_list );
     md_close( mfx.md );
-    m_free(cfx.dek);
-    m_free(s2k);
+    xfree(cfx.dek);
+    xfree(s2k);
     return rc;
 }
 
@@ -1153,7 +1337,7 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
     MD_HANDLE md;
 
     assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F
-           || sigclass == 0x20 || sigclass == 0x18
+           || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19
            || sigclass == 0x30 || sigclass == 0x28 );
 
     if (opt.force_v4_certs)
@@ -1165,7 +1349,9 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
     /* If you are making a signature on a v4 key using your v3 key, it
        doesn't make sense to generate a v3 sig.  After all, no v3-only
        PGP implementation could understand the v4 key in the first
-       place. */
+       place.  Note that this implies that a signature on an attribute
+       uid is usually going to be v4 as well, since they are not
+       generally found on v3 keys. */
     if (sigversion < pk->version)
         sigversion = pk->version;
 
@@ -1180,9 +1366,8 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
 
        if(opt.cert_digest_algo)
          digest_algo=opt.cert_digest_algo;
-       else if((sk->pubkey_algo==PUBKEY_ALGO_RSA ||
-                sk->pubkey_algo==PUBKEY_ALGO_RSA_S) &&
-               pk->version<4 && sigversion < 4)
+       else if(sk->pubkey_algo==PUBKEY_ALGO_RSA
+               && pk->version<4 && sigversion<4)
          digest_algo = DIGEST_ALGO_MD5;
        else
          digest_algo = DIGEST_ALGO_SHA1;
@@ -1190,16 +1375,21 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
 
     md = md_open( digest_algo, 0 );
 
-    /* hash the public key certificate and the user id */
+    /* hash the public key certificate */
     hash_public_key( md, pk );
-    if( sigclass == 0x18 || sigclass == 0x28 ) { /* subkey binding/revocation*/
+
+    if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 )
+      {
+       /* hash the subkey binding/backsig/revocation */
        hash_public_key( md, subpk );
-    }
-    else if( sigclass != 0x1F && sigclass != 0x20 ) {
+      }
+    else if( sigclass != 0x1F && sigclass != 0x20 )
+      {
+       /* hash the user id */
         hash_uid (md, sigversion, uid);
-    }
+      }
     /* and make the signature packet */
-    sig = m_alloc_clear( sizeof *sig );
+    sig = xmalloc_clear( sizeof *sig );
     sig->version = sigversion;
     sig->flags.exportable=1;
     sig->flags.revocable=1;
@@ -1215,12 +1405,15 @@ make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
     sig->sig_class = sigclass;
     if( sig->version >= 4 )
        build_sig_subpkt_from_sig( sig );
+    mk_notation_policy_etc( sig, pk, sk );
 
+    /* Crucial that the call to mksubpkt comes LAST before the calls
+       to finalize the sig as that makes it possible for the mksubpkt
+       function to get a reliable pointer to the subpacket area. */
     if( sig->version >= 4 && mksubpkt )
        rc = (*mksubpkt)( sig, opaque );
 
     if( !rc ) {
-       mk_notation_and_policy( sig, pk, sk );
         hash_sigversion_to_magic (md, sig);
        md_final(md);
 
@@ -1247,41 +1440,61 @@ update_keysig_packet( PKT_signature **ret_sig,
                       PKT_signature *orig_sig,
                       PKT_public_key *pk,
                       PKT_user_id *uid, 
+                      PKT_public_key *subpk,
                       PKT_secret_key *sk,
                       int (*mksubpkt)(PKT_signature *, void *),
-                      void *opaque
-                  )
+                      void *opaque )
 {
     PKT_signature *sig;
     int rc=0;
     MD_HANDLE md;
 
-    if (!orig_sig || !pk || !uid || !sk)
-        return G10ERR_GENERAL;
-    if (orig_sig->sig_class < 0x10 || orig_sig->sig_class > 0x13 )
-        return G10ERR_GENERAL;
+    if ((!orig_sig || !pk || !sk)
+       || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid)
+       || (orig_sig->sig_class == 0x18 && !subpk))
+      return G10ERR_GENERAL;
 
     md = md_open( orig_sig->digest_algo, 0 );
 
     /* hash the public key certificate and the user id */
     hash_public_key( md, pk );
-    hash_uid (md, orig_sig->version, uid);
+
+    if( orig_sig->sig_class == 0x18 )
+      hash_public_key( md, subpk );
+    else
+      hash_uid (md, orig_sig->version, uid);
 
     /* create a new signature packet */
     sig = copy_signature (NULL, orig_sig);
-    if ( sig->version >= 4 && mksubpkt)
-       rc = (*mksubpkt)(sig, opaque);
-
-    /* we increase the timestamp by one second so that a future import
-       of this key will replace the existing one.  We also make sure that
-       we don't create a timestamp in the future */
-    sig->timestamp++; 
-    while (sig->timestamp >= make_timestamp())
-        sleep (1);
-    /* put the updated timestamp back into the data */
+    /* We need to create a new timestamp so that new sig expiration
+       calculations are done correctly... */
+    sig->timestamp=make_timestamp();
+
+    /* ... but we won't make a timestamp earlier than the existing
+       one. */
+    while(sig->timestamp<=orig_sig->timestamp)
+      {
+       sleep(1);
+       sig->timestamp=make_timestamp();
+      }
+
+    /* Note that already expired sigs will remain expired (with a
+       duration of 0) since build-packet.c:build_sig_subpkt_from_sig
+       detects this case. */
+
     if( sig->version >= 4 )
+      {
+       /* Put the updated timestamp into the sig.  Note that this
+          will automagically lower any sig expiration dates to
+          correctly correspond to the differences in the timestamps
+          (i.e. the duration will shrink). */
        build_sig_subpkt_from_sig( sig );
 
+       if (mksubpkt)
+         rc = (*mksubpkt)(sig, opaque);
+      }
+
     if (!rc) {
         hash_sigversion_to_magic (md, sig);
        md_final(md);