* g10.c (main): Add --symmetric --encrypt command. This generates a
authorDavid Shaw <dshaw@jabberwocky.com>
Sun, 26 Oct 2003 03:26:14 +0000 (03:26 +0000)
committerDavid Shaw <dshaw@jabberwocky.com>
Sun, 26 Oct 2003 03:26:14 +0000 (03:26 +0000)
message that can be decrypted via a passphrase or public key system.

* main.h, encode.c (encode_seskey): Allow passing in an already-created
session key dek. (encode_simple): Use the actual symmetric cipher when
encrypting a session key for a symmetric message. (encode_crypt): Add a
flag to trigger a hybrid mode that can be decrypted via a passphrase or a
pk.  Change all callers.

* mainproc.c (symkey_decrypt_sesskey): There is no way to tell the
difference here between a bad passphrase and a cipher algorithm that we
don't have, so use a error message that makes that clear. Use the actual
list of ciphers when checking whether a cipher is invalid.  Return error
if the decrypted cipher algorithm is invalid. (proc_symkey_enc): In a
mixed passphrase/pk message, if a valid dek already exists from decrypting
via pk, do not try to process the passphrase. (proc_symkey_enc): Indicate
when we're decrypting a session key as opposed to decrypting data.  If a
passphrase is invalid, discard the dek so we'll keep trying.

g10/ChangeLog
g10/encode.c
g10/g10.c
g10/main.h
g10/mainproc.c

index 7d23d53..3ed0f52 100644 (file)
@@ -1,3 +1,29 @@
+2003-10-25  David Shaw  <dshaw@jabberwocky.com>
+
+       * g10.c (main): Add --symmetric --encrypt command.  This generates
+       a message that can be decrypted via a passphrase or public key
+       system.
+
+       * main.h, encode.c (encode_seskey): Allow passing in an
+       already-created session key dek.
+       (encode_simple): Use the actual symmetric cipher when encrypting a
+       session key for a symmetric message.
+       (encode_crypt): Add a flag to trigger a hybrid mode that can be
+       decrypted via a passphrase or a pk.  Change all callers.
+
+       * mainproc.c (symkey_decrypt_sesskey): There is no way to tell the
+       difference here between a bad passphrase and a cipher algorithm
+       that we don't have, so use a error message that makes that clear.
+       Use the actual list of ciphers when checking whether a cipher is
+       invalid.  Return error if the decrypted cipher algorithm is
+       invalid.
+       (proc_symkey_enc): In a mixed passphrase/pk message, if a valid
+       dek already exists from decrypting via pk, do not try to process
+       the passphrase.
+       (proc_symkey_enc): Indicate when we're decrypting a session key as
+       opposed to decrypting data.  If a passphrase is invalid, discard
+       the dek so we'll keep trying.
+
 2003-10-25  Werner Koch  <wk@gnupg.org>
 
        * ccid-driver.c (ccid_open_reader): Return an error if no USB
index 4486cba..05fdb6a 100644 (file)
@@ -42,8 +42,6 @@
 static int encode_simple( const char *filename, int mode, int use_seskey );
 static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
 
-
-
 /****************
  * Encode FILENAME with only the symmetric cipher.  Take input from
  * stdin if FILENAME is NULL.
@@ -65,32 +63,32 @@ encode_store( const char *filename )
 }
 
 static void
-encode_sesskey( DEK *dek, DEK **ret_dek, byte *enckey )
+encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
 {
     CIPHER_HANDLE hd;
-    DEK *c;
     byte buf[33];
 
     assert ( dek->keylen <= 32 );
-    
-    c = m_alloc_clear( sizeof *c );
-    c->keylen = dek->keylen;
-    c->algo = dek->algo;
-    make_session_key( c );
-    /*log_hexdump( "thekey", c->key, c->keylen );*/
-
-    buf[0] = c->algo;
-    memcpy( buf + 1, c->key, c->keylen );
+    if(!*seskey)
+      {
+       *seskey=m_alloc_clear(sizeof(DEK));
+       (*seskey)->keylen=dek->keylen;
+       (*seskey)->algo=dek->algo;
+       make_session_key(*seskey);
+       /*log_hexdump( "thekey", c->key, c->keylen );*/
+      }
+
+    buf[0] = (*seskey)->algo;
+    memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
     
     hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
     cipher_setkey( hd, dek->key, dek->keylen );
     cipher_setiv( hd, NULL, 0 );
