Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F26765984
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
36 KB
Subscribers
None
View Options
diff --git a/agent/ChangeLog b/agent/ChangeLog
index 3037e00f4..41ea89fb3 100644
--- a/agent/ChangeLog
+++ b/agent/ChangeLog
@@ -1,52 +1,80 @@
+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.
2001-12-19 Marcus Brinkmann <marcus@g10code.de>
* query.c (start_pinentry): Add new argument to assuan_pipe_connect.
2001-12-18 Werner Koch <wk@gnupg.org>
* Makefile.am: Use LIBGCRYPT macros
2001-12-14 Werner Koch <wk@gnupg.org>
* gpg-agent.c (main): New option --batch. New option --debug-wait
n, so that it is possible to attach gdb when used in server mode.
* query.c (agent_askpin): Don't ask in batch mode.
* command.c: Removed the conversion macros as they are now in
../common/util.h.
2001-12-14 Marcus Brinkmann <marcus@g10code.de>
* query.c (LINELENGTH): Removed.
(agent_askpin): Use ASSUAN_LINELENGTH, not LINELENGTH.
2001-11-19 Werner Koch <wk@gnupg.org>
* gpg-agent.c: Removed all GUI code, removed code for old
protocol. New code to use the Assuan protocol as a server and
also to communicate with a new ask-passphrase utility.
2000-11-22 Werner Koch <wk@gnupg.org>
* gpg-agent.c (main): csh support by Dan Winship, new options --sh
and --csh and set default by consulting $SHELL.
Mon Aug 21 17:59:17 CEST 2000 Werner Koch <wk@openit.de>
* gpg-agent.c (passphrase_dialog): Cleanup the window and added the
user supplied text to the window.
(main): Fixed segv in gtk_init when used without a command to start.
* gpg-agent.c: --flush option.
(req_flush): New.
(req_clear_passphrase): Implemented.
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.
diff --git a/agent/Makefile.am b/agent/Makefile.am
index 3d9cd2dfe..6b0518332 100644
--- a/agent/Makefile.am
+++ b/agent/Makefile.am
@@ -1,40 +1,41 @@
# Copyright (C) 2001 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
## Process this file with automake to produce Makefile.in
bin_PROGRAMS = gpg-agent
AM_CPPFLAGS = -I$(top_srcdir)/common $(LIBGCRYPT_CFLAGS)
LDFLAGS = @LDFLAGS@
gpg_agent_SOURCES = \
gpg-agent.c agent.h \
command.c \
query.c \
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)
diff --git a/agent/agent.h b/agent/agent.h
index 71cb2ecb3..b3e18c661 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -1,106 +1,114 @@
/* agent.h - Global definitions for the agent
* Copyright (C) 2001 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
*/
#ifndef AGENT_H
#define AGENT_H
#include <gcrypt.h>
#include "../common/util.h"
#include "../common/errors.h"
#define MAX_DIGEST_LEN 24
/* A large struct name "opt" to keep global flags */
struct {
unsigned int debug; /* debug flags (DBG_foo_VALUE) */
int verbose; /* verbosity level */
int quiet; /* be as quiet as possible */
int dry_run; /* don't change any persistent data */
int batch; /* batch mode */
const char *homedir; /* configuration directory name */
const char *pinentry_program;
} opt;
#define DBG_COMMAND_VALUE 1 /* debug commands i/o */
#define DBG_MPI_VALUE 2 /* debug mpi details */
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
#define DBG_CACHE_VALUE 64 /* debug the caching */
#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_COMMAND (opt.debug & DBG_COMMAND_VALUE)
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
struct server_local_s;
struct server_control_s {
struct server_local_s *server_local;
struct {
int algo;
unsigned char value[MAX_DIGEST_LEN];
int valuelen;
} digest;
char keygrip[20];
int have_keygrip;
};
typedef struct server_control_s *CTRL;
struct pin_entry_info_s {
int min_digits; /* min. number of digits required or 0 for freeform entry */
int max_digits; /* max. number of allowed digits allowed*/
int max_tries;
int failed_tries;
size_t max_length; /* allocated length of the buffer */
char pin[1];
};
/*-- gpg-agent.c --*/
void agent_exit (int rc);
/*-- trans.c --*/
const char *trans (const char *text);
/*-- command.c --*/
void start_command_handler (void);
/*-- findkey.c --*/
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);
/*-- pkdecrypt.c --*/
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*/
diff --git a/agent/command.c b/agent/command.c
index bbee7b412..b1357ce0e 100644
--- a/agent/command.c
+++ b/agent/command.c
@@ -1,318 +1,363 @@
/* 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.
*
* 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
*/
/* FIXME: we should not use the default assuan buffering but setup
some buffering in secure mempory to protect session keys etc. */
#include <config.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "agent.h"
#include "../assuan/assuan.h"
/* 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))
#if MAX_DIGEST_LEN < 20
#error MAX_DIGEST_LEN shorter than keygrip
#endif
/* Data used to associate an Assuan context with local server data */
struct server_local_s {
ASSUAN_CONTEXT assuan_ctx;
int message_fd;
};
-/* 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;
-}
static void
reset_notify (ASSUAN_CONTEXT ctx)
{
CTRL ctrl = assuan_get_pointer (ctx);
memset (ctrl->keygrip, 0, 20);
ctrl->have_keygrip = 0;
ctrl->digest.valuelen = 0;
}
/* SIGKEY <hexstring_with_keygrip>
SETKEY <hexstring_with_keygrip>
Set the key used for a sign or decrypt operation */
static int
cmd_sigkey (ASSUAN_CONTEXT ctx, char *line)
{
int n;
char *p;
CTRL ctrl = assuan_get_pointer (ctx);
unsigned char *buf;
/* parse the hash value */
for (p=line,n=0; hexdigitp (p); p++, n++)
;
if (*p)
return set_error (Parameter_Error, "invalid hexstring");
if ((n&1))
return set_error (Parameter_Error, "odd number of digits");
n /= 2;
if (n != 20)
return set_error (Parameter_Error, "invalid length of keygrip");
buf = ctrl->keygrip;
for (p=line, n=0; n < 20; p += 2, n++)
buf[n] = xtoi_2 (p);
ctrl->have_keygrip = 1;
return 0;
}
/* SETHASH <algonumber> <hexstring>
The client can use this command to tell the server about the data
(which usually is a hash) to be signed. */
static int
cmd_sethash (ASSUAN_CONTEXT ctx, char *line)
{
int n;
char *p;
CTRL ctrl = assuan_get_pointer (ctx);
unsigned char *buf;
char *endp;
int algo;
/* parse the algo number and check it */
algo = (int)strtoul (line, &endp, 10);
for (line = endp; *line == ' ' || *line == '\t'; line++)
;
if (!algo || gcry_md_test_algo (algo))
return set_error (Unsupported_Algorithm, NULL);
ctrl->digest.algo = algo;
/* parse the hash value */
for (p=line,n=0; hexdigitp (p); p++, n++)
;
if (*p)
return set_error (Parameter_Error, "invalid hexstring");
if ((n&1))
return set_error (Parameter_Error, "odd number of digits");
n /= 2;
if (n != 16 && n != 20 && n != 24 && n != 32)
return set_error (Parameter_Error, "unsupported length of hash");
if (n > MAX_DIGEST_LEN)
return set_error (Parameter_Error, "hash value to long");
buf = ctrl->digest.value;
ctrl->digest.valuelen = n;
for (p=line, n=0; n < ctrl->digest.valuelen; p += 2, n++)
buf[n] = xtoi_2 (p);
for (; n < ctrl->digest.valuelen; n++)
buf[n] = 0;
return 0;
}
/* PKSIGN <options>
Perform the actual sign operation. Neither input nor output are
sensitive to eavesdropping */
static int
cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
{
int rc;
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>
Perform the actual decrypt operation. Input is not
sensitive to eavesdropping */
static int
cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
{
int rc;
CTRL ctrl = assuan_get_pointer (ctx);
char *value;
size_t valuelen;
/* First inquire the data to decrypt */
rc = assuan_inquire (ctx, "CIPHERTEXT",
&value, &valuelen, MAXLEN_CIPHERTEXT);
if (rc)
return rc;
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);
}
/* Tell the assuan library about our commands */
static int
register_commands (ASSUAN_CONTEXT ctx)
{
static struct {
const char *name;
int cmd_id;
int (*handler)(ASSUAN_CONTEXT, char *line);
} table[] = {
{ "SIGKEY", 0, cmd_sigkey },
{ "SETKEY", 0, cmd_sigkey },
{ "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 }
};
int i, j, rc;
for (i=j=0; table[i].name; i++)
{
rc = assuan_register_command (ctx,
table[i].cmd_id? table[i].cmd_id
: (ASSUAN_CMD_USER + j++),
table[i].name, table[i].handler);
if (rc)
return rc;
}
assuan_register_reset_notify (ctx, reset_notify);
return 0;
}
/* Startup the server */
void
start_command_handler (void)
{
int rc;
int filedes[2];
ASSUAN_CONTEXT ctx;
struct server_control_s ctrl;
memset (&ctrl, 0, sizeof ctrl);
/* For now we use a simple pipe based server so that we can work
from scripts. We will later add options to run as a daemon and
wait for requests on a Unix domain socket */
filedes[0] = 0;
filedes[1] = 1;
rc = assuan_init_pipe_server (&ctx, filedes);
if (rc)
{
log_error ("failed to initialize the server: %s\n",
assuan_strerror(rc));
agent_exit (2);
}
rc = register_commands (ctx);
if (rc)
{
log_error ("failed to the register commands with Assuan: %s\n",
assuan_strerror(rc));
agent_exit (2);
}
assuan_set_pointer (ctx, &ctrl);
ctrl.server_local = xcalloc (1, sizeof *ctrl.server_local);
ctrl.server_local->assuan_ctx = ctx;
ctrl.server_local->message_fd = -1;
for (;;)
{
rc = assuan_accept (ctx);
if (rc == -1)
{
break;
}
else if (rc)
{
log_info ("Assuan accept problem: %s\n", assuan_strerror (rc));
break;
}
rc = assuan_process (ctx);
if (rc)
{
log_info ("Assuan processing failed: %s\n", assuan_strerror (rc));
continue;
}
}
assuan_deinit_pipe_server (ctx);
}
diff --git a/agent/genkey.c b/agent/genkey.c
new file mode 100644
index 000000000..166f4605a
--- /dev/null
+++ b/agent/genkey.c
@@ -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;
+}
+
diff --git a/agent/keyformat.txt b/agent/keyformat.txt
index dd34acc44..4f81f5b1d 100644
--- a/agent/keyformat.txt
+++ b/agent/keyformat.txt
@@ -1,138 +1,136 @@
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
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
(rsa
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(d #046129F..[some bytes not shown]..81#)
(p #00e861b..[some bytes not shown]..f1#)
(q #00f7a7c..[some bytes not shown]..61#)
(u #304559a..[some bytes not shown]..9b#)
)
)
Actually this form should not be used for regular purposes and only
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)
(private-key
(rsa
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(d #046129F..[some bytes not shown]..81#)
(p #00e861b..[some bytes not shown]..f1#)
(q #00f7a7c..[some bytes not shown]..61#)
(u #304559a..[some bytes not shown]..9b#)
)
)
)
This describes an unprotected key; a protected key is like this:
(oid.1.3.6.1.4.1.11591.2.2.3
(keyinfo human_readable_information_to_decribe_this_key)
(private-key
(rsa
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(oid.1.3.6.1.4.1.11591.2.1.1.1 (parms) encrypted_octet_string)
)
)
)
In this scheme the encrypted_octet_string is encrypted according to
the scheme identifier by the OID, most protection algorithms need
some parameters, which are given in a list before the
encrypted_octet_string. The result of the decryption process is a
list of the secret key parameters.
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:
(oid.1.3.6.1.4.1.11591.2.1.1.1
((salt iterations) iv)
encrypted_octet_string
)
The encrypted_octet string should yield this S-Exp (in canonical
representation) after decryption:
(sha1_hash
(d #046129F..[some bytes not shown]..81#)
(p #00e861b..[some bytes not shown]..f1#)
(q #00f7a7c..[some bytes not shown]..61#)
(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
the parenthesis.
(n #00e0ce9..[some bytes not shown]..51#)
(e #010001#)
(d #046129F..[some bytes not shown]..81#)
(p #00e861b..[some bytes not shown]..f1#)
(q #00f7a7c..[some bytes not shown]..61#)
(u #304559a..[some bytes not shown]..9b#)
After decryption the hash must be recalculated and compared against
the stored one - If they don't match the integrity of the key is not
given.
-
-
-
-
-
-
-
+TODO: write a more elaborated version.
Notes:
[1] I usually use the terms private and secret key exchangeable but prefer the
term secret key because it can be visually be better distinguished
from the term public key.
[2] The keygrip is a unique identifier for a key pair, it is
independent of any protocol, so that the same key can be ised with
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.
diff --git a/agent/query.c b/agent/query.c
index 9195f0ab4..1b90c49e1 100644
--- a/agent/query.c
+++ b/agent/query.c
@@ -1,220 +1,296 @@
/* query.c - fork of the pinentry to query stuff from the user
* Copyright (C) 2001 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"
#include "../assuan/assuan.h"
#ifdef _POSIX_OPEN_MAX
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
#else
#define MAX_OPEN_FDS 20
#endif
static ASSUAN_CONTEXT entry_ctx = NULL;
/* data to be passed to our callbacks */
struct entry_parm_s {
int lines;
size_t size;
char *buffer;
};
/* Fork off the pin entry if this has not already been done */
static int
start_pinentry (void)
{
int rc;
const char *pgmname;
ASSUAN_CONTEXT ctx;
const char *argv[3];
if (entry_ctx)
return 0; /* No need to serialize things becuase the agent is
expected to tun as a single-thread (or may be in
future using libpth) */
log_debug ("no running PIN Entry - starting it\n");
if (fflush (NULL))
{
log_error ("error flushing pending output: %s\n", strerror (errno));
return seterr (Write_Error);
}
/* FIXME: change the default location of the program */
if (!opt.pinentry_program || !*opt.pinentry_program)
opt.pinentry_program = "../../pinentry/kpinentry/kpinentry";
if ( !(pgmname = strrchr (opt.pinentry_program, '/')))
pgmname = opt.pinentry_program;
else
pgmname++;
argv[0] = pgmname;
argv[1] = NULL;
/* connect to the pinentry and perform initial handshaking */
rc = assuan_pipe_connect (&ctx, opt.pinentry_program, (char**)argv, 0);
if (rc)
{
log_error ("can't connect to the PIN entry module: %s\n",
assuan_strerror (rc));
return seterr (No_PIN_Entry);
}
entry_ctx = ctx;
log_debug ("connection to PIN entry established\n");
if (DBG_COMMAND)
{
log_debug ("waiting for debugger [hit RETURN when ready] .....\n");
getchar ();
log_debug ("... okay\n");
}
return 0;
}
static AssuanError
getpin_cb (void *opaque, const void *buffer, size_t length)
{
struct entry_parm_s *parm = opaque;
/* we expect the pin to fit on one line */
if (parm->lines || length >= parm->size)
return ASSUAN_Too_Much_Data;
/* fixme: we should make sure that the assuan buffer is allocated in
secure memory or read the response byte by byte */
memcpy (parm->buffer, buffer, length);
parm->buffer[length] = 0;
parm->lines++;
return 0;
}
static int
all_digitsp( const char *s)
{
for (; *s && *s >= '0' && *s <= '9'; s++)
;
return !*s;
}
/* Call the Entry and ask for the PIN. We do check for a valid PIN
number here and repeat it as long as we have invalid formed
numbers. */
int
agent_askpin (const char *desc_text,
struct pin_entry_info_s *pininfo)
{
int rc;
char line[ASSUAN_LINELENGTH];
struct entry_parm_s parm;
const char *errtext = NULL;
if (opt.batch)
return 0; /* fixme: we should return BAD PIN */
if (!pininfo || pininfo->max_length < 1)
return seterr (Invalid_Value);
if (!desc_text)
desc_text = trans ("Please enter you PIN, so that the secret key "
"can be unlocked for this session");
rc = start_pinentry ();
if (rc)
return rc;
snprintf (line, DIM(line)-1, "SETDESC %s", desc_text);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
rc = assuan_transact (entry_ctx,
pininfo->min_digits? "SETPROMPT PIN:"
: "SETPROMPT Passphrase:",
NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
for (;pininfo->failed_tries < pininfo->max_tries; pininfo->failed_tries++)
{
memset (&parm, 0, sizeof parm);
parm.size = pininfo->max_length;
parm.buffer = pininfo->pin;
if (errtext)
{
/* fixme: should we show the try count? It must be translated */
snprintf (line, DIM(line)-1, "SETERROR %s (try %d of %d)",
errtext, pininfo->failed_tries+1, pininfo->max_tries);
line[DIM(line)-1] = 0;
rc = assuan_transact (entry_ctx, line, NULL, NULL, NULL, NULL);
if (rc)
return map_assuan_err (rc);
errtext = NULL;
}
rc = assuan_transact (entry_ctx, "GETPIN", getpin_cb, &parm, NULL, NULL);
if (rc == ASSUAN_Too_Much_Data)
errtext = pininfo->min_digits? trans ("PIN too long")
: trans ("Passphrase too long");
else if (rc)
return map_assuan_err (rc);
if (!errtext && !pininfo->min_digits)
return 0; /* okay, got a passphrase */
if (!errtext && !all_digitsp (pininfo->pin))
errtext = trans ("Invalid characters in PIN");
if (!errtext && pininfo->max_digits
&& strlen (pininfo->pin) > pininfo->max_digits)
errtext = trans ("PIN too long");
if (!errtext
&& strlen (pininfo->pin) < pininfo->min_digits)
errtext = trans ("PIN too short");
if (!errtext)
return 0; /* okay, got a PIN */
}
return pininfo->min_digits? GNUPG_Bad_PIN : GNUPG_Bad_Passphrase;
}
+
+/* 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;
+}
+
+
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, Aug 6, 9:14 PM (1 d, 13 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
01/b8/2b702f4160d888dd44de47dcd36f
Attached To
rG GnuPG
Event Timeline
Log In to Comment