From 6fd5b6d5edf89a63cb38bfd0b41a9df9b4427cf1 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 10 Jan 2002 19:45:32 +0000 Subject: [PATCH] * genkey.c: Store the secret part and return the public part. --- agent/ChangeLog | 28 +++++++ agent/Makefile.am | 3 +- agent/agent.h | 8 ++ agent/command.c | 141 ++++++++++++++++++++++----------- agent/genkey.c | 189 ++++++++++++++++++++++++++++++++++++++++++++ agent/keyformat.txt | 26 +++--- agent/query.c | 76 ++++++++++++++++++ 7 files changed, 408 insertions(+), 63 deletions(-) create mode 100644 agent/genkey.c diff --git a/agent/ChangeLog b/agent/ChangeLog index 3037e00f4..41ea89fb3 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,21 @@ +2002-01-07 Werner Koch + + * genkey.c: Store the secret part and return the public part. + +2002-01-03 Werner Koch + + * command.c (cmd_get_passphrase): New. + (cmd_clear_passphrase): New. + * query.c (agent_get_passphrase): New. + +2002-01-02 Werner Koch + + * 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 * keyformat.txt: New. @@ -50,3 +68,13 @@ Fri Aug 18 14:27:14 CEST 2000 Werner Koch * 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 @@ -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) diff --git a/agent/agent.h b/agent/agent.h index 71cb2ecb3..b3e18c661 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -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*/ diff --git a/agent/command.c b/agent/command.c index bbee7b412..b1357ce0e 100644 --- a/agent/command.c +++ b/agent/command.c @@ -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; -} @@ -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 @@ -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 [ ] + + 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 + + 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 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 @@ -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. diff --git a/agent/query.c b/agent/query.c index 9195f0ab4..1b90c49e1 100644 --- a/agent/query.c +++ b/agent/query.c @@ -215,6 +215,82 @@ agent_askpin (const char *desc_text, } + +/* 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; +} + +