Mainly changes to adjust for the changed KSBA API.
[gnupg.git] / agent / genkey.c
index 166f460..1417abb 100644 (file)
@@ -1,5 +1,5 @@
 /* pksign.c - Generate a keypair
- *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *     Copyright (C) 2002, 2003 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <string.h>
 #include <ctype.h>
 #include <assert.h>
-#include <unistd.h>
-#include <sys/stat.h>
 
 #include "agent.h"
+#include "i18n.h"
 
 static int
-store_key (GCRY_SEXP private)
+store_key (gcry_sexp_t private, const char *passphrase, int force)
 {
-  int i;
-  char *fname;
-  FILE *fp;
+  int rc;
   char *buf;
   size_t len;
   unsigned char grip[20];
-  char hexgrip[41];
   
   if ( !gcry_pk_get_keygrip (private, grip) )
     {
       log_error ("can't calculate keygrip\n");
-      return seterr (General_Error);
-    }
-  for (i=0; i < 20; i++)
-    sprintf (hexgrip+2*i, "%02X", grip[i]);
-  hexgrip[40] = 0;
-
-  fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL);
-  if (!access (fname, F_OK))
-    {
-      log_error ("secret key file `%s' already exists - very strange\n",
-                 fname);
-      xfree (fname);
-      return seterr (General_Error);
-    }
-  fp = fopen (fname, "wbx");
-  if (!fp)
-    {
-      log_error ("can't create `%s': %s\n", fname, strerror (errno));
-      xfree (fname);
-      return seterr (File_Create_Error);
+      return gpg_error (GPG_ERR_GENERAL);
     }
 
   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
   assert (len);
   buf = gcry_malloc_secure (len);
   if (!buf)
-    {
-      fclose (fp);
-      remove (fname);
-      xfree (fname);
-      return seterr (Out_Of_Core);
-    }
+      return out_of_core ();
   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
   assert (len);
 
-  if (fwrite (buf, len, 1, fp) != 1)
-    {
-      log_error ("error writing `%s': %s\n", fname, strerror (errno));
-      fclose (fp);
-      remove (fname);
-      xfree (fname);
-      xfree (buf);
-      return seterr (File_Create_Error);
-    }
-  if ( fclose (fp) )
+  if (passphrase)
     {
-      log_error ("error closing `%s': %s\n", fname, strerror (errno));
-      remove (fname);
-      xfree (fname);
+      unsigned char *p;
+
+      rc = agent_protect (buf, passphrase, &p, &len);
+      if (rc)
+        {
+          xfree (buf);
+          return rc;
+        }
       xfree (buf);
-      return seterr (File_Create_Error);
+      buf = p;
     }
 
-  xfree (fname);
+  rc = agent_write_private_key (grip, buf, len, force);
   xfree (buf);
-  return 0;
+  return rc;
+}
+
+/* Callback function to compare the first entered PIN with the one
+   currently being entered. */
+static int
+reenter_compare_cb (struct pin_entry_info_s *pi)
+{
+  const char *pin1 = pi->check_cb_arg;
+
+  if (!strcmp (pin1, pi->pin))
+    return 0; /* okay */
+  pi->cb_errtext = _("does not match - try again");
+  return -1;
 }
 
 
@@ -110,7 +91,8 @@ int
 agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
               FILE *outfp) 
 {
-  GCRY_SEXP s_keyparam, s_key, s_private, s_public;
+  gcry_sexp_t s_keyparam, s_key, s_private, s_public;
+  struct pin_entry_info_s *pi, *pi2;
   int rc;
   size_t len;
   char *buf;
@@ -118,18 +100,44 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
   rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
   if (rc)
     {
-      log_error ("failed to convert keyparam: %s\n", gcry_strerror (rc));
-      return seterr (Invalid_Data);
+      log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc));
+      return gpg_error (GPG_ERR_INV_DATA);
     }
 
