summaryrefslogtreecommitdiff
path: root/libextra/opencdk/keygen.c
diff options
context:
space:
mode:
Diffstat (limited to 'libextra/opencdk/keygen.c')
-rw-r--r--libextra/opencdk/keygen.c870
1 files changed, 870 insertions, 0 deletions
diff --git a/libextra/opencdk/keygen.c b/libextra/opencdk/keygen.c
new file mode 100644
index 0000000000..c1f0ad1773
--- /dev/null
+++ b/libextra/opencdk/keygen.c
@@ -0,0 +1,870 @@
+/* -*- Mode: C; c-file-style: "bsd" -*-
+ * keygen.c - OpenPGP key generation
+ * Copyright (C) 2002, 2003 Timo Schulz
+ *
+ * This file is part of OpenCDK.
+ *
+ * OpenCDK is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * OpenCDK is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with OpenCDK; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include <stdio.h>
+
+#include "opencdk.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+#include "types.h"
+
+struct key_ctx_s {
+ u32 expire_date;
+ int algo;
+ int len;
+ gcry_mpi_t resarr[6];
+ size_t n;
+ cdk_pkt_pubkey_t pk;
+ cdk_pkt_seckey_t sk;
+};
+
+
+struct cdk_keygen_ctx_s {
+ char * user_id;
+ cdk_pkt_userid_t id;
+ byte * sym_prefs;
+ size_t sym_len;
+ byte * hash_prefs;
+ size_t hash_len;
+ byte * zip_prefs;
+ size_t zip_len;
+ unsigned mdc_feature:1;
+ unsigned ks_no_modify:1;
+ char * ks_pref_url;
+ cdk_pkt_signature_t sig;
+ unsigned protect:1;
+ struct key_ctx_s key[2];
+ char * pass;
+ size_t pass_len;
+};
+
+
+/* default preferences */
+static byte def_sym_prefs[] = {CDK_CIPHER_AES, CDK_CIPHER_CAST5,
+ CDK_CIPHER_TWOFISH, CDK_CIPHER_AES192,
+ CDK_CIPHER_AES256, CDK_CIPHER_3DES,
+ CDK_CIPHER_BLOWFISH};
+static byte def_hash_prefs[] = {CDK_MD_SHA1, CDK_MD_RMD160, CDK_MD_MD5};
+static byte def_zip_prefs[] = {CDK_COMPRESS_ZIP, CDK_COMPRESS_ZLIB};
+
+
+static int
+check_pref_array( const byte * p, size_t n, int type )
+{
+ int i;
+
+ if( !p )
+ return 0;
+
+ if( type == CDK_PREFTYPE_SYM ) {
+ for( i = 0; i < n; i++ ) {
+ if( cdk_cipher_test_algo( p[i] ) )
+ return -1;
+ }
+ }
+ else if( type == CDK_PREFTYPE_HASH ) {
+ for( i = 0; i < n; i++ ) {
+ if( cdk_md_test_algo( p[i] ) )
+ return -1;
+ }
+ }
+ else if( type == CDK_PREFTYPE_ZIP ) {
+ if( n > 2 )
+ return -1;
+ if( p[0] > 2 || p[1] > 2 )
+ return -1;
+ }
+ else
+ return -1;
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_prefs: Set the preferences for the userID
+ * @hd: the keygen object
+ * @hd: the preference type
+ * @array: one-octet array with algorithm numers
+ *
+ **/
+cdk_error_t
+cdk_keygen_set_prefs( cdk_keygen_ctx_t hd, enum cdk_pref_type_t type,
+ const byte * array, size_t n )
+{
+ int rc;
+
+ if( !hd )
+ return CDK_Inv_Value;
+
+ rc = check_pref_array( array, n, type );
+ if( rc )
+ return CDK_Inv_Value;
+
+ switch( type) {
+ case CDK_PREFTYPE_SYM:
+ hd->sym_len = array? n : DIM( def_sym_prefs );
+ hd->sym_prefs = cdk_calloc( 1, hd->sym_len );
+ if( hd->sym_prefs )
+ memcpy( hd->sym_prefs, array? array : def_sym_prefs, hd->sym_len );
+ break;
+
+ case CDK_PREFTYPE_HASH:
+ hd->hash_len = array? n : DIM( def_hash_prefs );
+ hd->hash_prefs = cdk_calloc( 1, hd->hash_len );
+ if( hd->hash_prefs )
+ memcpy( hd->hash_prefs, array? array : def_hash_prefs,
+ hd->hash_len );
+ break;
+
+ case CDK_PREFTYPE_ZIP:
+ hd->zip_len = array? n : DIM( def_zip_prefs );
+ hd->zip_prefs = cdk_calloc( 1, hd->zip_len );
+ if( hd->zip_prefs )
+ memcpy( hd->zip_prefs, array? array : def_zip_prefs, hd->zip_len );
+ break;
+
+ default:
+ return CDK_Inv_Mode;
+ }
+
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_name: set the userid name for the key
+ * @hd: the keygen object
+ * @name: name
+ *
+ * The name will be encoded in UTF8 to avoid problems.
+ **/
+void
+cdk_keygen_set_name( cdk_keygen_ctx_t hd, const char * name )
+{
+ if( hd ) {
+ cdk_free( hd->user_id );
+ hd->user_id = cdk_utf8_encode( name );
+ }
+}
+
+
+static int
+check_bits( int bits, int algo )
+{
+ if( bits < 768 )
+ return 768;
+ if( algo == CDK_PK_DSA && bits > 1024 )
+ return 1024;
+ if( bits > 4096 )
+ return 4096;
+ return bits;
+}
+
+
+/**
+ * cdk_keygen_set_algo_info: set the length and type of the key
+ * @hd: the keygen object.
+ * @type: key type (primary=0, subkey=1)
+ * @algo: algorithm compliant with rfc2440
+ * @bits: lengt of the key in bits
+ *
+ **/
+cdk_error_t
+cdk_keygen_set_algo_info( cdk_keygen_ctx_t hd, int type,
+ enum cdk_pk_algo_t algo, int bits )
+{
+ int rc;
+ int usage = type? PK_USAGE_ENCR : PK_USAGE_SIGN;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 1 )
+ return CDK_Inv_Value;
+
+ if( bits % 128 != 0 )
+ bits = bits + ( bits % 128 );
+
+ rc = _cdk_pk_test_algo( algo, usage );
+ if( rc )
+ return rc;
+
+ /* type=0 primary type=1 sub */
+ hd->key[type].algo = algo;
+ hd->key[type].len = check_bits( bits, algo );
+
+ return 0;
+}
+
+
+/**
+ * cdk_keygen_set_mdc_feature: set the mdc feature for the key
+ * @hd: keygen object
+ * @val: boolean( yes=1, no=0)
+ *
+ * if you want a RFC2440 compliant key, you've to disable this feature
+ * until the rfc2440-bis8 becomes the next standard.
+ **/
+void
+cdk_keygen_set_mdc_feature( cdk_keygen_ctx_t hd, int val )
+{
+ if( hd )
+ hd->mdc_feature = val;
+}
+
+
+
+void
+cdk_keygen_set_keyserver_flags( cdk_keygen_ctx_t hd, int no_modify,
+ const char *pref_url )
+{
+ if( no_modify )
+ hd->ks_no_modify = 1;
+ if( pref_url )
+ hd->ks_pref_url = cdk_strdup( pref_url );
+} /* cdk_keygen_set_keyserver_flags */
+
+
+/**
+ * cdk_keygen_set_expire_date: set the expire date of the primary key
+ * @hd: keygen object
+ * @type: key type( 0=primary, 1=seconardy)
+ * @timestamp: the date the key should expire
+ *
+ **/
+void
+cdk_keygen_set_expire_date( cdk_keygen_ctx_t hd, int type, long timestamp )
+{
+ if( !hd )
+ return;
+ if( type < 0 || type > 1 )
+ return;
+ if( timestamp < 0 || timestamp < _cdk_timestamp( ) )
+ timestamp = 0;
+ hd->key[type].expire_date = timestamp;
+}
+
+
+void
+cdk_keygen_set_passphrase( cdk_keygen_ctx_t hd, const char * pass )
+{
+ if( !hd )
+ return;
+ if( pass ) {
+ size_t n = strlen( pass );
+ _cdk_sec_free( hd->pass, hd->pass_len );
+ hd->pass = cdk_salloc( n + 1, 1 );
+ if( hd->pass ) {
+ memcpy( hd->pass, pass, n );
+ hd->pass[n] = '\0';
+ hd->pass_len = n;
+ hd->protect = 1;
+ }
+ }
+}
+
+
+static int
+read_single_mpi( gcry_sexp_t s_key, const char * val, gcry_mpi_t * r_resarr )
+{
+ gcry_sexp_t list;
+
+ if( !r_resarr )
+ return CDK_Inv_Value;
+ list = gcry_sexp_find_token( s_key, val, 0 );
+ if( list )
+ *r_resarr = gcry_sexp_nth_mpi( list, 1, 0 );
+ gcry_sexp_release( list );
+ return list? 0 : CDK_Gcry_Error;
+}
+
+
+static int
+read_dsa_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "p", &resarr[0] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "q", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "g", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "y", &resarr[3] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "x", &resarr[4] );
+ return rc;
+}
+
+
+static int
+read_elg_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "p", &resarr[0] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "g", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "y", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "x", &resarr[3] );
+ return rc;
+}
+
+
+static int
+read_rsa_key( gcry_sexp_t s_key, gcry_mpi_t * resarr )
+{
+ int rc = read_single_mpi( s_key, "n", &resarr[0] );
+ if( !rc )
+ rc =read_single_mpi( s_key, "e", &resarr[1] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "d", &resarr[2] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "p", &resarr[3] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "q", &resarr[4] );
+ if( !rc )
+ rc = read_single_mpi( s_key, "u", &resarr[5] );
+ return rc;
+}
+
+
+static int
+generate_subkey( cdk_keygen_ctx_t hd )
+{
+ gcry_sexp_t s_params = NULL, s_key;
+ size_t n = hd->key[1].len;
+ int rc;
+
+ if( !hd )
+ return CDK_Inv_Value;
+
+ if( is_DSA( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n );
+ else if( is_ELG( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(elg(nbits %d)))", n );
+ else if( is_RSA( hd->key[1].algo) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n );
+ else
+ rc = CDK_Inv_Algo;
+ if( !rc )
+ rc = gcry_pk_genkey( &s_key, s_params );
+ gcry_sexp_release( s_params );
+ if( !rc ) {
+ if( is_DSA( hd->key[1].algo) )
+ rc = read_dsa_key( s_key, hd->key[1].resarr );
+ else if( is_ELG( hd->key[1].algo) )
+ rc = read_elg_key( s_key, hd->key[1].resarr );
+ else if( is_RSA( hd->key[1].algo) )
+ rc = read_rsa_key( s_key, hd->key[1].resarr );
+ }
+ hd->key[1].n = cdk_pk_get_npkey( hd->key[1].algo );
+ gcry_sexp_release( s_key );
+ return rc;
+}
+
+
+/**
+ * cdk_keygen_start: kick off the key generation
+ * @hd: the keygen object
+ *
+ **/
+cdk_error_t
+cdk_keygen_start( cdk_keygen_ctx_t hd )
+{
+ gcry_sexp_t s_params = NULL, s_key = NULL;
+ size_t n;
+ int rc = 0;
+
+ if( !hd || !hd->user_id )
+ return CDK_Inv_Value;
+ if( is_ELG( hd->key[0].algo ) )
+ return CDK_Inv_Mode;
+ if( !hd->key[0].len )
+ hd->key[0].len = 1024;
+ n = hd->key[0].len;
+
+ if( !hd->sym_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_SYM, NULL, 0 );
+ if( !hd->hash_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_HASH, NULL, 0 );
+ if( !hd->zip_prefs )
+ cdk_keygen_set_prefs( hd, CDK_PREFTYPE_ZIP, NULL, 0 );
+
+ if( is_DSA( hd->key[0].algo ) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n );
+ else if( is_RSA( hd->key[0].algo ) )
+ rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n );
+ else
+ rc = CDK_Inv_Algo;
+ if( !rc )
+ rc = gcry_pk_genkey( &s_key, s_params );
+ gcry_sexp_release( s_params );
+ if( !rc ) {
+ if( is_DSA( hd->key[0].algo ) )
+ rc = read_dsa_key( s_key, hd->key[0].resarr );
+ else if( is_RSA( hd->key[0].algo ) )
+ rc = read_rsa_key( s_key, hd->key[0].resarr );
+ hd->key[0].n = cdk_pk_get_npkey( hd->key[0].algo );
+ }
+ gcry_sexp_release( s_key );
+ if( !rc ) {
+ if( hd->key[1].algo && hd->key[1].len )
+ rc = generate_subkey( hd );
+ }
+ return rc;
+}
+
+
+static int
+gcry_mpi_to_native( cdk_keygen_ctx_t hd, size_t nkey, int type,
+ cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk )
+{
+ gcry_mpi_t * resarr;
+ cdk_mpi_t a = NULL;
+ size_t nbytes;
+ int i = 0, j = 0, nbits;
+ int rc = 0;
+
+ if( !hd )
+ return CDK_Inv_Value;
+ if( !pk && !sk )
+ return CDK_Inv_Value;
+ if( type < 0 || type > 1 )
+ return CDK_Inv_Value;
+
+ resarr = hd->key[type].resarr;
+ if( sk )
+ i += cdk_pk_get_npkey( sk->pubkey_algo );
+ while( j != nkey ) {
+ nbits = gcry_mpi_get_nbits( resarr[i] );
+ if( pk )
+ a = cdk_calloc( 1, sizeof * a + (nbits + 7) / 8 + 2 + 1 );
+ else if( sk )
+ a = cdk_salloc( sizeof * a + (nbits + 7) / 8 + 2 + 1, 1 );
+ a->bits = nbits;
+ a->bytes = ( nbits + 7 ) / 8;
+ nbytes = a->bytes;
+ a->data[0] = nbits >> 8;
+ a->data[1] = nbits;
+ rc = gcry_mpi_print( GCRYMPI_FMT_USG, a->data+2, nbytes, &nbytes, resarr[i] );
+ if( rc )
+ break;
+ if( pk )
+ pk->mpi[j++] = a;
+ else if( sk )
+ sk->mpi[j++] = a;
+ i++;
+ }
+ return rc;
+}
+
+
+static cdk_pkt_pubkey_t
+pk_create( cdk_keygen_ctx_t hd, int type )
+{
+ cdk_pkt_pubkey_t pk;
+ int rc = 0, npkey = 0;
+
+ if( type < 0 || type > 1 )
+ return NULL;
+ pk = cdk_calloc( 1, sizeof * pk );
+ if( !pk )
+ return NULL;
+ pk->version = 4;
+ pk->pubkey_algo = hd->key[type].algo;
+ pk->timestamp = _cdk_timestamp( );
+ if( hd->key[type].expire_date )
+ pk->expiredate = pk->timestamp + hd->key[type].expire_date;
+ npkey = cdk_pk_get_npkey( pk->pubkey_algo );
+ rc = gcry_mpi_to_native( hd, npkey, type, pk, NULL );
+ if( rc ) {
+ cdk_free( pk );
+ pk = NULL;
+ }
+ return pk;
+}
+
+
+static cdk_pkt_seckey_t
+sk_create( cdk_keygen_ctx_t hd, int type )
+{
+ cdk_pkt_seckey_t sk;
+ int nskey, rc = 0;
+
+ if( type < 0 || type > 1 )
+ return NULL;
+ sk = cdk_calloc( 1, sizeof * sk );
+ if( !sk )
+ return NULL;
+ _cdk_copy_pubkey( &sk->pk, hd->key[type].pk );
+ sk->version = 4;
+ sk->pubkey_algo = hd->key[type].algo;
+ sk->csum = 0;
+ sk->is_protected = 0;
+ nskey = cdk_pk_get_nskey( sk->pubkey_algo );
+ rc = gcry_mpi_to_native( hd, nskey, type, NULL, sk );
+ if( rc ) {
+ cdk_free( sk );
+ sk = NULL;
+ }
+ return sk;
+}
+
+
+static cdk_pkt_userid_t
+uid_create( cdk_keygen_ctx_t hd )
+{
+ cdk_pkt_userid_t id;
+
+ if( !hd->user_id )
+ return NULL;
+ id = cdk_calloc( 1, sizeof * id + strlen( hd->user_id ) + 1 );
+ if( !id )
+ return NULL;
+ strcpy( id->name, hd->user_id );
+ id->len = strlen( hd->user_id );
+ return id;
+}
+
+
+static cdk_pkt_signature_t
+sig_subkey_create( cdk_keygen_ctx_t hd )
+{
+ cdk_md_hd_t md;
+ cdk_subpkt_t node;
+ cdk_pkt_signature_t sig;
+ cdk_pkt_pubkey_t pk = hd->key[0].pk;
+ cdk_pkt_pubkey_t sub_pk = hd->key[1].pk;
+ cdk_pkt_seckey_t sk = hd->key[0].sk;
+ byte buf[4];
+ int rc;
+
+ sig = cdk_calloc( 1, sizeof * sig );
+ if( !sig )
+ return NULL;
+ _cdk_sig_create( pk, sig );
+ sig->sig_class = 0x18;
+ sig->digest_algo = CDK_MD_SHA1;
+
+ if( sub_pk->expiredate ) {
+ _cdk_u32tobuf( sub_pk->expiredate - sub_pk->timestamp, buf );
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KEY_EXPIRE, buf, 4 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ md = cdk_md_open( sig->digest_algo, 0 );
+ if( !md ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+
+ _cdk_hash_pubkey( pk, md, 0 );
+ _cdk_hash_pubkey( sub_pk, md, 0 );
+ rc = _cdk_sig_complete( sig, sk, md );
+ cdk_md_close( md );
+ if( rc ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+ return sig;
+}
+
+
+static cdk_pkt_signature_t
+sig_self_create( cdk_keygen_ctx_t hd )
+{
+ cdk_md_hd_t md;
+ cdk_subpkt_t node;
+ cdk_pkt_signature_t sig;
+ cdk_pkt_pubkey_t pk = hd->key[0].pk;
+ cdk_pkt_userid_t id = hd->id;
+ cdk_pkt_seckey_t sk = hd->key[0].sk;
+ u32 keyid[2];
+ byte buf[8], * p;
+ int rc;
+
+ sig = cdk_calloc( 1, sizeof * sig );
+ if( !sig )
+ return NULL;
+ sig->version = 4;
+ sig->timestamp = _cdk_timestamp( );
+ sig->sig_class = 0x13;
+ sig->pubkey_algo = hd->key[0].algo;
+ sig->digest_algo = CDK_MD_SHA1;
+
+ _cdk_u32tobuf( sig->timestamp, buf );
+ sig->hashed = node = cdk_subpkt_new( 4 );
+ if( node )
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_SIG_CREATED, buf, 4 );
+
+ p = hd->sym_prefs;
+ node = cdk_subpkt_new( hd->sym_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_SYM, p, hd->sym_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ p = hd->hash_prefs;
+ node = cdk_subpkt_new( hd->hash_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_HASH, p, hd->hash_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ p = hd->zip_prefs;
+ node = cdk_subpkt_new( hd->zip_len + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREFS_ZIP, p, hd->zip_len );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+
+ if( hd->mdc_feature ) {
+ buf[0] = 0x01;
+ node = cdk_subpkt_new( 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_FEATURES, buf, 1 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( hd->ks_no_modify ) {
+ buf[0] = 0x80;
+ node = cdk_subpkt_new( 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KS_FLAGS, buf, 1 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( hd->ks_pref_url ) {
+ node = cdk_subpkt_new( strlen( hd->ks_pref_url ) + 1 );
+ if( node ) {
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_PREF_KS,
+ hd->ks_pref_url, strlen( hd->ks_pref_url ) );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ if( pk->expiredate ) {
+ node = cdk_subpkt_new( 4 );
+ if( node ) {
+ _cdk_u32tobuf( pk->expiredate - pk->timestamp, buf );
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_KEY_EXPIRE, buf, 4 );
+ cdk_subpkt_add( sig->hashed, node );
+ }
+ }
+
+ sig->unhashed = node = cdk_subpkt_new( 8 );
+ if( node ) {
+ cdk_pk_get_keyid( pk, keyid );
+ _cdk_u32tobuf( keyid[0], buf );
+ _cdk_u32tobuf( keyid[1], buf + 4 );
+ cdk_subpkt_init( node, CDK_SIGSUBPKT_ISSUER, buf, 8 );
+ }
+
+ md = cdk_md_open( sig->digest_algo, 0 );
+ if( !md ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+
+ _cdk_hash_pubkey( pk, md, 0 );
+ _cdk_hash_userid( id, sig->version == 4, md );
+ rc = _cdk_sig_complete( sig, sk, md );
+ cdk_md_close( md );
+ if( rc ) {
+ _cdk_free_signature( sig );
+ return NULL;
+ }
+ return sig;
+}
+
+
+/**
+ * cdk_keygen_save: save the generated keys to disk
+ * @hd: the keygen object
+ * @pub: name of the file to store the public key
+ * @sec: name of the file to store the secret key
+ *
+ **/
+cdk_error_t
+cdk_keygen_save( cdk_keygen_ctx_t hd, const char * pubf, const char * secf )
+{
+ cdk_stream_t out = NULL;
+ CDK_PACKET pkt;
+ int rc;
+
+ hd->key[0].pk = pk_create( hd, 0 );
+ if( !hd->key[0].pk )
+ return CDK_Inv_Packet;
+ hd->key[0].sk = sk_create( hd, 0 );
+ if( !hd->key[0].sk )
+ return CDK_Inv_Packet;
+ hd->id = uid_create( hd );
+ if( !hd->id )
+ return CDK_Inv_Packet;
+ hd->sig = sig_self_create( hd );
+ if( !hd->sig )
+ return CDK_Inv_Packet;
+
+ rc = cdk_stream_create( pubf, &out );
+ if( rc )
+ return rc;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_PUBLIC_KEY;
+ pkt.pkt.public_key = hd->key[0].pk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_USER_ID;
+ pkt.pkt.user_id = hd->id;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = hd->sig;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ if( hd->key[1].algo ) {
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_PUBLIC_SUBKEY;
+ pkt.pkt.public_key = hd->key[1].pk = pk_create( hd, 1 );
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = sig_subkey_create( hd );
+ rc = cdk_pkt_write( out, &pkt );
+ cdk_pkt_free( &pkt );
+ if( rc )
+ goto fail;
+ }
+
+ cdk_stream_close( out );
+ out = NULL;
+
+ rc = cdk_stream_create( secf, &out );
+ if( rc )
+ goto fail;
+
+ if( hd->protect ) {
+ rc = cdk_sk_protect( hd->key[0].sk, hd->pass );
+ if( rc )
+ goto fail;
+ }
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SECRET_KEY;
+ pkt.pkt.secret_key = hd->key[0].sk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_USER_ID;
+ pkt.pkt.user_id = hd->id;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SIGNATURE;
+ pkt.pkt.signature = hd->sig;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+
+ if( hd->key[1].algo ) {
+ hd->key[1].sk = sk_create( hd, 1 );
+ if( hd->protect && (rc = cdk_sk_protect( hd->key[1].sk, hd->pass )) )
+ goto fail;
+ cdk_pkt_init( &pkt );
+ pkt.pkttype = CDK_PKT_SECRET_SUBKEY;
+ pkt.pkt.secret_key = hd->key[1].sk;
+ rc = cdk_pkt_write( out, &pkt );
+ if( rc )
+ goto fail;
+ }
+
+ fail:
+ cdk_stream_close( out );
+ return rc;
+}
+
+
+/**
+ * cdk_keygen_free: free the keygen object
+ * @hd: the keygen object
+ *
+ **/
+void
+cdk_keygen_free( cdk_keygen_ctx_t hd )
+{
+ if( hd ) {
+ _cdk_free_pubkey( hd->key[0].pk );
+ _cdk_free_pubkey( hd->key[1].pk );
+ _cdk_free_seckey( hd->key[0].sk );
+ _cdk_free_seckey( hd->key[1].sk );
+ _cdk_free_userid( hd->id );
+ _cdk_free_signature( hd->sig );
+ cdk_free( hd->sym_prefs );
+ cdk_free( hd->hash_prefs );
+ cdk_free( hd->zip_prefs );
+ _cdk_sec_free( hd->pass, hd->pass_len );
+ _cdk_free_mpibuf( hd->key[0].n, hd->key[0].resarr );
+ _cdk_free_mpibuf( hd->key[1].n, hd->key[1].resarr );
+ cdk_free( hd );
+ }
+}
+
+
+/**
+ * cdk_keygen_new:
+ * @r_hd: the new object
+ *
+ **/
+cdk_error_t
+cdk_keygen_new( cdk_keygen_ctx_t * r_hd )
+{
+ cdk_keygen_ctx_t hd;
+
+ if( !r_hd )
+ return CDK_Inv_Value;
+ hd = cdk_calloc( 1, sizeof * hd );
+ if( !hd )
+ return CDK_Out_Of_Core;
+ *r_hd = hd;
+ return 0;
+}