From 0154d59f4d44955c311fbd822d66eec4445b2c6a Mon Sep 17 00:00:00 2001
From: Werner Koch <wk@gnupg.org>
Date: Wed, 8 Mar 2000 17:42:19 +0000
Subject: [PATCH] See ChangeLog: Wed Mar  8 18:44:59 CET 2000  Werner Koch

---
 g10/ChangeLog |  19 ++
 g10/g10.c     |  15 +-
 g10/keygen.c  | 537 +++++++++++++++++++++++++++++++++++++++++++-------
 g10/main.h    |   2 +-
 4 files changed, 502 insertions(+), 71 deletions(-)

diff --git a/g10/ChangeLog b/g10/ChangeLog
index ac7e6c846..3b21ace0c 100644
--- a/g10/ChangeLog
+++ b/g10/ChangeLog
@@ -1,3 +1,22 @@
+2000-03-08 10:38:38  Werner Koch  (wk@habibti.openit.de)
+
+	* keygen.c (ask_algo): Removed is_v4 return value and the commented
+	code to create Elg keys in a v3 packet. Removed the rounding
+	of key sizes here.
+	(do_create): Likewise removed arg v4_packet.
+	(gen_elg): Likewise removed arg version. Now rounding keysizes here.
+	(gen_dsa): Rounding keysize now here.
+	(release_parameter_list): New
+	(get_parameter*): New.
+	(proc_parameter_file): New.
+	(read_parameter_file): New.
+	(generate_keypair): Splitted. Now uses read_parameter_file when in
+	batch mode.  Additional argument to specify a parameter file.
+	(do_generate_keypair): Main bulk of above fucntion and uses the
+	parameter list.
+	(do_create): Don't print long notice in batch mode.
+	* g10.c (main): Allow batched key generation.
+
 Thu Mar  2 15:37:46 CET 2000  Werner Koch  <wk@gnupg.de>
 
 	* pubkey-enc.c (get_it): Print a note about unknown cipher algos.
diff --git a/g10/g10.c b/g10/g10.c
index ae04184b9..38cd4bbec 100644
--- a/g10/g10.c
+++ b/g10/g10.c
@@ -1258,10 +1258,17 @@ main( int argc, char **argv )
 	    wrong_args(_("-k[v][v][v][c] [user-id] [keyring]") );
 	break;
 
-      case aKeygen: /* generate a key (interactive) */
-	if( argc )
-	    wrong_args("--gen-key");
-	generate_keypair();
+      case aKeygen: /* generate a key */
+	if( opt.batch ) {
+	    if( argc > 1 )
+		wrong_args("--gen-key [parameterfile]");
+	    generate_keypair( argc? *argv : NULL );
+	}
+	else {
+	    if( argc )
+		wrong_args("--gen-key");
+	    generate_keypair(NULL);
+	}
 	break;
 
       case aFastImport:
diff --git a/g10/keygen.c b/g10/keygen.c
index 11c840786..2ed5a70e4 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -35,6 +35,37 @@
 #include "status.h"
 #include "i18n.h"
 
+enum para_name {
+  pKEYTYPE,
+  pKEYLENGTH,
+  pSUBKEYTYPE,
+  pSUBKEYLENGTH,
+  pNAMEREAL,
+  pNAMEEMAIL,
+  pNAMECOMMENT,
+  pUSERID,
+  pEXPIREDATE,
+  pKEYEXPIRE, /* in n seconds */
+  pSUBKEYEXPIRE, /* in n seconds */
+  pPASSPHRASE,
+  pPASSPHRASE_DEK,
+  pPASSPHRASE_S2K
+};
+
+struct para_data_s {
+    struct para_data_s *next;
+    int lnr;
+    enum para_name key;
+    union {
+       DEK *dek;
+       STRING2KEY *s2k;
+       char value[1];
+    } u;
+};
+
+
+static void do_generate_keypair( struct para_data_s *para );
+
 
 static void
 write_uid( KBNODE root, const char *s )
@@ -192,8 +223,7 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk )
 
 static int
 gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
