Implement decryption.
authorWerner Koch <wk@gnupg.org>
Mon, 25 Feb 2019 07:23:34 +0000 (08:23 +0100)
committerWerner Koch <wk@gnupg.org>
Mon, 25 Feb 2019 07:23:34 +0000 (08:23 +0100)
* src/agent.h (struct key_info_s, key_info_t): Move to ...
* src/cert.h (struct key_info_s): here.
* src/agent.c (learn_status_cb): Set usage info.
(scute_agent_learn): Use "SCD LEARN" instead of "LEARN"
(struct pkdecrypt_parm_s): New.
(pkdecrypt_data_cb): New.
(pkdecrypt_inq_cb): New.
(pkdecrypt_parse_result): New.
(scute_agent_decrypt): New.
* src/cert-object.c (scute_attr_prv): Set decrypt flag.  Replace arg
'grip' by 'kinfo'.  Change callers.
* src/gpgsm.c (struct search_cb_parm): Replace 'grip' by 'kinfo'.
(scute_gpgsm_get_cert): Replace args 'grip' and 'certref' by 'kinfo'.
* src/slots.c (struct session): Add 'decryption_key'.  Change callers.
(slot_create_session): Init decryption_key.
(session_sign): Allow NULL for PSIGNATURE but still return the
required length.
(session_init_decrypt): New.
(session_decrypt): New.
--

Tested with a Yubikey PIV card.  Requires recent fix for gpg-agent to
allow for the flags parameter in the decrypt input.  That parameter is
actually suggested by Libgcrypt.

Signed-off-by: Werner Koch <wk@gnupg.org>
12 files changed:
src/agent.c
src/agent.h
src/cert-object.c
src/cert.h
src/gpgsm.c
src/gpgsm.h
src/p11-decrypt.c
src/p11-decryptinit.c
src/p11-encryptinit.c
src/p11-sign.c
src/slots.c
src/slots.h

index 6228af9..c168a2e 100644 (file)
@@ -1,5 +1,5 @@
 /* agent.c - Talking to gpg-agent.
- * Copyright (C) 2006, 2007, 2008, 2015 g10 Code GmbH
+ * Copyright (C) 2006, 2007, 2008, 2015, 2019 g10 Code GmbH
  *
  * This file is part of Scute.
  *
@@ -65,7 +65,7 @@ gnupg_allow_set_foregound_window (pid_t pid)
     return;
 #ifdef HAVE_W32_SYSTEM
   else if (!AllowSetForegroundWindow (pid))
-    DEBUG (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %u\n",
+    DEBUG (DBG_CRIT, "AllowSetForegroundWindow(%lu) failed: %\n",
           (unsigned long)pid, (unsigned int)GetLastError ());
 #endif
 }
@@ -642,15 +642,35 @@ learn_status_cb (void *opaque, const char *line)
   else if (keywordlen == 11 && !memcmp (keyword, "KEYPAIRINFO", keywordlen))
     {
       /* The format of such a line is:
-       *   KEYPARINFO <hexgrip> <keyref>
+       *   KEYPAIRINFO <hexgrip> <keyref> [<usage>]
        */
       const char *hexgrip = line;
+      char *line_buffer, *p;
+      const char *usage;
 
       while (*line && !spacep (line))
         line++;
       while (spacep (line))
         line++;
-      keyref = line;
+
+      p = line_buffer = strdup (line);
+      if (!line_buffer)
+        goto no_core;
+      keyref = line_buffer;
+      while (*p && !spacep (p))
+        p++;
+      if (*p)
+        {
+          *p++ = 0;
+          while (spacep (p))
+            p++;
+          usage = p;
+          while (*p && !spacep (p))
+            p++;
+          *p = 0;
+        }
+      else
+        usage = "";
 
       if (hexgrip_valid_p (hexgrip))
         {
@@ -662,12 +682,26 @@ learn_status_cb (void *opaque, const char *line)
               if (!kinfo)
                 goto no_core;
             }
-          else /* Existing entry - clear the grip.  */
-            *kinfo->grip = 0;
+          else /* Existing entry - clear grip and usage.  */
+            {
+              *kinfo->grip = 0;
+              memset (&kinfo->usage, 0, sizeof kinfo->usage);
+            }
 
           strncpy (kinfo->grip, hexgrip, sizeof kinfo->grip);
           kinfo->grip[sizeof kinfo->grip -1] = 0;
+          for (; *usage; usage++)
+            {
+              switch (*usage)
+                {
+                case 's': kinfo->usage.sign = 1; break;
+                case 'c': kinfo->usage.cert = 1; break;
+                case 'a': kinfo->usage.auth = 1; break;
+                case 'e': kinfo->usage.encr = 1; break;
+                }
+            }
         }
