* genkey.c: Store the secret part and return the public part.
authorWerner Koch <wk@gnupg.org>
Thu, 10 Jan 2002 19:45:32 +0000 (19:45 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 10 Jan 2002 19:45:32 +0000 (19:45 +0000)
agent/ChangeLog
agent/Makefile.am
agent/agent.h
agent/command.c
agent/genkey.c [new file with mode: 0644]
agent/keyformat.txt
agent/query.c

index 3037e00..41ea89f 100644 (file)
@@ -1,3 +1,21 @@
+2002-01-07  Werner Koch  <wk@gnupg.org>
+
+       * genkey.c: Store the secret part and return the public part.
+
+2002-01-03  Werner Koch  <wk@gnupg.org>
+
+       * command.c (cmd_get_passphrase): New.
+       (cmd_clear_passphrase): New.
+       * query.c (agent_get_passphrase): New.
+
+2002-01-02  Werner Koch  <wk@gnupg.org>
+
+       * genkey.c: New.
+       * command.c (cmd_genkey): New.
+
+       * command.c (rc_to_assuan_status): Removed and changed all callers
+       to use map_to_assuan_status.
+       
 2001-12-19  Werner Koch  <wk@gnupg.org>
 
        * keyformat.txt: New. 
@@ -50,3 +68,13 @@ Fri Aug 18 14:27:14 CEST 2000  Werner Koch  <wk@openit.de>
         * gpg-agent.c: New.
         * Makefile.am: New.
 
+
+ Copyright 2001, 2002 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
index 3d9cd2d..6b05183 100644 (file)
@@ -30,7 +30,8 @@ gpg_agent_SOURCES = \
        trans.c \
        findkey.c \
        pksign.c \
-       pkdecrypt.c
+       pkdecrypt.c \
+       genkey.c 
 
 gpg_agent_LDADD = ../jnlib/libjnlib.a ../assuan/libassuan.a  \
                ../common/libcommon.a $(LIBGCRYPT_LIBS)
index 71cb2ec..b3e18c6 100644 (file)
@@ -94,6 +94,9 @@ GCRY_SEXP agent_key_from_file (const unsigned char *grip);
 
 /*-- query.c --*/
 int agent_askpin (const char *desc_text, struct pin_entry_info_s *pininfo);
+int agent_get_passphrase (char **retpass,
+                          const char *desc, const char *prompt,
+                          const char *errtext);
 
 /*-- pksign.c --*/
 int agent_pksign (CTRL ctrl, FILE *outfp);
@@ -102,5 +105,10 @@ int agent_pksign (CTRL ctrl, FILE *outfp);
 int agent_pkdecrypt (CTRL ctrl, const char *ciphertext, size_t ciphertextlen,
                      FILE *outfp);
 
+/*-- genkey.c --*/
+int agent_genkey (CTRL ctrl,
+                  const char *keyparam, size_t keyparmlen, FILE *outfp);
+
+
 
 #endif /*AGENT_H*/
index bbee7b4..b1357ce 100644 (file)
@@ -1,5 +1,5 @@
 /* command.c - gpg-agent command handler
- *     Copyright (C) 2001 Free Software Foundation, Inc.
+ *     Copyright (C) 2001, 2002 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -34,7 +34,8 @@
 
 /* maximum allowed size of the inquired ciphertext */
 #define MAXLEN_CIPHERTEXT 4096
-
+/* maximum allowed size of the key parameters */
+#define MAXLEN_KEYPARAM 1024
 
 #define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
 
@@ -50,50 +51,6 @@ struct server_local_s {
 };
 
 
-/* Map GNUPG_xxx error codes to Assuan status codes
-   FIXME: duplicated from ../sm/server.c */
-static int
-rc_to_assuan_status (int rc)
-{
-  switch (rc)
-    {
-    case 0: break;
-    case GNUPG_Bad_Certificate:   rc = ASSUAN_Bad_Certificate; break;
-    case GNUPG_Bad_Certificate_Path: rc = ASSUAN_Bad_Certificate_Path; break;
-    case GNUPG_Missing_Certificate: rc = ASSUAN_Missing_Certificate; break;
-    case GNUPG_No_Data:           rc = ASSUAN_No_Data_Available; break;
-    case GNUPG_Bad_Signature:     rc = ASSUAN_Bad_Signature; break;
-    case GNUPG_Not_Implemented:   rc = ASSUAN_Not_Implemented; break;
-    case GNUPG_No_Agent:          rc = ASSUAN_No_Agent; break;
-    case GNUPG_Agent_Error:       rc = ASSUAN_Agent_Error; break;
-    case GNUPG_No_Public_Key:     rc = ASSUAN_No_Public_Key; break;
-    case GNUPG_No_Secret_Key:     rc = ASSUAN_No_Secret_Key; break;
-    case GNUPG_Invalid_Data:      rc = ASSUAN_Invalid_Data; break;
-
-    case GNUPG_Bad_PIN:
-    case GNUPG_Bad_Passphrase:
-      rc = ASSUAN_No_Secret_Key;
-      break;
-
-    case GNUPG_Read_Error: 
-    case GNUPG_Write_Error:
-    case GNUPG_IO_Error: 
-      rc = ASSUAN_Server_IO_Error;
-      break;
-    case GNUPG_Out_Of_Core:    
-    case GNUPG_Resource_Limit: 
-      rc = ASSUAN_Server_Resource_Problem;
-      break;
-    case GNUPG_Bug: 
-    case GNUPG_Internal_Error:   
-      rc = ASSUAN_Server_Bug;
-      break;
-    default: 
-      rc = ASSUAN_Server_Fault;
-      break;
-    }
-  return rc;
-}
 
 
 \f
@@ -193,7 +150,7 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
   CTRL ctrl = assuan_get_pointer (ctx);
 
   rc = agent_pksign (ctrl, assuan_get_data_fp (ctx));
-  return rc_to_assuan_status (rc);
+  return map_to_assuan_status (rc);
 }
 
 /* PKDECRYPT <options>
@@ -216,7 +173,92 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
 
   rc = agent_pkdecrypt (ctrl, value, valuelen, assuan_get_data_fp (ctx));
   xfree (value);
-  return rc_to_assuan_status (rc);
+  return map_to_assuan_status (rc);
+}
+
+
+/* GENKEY
+
+   Generate a new key, store the secret part and return the public
+   part.  Here is an example transaction:
+
+   C: GENKEY
+   S: INQUIRE KEYPARM
+   C: D (genkey (rsa (nbits  1024)))
+   C: END
+   S: D (public-key
+   S: D   (rsa (n 326487324683264) (e 10001)))
+   S  OK key created
+*/
+
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+  CTRL ctrl = assuan_get_pointer (ctx);
+  int rc;
+  char *value;
+  size_t valuelen;
+
+  /* First inquire the parameters */
+  rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM);
+  if (rc)
+    return rc;
+
+  rc = agent_genkey (ctrl, value, valuelen, assuan_get_data_fp (ctx));
+  xfree (value);
+  return map_to_assuan_status (rc);
+}
+
+
+/* GET_PASSPHRASE <cache_id> [<error_message> <prompt> <description>]
+
+   This function is usually used to ask for a passphrase to be used
+   for conventional encryption, but may aslo be used by programs which
+   need specal handling of passphrases.  This command uses a syntax
+   which helps clients to use the agent with minimum effort.  The
+   agent either returns with an error or with a OK followed by the hex
+   encoded passphrase.  Note that the length of the strings is
+   implicitly limited by the maximum length of a command.
+*/
+
+static int
+cmd_get_passphrase (ASSUAN_CONTEXT ctx, char *line)
+{
+  int rc;
+  char *response;
+  char *desc, *prompt, *errtext;
+
+  /* FIXME: Parse that stuff */
+  desc = "We need a passphrase";
+  prompt = NULL;
+  errtext = "try again";
+
+  rc = agent_get_passphrase (&response, desc, prompt, errtext);
+  if (!rc)
+    {
+      rc = assuan_set_okay_line (ctx, response);
+      xfree (response);
+    }
+
+  return map_to_assuan_status (rc);
+}
+
+
+/* CLEAR_PASSPHRASE <cache_id>
+
+   may be used to invalidate the cache entry for a passphrase.  The
+   function returns with OK even when ther eis no cached passphrase.
+*/
+
+static int
+cmd_clear_passphrase (ASSUAN_CONTEXT ctx, char *line)
+{
+  int rc;
+
+  /* fixme: no caching yet. so return with OK */
+  rc = 0;
+
+  return map_to_assuan_status (rc);
 }
 
 
