aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2002-03-05 17:14:45 +0000
committerWerner Koch <[email protected]>2002-03-05 17:14:45 +0000
commit2fa73e781f78f7376d0b728189ddab731f8eda9d (patch)
tree73fd019895ab2c551f8a11fedd20d973b8cf880a
parent* gpgsm.c, gpgsm.h: Add local_user. (diff)
downloadgnupg-2fa73e781f78f7376d0b728189ddab731f8eda9d.tar.gz
gnupg-2fa73e781f78f7376d0b728189ddab731f8eda9d.zip
Signing using a PKCS15 smartcard does work. How to create such a card
is of course a different thing. Note, that you need to create the shadowed-private-key file manually.
-rw-r--r--agent/ChangeLog12
-rw-r--r--agent/agent.h9
-rw-r--r--agent/call-scd.c184
-rw-r--r--agent/divert-scd.c121
-rw-r--r--agent/pksign.c57
5 files changed, 354 insertions, 29 deletions
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 <[email protected]>
+
+ * call-scd.c (inq_needpin): New.
+ (agent_card_pksign): Add getpin_cb args.
+
+2002-03-04 Werner Koch <[email protected]>
+
+ * 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 <[email protected]>
* 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