Decryption using a Cryptoflex card does now work.
authorWerner Koch <wk@gnupg.org>
Wed, 6 Mar 2002 09:01:12 +0000 (09:01 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 6 Mar 2002 09:01:12 +0000 (09:01 +0000)
agent/ChangeLog
agent/agent.h
agent/call-scd.c
agent/divert-scd.c
agent/pkdecrypt.c
scd/card.c
scd/command.c
scd/scdaemon.h
sm/ChangeLog
sm/base64.c
sm/decrypt.c

index 94fb002..bc0ca11 100644 (file)
@@ -1,12 +1,19 @@
+2002-03-06  Werner Koch  <wk@gnupg.org>
+
+       * pkdecrypt.c (agent_pkdecrypt): Changed the way the diversion is done.
+       * divert-scd.c (divert_pkdecrypt): Changed interface and
+       implemented it.
+
 2002-03-05  Werner Koch  <wk@gnupg.org>
 
        * call-scd.c (inq_needpin): New.
        (agent_card_pksign): Add getpin_cb args.
+       (agent_card_pkdecrypt): New.
 
 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.
+       * divert-scd.c (divert_pksign): Changed interface and implemented it.
        (encode_md_for_card): New.
        * call-scd.c (agent_card_pksign): New.
 
index 643ed60..2d92f94 100644 (file)
@@ -153,8 +153,8 @@ int agent_marktrusted (const char *name, const char *fpr, int flag);
 /*-- divert-scd.c --*/
 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);
+int divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info,
+                      char **r_buf, size_t *r_len);
 
 /*-- call-scd.c --*/
 int agent_card_learn (void);
@@ -164,6 +164,11 @@ int agent_card_pksign (const char *keyid,
                        void *getpin_cb_arg,
                        const unsigned char *indata, size_t indatalen,
                        char **r_buf, size_t *r_buflen);
+int agent_card_pkdecrypt (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 6cf53fd..dd8853f 100644 (file)
@@ -403,3 +403,56 @@ agent_card_pksign (const char *keyid,
   return 0;
 }
 
+/* Decipher INDATA using the current card. Note that the returned value is */
+int
+agent_card_pkdecrypt (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;
+
+  *r_buf = NULL;
+  rc = start_scd ();
+  if (rc)
+    return rc;
+
+  /* FIXME: use secure memory where appropriate */
+  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, "PKDECRYPT %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);
+    }
+  *r_buf = get_membuf (&data, r_buflen);
+  if (!*r_buf)
+    return GNUPG_Out_Of_Core;
+
+  return 0;
+}
+
index d938d26..33fd5ce 100644 (file)
@@ -258,21 +258,65 @@ divert_pksign (const unsigned char *digest, size_t digestlen, int algo,
 }
 
 
-int 
-divert_pkdecrypt (GCRY_SEXP *s_plain, GCRY_SEXP s_cipher,
-                  const char *shadow_info)
+/* Decrypt the the value given asn an S-expression in CIPHER using the
+   key identified by SHADOW_INFO and return the plaintext in an
+   allocated buffer in R_BUF.  */
+int  
+divert_pkdecrypt (const unsigned char *cipher, const char *shadow_info,
+                  char **r_buf, size_t *r_len)
 {
   int rc;
   char *kid;
+  const unsigned char *s;
+  size_t n;
+  const unsigned char *ciphertext;
+  size_t ciphertextlen;
+  char *plaintext;
+  size_t plaintextlen;
+
+  s = cipher;
+  if (*s != '(')
+    return GNUPG_Invalid_Sexp;
+  s++;
+  n = snext (&s);
+  if (!n)
+    return GNUPG_Invalid_Sexp; 
+  if (!smatch (&s, n, "enc-val"))
+    return GNUPG_Unknown_Sexp; 
+  if (*s != '(')
+    return GNUPG_Unknown_Sexp;
+  s++;
+  n = snext (&s);
+  if (!n)
+    return GNUPG_Invalid_Sexp; 
+  if (!smatch (&s, n, "rsa"))
+    return GNUPG_Unsupported_Algorithm; 
+  if (*s != '(')
+    return GNUPG_Unknown_Sexp;
+  s++;
+  n = snext (&s);
+  if (!n)
+    return GNUPG_Invalid_Sexp; 
+  if (!smatch (&s, n, "a"))
+    return GNUPG_Unknown_Sexp;
+  n = snext (&s);
+  if (!n)
+    return GNUPG_Unknown_Sexp; 
+  ciphertext = s;
+  ciphertextlen = n;
 
   rc = ask_for_card (shadow_info, &kid);
   if (rc)
     return rc;
 
+  rc = agent_card_pkdecrypt (kid, getpin_cb, NULL,
+                             ciphertext, ciphertextlen,
+                             &plaintext, &plaintextlen);
+  if (!rc)
+    {
+      *r_buf = plaintext;
+      *r_len = plaintextlen;
+    }
   xfree (kid);
-  return GNUPG_Not_Implemented;
+  return rc;
 }