-  /* fixme: Get the passphrase now, cause key generation may take a while */
+  /* Get the passphrase now, cause key generation may take a while. */
+  {
+    const char *text1 = _("Please enter the passphrase to%0A"
+                               "to protect your new key");
+    const char *text2 = _("Please re-enter this passphrase");
+
+    pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
+    pi2 = pi + (sizeof *pi + 100);
+    pi->max_length = 100;
+    pi->max_tries = 3;
+    pi2->max_length = 100;
+    pi2->max_tries = 3;
+    pi2->check_cb = reenter_compare_cb;
+    pi2->check_cb_arg = pi->pin;
+
+    rc = agent_askpin (ctrl, text1, pi);
+    if (!rc)
+      rc = agent_askpin (ctrl, text2, pi2);
+    if (rc)
+      return rc;
+    if (!*pi->pin)
+      {
+        xfree (pi);
+        pi = NULL; /* User does not want a passphrase. */
+      }
+  }
 
   rc = gcry_pk_genkey (&s_key, s_keyparam );
   gcry_sexp_release (s_keyparam);
   if (rc)
     {
-      log_error ("key generation failed: %s\n", gcry_strerror (rc));
-      return map_gcry_err (rc);
+      log_error ("key generation failed: %s\n", gpg_strerror (rc));
+      xfree (pi);
+      return rc;
     }
 
   /* break out the parts */
@@ -138,7 +146,8 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
     {
       log_error ("key generation failed: invalid return value\n");
       gcry_sexp_release (s_key);
-      return seterr (Invalid_Data);
+      xfree (pi);
+      return gpg_error (GPG_ERR_INV_DATA);
     }
   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
   if (!s_public)
@@ -146,13 +155,15 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
       log_error ("key generation failed: invalid return value\n");
       gcry_sexp_release (s_private);
       gcry_sexp_release (s_key);
-      return seterr (Invalid_Data);
+      xfree (pi);
+      return gpg_error (GPG_ERR_INV_DATA);
     }
   gcry_sexp_release (s_key); s_key = NULL;
   
   /* store the secret key */
   log_debug ("storing private key\n");
-  rc = store_key (s_private);
+  rc = store_key (s_private, pi? pi->pin:NULL, 0);
+  xfree (pi); pi = NULL;
   gcry_sexp_release (s_private);
   if (rc)
     {
@@ -164,22 +175,24 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
   log_debug ("returning public key\n");
   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
   assert (len);
-  buf = xmalloc (len);
+  buf = xtrymalloc (len);
   if (!buf)
     {
+      gpg_error_t tmperr = out_of_core ();
       gcry_sexp_release (s_private);
       gcry_sexp_release (s_public);
-      return seterr (Out_Of_Core);
+      return tmperr;
     }
   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
   assert (len);
   if (fwrite (buf, len, 1, outfp) != 1)
     {
+      gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
       log_error ("error writing public key: %s\n", strerror (errno));
       gcry_sexp_release (s_private);
       gcry_sexp_release (s_public);
       xfree (buf);
-      return seterr (File_Create_Error);
+      return tmperr;
     }
   gcry_sexp_release (s_public);
   xfree (buf);
@@ -187,3 +200,41 @@ agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
   return 0;
 }
 
+
+\f
+/* Apply a new passpahrse to the key S_SKEY and store it. */
+int
+agent_protect_and_store (CTRL ctrl, gcry_sexp_t s_skey) 
+{
+  struct pin_entry_info_s *pi, *pi2;
+  int rc;
+
+  {
+    const char *text1 = _("Please enter the new passphrase");
+    const char *text2 = _("Please re-enter this passphrase");
+
+    pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
+    pi2 = pi + (sizeof *pi + 100);
+    pi->max_length = 100;
+    pi->max_tries = 3;
+    pi2->max_length = 100;
+    pi2->max_tries = 3;
+    pi2->check_cb = reenter_compare_cb;
+    pi2->check_cb_arg = pi->pin;
+
+    rc = agent_askpin (ctrl, text1, pi);
+    if (!rc)
+      rc = agent_askpin (ctrl, text2, pi2);
+    if (rc)
+      return rc;
+    if (!*pi->pin)
+      {
+        xfree (pi);
+        pi = NULL; /* User does not want a passphrase. */
+      }
+  }
+
+  rc = store_key (s_skey, pi? pi->pin:NULL, 1);
+  xfree (pi);
+  return 0;
+}