aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keygen.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--g10/keygen.c387
1 files changed, 317 insertions, 70 deletions
diff --git a/g10/keygen.c b/g10/keygen.c
index b6894ab79..367c22de5 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -79,7 +79,47 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_cert *skc )
pkc = node->pkt->pkt.public_cert;
/* and make the signature */
- rc = make_keysig_packet( &sig, pkc, uid, skc, 0x13, 0 );
+ rc = make_keysig_packet( &sig, pkc, uid, NULL, skc, 0x13, 0 );
+ if( rc ) {
+ log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ pkt = m_alloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ add_kbnode( root, new_kbnode( pkt ) );
+ return rc;
+}
+
+static int
+write_keybinding( KBNODE root, KBNODE pub_root, PKT_secret_cert *skc )
+{
+ PACKET *pkt;
+ PKT_signature *sig;
+ int rc=0;
+ KBNODE node;
+ PKT_public_cert *pkc, *subpkc;
+
+ if( opt.verbose )
+ log_info(_("writing key binding signature\n"));
+
+ /* get the pkc packet from the pub_tree */
+ node = find_kbnode( pub_root, PKT_PUBLIC_CERT );
+ if( !node )
+ BUG();
+ pkc = node->pkt->pkt.public_cert;
+ /* find the last subkey */
+ subpkc = NULL;
+ for(node=pub_root; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBKEY_SUBCERT )
+ subpkc = node->pkt->pkt.public_cert;
+ }
+ if( !subpkc )
+ BUG();
+
+ /* and make the signature */
+ rc = make_keysig_packet( &sig, pkc, NULL, subpkc, skc, 0x18, 0 );
if( rc ) {
log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
return rc;
@@ -95,7 +135,8 @@ write_selfsig( KBNODE root, KBNODE pub_root, PKT_secret_cert *skc )
static int
gen_elg(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
- STRING2KEY *s2k, PKT_secret_cert **ret_skc, u16 valid_days )
+ STRING2KEY *s2k, PKT_secret_cert **ret_skc, u16 valid_days,
+ int version )
{
int rc;
int i;
@@ -111,6 +152,7 @@ gen_elg(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
skc = m_alloc_clear( sizeof *skc );
pkc = m_alloc_clear( sizeof *pkc );
skc->timestamp = pkc->timestamp = make_timestamp();
+ skc->version = pkc->version = version;
skc->valid_days = pkc->valid_days = valid_days;
skc->pubkey_algo = pkc->pubkey_algo = PUBKEY_ALGO_ELGAMAL;
pkc->d.elg.p = pk.p;
@@ -124,8 +166,8 @@ gen_elg(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
skc->protect.algo = 0;
skc->csum = checksum_mpi( skc->d.elg.x );
- /* return an unprotected version of the skc */
- *ret_skc = copy_secret_cert( NULL, skc );
+ if( ret_skc ) /* not a subkey: return an unprotected version of the skc */
+ *ret_skc = copy_secret_cert( NULL, skc );
if( dek ) {
skc->protect.algo = dek->algo;
@@ -140,14 +182,14 @@ gen_elg(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
}
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_PUBLIC_CERT;
+ pkt->pkttype = ret_skc ? PKT_PUBLIC_CERT : PKT_PUBKEY_SUBCERT;
pkt->pkt.public_cert = pkc;
add_kbnode(pub_root, new_kbnode( pkt ));
/* don't know whether it makes sense to have the factors, so for now
* we store them in the secret keyring (but they are not secret) */
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_SECRET_CERT;
+ pkt->pkttype = ret_skc ? PKT_SECRET_CERT : PKT_SECKEY_SUBCERT;
pkt->pkt.secret_cert = skc;
add_kbnode(sec_root, new_kbnode( pkt ));
for(i=0; factors[i]; i++ )
@@ -192,8 +234,8 @@ gen_rsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
skc->d.rsa.csum += checksum_mpi( skc->d.rsa.rsa_q );
skc->d.rsa.csum += checksum_mpi( skc->d.rsa.rsa_u );
- /* return an unprotected version of the skc */
- *ret_skc = copy_secret_cert( NULL, skc );
+ if( ret_skc ) /* not a subkey: return an unprotected version of the skc */
+ *ret_skc = copy_secret_cert( NULL, skc );
if( dek ) {
skc->d.rsa.is_protected = 1;
@@ -210,12 +252,12 @@ gen_rsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
}
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_PUBLIC_CERT;
+ pkt->pkttype = ret_skc ? PKT_PUBLIC_CERT : PKT_PUBKEY_SUBCERT;
pkt->pkt.public_cert = pkc;
add_kbnode(pub_root, new_kbnode( pkt ));
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_SECRET_CERT;
+ pkt->pkttype = ret_skc ? PKT_SECRET_CERT : PKT_SECKEY_SUBCERT;
pkt->pkt.secret_cert = skc;
add_kbnode(sec_root, new_kbnode( pkt ));
@@ -267,8 +309,8 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
skc->protect.algo = 0;
skc->csum = checksum_mpi( skc->d.dsa.x );
- /* return an unprotected version of the skc */
- *ret_skc = copy_secret_cert( NULL, skc );
+ if( ret_skc ) /* not a subkey: return an unprotected version of the skc */
+ *ret_skc = copy_secret_cert( NULL, skc );
if( dek ) {
skc->protect.algo = dek->algo;
@@ -283,7 +325,7 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
}
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_PUBLIC_CERT;
+ pkt->pkttype = ret_skc ? PKT_PUBLIC_CERT : PKT_PUBKEY_SUBCERT;
pkt->pkt.public_cert = pkc;
add_kbnode(pub_root, new_kbnode( pkt ));
@@ -294,7 +336,7 @@ gen_dsa(unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
* are known.
*/
pkt = m_alloc_clear(sizeof *pkt);
- pkt->pkttype = PKT_SECRET_CERT;
+ pkt->pkttype = ret_skc ? PKT_SECRET_CERT : PKT_SECKEY_SUBCERT;
pkt->pkt.secret_cert = skc;
add_kbnode(sec_root, new_kbnode( pkt ));
for(i=1; factors[i]; i++ ) /* the first one is q */
@@ -334,74 +376,63 @@ check_valid_days( const char *s )
}
-/****************
- * Generate a keypair
- */
-void
-generate_keypair()
+static int
+ask_algo( int *ret_v4 )
{
char *answer;
- unsigned nbits;
- char *pub_fname = NULL;
- char *sec_fname = NULL;
- char *uid = NULL;
- KBNODE pub_root = NULL;
- KBNODE sec_root = NULL;
- PKT_secret_cert *skc = NULL;
- DEK *dek = NULL;
- STRING2KEY *s2k;
- int rc;
int algo;
- const char *algo_name;
- char *aname, *acomment, *amail;
- int valid_days=0;
-
- if( opt.batch || opt.answer_yes || opt.answer_no ) {
- log_error(_("Key generation can only be used in interactive mode\n"));
- return;
- }
tty_printf(_("Please select the algorithm to use:\n"
" (1) ElGamal is the suggested one.\n"
- " (2) DSA can only be used for signatures.\n"));
+ " (2) ElGamal using v4 packets (OpenPGP)\n"
+ " (3) DSA can only be used for signatures.\n"));
#ifdef ENABLE_RSA_KEYGEN
- tty_printf(_(" (3) RSA cannot be used in the U.S.\n"));
+ tty_printf(_(" (4) RSA cannot be used in the U.S.\n"));
#endif
+ *ret_v4 = 0;
for(;;) {
#ifdef ENABLE_RSA_KEYGEN
- answer = tty_get(_("Your selection? (1,2,3) "));
+ answer = tty_get(_("Your selection? (1,2,3,4) "));
#else
- answer = tty_get(_("Your selection? (1,2) "));
+ answer = tty_get(_("Your selection? (1,2,3) "));
#endif
tty_kill_prompt();
algo = *answer? atoi(answer): 1;
m_free(answer);
- if( algo == 1 ) {
+ if( algo == 1 || algo == 2 ) {
+ if( algo == 2 )
+ *ret_v4 = 1;
algo = PUBKEY_ALGO_ELGAMAL;
- algo_name = "ElGamal";
break;
}
- else if( algo == 2 ) {
+ else if( algo == 3 ) {
+ *ret_v4 = 1;
algo = PUBKEY_ALGO_DSA;
- algo_name = "DSA";
break;
}
#ifdef ENABLE_RSA_KEYGEN
- else if( algo == 3 ) {
+ else if( algo == 4 ) {
algo = PUBKEY_ALGO_RSA;
- algo_name = "RSA";
break;
}
#endif
}
+ return algo;
+}
+static unsigned
+ask_keysize( int algo )
+{
+ char *answer;
+ unsigned nbits;
tty_printf(_("About to generate a new %s keypair.\n"
" minimum keysize is 768 bits\n"
" default keysize is 1024 bits\n"
- " highest suggested keysize is 2048 bits\n"), algo_name );
+ " highest suggested keysize is 2048 bits\n"),
+ pubkey_algo_to_string(algo) );
for(;;) {
answer = tty_get(_("What keysize do you want? (1024) "));
tty_kill_prompt();
@@ -446,6 +477,15 @@ generate_keypair()
nbits = ((nbits + 31) / 32) * 32;
tty_printf(_("rounded up to %u bits\n"), nbits );
}
+ return nbits;
+}
+
+
+static int
+ask_valid_days()
+{
+ char *answer;
+ int valid_days=0;
tty_printf(_("Please specify how long the key should be valid.\n"
" 0 = key does not expire\n"
@@ -491,15 +531,21 @@ generate_keypair()
break;
}
m_free(answer);
+ return valid_days;
+}
+static char *
+ask_user_id()
+{
+ char *answer;
+ char *aname, *acomment, *amail, *uid;
tty_printf( _("\n"
"You need a User-ID to identify your key; the software constructs the user id\n"
"from Real Name, Comment and Email Address in this form:\n"
" \"Heinrich Heine (Der Dichter) <[email protected]>\"\n\n") );
- uid = NULL;
- aname=acomment=amail=NULL;
+ uid = aname = acomment = amail = NULL;
for(;;) {
char *p;
@@ -600,7 +646,15 @@ generate_keypair()
break;
m_free(uid); uid = NULL;
}
+ return uid;
+}
+
+static DEK *
+ask_passphrase( STRING2KEY **ret_s2k )
+{
+ DEK *dek = NULL;
+ STRING2KEY *s2k;
tty_printf(_("You need a Passphrase to protect your secret key.\n\n") );
@@ -624,23 +678,17 @@ generate_keypair()
else
break; /* okay */
}
+ *ret_s2k = s2k;
+ return dek;
+}
- /* 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 );
- if( opt.verbose ) {
- tty_printf(_("writing public certificate to '%s'\n"), pub_fname );
- tty_printf(_("writing secret certificate to '%s'\n"), sec_fname );
- }
-
- /* we create the packets as a tree of kbnodes. Because the structure
- * we create is known in advance we simply generate a linked list
- * The first packet is a dummy comment packet which we flag
- * as deleted. The very first packet must always be a CERT packet.
- */
- pub_root = make_comment_node("#"); delete_kbnode(pub_root);
- sec_root = make_comment_node("#"); delete_kbnode(sec_root);
+static int
+do_create( int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root,
+ DEK *dek, STRING2KEY *s2k, PKT_secret_cert **skc, int valid_days,
+ int v4_packet )
+{
+ int rc=0;
tty_printf(_(
"We need to generate a lot of random bytes. It is a good idea to perform\n"
@@ -649,13 +697,14 @@ generate_keypair()
"number generator a better chance to gain enough entropy.\n") );
if( algo == PUBKEY_ALGO_ELGAMAL )
- rc = gen_elg(nbits, pub_root, sec_root, dek, s2k, &skc, valid_days );
+ rc = gen_elg(nbits, pub_root, sec_root, dek, s2k,
+ skc, valid_days, v4_packet? 4:3 );
#ifdef ENABLE_RSA_KEYGEN
else if( algo == PUBKEY_ALGO_RSA )
- rc = gen_rsa(nbits, pub_root, sec_root, dek, s2k, &skc, valid_days );
+ rc = gen_rsa(nbits, pub_root, sec_root, dek, s2k, skc, valid_days );
#endif
else if( algo == PUBKEY_ALGO_DSA )
- rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, &skc, valid_days);
+ rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, skc, valid_days);
else
BUG();
if( !rc ) {
@@ -666,6 +715,59 @@ generate_keypair()
make_comment_node("#created by GNUPG v" VERSION " ("
PRINTABLE_OS_NAME ")"));
}
+ return rc;
+}
+
+
+/****************
+ * Generate a keypair
+ */
+void
+generate_keypair()
+{
+ unsigned nbits;
+ char *pub_fname = NULL;
+ char *sec_fname = NULL;
+ char *uid = NULL;
+ KBNODE pub_root = NULL;
+ KBNODE sec_root = NULL;
+ PKT_secret_cert *skc = NULL;
+ DEK *dek;
+ STRING2KEY *s2k;
+ int rc;
+ int algo;
+ int ndays;
+ int v4;
+
+ if( opt.batch || opt.answer_yes || opt.answer_no ) {
+ log_error(_("Key generation can only be used in interactive mode\n"));
+ return;
+ }
+
+ algo = ask_algo( &v4 );
+ nbits = ask_keysize( algo );
+ ndays = ask_valid_days();
+ uid = ask_user_id();
+ dek = ask_passphrase( &s2k );
+
+
+ /* 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 );
+ if( opt.verbose ) {
+ tty_printf(_("writing public certificate to '%s'\n"), pub_fname );
+ tty_printf(_("writing secret certificate to '%s'\n"), sec_fname );
+ }
+
+ /* we create the packets as a tree of kbnodes. Because the structure
+ * we create is known in advance we simply generate a linked list
+ * The first packet is a dummy comment packet which we flag
+ * as deleted. The very first packet must always be a CERT packet.
+ */
+ pub_root = make_comment_node("#"); delete_kbnode(pub_root);
+ sec_root = make_comment_node("#"); delete_kbnode(sec_root);
+
+ rc = do_create( algo, nbits, pub_root, sec_root, dek, s2k, &skc, ndays, v4);
if( !rc )
write_uid(pub_root, uid );
if( !rc )
@@ -718,6 +820,11 @@ generate_keypair()
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 )
+ tty_printf(_("Note that this key cannot be used for "
+ "encryption. You may want to use\n"
+ "the command \"--add-key\" to generate a "
+ "secondary key for this purpose.\n") );
}
if( !rc1 )
@@ -745,8 +852,148 @@ generate_keypair()
* add a new subkey to an existing key.
*/
void
-generate_subkeypair( const char *userid )
+generate_subkeypair( const char *username )
{
- log_fatal("To be implemented :-)\n");
+ int rc=0;
+ KBPOS pub_kbpos, sec_kbpos;
+ KBNODE pub_keyblock = NULL;
+ KBNODE sec_keyblock = NULL;
+ KBNODE node;
+ PKT_secret_cert *skc = NULL; /* this is the primary skc */
+ u32 keyid[2];
+ int v4, algo, ndays;
+ unsigned nbits;
+ char *passphrase = NULL;
+ DEK *dek = NULL;
+ STRING2KEY *s2k = NULL;
+
+ if( opt.batch || opt.answer_yes || opt.answer_no ) {
+ log_error(_("Key generation can only be used in interactive mode\n"));
+ return;
+ }
+
+ /* search the userid */
+ rc = find_secret_keyblock_byname( &sec_kbpos, username );
+ if( rc ) {
+ log_error("user '%s' not found\n", username );
+ goto leave;
+ }
+ rc = read_keyblock( &sec_kbpos, &sec_keyblock );
+ if( rc ) {
+ log_error("error reading the secret key: %s\n", g10_errstr(rc) );
+ goto leave;
+ }
+ /* and the public key */
+ rc = find_keyblock_byname( &pub_kbpos, username );
+ if( rc ) {
+ log_error("user '%s' not found in public ring\n", username );
+ goto leave;
+ }
+ rc = read_keyblock( &pub_kbpos, &pub_keyblock );
+ if( rc ) {
+ log_error("error reading the public key: %s\n", g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* break out the primary key */
+ node = find_kbnode( sec_keyblock, PKT_SECRET_CERT );
+ if( !node ) {
+ log_error("Oops; secret key not found anymore!\n");
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ /* make a copy of the skc to keep the protected one in the keyblock */
+ skc = copy_secret_cert( NULL, node->pkt->pkt.secret_cert );
+ keyid_from_skc( skc, keyid );
+ /* display primary and all secondary keys */
+ tty_printf("sec %4u%c/%08lX %s ",
+ nbits_from_skc( skc ),
+ pubkey_letter( skc->pubkey_algo ),
+ keyid[1], datestr_from_skc(skc) );
+ {
+ size_t n;
+ char *p = get_user_id( keyid, &n );
+ tty_print_string( p, n );
+ m_free(p);
+ tty_printf("\n");
+ }
+ for(node=sec_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_SECKEY_SUBCERT ) {
+ PKT_secret_cert *subskc = node->pkt->pkt.secret_cert;
+ keyid_from_skc( subskc, keyid );
+ tty_printf("sub %4u%c/%08lX %s\n",
+ nbits_from_skc( subskc ),
+ pubkey_letter( subskc->pubkey_algo ),
+ keyid[1], datestr_from_skc(subskc) );
+ }
+ }
+ tty_printf("\n");
+
+
+
+ /* unprotect to get the passphrase */
+ switch( is_secret_key_protected( skc ) ) {
+ case -1:
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case 0:
+ tty_printf("This key is not protected.\n");
+ break;
+ default:
+ tty_printf("Key is protected.\n");
+ rc = check_secret_key( skc );
+ if( !rc )
+ passphrase = get_last_passphrase();
+ break;
+ }
+ if( rc )
+ goto leave;
+
+
+ algo = ask_algo( &v4 );
+ nbits = ask_keysize( algo );
+ ndays = ask_valid_days();
+
+ if( passphrase ) {
+ s2k = m_alloc_secure( sizeof *s2k );
+ s2k->mode = 1;
+ s2k->hash_algo = DIGEST_ALGO_RMD160;
+ set_next_passphrase( passphrase );
+ dek = passphrase_to_dek( NULL, CIPHER_ALGO_BLOWFISH, s2k, 2 );
+ }
+
+ rc = do_create( algo, nbits, pub_keyblock, sec_keyblock,
+ dek, s2k, NULL, ndays, v4 );
+ if( !rc )
+ rc = write_keybinding(pub_keyblock, pub_keyblock, skc);
+ if( !rc )
+ rc = write_keybinding(sec_keyblock, pub_keyblock, skc);
+ /* write back */
+ if( !rc ) {
+ rc = update_keyblock( &pub_kbpos, pub_keyblock );
+ if( rc )
+ log_error("update_public_keyblock failed\n" );
+ }
+ if( !rc ) {
+ rc = update_keyblock( &sec_kbpos, sec_keyblock );
+ if( rc )
+ log_error("update_secret_keyblock failed\n" );
+ }
+ if( !rc )
+ tty_printf(_("public and secret subkey created.\n") );
+
+
+ leave:
+ if( rc )
+ tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) );
+ m_free( passphrase );
+ m_free( dek );
+ m_free( s2k );
+ if( skc ) /* release the copy of the (now unprotected) secret key */
+ free_secret_cert(skc);
+ release_kbnode( sec_keyblock );
+ release_kbnode( pub_keyblock );
+ set_next_passphrase( NULL );
}