-
-
-
index 33663a9..9a32e76 100644 (file)
@@ -71,14 +71,27 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
       rc = seterr (No_Secret_Key);
       goto leave;
     }
+
   if (!s_skey)
     { /* divert operation to the smartcard */
-      rc = divert_pkdecrypt (&s_plain, s_cipher, shadow_info);
+
+      if (!gcry_sexp_canon_len (ciphertext, ciphertextlen, NULL, NULL))
+        {
+          rc = GNUPG_Invalid_Sexp;
+          goto leave;
+        }
+
+      rc = divert_pkdecrypt (ciphertext, shadow_info, &buf, &len );
       if (rc)
         {
           log_error ("smartcard decryption failed: %s\n", gnupg_strerror (rc));
           goto leave;
         }
+      /* FIXME: don't use buffering and change the protocol to return
+         a complete S-expression and not just a part. */
+      fprintf (outfp, "%u:", (unsigned int)len);
+      fwrite (buf, 1, len, outfp);
+      putc (0, outfp);
     }
   else
     { /* no smartcard, but a private key */
@@ -95,23 +108,23 @@ agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
           rc = map_gcry_err (rc);
           goto leave;
         }
+
+      if (DBG_CRYPTO)
+        {
+          log_debug ("plain: ");
+          gcry_sexp_dump (s_plain);
+        }
+      len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0);
+      assert (len);
+      buf = xmalloc (len);
+      len = gcry_sexp_sprint (s_plain, 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
+         into assuan. */
+      fwrite (buf, 1, len, outfp);
     }      
 
-  if (DBG_CRYPTO)
-    {
-      log_debug ("plain: ");
-      gcry_sexp_dump (s_plain);
-    }
-  len = gcry_sexp_sprint (s_plain, GCRYSEXP_FMT_CANON, NULL, 0);
-  assert (len);
-  buf = xmalloc (len);
-  len = gcry_sexp_sprint (s_plain, 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
-     into assuan. */
-  fwrite (buf, 1, len, outfp);
 
  leave:
   gcry_sexp_release (s_skey);
index 281c810..e7640ff 100644 (file)
@@ -541,3 +541,103 @@ leave:
 }
 
 
+/* Create the signature and return the allocated result in OUTDATA.
+   If a PIN is required the PINCB will be used to ask for the PIN; it
+   should return the PIN in an allocated buffer and put it into PIN.  */
+int 
+card_decipher (CARD card, const char *keyidstr,
+               int (pincb)(void*, const char *, char **),
+               void *pincb_arg,
+               const void *indata, size_t indatalen,
+               void **outdata, size_t *outdatalen )
+{
+  struct sc_pkcs15_id keyid;
+  struct sc_pkcs15_prkey_info *key;
+  struct sc_pkcs15_pin_info *pin;
+  struct sc_pkcs15_object *keyobj, *pinobj;
+  char *pinvalue;
+  int rc;
+  unsigned char *outbuf = NULL;
+  size_t outbuflen;
+
+  if (!card || !card->p15card || !indata || !indatalen
+      || !outdata || !outdatalen || !pincb)
+    return GNUPG_Invalid_Value;
+  
+  rc = idstr_to_id (keyidstr, &keyid);
+  if (rc)
+    return rc;
+
+  rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj);
+  if (rc < 0)
+    {
+      log_error ("private key not found: %s\n", sc_strerror(rc));
+      rc = GNUPG_No_Secret_Key;
+      goto leave;
+    }
+  rc = 0;
+  key = keyobj->data;
+
+  rc = sc_pkcs15_find_pin_by_auth_id (card->p15card,
+                                      &keyobj->auth_id, &pinobj);
+  if (rc)
+    {
+      log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc));
+      rc = GNUPG_Bad_PIN_Method;
+      goto leave;
+    }
+  pin = pinobj->data;
+
+  /* Fixme: pack this into a verification loop */
+  /* Fixme: we might want to pass pin->min_length and 
+     pin->stored_length */
+  rc = pincb (pincb_arg, pinobj->label, &pinvalue);
+  if (rc)
+    {
+      log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc));
+      goto leave;
+    }
+
+  rc = sc_pkcs15_verify_pin (card->p15card, pin,
+                             pinvalue, strlen (pinvalue));
+  xfree (pinvalue);
+  if (rc)
+    {
+      log_info ("PIN verification failed: %s\n", sc_strerror (rc));
+      rc = GNUPG_Bad_PIN;
+      goto leave;
+    }
+
+  outbuflen = indatalen < 256? 256 : indatalen; 
+  outbuf = xtrymalloc (outbuflen);
+  if (!outbuf)
+    return GNUPG_Out_Of_Core;
+
+  /* OpenSC does not yet support decryption for cryptflex cards */  
+/*    rc = sc_pkcs15_decipher (card->p15card, key, */
+/*                             indata, indatalen, */
+/*                             outbuf, outbuflen); */
+  rc = sc_pkcs15_compute_signature (card->p15card, key,
+                                    0,
+                                    indata, indatalen,
+                                    outbuf, outbuflen );
+  if (rc < 0)
+    {
+      log_error ("failed to decipger the data: %s\n", sc_strerror (rc));
+      rc = GNUPG_Card_Error;
+    }
+  else
+    {
+      *outdatalen = rc;
+      *outdata = outbuf;
+      outbuf = NULL;
+      rc = 0;
+    }
+
+
+leave:
+  xfree (outbuf);
+  return rc;
+}
+
+
index a171de3..30191dd 100644 (file)
@@ -400,6 +400,40 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
   return map_to_assuan_status (rc);
 }
 
