Implement /run/user/UID/gnupg based sockets.
[gnupg.git] / agent / cvt-openpgp.c
index 6ea2666..40d9a3e 100644 (file)
@@ -1,6 +1,5 @@
 /* cvt-openpgp.c - Convert an OpenPGP key to our internal format.
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
- *               2010 Free Software Foundation, Inc.
+ * Copyright (C) 1998-2002, 2006, 2009, 2010 Free Software Foundation, Inc.
  * Copyright (C) 2013, 2014 Werner Koch
  *
  * This file is part of GnuPG.
@@ -28,6 +27,7 @@
 #include "agent.h"
 #include "i18n.h"
 #include "cvt-openpgp.h"
+#include "host2net.h"
 
 
 /* Helper to pass data via the callback to do_unprotect. */
@@ -83,14 +83,19 @@ get_keygrip (int pubkey_algo, const char *curve, gcry_mpi_t *pkey,
     case GCRY_PK_ECC:
       if (!curve)
         err = gpg_error (GPG_ERR_BAD_SECKEY);
-      else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
-        err = gcry_sexp_build (&s_pkey, NULL,
-                               "(public-key(ecc(curve %s)(flags eddsa)(q%m)))",
-                               "Ed25519", pkey[0]);
       else
-        err = gcry_sexp_build (&s_pkey, NULL,
-                               "(public-key(ecc(curve %s)(q%m)))",
-                               curve, pkey[0]);
+        {
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            format = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(public-key(ecc(curve %s)(flags djb-tweak)(q%m)))";
+          else
+            format = "(public-key(ecc(curve %s)(q%m)))";
+
+          err = gcry_sexp_build (&s_pkey, NULL, format, curve, pkey[0]);
+        }
       break;
 
     default:
@@ -146,19 +151,21 @@ convert_secret_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
     case GCRY_PK_ECC:
       if (!curve)
         err = gpg_error (GPG_ERR_BAD_SECKEY);
-      else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
+      else
         {
-          /* Do not store the OID as name but the real name and the
-             EdDSA flag.  */
-          err = gcry_sexp_build (&s_skey, NULL,
-                                 "(private-key(ecc(curve%s)(flags eddsa)"
-                                 "(q%m)(d%m)))",
-                                 "Ed25519", skey[0], skey[1]);
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            /* Do not store the OID as name but the real name and the
+               EdDSA flag.  */
+            format = "(private-key(ecc(curve %s)(flags eddsa)(q%m)(d%m)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(private-key(ecc(curve %s)(flags djb-tweak)(q%m)(d%m)))";
+          else
+            format = "(private-key(ecc(curve %s)(q%m)(d%m)))";
+
+          err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], skey[1]);
         }
-      else
-        err = gcry_sexp_build (&s_skey, NULL,
-                               "(private-key(ecc(curve%s)(q%m)(d%m)))",
-                               curve, skey[0], skey[1]);
       break;
 
     default:
