sm: Support generation of card-based ECDSA CSR.
authorDamien Goutte-Gattat via Gnupg-devel <gnupg-devel@gnupg.org>
Fri, 16 Nov 2018 01:27:37 +0000 (01:27 +0000)
committerNIIBE Yutaka <gniibe@fsij.org>
Fri, 15 Feb 2019 02:01:20 +0000 (11:01 +0900)
* sm/call-agent.c (gpgsm_scd_pksign): Identify type of signing key
and format resulting S-expression accordingly.
* sm/misc.c (transform_sigval): Support ECDSA signatures.
--

Current GpgSM implementation assumes card-based keys are RSA keys.
This patch introduces support for ECDSA keys.

By itself this patch is not sufficient, we also need support
from libksba.

GnuPG-bug-id: 4092
Signed-off-by: Damien Goutte-Gattat <dgouttegattat@incenp.org>
sm/call-agent.c
sm/misc.c

index 20d879f..6ac715f 100644 (file)
@@ -334,7 +334,7 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
                   unsigned char *digest, size_t digestlen, int digestalgo,
                   unsigned char **r_buf, size_t *r_buflen )
 {
-  int rc, i;
+  int rc, i, pkalgo;
   char *p, line[ASSUAN_LINELENGTH];
   membuf_t data;
   size_t len;
@@ -342,6 +342,7 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
   unsigned char *sigbuf;
   size_t sigbuflen;
   struct default_inq_parm_s inq_parm;
+  gcry_sexp_t sig;
 
   (void)desc;
 
@@ -366,6 +367,23 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
   if (digestlen*2 + 50 > DIM(line))
     return gpg_error (GPG_ERR_GENERAL);
 
+  /* Get the key type from the scdaemon. */
+  snprintf (line, DIM(line), "SCD READKEY %s", keyid);
+  init_membuf (&data, 1024);
+  rc = assuan_transact (agent_ctx, line,
+                        put_membuf_cb, &data, NULL, NULL, NULL, NULL);
+  if (rc)
+    {
+      xfree (get_membuf (&data, &len));
+      return rc;
+    }
+
+  p = get_membuf (&data, &len);
+  pkalgo = get_pk_algo_from_canon_sexp (p, len);
+  xfree (p);
+  if (!pkalgo)
+    return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+
   p = stpcpy (line, "SCD SETDATA " );
   for (i=0; i < digestlen ; i++, p += 2 )
     sprintf (p, "%02X", digest[i]);
@@ -386,24 +404,31 @@ gpgsm_scd_pksign (ctrl_t ctrl, const char *keyid, const char *desc,
     }
   sigbuf = get_membuf (&data, &sigbuflen);
 
-  /* Create an S-expression from it which is formatted like this:
-     "(7:sig-val(3:rsa(1:sSIGBUFLEN:SIGBUF)))" Fixme: If a card ever
-     creates non-RSA keys we need to change things. */
-  *r_buflen = 21 + 11 + sigbuflen + 4;
-  p = xtrymalloc (*r_buflen);
-  *r_buf = (unsigned char*)p;
-  if (!p)
+  switch(pkalgo)
     {
-      xfree (sigbuf);
-      return 0;
+    case GCRY_PK_RSA:
+      rc = gcry_sexp_build (&sig, NULL, "(sig-val(rsa(s%b)))",
+                            sigbuflen, sigbuf);
+      break;
+
+    case GCRY_PK_ECC:
+      rc = gcry_sexp_build (&sig, NULL, "(sig-val(ecdsa(r%b)(s%b)))",
+                            sigbuflen/2, sigbuf,
+                            sigbuflen/2, sigbuf + sigbuflen/2);
+      break;
+
+    default:
+      rc = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+      break;
     }
-  p = stpcpy (p, "(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);
+  if (rc)
+    return rc;
+
+  rc = make_canon_sexp (sig, r_buf, r_buflen);
+  gcry_sexp_release (sig);
+  if (rc)
+    return rc;
 
   assert (gcry_sexp_canon_len (*r_buf, *r_buflen, NULL, NULL));
   return  0;
index 6d04776..9bf5285 100644 (file)
--- a/sm/misc.c
+++ b/sm/misc.c
@@ -109,13 +109,16 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
   gpg_error_t err;
   const unsigned char *buf, *tok;
   size_t buflen, toklen;
-  int depth, last_depth1, last_depth2;
+  int depth, last_depth1, last_depth2, pkalgo;
   int is_pubkey = 0;
-  const unsigned char *rsa_s = NULL;
-  size_t rsa_s_len = 0;
+  const unsigned char *rsa_s, *ecc_r, *ecc_s;
+  size_t rsa_s_len, ecc_r_len, ecc_s_len;
   const char *oid;
   gcry_sexp_t sexp;
 
+  rsa_s = ecc_r = ecc_s = NULL;
+  rsa_s_len = ecc_r_len = ecc_s_len = 0;
+
   *r_newsigval = NULL;
   if (r_newsigvallen)
     *r_newsigvallen = 0;
@@ -137,7 +140,13 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
     return err;
   if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
     return err;
-  if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+  if (!tok)
+    return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+  if (toklen == 3 && !memcmp ("rsa", tok, 3))
+    pkalgo = GCRY_PK_RSA;
+  else if (toklen == 5 && !memcmp ("ecdsa", tok, 5))
+    pkalgo = GCRY_PK_ECC;
+  else
     return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
 
   last_depth1 = depth;
@@ -150,16 +159,27 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
         return err;
       if (tok && toklen == 1)
         {
-          const unsigned char **mpi;
-          size_t *mpi_len;
+          const unsigned char **mpi = NULL;
+          size_t *mpi_len = NULL;
 
           switch (*tok)
             {
-            case 's': mpi = &rsa_s; mpi_len = &rsa_s_len; break;
+            case 's':
+              if (pkalgo == GCRY_PK_RSA)
+                {
+                  mpi = &rsa_s;
+                  mpi_len = &rsa_s_len;
+                }
+              else if (pkalgo == GCRY_PK_ECC)
+                {
+                  mpi = &ecc_s;
+                  mpi_len = &ecc_s_len;
+                }
+              break;
+
+            case 'r': mpi = &ecc_r; mpi_len = &ecc_r_len; break;
             default:  mpi = NULL;   mpi_len = NULL; break;
             }
-          if (mpi && *mpi)
-            return gpg_error (GPG_ERR_DUP_VALUE);
 
           if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
             return err;
@@ -182,33 +202,52 @@ transform_sigval (const unsigned char *sigval, size_t sigvallen, int mdalgo,
     return err;
 
   /* Map the hash algorithm to an OID.  */
-  switch (mdalgo)
+  switch (mdalgo | (pkalgo << 8))
     {
-    case GCRY_MD_SHA1:
+    case GCRY_MD_SHA1 | (GCRY_PK_RSA << 8):
       oid = "1.2.840.113549.1.1.5";  /* sha1WithRSAEncryption */
       break;
 
-    case GCRY_MD_SHA256:
+    case GCRY_MD_SHA256 | (GCRY_PK_RSA << 8):
       oid = "1.2.840.113549.1.1.11"; /* sha256WithRSAEncryption */
       break;
 
-    case GCRY_MD_SHA384:
+    case GCRY_MD_SHA384 | (GCRY_PK_RSA << 8):
       oid = "1.2.840.113549.1.1.12"; /* sha384WithRSAEncryption */
       break;
 
-    case GCRY_MD_SHA512:
+    case GCRY_MD_SHA512 | (GCRY_PK_RSA << 8):
       oid = "1.2.840.113549.1.1.13"; /* sha512WithRSAEncryption */
       break;
 
+    case GCRY_MD_SHA224 | (GCRY_PK_ECC << 8):
+      oid = "1.2.840.10045.4.3.1"; /* ecdsa-with-sha224 */
+      break;
+
+    case GCRY_MD_SHA256 | (GCRY_PK_ECC << 8):
+      oid = "1.2.840.10045.4.3.2"; /* ecdsa-with-sha256 */
+      break;
+
+    case GCRY_MD_SHA384 | (GCRY_PK_ECC << 8):
+      oid = "1.2.840.10045.4.3.3"; /* ecdsa-with-sha384 */
+      break;
+
+    case GCRY_MD_SHA512 | (GCRY_PK_ECC << 8):
+      oid = "1.2.840.10045.4.3.4"; /* ecdsa-with-sha512 */
+      break;
+
     default:
       return gpg_error (GPG_ERR_DIGEST_ALGO);
     }
 
-  if (rsa_s && !is_pubkey)
-    err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))",
-                           oid, (int)rsa_s_len, rsa_s);
-  else
+  if (is_pubkey)
     err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s))", oid);
+  else if (pkalgo == GCRY_PK_RSA)
+    err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(s%b)))", oid,
+                           (int)rsa_s_len, rsa_s);
+  else if (pkalgo == GCRY_PK_ECC)
+    err = gcry_sexp_build (&sexp, NULL, "(sig-val(%s(r%b)(s%b)))", oid,
+                           (int)ecc_r_len, ecc_r, (int)ecc_s_len, ecc_s);
   if (err)
     return err;
   err = make_canon_sexp (sexp, r_newsigval, r_newsigvallen);