+/* PKDECRYPT <hexified_id>
+
+ */
+static int
+cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int rc;
+  void *outdata;
+  size_t outdatalen;
+
+  if ((rc = open_card (ctrl)))
+    return rc;
+
+  rc = card_decipher (ctrl->card_ctx,
+                      line, 
+                      pin_cb, ctx,
+                      ctrl->in_data.value, ctrl->in_data.valuelen,
+                      &outdata, &outdatalen);
+  if (rc)
+    {
+      log_error ("card_create_signature failed: %s\n", gnupg_strerror (rc));
+    }
+  else
+    {
+      rc = assuan_send_data (ctx, outdata, outdatalen);
+      xfree (outdata);
+      if (rc)
+        return rc; /* that is already an assuan error code */
+    }
+
+  return map_to_assuan_status (rc);
+}
+
 
 
 \f
@@ -417,6 +451,7 @@ register_commands (ASSUAN_CONTEXT ctx)
     { "READCERT", 0, cmd_readcert },
     { "SETDATA", 0,  cmd_setdata },
     { "PKSIGN", 0,   cmd_pksign },
+    { "PKDECRYPT", 0,cmd_pkdecrypt },
     { "",     ASSUAN_CMD_INPUT, NULL }, 
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
     { NULL }
index 8a5506d..bd604b4 100644 (file)
@@ -92,7 +92,11 @@ int card_create_signature (CARD card,
                            void *pincb_arg,
                            const void *indata, size_t indatalen,
                            void **outdata, size_t *outdatalen );
-
+int card_decipher (CARD card, const char *keyidstr,
+                   int (pincb)(void*, const char *, char **),
+                   void *pincb_arg,
+                   const void *indata, size_t indatalen,
+                   void **outdata, size_t *outdatalen);
 
 
 #endif /*SCDAEMON_H*/
index 846388f..e47057e 100644 (file)
@@ -1,3 +1,8 @@
+2002-03-06  Werner Koch  <wk@gnupg.org>
+
+       * base64.c (base64_reader_cb): Use case insensitive compare of the
+       Content-Type string to detect plain base-64.
+
 2002-03-05  Werner Koch  <wk@gnupg.org>
 
        * gpgsm.c, gpgsm.h: Add local_user.
index bf1aea1..e7a1ae9 100644 (file)
@@ -224,8 +224,9 @@ base64_reader_cb (void *cb_value, char *buffer, size_t count, size_t *nread)
           parm->linelen = parm->readpos = 0;
         }
       else if ( parm->have_lf && parm->line_counter == 1
-                && !strncmp (parm->line, "Content-Type:", 13))
-        { /* Might be a S/MIME body */
+                && parm->linelen >= 13
+                && !ascii_memcasecmp (parm->line, "Content-Type:", 13))
+        { /* might be a S/MIME body */
           parm->might_be_smime = 1;
           parm->linelen = parm->readpos = 0;
           goto next;
index d16bb59..382ec09 100644 (file)
@@ -79,6 +79,14 @@ prepare_decryption (const char *hexkeygrip, KsbaConstSexp enc_val,
       goto leave; 
     }
 
+  /* FIXME: Actually the leading zero is required but due to the way
+     we encode the output in libgcrypt as an MPI we are not able to
+     encode that leading zero.  However, when using a Smartcard we are
+     doing it the rightway and therefore we have skip the zero.  This
+     should be fixed in gpg-agent of course. */
+  if (!seskey[n])
+    n++;
+
   if (seskey[n] != 2 )  /* wrong block type version */
     { 
       rc = seterr (Invalid_Session_Key);