Remove keyserver helper code.
[gnupg.git] / agent / protect.c
index 0b8c9b4..3a00218 100644 (file)
@@ -1,6 +1,7 @@
 /* protect.c - Un/Protect a secret key
  * Copyright (C) 1998, 1999, 2000, 2001, 2002,
  *               2003, 2007, 2009, 2011 Free Software Foundation, Inc.
+ * Copyright (C) 2013 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -28,6 +29,9 @@
 #include <unistd.h>
 #include <sys/stat.h>
 #ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_WINSOCK2_H
+#  include <winsock2.h>
+# endif
 # include <windows.h>
 #else
 # include <sys/times.h>
 
 #include "agent.h"
 
+#include "cvt-openpgp.h"
 #include "sexp-parse.h"
 
 #define PROT_CIPHER        GCRY_CIPHER_AES
 #define PROT_CIPHER_STRING "aes"
 #define PROT_CIPHER_KEYLEN (128/8)
 
+/* Decode an rfc4880 encoded S2K count.  */
+#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6))
+
 
 /* A table containing the information needed to create a protected
    private key.  */
@@ -48,13 +56,14 @@ static struct {
   const char *algo;
   const char *parmlist;
   int prot_from, prot_to;
+  int ecc_hack;
 } protect_info[] = {
   { "rsa",  "nedpqu", 2, 5 },
   { "dsa",  "pqgyx", 4, 4 },
   { "elg",  "pgyx", 3, 3 },
-  { "ecdsa","pabgnqd", 6, 6 },
-  { "ecdh", "pabgnqd", 6, 6 },
-  { "ecc",  "pabgnqd", 6, 6 },
+  { "ecdsa","pabgnqd", 6, 6, 1 },
+  { "ecdh", "pabgnqd", 6, 6, 1 },
+  { "ecc",  "pabgnqd", 6, 6, 1 },
   { NULL }
 };
 
@@ -192,6 +201,33 @@ get_standard_s2k_count (void)
 }
 
 
