diff --git a/NEWS b/NEWS index e1798d6a0..aaca93680 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +Noteworthy changes in version 1.9.93 +------------------------------------------------- + + Noteworthy changes in version 1.9.92 (2006-10-11) ------------------------------------------------- diff --git a/agent/command.c b/agent/command.c index a2634f7d5..cebbf9a2f 100644 --- a/agent/command.c +++ b/agent/command.c @@ -236,7 +236,7 @@ parse_hexstring (assuan_context_t ctx, const char *string, size_t *len) } /* Parse the keygrip in STRING into the provided buffer BUF. BUF must - provide space for 20 bytes. BUF is not changed if the fucntions + provide space for 20 bytes. BUF is not changed if the function returns an error. */ static int parse_keygrip (assuan_context_t ctx, const char *string, unsigned char *buf) diff --git a/configure.ac b/configure.ac index 7c2bf75c1..f93ab7260 100644 --- a/configure.ac +++ b/configure.ac @@ -26,8 +26,8 @@ min_automake_version="1.9.3" # Remember to change the version number immediately *after* a release. # Set my_issvn to "yes" for non-released code. Remember to run an # "svn up" and "autogen.sh" right before creating a distribution. -m4_define([my_version], [1.9.92]) -m4_define([my_issvn], [no]) +m4_define([my_version], [1.9.93]) +m4_define([my_issvn], [yes]) m4_define([svn_revision], m4_esyscmd([echo -n $((svn info 2>/dev/null \ diff --git a/scd/ChangeLog b/scd/ChangeLog index 9f6a0f5c5..41a89b8b7 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,7 @@ +2006-10-11 Werner Koch + + * app-openpgp.c (do_sign): Redirect to do_auth for OpenPGP.3. + 2006-10-06 Werner Koch * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 4de465ee1..466f37c57 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -142,6 +142,11 @@ struct app_local_s { static unsigned long convert_sig_counter_value (const unsigned char *value, size_t valuelen); static unsigned long get_sig_counter (app_t app); +static gpg_error_t do_auth (app_t app, const char *keyidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const void *indata, size_t indatalen, + unsigned char **outdata, size_t *outdatalen); @@ -2088,7 +2093,11 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno) Note that this function may return the error code GPG_ERR_WRONG_CARD to indicate that the card currently present does not match the one required for the requested action (e.g. the - serial number does not match). */ + serial number does not match). + + As a special feature a KEYIDSTR of "OPENPGP.3" redirects the + operation to the auth command. +*/ static gpg_error_t do_sign (app_t app, const char *keyidstr, int hashalgo, gpg_error_t (*pincb)(void*, const char *, char **), @@ -2109,6 +2118,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, int n; const char *fpr = NULL; unsigned long sigcount; + int use_auth = 0; if (!keyidstr || !*keyidstr) return gpg_error (GPG_ERR_INV_VALUE); @@ -2136,6 +2146,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, /* Check whether an OpenPGP card of any version has been requested. */ if (!strcmp (keyidstr, "OPENPGP.1")) ; + else if (!strcmp (keyidstr, "OPENPGP.3")) + use_auth = 1; else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12)) return gpg_error (GPG_ERR_INV_ID); else @@ -2178,6 +2190,14 @@ do_sign (app_t app, const char *keyidstr, int hashalgo, return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM); memcpy (data+15, indata, indatalen); + if (use_auth) + { + /* This is a hack to redirect to the internal authenticate command. */ + return do_auth (app, "OPENPGP.3", pincb, pincb_arg, + data, 35, + outdata, outdatalen); + } + sigcount = get_sig_counter (app); log_info (_("signatures created so far: %lu\n"), sigcount); diff --git a/sm/ChangeLog b/sm/ChangeLog index 74c5c4302..8bf306e54 100644 --- a/sm/ChangeLog +++ b/sm/ChangeLog @@ -1,3 +1,10 @@ +2006-10-11 Werner Koch + + * certreqgen.c (proc_parameters, create_request): Allow for + creation directly from a card. + * call-agent.c (gpgsm_agent_readkey): New arg FROMCARD. + (gpgsm_scd_pksign): New. + 2006-10-06 Werner Koch * Makefile.am (AM_CFLAGS): Use PTH version of libassuan. diff --git a/sm/call-agent.c b/sm/call-agent.c index 35ad1b83b..47f53041d 100644 --- a/sm/call-agent.c +++ b/sm/call-agent.c @@ -271,6 +271,84 @@ gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, } +/* Call the scdaemon to do a sign operation using the key identified by + the hex string KEYID. */ +int +gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, + unsigned char *digest, size_t digestlen, int digestalgo, + unsigned char **r_buf, size_t *r_buflen ) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + membuf_t data; + size_t len; + const char *hashopt; + unsigned char *sigbuf; + size_t sigbuflen; + + *r_buf = NULL; + + switch(digestalgo) + { + case GCRY_MD_SHA1: hashopt = "--hash=sha1"; break; + case GCRY_MD_RMD160:hashopt = "--hash=rmd160"; break; + case GCRY_MD_MD5: hashopt = "--hash=md5"; break; + case GCRY_MD_SHA256:hashopt = "--hash=sha256"; break; + default: + return gpg_error (GPG_ERR_DIGEST_ALGO); + } + + rc = start_agent (ctrl); + if (rc) + return rc; + + if (digestlen*2 + 50 > DIM(line)) + return gpg_error (GPG_ERR_GENERAL); + + p = stpcpy (line, "SCD SETDATA " ); + for (i=0; i < digestlen ; i++, p += 2 ) + sprintf (p, "%02X", digest[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 %s", hashopt, 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; + } + sigbuf = get_membuf (&data, &sigbuflen); + + /* Create an S-expression from it which is formatted like this: + "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever + creates non-RSA keys we need to change things. */ + *r_buflen = 21 + 11 + sigbuflen + 4; + p = xtrymalloc (*r_buflen); + *r_buf = (unsigned char*)p; + if (!p) + { + xfree (sigbuf); + return 0; + } + p = stpcpy (p, "(7:sig-val(3:rsa(1:s" ); + sprintf (p, "%u:", (unsigned int)sigbuflen); + p += strlen (p); + memcpy (p, sigbuf, sigbuflen); + p += sigbuflen; + strcpy (p, ")))"); + xfree (sigbuf); + + assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL)); + return 0; +} + + /* Handle a CIPHERTEXT inquiry. Note, we only send the data, @@ -449,9 +527,12 @@ gpgsm_agent_genkey (ctrl_t ctrl, } -/* Call the agent to read the public key part for a given keygrip. */ +/* Call the agent to read the public key part for a given keygrip. If + FROMCARD is true, the key is directly read from the current + smartcard. In this case HEXKEYGRIP should be the keyID + (e.g. OPENPGP.3). */ int -gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip, +gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey) { int rc; @@ -469,7 +550,8 @@ gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip, if (rc) return rc; - snprintf (line, DIM(line)-1, "READKEY %s", hexkeygrip); + snprintf (line, DIM(line)-1, "%sREADKEY %s", + fromcard? "SCD ":"", hexkeygrip); line[DIM(line)-1] = 0; init_membuf (&data, 1024); diff --git a/sm/certreqgen.c b/sm/certreqgen.c index e1006753e..f0221d3fd 100644 --- a/sm/certreqgen.c +++ b/sm/certreqgen.c @@ -148,6 +148,7 @@ static int proc_parameters (ctrl_t ctrl, struct reqgen_ctrl_s *outctrl); static int create_request (ctrl_t ctrl, struct para_data_s *para, + const char *carddirect, ksba_const_sexp_t public, struct reqgen_ctrl_s *outctrl); @@ -452,15 +453,24 @@ proc_parameters (ctrl_t ctrl, ksba_sexp_t public; int seq; size_t erroff, errlen; + char *cardkeyid = NULL; /* Check that we have all required parameters; */ assert (get_parameter (para, pKEYTYPE, 0)); - /* We can only use RSA for now. There is a with pkcs-10 on how to - use ElGamal because it is expected that a PK algorithm can always - be used for signing. */ + /* We can only use RSA for now. There is a problem with pkcs-10 on + how to use ElGamal because it is expected that a PK algorithm can + always be used for signing. Another problem is that on-card + generated encryption keys may not be used for signing. */ i = get_parameter_algo (para, pKEYTYPE); - if (i < 1 || i != GCRY_PK_RSA ) + if (!i && (s = get_parameter_value (para, pKEYTYPE, 0)) && *s) + { + /* Hack to allow creation of certificates directly from a smart + card. For example: "Key-Type: card:OPENPGP.3". */ + if (!strncmp (s, "card:", 5) && s[5]) + cardkeyid = xtrystrdup (s+5); + } + if ( (i < 1 || i != GCRY_PK_RSA) && !cardkeyid ) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: invalid algorithm\n"), r->lnr); @@ -472,18 +482,22 @@ proc_parameters (ctrl_t ctrl, nbits = 1024; else nbits = get_parameter_uint (para, pKEYLENGTH); - if (nbits < 1024 || nbits > 4096) + if ((nbits < 1024 || nbits > 4096) && !cardkeyid) { /* The BSI specs dated 2002-11-25 don't allow lengths below 1024. */ r = get_parameter (para, pKEYLENGTH, 0); log_error (_("line %d: invalid key length %u (valid are %d to %d)\n"), r->lnr, nbits, 1024, 4096); + xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } /* Check the usage. */ if (parse_parameter_usage (para, pKEYUSAGE)) - return gpg_error (GPG_ERR_INV_PARAMETER); + { + xfree (cardkeyid); + return gpg_error (GPG_ERR_INV_PARAMETER); + } /* Check that there is a subject name and that this DN fits our requirements. */ @@ -491,6 +505,7 @@ proc_parameters (ctrl_t ctrl, { r = get_parameter (para, pNAMEDN, 0); log_error (_("line %d: no subject name given\n"), r->lnr); + xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } err = ksba_dn_teststr (s, 0, &erroff, &errlen); @@ -504,6 +519,7 @@ proc_parameters (ctrl_t ctrl, log_error (_("line %d: invalid subject name `%s' at pos %d\n"), r->lnr, s, erroff); + xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } @@ -518,19 +534,32 @@ proc_parameters (ctrl_t ctrl, { r = get_parameter (para, pNAMEEMAIL, seq); log_error (_("line %d: not a valid email address\n"), r->lnr); + xfree (cardkeyid); return gpg_error (GPG_ERR_INV_PARAMETER); } } - s = get_parameter_value (para, pKEYGRIP, 0); - if (s) /* Use existing key. */ + if (cardkeyid) /* Take the key from the current smart card. */ { - rc = gpgsm_agent_readkey (ctrl, s, &public); + rc = gpgsm_agent_readkey (ctrl, 1, cardkeyid, &public); + if (rc) + { + r = get_parameter (para, pKEYTYPE, 0); + log_error (_("line %d: error reading key `%s' from card: %s\n"), + r->lnr, cardkeyid, gpg_strerror (rc)); + xfree (cardkeyid); + return rc; + } + } + else if ((s=get_parameter_value (para, pKEYGRIP, 0))) /* Use existing key.*/ + { + rc = gpgsm_agent_readkey (ctrl, 0, s, &public); if (rc) { r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: error getting key by keygrip `%s': %s\n"), r->lnr, s, gpg_strerror (rc)); + xfree (cardkeyid); return rc; } } @@ -546,12 +575,14 @@ proc_parameters (ctrl_t ctrl, r = get_parameter (para, pKEYTYPE, 0); log_error (_("line %d: key generation failed: %s\n"), r->lnr, gpg_strerror (rc)); + xfree (cardkeyid); return rc; } } - rc = create_request (ctrl, para, public, outctrl); + rc = create_request (ctrl, para, cardkeyid, public, outctrl); xfree (public); + xfree (cardkeyid); return rc; } @@ -560,8 +591,10 @@ proc_parameters (ctrl_t ctrl, /* Parameters are checked, the key pair has been created. Now generate the request and write it out */ static int -create_request (ctrl_t ctrl, - struct para_data_s *para, ksba_const_sexp_t public, +create_request (ctrl_t ctrl, + struct para_data_s *para, + const char *carddirect, + ksba_const_sexp_t public, struct reqgen_ctrl_s *outctrl) { ksba_certreq_t cr; @@ -758,11 +791,18 @@ create_request (ctrl_t ctrl, for (n=0; n < 20; n++) sprintf (hexgrip+n*2, "%02X", grip[n]); - rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL, - gcry_md_read(md, GCRY_MD_SHA1), - gcry_md_get_algo_dlen (GCRY_MD_SHA1), - GCRY_MD_SHA1, - &sigval, &siglen); + if (carddirect) + rc = gpgsm_scd_pksign (ctrl, carddirect, NULL, + gcry_md_read(md, GCRY_MD_SHA1), + gcry_md_get_algo_dlen (GCRY_MD_SHA1), + GCRY_MD_SHA1, + &sigval, &siglen); + else + rc = gpgsm_agent_pksign (ctrl, hexgrip, NULL, + gcry_md_read(md, GCRY_MD_SHA1), + gcry_md_get_algo_dlen (GCRY_MD_SHA1), + GCRY_MD_SHA1, + &sigval, &siglen); if (rc) { log_error ("signing failed: %s\n", gpg_strerror (rc)); diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 541783dd7..d92bf5923 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -322,12 +322,15 @@ int gpgsm_agent_pksign (ctrl_t ctrl, const char *keygrip, const char *desc, size_t digestlen, int digestalgo, unsigned char **r_buf, size_t *r_buflen); +int gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc, + unsigned char *digest, size_t digestlen, int digestalgo, + unsigned char **r_buf, size_t *r_buflen); int gpgsm_agent_pkdecrypt (ctrl_t ctrl, const char *keygrip, const char *desc, ksba_const_sexp_t ciphertext, char **r_buf, size_t *r_buflen); int gpgsm_agent_genkey (ctrl_t ctrl, ksba_const_sexp_t keyparms, ksba_sexp_t *r_pubkey); -int gpgsm_agent_readkey (ctrl_t ctrl, const char *hexkeygrip, +int gpgsm_agent_readkey (ctrl_t ctrl, int fromcard, const char *hexkeygrip, ksba_sexp_t *r_pubkey); int gpgsm_agent_istrusted (ctrl_t ctrl, ksba_cert_t cert, struct rootca_flags_s *rootca_flags); diff --git a/tools/ChangeLog b/tools/ChangeLog index de22a2ec6..5ed381049 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,7 @@ +2006-10-11 Werner Koch + + * gpgsm-gencert.sh: Allow generation of card keys. + 2006-10-08 Werner Koch * Makefile.am (gpgkey2ssh_LDADD): Add LIBINTL. Suggested by diff --git a/tools/gpgsm-gencert.sh b/tools/gpgsm-gencert.sh index 3949f2361..19e961f03 100755 --- a/tools/gpgsm-gencert.sh +++ b/tools/gpgsm-gencert.sh @@ -84,19 +84,27 @@ query_user_menu() echo "You selected: $ANSWER" >&2 } -query_user_menu "Key type" "RSA" "existing key" -if [ "$ANSWER" = "existing key" ]; then - # User requested to use an existing key; need to set some dummy defaults - KEY_TYPE=RSA - KEY_LENGTH=1024 - query_user "Keygrip " - KEY_GRIP=$ANSWER -else - KEY_TYPE=$ANSWER - query_user_menu "Key length" "1024" "2048" - KEY_LENGTH=$ANSWER - KEY_GRIP= -fi +query_user_menu "Key type" "RSA" "existing key" "OPENPGP.1" "OPENPGP.3" +case "$ANSWER" in + RSA) + KEY_TYPE=$ANSWER + query_user_menu "Key length" "1024" "2048" + KEY_LENGTH=$ANSWER + KEY_GRIP= + ;; + existing*) + # User requested to use an existing key; need to set some dummy defaults + KEY_TYPE=RSA + KEY_LENGTH=1024 + query_user "Keygrip " + KEY_GRIP=$ANSWER + ;; + *) + KEY_TYPE="card:$ANSWER" + KEY_LENGTH= + KEY_GRIP= + ;; +esac query_user_menu "Key usage" "sign, encrypt" "sign" "encrypt" @@ -162,7 +170,7 @@ query_user_menu "Really create such a CSR?" "yes" "no" echo -e "$ASSUAN_COMMANDS" | \ - gpgsm --no-log-file --debug-level none --debug-none \ + gpgsm --no-log-file --debug-level none --debug-none \ --server 4< "$file_parameter" 5>"$outfile" >/dev/null cat "$outfile"