Avoid catch-22 with README.main not being distributed but having the
[gnupg.git] / agent / genkey.c
blobd862963907b62178657e61ee83dbbb8dd7f4c941
1 /* genkey.c - Generate a keypair
2 * Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
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>
28 #include "agent.h"
29 #include "i18n.h"
30 #include "exechelp.h"
31 #include "sysutils.h"
33 static int
34 store_key (gcry_sexp_t private, const char *passphrase, int force)
36 int rc;
37 unsigned char *buf;
38 size_t len;
39 unsigned char grip[20];
41 if ( !gcry_pk_get_keygrip (private, grip) )
43 log_error ("can't calculate keygrip\n");
44 return gpg_error (GPG_ERR_GENERAL);
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);
55 if (passphrase)
57 unsigned char *p;
59 rc = agent_protect (buf, passphrase, &p, &len);
60 if (rc)
62 xfree (buf);
63 return rc;
65 xfree (buf);
66 buf = p;
69 rc = agent_write_private_key (grip, buf, len, force);
70 xfree (buf);
71 return rc;
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)
80 size_t n;
82 for (n=0; *s; s++)
83 if (isascii (*s) && ( isdigit (*s) || ispunct (*s) ))
84 n++;
86 return n;
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)
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;
102 (void)ctrl;
104 infp = gnupg_tmpfile ();
105 if (!infp)
107 err = gpg_error_from_syserror ();
108 log_error (_("error creating temporary file: %s\n"), strerror (errno));
109 return 1; /* Error - assume password should not be used. */
112 if (fwrite (pw, strlen (pw), 1, infp) != 1)
114 err = gpg_error_from_syserror ();
115 log_error (_("error writing to temporary file: %s\n"),
116 strerror (errno));
117 fclose (infp);
118 return 1; /* Error - assume password should not be used. */
120 rewind (infp);
122 i = 0;
123 argv[i++] = "--null";
124 argv[i++] = "--",
125 argv[i++] = opt.check_passphrase_pattern,
126 argv[i] = NULL;
127 assert (i < sizeof argv);
129 if (gnupg_spawn_process_fd (pgmname, argv, fileno (infp), -1, -1, &pid))
130 result = 1; /* Execute error - assume password should no be used. */
131 else if (gnupg_wait_process (pgmname, pid, NULL))
132 result = 1; /* Helper returned an error - probably a match. */
133 else
134 result = 0; /* Success; i.e. no match. */
136 /* Overwrite our temporary file. */
137 rewind (infp);
138 for (i=((strlen (pw)+99)/100)*100; i > 0; i--)
139 putc ('\xff', infp);
140 fflush (infp);
141 fclose (infp);
142 return result;
146 static int
147 take_this_one_anyway2 (ctrl_t ctrl, const char *desc, const char *anyway_btn)
149 gpg_error_t err;
151 if (opt.enforce_passphrase_constraints)
153 err = agent_show_message (ctrl, desc, _("Enter new passphrase"));
154 if (!err)
155 err = gpg_error (GPG_ERR_CANCELED);
157 else
158 err = agent_get_confirmation (ctrl, desc,
159 anyway_btn, _("Enter new passphrase"), 0);
160 return err;
164 static int
165 take_this_one_anyway (ctrl_t ctrl, const char *desc)
167 return take_this_one_anyway2 (ctrl, desc, _("Take this one anyway"));
171 /* Check whether the passphrase PW is suitable. Returns 0 if the
172 passphrase is suitable and true if it is not and the user should be
173 asked to provide a different one. If SILENT is set, no message are
174 displayed. */
176 check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent)
178 gpg_error_t err;
179 unsigned int minlen = opt.min_passphrase_len;
180 unsigned int minnonalpha = opt.min_passphrase_nonalpha;
182 if (!pw)
183 pw = "";
185 if (utf8_charcount (pw) < minlen )
187 char *desc;
189 if (silent)
190 return gpg_error (GPG_ERR_INV_PASSPHRASE);
192 desc = xtryasprintf
193 ( ngettext ("Warning: You have entered an insecure passphrase.%%0A"
194 "A passphrase should be at least %u character long.",
195 "Warning: You have entered an insecure passphrase.%%0A"
196 "A passphrase should be at least %u characters long.",
197 minlen), minlen );
198 if (!desc)
199 return gpg_error_from_syserror ();
200 err = take_this_one_anyway (ctrl, desc);
201 xfree (desc);
202 if (err)
203 return err;
206 if (nonalpha_count (pw) < minnonalpha )
208 char *desc;
210 if (silent)
211 return gpg_error (GPG_ERR_INV_PASSPHRASE);
213 desc = xtryasprintf
214 ( ngettext ("Warning: You have entered an insecure passphrase.%%0A"
215 "A passphrase should contain at least %u digit or%%0A"
216 "special character.",
217 "Warning: You have entered an insecure passphrase.%%0A"
218 "A passphrase should contain at least %u digits or%%0A"
219 "special characters.",
220 minnonalpha), minnonalpha );
221 if (!desc)
222 return gpg_error_from_syserror ();
223 err = take_this_one_anyway (ctrl, desc);
224 xfree (desc);
225 if (err)
226 return err;
229 /* If configured check the passphrase against a list of know words
230 and pattern. The actual test is done by an external program.
231 The warning message is generic to give the user no hint on how to
232 circumvent this list. */
233 if (*pw && opt.check_passphrase_pattern &&
234 check_passphrase_pattern (ctrl, pw))
236 const char *desc =
237 /* */ _("Warning: You have entered an insecure passphrase.%%0A"
238 "A passphrase may not be a known term or match%%0A"
239 "certain pattern.");
241 if (silent)
242 return gpg_error (GPG_ERR_INV_PASSPHRASE);
244 err = take_this_one_anyway (ctrl, desc);
245 if (err)
246 return err;
249 /* The final check is to warn about an empty passphrase. */
250 if (!*pw)
252 const char *desc = (opt.enforce_passphrase_constraints?
253 _("You have not entered a passphrase!%0A"
254 "An empty passphrase is not allowed.") :
255 _("You have not entered a passphrase - "
256 "this is in general a bad idea!%0A"
257 "Please confirm that you do not want to "
258 "have any protection on your key."));
260 if (silent)
261 return gpg_error (GPG_ERR_INV_PASSPHRASE);
263 err = take_this_one_anyway2 (ctrl, desc,
264 _("Yes, protection is not needed"));
265 if (err)
266 return err;
269 return 0;
273 /* Callback function to compare the first entered PIN with the one
274 currently being entered. */
275 static int
276 reenter_compare_cb (struct pin_entry_info_s *pi)
278 const char *pin1 = pi->check_cb_arg;
280 if (!strcmp (pin1, pi->pin))
281 return 0; /* okay */
282 return -1;
287 /* Generate a new keypair according to the parameters given in
288 KEYPARAM */
290 agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
291 membuf_t *outbuf)
293 gcry_sexp_t s_keyparam, s_key, s_private, s_public;
294 struct pin_entry_info_s *pi, *pi2;
295 int rc;
296 size_t len;
297 char *buf;
299 rc = gcry_sexp_sscan (&s_keyparam, NULL, keyparam, keyparamlen);
300 if (rc)
302 log_error ("failed to convert keyparam: %s\n", gpg_strerror (rc));
303 return gpg_error (GPG_ERR_INV_DATA);
306 /* Get the passphrase now, cause key generation may take a while. */
308 const char *text1 = _("Please enter the passphrase to%0A"
309 "to protect your new key");
310 const char *text2 = _("Please re-enter this passphrase");
311 const char *initial_errtext = NULL;
313 pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
314 pi2 = pi + (sizeof *pi + 100);
315 pi->max_length = 100;
316 pi->max_tries = 3;
317 pi->with_qualitybar = 1;
318 pi2->max_length = 100;
319 pi2->max_tries = 3;
320 pi2->check_cb = reenter_compare_cb;
321 pi2->check_cb_arg = pi->pin;
323 next_try:
324 rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
325 initial_errtext = NULL;
326 if (!rc)
328 if (check_passphrase_constraints (ctrl, pi->pin, 0))
330 pi->failed_tries = 0;
331 pi2->failed_tries = 0;
332 goto next_try;
334 if (pi->pin && *pi->pin)
336 rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
337 if (rc == -1)
338 { /* The re-entered one did not match and the user did not
339 hit cancel. */
340 initial_errtext = _("does not match - try again");
341 goto next_try;
345 if (rc)
347 xfree (pi);
348 return rc;
351 if (!*pi->pin)
353 xfree (pi);
354 pi = NULL; /* User does not want a passphrase. */
358 rc = gcry_pk_genkey (&s_key, s_keyparam );
359 gcry_sexp_release (s_keyparam);
360 if (rc)
362 log_error ("key generation failed: %s\n", gpg_strerror (rc));
363 xfree (pi);
364 return rc;
367 /* break out the parts */
368 s_private = gcry_sexp_find_token (s_key, "private-key", 0);
369 if (!s_private)
371 log_error ("key generation failed: invalid return value\n");
372 gcry_sexp_release (s_key);
373 xfree (pi);
374 return gpg_error (GPG_ERR_INV_DATA);
376 s_public = gcry_sexp_find_token (s_key, "public-key", 0);
377 if (!s_public)
379 log_error ("key generation failed: invalid return value\n");
380 gcry_sexp_release (s_private);
381 gcry_sexp_release (s_key);
382 xfree (pi);
383 return gpg_error (GPG_ERR_INV_DATA);
385 gcry_sexp_release (s_key); s_key = NULL;
387 /* store the secret key */
388 if (DBG_CRYPTO)
389 log_debug ("storing private key\n");
390 rc = store_key (s_private, pi? pi->pin:NULL, 0);
391 xfree (pi); pi = NULL;
392 gcry_sexp_release (s_private);
393 if (rc)
395 gcry_sexp_release (s_public);
396 return rc;
399 /* return the public key */
400 if (DBG_CRYPTO)
401 log_debug ("returning public key\n");
402 len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, NULL, 0);
403 assert (len);
404 buf = xtrymalloc (len);
405 if (!buf)
407 gpg_error_t tmperr = out_of_core ();
408 gcry_sexp_release (s_private);
409 gcry_sexp_release (s_public);
410 return tmperr;
412 len = gcry_sexp_sprint (s_public, GCRYSEXP_FMT_CANON, buf, len);
413 assert (len);
414 put_membuf (outbuf, buf, len);
415 gcry_sexp_release (s_public);
416 xfree (buf);
418 return 0;
423 /* Apply a new passpahrse to the key S_SKEY and store it. */
425 agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey)
427 struct pin_entry_info_s *pi, *pi2;
428 int rc;
431 const char *text1 = _("Please enter the new passphrase");
432 const char *text2 = _("Please re-enter this passphrase");
433 const char *initial_errtext = NULL;
435 pi = gcry_calloc_secure (2, sizeof (*pi) + 100);
436 pi2 = pi + (sizeof *pi + 100);
437 pi->max_length = 100;
438 pi->max_tries = 3;
439 pi->with_qualitybar = 1;
440 pi2->max_length = 100;
441 pi2->max_tries = 3;
442 pi2->check_cb = reenter_compare_cb;
443 pi2->check_cb_arg = pi->pin;
445 next_try:
446 rc = agent_askpin (ctrl, text1, NULL, initial_errtext, pi);
447 initial_errtext = NULL;
448 if (!rc)
450 if (check_passphrase_constraints (ctrl, pi->pin, 0))
452 pi->failed_tries = 0;
453 pi2->failed_tries = 0;
454 goto next_try;
456 /* Unless the passphrase is empty, ask to confirm it. */
457 if (pi->pin && *pi->pin)
459 rc = agent_askpin (ctrl, text2, NULL, NULL, pi2);
460 if (rc == -1)
461 { /* The re-entered one did not match and the user did not
462 hit cancel. */
463 initial_errtext = _("does not match - try again");
464 goto next_try;
468 if (rc)
470 xfree (pi);
471 return rc;
474 if (!*pi->pin)
476 xfree (pi);
477 pi = NULL; /* User does not want a passphrase. */
481 rc = store_key (s_skey, pi? pi->pin:NULL, 1);
482 xfree (pi);
483 return rc;