+/* Same as get_standard_s2k_count but return the count in the encoding
+   as described by rfc4880.  */
+unsigned char
+get_standard_s2k_count_rfc4880 (void)
+{
+  unsigned long iterations;
+  unsigned int count;
+  unsigned char result;
+  unsigned char c=0;
+
+  iterations = get_standard_s2k_count ();
+  if (iterations >= 65011712)
+    return 255;
+
+  /* Need count to be in the range 16-31 */
+  for (count=iterations>>6; count>=32; count>>=1)
+    c++;
+
+  result = (c<<4)|(count-16);
+
+  if (S2K_DECODE_COUNT(result) < iterations)
+    result++;
+
+  return result;
+
+}
+
 
 \f
 /* Calculate the MIC for a private key or shared secret S-expression.
@@ -279,7 +315,8 @@ calculate_mic (const unsigned char *plainkey, unsigned char *sha1hash)
 static int
 do_encryption (const unsigned char *protbegin, size_t protlen,
                const char *passphrase,  const unsigned char *sha1hash,
-               unsigned char **result, size_t *resultlen)
+               unsigned char **result, size_t *resultlen,
+              unsigned long s2k_count)
 {
   gcry_cipher_hd_t hd;
   const char *modestr = "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc";
@@ -338,7 +375,8 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
         {
           rc = hash_passphrase (passphrase, GCRY_MD_SHA1,
                                 3, iv+2*blklen,
-                                get_standard_s2k_count (), key, keylen);
+                               s2k_count ? s2k_count : get_standard_s2k_count(),
+                               key, keylen);
           if (!rc)
             rc = gcry_cipher_setkey (hd, key, keylen);
           xfree (key);
@@ -381,7 +419,8 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
   {
     char countbuf[35];
 
-    snprintf (countbuf, sizeof countbuf, "%lu", get_standard_s2k_count ());
+    snprintf (countbuf, sizeof countbuf, "%lu",
+           s2k_count ? s2k_count : get_standard_s2k_count ());
     p = xtryasprintf
       ("(9:protected%d:%s((4:sha18:%n_8bytes_%u:%s)%d:%n%*s)%d:%n%*s)",
        (int)strlen (modestr), modestr,
@@ -413,9 +452,12 @@ do_encryption (const unsigned char *protbegin, size_t protlen,
    a valid S-Exp here. */
 int
 agent_protect (const unsigned char *plainkey, const char *passphrase,
-               unsigned char **result, size_t *resultlen)
+               unsigned char **result, size_t *resultlen,
+              unsigned long s2k_count)
 {
   int rc;
+  const char *parmlist;
+  int prot_from_idx, prot_to_idx;
   const unsigned char *s;
   const unsigned char *hash_begin, *hash_end;
   const unsigned char *prot_begin, *prot_end, *real_end;
@@ -428,6 +470,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   int depth = 0;
   unsigned char *p;
   gcry_md_hd_t md;
+  int have_curve = 0;
 
   /* Create an S-expression with the protected-at timestamp.  */
   memcpy (timestamp_exp, "(12:protected-at15:", 19);
@@ -460,10 +503,18 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   if (!protect_info[infidx].algo)
     return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
 
+  /* The parser below is a complete mess: To make it robust for ECC
+     use we should reorder the s-expression to include only what we
+     really need and thus guarantee the right order for saving stuff.
+     This should be done before calling this function and maybe with
+     the help of the new gcry_sexp_extract_param.  */
+  parmlist      = protect_info[infidx].parmlist;
+  prot_from_idx = protect_info[infidx].prot_from;
+  prot_to_idx   = protect_info[infidx].prot_to;
   prot_begin = prot_end = NULL;
-  for (i=0; (c=protect_info[infidx].parmlist[i]); i++)
+  for (i=0; (c=parmlist[i]); i++)
     {
-      if (i == protect_info[infidx].prot_from)
+      if (i == prot_from_idx)
         prot_begin = s;
       if (*s != '(')
         return gpg_error (GPG_ERR_INV_SEXP);
@@ -473,7 +524,29 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
       if (!n)
         return gpg_error (GPG_ERR_INV_SEXP);
       if (n != 1 || c != *s)
-        return gpg_error (GPG_ERR_INV_SEXP);
+        {
+          if (n == 5 && !memcmp (s, "curve", 5)
+              && !i && protect_info[infidx].ecc_hack)
+            {
+              /* This is a private ECC key but the first parameter is
+                 the name of the curve.  We change the parameter list
+                 here to the one we expect in this case.  */
+              have_curve = 1;
+              parmlist = "?qd";
+              prot_from_idx = 2;
+              prot_to_idx = 2;
+            }
+          else if (n == 5 && !memcmp (s, "flags", 5)
+                   && i == 1 && have_curve)
+            {
+              /* "curve" followed by "flags": Change again.  */
+              parmlist = "??qd";
+              prot_from_idx = 3;
+              prot_to_idx = 3;
+            }
+          else
+            return gpg_error (GPG_ERR_INV_SEXP);
+        }
       s += n;
       n = snext (&s);
       if (!n)
@@ -482,7 +555,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
       if (*s != ')')
         return gpg_error (GPG_ERR_INV_SEXP);
       depth--;
-      if (i == protect_info[infidx].prot_to)
+      if (i == prot_to_idx)
         prot_end = s;
       s++;
     }
@@ -499,7 +572,6 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   assert (!depth);
   real_end = s-1;
 
-
   /* Hash the stuff.  Because the timestamp_exp won't get protected,
      we can't simply hash a continuous buffer but need to use several
      md_writes.  */
@@ -514,7 +586,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
 
   rc = do_encryption (prot_begin, prot_end - prot_begin + 1,
                       passphrase,  hashvalue,
-                      &protected, &protectedlen);
+                      &protected, &protectedlen, s2k_count);
   if (rc)
     return rc;
 
@@ -552,6 +624,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase,
   return 0;
 }
 
+
 \f
 /* Do the actual decryption and check the return list for consistency.  */
 static int