+      free (line_buffer);
     }
   else if (keywordlen == 6 && !memcmp (keyword, "EXTCAP", keywordlen))
     {
@@ -707,7 +741,7 @@ scute_agent_learn (struct agent_card_info_s *info)
   gpg_error_t err;
 
   memset (info, 0, sizeof (*info));
-  err = assuan_transact (agent_ctx, "LEARN --sendinfo",
+  err = assuan_transact (agent_ctx, "SCD LEARN --force",
                         NULL, NULL,
                          default_inq_cb, NULL,
                         learn_status_cb, info);
@@ -720,7 +754,7 @@ scute_agent_learn (struct agent_card_info_s *info)
       if (!err)
         {
           memset (info, 0, sizeof (*info));
-          err = assuan_transact (agent_ctx, "LEARN --sendinfo",
+          err = assuan_transact (agent_ctx, "SCD LEARN --force",
                                  NULL, NULL,
                                  default_inq_cb, NULL,
                                  learn_status_cb, info);
@@ -998,7 +1032,7 @@ decode_hash (const unsigned char *data, int len,
 
 
 /* Call the agent to sign (DATA,LEN) using the key described by
- * HEXGRIP.  Stores the signature in SIG_RESULT and its lengtn at
+ * HEXGRIP.  Stores the signature in SIG_RESULT and its length at
  * SIG_LEN; SIGLEN must initially point to the allocated size of
  * SIG_RESULT.  */
 gpg_error_t
@@ -1060,6 +1094,187 @@ scute_agent_sign (const char *hexgrip, unsigned char *data, int len,
 }
 
 
+\f
+struct pkdecrypt_parm_s
+{
+  unsigned int len;
+  unsigned char data[512];
+  assuan_context_t ctx;
+  const unsigned char *ciphertext;
+  size_t ciphertextlen;
+};
+
+
+static gpg_error_t
+pkdecrypt_data_cb (void *opaque, const void *buffer, size_t length)
+{
+  struct pkdecrypt_parm_s *parm = opaque;
+
+  if (parm->len + length > sizeof parm->data)
+    {
+      DEBUG (DBG_INFO, "maximum decryption result length exceeded");
+      return gpg_error (GPG_ERR_BAD_DATA);
+    }
+
+  memcpy (parm->data + parm->len, buffer, length);
+  parm->len += length;
+
+  return 0;
+}
+
+
+/* Handle the inquiries from pkdecrypt.  Note, we only send the data,
+ * assuan_transact takes care of flushing and writing the "END".  */
+static gpg_error_t
+pkdecrypt_inq_cb (void *opaque, const char *line)
+{
+  struct pkdecrypt_parm_s *parm = opaque;
+  gpg_error_t err;
+  const char *keyword = line;
+  int keywordlen;
+
+  for (keywordlen = 0; *line && !spacep (line); line++, keywordlen++)
+    ;
+  while (spacep (line))
+    line++;
+
+  if (keywordlen == 10 && !memcmp (keyword, "CIPHERTEXT", 10))
+    err = assuan_send_data (parm->ctx, parm->ciphertext, parm->ciphertextlen);
+  else
+    err = default_inq_cb (NULL, line);
+
+  return err;
+}
+
+
+
+/* Parse the result of a pkdecrypt operation which is an s-expression
+ * in canonical form that looks like
+ * (5:value<NDATA>:<DATA>).
+ *
+ * The raw result is stored in RESULT which has a size of *R_LEN, and
+ * *R_LEN is adjusted to the actual size.  */
+static gpg_error_t
+pkdecrypt_parse_result (struct pkdecrypt_parm_s *ctx,
+                        unsigned char *result, unsigned int *r_len)
+{
+  char *buf = ctx->data;
+  size_t len = ctx->len;
+  char *endp, *raw;
+  size_t n, rawlen;
+
+  if (len < 13 || memcmp (buf, "(5:value", 8) )
+    return gpg_error (GPG_ERR_INV_SEXP);
+  len -= 8;
+  buf += 8;
+
+  n = strtoul (buf, &endp, 10);
+  if (!n || *endp != ':')
+    return gpg_error (GPG_ERR_INV_SEXP);
+  endp++;
+  if ((endp-buf)+n > len)
+    return gpg_error (GPG_ERR_INV_SEXP); /* Oops: Inconsistent S-Exp. */
+
+  /* Let (RAW,RAWLEN) describe the pkcs#1 block and remove that padding.  */
+  raw = endp;
+  rawlen = n;
+
+  if (rawlen < 10)  /* 0x00 + 0x02 + <1_random> + 0x00 + <16-session> */
+    return gpg_error (GPG_ERR_INV_SESSION_KEY);
+
+  if (raw[0] || raw[1] != 2 )  /* Wrong block type version. */
+    return gpg_error (GPG_ERR_INV_SESSION_KEY);
+
+  for (n=2; n < rawlen && raw[n]; n++) /* Skip the random bytes. */
+    ;
+  if (n+1 >= rawlen || raw[n] )
+    return gpg_error (GPG_ERR_INV_SESSION_KEY);
+  n++; /* Skip the zero byte */
+
+  if (*r_len < (rawlen - n))
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  memcpy (result, raw + n, rawlen - n);
+  *r_len = rawlen - n;
+  return 0;
+}
+
+
+/* Call the agent to decrypt (ENCDATA,ENCDATALEN) using the key
+ * described by HEXGRIP.  Stores the plaintext at R_PLAINDATA and its
+ * length at R_PLAINDATALEN; R_PLAINDATALEN must initially point to
+ * the allocated size of R_PLAINDATA and is updated to the actual used
+ * size on return.  */
+gpg_error_t
+scute_agent_decrypt (const char *hexgrip,
+                     unsigned char *encdata, int encdatalen,
+                     unsigned char *r_plaindata, unsigned int *r_plaindatalen)
+{
+  char cmd[150];
+  gpg_error_t err;
+  struct pkdecrypt_parm_s pkdecrypt;
+  char *s_data;
+  size_t s_datalen;
+
+  if (!hexgrip || !encdata || !encdatalen || !r_plaindatalen)
+    return gpg_error (GPG_ERR_INV_ARG);
+
+  if (!r_plaindata)
+    {
+      /* Fixme: We do not return the minimal required length but our
+       * internal buffer size.  */
+      pkdecrypt.len = *r_plaindatalen;
+      *r_plaindatalen = sizeof pkdecrypt.data - 1;
+      if (pkdecrypt.len > sizeof pkdecrypt.data - 1)
+        return gpg_error (GPG_ERR_INV_LENGTH);
+      return 0;
+    }
+
+  snprintf (cmd, sizeof (cmd), "SETKEY %s", hexgrip);
+  err = assuan_transact (agent_ctx, cmd,
+                         NULL, NULL,
+                         default_inq_cb, NULL,
+                        NULL, NULL);
+  if (err)
+    return err;
+
+  /* Convert the input into an appropriate s-expression as expected by
+   * gpg-agent which is:
+   *
+   *  (enc-val
+   *    (flags pkcs1)
+   *    (rsa
+   *      (a VALUE)))
+   *
+   * Out of convenience we append a non-counted extra nul to the
+   * created canonical s-expression.
+   */
+  s_data = malloc (100 + encdatalen);
+  if (!s_data)
+    return gpg_error_from_syserror ();
+  snprintf (s_data, 50, "(7:enc-val(5:flags5:pkcs1)(3:rsa(1:a%d:",
+            encdatalen);
+  s_datalen = strlen (s_data);
+  memcpy (s_data + s_datalen, encdata, encdatalen);
+  s_datalen += encdatalen;
+  memcpy (s_data + s_datalen, ")))", 4);
+  s_datalen += 3;
+
+  pkdecrypt.len = 0;
+  pkdecrypt.ctx = agent_ctx;
+  pkdecrypt.ciphertext = s_data;
+  pkdecrypt.ciphertextlen = s_datalen;
+  err = assuan_transact (agent_ctx, "PKDECRYPT",
+                        pkdecrypt_data_cb, &pkdecrypt,
+                         pkdecrypt_inq_cb, &pkdecrypt,
+                         NULL, NULL);
+  if (!err)
+    err = pkdecrypt_parse_result (&pkdecrypt, r_plaindata, r_plaindatalen);
+
+  free (s_data);
+  return err;
+}
+
+
 /* Determine if FPR is trusted.  */
 gpg_error_t
 scute_agent_is_trusted (const char *fpr, bool *is_trusted)
index d14ebb7..0dfce0e 100644 (file)
 
 #include "cert.h"
 
-\f
-/* An object to store information pertaining to a keypair as stored on
- * a card.  This is commonly used as a linked list of all keys known
- * for a card.  */
-struct key_info_s
-{
-  struct key_info_s *next;
-
-  char grip[41];/* The keygrip as hex encoded string.  */
-
-  unsigned char xflag;   /* Temporary flag to help processing a list. */
-
-  /* The three next items are mostly useful for OpenPGP cards.  */
-  unsigned char fprlen;  /* Use length of the next item.  */
-  unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN.  */
-  unsigned long created; /* The time the key was created.  */
-
-  char keyref[1];        /* String with the keyref (e.g. OPENPGP.1).  */
-};
-typedef struct key_info_s *key_info_t;
-
 
 /* The information structure for a smart card.  */
 struct agent_card_info_s
@@ -132,6 +111,12 @@ gpg_error_t scute_agent_sign (const char *hexgrip,
                               unsigned char *data, int len,
                              unsigned char *sig_result, unsigned int *sig_len);
 
+/* Decrypt data.  */
+gpg_error_t scute_agent_decrypt (const char *hexgrip,
+                                 unsigned char *encdata, int encdatalen,
+                                 unsigned char *r_plaindata,
+                                 unsigned int *r_plaindatalen);
+
 /* Determine if FPR is trusted.  */
 gpg_error_t scute_agent_is_trusted (const char *fpr, bool *is_trusted);
 
index d3a594d..0184bad 100644 (file)
@@ -590,7 +590,7 @@ scute_attr_cert (struct cert *cert, const char *grip,
 
 
 gpg_error_t
-scute_attr_prv (struct cert *cert, const char *grip,
+scute_attr_prv (struct cert *cert, key_info_t kinfo,
                 CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp)
 {
   CK_RV err = 0;
@@ -617,8 +617,8 @@ scute_attr_prv (struct cert *cert, const char *grip,
   CK_MECHANISM_TYPE obj_mechanisms[] = { CKM_RSA_PKCS };
 
   CK_BBOOL obj_sensitive = CK_TRUE;
-  CK_BBOOL obj_decrypt = CK_FALSE;     /* Authentication only for now.  */
-  CK_BBOOL obj_sign = CK_TRUE;
+  CK_BBOOL obj_decrypt = CK_FALSE;      /* Updated below.  */
+  CK_BBOOL obj_sign = CK_FALSE;         /* Updated below.  */
   CK_BBOOL obj_sign_recover = CK_FALSE;
   CK_BBOOL obj_unwrap = CK_FALSE;
   CK_BBOOL obj_extractable = CK_FALSE;
@@ -627,6 +627,13 @@ scute_attr_prv (struct cert *cert, const char *grip,
   CK_BBOOL obj_wrap_with_trusted = CK_FALSE;
   CK_BBOOL obj_always_authenticate = CK_FALSE;
 
+  if (kinfo->usage.sign || kinfo->usage.cert || kinfo->usage.auth)
+    obj_sign = CK_TRUE;
+
+  if (kinfo->usage.encr)
+    obj_decrypt = CK_TRUE;
+
+
   err = asn1_get_subject (cert->cert_der, cert->cert_der_len,
                          &subject_start, &subject_len);
   if (err)
@@ -695,7 +702,7 @@ scute_attr_prv (struct cert *cert, const char *grip,
 
       snprintf (cka_id_buffer, sizeof cka_id_buffer, "%s %s",
                 *cert->certref ? cert->certref:"-",
-                grip && *grip? grip : "?" );
+                kinfo->grip && *kinfo->grip? kinfo->grip : "?" );
       err = attr_one (attr, &attr_count, CKA_ID,
                       cka_id_buffer, strlen (cka_id_buffer));
     }
index d1a6cb1..0ba2dfb 100644 (file)
 
 #include "cryptoki.h"
 
+
 \f
+/* An object to store information pertaining to a keypair as stored on
+ * a card.  This is commonly used as a linked list of all keys known
+ * for a card.  */
+struct key_info_s
+{
+  struct key_info_s *next;
+
+  char grip[41];/* The keygrip as hex encoded string.  */
+
+  unsigned char xflag;   /* Temporary flag to help processing a list. */
+
+  /* The three next items are mostly useful for OpenPGP cards.  */
+  unsigned char fprlen;  /* Use length of the next item.  */
+  unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN.  */
+  unsigned long created; /* The time the key was created.  */
+  struct {
+    unsigned int sign:1;
+    unsigned int cert:1;
+    unsigned int auth:1;
+    unsigned int encr:1;
+  } usage;
+
+  char keyref[1];        /* String with the keyref (e.g. OPENPGP.1).  */
+};
+typedef struct key_info_s *key_info_t;
+
+
 /* A certificate structure holds all information of a certificate
    during a certificate search.  */
 struct cert
@@ -47,7 +75,9 @@ struct cert
   bool valid;
 
   /* The certifciate reference if retrieved from a card or an empty
-   * string if not known.  Example value: "OPENPGP.3".  */
+   * string if not known.  Example value: "OPENPGP.3".  This is
+   * required because we do not always have access to a corresponding
+   * key_info_t object.  */
   char certref[25];
 
 #if 1
@@ -133,7 +163,7 @@ gpg_error_t scute_gpgsm_search_certs (enum keylist_modes mode,
 gpg_error_t scute_attr_cert (struct cert *cert, const char *grip,
                             CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp);
 
-gpg_error_t scute_attr_prv (struct cert *cert, const char *grip,
+gpg_error_t scute_attr_prv (struct cert *cert, key_info_t kinfo,
                             CK_ATTRIBUTE_PTR *attrp, CK_ULONG *attr_countp);
 
 void scute_attr_free (CK_ATTRIBUTE_PTR attr, CK_ULONG attr_count);
index 5c3e220..dead6e1 100644 (file)
@@ -47,7 +47,7 @@ struct search_cb_parm
   cert_get_cb_t cert_get_cb;
   void *hook;
   bool with_chain;
-  const char *grip;
+  key_info_t kinfo;
 };
 
 
@@ -63,7 +63,7 @@ search_cb (void *hook, struct cert *cert)
   /* Add the private key object only once.  */
   if (!ctx->found)
     {
-      err = scute_attr_prv (cert, ctx->grip, &attrp, &attr_countp);
+      err = scute_attr_prv (cert, ctx->kinfo, &attrp, &attr_countp);
       if (err)
        return err;
 
@@ -85,7 +85,7 @@ search_cb (void *hook, struct cert *cert)
     scute_gpgsm_search_certs (KEYLIST_BY_FPR, cert->chain_id, search_cb, ctx);
 
   /* Turn this certificate into a certificate object.  */
-  err = scute_attr_cert (cert, ctx->grip, &attrp, &attr_countp);
+  err = scute_attr_cert (cert, ctx->kinfo->grip, &attrp, &attr_countp);
   if (err)
     return err;
 
@@ -101,9 +101,10 @@ search_cb (void *hook, struct cert *cert)
 
 
 /* Create the attributes required for a new certificate object.  If
- * CERTREF is not NULL it is used to locate the cert directly from the
- * card; if CERTREF is NULL or a cert was not found on the card, GRIP
- * is used to find the certificate in the local key store of gpgsm.
+ * KINFO->KEYREF is not NULL it is used to locate the cert directly
+ * from the card; if KINFO->KEYREF is NULL or a cert was not found on
+ * the card, KINFO->GRIP is used to find the certificate in the local
+ * key store of gpgsm.
  *
  * FIXME: This is all pretty questionable because our input data
  * always comes from the card.
@@ -112,8 +113,7 @@ search_cb (void *hook, struct cert *cert)
  * and ATTR_COUNTP, and for the private key object in PRV_ATTRP and
  * PRV_ATTR_COUNTP.  */
 gpg_error_t
-scute_gpgsm_get_cert (char *grip, const char *certref,
-                      cert_get_cb_t cert_get_cb, void *hook)
+scute_gpgsm_get_cert (key_info_t kinfo, cert_get_cb_t cert_get_cb, void *hook)
 {
   gpg_error_t err;
   struct search_cb_parm search;
@@ -122,36 +122,25 @@ scute_gpgsm_get_cert (char *grip, const char *certref,
   search.cert_get_cb = cert_get_cb;
   search.hook = hook;
   search.with_chain = false;
-  search.grip = grip;
+  search.kinfo = kinfo;
 
-  DEBUG (DBG_INFO, "scute_gpgsm_get_cert: certref='%s'", certref);
+  DEBUG (DBG_INFO, "scute_gpgsm_get_cert: keyref='%s'", kinfo->keyref);
 
   /* If the cert is requested from the card, we try to get it from
    * the card as well.  */
-  if (certref)
+  if (kinfo->keyref)
     {
       struct cert cert;
 
       memset (&cert, '\0', sizeof (cert));
-      err = scute_agent_get_cert (certref, &cert);
-      if (! err)
-       {
-#if 0
-         /* For now, we don't need no stinking chain.  */
-
-         /* As we only have the DER certificate from the card, we need to
-            parse that and fill out the missing info and try to get the
-            certificate chain from gpgsm.  */
-         err = scute_cert_from_der (&cert);
-#endif
-         if (! err)
-           err = search_cb (&search, &cert);
-         return err;
-       }
+      err = scute_agent_get_cert (kinfo->keyref, &cert);
+      if (!err)
+        return search_cb (&search, &cert);
     }
 
   DEBUG (DBG_INFO, "scute_gpgsm_get_cert: falling back to gpgsm");
   search.with_chain = true;
-  err = scute_gpgsm_search_certs (KEYLIST_BY_GRIP, grip, search_cb, &search);
+  err = scute_gpgsm_search_certs (KEYLIST_BY_GRIP, kinfo->grip,
+                                  search_cb, &search);
   return err;
 }
index a68a38e..515d2f8 100644 (file)
@@ -43,11 +43,8 @@ typedef gpg_error_t (*cert_get_cb_t) (void *hook,
                                      CK_ATTRIBUTE_PTR attrp,
                                      CK_ULONG attr_countp);
 
-/* Create the attributes required for a new certificate object.
-   Returns allocated attributes for the certificate object in ATTRP
-   and ATTR_COUNTP, and for the private key object in PRV_ATTRP
-   and PRV_ATTR_COUNTP.  */
-gpg_error_t scute_gpgsm_get_cert (char *grip, const char *certref,
-                                 cert_get_cb_t cert_get_cb, void *hook);
+/* Create the attributes required for a new certificate object.  */
+gpg_error_t scute_gpgsm_get_cert (key_info_t kinfo,
+                                  cert_get_cb_t cert_get_cb, void *hook);
 
 #endif /* GPGSM_H */
index 8530720..485af6d 100644 (file)
@@ -1,5 +1,5 @@
 /* p11-decrypt.c - Cryptoki implementation.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
  *
  * This file is part of Scute.
  *
 #endif
 
 #include "cryptoki.h"
+#include "locking.h"
+#include "slots.h"
 
-\f
+/* Decrypt the data (ENCDATA,ENCDATALEN) using the information
+ * recorded in HSESSION by C_DecryptInit.  R_DATA is a buffer to
+ * receive the decrypted data.  The length of that buffer must be
+ * stored in a variable to which R_DATALEN points to; on success that
+ * length is updated to the actual length of the decrypted data at
+ * R_DATA.  In-place decryption is supported; that is ENCDATA and
+ * R_DATA may be the same buffer.
+ *
+ * If the function returns CKR_BUFFER_TOO_SMALL no further
+ * C_DecryptInit is required, instead the function can be called again
+ * with a larger buffer.  On all other return codes a new
+ * C_DecryptInit is required.  However, in contrast to the specs the
+ * return code CKR_ARGUMENTS_BAD may not require a new C_DecryptInit
+ * because this can be considered a bug in the caller's code.  In case
+ * the input cannot be decrypted because it has an inappropriate
+ * length, then either CKR_ENCRYPTED_DATA_INVALID or
+ * CKR_ENCRYPTED_DATA_LEN_RANGE may be returned.
+ */
 CK_RV CK_SPEC
-C_Decrypt (CK_SESSION_HANDLE hSession,
-           CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen,
-           CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen)
+C_Decrypt (CK_SESSION_HANDLE hsession,
+           CK_BYTE *encdata, CK_ULONG encdatalen,
+           CK_BYTE *r_data, CK_ULONG *r_datalen)
 {
-  /* FIXME: Implement this.  */
-  (void) hSession;
-  (void) pEncryptedData;
-  (void) ulEncryptedDataLen;
-  (void) pData;
-  (void) pulDataLen;
-  return CKR_FUNCTION_NOT_SUPPORTED;
+  CK_RV rv;
+  slot_iterator_t slot;
+  session_iterator_t sid;
+
+  if (!hsession || !encdata || !r_datalen)
+    return CKR_ARGUMENTS_BAD;
+
+  rv = scute_global_lock ();
+  if (rv)
+    return rv;
+
+  rv = slots_lookup_session (hsession, &slot, &sid);
+  if (!rv)
+    rv = session_decrypt (slot, sid, encdata, encdatalen, r_data, r_datalen);
+
+  scute_global_unlock ();
+  return rv;
 }
index dce1e00..71131be 100644 (file)
@@ -1,5 +1,5 @@
 /* p11-decryptinit.c - Cryptoki implementation.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
  *
  * This file is part of Scute.
  *
 #endif
 
 #include "cryptoki.h"
+#include "locking.h"
+#include "slots.h"
+
+
+/* Prepare a decryption operation.  HSESSION is the session's handle,
+ * MECHANISM points to an object describing the mechanism to be used,
+ * and HKEY is a handle to the decryption key.  After calling this
+ * function either C_Decrypt or (C_DecryptUpdate, C_DecryptFinal) can
+ * be used to actually decrypt the data.  The preparation is valid
+ * until a C_Decrypt or C_DecryptFinal.
+ */
 
-\f
 CK_RV CK_SPEC
-C_DecryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
-               CK_OBJECT_HANDLE hKey)
+C_DecryptInit (CK_SESSION_HANDLE hsession, CK_MECHANISM *mechanism,
+               CK_OBJECT_HANDLE hkey)
 {
-  /* FIXME: Implement this.  */
-  (void) hSession;
-  (void) pMechanism;
-  (void) hKey;
-  return CKR_FUNCTION_NOT_SUPPORTED;
+  CK_RV rv;
+  slot_iterator_t slot;
+  session_iterator_t sid;
+
+  if (!hsession || !mechanism || hkey == CK_INVALID_HANDLE)
+    return CKR_ARGUMENTS_BAD;
+
+  rv = scute_global_lock ();
+  if (rv)
+    return rv;
+
+  rv = slots_lookup_session (hsession, &slot, &sid);
+  if (!rv)
+    rv = session_init_decrypt (slot, sid, mechanism, hkey);
+
+  scute_global_unlock ();
+  return rv;
 }
index d769a91..a34be0b 100644 (file)
@@ -31,5 +31,9 @@ C_EncryptInit (CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism,
   (void) hSession;
   (void) pMechanism;
   (void) hKey;
+
+
+
+
   return CKR_FUNCTION_NOT_SUPPORTED;
 }
index 4f2ae7d..ae1e5e5 100644 (file)
@@ -29,7 +29,7 @@
 
 
 /* Sign the data (PDATA,ULDATALEN) using the information recorded in
- * the HSESSION by C_SignInit.  PSIGNAURE is a buffer to receive the
+ * the HSESSION by C_SignInit.  PSIGNATURE is a buffer to receive the
  * signature.  The length of that buffer must be stored in a variable
  * to which PULSIGNATURELEN points to; on success that length is
  * updated to the actual length of the signature in PULSIGNATURE.
index ca6a015..5ac8486 100644 (file)
@@ -1,5 +1,5 @@
 /* slots.c - Slot management.
- * Copyright (C) 2006 g10 Code GmbH
+ * Copyright (C) 2006, 2019 g10 Code GmbH
  *
  * This file is part of Scute.
  *
@@ -97,8 +97,12 @@ struct session
 
   /* The signing key.  */
   CK_OBJECT_HANDLE signing_key;
+
+  /* The decryption key.  */
+  CK_OBJECT_HANDLE decryption_key;
 };
 
+
 /* The slot status.  */
 typedef enum
   {
@@ -132,6 +136,7 @@ struct slot
 
 \f
 /* The slot table.  */
+/* FIXME: That symbol name is pretty short for a global.  */
 static scute_table_t slots;
 
 \f
@@ -379,7 +384,7 @@ slot_init (slot_iterator_t id)
 
   for (ki = slot->info.kinfo; ki; ki = ki->next)
     {
-      err = scute_gpgsm_get_cert (ki->grip, ki->keyref, add_object, slot);
+      err = scute_gpgsm_get_cert (ki, add_object, slot);
       if (err)
         goto leave;
     }
@@ -799,6 +804,7 @@ slot_create_session (slot_iterator_t id, session_iterator_t *session,
   session_p->search_result = NULL;
   session_p->search_result_len = 0;
   session_p->signing_key = CK_INVALID_HANDLE;
+  session_p->decryption_key = CK_INVALID_HANDLE;
 
   *session = SESSION_BUILD_ID (id, tsid);
 
@@ -1046,9 +1052,6 @@ session_sign (slot_iterator_t id, session_iterator_t sid,
   int i;
   const char *keyref;
 
-  if (!pSignature)
-    return CKR_ARGUMENTS_BAD;
-
   if (!session->signing_key)
     return CKR_OPERATION_NOT_INITIALIZED;
 
@@ -1099,9 +1102,139 @@ session_sign (slot_iterator_t id, session_iterator_t sid,
     rc = CKR_ARGUMENTS_BAD;
   else
     rc = scute_gpg_err_to_ck (err);
+  /* Return the length.  */
+  if (rc == CKR_OK || rc == CKR_BUFFER_TOO_SMALL)
+    *pulSignatureLen = sig_len;
 
  leave:
   if (rc != CKR_OK && rc != CKR_BUFFER_TOO_SMALL)
     session->signing_key = 0;
   return rc;
 }
+
+
+/* Prepare a decryption for slot with SLOTID and the session SID using
+ * MECHANISM and KEY.  This is the core of C_DecryptInit.  */
+CK_RV
+session_init_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+                      CK_MECHANISM *mechanism, object_iterator_t key)
+{
+  struct slot *slot;
+  struct session *session;
+  CK_RV rv;
+  CK_ATTRIBUTE_PTR attr;
+  CK_ULONG attr_count;
+  CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+
+  if (mechanism->mechanism != CKM_RSA_PKCS)
+    return CKR_MECHANISM_INVALID;
+
+  slot = scute_table_data (slots, slotid);
+  session = scute_table_data (slot->sessions, sid);
+
+  rv = slot_get_object (slotid, key, &attr, &attr_count);
+  if (rv)
+    return rv;
+
+  /* FIXME: What kind of strange loop is this?  */
+  while (attr_count-- > 0)
+    if (attr->type == CKA_CLASS)
+      break;
+
+  if (attr_count == (CK_ULONG) -1)
+    return CKR_KEY_HANDLE_INVALID;
+
+  if (attr->ulValueLen != sizeof (key_class)
+      || memcmp (attr->pValue, &key_class, sizeof (key_class)))
+    return CKR_KEY_HANDLE_INVALID;
+
+  /* It's the private RSA key object.  */
+  session->decryption_key = key;
+
+  return 0;
+}
+
+
+/* The core of C_Decrypt - see there for a description.  */
+CK_RV
+session_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+                 CK_BYTE *encdata, CK_ULONG encdatalen,
+                 CK_BYTE *r_plaindata, CK_ULONG *r_plaindatalen)
+{
+  CK_RV rv;
+  gpg_error_t err;
+  struct slot *slot;
+  struct session *session;
+  CK_ATTRIBUTE_PTR attr;
+  CK_ULONG attr_count;
+  CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+  CK_BYTE key_id[100];
+  int i;
+  const char *keyref;
+  unsigned int plaindatalen;
+
+  slot = scute_table_data (slots, slotid);
+  session = scute_table_data (slot->sessions, sid);
+
+  if (!session->decryption_key)
+    return CKR_OPERATION_NOT_INITIALIZED;
+
+  rv = slot_get_object (slotid, session->decryption_key, &attr, &attr_count);
+  if (rv)
+    goto leave;
+  if (attr_count == (CK_ULONG) -1)
+    {
+      rv = CKR_KEY_HANDLE_INVALID;
+      goto leave;
+    }
+  if (attr->ulValueLen != sizeof (key_class)
+      || memcmp (attr->pValue, &key_class, sizeof (key_class)))
+    {
+      rv = CKR_KEY_HANDLE_INVALID;
+      goto leave;
+    }
+
+  /* Find the CKA_ID */
+  for (i = 0; i < attr_count; i++)
+    if (attr[i].type == CKA_ID)
+      break;
+  if (i == attr_count)
+    {
+      rv = CKR_GENERAL_ERROR;
+      goto leave;
+    }
+
+  if (attr[i].ulValueLen >= sizeof key_id - 1)
+    {
+      rv = CKR_GENERAL_ERROR;
+      goto leave;
+    }
+  strncpy (key_id, attr[i].pValue, attr[i].ulValueLen);
+  key_id[attr[i].ulValueLen] = 0;
+  DEBUG (DBG_INFO, "Found CKA_ID '%s'", key_id);
+  for (keyref=key_id; *keyref && *keyref != ' '; keyref++)
+    ;
+  if (*keyref)
+    keyref++;  /* Point to the grip.  */
+
+  plaindatalen = *r_plaindatalen;
+  err = scute_agent_decrypt (keyref, encdata, encdatalen,
+                             r_plaindata, &plaindatalen);
+  DEBUG (DBG_INFO, "agent returned gpg error %d datalen=%u", err, plaindatalen);
+  /* Take care of error codes which are not mapped by default.  */
+  if (gpg_err_code (err) == GPG_ERR_INV_LENGTH)
+    rv = CKR_BUFFER_TOO_SMALL;
+  else if (gpg_err_code (err) == GPG_ERR_INV_ARG)
+    rv = CKR_ARGUMENTS_BAD;
+  else
+    rv = scute_gpg_err_to_ck (err);
+  /* Return the length.  */
+  if (rv == CKR_OK || rv == CKR_BUFFER_TOO_SMALL)
+    *r_plaindatalen = plaindatalen;
+
+ leave:
+  if (rv != CKR_BUFFER_TOO_SMALL)
+    session->decryption_key = 0;
+  DEBUG (DBG_INFO, "leaving decrypt with rv=%lu", rv);
+  return rv;
+}
index 6783219..b2fa224 100644 (file)
@@ -199,12 +199,23 @@ CK_RV session_get_search_result (slot_iterator_t id, session_iterator_t sid,
                                 object_iterator_t **search_result,
                                 int *search_result_len);
 
-/* Set the signing key for session SID in slot ID to KEY.  */
+/* The core of C_SignInit.  */
 CK_RV session_set_signing_key (slot_iterator_t id, session_iterator_t sid,
                               object_iterator_t key);
 
+/* The core of C_Sign.  */
 CK_RV session_sign (slot_iterator_t id, session_iterator_t sid,
                    CK_BYTE_PTR pData, CK_ULONG ulDataLen,
                    CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen);
 
+/* The core of C_DecryptInit.  */
+CK_RV session_init_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+                            CK_MECHANISM *mechanism, object_iterator_t key);
+
+/* The core of C_Decrypt.  */
+CK_RV session_decrypt (slot_iterator_t slotid, session_iterator_t sid,
+                       CK_BYTE *encdata, CK_ULONG encdatalen,
+                       CK_BYTE *r_plaindata, CK_ULONG *r_plaindatalen);
+
+
 #endif /* !SLOTS_H */