@@ -235,6 +277,9 @@ register_commands (ASSUAN_CONTEXT ctx)
     { "SETHASH",    0,  cmd_sethash },
     { "PKSIGN",     0,  cmd_pksign },
     { "PKDECRYPT",  0,  cmd_pkdecrypt },
+    { "GENKEY",     0,  cmd_genkey },
+    { "GET_PASSPHRASE",0, cmd_get_passphrase },
+    { "CLEAR_PASSPHRASE",0, cmd_clear_passphrase },
     { "",     ASSUAN_CMD_INPUT, NULL }, 
     { "",     ASSUAN_CMD_OUTPUT, NULL }, 
     { NULL }
diff --git a/agent/genkey.c b/agent/genkey.c
new file mode 100644 (file)
index 0000000..166f460
--- /dev/null
@@ -0,0 +1,189 @@
+/* pksign.c - Generate a keypair
+ *     Copyright (C) 2002 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include "agent.h"
+
+static int
+store_key (GCRY_SEXP private)
+{
+  int i;
+  char *fname;
+  FILE *fp;
+  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);
+    }
+
+  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);
+    }
+  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) )
+    {
+      log_error ("error closing `%s': %s\n", fname, strerror (errno));
+      remove (fname);
+      xfree (fname);
+      xfree (buf);
+      return seterr (File_Create_Error);
+    }
+
+  xfree (fname);
+  xfree (buf);
+  return 0;
+}
+
+
+
+/* Generate a new keypair according to the parameters given in
+   KEYPARAM */
+int
+agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
+              FILE *outfp) 
+{
+  GCRY_SEXP s_keyparam, s_key, s_private, s_public;
+  int rc;
+  size_t len;
+  char *buf;
+
+  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);
+    }
+
+  /* fixme: Get the passphrase now, cause key generation may take a while */
+
+  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);
+    }
+
+  /* break out the parts */
+  s_private = gcry_sexp_find_token (s_key, "private-key", 0);
+  if (!s_private)
+    {
+      log_error ("key generation failed: invalid return value\n");
+      gcry_sexp_release (s_key);
+      return seterr (Invalid_Data);
+    }
+  s_public = gcry_sexp_find_token (s_key, "public-key", 0);
+  if (!s_public)
+    {
+      log_error ("key generation failed: invalid return value\n");
+      gcry_sexp_release (s_private);
+      gcry_sexp_release (s_key);
+      return seterr (Invalid_Data);
+    }
+  gcry_sexp_release (s_key); s_key = NULL;
+  
+  /* store the secret key */
+  log_debug ("storing private key\n");
+  rc = store_key (s_private);
+  gcry_sexp_release (s_private);
+  if (rc)
+    {
+      gcry_sexp_release (s_public);
+      return rc;
+    }
+
+  /* return the public key */
+  log_debug ("returning public key\n");
+  len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
+  assert (len);
+  buf = xmalloc (len);
+  if (!buf)
+    {
+      gcry_sexp_release (s_private);
+      gcry_sexp_release (s_public);
+      return seterr (Out_Of_Core);
+    }
+  len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
+  assert (len);
+  if (fwrite (buf, len, 1, outfp) != 1)
+    {
+      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);
+    }
+  gcry_sexp_release (s_public);
+  xfree (buf);
+
+  return 0;
+}
+
index dd34acc..4f81f5b 100644 (file)
@@ -5,8 +5,8 @@ keyformat.txt (wk 2001-12-18)
 Some notes on the format of the secret keys used with gpg-agent.
 
 