-	STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval,
-							int version )
+	STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval )
 {
     int rc;
     int i;
@@ -204,6 +234,17 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
     MPI *factors;
 
     assert( is_ELGAMAL(algo) );
+
+    if( nbits < 512 ) {
+	nbits = 1024;
+	log_info(_("keysize invalid; using %u bits\n"), nbits );
+    }
+
+    if( (nbits % 32) ) {
+	nbits = ((nbits + 31) / 32) * 32;
+	log_info(_("keysize rounded up to %u bits\n"), nbits );
+    }
+
     rc = pubkey_generate( algo, nbits, skey, &factors );
     if( rc ) {
 	log_error("pubkey_generate failed: %s\n", g10_errstr(rc) );
@@ -213,7 +254,7 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
     sk = m_alloc_clear( sizeof *sk );
     pk = m_alloc_clear( sizeof *pk );
     sk->timestamp = pk->timestamp = make_timestamp();
-    sk->version = pk->version = version;
+    sk->version = pk->version = 4;
     if( expireval ) {
 	sk->expiredate = pk->expiredate = sk->timestamp + expireval;
     }
@@ -267,7 +308,7 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
  * Generate a DSA key
  */
 static int
-gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
+gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
 	    STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval )
 {
     int rc;
@@ -278,8 +319,15 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
     MPI skey[5];
     MPI *factors;
 
-    if( nbits > 1024 )
+    if( nbits > 1024 || nbits < 512 ) {
 	nbits = 1024;
+	log_info(_("keysize invalid; using %u bits\n"), nbits );
+    }
+
+    if( (nbits % 64) ) {
+	nbits = ((nbits + 63) / 64) * 64;
+	log_info(_("keysize rounded up to %u bits\n"), nbits );
+    }
 
     rc = pubkey_generate( PUBKEY_ALGO_DSA, nbits, skey, &factors );
     if( rc ) {
@@ -379,7 +427,7 @@ check_valid_days( const char *s )
  * Returns: 0 to create both a DSA and a ElGamal key.
  */
 static int
-ask_algo( int *ret_v4, int addmode )
+ask_algo( int addmode )
 {
     char *answer;
     int algo;
@@ -391,11 +439,7 @@ ask_algo( int *ret_v4, int addmode )
     if( addmode )
 	tty_printf(    _("   (%d) ElGamal (encrypt only)\n"), 3 );
     tty_printf(    _("   (%d) ElGamal (sign and encrypt)\n"), 4 );
-  #if 0
-    tty_printf(    _("   (%d) ElGamal in a v3 packet\n"), 5 );
-  #endif
 
-    *ret_v4 = 1;
     for(;;) {
 	answer = cpr_get("keygen.algo",_("Your selection? "));
 	cpr_kill_prompt();
@@ -420,13 +464,6 @@ ask_algo( int *ret_v4, int addmode )
 	    algo = PUBKEY_ALGO_DSA;
 	    break;
 	}
-      #if 0
-	else if( algo == 5 ) {
-	    algo = PUBKEY_ALGO_ELGAMAL_E;
-	    *ret_v4 = 0;
-	    break;
-	}
-      #endif
 	else
 	    tty_printf(_("Invalid selection.\n"));
     }
@@ -781,12 +818,12 @@ ask_passphrase( STRING2KEY **ret_s2k )
 
 static int
 do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root,
-	   DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate,
-							     int v4_packet )
+	   DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate )
 {
     int rc=0;
 
-    tty_printf(_(
+    if( !opt.batch )
+	tty_printf(_(
 "We need to generate a lot of random bytes. It is a good idea to perform\n"
 "some other action (type on the keyboard, move the mouse, utilize the\n"
 "disks) during the prime generation; this gives the random number\n"
@@ -794,7 +831,7 @@ do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root,
 
     if( algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E )
 	rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k,
-			   sk, expiredate, v4_packet? 4:3 );
+			   sk, expiredate );
     else if( algo == PUBKEY_ALGO_DSA )
 	rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate);
     else
@@ -835,54 +872,409 @@ generate_user_id()
 }
 
 
+static void
+release_parameter_list( struct para_data_s *r )
+{
+    struct para_data_s *r2;
+
+    for( ; r ; r = r2 ) {
+	r2 = r->next;
+	if( r->key == pPASSPHRASE_DEK )
+	    m_free( r->u.dek );
+	else if( r->key == pPASSPHRASE_S2K )
+	    m_free( r->u.s2k );
+
+	m_free(r);
+    }
+}
+
+static struct para_data_s *
+get_parameter( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r;
+
+    for( r = para; r && r->key != key; r = r->next )
+	;
+    return r;
+}
+
+static const char *
+get_parameter_value( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r = get_parameter( para, key );
+    return (r && *r->u.value)? r->u.value : NULL;
+}
+
+static int
+get_parameter_algo( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r = get_parameter( para, key );
+    if( !r )
+	return -1;
+    if( isdigit( *r->u.value ) )
+	return atoi( r->u.value );
+    return string_to_pubkey_algo( r->u.value );
+}
+
+
+static u32
+get_parameter_u32( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r = get_parameter( para, key );
+
+    if( !r )
+	return 0;
+    return (unsigned int)strtoul( r->u.value, NULL, 10 );
+}
+
+static unsigned int
+get_parameter_uint( struct para_data_s *para, enum para_name key )
+{
+    return get_parameter_u32( para, key );
+}
+
+static DEK *
+get_parameter_dek( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r = get_parameter( para, key );
+    return r? r->u.dek : NULL;
+}
+
+static STRING2KEY *
+get_parameter_s2k( struct para_data_s *para, enum para_name key )
+{
+    struct para_data_s *r = get_parameter( para, key );
+    return r? r->u.s2k : NULL;
+}
+
+
+static int
+proc_parameter_file( struct para_data_s *para, const char *fname )
+{
+    struct para_data_s *r;
+    const char *s1, *s2, *s3;
+    size_t n;
+    char *p;
+    int i;
+
+    /* check that we have all required parameters */
+    assert( get_parameter( para, pKEYTYPE ) );
+    i = get_parameter_algo( para, pKEYTYPE );
+    if( i < 1 || check_pubkey_algo2( i, PUBKEY_USAGE_SIG ) ) {
+	r = get_parameter( para, pKEYTYPE );
+	log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+	return -1;
+    }
+
+    i = get_parameter_algo( para, pSUBKEYTYPE );
+    if( i > 1 && check_pubkey_algo( i ) ) {
+	r = get_parameter( para, pSUBKEYTYPE );
+	log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+	return -1;
+    }
+
+    if( !get_parameter_value( para, pUSERID ) ) {
+	/* create the formatted user ID */
+	s1 = get_parameter_value( para, pNAMEREAL );
+	s2 = get_parameter_value( para, pNAMECOMMENT );
+	s3 = get_parameter_value( para, pNAMEEMAIL );
+	if( s1 || s2 || s3 ) {
+	    n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0);
+	    r = m_alloc_clear( sizeof *r + n + 20 );
+	    r->key = pUSERID;
+	    p = r->u.value;
+	    if( s1 )
+		p = stpcpy(p, s1 );
+	    if( s2 )
+		p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")");
+	    if( s3 )
+		p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
+	    r->next = para;
+	    para = r;
+	}
+    }
+
+    r = get_parameter( para, pPASSPHRASE );
+    if( r && *r->u.value ) {
+	/* we have a plain text passphrase - create a DEK from it.
+	 * It is a little bit ridiculous to keep it ih secure memory
+	 * but becuase we do this alwasy, why not here */
+	STRING2KEY *s2k;
+	DEK *dek;
+
+	s2k = m_alloc_secure( sizeof *s2k );
+	s2k->mode = opt.s2k_mode;
+	s2k->hash_algo = opt.s2k_digest_algo;
+	set_next_passphrase( r->u.value );
+	dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2 );
+	set_next_passphrase( NULL );
+	assert( dek );
+	memset( r->u.value, 0, strlen(r->u.value) );
+
+	r = m_alloc_clear( sizeof *r );
+	r->key = pPASSPHRASE_S2K;
+	r->u.s2k = s2k;
+	r->next = para;
+	para = r;
+	r = m_alloc_clear( sizeof *r );
+	r->key = pPASSPHRASE_DEK;
+	r->u.dek = dek;
+	r->next = para;
+	para = r;
+    }
+    do_generate_keypair( para );
+    return 0;
+}
+
+
+/****************
+ * Kludge to allow non interactive key generation controlled
+ * by a parameter file (which currently is only stdin)
+ * Note, that string parameters are expected to be in UTF-8
+ */
+static void
+read_parameter_file( const char *fname )
+{
+    static struct { const char *name;
+		    enum para_name key;
+    } keywords[] = {
+	{ "Key-Type",       pKEYTYPE},
+	{ "Key-Length",     pKEYLENGTH },
+	{ "Subkey-Type",    pSUBKEYTYPE },
+	{ "Subkey-Length",  pSUBKEYLENGTH },
+	{ "Name-Real",      pNAMEREAL },
+	{ "Name-Email",     pNAMEEMAIL },
+	{ "Name-Comment",   pNAMECOMMENT },
+	{ "Expire-Date",    pEXPIREDATE },
+	{ "Passphrase",     pPASSPHRASE },
+	{ NULL, 0 }
+    };
+    FILE *fp;
+    char line[1024], *p;
+    int lnr;
+    const char *err = NULL;
+    struct para_data_s *para, *r;
+    int i;
+
+    if( !fname || !*fname || !strcmp(fname,"-") ) {
+	fp = stdin;
+	fname = "-";
+    }
+    else {
+	fp = fopen( fname, "r" );
+	if( !fp ) {
+	    log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+	    return;
+	}
+    }
+
+    lnr = 0;
+    err = NULL;
+    para = NULL;
+    while( fgets( line, DIM(line)-1, fp ) ) {
+	char *keyword, *value;
+
+	lnr++;
+	if( *line && line[strlen(line)-1] != '\n' ) {
+	    err = "line too long";
+	    break;
+	}
+	for( p = line; isspace(*p); p++ )
+	    ;
+	if( !*p || *p == '#' )
+	    continue;
+	keyword = p;
+	if( *keyword == '%' ) {
+	    /* for now these are all comments but in future they may be used
+	     * to control certain aspects */
+	    continue;
+	}
+
+
+	if( !(p = strchr( p, ':' )) || p == keyword ) {
+	    err = "missing colon";
+	    break;
+	}
+	*p++ = 0;
+	for( ; isspace(*p); p++ )
+	    ;
+	if( !*p ) {
+	    err = "missing argument";
+	    break;
+	}
+	value = p;
+	trim_trailing_ws( value, strlen(value) );
+
+	for(i=0; keywords[i].name; i++ ) {
+	    if( !stricmp( keywords[i].name, keyword ) )
+		break;
+	}
+	if( !keywords[i].name ) {
+	    err = "unknown keyword";
+	    break;
+	}
+	if( keywords[i].key != pKEYTYPE && !para ) {
+	    err = "parameter block does not start with \"Key-Type\"";
+	    break;
+	}
+
+	if( keywords[i].key == pKEYTYPE && para ) {
+	    proc_parameter_file( para, fname );
+	    release_parameter_list( para );
+	    para = NULL;
+	}
+	else {
+	    for( r = para; r; r = r->next ) {
+		if( r->key == keywords[i].key )
+		    break;
+	    }
+	    if( r ) {
+		err = "duplicate keyword";
+		break;
+	    }
+	}
+	r = m_alloc_clear( sizeof *r + strlen( value ) );
+	r->lnr = lnr;
+	r->key = keywords[i].key;
+	strcpy( r->u.value, value );
+	r->next = para;
+	para = r;
+    }
+    if( err )
+	log_error("%s:%d: %s\n", fname, lnr, err );
+    else if( ferror(fp) ) {
+	log_error("%s:%d: read error: %s\n", fname, lnr, strerror(errno) );
+    }
+    else if( para ) {
+	proc_parameter_file( para, fname );
+    }
+    release_parameter_list( para );
+    if( strcmp( fname, "-" ) )
+	fclose(fp);
+}
+
+
 /****************
  * Generate a keypair
+ * (fname is only used in batch mode)
  */
 void
