Signing using a PKCS15 smartcard does work. How to create such a card
authorWerner Koch <wk@gnupg.org>
Tue, 5 Mar 2002 17:14:45 +0000 (17:14 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 5 Mar 2002 17:14:45 +0000 (17:14 +0000)
is of course a different thing.  Note, that you need to create the
shadowed-private-key file manually.

agent/ChangeLog
agent/agent.h
agent/call-scd.c
agent/divert-scd.c
agent/pksign.c

index 72d2ba8..94fb002 100644 (file)
@@ -1,3 +1,15 @@
+2002-03-05  Werner Koch  <wk@gnupg.org>
+
+       * call-scd.c (inq_needpin): New.
+       (agent_card_pksign): Add getpin_cb args.
+
+2002-03-04  Werner Koch  <wk@gnupg.org>
+
+       * 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  <wk@gnupg.org>
 
        * pksign.c (agent_pksign): Detect whether a Smartcard is to be
index 56e89f0..643ed60 100644 (file)
@@ -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*/
index af4d59f..6cf53fd 100644 (file)
@@ -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;
+};
+
+
+\f
+/* 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;
+}
+
 
 
 \f
@@ -216,6 +290,116 @@ agent_card_serialno (char **r_serialno)
   return 0;
 }
 
+\f
+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;
+}
 
index 0dc9c12..d938d26 100644 (file)
@@ -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;
 }
 
 
index bdf1ff4..7873ce8 100644 (file)
@@ -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