@@ -780,9 +853,10 @@ merge_lists (const unsigned char *protectedkey,
 
 /* Unprotect the key encoded in canonical format.  We assume a valid
    S-Exp here.  If a protected-at item is available, its value will
-   be stored at protocted_at unless this is NULL.  */
+   be stored at protected_at unless this is NULL.  */
 int
-agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
+agent_unprotect (ctrl_t ctrl,
+                 const unsigned char *protectedkey, const char *passphrase,
                  gnupg_isotime_t protected_at,
                  unsigned char **result, size_t *resultlen)
 {
@@ -886,7 +960,30 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase,
   if (!n)
     return gpg_error (GPG_ERR_INV_SEXP);
   if (!smatch (&s, n, "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"))
-    return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+    {
+      if (smatch (&s, n, "openpgp-native"))
+        {
+          gcry_sexp_t s_prot_begin;
+
+          rc = gcry_sexp_sscan (&s_prot_begin, NULL,
+                                prot_begin,
+                                gcry_sexp_canon_len (prot_begin, 0,NULL,NULL));
+          if (rc)
+            return rc;
+
+          rc = convert_from_openpgp_native (ctrl,
+                                            s_prot_begin, passphrase, &final);
+          gcry_sexp_release (s_prot_begin);
+          if (!rc)
+            {
+              *result = final;
+              *resultlen = gcry_sexp_canon_len (final, 0, NULL, NULL);
+            }
+          return rc;
+        }
+      else
+        return gpg_error (GPG_ERR_UNSUPPORTED_PROTECTION);
+    }
   if (*s != '(' || s[1] != '(')
     return gpg_error (GPG_ERR_INV_SEXP);
   s += 2;
@@ -1023,7 +1120,11 @@ hash_passphrase (const char *passphrase, int hashalgo,
                  unsigned long s2kcount,
                  unsigned char *key, size_t keylen)
 {
-
+  /* The key derive function does not support a zero length string for
+     the passphrase in the S2K modes.  Return a better suited error
+     code than GPG_ERR_INV_DATA.  */
+  if (!passphrase || !*passphrase)
+    return gpg_error (GPG_ERR_NO_PASSPHRASE);
   return gcry_kdf_derive (passphrase, strlen (passphrase),
                           s2kmode == 3? GCRY_KDF_ITERSALTED_S2K :
                           s2kmode == 1? GCRY_KDF_SALTED_S2K :
@@ -1041,7 +1142,7 @@ s2k_hash_passphrase (const char *passphrase, int hashalgo,
                      unsigned char *key, size_t keylen)
 {
   return hash_passphrase (passphrase, hashalgo, s2kmode, s2ksalt,
-                          (16ul + (s2kcount & 15)) << ((s2kcount >> 4) + 6),
+                          S2K_DECODE_COUNT (s2kcount),
                           key, keylen);
 }
 
@@ -1238,7 +1339,7 @@ agent_get_shadow_info (const unsigned char *shadowkey,
    required, NULL may be passed for them.  */
 gpg_error_t
 parse_shadow_info (const unsigned char *shadow_info,
-                   char **r_hexsn, char **r_idstr)
+                   char **r_hexsn, char **r_idstr, int *r_pinlen)
 {
   const unsigned char *s;
   size_t n;
@@ -1247,6 +1348,8 @@ parse_shadow_info (const unsigned char *shadow_info,
     *r_hexsn = NULL;
   if (r_idstr)
     *r_idstr = NULL;
+  if (r_pinlen)
+    *r_pinlen = 0;
 
   s = shadow_info;
   if (*s != '(')
@@ -1291,5 +1394,34 @@ parse_shadow_info (const unsigned char *shadow_info,
       (*r_idstr)[n] = 0;
     }
 
+  /* Parse the optional PINLEN.  */
+  n = snext (&s);
+  if (!n)
+    return 0;
+
+  if (r_pinlen)
+    {
+      char *tmpstr = xtrymalloc (n+1);
+      if (!tmpstr)
+        {
+          if (r_hexsn)
+            {
+              xfree (*r_hexsn);
+              *r_hexsn = NULL;
+            }
+          if (r_idstr)
+            {
+              xfree (*r_idstr);
+              *r_idstr = NULL;
+            }
+          return gpg_error_from_syserror ();
+        }
+      memcpy (tmpstr, s, n);
+      tmpstr[n] = 0;
+
+      *r_pinlen = (int)strtol (tmpstr, NULL, 10);
+      xfree (tmpstr);
+    }
+
   return 0;
 }