aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keygen.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/keygen.c')
-rw-r--r--g10/keygen.c954
1 files changed, 843 insertions, 111 deletions
diff --git a/g10/keygen.c b/g10/keygen.c
index af288b3aa..b6c6cc7e3 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -1,5 +1,5 @@
/* keygen.c - generate a key pair
- * Copyright (C) 1998, 1999 Free Software Foundation, Inc.
+ * Copyright (C) 1998, 1999, 2000 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -35,6 +35,58 @@
#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;
+ u32 expire;
+ char value[1];
+ } u;
+};
+
+struct output_control_s {
+ int lnr;
+ int dryrun;
+ int use_files;
+ struct {
+ char *fname;
+ char *newfname;
+ IOBUF stream;
+ armor_filter_context_t afx;
+ } pub;
+ struct {
+ char *fname;
+ char *newfname;
+ IOBUF stream;
+ armor_filter_context_t afx;
+ } sec;
+};
+
+
+static void do_generate_keypair( struct para_data_s *para,
+ struct output_control_s *outctrl );
+static int write_keyblock( IOBUF out, KBNODE node );
+
static void
write_uid( KBNODE root, const char *s )
@@ -43,7 +95,7 @@ write_uid( KBNODE root, const char *s )
size_t n = strlen(s);
pkt->pkttype = PKT_USER_ID;
- pkt->pkt.user_id = gcry_xmalloc( sizeof *pkt->pkt.user_id + n - 1 );
+ pkt->pkt.user_id = gcry_xcalloc( 1, sizeof *pkt->pkt.user_id + n - 1 );
pkt->pkt.user_id->len = n;
strcpy(pkt->pkt.user_id->name, s);
add_kbnode( root, new_kbnode( pkt ) );
@@ -84,8 +136,9 @@ keygen_add_std_prefs( PKT_signature *sig, void *opaque )
keygen_add_key_expire( sig, opaque );
buf[0] = GCRY_CIPHER_TWOFISH;
- buf[1] = GCRY_CIPHER_CAST5;
- build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 2 );
+ buf[1] = GCRY_CIPHER_BLOWFISH;
+ buf[2] = GCRY_CIPHER_CAST5;
+ build_sig_subpkt( sig, SIGSUBPKT_PREF_SYM, buf, 3 );
buf[0] = GCRY_MD_RMD160;
buf[1] = GCRY_MD_SHA1;
@@ -189,6 +242,7 @@ write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_key *sk )
}
+
static int
key_from_sexp( GCRY_MPI *array,
GCRY_SEXP sexp, const char *topname, const char *elems )
@@ -252,7 +306,7 @@ factors_from_sexp( MPI **retarray, GCRY_SEXP sexp )
for( n=0; (l2 = gcry_sexp_enum( list, &ctx, 0 )); n++ )
;
- array = gcry_calloc( n, sizeof *array );
+ array = gcry_xcalloc( n, sizeof *array );
if( !array )
return GCRYERR_NO_MEM;
@@ -275,10 +329,10 @@ factors_from_sexp( MPI **retarray, GCRY_SEXP sexp )
}
+
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;
@@ -289,6 +343,18 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
char buf[100];
GCRY_SEXP s_parms, s_key;
+ 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 );
+ }
+
sprintf(buf, "%u", nbits );
s_parms = SEXP_CONS( SEXP_NEW( "genkey", 0 ),
SEXP_CONS( SEXP_NEW(algo == GCRY_PK_ELG_E ? "openpgp-elg" :
@@ -303,10 +369,11 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
return rc;
}
+
sk = gcry_xcalloc( 1, sizeof *sk );
pk = gcry_xcalloc( 1, 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;
}
@@ -361,7 +428,6 @@ gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
for(i=0; factors[i]; i++ ) {
add_kbnode( sec_root,
make_mpi_comment_node("#:ELG_factor:", factors[i] ));
- gcry_mpi_release(factors[i]);
}
return 0;
@@ -372,7 +438,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;
@@ -384,8 +450,15 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
char buf[100];
GCRY_SEXP s_parms, s_key;
- 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 );
+ }
sprintf(buf, "%u", nbits );
s_parms = SEXP_CONS( SEXP_NEW( "genkey", 0 ),
@@ -400,6 +473,7 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
return rc;
}
+
sk = gcry_xcalloc( 1, sizeof *sk );
pk = gcry_xcalloc( 1, sizeof *pk );
sk->timestamp = pk->timestamp = make_timestamp();
@@ -464,10 +538,91 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
make_mpi_comment_node("#:DSA_factor:", factors[i] ));
/* fixme: Merge this with the elg-generate function and release
- * some more stuff */
+ * some more stuff (memory-leak) */
return 0;
}
+#if 0
+static int
+gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
+ STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval )
+{
+ int rc;
+ PACKET *pkt;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+ MPI skey[4];
+ MPI *factors;
+
+ assert( is_RSA(algo) );
+
+ if( nbits < 1024 ) {
+ 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", gpg_errstr(rc) );
+ return rc;
+ }
+
+ sk = gcry_xcalloc( 1, sizeof *sk );
+ pk = gcry_xcalloc( 1, sizeof *pk );
+ sk->timestamp = pk->timestamp = make_timestamp();
+ sk->version = pk->version = 4;
+ if( expireval ) {
+ sk->expiredate = pk->expiredate = sk->timestamp + expireval;
+ }
+ sk->pubkey_algo = pk->pubkey_algo = algo;
+ pk->pkey[0] = mpi_copy( skey[0] );
+ pk->pkey[1] = mpi_copy( skey[1] );
+ sk->skey[0] = skey[0];
+ sk->skey[1] = skey[1];
+ sk->skey[2] = skey[2];
+ sk->skey[3] = skey[3];
+ sk->skey[4] = skey[4];
+ sk->skey[5] = skey[5];
+ sk->is_protected = 0;
+ sk->protect.algo = 0;
+
+ sk->csum = checksum_mpi_counted_nbits( sk->skey[2] );
+ sk->csum += checksum_mpi_counted_nbits( sk->skey[3] );
+ sk->csum += checksum_mpi_counted_nbits( sk->skey[4] );
+ sk->csum += checksum_mpi_counted_nbits( sk->skey[5] );
+ if( ret_sk ) /* not a subkey: return an unprotected version of the sk */
+ *ret_sk = copy_secret_key( NULL, sk );
+
+ if( dek ) {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key( sk, dek );
+ if( rc ) {
+ log_error("protect_secret_key failed: %s\n", gpg_errstr(rc) );
+ free_public_key(pk);
+ free_secret_key(sk);
+ return rc;
+ }
+ }
+
+ pkt = gcry_xcalloc( 1,sizeof *pkt);
+ pkt->pkttype = ret_sk ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ pkt = gcry_xcalloc( 1,sizeof *pkt);
+ pkt->pkttype = ret_sk ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+}
+#endif
/****************
@@ -502,7 +657,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;
@@ -515,10 +670,9 @@ ask_algo( int *ret_v4, int 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 );
+ tty_printf( _(" (%d) RSA (sign and encrypt)\n"), 5 );
#endif
- *ret_v4 = 1;
for(;;) {
answer = cpr_get("keygen.algo",_("Your selection? "));
cpr_kill_prompt();
@@ -528,6 +682,15 @@ ask_algo( int *ret_v4, int addmode )
algo = 0; /* create both keys */
break;
}
+ #if 0
+ else if( algo == 5 ) {
+ if( cpr_get_answer_is_yes("keygen.algo.rsa_se",_(
+ "Do you really want to create a sign and encrypt key? "))) {
+ algo = GCRY_PK_RSA;
+ break;
+ }
+ }
+ #endif
else if( algo == 4 ) {
if( cpr_get_answer_is_yes("keygen.algo.elg_se",_(
"Do you really want to create a sign and encrypt key? "))) {
@@ -543,13 +706,6 @@ ask_algo( int *ret_v4, int addmode )
algo = GCRY_PK_DSA;
break;
}
- #if 0
- else if( algo == 5 ) {
- algo = GCRY_PK_ELG_E;
- *ret_v4 = 0;
- break;
- }
- #endif
else
tty_printf(_("Invalid selection.\n"));
}
@@ -578,6 +734,9 @@ ask_keysize( int algo )
tty_printf(_("DSA only allows keysizes from 512 to 1024\n"));
else if( nbits < 768 )
tty_printf(_("keysize too small; 768 is smallest value allowed.\n"));
+ else if( algo == GCRY_PK_RSA && nbits < 1024 )
+ tty_printf(_("keysize too small;"
+ " 1024 is smallest value allowed for RSA.\n"));
else if( nbits > 4096 ) {
/* It is ridiculous and an annoyance to use larger key sizes!
* GnuPG can handle much larger sizes; but it takes an eternity
@@ -602,7 +761,7 @@ ask_keysize( int algo )
break;
}
}
- else if( nbits > 1536 && !cpr_enabled() ) {
+ else if( nbits > 1536 && !cpr_enabled() && algo != GCRY_PK_RSA ) {
if( cpr_get_answer_is_yes("keygen.size.large.okay",_(
"Do you really need such a large keysize? ")) )
break;
@@ -623,6 +782,41 @@ ask_keysize( int algo )
}
+/****************
+ * Parse an expire string and return it's value in days.
+ * Returns -1 on error.
+ */
+static int
+parse_expire_string( const char *string )
+{
+ int mult;
+ u32 abs_date=0;
+ u32 curtime = make_timestamp();
+ int valid_days;
+
+ if( !*string )
+ valid_days = 0;
+ else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime ) {
+ /* This calculation is not perfectly okay because we
+ * are later going to simply multiply by 86400 and don't
+ * correct for leapseconds. A solution would be to change
+ * the whole implemenation to work with dates and not intervals
+ * which are required for v3 keys.
+ */
+ valid_days = abs_date/86400-curtime/86400+1;
+ }
+ else if( (mult=check_valid_days(string)) ) {
+ valid_days = atoi(string) * mult;
+ if( valid_days < 0 || valid_days > 39447 )
+ valid_days = 0;
+ }
+ else {
+ valid_days = -1;
+ }
+ return valid_days;
+}
+
+
static u32
ask_expire_interval(void)
{
@@ -642,32 +836,14 @@ ask_expire_interval(void)
answer = NULL;
for(;;) {
- int mult;
- u32 abs_date=0;
- u32 curtime=0;;
+ u32 curtime=make_timestamp();
gcry_free(answer);
answer = cpr_get("keygen.valid",_("Key is valid for? (0) "));
cpr_kill_prompt();
trim_spaces(answer);
- curtime = make_timestamp();
- if( !*answer )
- valid_days = 0;
- else if( (abs_date = scan_isodatestr(answer)) && abs_date > curtime ) {
- /* This calculation is not perfectly okay because we
- * are later going to simply multiply by 86400 and don't
- * correct for leapseconds. A solution would be to change
- * the whole implemenation to work with dates and not intervals
- * which are required for v3 keys.
- */
- valid_days = abs_date/86400-curtime/86400+1;
- }
- else if( (mult=check_valid_days(answer)) ) {
- valid_days = atoi(answer) * mult;
- if( valid_days < 0 || valid_days > 32767 )
- valid_days = 0;
- }
- else {
+ valid_days = parse_expire_string( answer );
+ if( valid_days < 0 ) {
tty_printf(_("invalid value\n"));
continue;
}
@@ -680,7 +856,10 @@ ask_expire_interval(void)
interval = valid_days * 86400L;
/* print the date when the key expires */
tty_printf(_("Key expires at %s\n"),
- asctimestamp(curtime + interval ) );
+ asctimestamp((ulong)(curtime + interval) ) );
+ if( (time_t)((ulong)(curtime+interval)) < 0 )
+ tty_printf(_("Your system can't display dates beyond 2038.\n"
+ "However, it will be correctly handled up to 2106.\n"));
}
if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay",
@@ -734,6 +913,7 @@ ask_user_id( int mode )
uid = aname = acomment = amail = NULL;
for(;;) {
char *p;
+ int fail=0;
if( !aname ) {
for(;;) {
@@ -785,6 +965,7 @@ ask_user_id( int mode )
}
}
+
gcry_free(uid);
uid = p = gcry_xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10);
p = stpcpy(p, aname );
@@ -812,8 +993,14 @@ ask_user_id( int mode )
tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid);
/* fixme: add a warning if this user-id already exists */
+ if( !*amail && (strchr( aname, '@' ) || strchr( acomment, '@'))) {
+ fail = 1;
+ tty_printf(_("Please don't put the email address "
+ "into the real name or the comment\n") );
+ }
+
for(;;) {
- const char *ansstr = _("NnCcEeOoQq");
+ char *ansstr = _("NnCcEeOoQq");
if( strlen(ansstr) != 10 )
BUG();
@@ -822,8 +1009,9 @@ ask_user_id( int mode )
answer[1] = 0;
}
else {
- answer = cpr_get("keygen.userid.cmd",_(
- "Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "));
+ answer = cpr_get("keygen.userid.cmd", fail?
+ _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") :
+ _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "));
cpr_kill_prompt();
}
if( strlen(answer) > 1 )
@@ -841,10 +1029,15 @@ ask_user_id( int mode )
break;
}
else if( *answer == ansstr[6] || *answer == ansstr[7] ) {
- gcry_free(aname); aname = NULL;
- gcry_free(acomment); acomment = NULL;
- gcry_free(amail); amail = NULL;
- break;
+ if( fail ) {
+ tty_printf(_("Please correct the error first\n"));
+ }
+ else {
+ gcry_free(aname); aname = NULL;
+ gcry_free(acomment); acomment = NULL;
+ gcry_free(amail); amail = NULL;
+ break;
+ }
}
else if( *answer == ansstr[8] || *answer == ansstr[9] ) {
gcry_free(aname); aname = NULL;
@@ -904,22 +1097,25 @@ 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"
"generator a better chance to gain enough entropy.\n") );
if( algo == GCRY_PK_ELG || algo == GCRY_PK_ELG_E )
- rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k,
- sk, expiredate, v4_packet? 4:3 );
+ rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate);
else if( algo == GCRY_PK_DSA )
rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate);
+ #if 0
+ else if( algo == GCRY_PK_RSA )
+ rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate);
+ #endif
else
BUG();
@@ -951,61 +1147,553 @@ generate_user_id()
if( !p )
return NULL;
n = strlen(p);
- uid = gcry_xmalloc( sizeof *uid + n - 1 );
+ uid = gcry_xcalloc( 1, sizeof *uid + n - 1 );
uid->len = n;
strcpy(uid->name, p);
return uid;
}
+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 )
+ gcry_free( r->u.dek );
+ else if( r->key == pPASSPHRASE_S2K )
+ gcry_free( r->u.s2k );
+
+ gcry_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 gcry_pk_map_name( 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;
+ if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE )
+ return r->u.expire;
+
+ 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 output_control_s *outctrl )
+{
+ 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 || openpgp_pk_test_algo( i, GCRY_PK_USAGE_SIGN ) ) {
+ 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 && openpgp_pk_test_algo( i, 0 ) ) {
+ 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 = gcry_xcalloc( 1, 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;
+ }
+ }
+
+ /* make DEK and S2K from the Passphrase */
+ 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 = gcry_xmalloc_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 = gcry_xcalloc( 1, sizeof *r );
+ r->key = pPASSPHRASE_S2K;
+ r->u.s2k = s2k;
+ r->next = para;
+ para = r;
+ r = gcry_xcalloc( 1, sizeof *r );
+ r->key = pPASSPHRASE_DEK;
+ r->u.dek = dek;
+ r->next = para;
+ para = r;
+ }
+
+ /* make KEYEXPIRE from Expire-Date */
+ r = get_parameter( para, pEXPIREDATE );
+ if( r && *r->u.value ) {
+ i = parse_expire_string( r->u.value );
+ if( i < 0 ) {
+ log_error("%s:%d: invalid expire date\n", fname, r->lnr );
+ return -1;
+ }
+ r->u.expire = i * 86400L;
+ r->key = pKEYEXPIRE; /* change hat entry */
+ /* also set it for the subkey */
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pSUBKEYEXPIRE;
+ r->u.expire = i * 86400L;
+ r->next = para;
+ para = r;
+ }
+
+ if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) {
+ log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr );
+ return -1;
+ }
+
+ do_generate_keypair( para, outctrl );
+ 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;
+ struct output_control_s outctrl;
+
+ memset( &outctrl, 0, sizeof( outctrl ) );
+
+ 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( ; !isspace(*p); p++ )
+ ;
+ if( *p )
+ *p++ = 0;
+ for( ; isspace(*p); p++ )
+ ;
+ value = p;
+ trim_trailing_ws( value, strlen(value) );
+ if( !stricmp( keyword, "%echo" ) )
+ log_info("%s\n", value );
+ else if( !stricmp( keyword, "%dry-run" ) )
+ outctrl.dryrun = 1;
+ else if( !stricmp( keyword, "%commit" ) ) {
+ outctrl.lnr = lnr;
+ proc_parameter_file( para, fname, &outctrl );
+ release_parameter_list( para );
+ para = NULL;
+ }
+ else if( !stricmp( keyword, "%pubring" ) ) {
+ if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) )
+ ; /* still the same file - ignore it */
+ else {
+ gcry_free( outctrl.pub.newfname );
+ outctrl.pub.newfname = gcry_xstrdup( value );
+ outctrl.use_files = 1;
+ }
+ }
+ else if( !stricmp( keyword, "%secring" ) ) {
+ if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) )
+ ; /* still the same file - ignore it */
+ else {
+ gcry_free( outctrl.sec.newfname );
+ outctrl.sec.newfname = gcry_xstrdup( value );
+ outctrl.use_files = 1;
+ }
+ }
+ else
+ log_info("skipping control `%s' (%s)\n", keyword, value );
+
+
+ continue;
+ }
+
+
+ if( !(p = strchr( p, ':' )) || p == keyword ) {
+ err = "missing colon";
+ break;
+ }
+ if( *p )
+ *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 ) {
+ outctrl.lnr = lnr;
+ proc_parameter_file( para, fname, &outctrl );
+ 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 = gcry_xcalloc( 1, 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 ) {
+ outctrl.lnr = lnr;
+ proc_parameter_file( para, fname, &outctrl );
+ }
+
+ if( outctrl.use_files ) { /* close open streams */
+ iobuf_close( outctrl.pub.stream );
+ iobuf_close( outctrl.sec.stream );
+ gcry_free( outctrl.pub.fname );
+ gcry_free( outctrl.pub.newfname );
+ gcry_free( outctrl.sec.fname );
+ gcry_free( outctrl.sec.newfname );
+ }
+
+ 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;
+ struct output_control_s outctrl;
+
+ memset( &outctrl, 0, sizeof( outctrl ) );
- 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 = GCRY_PK_ELG_E;
+ algo = ask_algo( 0 );
+ if( !algo ) { /* default: DSA with ElG subkey of the specified size */
both = 1;
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pKEYTYPE;
+ sprintf( r->u.value, "%d", GCRY_PK_DSA );
+ r->next = para;
+ para = r;
tty_printf(_("DSA keypair will have 1024 bits.\n"));
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pKEYLENGTH;
+ strcpy( r->u.value, "1024" );
+ r->next = para;
+ para = r;
+
+ algo = GCRY_PK_ELG_E;
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pSUBKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+ }
+ else {
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
}
+
nbits = ask_keysize( algo );
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+ sprintf( r->u.value, "%u", nbits);
+ r->next = para;
+ para = r;
+
expire = ask_expire_interval();
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pKEYEXPIRE;
+ r->u.expire = expire;
+ r->next = para;
+ para = r;
+ r = gcry_xcalloc( 1, sizeof *r + 20 );
+ r->key = pSUBKEYEXPIRE;
+ r->u.expire = 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 = gcry_xcalloc( 1, sizeof *r + strlen(uid) );
+ r->key = pUSERID;
+ strcpy( r->u.value, uid );
+ r->next = para;
+ para = r;
+
dek = ask_passphrase( &s2k );
+ if( dek ) {
+ r = gcry_xcalloc( 1, sizeof *r );
+ r->key = pPASSPHRASE_DEK;
+ r->u.dek = dek;
+ r->next = para;
+ para = r;
+ r = gcry_xcalloc( 1, sizeof *r );
+ r->key = pPASSPHRASE_S2K;
+ r->u.s2k = s2k;
+ r->next = para;
+ para = r;
+ }
+
+ proc_parameter_file( para, "[internal]", &outctrl );
+ release_parameter_list( para );
+}
- /* now 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 );
+static void
+do_generate_keypair( struct para_data_s *para,
+ struct output_control_s *outctrl )
+{
+ 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;
+
+ if( outctrl->dryrun ) {
+ log_info("dry-run mode - key generation skipped\n");
+ return;
+ }
+
+
+ if( outctrl->use_files ) {
+ if( outctrl->pub.newfname ) {
+ iobuf_close(outctrl->pub.stream);
+ outctrl->pub.stream = NULL;
+ gcry_free( outctrl->pub.fname );
+ outctrl->pub.fname = outctrl->pub.newfname;
+ outctrl->pub.newfname = NULL;
+
+ outctrl->pub.stream = iobuf_create( outctrl->pub.fname );
+ if( !outctrl->pub.stream ) {
+ log_error("can't create `%s': %s\n", outctrl->pub.newfname,
+ strerror(errno) );
+ return;
+ }
+ if( opt.armor ) {
+ outctrl->pub.afx.what = 1;
+ iobuf_push_filter( outctrl->pub.stream, armor_filter,
+ &outctrl->pub.afx );
+ }
+ }
+ if( outctrl->sec.newfname ) {
+ iobuf_close(outctrl->sec.stream);
+ outctrl->sec.stream = NULL;
+ gcry_free( outctrl->sec.fname );
+ outctrl->sec.fname = outctrl->sec.newfname;
+ outctrl->sec.newfname = NULL;
+
+ outctrl->sec.stream = iobuf_create( outctrl->sec.fname );
+ if( !outctrl->sec.stream ) {
+ log_error("can't create `%s': %s\n", outctrl->sec.newfname,
+ strerror(errno) );
+ return;
+ }
+ if( opt.armor ) {
+ outctrl->sec.afx.what = 5;
+ iobuf_push_filter( outctrl->sec.stream, armor_filter,
+ &outctrl->sec.afx );
+ }
+ }
+ pub_fname = outctrl->pub.fname; /* only for info output */
+ sec_fname = outctrl->sec.fname; /* only for info output */
+ assert( outctrl->pub.stream );
+ assert( outctrl->sec.stream );
+ }
+ else {
+ pub_fname = get_writable_keyblock_file( 0 );
+ sec_fname = get_writable_keyblock_file( 1 );
+ }
+
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 key to `%s'\n"), pub_fname );
+ log_info(_("writing secret key to `%s'\n"), sec_fname );
}
/* we create the packets as a tree of kbnodes. Because the structure
@@ -1016,24 +1704,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( GCRY_PK_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 )
@@ -1041,7 +1736,18 @@ generate_keypair()
}
- if( !rc ) {
+ if( !rc && outctrl->use_files ) { /* direct write to specified files */
+ rc = write_keyblock( outctrl->pub.stream, pub_root );
+ if( rc )
+ log_error("can't write public key: %s\n", gpg_errstr(rc) );
+ if( !rc ) {
+ rc = write_keyblock( outctrl->sec.stream, sec_root );
+ if( rc )
+ log_error("can't write secret key: %s\n", gpg_errstr(rc) );
+ }
+
+ }
+ else if( !rc ) { /* write to the standard keyrings */
KBPOS pub_kbpos;
KBPOS sec_kbpos;
int rc1 = -1;
@@ -1082,12 +1788,17 @@ generate_keypair()
else if( (rc=insert_keyblock( &sec_kbpos, sec_root )) )
log_error("can't write secret key: %s\n", gpg_errstr(rc) );
else {
- tty_printf(_("public and secret key created and signed.\n") );
- if( algo == GCRY_PK_DSA )
+ if( !opt.batch )
+ tty_printf(_("public and secret key created and signed.\n") );
+ if( !opt.batch
+ && get_parameter_algo( para, pKEYTYPE ) == GCRY_PK_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 )
@@ -1096,18 +1807,20 @@ generate_keypair()
unlock_keyblock( &sec_kbpos );
}
-
- if( rc )
- tty_printf(_("Key generation failed: %s\n"), gpg_errstr(rc) );
+ if( rc ) {
+ if( opt.batch )
+ log_error("key generation failed: %s\n", gpg_errstr(rc) );
+ else
+ tty_printf(_("Key generation failed: %s\n"), gpg_errstr(rc) );
+ }
release_kbnode( pub_root );
release_kbnode( sec_root );
if( sk ) /* the unprotected secret key */
free_secret_key(sk);
- gcry_free(uid);
- gcry_free(dek);
- gcry_free(s2k);
- gcry_free(pub_fname);
- gcry_free(sec_fname);
+ if( !outctrl->use_files ) {
+ gcry_free(pub_fname);
+ gcry_free(sec_fname);
+ }
}
@@ -1121,7 +1834,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;
@@ -1146,8 +1859,10 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
"in future (time warp or clock problem)\n")
: _("key has been created %lu seconds "
"in future (time warp or clock problem)\n"), d );
- rc = GPGERR_TIME_CONFLICT;
- goto leave;
+ if( !opt.ignore_time_conflict ) {
+ rc = GPGERR_TIME_CONFLICT;
+ goto leave;
+ }
}
@@ -1170,7 +1885,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();
@@ -1187,7 +1902,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 )
@@ -1207,3 +1922,20 @@ generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
return okay;
}
+/****************
+ * Write a keyblock to an output stream
+ */
+static int
+write_keyblock( IOBUF out, KBNODE node )
+{
+ for( ; node ; node = node->next ) {
+ int rc = build_packet( out, node->pkt );
+ if( rc ) {
+ log_error("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, gpg_errstr(rc) );
+ return GPGERR_WRITE_FILE;
+ }
+ }
+ return 0;
+}
+