diff --git a/agent/ChangeLog b/agent/ChangeLog index 72d2ba8b0..94fb00241 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,15 @@ +2002-03-05 Werner Koch + + * call-scd.c (inq_needpin): New. + (agent_card_pksign): Add getpin_cb args. + +2002-03-04 Werner Koch + + * pksign.c (agent_pksign): Changed how the diversion is done. + * divert-scd.c (divert_pksign): Change interface and implemented it. + (encode_md_for_card): New. + * call-scd.c (agent_card_pksign): New. + 2002-02-28 Werner Koch * pksign.c (agent_pksign): Detect whether a Smartcard is to be diff --git a/agent/agent.h b/agent/agent.h index 56e89f019..643ed60a1 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -151,14 +151,19 @@ int agent_marktrusted (const char *name, const char *fpr, int flag); /*-- divert-scd.c --*/ -int divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash, - const char *shadow_info); +int divert_pksign (const unsigned char *digest, size_t digestlen, int algo, + const char *shadow_info, unsigned char **r_sig); int divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher, const char *shadow_info); /*-- call-scd.c --*/ int agent_card_learn (void); int agent_card_serialno (char **r_serialno); +int agent_card_pksign (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen); #endif /*AGENT_H*/ diff --git a/agent/call-scd.c b/agent/call-scd.c index af4d59fbd..6cf53fdfd 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -46,6 +46,80 @@ struct learn_parm_s { char *buffer; }; +struct inq_needpin_s { + ASSUAN_CONTEXT ctx; + int (*getpin_cb)(void *, const char *, char*, size_t); + void *getpin_cb_arg; +}; + +struct membuf { + size_t len; + size_t size; + char *buf; + int out_of_core; +}; + + + +/* A simple implemnation of a dynamic buffer. Use init_membuf() to + create a buffer, put_membuf to append bytes and get_membuf to + release and return the buffer. Allocation errors are detected but + only returned at the final get_membuf(), this helps not to clutter + the code with out of core checks. */ + +static void +init_membuf (struct membuf *mb, int initiallen) +{ + mb->len = 0; + mb->size = initiallen; + mb->out_of_core = 0; + mb->buf = xtrymalloc (initiallen); + if (!mb->buf) + mb->out_of_core = 1; +} + +static void +put_membuf (struct membuf *mb, const void *buf, size_t len) +{ + if (mb->out_of_core) + return; + + if (mb->len + len >= mb->size) + { + char *p; + + mb->size += len + 1024; + p = xtryrealloc (mb->buf, mb->size); + if (!p) + { + mb->out_of_core = 1; + return; + } + mb->buf = p; + } + memcpy (mb->buf + mb->len, buf, len); + mb->len += len; +} + +static void * +get_membuf (struct membuf *mb, size_t *len) +{ + char *p; + + if (mb->out_of_core) + { + xfree (mb->buf); + mb->buf = NULL; + return NULL; + } + + p = mb->buf; + *len = mb->len; + mb->buf = NULL; + mb->out_of_core = 1; /* don't allow a reuse */ + return p; +} + @@ -216,6 +290,116 @@ agent_card_serialno (char **r_serialno) return 0; } + +static AssuanError +membuf_data_cb (void *opaque, const void *buffer, size_t length) +{ + struct membuf *data = opaque; + + put_membuf (data, buffer, length); + return 0; +} + +/* Handle the NEEDPIN inquiry. */ +static AssuanError +inq_needpin (void *opaque, const char *line) +{ + struct inq_needpin_s *parm = opaque; + char *pin; + size_t pinlen; + int rc; + + if (!(!strncmp (line, "NEEDPIN", 7) && (line[7] == ' ' || !line[7]))) + { + log_error ("unsupported inquiry `%s'\n", line); + return ASSUAN_Inquire_Unknown; + } + line += 7; + + pinlen = 90; + pin = gcry_malloc_secure (pinlen); + if (!pin) + return ASSUAN_Out_Of_Core; + + rc = parm->getpin_cb (parm->getpin_cb_arg, line, pin, pinlen); + if (rc) + rc = ASSUAN_Canceled; + if (!rc) + rc = assuan_send_data (parm->ctx, pin, pinlen); + xfree (pin); + + return rc; +} +/* Create a signature using the current card */ +int +agent_card_pksign (const char *keyid, + int (*getpin_cb)(void *, const char *, char*, size_t), + void *getpin_cb_arg, + const unsigned char *indata, size_t indatalen, + char **r_buf, size_t *r_buflen) +{ + int rc, i; + char *p, line[ASSUAN_LINELENGTH]; + struct membuf data; + struct inq_needpin_s inqparm; + size_t len; + unsigned char *sigbuf; + size_t sigbuflen; + + *r_buf = NULL; + rc = start_scd (); + if (rc) + return rc; + + if (indatalen*2 + 50 > DIM(line)) + return seterr (General_Error); + + sprintf (line, "SETDATA "); + p = line + strlen (line); + for (i=0; i < indatalen ; i++, p += 2 ) + sprintf (p, "%02X", indata[i]); + rc = assuan_transact (scd_ctx, line, NULL, NULL, NULL, NULL, NULL, NULL); + if (rc) + return map_assuan_err (rc); + + init_membuf (&data, 1024); + inqparm.ctx = scd_ctx; + inqparm.getpin_cb = getpin_cb; + inqparm.getpin_cb_arg = getpin_cb_arg; + snprintf (line, DIM(line)-1, "PKSIGN %s", keyid); + line[DIM(line)-1] = 0; + rc = assuan_transact (scd_ctx, line, + membuf_data_cb, &data, + inq_needpin, &inqparm, + NULL, NULL); + if (rc) + { + xfree (get_membuf (&data, &len)); + return map_assuan_err (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)))" */ + *r_buflen = 21 + 11 + sigbuflen + 4; + *r_buf = xtrymalloc (*r_buflen); + if (!*r_buf) + { + xfree (*r_buf); + return GNUPG_Out_Of_Core; + } + p = stpcpy (*r_buf, "(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; +} + diff --git a/agent/divert-scd.c b/agent/divert-scd.c index 0dc9c121f..d938d2618 100644 --- a/agent/divert-scd.c +++ b/agent/divert-scd.c @@ -126,20 +126,135 @@ ask_for_card (const unsigned char *shadow_info, char **r_kid) } +/* fixme: this should be moved to libgcrypt and only be used if the + smartcard does not support pkcs-1 itself */ +static int +encode_md_for_card (const unsigned char *digest, size_t digestlen, int algo, + unsigned int nbits, unsigned char **r_val, size_t *r_len) +{ + int nframe = (nbits+7) / 8; + byte *frame; + int i, n; + byte asn[100]; + size_t asnlen; + + asnlen = DIM(asn); + if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) + { + log_error ("no object identifier for algo %d\n", algo); + return GNUPG_Internal_Error; + } + + if (digestlen + asnlen + 4 > nframe ) + { + log_error ("can't encode a %d bit MD into a %d bits frame\n", + (int)(digestlen*8), (int)nbits); + return GNUPG_Internal_Error; + } + + /* We encode the MD in this way: + * + * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes) + * + * PAD consists of FF bytes. + */ + frame = xtrymalloc (nframe); + if (!frame) + return GNUPG_Out_Of_Core; + n = 0; + frame[n++] = 0; + frame[n++] = 1; /* block type */ + i = nframe - digestlen - asnlen -3 ; + assert ( i > 1 ); + memset ( frame+n, 0xff, i ); n += i; + frame[n++] = 0; + memcpy ( frame+n, asn, asnlen ); n += asnlen; + memcpy ( frame+n, digest, digestlen ); n += digestlen; + assert ( n == nframe ); + if (DBG_CRYPTO) + log_printhex ("encoded hash:", frame, nframe); + + *r_val = frame; + *r_len = nframe; + return 0; +} + + +/* Callback used to ask for the PIN which should be set into BUF. The + buf has been allocated by the caller and is of size MAXBUF which + includes the terminating null. The function should return an UTF-8 + string with the passphrase, the buffer may optioanlly be padded + with arbitrary characters */ +static int +getpin_cb (void *opaque, const char *info, char *buf, size_t maxbuf) +{ + struct pin_entry_info_s *pi; + int rc; + int tries = 0; + const char *errtext; + + assert (!opaque); + + if (maxbuf < 2) + return GNUPG_Invalid_Value; + + /* FIXME: keep PI and TRIES in OPAQUE */ + pi = gcry_calloc_secure (1, sizeof (*pi) + 100); + pi->max_length = maxbuf-1; + pi->min_digits = 0; /* we want a real passphrase */ + pi->max_digits = 8; + pi->max_tries = 3; + + errtext = NULL; + do + { + rc = agent_askpin (info, errtext, pi); + if (!rc) + { + strncpy (buf, pi->pin, maxbuf-1); + buf[maxbuf-1] = 0; + xfree (pi); + return 0; + } + errtext = pi->min_digits? trans ("Bad PIN") : trans ("Bad Passphrase"); + } + while ((rc == GNUPG_Bad_Passphrase || rc == GNUPG_Bad_PIN) + && tries++ < 3); + xfree (pi); + return rc; +} + + + int -divert_pksign (GCRY_SEXP *s_sig, GCRY_SEXP s_hash, const char *shadow_info) +divert_pksign (const unsigned char *digest, size_t digestlen, int algo, + const char *shadow_info, unsigned char **r_sig) { int rc; char *kid; + size_t siglen; + char *sigval; + unsigned char *data; + size_t ndata; rc = ask_for_card (shadow_info, &kid); if (rc) return rc; - + rc = encode_md_for_card (digest, digestlen, algo, 1024 /* fixme*/, + &data, &ndata); + if (rc) + return rc; + + rc = agent_card_pksign (kid, getpin_cb, NULL, + data, ndata, &sigval, &siglen); + if (!rc) + *r_sig = sigval; + xfree (data); xfree (kid); - return GNUPG_Not_Implemented; + + return rc; } diff --git a/agent/pksign.c b/agent/pksign.c index bdf1ff4f3..7873ce8c4 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -44,7 +44,7 @@ do_encode_md (const unsigned char *digest, size_t digestlen, int algo, asnlen = DIM(asn); if (gcry_md_algo_info (algo, GCRYCTL_GET_ASNOID, asn, &asnlen)) { - log_error ("No object identifier for algo %d\n", algo); + log_error ("no object identifier for algo %d\n", algo); return GNUPG_Internal_Error; } @@ -106,28 +106,37 @@ agent_pksign (CTRL ctrl, FILE *outfp) goto leave; } - /* put the hash into a sexp FIXME: this belongs into libgcrypt/divert-scd.c*/ - rc = do_encode_md (ctrl->digest.value, - ctrl->digest.valuelen, - ctrl->digest.algo, - gcry_pk_get_nbits (s_skey), - &frame); - if (rc) - goto leave; - if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) - BUG (); - if (!s_skey) { /* divert operation to the smartcard */ - rc = divert_pksign (&s_sig, s_hash, shadow_info); + unsigned char *sigbuf; + + rc = divert_pksign (ctrl->digest.value, + ctrl->digest.valuelen, + ctrl->digest.algo, + shadow_info, &sigbuf); if (rc) { log_error ("smartcard signing failed: %s\n", gnupg_strerror (rc)); goto leave; } + len = gcry_sexp_canon_len (sigbuf, 0, NULL, NULL); + assert (len); + buf = sigbuf; } else { /* no smartcard, but a private key */ + + /* put the hash into a sexp */ + rc = do_encode_md (ctrl->digest.value, + ctrl->digest.valuelen, + ctrl->digest.algo, + gcry_pk_get_nbits (s_skey), + &frame); + if (rc) + goto leave; + if ( gcry_sexp_build (&s_hash, NULL, "%m", frame) ) + BUG (); + if (DBG_CRYPTO) { log_debug ("skey: "); @@ -142,19 +151,19 @@ agent_pksign (CTRL ctrl, FILE *outfp) rc = map_gcry_err (rc); goto leave; } - } - if (DBG_CRYPTO) - { - log_debug ("result: "); - gcry_sexp_dump (s_sig); - } + if (DBG_CRYPTO) + { + log_debug ("result: "); + gcry_sexp_dump (s_sig); + } - len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0); - assert (len); - buf = xmalloc (len); - len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len); - assert (len); + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, NULL, 0); + assert (len); + buf = xmalloc (len); + len = gcry_sexp_sprint (s_sig, GCRYSEXP_FMT_CANON, buf, len); + assert (len); + } /* FIXME: we must make sure that no buffering takes place or we are in full control of the buffer memory (easy to do) - should go