-generate_keypair()
+generate_keypair( const char *fname )
 {
-    unsigned nbits;
-    char *pub_fname = NULL;
-    char *sec_fname = NULL;
+    unsigned int nbits;
     char *uid = NULL;
-    KBNODE pub_root = NULL;
-    KBNODE sec_root = NULL;
-    PKT_secret_key *sk = NULL;
     DEK *dek;
     STRING2KEY *s2k;
-    int rc;
     int algo;
-    u32 expire;
-    int v4;
     int both = 0;
+    u32 expire;
+    struct para_data_s *para = NULL;
+    struct para_data_s *r;
 
-    if( opt.batch || opt.answer_yes || opt.answer_no ) {
-	log_error(_("Key generation can only be used in interactive mode\n"));
+    if( opt.batch ) {
+	read_parameter_file( fname );
 	return;
     }
 
-    algo = ask_algo( &v4, 0 );
-    if( !algo ) {
-	algo = PUBKEY_ALGO_ELGAMAL_E;
+    algo = ask_algo( 0 );
+    if( !algo ) { /* default: DSA with ElG subkey of the specified size */
 	both = 1;
+	r = m_alloc_clear( sizeof *r + 20 );
+	r->key = pKEYTYPE;
+	sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA );
+	r->next = para;
+	para = r;
 	tty_printf(_("DSA keypair will have 1024 bits.\n"));
+	r = m_alloc_clear( sizeof *r + 20 );
+	r->key = pKEYLENGTH;
+	strcpy( r->u.value, "1024" );
+	r->next = para;
+	para = r;
+
+	algo = PUBKEY_ALGO_ELGAMAL_E;
+	r = m_alloc_clear( sizeof *r + 20 );
+	r->key = pSUBKEYTYPE;
+	sprintf( r->u.value, "%d", algo );
+	r->next = para;
+	para = r;
     }
+    else {
+	r = m_alloc_clear( sizeof *r + 20 );
+	r->key = pKEYTYPE;
+	sprintf( r->u.value, "%d", algo );
+	r->next = para;
+	para = r;
+    }
+
     nbits = ask_keysize( algo );
+    r = m_alloc_clear( sizeof *r + 20 );
+    r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+    sprintf( r->u.value, "%u", nbits);
+    r->next = para;
+    para = r;
+
     expire = ask_expire_interval();
+    r = m_alloc_clear( sizeof *r + 20 );
+    r->key = pKEYEXPIRE;
+    sprintf( r->u.value, "%lu", (ulong)expire );
+    r->next = para;
+    para = r;
+    if( both ) {
+	r = m_alloc_clear( sizeof *r + 20 );
+	r->key = pSUBKEYEXPIRE;
+	sprintf( r->u.value, "%lu", (ulong)expire );
+	r->next = para;
+	para = r;
+    }
+
     uid = ask_user_id(0);
     if( !uid ) {
 	log_error(_("Key generation canceled.\n"));
+	release_parameter_list( para );
 	return;
     }
+    r = m_alloc_clear( sizeof *r + strlen(uid) );
+    r->key = pUSERID;
+    strcpy( r->u.value, uid );
+    r->next = para;
+    para = r;
+
     dek = ask_passphrase( &s2k );
+    if( dek ) {
+	r = m_alloc_clear( sizeof *r );
+	r->key = pPASSPHRASE_DEK;
+	r->u.dek = dek;
+	r->next = para;
+	para = r;
+	r = m_alloc_clear( sizeof *r );
+	r->key = pPASSPHRASE_S2K;
+	r->u.s2k = s2k;
+	r->next = para;
+	para = r;
+    }
+
+    proc_parameter_file( para, "[internal]" );
+    release_parameter_list( para );
+}
 
 
-    /* now check whether we are allowed to write to the keyrings */
+static void
+do_generate_keypair( struct para_data_s *para )
+{
+    char *pub_fname = NULL;
+    char *sec_fname = NULL;
+    KBNODE pub_root = NULL;
+    KBNODE sec_root = NULL;
+    PKT_secret_key *sk = NULL;
+    const char *s;
+    int rc;
+
+    /* check whether we are allowed to write to the keyrings */
     pub_fname = make_filename(opt.homedir, "pubring.gpg", NULL );
     sec_fname = make_filename(opt.homedir, "secring.gpg", NULL );
     if( opt.verbose ) {
-	tty_printf(_("writing public certificate to `%s'\n"), pub_fname );
-	tty_printf(_("writing secret certificate to `%s'\n"), sec_fname );
+	log_info(_("writing public certificate to `%s'\n"), pub_fname );
+	log_info(_("writing secret certificate to `%s'\n"), sec_fname );
     }
 
     /* we create the packets as a tree of kbnodes. Because the structure
@@ -893,24 +1285,31 @@ generate_keypair()
     pub_root = make_comment_node("#"); delete_kbnode(pub_root);
     sec_root = make_comment_node("#"); delete_kbnode(sec_root);
 
-    if( both )
-	rc = do_create( PUBKEY_ALGO_DSA, 1024, pub_root, sec_root,
-					       dek, s2k, &sk, expire, 1);
-    else
-	rc = do_create( algo,		nbits, pub_root, sec_root,
-					       dek, s2k, &sk, expire, v4);
-    if( !rc )
-	write_uid(pub_root, uid );
-    if( !rc )
-	write_uid(sec_root, uid );
-    if( !rc )
-	rc = write_selfsig(pub_root, pub_root, sk);
-    if( !rc )
-	rc = write_selfsig(sec_root, pub_root, sk);
+    rc = do_create( get_parameter_algo( para, pKEYTYPE ),
+		    get_parameter_uint( para, pKEYLENGTH ),
+		    pub_root, sec_root,
+		    get_parameter_dek( para, pPASSPHRASE_DEK ),
+		    get_parameter_s2k( para, pPASSPHRASE_S2K ),
+		    &sk,
+		    get_parameter_u32( para, pKEYEXPIRE ) );
+    if( !rc && (s=get_parameter_value(para, pUSERID)) ) {
+	write_uid(pub_root, s );
+	if( !rc )
+	    write_uid(sec_root, s );
+	if( !rc )
+	    rc = write_selfsig(pub_root, pub_root, sk);
+	if( !rc )
+	    rc = write_selfsig(sec_root, pub_root, sk);
+    }
 
-    if( both ) {
-	rc = do_create( algo, nbits, pub_root, sec_root,
-					  dek, s2k, NULL, expire, 1 );
+    if( get_parameter( para, pSUBKEYTYPE ) ) {
+	rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ),
+			get_parameter_uint( para, pSUBKEYLENGTH ),
+			pub_root, sec_root,
+			get_parameter_dek( para, pPASSPHRASE_DEK ),
+			get_parameter_s2k( para, pPASSPHRASE_S2K ),
+			NULL,
+			get_parameter_u32( para, pSUBKEYEXPIRE ) );
 	if( !rc )
 	    rc = write_keybinding(pub_root, pub_root, sk);
 	if( !rc )
@@ -959,12 +1358,17 @@ generate_keypair()
 	else if( (rc=insert_keyblock( &sec_kbpos, sec_root )) )
 	    log_error("can't write secret key: %s\n", g10_errstr(rc) );
 	else {
-	    tty_printf(_("public and secret key created and signed.\n") );
-	    if( algo == PUBKEY_ALGO_DSA )
+	    if( !opt.batch )
+		 tty_printf(_("public and secret key created and signed.\n") );
+	    if( !opt.batch
+		&& get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA
+		&& !get_parameter( para, pSUBKEYTYPE ) )
+	    {
 		tty_printf(_("Note that this key cannot be used for "
 			     "encryption.  You may want to use\n"
 			     "the command \"--edit-key\" to generate a "
 			     "secondary key for this purpose.\n") );
+	    }
 	}
 
 	if( !rc1 )
@@ -974,15 +1378,16 @@ generate_keypair()
     }
 
 
-    if( rc )
-	tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) );
+    if( rc ) {
+	if( opt.batch )
+	    log_error(_("Key generation failed: %s\n"), g10_errstr(rc) );
+	else
+	    tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) );
+    }
     release_kbnode( pub_root );
     release_kbnode( sec_root );
     if( sk ) /* the unprotected  secret key */
 	free_secret_key(sk);
-    m_free(uid);
-    m_free(dek);
-    m_free(s2k);
     m_free(pub_fname);
     m_free(sec_fname);
 }
@@ -998,7 +1403,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
     int okay=0, rc=0;
     KBNODE node;
     PKT_secret_key *sk = NULL; /* this is the primary sk */
-    int v4, algo;
+    int algo;
     u32 expire;
     unsigned nbits;
     char *passphrase = NULL;
@@ -1049,7 +1454,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
 	goto leave;
 
 
-    algo = ask_algo( &v4, 1 );
+    algo = ask_algo( 1 );
     assert(algo);
     nbits = ask_keysize( algo );
     expire = ask_expire_interval();
@@ -1066,7 +1471,7 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
     }
 
     rc = do_create( algo, nbits, pub_keyblock, sec_keyblock,
-				      dek, s2k, NULL, expire, v4 );
+				      dek, s2k, NULL, expire );
     if( !rc )
 	rc = write_keybinding(pub_keyblock, pub_keyblock, sk);
     if( !rc )
diff --git a/g10/main.h b/g10/main.h
index 9a4969415..5a63fe636 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -92,7 +92,7 @@ void keyedit_menu( const char *username, STRLIST locusr, STRLIST cmds,
 
 /*-- keygen.c --*/
 u32 ask_expiredate(void);
-void generate_keypair(void);
+void generate_keypair( const char *fname );
 int keygen_add_key_expire( PKT_signature *sig, void *opaque );
 int keygen_add_std_prefs( PKT_signature *sig, void *opaque );
 int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );