Changed wording of passphrase checking messages.
[gnupg.git] / agent / genkey.c
1 /* genkey.c - Generate a keypair
2  *      Copyright (C) 2002, 2003, 2004, 2007 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 #include "exechelp.h"
31 #include "sysutils.h"
32
33 static int
34 store_key (gcry_sexp_t private, const char *passphrase, int force)
35 {
36   int rc;
37   unsigned char *buf;
38   size_t len;
39   unsigned char grip[20];
40   
41   if ( !gcry_pk_get_keygrip (private, grip) )
42     {
43       log_error ("can't calculate keygrip\n");
44       return gpg_error (GPG_ERR_GENERAL);
45     }
46
47   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, NULL, 0);
48   assert (len);
49   buf = gcry_malloc_secure (len);
50   if (!buf)
51       return out_of_core ();
52   len = gcry_sexp_sprint (private, GCRYSEXP_FMT_CANON, buf, len);
53   assert (len);
54
55   if (passphrase)
56     {
57       unsigned char *p;
58
59       rc = agent_protect (buf, passphrase, &p, &len);
60       if (rc)
61         {
62           xfree (buf);
63           return rc;
64         }
65       xfree (buf);
66       buf = p;
67     }
68
69   rc = agent_write_private_key (grip, buf, len, force);
70   xfree (buf);
71   return rc;
72 }
73
74
75 /* Count the number of non-alpha characters in S.  Control characters
76    and non-ascii characters are not considered.  */
77 static size_t
78 nonalpha_count (const char *s)
79 {
80   size_t n;
81
82   for (n=0; *s; s++)
83     if (isascii (*s) && ( isdigit (*s) || ispunct (*s) ))
84       n++;
85
86   return n;
87 }
88
89
90 /* Check PW against a list of pattern.  Return 0 if PW does not match
91    these pattern.  */
92 static int
93 check_passphrase_pattern (ctrl_t ctrl, const char *pw)
94 {
95   gpg_error_t err = 0;
96   const char *pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CHECK_PATTERN);
97   FILE *infp;
98   const char *argv[10];
99   pid_t pid;
100   int result, i;
101
102   infp = gnupg_tmpfile ();
103   if (!infp)
104     {
105       err = gpg_error_from_syserror ();
106       log_error (_("error creating temporary file: %s\n"), strerror (errno));
107       return 1; /* Error - assume password should not be used.  */
108     }
109
110   if (fwrite (pw, strlen (pw), 1, infp) != 1)
111     {
112       err = gpg_error_from_syserror ();
113       log_error (_("error writing to temporary file: %s\n"),
114                  strerror (errno));
115       fclose (infp);
116       return 1; /* Error - assume password should not be used.  */
117     }
118   rewind (infp);
119
120   i = 0;
121   argv[i++] = "--null";
122   argv[i++] = "--",
123   argv[i++] = opt.check_passphrase_pattern,
124   argv[i] = NULL;
125   assert (i < sizeof argv);
126
127   if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
128     result = 1; /* Execute error - assume password should no be used.  */
129   else if (gnupg_wait_process (pgmname, pid, NULL))
130     result = 1; /* Helper returned an error - probably a match.  */
131   else
132     result = 0; /* Success; i.e. no match.  */
133
134   /* Overwrite our temporary file. */
135   rewind (infp);
136   for (i=((strlen (pw)+99)/100)*100; i > 0; i--)
137     putc ('\xff', infp);
138   fflush (infp);
139   fclose (infp);
140   return result;
141 }
142
143
144 static int 
145 take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn)
146 {
147   gpg_error_t err;
148
149   if (opt.enforce_passphrase_constraints)
150     {
151       err = agent_show_message (ctrl, desc, _("Enter new passphrase"));
152       if (!err)
153         err = gpg_error (GPG_ERR_CANCELED);
154     }
155   else
156     err = agent_get_confirmation (ctrl, desc,
157                                   anyway_btn, _("Enter new passphrase"));
158   return err;
159 }
160
161
162 static int 
163 take_this_one_anyway (ctrl_t ctrl, const char *desc)
164 {
165   return take_this_one_anyway2 (ctrl, desc, _("Take this one anyway"));
166 }
167
168
169 /* Check whether the passphrase PW is suitable. Returns 0 if the
170    passphrase is suitable and true if it is not and the user should be
171    asked to provide a different one.  If SILENT is set, no message are
172    displayed.  */
173 int
174 check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent)
175 {
176   gpg_error_t err;
177   unsigned int minlen = opt.min_passphrase_len;
178   unsigned int minnonalpha = opt.min_passphrase_nonalpha;
179
180   if (!pw)
181     pw = "";
182
183   if (utf8_charcount (pw) < minlen ) 
184     {
185       char *desc;
186       
187       if (silent)
188         return gpg_error (GPG_ERR_INV_PASSPHRASE);
189
190       desc = xtryasprintf 
191         ( ngettext ("Warning: You have entered an insecure passphrase.%%0A"
192                     "A passphrase should be at least %u character long.", 
193                     "Warning: You have entered an insecure passphrase.%%0A"
194                     "A passphrase should be at least %u characters long.", 
195                     minlen), minlen );
196       if (!desc)
197         return gpg_error_from_syserror ();
198       err = take_this_one_anyway (ctrl, desc);
199       xfree (desc);
200       if (err)
201         return err;
202     }
203
204   if (nonalpha_count (pw) < minnonalpha ) 
205     {
206       char *desc;
207
208       if (silent)
209         return gpg_error (GPG_ERR_INV_PASSPHRASE);
210
211       desc = xtryasprintf 
212         ( ngettext ("Warning: You have entered an insecure passphrase.%%0A"
213                     "A passphrase should contain at least %u digit or%%0A"
214                     "special character.", 
215                     "Warning: You have entered an insecure passphrase.%%0A"
216                     "A passphrase should contain at least %u digits or%%0A"
217                     "special characters.",
218                     minnonalpha), minnonalpha );
219       if (!desc)
220         return gpg_error_from_syserror ();
221       err = take_this_one_anyway (ctrl, desc);
222       xfree (desc);
223       if (err)
224         return err;
225     }
226
227   /* If configured check the passphrase against a list of know words
228      and pattern.  The actual test is done by an external program.
229      The warning message is generic to give the user no hint on how to
230      circumvent this list.  */
231   if (*pw && opt.check_passphrase_pattern &&
232       check_passphrase_pattern (ctrl, pw))
233     {
234       const char *desc =
235         /* */     _("Warning: You have entered an insecure passphrase.%0A"
236                     "A passphrase may not be a known term or match%%0A"
237                     "certain pattern.");
238
239       if (silent)
240         return gpg_error (GPG_ERR_INV_PASSPHRASE);
241
242       err = take_this_one_anyway (ctrl, desc);
243       if (err)
244         return err;
245     }
246
247   /* The final check is to warn about an empty passphrase. */
248   if (!*pw)
249     {
250       const char *desc = (opt.enforce_passphrase_constraints?
251                           _("You have not entered a passphrase!%0A"
252                             "An empty passphrase is not allowed.") :
253                           _("You have not entered a passphrase - "
254                             "this is in general a bad idea!%0A"
255                             "Please confirm that you do not want to "
256                             "have any protection on your key."));
257       
258       if (silent)
259         return gpg_error (GPG_ERR_INV_PASSPHRASE);
260
261       err = take_this_one_anyway2 (ctrl, desc,
262                                    _("Yes, protection is not needed"));
263       if (err)
264         return err;
265     }
266
267   return 0;
268 }
269
270
271 /* Callback function to compare the first entered PIN with the one
272    currently being entered. */
273 static int
274 reenter_compare_cb (struct pin_entry_info_s *pi)
275 {
276   const char *pin1 = pi->check_cb_arg;
277
278   if (!strcmp (pin1, pi->pin))
279     return 0; /* okay */
280   return -1;
281 }
282
283
284
285 /* Generate a new keypair according to the parameters given in
286    KEYPARAM */
287 int
288 agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
289               membuf_t *outbuf) 
290 {
291   gcry_sexp_t s_keyparam, s_key, s_private, s_public;
292   struct pin_entry_info_s *pi, *pi2;
293   int rc;
294   size_t len;
295   char *buf;
296
297   rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
298   if (rc)
299     {
300       log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc));
301       return gpg_error (GPG_ERR_INV_DATA);
302     }
303
304   /* Get the passphrase now, cause key generation may take a while. */
305   {
306     const char *text1 = _("Please enter the passphrase to%0A"
307                                "to protect your new key");
308     const char *text2 = _("Please re-enter this passphrase");
309     const char *initial_errtext = NULL;
310
311     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
312     pi2 = pi + (sizeof *pi + 100);
313     pi->max_length = 100;
314     pi->max_tries = 3;
315     pi->with_qualitybar = 1;
316     pi2->max_length = 100;
317     pi2->max_tries = 3;
318     pi2->check_cb = reenter_compare_cb;
319     pi2->check_cb_arg = pi->pin;
320
321   next_try:
322     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
323     initial_errtext = NULL;
324     if (!rc)
325       {
326         if (check_passphrase_constraints (ctrl, pi->pin, 0))
327           {
328             pi->failed_tries = 0;
329             pi2->failed_tries = 0;
330             goto next_try;
331           }
332         if (pi->pin && *pi->pin)
333           {
334             rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
335             if (rc == -1)
336               { /* The re-entered one did not match and the user did not
337                    hit cancel. */
338                 initial_errtext = _("does not match - try again");
339                 goto next_try;
340               }
341           }
342       }
343     if (rc)
344       {
345         xfree (pi);
346         return rc;
347       }
348         
349     if (!*pi->pin)
350       {
351         xfree (pi);
352         pi = NULL; /* User does not want a passphrase. */
353       }
354   }
355
356   rc = gcry_pk_genkey (&s_key, s_keyparam );
357   gcry_sexp_release (s_keyparam);
358   if (rc)
359     {
360       log_error ("key generation failed: %s\n", gpg_strerror (rc));
361       xfree (pi);
362       return rc;
363     }
364
365   /* break out the parts */
366   s_private = gcry_sexp_find_token (s_key, "private-key", 0);
367   if (!s_private)
368     {
369       log_error ("key generation failed: invalid return value\n");
370       gcry_sexp_release (s_key);
371       xfree (pi);
372       return gpg_error (GPG_ERR_INV_DATA);
373     }
374   s_public = gcry_sexp_find_token (s_key, "public-key", 0);
375   if (!s_public)
376     {
377       log_error ("key generation failed: invalid return value\n");
378       gcry_sexp_release (s_private);
379       gcry_sexp_release (s_key);
380       xfree (pi);
381       return gpg_error (GPG_ERR_INV_DATA);
382     }
383   gcry_sexp_release (s_key); s_key = NULL;
384   
385   /* store the secret key */
386   if (DBG_CRYPTO)
387     log_debug ("storing private key\n");
388   rc = store_key (s_private, pi? pi->pin:NULL, 0);
389   xfree (pi); pi = NULL;
390   gcry_sexp_release (s_private);
391   if (rc)
392     {
393       gcry_sexp_release (s_public);
394       return rc;
395     }
396
397   /* return the public key */
398   if (DBG_CRYPTO)
399     log_debug ("returning public key\n");
400   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
401   assert (len);
402   buf = xtrymalloc (len);
403   if (!buf)
404     {
405       gpg_error_t tmperr = out_of_core ();
406       gcry_sexp_release (s_private);
407       gcry_sexp_release (s_public);
408       return tmperr;
409     }
410   len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
411   assert (len);
412   put_membuf (outbuf, buf, len);
413   gcry_sexp_release (s_public);
414   xfree (buf);
415
416   return 0;
417 }
418
419
420 \f
421 /* Apply a new passpahrse to the key S_SKEY and store it. */
422 int
423 agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey) 
424 {
425   struct pin_entry_info_s *pi, *pi2;
426   int rc;
427
428   {
429     const char *text1 = _("Please enter the new passphrase");
430     const char *text2 = _("Please re-enter this passphrase");
431     const char *initial_errtext = NULL;
432
433     pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
434     pi2 = pi + (sizeof *pi + 100);
435     pi->max_length = 100;
436     pi->max_tries = 3;
437     pi->with_qualitybar = 1;
438     pi2->max_length = 100;
439     pi2->max_tries = 3;
440     pi2->check_cb = reenter_compare_cb;
441     pi2->check_cb_arg = pi->pin;
442
443   next_try:
444     rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
445     initial_errtext = NULL;
446     if (!rc)
447       {
448         if (check_passphrase_constraints (ctrl, pi->pin, 0))
449           {
450             pi->failed_tries = 0;
451             pi2->failed_tries = 0;
452             goto next_try;
453           }
454         /* Unless the passphrase is empty, ask to confirm it.  */
455         if (pi->pin && *pi->pin)
456           {
457             rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
458             if (rc == -1)
459               { /* The re-entered one did not match and the user did not
460                    hit cancel. */
461                 initial_errtext = _("does not match - try again");
462                 goto next_try;
463               }
464           }
465       }
466     if (rc)
467       {
468         xfree (pi);
469         return rc;
470       }
471
472     if (!*pi->pin)
473       {
474         xfree (pi);
475         pi = NULL; /* User does not want a passphrase. */
476       }
477   }
478
479   rc = store_key (s_skey, pi? pi->pin:NULL, 1);
480   xfree (pi);
481   return 0;
482 }