From bb7986e9a6a43a8732797ecf72f6759b1c4feaa4 Mon Sep 17 00:00:00 2001 From: David Shaw Date: Sun, 26 Oct 2003 03:26:14 +0000 Subject: [PATCH] * 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. --- g10/ChangeLog | 26 ++++++++++++++ g10/encode.c | 97 +++++++++++++++++++++++++++++++++++++------------- g10/g10.c | 34 ++++++++++++++++-- g10/main.h | 2 +- g10/mainproc.c | 79 +++++++++++++++++++++++++++------------- 5 files changed, 185 insertions(+), 53 deletions(-) diff --git a/g10/ChangeLog b/g10/ChangeLog index 7d23d53e4..3ed0f520e 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,29 @@ +2003-10-25 David Shaw + + * 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 * ccid-driver.c (ccid_open_reader): Return an error if no USB diff --git a/g10/encode.c b/g10/encode.c index 4486cba4e..05fdb6ac3 100644 --- a/g10/encode.c +++ b/g10/encode.c @@ -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 );*/ + 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] = c->algo; - memcpy( buf + 1, 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 ); diff --git a/g10/g10.c b/g10/g10.c index 0f6003565..e1b4d42aa 100644 --- 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 */ diff --git a/g10/main.h b/g10/main.h index b64e33577..5ea89b18d 100644 --- a/g10/main.h +++ b/g10/main.h @@ -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); diff --git a/g10/mainproc.c b/g10/mainproc.c index e6522f1ed..aefaad6c3 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -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); }