Changed to GPLv3.
[gnupg.git] / agent / genkey.c
1 /* pksign.c - Generate a keypair
2  *      Copyright (C) 2002, 2003, 2004 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 3 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, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <assert.h>
27
28 #include "agent.h"
29 #include "i18n.h"
30
31 static int
32 store_key (gcry_sexp_t private, const char *passphrase, int force)
33 {
34   int rc;
35   unsigned char *buf;
36   size_t len;
37   unsigned char grip[20];
38   
39   if ( !gcry_pk_get_keygrip (private, grip) )
40     {
41       log_error ("can't calculate keygrip\n");
42       return gpg_error (GPG_ERR_GENERAL);
43     }
44
45   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
46   assert (len);
47   buf = gcry_malloc_secure (len);
48   if (!buf)
49       return out_of_core ();
50   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
51   assert (len);
52
53   if (passphrase)
54     {
55       unsigned char *p;
56
57       rc = agent_protect (buf, passphrase, &p, &len);
58       if (rc)
59         {
60           xfree (buf);
61           return rc;
62         }
63       xfree (buf);
64       buf = p;
65     }
66
67   rc = agent_write_private_key (grip, buf, len, force);
68   xfree (buf);
69   return rc;
70 }
71
72
73 /* Check whether the passphrase PW is suitable. Returns 0 if the
74    passphrase is suitable and true if it is not and the user should be
75    asked to provide a different one. */
76 int
77 check_passphrase_constraints (ctrl_t ctrl, const char *pw)
78 {
79   gpg_error_t err;
80   unsigned int minlen = opt.min_passphrase_len;
81   
82   if (!pw)
83     pw = "";
84
85   if (utf8_charcount (pw) < minlen ) 
86     {
87       char *desc = xtryasprintf 
88         ( ngettext ("Warning:  You have entered a passphrase that%%0A"
89                     "is obviously not secure.  A passphrase should%%0A"
90                     "be at least %u character long.", 
91                     "Warning:  You have entered a passphrase that%%0A"
92                     "is obviously not secure.  A passphrase should%%0A"
93                     "be at least %u characters long.", minlen), minlen );
94       if (!desc)
95         return gpg_error_from_syserror ();
96       
97       err = agent_get_confirmation (ctrl, desc,
98                                     _("Take this one anyway"),
99                                     _("Enter new passphrase"));
100       xfree (desc);
101       if (err)
102         return err;
103     }
104
105   return 0;
106 }
107
108
109 /* Callback function to compare the first entered PIN with the one
110    currently being entered. */
111 static int
112 reenter_compare_cb (struct pin_entry_info_s *pi)
113 {
114   const char *pin1 = pi->check_cb_arg;
115
116   if (!strcmp (pin1, pi->pin))
117     return 0; /* okay */
118   return -1;
119 }
120
121
122
123 /* Generate a new keypair according to the parameters given in
124    KEYPARAM */
125 int
126 agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
127               membuf_t *outbuf) 
128 {
129   gcry_sexp_t s_keyparam, s_key, s_private, s_public;
130   struct pin_entry_info_s *pi, *pi2;
131   int rc;
132   size_t len;
133   char *buf;
134
135   rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
136   if (rc)
137     {
138       log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc));
139       return gpg_error (GPG_ERR_INV_DATA);
140     }
141
142   /* Get the passphrase now, cause key generation may take a while. */
143   {
144     const char *text1 = _("Please enter the passphrase to%0A"
145                                "to protect your new key");
146     const char *text2 = _("Please re-enter this passphrase");
147     const char *initial_errtext = NULL;
148
149     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
150     pi2 = pi + (sizeof *pi + 100);
151     pi->max_length = 100;
152     pi->max_tries = 3;
153     pi2->max_length = 100;
154     pi2->max_tries = 3;
155     pi2->check_cb = reenter_compare_cb;
156     pi2->check_cb_arg = pi->pin;
157
158   next_try:
159     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
160     initial_errtext = NULL;
161     if (!rc)
162       {
163         if (check_passphrase_constraints (ctrl, pi->pin))
164           {
165             pi->failed_tries = 0;
166             pi2->failed_tries = 0;
167             goto next_try;
168           }
169         rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
170         if (rc == -1)
171           { /* The re-entered one did not match and the user did not
172                hit cancel. */
173             initial_errtext = _("does not match - try again");
174             goto next_try;
175           }
176       }
177     if (rc)
178       {
179         xfree (pi);
180         return rc;
181       }
182         
183     if (!*pi->pin)
184       {
185         xfree (pi);
186         pi = NULL; /* User does not want a passphrase. */
187       }
188   }
189
190   rc = gcry_pk_genkey (&s_key, s_keyparam );
191   gcry_sexp_release (s_keyparam);
192   if (rc)
193     {
194       log_error ("key generation failed: %s\n", gpg_strerror (rc));
195       xfree (pi);
196       return rc;
197     }
198
199   /* break out the parts */
200   s_private = gcry_sexp_find_token (s_key, "private-key", 0);
201   if (!s_private)
202     {
203       log_error ("key generation failed: invalid return value\n");
204       gcry_sexp_release (s_key);
205       xfree (pi);
206       return gpg_error (GPG_ERR_INV_DATA);
207     }
208   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
209   if (!s_public)
210     {
211       log_error ("key generation failed: invalid return value\n");
212       gcry_sexp_release (s_private);
213       gcry_sexp_release (s_key);
214       xfree (pi);
215       return gpg_error (GPG_ERR_INV_DATA);
216     }
217   gcry_sexp_release (s_key); s_key = NULL;
218   
219   /* store the secret key */
220   if (DBG_CRYPTO)
221     log_debug ("storing private key\n");
222   rc = store_key (s_private, pi? pi->pin:NULL, 0);
223   xfree (pi); pi = NULL;
224   gcry_sexp_release (s_private);
225   if (rc)
226     {
227       gcry_sexp_release (s_public);
228       return rc;
229     }
230
231   /* return the public key */
232   if (DBG_CRYPTO)
233     log_debug ("returning public key\n");
234   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
235   assert (len);
236   buf = xtrymalloc (len);
237   if (!buf)
238     {
239       gpg_error_t tmperr = out_of_core ();
240       gcry_sexp_release (s_private);
241       gcry_sexp_release (s_public);
242       return tmperr;
243     }
244   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
245   assert (len);
246   put_membuf (outbuf, buf, len);
247   gcry_sexp_release (s_public);
248   xfree (buf);
249
250   return 0;
251 }
252
253
254 \f
255 /* Apply a new passpahrse to the key S_SKEY and store it. */
256 int
257 agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) 
258 {
259   struct pin_entry_info_s *pi, *pi2;
260   int rc;
261
262   {
263     const char *text1 = _("Please enter the new passphrase");
264     const char *text2 = _("Please re-enter this passphrase");
265     const char *initial_errtext = NULL;
266
267     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
268     pi2 = pi + (sizeof *pi + 100);
269     pi->max_length = 100;
270     pi->max_tries = 3;
271     pi2->max_length = 100;
272     pi2->max_tries = 3;
273     pi2->check_cb = reenter_compare_cb;
274     pi2->check_cb_arg = pi->pin;
275
276   next_try:
277     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
278     initial_errtext = NULL;
279     if (!rc)
280       {
281         if (check_passphrase_constraints (ctrl, pi->pin))
282           {
283             pi->failed_tries = 0;
284             pi2->failed_tries = 0;
285             goto next_try;
286           }
287         rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
288         if (rc == -1)
289           { /* The re-entered one did not match and the user did not
290                hit cancel. */
291             initial_errtext = _("does not match - try again");
292             goto next_try;
293           }
294       }
295     if (rc)
296       {
297         xfree (pi);
298         return rc;
299       }
300
301     if (!*pi->pin)
302       {
303         xfree (pi);
304         pi = NULL; /* User does not want a passphrase. */
305       }
306   }
307
308   rc = store_key (s_skey, pi? pi->pin:NULL, 1);
309   xfree (pi);
310   return 0;
311 }