-    cipher_encrypt( hd, buf, buf, c->keylen + 1 );
+    cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 );
     cipher_close( hd );
 
-    memcpy( enckey, buf, c->keylen + 1 );
+    memcpy( enckey, buf, (*seskey)->keylen + 1 );
     wipememory( buf, sizeof buf ); /* burn key */
-    *ret_dek = c;
 }
 
 /* We try very hard to use a MDC */
@@ -152,7 +150,6 @@ encode_simple( const char *filename, int mode, int use_seskey )
 {
     IOBUF inp, out;
     PACKET pkt;
-    DEK *dek = NULL;
     PKT_plaintext *pt = NULL;
     STRING2KEY *s2k = NULL;
     byte enckey[33];
@@ -212,11 +209,13 @@ encode_simple( const char *filename, int mode, int use_seskey )
                         "due to the S2K mode\n"));
         }
 
-        if ( use_seskey ) {            
-            seskeylen = cipher_get_keylen( opt.s2k_cipher_algo ) / 8;
-            encode_sesskey( cfx.dek, &dek, enckey );
+        if ( use_seskey )
+         {
+           DEK *dek = NULL;
+            seskeylen = cipher_get_keylen( default_cipher_algo() ) / 8;
+            encode_seskey( cfx.dek, &dek, enckey );
             m_free( cfx.dek ); cfx.dek = dek;
-        }
+         }
 
        cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
     }
@@ -378,11 +377,13 @@ encode_simple( const char *filename, int mode, int use_seskey )
  * is supplied).
  */
 int
-encode_crypt( const char *filename, STRLIST remusr )
+encode_crypt( const char *filename, STRLIST remusr, int use_symkey )
 {
     IOBUF inp = NULL, out = NULL;
     PACKET pkt;
     PKT_plaintext *pt = NULL;
+    DEK *symkey_dek = NULL;
+    STRING2KEY *symkey_s2k = NULL;
     int rc = 0, rc2 = 0;
     u32 filesize;
     cipher_filter_context_t cfx;
@@ -393,7 +394,6 @@ encode_crypt( const char *filename, STRLIST remusr )
     PK_LIST pk_list,work_list;
     int do_compress = opt.compress && !RFC1991;
 
-
     memset( &cfx, 0, sizeof cfx);
     memset( &afx, 0, sizeof afx);
     memset( &zfx, 0, sizeof zfx);
@@ -403,6 +403,22 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
        return rc;
 
+    if(use_symkey)
+      {
+       symkey_s2k=m_alloc_clear(sizeof(STRING2KEY));
+       symkey_s2k->mode = opt.s2k_mode;
+       symkey_s2k->hash_algo = opt.s2k_digest_algo;
+
+       symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo,
+                                    symkey_s2k,2,NULL,NULL);
+       if(!symkey_dek || !symkey_dek->keylen)
+         {
+           m_free(symkey_dek);
+           m_free(symkey_s2k);
+           return G10ERR_PASSPHRASE;
+         }
+      }
+
     if(PGP2) {
       for(work_list=pk_list; work_list; work_list=work_list->next)
        if(!(is_RSA(work_list->pk->pubkey_algo) &&
@@ -505,6 +521,37 @@ encode_crypt( const char *filename, STRLIST remusr )
     if( rc  )
        goto leave;
 
+    /* We put the passphrase (if any) after any public keys as this
+       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)
+      {
+       int seskeylen=cipher_get_keylen(cfx.dek->algo)/8;
+       PKT_symkey_enc *enc;
+       byte enckey[33];
+
+       enc=m_alloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
+       encode_seskey(symkey_dek,&cfx.dek,enckey);
+
+       enc->version = 4;
+       enc->cipher_algo = opt.s2k_cipher_algo;
+       enc->s2k = *symkey_s2k;
+       enc->seskeylen = seskeylen + 1; /* algo id */
+       memcpy( enc->seskey, enckey, seskeylen + 1 );
+
+       pkt.pkttype = PKT_SYMKEY_ENC;
+       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);
+           goto leave;
+         }
+
+       m_free(enc);
+      }
+
     if (!opt.no_literal) {
        /* setup the inner packet */
        if( filename || opt.set_filename ) {
@@ -618,6 +665,8 @@ encode_crypt( const char *filename, STRLIST remusr )
        pt->buf = NULL;
     free_packet(&pkt);
     m_free(cfx.dek);
+    m_free(symkey_dek);
+    m_free(symkey_s2k);
     release_pk_list( pk_list );
     return rc;
 }
@@ -788,7 +837,7 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
             }
           line[strlen(line)-1] = '\0';
           print_file_status(STATUS_FILE_START, line, 2);
-          if ( (rc = encode_crypt(line, remusr)) )
+          if ( (rc = encode_crypt(line, remusr, 0)) )
             log_error("%s: encryption failed: %s\n",
                       print_fname_stdin(line), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
@@ -799,7 +848,7 @@ encode_crypt_files(int nfiles, char **files, STRLIST remusr)
       while (nfiles--)
         {
           print_file_status(STATUS_FILE_START, *files, 2);
-          if ( (rc = encode_crypt(*files, remusr)) )
+          if ( (rc = encode_crypt(*files, remusr, 0)) )
             log_error("%s: encryption failed: %s\n",
                       print_fname_stdin(*files), g10_errstr(rc) );
           write_status( STATUS_FILE_DONE );
index 0f60035..e1b4d42 100644 (file)
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -80,7 +80,8 @@ enum cmd_and_opt_values
     oShowNotation,
     oNoShowNotation,
     aEncrFiles,
-    aDecryptFiles,                          
+    aEncrSym,
+    aDecryptFiles,
     aClearsign,
     aStore,
     aKeygen,
@@ -866,6 +867,10 @@ set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd )
        cmd = aSignSym;
     else if( cmd == aSym && new_cmd == aSign )
        cmd = aSignSym;
+    else if( cmd == aSym && new_cmd == aEncr )
+       cmd = aEncrSym;
+    else if( cmd == aEncr && new_cmd == aSym )
+       cmd = aEncrSym;
     else if( cmd == aKMode && new_cmd == aSym )
        cmd = aKModeC;
     else if(   ( cmd == aSign     && new_cmd == aClearsign )
@@ -2255,6 +2260,9 @@ main( int argc, char **argv )
          case aSym:
            cmdname="--symmetric";
            break;
+         case aEncrSym:
+           cmdname="--symmetric --encrypt";
+           break;
          case aStore:
            cmdname="--store";
            break;
@@ -2450,12 +2458,32 @@ main( int argc, char **argv )
          {
            if( argc > 1 )
              wrong_args(_("--encrypt [filename]"));
-           if( (rc = encode_crypt(fname,remusr)) )
+           if( (rc = encode_crypt(fname,remusr,0)) )
+             log_error("%s: encryption failed: %s\n",
+                       print_fname_stdin(fname), g10_errstr(rc) );
+         }
+       break;
+
+      case aEncrSym:
+       /* This works with PGP 8.  It doesn't work with 2 or 6.  It
+          might work with 7, but alas, I don't have a copy to test
+          with right now. */
+       if( argc > 1 )
+         wrong_args(_("--symmetric --encrypt [filename]"));
+       else if(opt.s2k_mode==0)
+         log_error(_("you cannot use --symmetric --encrypt"
+                     " with --s2k-mode 0\n"));
+       else if(PGP2 || PGP6 || PGP7 || RFC1991)
+         log_error(_("you cannot use --symmetric --encrypt"
+                     " while in %s mode\n"),compliance_option_string());
+       else
+         {
+           if( (rc = encode_crypt(fname,remusr,1)) )
              log_error("%s: encryption failed: %s\n",
                        print_fname_stdin(fname), g10_errstr(rc) );
          }
        break;
-          
+
       case aSign: /* sign the given file */
        sl = NULL;
        if( detached_sig ) { /* sign all files */
index b64e335..5ea89b1 100644 (file)
@@ -112,7 +112,7 @@ void display_online_help( const char *keyword );
 /*-- encode.c --*/
 int encode_symmetric( const char *filename );
 int encode_store( const char *filename );
-int encode_crypt( const char *filename, STRLIST remusr );
+int encode_crypt( const char *filename, STRLIST remusr, int use_symkey );
 void encode_crypt_files(int nfiles, char **files, STRLIST remusr);
 int encrypt_filter( void *opaque, int control,
                    IOBUF a, byte *buf, size_t *ret_len);
index e6522f1..aefaad6 100644 (file)
@@ -1,5 +1,6 @@
 /* mainproc.c - handle packets
- * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ *               2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -238,8 +239,8 @@ add_signature( CTX c, PACKET *pkt )
     return 1;
 }
 
-static void
-symkey_decrypt_sesskey( DEK *dek, byte *sesskey, size_t slen )
+static int
+symkey_decrypt_seskey( DEK *dek, byte *seskey, size_t slen )
 {
     CIPHER_HANDLE hd;
     int n;
@@ -247,28 +248,35 @@ symkey_decrypt_sesskey( DEK *dek, byte *sesskey, size_t slen )
     if ( slen < 17 || slen > 33 ) {
         log_error ( _("weird size for an encrypted session key (%d)\n"),
                    (int)slen);
-        return;   
+        return G10ERR_BAD_KEY;
     }
     hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
     cipher_setkey( hd, dek->key, dek->keylen );
     cipher_setiv( hd, NULL, 0 );
-    cipher_decrypt( hd, sesskey, sesskey, slen );
+    cipher_decrypt( hd, seskey, seskey, slen );
     cipher_close( hd );
     /* check first byte (the cipher algo) */
-    if ( sesskey[0] > 10 ) {
-        log_error ( _("invalid symkey algorithm detected (%d)\n"),
-                    sesskey[0] );
-        return;
-    }
-    n = cipher_get_keylen (sesskey[0]) / 8;
+    if(check_cipher_algo(seskey[0]))
+      {
+       /* There is no way to tell the difference here between a bad
+          passphrase and a cipher algorithm that we don't have. */
+       log_error(_("bad passphrase or unknown cipher algorithm (%d)\n"),
+                 seskey[0]);
+       if(seskey[0]==CIPHER_ALGO_IDEA)
+         idea_cipher_warn(0);
+       return G10ERR_PASSPHRASE;
+      }
+    n = cipher_get_keylen (seskey[0]) / 8;
     if (n > DIM(dek->key))
          BUG ();
     /* now we replace the dek components with the real session key
        to decrypt the contents of the sequencing packet. */
-    dek->keylen = cipher_get_keylen( sesskey[0] ) / 8;
-    dek->algo = sesskey[0];
-    memcpy( dek->key, sesskey + 1, dek->keylen );
+    dek->keylen = cipher_get_keylen( seskey[0] ) / 8;
+    dek->algo = seskey[0];
+    memcpy( dek->key, seskey + 1, dek->keylen );
     /*log_hexdump( "thekey", dek->key, dek->keylen );*/
+
+    return 0;
 }   
 
 static void
@@ -279,26 +287,47 @@ proc_symkey_enc( CTX c, PACKET *pkt )
     enc = pkt->pkt.symkey_enc;
     if (!enc)
         log_error ("invalid symkey encrypted packet\n");
-    else {
+    else if(!c->dek)
+      {
         int algo = enc->cipher_algo;
-       const char *s;
+       const char *s = cipher_algo_to_string (algo);
 
-       s = cipher_algo_to_string (algo);
        if( s )
-           log_info(_("%s encrypted data\n"), s );
+         {
+           if(enc->seskeylen)
+             log_info(_("%s encrypted session key\n"), s );
+           else
+             log_info(_("%s encrypted data\n"), s );
+         }
        else
-           log_info(_("encrypted with unknown algorithm %d\n"), algo );
+         log_info(_("encrypted with unknown algorithm %d\n"), algo );
 
        c->last_was_session_key = 2;
        if ( opt.list_only )
            goto leave;
        c->dek = passphrase_to_dek( NULL, 0, algo, &enc->s2k, 0, NULL, NULL );
-        if (c->dek)
-            c->dek->algo_info_printed = 1;
-        if ( c->dek && enc->seskeylen )
-            symkey_decrypt_sesskey( c->dek, enc->seskey, enc->seskeylen );
-    }
-leave:
+       if(c->dek)
+         {
+           /* FIXME: This doesn't work perfectly if a symmetric key
+              comes before a public key in the message - if the user
+              doesn't know the passphrase, then there is a chance
+              that the "decrypted" algorithm will happen to be a
+              valid one, which will make the returned dek appear
+              valid, so we won't try any public keys that come
+              later. */
+           if(enc->seskeylen)
+             {
+               if(symkey_decrypt_seskey(c->dek, enc->seskey, enc->seskeylen))
+                 {
+                   m_free(c->dek);
+                   c->dek=NULL;
+                 }
+             }
+           else
+             c->dek->algo_info_printed = 1;
+         }
+      }
+ leave:
     free_packet(pkt);
 }