diff --git a/NEWS b/NEWS index 5c8a8342a..794ad11f1 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ Noteworthy changes in version 1.9.0 (unreleased) ------------------------------------------------ + * gpg does now also use libgcrypt, libgpg-error is required. + * New gpgsm commands --call-dirmngr and --call-protect-tool. * Changing a passphrase is now possible using "gpgsm --passwd" diff --git a/g10/ChangeLog b/g10/ChangeLog index b4db7b5f0..023f66b0d 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,25 @@ +2003-06-27 Werner Koch + + * seckey-cert.c (check_secret_key): Bypass the unprotection for + mode 1002. + * sign.c (do_sign): Handle card case (i.e. mode 1002). + +2003-06-26 Werner Koch + + * build-packet.c (do_secret_key): Implement special protection + mode 1002. + * parse-packet.c (parse_key): Likewise. + + * keygen.c (smartcard_gen_key): New. + * call-agent.c (agent_scd_setattr): New. + +2003-06-24 Werner Koch + + * Makefile.am: Removed signal.c + + * g10.c (emergency_cleanup): New. + (main): Use gnupg_init_signals and register malloc for assuan. + 2003-06-23 Werner Koch * keyid.c (do_fingerprint_md): Made it work again. diff --git a/g10/Makefile.am b/g10/Makefile.am index 6940be67a..3137bb894 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -63,8 +63,7 @@ common_source = \ plaintext.c \ sig-check.c \ keylist.c \ - pkglue.c pkglue.h \ - signal.c + pkglue.c pkglue.h gpg_SOURCES = g10.c \ $(common_source) \ diff --git a/g10/build-packet.c b/g10/build-packet.c index a3177525e..afc123624 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -415,8 +415,9 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) if( sk->protect.s2k.mode == 3 ) iobuf_put(a, sk->protect.s2k.count ); - /* For out special mode 1001 we do not need an IV */ - if( sk->protect.s2k.mode != 1001 ) + /* For out special modes 1001 and 1002 we do not need an IV */ + if( sk->protect.s2k.mode != 1001 + && sk->protect.s2k.mode != 1002 ) iobuf_write(a, sk->protect.iv, sk->protect.ivlen ); } } @@ -425,6 +426,11 @@ do_secret_key( iobuf_t out, int ctb, PKT_secret_key *sk ) if( sk->protect.s2k.mode == 1001 ) ; /* GnuPG extension - don't write a secret key at all */ + else if( sk->protect.s2k.mode == 1002 ) + { /* GnuPG extension - divert to OpenPGP smartcard. */ + iobuf_put(a, 0 ); /* length of the serial number or 0 for unknown. */ + /* fixme: write the serial number. */ + } else if( sk->is_protected && sk->version >= 4 ) { /* The secret key is protected - write it out as it is */ byte *p; diff --git a/g10/call-agent.c b/g10/call-agent.c index e888820cc..67aa333ee 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -47,7 +47,7 @@ #endif static ASSUAN_CONTEXT agent_ctx = NULL; -static int force_pipe_server = 0; +static int force_pipe_server = 1; /* FIXME: set this back to 0. */ struct cipher_parm_s { ASSUAN_CONTEXT ctx; @@ -61,11 +61,6 @@ struct genkey_parm_s { size_t sexplen; }; -struct learn_parm_s { - int error; - ASSUAN_CONTEXT ctx; - struct membuf *data; -}; /* Try to connect to the agent via socket or fork it off and work by @@ -292,16 +287,59 @@ start_agent (void) } -static AssuanError -membuf_data_cb (void *opaque, const void *buffer, size_t length) +/* Return a new malloced string by unescaping the string S. Escaping + is percent escaping and '+'/space mapping. A binary nul will + silently be replaced by a 0xFF. Function returns NULL to indicate + an out of memory status. */ +static char * +unescape_status_string (const unsigned char *s) { - membuf_t *data = opaque; + char *buffer, *d; - if (buffer) - put_membuf (data, buffer, length); - return 0; + buffer = d = xtrymalloc (strlen (s)+1); + if (!buffer) + return NULL; + while (*s) + { + if (*s == '%' && s[1] && s[2]) + { + s++; + *d = xtoi_2 (s); + if (!*d) + *d = '\xff'; + d++; + s += 2; + } + else if (*s == '+') + { + *d++ = ' '; + s++; + } + else + *d++ = *s++; + } + *d = 0; + return buffer; } - + +/* Take a 20 byte hexencoded string and put it into the the provided + 20 byte buffer FPR in binary format. */ +static int +unhexify_fpr (const char *hexstr, unsigned char *fpr) +{ + const char *s; + int n; + + for (s=hexstr, n=0; hexdigitp (s); s++, n++) + ; + if (*s || (n != 40)) + return 0; /* no fingerprint (invalid or wrong length). */ + n /= 2; + for (s=hexstr, n=0; *s; s += 2, n++) + fpr[n] = xtoi_2 (s); + return 1; /* okay */ +} + #if 0 @@ -391,10 +429,161 @@ agent_havekey (const char *hexkeygrip) } -/* Ask the agent to change the passphrase of the key identified by - HEXKEYGRIP. */ +static AssuanError +learn_status_cb (void *opaque, const char *line) +{ + struct agent_card_info_s *parm = opaque; + const char *keyword = line; + int keywordlen; + + log_debug ("got status line `%s'\n", line); + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen)) + { + parm->disp_name = unescape_status_string (line); + } + else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY_URL", keywordlen)) + { + parm->pubkey_url = unescape_status_string (line); + } + else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) + { + int no = atoi (line); + while (!spacep (line)) + line++; + while (spacep (line)) + line++; + if (no == 1) + parm->fpr1valid = unhexify_fpr (line, parm->fpr1); + else if (no == 2) + parm->fpr2valid = unhexify_fpr (line, parm->fpr2); + else if (no == 3) + parm->fpr3valid = unhexify_fpr (line, parm->fpr3); + } + + return 0; +} + +/* Call the agent to learn about a smartcard */ int -agent_passwd (const char *hexkeygrip) +agent_learn (struct agent_card_info_s *info) +{ + int rc; + + rc = start_agent (); + if (rc) + return rc; + + memset (info, 0, sizeof *info); + rc = assuan_transact (agent_ctx, "LEARN --send", + NULL, NULL, NULL, NULL, + learn_status_cb, info); + + return map_assuan_err (rc); +} + + + +/* Send an setattr command to the SCdaemon. */ +int +agent_scd_setattr (const char *name, + const unsigned char *value, size_t valuelen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + char *p; + + if (!*name || !valuelen) + return gpg_error (GPG_ERR_INV_VALUE); + + /* We assume that NAME does not need escaping. */ + if (12 + strlen (name) > DIM(line)-1) + return gpg_error (GPG_ERR_TOO_LARGE); + + p = stpcpy (stpcpy (line, "SCD SETATTR "), name); + *p++ = ' '; + for (; valuelen; value++, valuelen--) + { + if (p >= line + DIM(line)-5 ) + return gpg_error (GPG_ERR_TOO_LARGE); + if (*value < ' ' || *value == '+' || *value == '%') + { + sprintf (p, "%%%02X", *value); + p += 3; + } + else if (*value == ' ') + *p++ = '+'; + else + *p++ = *value; + } + *p = 0; + + rc = start_agent (); + if (rc) + return rc; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + return map_assuan_err (rc); +} + + +/* Status callback for the SCD GENKEY command. */ +static AssuanError +scd_genkey_cb (void *opaque, const char *line) +{ + struct agent_card_genkey_s *parm = opaque; + const char *keyword = line; + int keywordlen; + gpg_error_t rc; + + log_debug ("got status line `%s'\n", line); + for (keywordlen=0; *line && !spacep (line); line++, keywordlen++) + ; + while (spacep (line)) + line++; + + if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen)) + { + parm->fprvalid = unhexify_fpr (line, parm->fpr); + } + if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen)) + { + gcry_mpi_t a; + const char *name = line; + + while (!spacep (line)) + line++; + while (spacep (line)) + line++; + + rc = gcry_mpi_scan (&a, GCRYMPI_FMT_HEX, line, 0); + if (rc) + log_error ("error parsing received key data: %s\n", gpg_strerror (rc)); + else if (*name == 'n' && spacep (name+1)) + parm->n = a; + else if (*name == 'e' && spacep (name+1)) + parm->e = a; + else + { + log_info ("unknown parameter name in received key data\n"); + gcry_mpi_release (a); + } + } + else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen)) + { + parm->created_at = (u32)strtoul (line, NULL, 10); + } + + return 0; +} + +/* Send a GENKEY command to the SCdaemon. */ +int +agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force) { int rc; char line[ASSUAN_LINELENGTH]; @@ -403,13 +592,95 @@ agent_passwd (const char *hexkeygrip) if (rc) return rc; - if (!hexkeygrip || strlen (hexkeygrip) != 40) - return gpg_error (GPG_ERR_INV_VALUE); - - snprintf (line, DIM(line)-1, "PASSWD %s", hexkeygrip); + memset (info, 0, sizeof *info); + snprintf (line, DIM(line)-1, "SCD GENKEY %s%d", + force? "--force ":"", keyno); line[DIM(line)-1] = 0; - rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + memset (info, 0, sizeof *info); + rc = assuan_transact (agent_ctx, line, + NULL, NULL, NULL, NULL, + scd_genkey_cb, info); + return map_assuan_err (rc); } + + +static AssuanError +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + membuf_t *data = opaque; + + if (buffer) + put_membuf (data, buffer, length); + return 0; +} + +/* Send a sign command to the scdaemon via gpg-agent's pass thru + mechanism. */ +int +agent_scd_pksign (const char *keyid, int hashalgo, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + + /* Note, hashalgo is not yet used but hardwired to SHA1 in SCdaemon. */ + + *r_buf = NULL; + *r_buflen = 0; + + rc = start_agent (); + if (rc) + return rc; + + if (indatalen*2 + 50 > DIM(line)) + return gpg_error (GPG_ERR_GENERAL); + + sprintf (line, "SCD SETDATA "); + p = line + strlen (line); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = assuan_transact (agent_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return rc; + + init_membuf (&data, 1024); + snprintf (line, DIM(line)-1, "SCD PKSIGN %s", keyid); + line[DIM(line)-1] = 0; + rc = assuan_transact (agent_ctx, line, membuf_data_cb, &data, + NULL, NULL, NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return rc; + } + *r_buf = get_membuf (&data, r_buflen); + + return 0; +} + + + + + + + + + + + + + + + + + + + + + diff --git a/g10/call-agent.h b/g10/call-agent.h index fbb11958c..c620268f2 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -20,10 +20,46 @@ #ifndef GNUPG_G10_CALL_AGENT_H #define GNUPG_G10_CALL_AGENT_H + +struct agent_card_info_s { + int error; /* private. */ + char *disp_name; /* malloced. */ + char *pubkey_url; /* malloced. */ + char fpr1valid; + char fpr2valid; + char fpr3valid; + char fpr1[20]; + char fpr2[20]; + char fpr3[20]; +}; + +struct agent_card_genkey_s { + char fprvalid; + char fpr[20]; + u32 created_at; + gcry_mpi_t n; + gcry_mpi_t e; +}; + +/* Return card info. */ +int agent_learn (struct agent_card_info_s *info); + /* Check whether the secret key for the key identified by HEXKEYGRIP is available. Return 0 for yes or an error code. */ int agent_havekey (const char *hexkeygrip); +/* Send a SETATTR command to the SCdaemon. */ +int agent_scd_setattr (const char *name, + const unsigned char *value, size_t valuelen); + +/* Send a GENKEY command to the SCdaemon. */ +int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force); + +/* Send a PKSIGN command to the SCdaemon. */ +int agent_scd_pksign (const char *keyid, int hashalgo, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen); + /* Ask the agent to let the user change the passphrase of the secret key identified by HEXKEYGRIP. */ int agent_passwd (const char *hexkeygrip); @@ -31,4 +67,7 @@ int agent_passwd (const char *hexkeygrip); + + #endif /*GNUPG_G10_CALL_AGENT_H*/ + diff --git a/g10/g10.c b/g10/g10.c index cf6240d55..6909e0404 100644 --- a/g10/g10.c +++ b/g10/g10.c @@ -639,6 +639,7 @@ static void set_cmd( enum cmd_and_opt_values *ret_cmd, static void print_mds( const char *fname, int algo ); static void add_notation_data( const char *string, int which ); static void add_policy_url( const char *string, int which ); +static void emergency_cleanup (void); #ifdef __riscos__ RISCOS_GLOBAL_STATICS("GnuPG Heap") @@ -1191,7 +1192,7 @@ main( int argc, char **argv ) gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps(); - init_signals (); /* why not gnupg_init_signals. */ + gnupg_init_signals (0, emergency_cleanup); create_dotlock (NULL); /* register locking cleanup */ i18n_init(); @@ -1303,7 +1304,8 @@ main( int argc, char **argv ) maybe_setuid = 0; /* Okay, we are now working under our real uid */ - /* malloc hooks gohere ... */ + /* malloc hooks go here ... */ + assuan_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free); set_native_charset (NULL); /* Try to auto set the character set */ @@ -2894,6 +2896,13 @@ main( int argc, char **argv ) return 8; /*NEVER REACHED*/ } +/* Note: This function is used by signal handlers!. */ +static void +emergency_cleanup (void) +{ + gcry_control (GCRYCTL_TERM_SECMEM ); +} + void g10_exit( int rc ) diff --git a/g10/gpgv.c b/g10/gpgv.c index 91574a78b..da108d225 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -147,7 +147,7 @@ main( int argc, char **argv ) set_strusage (my_strusage); log_set_prefix ("gpgv", 1); - init_signals(); + gnupg_init_signals(0, NULL); i18n_init(); opt.command_fd = -1; /* no command fd */ opt.pgp2_workarounds = 1; diff --git a/g10/keyedit.c b/g10/keyedit.c index 38248a3d0..11e2d870e 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -921,6 +921,10 @@ change_passphrase( KBNODE keyblock ) tty_printf(_("Secret parts of primary key are not available.\n")); no_primary_secrets = 1; } + else if( sk->protect.s2k.mode == 1002 ) { + tty_printf(_("Secret key is actually stored on a card.\n")); + no_primary_secrets = 1; + } else { tty_printf(_("Key is protected.\n")); rc = check_secret_key( sk, 0 ); diff --git a/g10/keygen.c b/g10/keygen.c index 041a495bd..328647f03 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -38,6 +38,8 @@ #include "trustdb.h" #include "status.h" #include "i18n.h" +#include "call-agent.h" + #define MAX_PREFS 30 @@ -111,8 +113,11 @@ static int nzip_prefs; static int mdc_available,ks_modify; static void do_generate_keypair( struct para_data_s *para, - struct output_control_s *outctrl ); -static int write_keyblock( iobuf_t out, KBNODE node ); + struct output_control_s *outctrl, int card); +static int write_keyblock( iobuf_t out, KBNODE node ); +static int check_smartcard (void); +static int gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, u32 expireval); + static void @@ -1752,7 +1757,7 @@ get_parameter_revkey( struct para_data_s *para, enum para_name key ) static int proc_parameter_file( struct para_data_s *para, const char *fname, - struct output_control_s *outctrl ) + struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; @@ -1814,8 +1819,8 @@ proc_parameter_file( struct para_data_s *para, const char *fname, 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 */ + * It is a little bit ridiculous to keep it in secure memory + * but because we do this always, why not here. */ STRING2KEY *s2k; DEK *dek; @@ -1864,7 +1869,7 @@ proc_parameter_file( struct para_data_s *para, const char *fname, return -1; } - do_generate_keypair( para, outctrl ); + do_generate_keypair( para, outctrl, card); return 0; } @@ -1948,7 +1953,7 @@ read_parameter_file( const char *fname ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl ); + proc_parameter_file( para, fname, &outctrl, 0 ); release_parameter_list( para ); para = NULL; } @@ -2008,7 +2013,7 @@ read_parameter_file( const char *fname ) if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl ); + proc_parameter_file( para, fname, &outctrl, 0 ); release_parameter_list( para ); para = NULL; } @@ -2036,7 +2041,7 @@ read_parameter_file( const char *fname ) } else if( para ) { outctrl.lnr = lnr; - proc_parameter_file( para, fname, &outctrl ); + proc_parameter_file( para, fname, &outctrl, 0 ); } if( outctrl.use_files ) { /* close open streams */ @@ -2061,113 +2066,139 @@ read_parameter_file( const char *fname ) void generate_keypair( const char *fname ) { - unsigned int nbits; - char *uid = NULL; - DEK *dek; - STRING2KEY *s2k; - int algo; - unsigned int use; - int both = 0; - u32 expire; - struct para_data_s *para = NULL; - struct para_data_s *r; - struct output_control_s outctrl; + unsigned int nbits; + char *uid = NULL; + DEK *dek; + STRING2KEY *s2k; + int algo; + unsigned int use; + int both = 0; + int card = 0; + u32 expire; + struct para_data_s *para = NULL; + struct para_data_s *r; + struct output_control_s outctrl; - memset( &outctrl, 0, sizeof( outctrl ) ); + memset (&outctrl, 0, sizeof (outctrl)); - if( opt.batch ) { - read_parameter_file( fname ); - return; + if (opt.batch) + { + read_parameter_file( fname ); + return; } - algo = ask_algo( 0, &use ); - if( !algo ) { /* default: DSA with ElG subkey of the specified size */ - both = 1; - r = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 ); - r->key = pKEYLENGTH; - strcpy( r->u.value, "1024" ); - r->next = para; - para = r; - - algo = PUBKEY_ALGO_ELGAMAL_E; - r = xcalloc (1, sizeof *r + 20 ); - r->key = pSUBKEYTYPE; - sprintf( r->u.value, "%d", algo ); - r->next = para; - para = r; + do + { + card = check_smartcard (); + if (card < 0) + return; + if (card > 1) + log_error (_("can't generate subkey here\n")); } - else { - r = xcalloc (1, sizeof *r + 20 ); - r->key = pKEYTYPE; - sprintf( r->u.value, "%d", algo ); - r->next = para; - para = r; + while (card > 1); - if (use) { - r = xcalloc (1, sizeof *r + 20 ); - r->key = pKEYUSAGE; - sprintf( r->u.value, "%s%s", - (use & PUBKEY_USAGE_SIG)? "sign ":"", - (use & PUBKEY_USAGE_ENC)? "encrypt ":"" ); - r->next = para; - para = r; + if (card) + { + algo = PUBKEY_ALGO_RSA; + use = PUBKEY_USAGE_SIG; + } + else + algo = ask_algo (0, &use); + + if (!algo) + { /* default: DSA with ElG subkey of the specified size */ + both = 1; + r = xcalloc (1, 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 = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYLENGTH; + strcpy( r->u.value, "1024" ); + r->next = para; + para = r; + + algo = PUBKEY_ALGO_ELGAMAL_E; + r = xcalloc (1, sizeof *r + 20 ); + r->key = pSUBKEYTYPE; + sprintf( r->u.value, "%d", algo ); + r->next = para; + para = r; + } + else + { + r = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYTYPE; + sprintf( r->u.value, "%d", algo ); + r->next = para; + para = r; + + if (use) + { + r = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYUSAGE; + sprintf( r->u.value, "%s%s", + (use & PUBKEY_USAGE_SIG)? "sign ":"", + (use & PUBKEY_USAGE_ENC)? "encrypt ":"" ); + r->next = para; + para = r; } - } - nbits = ask_keysize( algo ); - r = 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(0); - r = xcalloc (1, sizeof *r + 20 ); - r->key = pKEYEXPIRE; - r->u.expire = expire; - r->next = para; - para = r; - r = 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; + if (!card) + { + nbits = ask_keysize( algo ); + r = xcalloc (1, sizeof *r + 20 ); + r->key = both? pSUBKEYLENGTH : pKEYLENGTH; + sprintf( r->u.value, "%u", nbits); + r->next = para; + para = r; } - r = 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 = xcalloc (1, sizeof *r ); - r->key = pPASSPHRASE_DEK; - r->u.dek = dek; - r->next = para; - para = r; - r = xcalloc (1, sizeof *r ); - r->key = pPASSPHRASE_S2K; - r->u.s2k = s2k; - r->next = para; - para = r; + + expire = ask_expire_interval(0); + r = xcalloc (1, sizeof *r + 20 ); + r->key = pKEYEXPIRE; + r->u.expire = expire; + r->next = para; + para = r; + r = 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 = xcalloc (1, sizeof *r + strlen(uid) ); + r->key = pUSERID; + strcpy( r->u.value, uid ); + r->next = para; + para = r; - proc_parameter_file( para, "[internal]", &outctrl ); - release_parameter_list( para ); + dek = card? NULL : ask_passphrase( &s2k ); + if (dek) + { + r = xcalloc (1, sizeof *r ); + r->key = pPASSPHRASE_DEK; + r->u.dek = dek; + r->next = para; + para = r; + r = xcalloc (1, sizeof *r ); + r->key = pPASSPHRASE_S2K; + r->u.s2k = s2k; + r->next = para; + para = r; + } + + proc_parameter_file (para, "[internal]", &outctrl, card); + release_parameter_list (para); } @@ -2189,225 +2220,271 @@ print_status_key_created (int letter, PKT_public_key *pk) write_status_text (STATUS_KEY_CREATED, buf); } + + static void -do_generate_keypair( struct para_data_s *para, - struct output_control_s *outctrl ) +do_generate_keypair (struct para_data_s *para, + struct output_control_s *outctrl, int card) { - KBNODE pub_root = NULL; - KBNODE sec_root = NULL; - PKT_secret_key *sk = NULL; - const char *s; - struct revocation_key *revkey; - int rc; - int did_sub = 0; + KBNODE pub_root = NULL; + KBNODE sec_root = NULL; + PKT_secret_key *sk = NULL; + const char *s; + struct revocation_key *revkey; + int rc; + int did_sub = 0; - if( outctrl->dryrun ) { - log_info("dry-run mode - key generation skipped\n"); - return; + 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; - xfree ( outctrl->pub.fname ); - outctrl->pub.fname = outctrl->pub.newfname; - outctrl->pub.newfname = NULL; + if (outctrl->use_files) + { + if (outctrl->pub.newfname) + { + iobuf_close (outctrl->pub.stream); + outctrl->pub.stream = NULL; + xfree (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; - xfree ( 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 ); - } - } - assert( outctrl->pub.stream ); - assert( outctrl->sec.stream ); - if( opt.verbose ) { - log_info(_("writing public key to `%s'\n"), outctrl->pub.fname ); - log_info(_("writing secret key to `%s'\n"), outctrl->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 KEY packet. - */ - pub_root = make_comment_node("#"); delete_kbnode(pub_root); - sec_root = make_comment_node("#"); delete_kbnode(sec_root); - - 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 && (revkey=get_parameter_revkey(para,pREVOKER))) - { - rc=write_direct_sig(pub_root,pub_root,sk,revkey); - if(!rc) - write_direct_sig(sec_root,pub_root,sk,revkey); - } - - 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, - get_parameter_uint (para, pKEYUSAGE)); - if( !rc ) - rc = write_selfsig(sec_root, pub_root, sk, - get_parameter_uint (para, pKEYUSAGE)); - } - - 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, - get_parameter_uint (para, pSUBKEYUSAGE)); - if( !rc ) - rc = write_keybinding(sec_root, pub_root, sk, - get_parameter_uint (para, pSUBKEYUSAGE)); - did_sub = 1; - } - - - 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_strerror (rc) ); - if( !rc ) { - rc = write_keyblock( outctrl->sec.stream, sec_root ); - if( rc ) - log_error("can't write secret key: %s\n", gpg_strerror (rc) ); - } - - } - else if( !rc ) { /* write to the standard keyrings */ - KEYDB_HANDLE pub_hd = keydb_new (0); - KEYDB_HANDLE sec_hd = keydb_new (1); - - /* FIXME: we may have to create the keyring first */ - rc = keydb_locate_writable (pub_hd, NULL); - if (rc) - log_error (_("no writable public keyring found: %s\n"), - gpg_strerror (rc)); - - if (!rc) { - rc = keydb_locate_writable (sec_hd, NULL); - if (rc) - log_error (_("no writable secret keyring found: %s\n"), - gpg_strerror (rc)); - } - - if (!rc && opt.verbose) { - log_info(_("writing public key to `%s'\n"), - keydb_get_resource_name (pub_hd)); - log_info(_("writing secret key to `%s'\n"), - keydb_get_resource_name (sec_hd)); - } - - if (!rc) { - rc = keydb_insert_keyblock (pub_hd, pub_root); - if (rc) - log_error (_("error writing public keyring `%s': %s\n"), - keydb_get_resource_name (pub_hd), gpg_strerror (rc)); - } - - if (!rc) { - rc = keydb_insert_keyblock (sec_hd, sec_root); - if (rc) - log_error (_("error writing secret keyring `%s': %s\n"), - keydb_get_resource_name (pub_hd), gpg_strerror (rc)); - } - - keydb_release (pub_hd); - keydb_release (sec_hd); - - if (!rc) { - int no_enc_rsa = - get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA - && get_parameter_uint( para, pKEYUSAGE ) - && !(get_parameter_uint( para,pKEYUSAGE) & PUBKEY_USAGE_ENC); - PKT_public_key *pk = find_kbnode (pub_root, - PKT_PUBLIC_KEY)->pkt->pkt.public_key; - - update_ownertrust (pk, - ((get_ownertrust (pk) & ~TRUST_MASK) - | TRUST_ULTIMATE )); - - if (!opt.batch) { - tty_printf(_("public and secret key created and signed.\n") ); - tty_printf(_("key marked as ultimately trusted.\n") ); - tty_printf("\n"); - list_keyblock(pub_root,0,1,NULL); - } - - - if( !opt.batch - && ( get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA - || no_enc_rsa ) - && !get_parameter( para, pSUBKEYTYPE ) ) + outctrl->pub.stream = iobuf_create (outctrl->pub.fname); + if (!outctrl->pub.stream) { - 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") ); + 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; + xfree (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); + } + } + assert (outctrl->pub.stream); + assert (outctrl->sec.stream); + if (opt.verbose) + { + log_info (_("writing public key to `%s'\n"), outctrl->pub.fname); + if (card) + log_info (_("writing secret key stub to `%s'\n"), + outctrl->sec.fname); + else + log_info (_("writing secret key to `%s'\n"), outctrl->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 KEY packet. + */ + pub_root = make_comment_node ("#"); + delete_kbnode (pub_root); + sec_root = make_comment_node ("#"); + delete_kbnode (sec_root); + if (!card) + { + 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)); + } + else + { + rc = gen_card_key (PUBKEY_ALGO_RSA, 1, pub_root, sec_root, + get_parameter_u32 (para, pKEYEXPIRE)); + if (!rc) + { + sk = sec_root->next->pkt->pkt.secret_key; + assert (sk); + } + + } + + if (!rc && (revkey = get_parameter_revkey (para, pREVOKER))) + { + rc = write_direct_sig (pub_root, pub_root, sk, revkey); + if (!rc) + write_direct_sig (sec_root, pub_root, sk, revkey); + } + + 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, + get_parameter_uint (para, pKEYUSAGE)); + if (!rc) + rc = write_selfsig (sec_root, pub_root, sk, + get_parameter_uint (para, pKEYUSAGE)); + } + + 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, + get_parameter_uint (para, pSUBKEYUSAGE)); + if (!rc) + rc = write_keybinding (sec_root, pub_root, sk, + get_parameter_uint (para, pSUBKEYUSAGE)); + did_sub = 1; + } + + + 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_strerror (rc)); + if (!rc) + { + rc = write_keyblock (outctrl->sec.stream, sec_root); + if (rc) + log_error ("can't write secret key: %s\n", gpg_strerror (rc)); + } + + } + else if (!rc) + { /* write to the standard keyrings */ + KEYDB_HANDLE pub_hd = keydb_new (0); + KEYDB_HANDLE sec_hd = keydb_new (1); + + /* FIXME: we may have to create the keyring first */ + rc = keydb_locate_writable (pub_hd, NULL); + if (rc) + log_error (_("no writable public keyring found: %s\n"), + gpg_strerror (rc)); + + if (!rc) + { + rc = keydb_locate_writable (sec_hd, NULL); + if (rc) + log_error (_("no writable secret keyring found: %s\n"), + gpg_strerror (rc)); + } + + if (!rc && opt.verbose) + { + log_info (_("writing public key to `%s'\n"), + keydb_get_resource_name (pub_hd)); + if (card) + log_info (_("writing secret key stub to `%s'\n"), + keydb_get_resource_name (sec_hd)); + else + log_info (_("writing secret key to `%s'\n"), + keydb_get_resource_name (sec_hd)); + } + + if (!rc) + { + rc = keydb_insert_keyblock (pub_hd, pub_root); + if (rc) + log_error (_("error writing public keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), gpg_strerror (rc)); + } + + if (!rc) + { + rc = keydb_insert_keyblock (sec_hd, sec_root); + if (rc) + log_error (_("error writing secret keyring `%s': %s\n"), + keydb_get_resource_name (pub_hd), gpg_strerror (rc)); + } + + keydb_release (pub_hd); + keydb_release (sec_hd); + + if (!rc) + { + int no_enc_rsa = + get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_RSA + && get_parameter_uint (para, pKEYUSAGE) + && !(get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC); + PKT_public_key *pk = find_kbnode (pub_root, + PKT_PUBLIC_KEY)->pkt->pkt. + public_key; + + update_ownertrust (pk, + ((get_ownertrust (pk) & ~TRUST_MASK) + | TRUST_ULTIMATE)); + + if (!opt.batch) + { + tty_printf (_("public and secret key created and signed.\n")); + tty_printf (_("key marked as ultimately trusted.\n")); + tty_printf ("\n"); + list_keyblock (pub_root, 0, 1, NULL); + } + + + if (!opt.batch + && (get_parameter_algo (para, pKEYTYPE) == PUBKEY_ALGO_DSA + || no_enc_rsa) && !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( rc ) { - if( opt.batch ) - log_error("key generation failed: %s\n", gpg_strerror (rc) ); - else - tty_printf(_("Key generation failed: %s\n"), gpg_strerror (rc) ); + if (rc) + { + if (opt.batch) + log_error ("key generation failed: %s\n", gpg_strerror (rc)); + else + tty_printf (_("Key generation failed: %s\n"), gpg_strerror (rc)); } - else { - PKT_public_key *pk = find_kbnode (pub_root, - PKT_PUBLIC_KEY)->pkt->pkt.public_key; - print_status_key_created (did_sub? 'B':'P', pk); + else + { + PKT_public_key *pk = find_kbnode (pub_root, + PKT_PUBLIC_KEY)->pkt->pkt.public_key; + print_status_key_created (did_sub ? 'B' : 'P', pk); } - release_kbnode( pub_root ); - release_kbnode( sec_root ); - if( sk ) /* the unprotected secret key */ - free_secret_key(sk); + + release_kbnode (pub_root); + release_kbnode (sec_root); + if (sk && !card) /* The unprotected secret key unless we have */ + free_secret_key (sk); /* shallow copy in card mode. */ } @@ -2534,3 +2611,268 @@ write_keyblock( iobuf_t out, KBNODE node ) } return 0; } + + +static void +show_sha1_fpr (const unsigned char *fpr) +{ + int i; + + if (fpr) + { + for (i=0; i < 20 ; i+=2, fpr += 2 ) + { + if (i == 10 ) + tty_printf (" "); + tty_printf (" %02X%02X", *fpr, fpr[1]); + } + } + else + tty_printf ("[none]"); + tty_printf ("\n"); +} + +static void +show_smartcard (struct agent_card_info_s *info) +{ + /* FIXME: Sanitize what we show. */ + tty_printf ("Name of cardholder: %s\n", + info->disp_name && *info->disp_name? info->disp_name + : "[not set]"); + tty_printf ("URL of public key : %s\n", + info->pubkey_url && *info->pubkey_url? info->pubkey_url + : "[not set]"); + tty_printf ("Signature key ....: "); + show_sha1_fpr (info->fpr1valid? info->fpr1:NULL); + tty_printf ("Encryption key....: "); + show_sha1_fpr (info->fpr2valid? info->fpr2:NULL); + tty_printf ("Authentication key: "); + show_sha1_fpr (info->fpr3valid? info->fpr3:NULL); +} + + +static char * +smartcard_get_one_name (const char *prompt1, const char *prompt2) +{ + char *name; + int i; + + for (;;) + { + name = cpr_get (prompt1, prompt2); + if (!name) + return NULL; + trim_spaces (name); + cpr_kill_prompt (); + for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++) + ; + + /* The name must be in Latin-1 and not UTF-8 - lacking the code + to ensure this we restrict it to ASCII. */ + if (name[i]) + tty_printf (_("Error: Only plain ASCII is currently allowed.\n")); + else if (strchr (name, '<')) + tty_printf (_("Error: The \"<\" character may not be used.\n")); + else if (strstr (name, " ")) + tty_printf (_("Error: Double spaces are not allowed.\n")); + else + return name; + xfree (name); + } +} + +static int +smartcard_change_name (const char *current_name) +{ + char *surname = NULL, *givenname = NULL; + char *isoname, *p; + int rc; + + surname = smartcard_get_one_name ("keygen.smartcard.surname", + _("Cardholder's surname: ")); + givenname = smartcard_get_one_name ("keygen.smartcard.givenname", + _("Cardholder's given name: ")); + if (!surname || !givenname || (!*surname && !*givenname)) + { + xfree (surname); + xfree (givenname); + return -1; /*canceled*/ + } + + isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1); + strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname); + xfree (surname); + xfree (givenname); + for (p=isoname; *p; p++) + if (*p == ' ') + *p = '<'; + + log_debug ("setting Name to `%s'\n", isoname); + rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) ); + if (rc) + log_error ("error setting Name: %s\n", gpg_strerror (rc)); + + return rc; +} + + +/* Check whether a smartcatrd is available and alow to select it as + the target for key generation. + + Return values: -1 = Quit generation + 0 = No smartcard + 1 = Generate primary key + 2 = generate subkey +*/ +static int +check_smartcard (void) +{ + struct agent_card_info_s info; + int rc; + + rc = agent_learn (&info); + if (rc) + { + tty_printf (_("OpenPGP card not available: %s\n"), + gpg_strerror (rc)); + return 0; + } + + tty_printf (_("OpenPGP card with serial number %s detected\n"), "xxx"); + + + for (;;) + { + char *answer; + int reread = 0; + + tty_printf ("\n"); + show_smartcard (&info); + + tty_printf ("\n" + "N - change cardholder name\n" + "U - change public key URL\n" + "1 - generate signature key\n" + "2 - generate encryption key\n" + "3 - generate authentication key\n" + "Q - quit\n" + "\n"); + + answer = cpr_get("keygen.smartcard.menu",_("Your selection? ")); + cpr_kill_prompt(); + if (strlen (answer) != 1) + continue; + + rc = 0; + if ( *answer == 'N' || *answer == 'n') + { + if (!smartcard_change_name (info.disp_name)) + reread = 1; + } + else if ( *answer == 'U' || *answer == 'u') + { + } + else if ( *answer == '1' || *answer == '2') + { + rc = *answer - '0'; + break; + } + else if ( *answer == '3' ) + { + tty_printf (_("Generation of authentication key" + " not yet implemented\n")); + } + else if ( *answer == 'q' || *answer == 'Q') + { + rc = -1; + break; + } + + if (reread) + { + xfree (info.disp_name); info.disp_name = NULL; + xfree (info.pubkey_url); info.pubkey_url = NULL; + rc = agent_learn (&info); + if (rc) + { + tty_printf (_("OpenPGP card not anymore available: %s\n"), + gpg_strerror (rc)); + g10_exit (1); + } + reread = 0; + } + } + + xfree (info.disp_name); + xfree (info.pubkey_url); + + return rc; +} + + + +static int +gen_card_key (int algo, int keyno, KBNODE pub_root, KBNODE sec_root, + u32 expireval) +{ + int rc; + struct agent_card_genkey_s info; + PACKET *pkt; + PKT_secret_key *sk; + PKT_public_key *pk; + + assert (algo == PUBKEY_ALGO_RSA); + + rc = agent_scd_genkey (&info, keyno, 0); + if (gpg_err_code (rc) == GPG_ERR_EEXIST) + { + tty_printf ("\n"); + log_error ("WARNING: key does already exists!\n"); + tty_printf ("\n"); + if ( cpr_get_answer_is_yes( "keygen.card.replace_key", + _("Replace existing key? "))) + rc = agent_scd_genkey (&info, keyno, 1); + } + + if (rc) + { + log_error ("key generation failed: %s\n", gpg_strerror (rc)); + return rc; + } + if ( !info.n || !info.e ) + { + log_error ("communication error with SCD\n"); + gcry_mpi_release (info.n); + gcry_mpi_release (info.e); + return gpg_error (GPG_ERR_GENERAL); + } + + + pk = xcalloc (1, sizeof *pk ); + sk = xcalloc (1, sizeof *sk ); + sk->timestamp = pk->timestamp = make_timestamp(); + sk->version = pk->version = 4; + if (expireval) + sk->expiredate = pk->expiredate = pk->timestamp + expireval; + sk->pubkey_algo = pk->pubkey_algo = algo; + pk->pkey[0] = info.n; + pk->pkey[1] = info.e; + sk->skey[0] = mpi_copy (pk->pkey[0]); + sk->skey[1] = mpi_copy (pk->pkey[1]); + sk->skey[2] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10); + sk->is_protected = 1; + sk->protect.s2k.mode = 1002; + + pkt = xcalloc (1,sizeof *pkt); + pkt->pkttype = keyno == 1 ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; + pkt->pkt.public_key = pk; + add_kbnode(pub_root, new_kbnode( pkt )); + + pkt = xcalloc (1,sizeof *pkt); + pkt->pkttype = keyno == 1 ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY; + pkt->pkt.secret_key = sk; + add_kbnode(sec_root, new_kbnode( pkt )); + + return 0; +} + diff --git a/g10/parse-packet.c b/g10/parse-packet.c index c1a716b1d..fc9e2559f 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1634,6 +1634,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, break; case 1001: if( list_mode ) printf( "\tgnu-dummy S2K" ); break; + case 1002: if (list_mode) printf("\tgnu-divert-to-card S2K"); + break; default: if( list_mode ) printf( "\tunknown %sS2K %d\n", @@ -1669,6 +1671,31 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, printf("\tprotect count: %lu\n", (ulong)sk->protect.s2k.count); } + else if( sk->protect.s2k.mode == 1002 ) { + size_t snlen; + /* Read the serial number. */ + if (pktlen < 1) { + rc = GPG_ERR_INV_PACKET; + goto leave; + } + snlen = iobuf_get (inp); + pktlen--; + if (pktlen < snlen || snlen == -1) { + rc = GPG_ERR_INV_PACKET; + goto leave; + } + + if( list_mode ) { + printf("\tserial-number: "); + for (;snlen; snlen--) + printf ("%02X", (unsigned int)iobuf_get_noeof (inp)); + putchar ('\n'); + } + else { + for (;snlen; snlen--) + iobuf_get_noeof (inp); + } + } } /* Note that a sk->protect.algo > 110 is illegal, but I'm not erroring on it here as otherwise there would be no @@ -1698,6 +1725,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, } if( sk->protect.s2k.mode == 1001 ) sk->protect.ivlen = 0; + else if( sk->protect.s2k.mode == 1002 ) + sk->protect.ivlen = 0; if( pktlen < sk->protect.ivlen ) { rc = GPG_ERR_INV_PACKET; @@ -1719,7 +1748,8 @@ parse_key( iobuf_t inp, int pkttype, unsigned long pktlen, * If the user is so careless, not to protect his secret key, * we can assume, that he operates an open system :=(. * So we put the key into secure memory when we unprotect it. */ - if( sk->protect.s2k.mode == 1001 ) { + if( sk->protect.s2k.mode == 1001 + || sk->protect.s2k.mode == 1002 ) { /* better set some dummy stuff here */ sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup ("dummydata"), 10); pktlen = 0; diff --git a/g10/passphrase.c b/g10/passphrase.c index 41cd31f91..d00340109 100644 --- a/g10/passphrase.c +++ b/g10/passphrase.c @@ -794,7 +794,7 @@ agent_get_passphrase ( u32 *keyid, int mode, const char *tryagain_text, tryagain_text = _(tryagain_text); /* We allocate 2 time the needed space for atext so that there - is nenough space for escaping */ + is enough space for escaping */ line = xmalloc (15 + 46 + 3*strlen (tryagain_text) + 3*strlen (atext) + 2); strcpy (line, "GET_PASSPHRASE "); diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c index 573c78f7a..dff463c53 100644 --- a/g10/seckey-cert.c +++ b/g10/seckey-cert.c @@ -253,6 +253,9 @@ check_secret_key( PKT_secret_key *sk, int n ) int rc = gpg_error (GPG_ERR_BAD_PASSPHRASE); int i,mode; + if (sk && sk->is_protected && sk->protect.s2k.mode == 1002) + return 0; /* Let the scdaemon handle it. */ + if(n<0) { n=abs(n); diff --git a/g10/seskey.c b/g10/seskey.c index 9eeed2c74..5d0490cdf 100644 --- a/g10/seskey.c +++ b/g10/seskey.c @@ -231,3 +231,8 @@ encode_md_value (int pubkey_algo, gcry_md_hd_t md, int hash_algo, return frame; } + + + + + diff --git a/g10/sign.c b/g10/sign.c index fa9e9ea3f..86159a4a1 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -249,74 +249,94 @@ static int do_sign( PKT_secret_key *sk, PKT_signature *sig, MD_HANDLE md, int digest_algo ) { - gcry_mpi_t frame; - byte *dp; - int rc; + gcry_mpi_t frame; + byte *dp; + int rc; - if( sk->timestamp > sig->timestamp ) { - ulong d = sk->timestamp - sig->timestamp; - log_info( d==1 ? _("key has been created %lu second " - "in future (time warp or clock problem)\n") - : _("key has been created %lu seconds " - "in future (time warp or clock problem)\n"), d ); - if( !opt.ignore_time_conflict ) - return GPG_ERR_TIME_CONFLICT; - } + if( sk->timestamp > sig->timestamp ) { + ulong d = sk->timestamp - sig->timestamp; + log_info( d==1 ? _("key has been created %lu second " + "in future (time warp or clock problem)\n") + : _("key has been created %lu seconds " + "in future (time warp or clock problem)\n"), d ); + if( !opt.ignore_time_conflict ) + return GPG_ERR_TIME_CONFLICT; + } + print_pubkey_algo_note(sk->pubkey_algo); - print_pubkey_algo_note(sk->pubkey_algo); + if( !digest_algo ) + digest_algo = gcry_md_get_algo(md); - if( !digest_algo ) - digest_algo = gcry_md_get_algo(md); + print_digest_algo_note( digest_algo ); + dp = gcry_md_read ( md, digest_algo ); + sig->digest_algo = digest_algo; + sig->digest_start[0] = dp[0]; + sig->digest_start[1] = dp[1]; + if (sk->is_protected && sk->protect.s2k.mode == 1002) + { /* FIXME: Note that we do only support RSA for now. */ + char *rbuf; + size_t rbuflen; - print_digest_algo_note( digest_algo ); - dp = gcry_md_read ( md, digest_algo ); - sig->digest_algo = digest_algo; - sig->digest_start[0] = dp[0]; - sig->digest_start[1] = dp[1]; - frame = encode_md_value( sk->pubkey_algo, md, - digest_algo, mpi_get_nbits(sk->skey[0]), 0 ); - if (!frame) - return GPG_ERR_GENERAL; - rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); - gcry_mpi_release (frame); - if (!rc && !opt.no_sig_create_check) { - /* check that the signature verification worked and nothing is - * fooling us e.g. by a bug in the signature create - * code or by deliberately introduced faults. */ - PKT_public_key *pk = xcalloc (1,sizeof *pk); - - if( get_pubkey( pk, sig->keyid ) ) - rc = GPG_ERR_NO_PUBKEY; - else { - frame = encode_md_value (pk->pubkey_algo, md, - sig->digest_algo, - mpi_get_nbits(pk->pkey[0]), 0); - if (!frame) - rc = GPG_ERR_GENERAL; - else - rc = pk_verify (pk->pubkey_algo, frame, - sig->data, pk->pkey); - gcry_mpi_release (frame); + /* FIXME: We need to pass the correct keyid or better the + fingerprint to the scdaemon. */ + rc = agent_scd_pksign ("nokeyid", digest_algo, + gcry_md_read (md, digest_algo), + gcry_md_get_algo_dlen (digest_algo), + &rbuf, &rbuflen); + if (!rc) + { + unsigned int nbytes = rbuflen; + if (gcry_mpi_scan (&sig->data[0], GCRYMPI_FMT_USG, rbuf, &nbytes )) + BUG (); } - if (rc) - log_error (_("checking created signature failed: %s\n"), - gpg_strerror (rc)); - free_public_key (pk); } - if( rc ) - log_error(_("signing failed: %s\n"), gpg_strerror (rc) ); + else + { + frame = encode_md_value( sk->pubkey_algo, md, + digest_algo, mpi_get_nbits(sk->skey[0]), 0 ); + if (!frame) + return GPG_ERR_GENERAL; + rc = pk_sign( sk->pubkey_algo, sig->data, frame, sk->skey ); + gcry_mpi_release (frame); + } + if (!rc && !opt.no_sig_create_check) { + /* check that the signature verification worked and nothing is + * fooling us e.g. by a bug in the signature create + * code or by deliberately introduced faults. */ + PKT_public_key *pk = xcalloc (1,sizeof *pk); + + if( get_pubkey( pk, sig->keyid ) ) + rc = GPG_ERR_NO_PUBKEY; else { - if( opt.verbose ) { - char *ustr = get_user_id_string_printable (sig->keyid); - log_info(_("%s/%s signature from: \"%s\"\n"), - gcry_pk_algo_name (sk->pubkey_algo), - gcry_md_algo_name (sig->digest_algo), - ustr ); - xfree (ustr); - } + frame = encode_md_value (pk->pubkey_algo, md, + sig->digest_algo, + mpi_get_nbits(pk->pkey[0]), 0); + if (!frame) + rc = GPG_ERR_GENERAL; + else + rc = pk_verify (pk->pubkey_algo, frame, + sig->data, pk->pkey); + gcry_mpi_release (frame); } - return rc; + if (rc) + log_error (_("checking created signature failed: %s\n"), + gpg_strerror (rc)); + free_public_key (pk); + } + if( rc ) + log_error(_("signing failed: %s\n"), gpg_strerror (rc) ); + else { + if( opt.verbose ) { + char *ustr = get_user_id_string_printable (sig->keyid); + log_info(_("%s/%s signature from: \"%s\"\n"), + gcry_pk_algo_name (sk->pubkey_algo), + gcry_md_algo_name (sig->digest_algo), + ustr ); + xfree (ustr); + } + } + return rc; } @@ -1170,7 +1190,7 @@ sign_symencrypt_file (const char *fname, STRLIST locusr) * SIGVERSION gives the minimal required signature packet version; * this is needed so that special properties like local sign are not * applied (actually: dropped) when a v3 key is used. TIMESTAMP is - * the timestamp to use for the signature. 0 means "now" */ + * the timestamp to use for the signature. 0 means "now". */ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, diff --git a/g10/tdbio.c b/g10/tdbio.c index d8af2ef8a..39239be20 100644 --- a/g10/tdbio.c +++ b/g10/tdbio.c @@ -372,10 +372,11 @@ tdbio_end_transaction() else is_locked = 1; } - block_all_signals(); +#warning block_all_signals is not yet available in ../common/signals.c + /* block_all_signals(); */ in_transaction = 0; rc = tdbio_sync(); - unblock_all_signals(); +/* unblock_all_signals(); */ if( !opt.lock_once ) { if( !release_dotlock( lockhandle ) ) is_locked = 0;