@@ -216,22 +223,24 @@ convert_transfer_key (gcry_sexp_t *r_key, int pubkey_algo, gcry_mpi_t *skey,
     case GCRY_PK_ECC:
       if (!curve)
         err = gpg_error (GPG_ERR_BAD_SECKEY);
-      else if (!strcmp (curve, openpgp_curve_to_oid ("Ed25519", NULL)))
+      else
         {
-          /* Do not store the OID as name but the real name and the
-             EdDSA flag.  */
-          err = gcry_sexp_build
-            (&s_skey, NULL,
-             "(protected-private-key(ecc(curve%s)(flags eddsa)(q%m)"
-             "(protected openpgp-native%S)))",
-             "Ed25519", skey[0], transfer_key);
+          const char *format;
+
+          if (!strcmp (curve, "Ed25519"))
+            /* Do not store the OID as name but the real name and the
+               EdDSA flag.  */
+            format = "(protected-private-key(ecc(curve %s)(flags eddsa)(q%m)"
+              "(protected openpgp-native%S)))";
+          else if (!strcmp (curve, "Curve25519"))
+            format = "(protected-private-key(ecc(curve %s)(flags djb-tweak)(q%m)"
+              "(protected openpgp-native%S)))";
+          else
+            format = "(protected-private-key(ecc(curve %s)(q%m)"
+              "(protected openpgp-native%S)))";
+
+          err = gcry_sexp_build (&s_skey, NULL, format, curve, skey[0], transfer_key);
         }
-      else
-        err = gcry_sexp_build
-          (&s_skey, NULL,
-           "(protected-private-key(ecc(curve%s)(q%m)"
-           "(protected openpgp-native%S)))",
-           curve, skey[0], transfer_key);
       break;
 
     default:
@@ -488,7 +497,7 @@ do_unprotect (const char *passphrase,
       ndata = (ndatabits+7)/8;
 
       if (ndata > 1)
-        csum_pgp7 = p[ndata-2] << 8 | p[ndata-1];
+        csum_pgp7 = buf16_to_u16 (p+ndata-2);
       data = xtrymalloc_secure (ndata);
       if (!data)
         {
@@ -532,7 +541,7 @@ do_unprotect (const char *passphrase,
             }
           else
             {
-              desired_csum = (data[ndata-2] << 8 | data[ndata-1]);
+              desired_csum = buf16_to_u16 (data+ndata-2);
               actual_csum = checksum (data, ndata-2);
               if (desired_csum != actual_csum)
                 {
@@ -587,7 +596,7 @@ do_unprotect (const char *passphrase,
           p = gcry_mpi_get_opaque (skey[i], &ndatabits);
           ndata = (ndatabits+7)/8;
 
-          if (!(ndata >= 2) || !(ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2))
+          if (!(ndata >= 2) || !(ndata == (buf16_to_ushort (p) + 7)/8 + 2))
             {
               gcry_cipher_close (cipher_hd);
               return gpg_error (GPG_ERR_BAD_SECKEY);
@@ -648,7 +657,7 @@ do_unprotect (const char *passphrase,
 
 /* Callback function to try the unprotection from the passphrase query
    code.  */
-static int
+static gpg_error_t
 try_do_unprotect_cb (struct pin_entry_info_s *pi)
 {
   gpg_error_t err;
@@ -676,7 +685,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
    silently decrypt the key; CACHE_NONCE and R_PASSPHRASE must both be
    NULL in this mode.  */
 static gpg_error_t
-convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
+convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
                            unsigned char *grip, const char *prompt,
                            const char *cache_nonce, const char *passphrase,
                            unsigned char **r_key, char **r_passphrase)
@@ -829,15 +838,13 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       value = gcry_sexp_nth_data (list, ++idx, &valuelen);
       if (!value || !valuelen)
         goto bad_seckey;
-      if (is_enc || curve)
+      if (is_enc)
         {
-          /* Encrypted parameters and ECC parameters need or can be
-             stored as opaque.  */
+          /* Encrypted parameters need to be stored as opaque.  */
           skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
           if (!skey[skeyidx])
             goto outofmem;
-          if (is_enc)
-            gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
+          gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
         }
       else
         {
@@ -887,7 +894,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
   if (err)
     goto leave;
 
-  if (!from_native && !agent_key_available (grip))
+  if (!dontcare_exist && !from_native && !agent_key_available (grip))
     {
       err = gpg_error (GPG_ERR_EEXIST);
       goto leave;
@@ -909,10 +916,10 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
       struct pin_entry_info_s *pi;
       struct try_do_unprotect_arg_s pi_arg;
 
-      pi = xtrycalloc_secure (1, sizeof (*pi) + 100);
+      pi = xtrycalloc_secure (1, sizeof (*pi) + MAX_PASSPHRASE_LEN + 1);
       if (!pi)
         return gpg_error_from_syserror ();
-      pi->max_length = 100;
+      pi->max_length = MAX_PASSPHRASE_LEN + 1;
       pi->min_digits = 0;  /* We want a real passphrase.  */
       pi->max_digits = 16;
       pi->max_tries = 3;
@@ -961,7 +968,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
           err = try_do_unprotect_cb (pi);
         }
       if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE && !from_native)
-        err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
+        err = agent_askpin (ctrl, prompt, NULL, NULL, pi, NULL, 0);
       skeyidx = pi_arg.skeyidx;
       if (!err && r_passphrase && is_protected)
         {
@@ -1021,12 +1028,12 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp,
    the key.  The keygrip will be stored at the 20 byte buffer pointed
    to by GRIP.  On error NULL is stored at all return arguments.  */
 gpg_error_t
-convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
+convert_from_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist,
                       unsigned char *grip, const char *prompt,
                       const char *cache_nonce,
                       unsigned char **r_key, char **r_passphrase)
 {
-  return convert_from_openpgp_main (ctrl, s_pgp, grip, prompt,
+  return convert_from_openpgp_main (ctrl, s_pgp, dontcare_exist, grip, prompt,
                                     cache_nonce, NULL,
                                     r_key, r_passphrase);
 }
@@ -1045,20 +1052,32 @@ convert_from_openpgp_native (ctrl_t ctrl,
   if (!passphrase)
     return gpg_error (GPG_ERR_INTERNAL);
 
-  err = convert_from_openpgp_main (ctrl, s_pgp, grip, NULL,
+  err = convert_from_openpgp_main (ctrl, s_pgp, 0, grip, NULL,
                                    NULL, passphrase,
                                    r_key, NULL);
 
   /* On success try to re-write the key.  */
   if (!err)
     {
-      unsigned char *protectedkey = NULL;
-      size_t protectedkeylen;
-
-      if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen,
-                          ctrl->s2k_count))
-        agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
-      xfree (protectedkey);
+      if (*passphrase)
+        {
+          unsigned char *protectedkey = NULL;
+          size_t protectedkeylen;
+
+          if (!agent_protect (*r_key, passphrase,
+                              &protectedkey, &protectedkeylen,
+                              ctrl->s2k_count, -1))
+            agent_write_private_key (grip, protectedkey, protectedkeylen, 1);
+          xfree (protectedkey);
+        }
+      else
+        {
+          /* Empty passphrase: write key without protection.  */
+          agent_write_private_key (grip,
+                                   *r_key,
+                                   gcry_sexp_canon_len (*r_key, 0, NULL,NULL),
+                                   1);
+        }
     }
 
   return err;
@@ -1093,36 +1112,14 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey,
   ndata = 20; /* Space for the SHA-1 checksum.  */
   for (i = npkey, j = 0; i < nskey; i++, j++ )
     {
-      if (gcry_mpi_get_flag (array[i], GCRYMPI_FLAG_OPAQUE))
-        {
-          const void *s;
-          unsigned int n;
-
-          s = gcry_mpi_get_opaque (array[i], &n);
-          nbits[j] = n;
-          n = (n+7)/8;
-          narr[j] = n;
-          bufarr[j] = gcry_is_secure (s)? xtrymalloc_secure (n):xtrymalloc (n);
-          if (!bufarr[j])
-            {
-              err = gpg_error_from_syserror ();
-              for (i = 0; i < j; i++)
-                xfree (bufarr[i]);
-              return err;
-            }
-          memcpy (bufarr[j], s, n);
-        }
-      else
+      err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
+      if (err)
         {
-          err = gcry_mpi_aprint (GCRYMPI_FMT_USG, bufarr+j, narr+j, array[i]);
-          if (err)
-            {
-              for (i = 0; i < j; i++)
-                xfree (bufarr[i]);
-              return err;
-            }
-          nbits[j] = gcry_mpi_get_nbits (array[i]);
+          for (i = 0; i < j; i++)
+            xfree (bufarr[i]);
+          return err;
         }
+      nbits[j] = gcry_mpi_get_nbits (array[i]);
       ndata += 2 + narr[j];
     }
 
@@ -1178,36 +1175,55 @@ apply_protection (gcry_mpi_t *array, int npkey, int nskey,
 }
 
 
-/* Convert our key S_KEY into an OpenPGP key transfer format.  On
-   success a canonical encoded S-expression is stored at R_TRANSFERKEY
-   and its length at R_TRANSFERKEYLEN; this S-expression is also
-   padded to a multiple of 64 bits.  */
+/*
+ * Examining S_KEY in S-Expression and extract data.
+ * When REQ_PRIVATE_KEY_DATA == 1, S_KEY's CAR should be 'private-key',
+ * but it also allows shadowed or protected versions.
+ * On success, it returns 0, otherwise error number.
+ * R_ALGONAME is static string which is no need to free by caller.
+ * R_NPKEY is pointer to number of public key data.
+ * R_NSKEY is pointer to number of private key data.
+ * R_ELEMS is static string which is no need to free by caller.
+ * ARRAY contains public and private key data.
+ * ARRAYSIZE is the allocated size of the array for cross-checking.
+ * R_CURVE is pointer to S-Expression of the curve (can be NULL).
+ * R_FLAGS is pointer to S-Expression of the flags (can be NULL).
+ */
 gpg_error_t
-convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
-                    unsigned char **r_transferkey, size_t *r_transferkeylen)
+extract_private_key (gcry_sexp_t s_key, int req_private_key_data,
+                     const char **r_algoname, int *r_npkey, int *r_nskey,
+                     const char **r_elems,
+                     gcry_mpi_t *array, int arraysize,
+                     gcry_sexp_t *r_curve, gcry_sexp_t *r_flags)
 {
   gpg_error_t err;
   gcry_sexp_t list, l2;
   char *name;
-  const char *algoname;
+  const char *algoname, *format;
   int npkey, nskey;
-  gcry_mpi_t array[10];
   gcry_sexp_t curve = NULL;
-  char protect_iv[16];
-  char salt[8];
-  unsigned long s2k_count;
-  int i, j;
+  gcry_sexp_t flags = NULL;
 
-  (void)ctrl;
-
-  *r_transferkey = NULL;
+  *r_curve = NULL;
+  *r_flags = NULL;
 
-  for (i=0; i < DIM (array); i++)
-    array[i] = NULL;
+  if (!req_private_key_data)
+    {
+      list = gcry_sexp_find_token (s_key, "shadowed-private-key", 0 );
+      if (!list)
+        list = gcry_sexp_find_token (s_key, "protected-private-key", 0 );
+      if (!list)
+        list = gcry_sexp_find_token (s_key, "private-key", 0 );
+    }
+  else
+    list = gcry_sexp_find_token (s_key, "private-key", 0);
 
-  list = gcry_sexp_find_token (s_key, "private-key", 0);
   if (!list)
-    return gpg_error (GPG_ERR_NO_OBJ); /* Does not contain a key object.  */
+    {
+      log_error ("invalid private key format\n");
+      return gpg_error (GPG_ERR_BAD_SECKEY);
+    }
+
   l2 = gcry_sexp_cadr (list);
   gcry_sexp_release (list);
   list = l2;
@@ -1218,6 +1234,9 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
     }
 
+  if (arraysize < 7)
+    BUG ();
+
   /* Map NAME to a name as used by Libgcrypt.  We do not use the
      Libgcrypt function here because we need a lowercase name and
      require special treatment for some algorithms.  */
@@ -1225,80 +1244,101 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   if (!strcmp (name, "rsa"))
     {
       algoname = "rsa";
+      format = "ned?p?q?u?";
       npkey = 2;
       nskey = 6;
-      err = gcry_sexp_extract_param (list, NULL, "nedpqu",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      array+4, array+5, NULL);
     }
   else if (!strcmp (name, "elg"))
     {
       algoname = "elg";
+      format = "pgyx?";
       npkey = 3;
       nskey = 4;
-      err = gcry_sexp_extract_param (list, NULL, "pgyx",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      NULL);
     }
   else if (!strcmp (name, "dsa"))
     {
       algoname = "dsa";
+      format = "pqgyx?";
       npkey = 4;
       nskey = 5;
-      err = gcry_sexp_extract_param (list, NULL, "pqgyx",
+      err = gcry_sexp_extract_param (list, NULL, format,
                                      array+0, array+1, array+2, array+3,
                                      array+4, NULL);
     }
   else if (!strcmp (name, "ecc"))
     {
-      gcry_buffer_t iob;
-      char iobbuf[32];
-
-      algoname = "ecc"; /* Decide later by checking the usage.  */
+      algoname = "ecc";
+      format = "qd?";
       npkey = 1;
       nskey = 2;
-      iob.data = iobbuf;
-      iob.size = sizeof iobbuf - 1;
-      iob.off = 0;
-      iob.len = 0;
-      err = gcry_sexp_extract_param (list, NULL, "&'curve'/qd",
-                                     &iob, array+0, array+1, NULL);
-      if (!err)
-        {
-          assert (iob.len < sizeof iobbuf -1);
-          iobbuf[iob.len] = 0;
-          err = gcry_sexp_build (&curve, NULL, "(curve %s)", iobbuf);
-        }
-    }
-  else if (!strcmp (name, "ecdsa"))
-    {
-      algoname = "ecdsa";
-      npkey = 6;
-      nskey = 7;
-      err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
-                                     array+0, array+1, array+2, array+3,
-                                     array+4, array+5, array+6, NULL);
-    }
-  else if (!strcmp (name, "ecdh"))
-    {
-      algoname = "ecdh";
-      npkey = 6;
-      nskey= 7;
-      err = gcry_sexp_extract_param (list, NULL, "pabgnqd",
-                                     array+0, array+1, array+2, array+3,
-                                     array+4, array+5, array+6, NULL);
+      curve = gcry_sexp_find_token (list, "curve", 0);
+      flags = gcry_sexp_find_token (list, "flags", 0);
+      err = gcry_sexp_extract_param (list, NULL, format,
+                                     array+0, array+1, NULL);
     }
   else
     {
       err = gpg_error (GPG_ERR_PUBKEY_ALGO);
     }
   xfree (name);
-  gcry_sexp_release (list); list = NULL;
+  gcry_sexp_release (list);
   if (err)
     {
       gcry_sexp_release (curve);
+      gcry_sexp_release (flags);
       return err;
     }
+  else
+    {
+      *r_algoname = algoname;
+      if (r_elems)
+        *r_elems = format;
+      *r_npkey = npkey;
+      if (r_nskey)
+        *r_nskey = nskey;
+      *r_curve = curve;
+      *r_flags = flags;
+
+      return 0;
+    }
+}
+
+/* Convert our key S_KEY into an OpenPGP key transfer format.  On
+   success a canonical encoded S-expression is stored at R_TRANSFERKEY
+   and its length at R_TRANSFERKEYLEN; this S-expression is also
+   padded to a multiple of 64 bits.  */
+gpg_error_t
+convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
+                    unsigned char **r_transferkey, size_t *r_transferkeylen)
+{
+  gpg_error_t err;
+  const char *algoname;
+  int npkey, nskey;
+  gcry_mpi_t array[10];
+  gcry_sexp_t curve = NULL;
+  gcry_sexp_t flags = NULL;
+  char protect_iv[16];
+  char salt[8];
+  unsigned long s2k_count;
+  int i, j;
+
+  (void)ctrl;
+
+  *r_transferkey = NULL;
+
+  for (i=0; i < DIM (array); i++)
+    array[i] = NULL;
+
+  err = extract_private_key (s_key, 1, &algoname, &npkey, &nskey, NULL,
+                             array, DIM (array), &curve, &flags);
+  if (err)
+    return err;
 
   gcry_create_nonce (protect_iv, sizeof protect_iv);
   gcry_create_nonce (salt, sizeof salt);
@@ -1364,6 +1404,7 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   for (i=0; i < DIM (array); i++)
     gcry_mpi_release (array[i]);
   gcry_sexp_release (curve);
+  gcry_sexp_release (flags);
 
   return err;
 }