* genkey.c (store_key): Protect the key.
[gnupg.git] / agent / genkey.c
1 /* pksign.c - Generate a keypair
2  *      Copyright (C) 2002 Free Software Foundation, Inc.
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <errno.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <assert.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30
31 #include "agent.h"
32
33
34 static int
35 store_key (GCRY_SEXP private, const char *passphrase)
36 {
37   int i;
38   char *fname;
39   FILE *fp;
40   char *buf;
41   size_t len;
42   unsigned char grip[20];
43   char hexgrip[41];
44   
45   if ( !gcry_pk_get_keygrip (private, grip) )
46     {
47       log_error ("can't calculate keygrip\n");
48       return seterr (General_Error);
49     }
50   for (i=0; i < 20; i++)
51     sprintf (hexgrip+2*i, "%02X", grip[i]);
52   hexgrip[40] = 0;
53
54   fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL);
55   if (!access (fname, F_OK))
56     {
57       log_error ("secret key file `%s' already exists - very strange\n",
58                  fname);
59       xfree (fname);
60       return seterr (General_Error);
61     }
62   fp = fopen (fname, "wbx");  /* FIXME: the x is a GNU extension - let
63                                  configure check whether this actually
64                                  works */
65   if (!fp) 
66     { 
67       log_error ("can't create `%s': %s\n", fname, strerror (errno));
68       xfree (fname);
69       return seterr (File_Create_Error);
70     }
71
72   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
73   assert (len);
74   buf = gcry_malloc_secure (len);
75   if (!buf)
76     {
77       fclose (fp);
78       remove (fname);
79       xfree (fname);
80       return seterr (Out_Of_Core);
81     }
82   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
83   assert (len);
84
85   if (passphrase)
86     {
87       unsigned char *p;
88       int rc;
89
90       rc = agent_protect (buf, passphrase, &p, &len);
91       if (rc)
92         {
93           fclose (fp);
94           remove (fname);
95           xfree (fname);
96           xfree (buf);
97           return rc;
98         }
99       xfree (buf);
100       buf = p;
101     }
102
103   if (fwrite (buf, len, 1, fp) != 1)
104     {
105       log_error ("error writing `%s': %s\n", fname, strerror (errno));
106       fclose (fp);
107       remove (fname);
108       xfree (fname);
109       xfree (buf);
110       return seterr (File_Create_Error);
111     }
112   if ( fclose (fp) )
113     {
114       log_error ("error closing `%s': %s\n", fname, strerror (errno));
115       remove (fname);
116       xfree (fname);
117       xfree (buf);
118       return seterr (File_Create_Error);
119     }
120
121   xfree (fname);
122   xfree (buf);
123   return 0;
124 }
125
126
127
128 /* Generate a new keypair according to the parameters given in
129    KEYPARAM */
130 int
131 agent_genkey (CTRL ctrl, const char *keyparam, size_t keyparamlen,
132               FILE *outfp) 
133 {
134   GCRY_SEXP s_keyparam, s_key, s_private, s_public;
135   struct pin_entry_info_s *pi, *pi2;
136   int rc;
137   size_t len;
138   char *buf;
139
140   rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
141   if (rc)
142     {
143       log_error ("failed to convert keyparam: %s\n", gcry_strerror (rc));
144       return seterr (Invalid_Data);
145     }
146
147   /* Get the passphrase now, cause key generation may take a while */
148   {
149     const char *text1 = trans ("Please enter the passphrase to%0A"
150                                "to protect your new key");
151     const char *text2 = trans ("Please re-enter this passphrase");
152     const char *nomatch = trans ("does not match - try again");
153     int tries = 0;
154
155     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
156     pi2 = pi + sizeof *pi;
157     pi->max_length = 100;
158     pi->max_tries = 3;
159     pi2->max_length = 100;
160     pi2->max_tries = 3;
161
162     rc = agent_askpin (text1, NULL, pi);
163     if (!rc)
164       {
165         do 
166           {
167             rc = agent_askpin (text2, tries? nomatch:NULL, pi2);
168             tries++;
169           }
170         while (!rc && tries < 3 && strcmp (pi->pin, pi2->pin));
171         if (!rc && strcmp (pi->pin, pi2->pin))
172           rc = GNUPG_Canceled;
173       }
174     if (rc)
175       return rc;
176     if (!*pi->pin)
177       {
178         xfree (pi);
179         pi = NULL; /* use does not want a passphrase */
180       }
181   }
182
183   rc = gcry_pk_genkey (&s_key, s_keyparam );
184   gcry_sexp_release (s_keyparam);
185   if (rc)
186     {
187       log_error ("key generation failed: %s\n", gcry_strerror (rc));
188       xfree (pi);
189       return map_gcry_err (rc);
190     }
191
192   /* break out the parts */
193   s_private = gcry_sexp_find_token (s_key, "private-key", 0);
194   if (!s_private)
195     {
196       log_error ("key generation failed: invalid return value\n");
197       gcry_sexp_release (s_key);
198       xfree (pi);
199       return seterr (Invalid_Data);
200     }
201   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
202   if (!s_public)
203     {
204       log_error ("key generation failed: invalid return value\n");
205       gcry_sexp_release (s_private);
206       gcry_sexp_release (s_key);
207       xfree (pi);
208       return seterr (Invalid_Data);
209     }
210   gcry_sexp_release (s_key); s_key = NULL;
211   
212   /* store the secret key */
213   log_debug ("storing private key\n");
214   rc = store_key (s_private, pi->pin);
215   xfree (pi); pi = NULL;
216   gcry_sexp_release (s_private);
217   if (rc)
218     {
219       gcry_sexp_release (s_public);
220       return rc;
221     }
222
223   /* return the public key */
224   log_debug ("returning public key\n");
225   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
226   assert (len);
227   buf = xmalloc (len);
228   if (!buf)
229     {
230       gcry_sexp_release (s_private);
231       gcry_sexp_release (s_public);
232       return seterr (Out_Of_Core);
233     }
234   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
235   assert (len);
236   if (fwrite (buf, len, 1, outfp) != 1)
237     {
238       log_error ("error writing public key: %s\n", strerror (errno));
239       gcry_sexp_release (s_private);
240       gcry_sexp_release (s_public);
241       xfree (buf);
242       return seterr (File_Create_Error);
243     }
244   gcry_sexp_release (s_public);
245   xfree (buf);
246
247   return 0;
248 }
249