-The secret[1] keys are store one per file in a directory below
-the .gnupg homedirectory.  This directory is named
+The secret keys[1] are stored on a per file basis in a directory below
+the .gnupg home directory.  This directory is named
 
    private-keys-v1.d
 
@@ -14,7 +14,7 @@ and should have permissions 700.
 
 The secret keys are stored in files with a name matching the
 hexadecimal representation of the keygrip[2].  The content of the file
-is an S-Expression like tyhe ones used with Libgcrypt.  Here is the
+is an S-Expression like the ones used with Libgcrypt.  Here is an
 example of an unprotected file:
 
 (private-key
@@ -33,7 +33,8 @@ accepted by gpg-agent with the configuration option:
 --allow-non-canonical-key-format.  
 
 The regular way to represent the keys is in canonical representation
-with the additional requirement of an extra object around it[3]:
+with the additional requirement of an extra object container around
+it[3]:
 
 (oid.1.3.6.1.4.1.11591.2.2.2
  (keyinfo human_readable_information_to_decribe_this_key)
@@ -74,8 +75,8 @@ Defined protection methods are:
 1.3.6.1.4.1.gnu(11591).aegypten(2)
 .algorithms(1).keyprotection(1).s2k3-sha1-aes-cbc(1)
 
-This uses AES in CBS mode for encryption, SHA-1 fro integrity
-protecion and the String to Key algorithm 3 from OpenPGP (rfc2440).
+This uses AES in CBC mode for encryption, SHA-1 for integrity
+protection and the String to Key algorithm 3 from OpenPGP (rfc2440).
 
 Example:
 
@@ -94,6 +95,9 @@ representation) after decryption:
  (u #304559a..[some bytes not shown]..9b#)
 )
 
+For padding reasons, random bytes are appended to this list - they can
+easily be stripped by looking for the end of the list.
+
 The first element is the SHA-1 hash calculated on the concatenation of the
 public key and secret key parameter lists: i.e one has to hash the
 concatenatiohn of these 6 canonical encoded lists for RSA, including
@@ -112,13 +116,7 @@ the stored one - If they don't match the integrity of the key is not
 given.
 
 
-
-
-
-
-
-
-
+TODO: write a more elaborated version.
 
 
 
@@ -135,4 +133,4 @@ different protocols.  PKCS-15 calls this a subjectKeyHash; it can be
 calculate using Libgcrypt's gcry_pk_get_keygrip().
 
 [3] Even when canonical representation is required we will show the
-S-expression here in a more readable representation.
\ No newline at end of file
+S-expression here in a more readable representation.
index 9195f0a..1b90c49 100644 (file)
@@ -215,6 +215,82 @@ agent_askpin (const char *desc_text,
 }
 
 
+\f
+/* Ask for the passphrase using the supplied arguments.  The
+   passphrase is returned in RETPASS as an hex encoded string to be
+   freed by the caller */
+int 
+agent_get_passphrase (char **retpass, const char *desc, const char *prompt,
+                      const char *errtext)
+{
+
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  struct entry_parm_s parm;
+  unsigned char *p, *hexstring;
+  int i;
+
+  *retpass = NULL;
+  if (opt.batch)
+    return GNUPG_Bad_Passphrase; 
+
+  rc = start_pinentry ();
+  if (rc)
+    return rc;
+
+  if (desc)
+    snprintf (line, DIM(line)-1, "SETDESC %s", desc);
+  else
+    snprintf (line, DIM(line)-1, "RESET");
+  line[DIM(line)-1] = 0;
+  rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
+  if (rc)
+    return map_assuan_err (rc);
+
+  snprintf (line, DIM(line)-1, "SETPROMPT %s", prompt? prompt : "Passphrase");
+  line[DIM(line)-1] = 0;
+  rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
+  if (rc)
+    return map_assuan_err (rc);
+
+  if (errtext)
+    {
+      snprintf (line, DIM(line)-1, "SETERROR %s", errtext);
+      line[DIM(line)-1] = 0;
+      rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
+      if (rc)
+        return map_assuan_err (rc);
+    }
+
+  memset (&parm, 0, sizeof parm);
+  parm.size = ASSUAN_LINELENGTH/2 - 5;
+  parm.buffer = gcry_malloc_secure (parm.size+10);
+  if (!parm.buffer)
+    return seterr (Out_Of_Core);
+
+  rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
+  if (rc)
+    {
+      xfree (parm.buffer);
+      return map_assuan_err (rc);
+    }
+  
+  hexstring = gcry_malloc_secure (strlen (parm.buffer)*2+1);
+  if (!hexstring)
+    {
+      xfree (parm.buffer);
+      return seterr (Out_Of_Core);
+    }
+
+  for (i=0, p=parm.buffer; *p; p++, i += 2)
+    sprintf (hexstring+i, "%02X", *p);
+  
+  xfree (parm.buffer);
+  *retpass = hexstring;
+  return 0;
+}
+
+