Implemented the --gen-key command as we can't use the gpgsm-gencert.sh under Windows.
[gnupg.git] / sm / certreqgen-ui.c
1 /* certreqgen-ui.c - Simple user interface for certreqgen.c
2  * Copyright (C) 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 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
19  * USA.
20  */
21
22 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <unistd.h> 
28 #include <time.h>
29 #include <assert.h>
30
31 #include "gpgsm.h"
32 #include <gcrypt.h>
33
34 #include "i18n.h"
35 #include "ttyio.h"
36 #include "membuf.h"
37
38
39 /* Prompt for lines and append them to MB.  */
40 static void
41 ask_mb_lines (membuf_t *mb, const char *prefix)
42 {
43   char *answer = NULL;
44
45   do 
46     {
47       xfree (answer);
48       answer = tty_get ("> ");
49       tty_kill_prompt ();
50       trim_spaces (answer);
51       if (*answer)
52         {
53           put_membuf_str (mb, prefix);
54           put_membuf_str (mb, answer);
55           put_membuf (mb, "\n", 1);
56         }
57     }
58   while (*answer);
59   xfree (answer);
60 }
61
62 /* Helper to store stuff in a membuf.  */
63 void
64 store_key_value_lf (membuf_t *mb, const char *key, const char *value)
65 {
66   put_membuf_str (mb, key);
67   put_membuf_str (mb, value);
68   put_membuf (mb, "\n", 1);
69 }
70
71 /* Helper tp store a membuf create by mb_ask_lines into MB.  Returns
72    -1 on error. */
73 int
74 store_mb_lines (membuf_t *mb, membuf_t *lines)
75 {
76   char *p;
77
78   if (get_membuf_len (lines))
79     {
80       put_membuf (lines, "", 1);
81       p = get_membuf (lines, NULL);
82       if (!p)
83         return -1;
84       put_membuf_str (mb, p);
85       xfree (p);
86     }
87   return 0;
88 }
89
90
91 /* This function is used to create a certificate request from the
92    command line.  In the past the similar gpgsm-gencert.sh script has
93    been used for it; however that scripts requires a full Unix shell
94    and thus is not suitable for the Windows port.  So here is the
95    re-implementation.  */
96 void
97 gpgsm_gencertreq_tty (ctrl_t ctrl, FILE *output_fp)
98 {
99   gpg_error_t err;
100   char *answer;
101   int selection;
102   FILE *fp = NULL;
103   int method;
104   char *keytype;
105   char *keygrip = NULL;
106   unsigned int nbits;
107   int minbits = 1024;
108   int maxbits = 4096;
109   int defbits = 2048;
110   const char *keyusage;
111   char *subject_name;
112   membuf_t mb_email, mb_dns, mb_uri, mb_result;
113   char *result = NULL;
114   int i;
115   const char *s, *s2;
116
117   init_membuf (&mb_email, 100);
118   init_membuf (&mb_dns, 100);
119   init_membuf (&mb_uri, 100);
120   init_membuf (&mb_result, 512);
121
122   /* Get the type of the key.  */
123   tty_printf (_("Please select what kind of key you want:\n"));
124   tty_printf (_("   (%d) RSA\n"), 1 );
125   tty_printf (_("   (%d) Existing key\n"), 2 );
126   tty_printf (_("   (%d) Existing key from card\n"), 3 );
127
128   do
129     {
130       answer = tty_get (_("Your selection? "));
131       tty_kill_prompt ();
132       selection = *answer? atoi (answer): 1;
133       xfree (answer);
134     }
135   while (!(selection >= 1 && selection <= 3));
136   method = selection;
137
138   /* Get  size of the key.  */
139   if (method == 1)
140     {
141       keytype = xstrdup ("RSA");
142       for (;;)
143         {
144           answer = tty_getf (_("What keysize do you want? (%u) "), defbits);
145           tty_kill_prompt ();
146           nbits = *answer? atoi (answer): defbits;
147           xfree (answer);
148           if (nbits < minbits || nbits > maxbits)
149             tty_printf(_("%s keysizes must be in the range %u-%u\n"),
150                          "RSA", minbits, maxbits);
151           else
152             break; /* Okay.  */
153         }
154       tty_printf (_("Requested keysize is %u bits\n"), nbits);
155       /* We round it up so that it better matches the word size.  */
156       if (( nbits % 64))
157         {
158           nbits = ((nbits + 63) / 64) * 64;
159           tty_printf (_("rounded up to %u bits\n"), nbits);
160         }
161     }
162   else if (method == 2)
163     {
164       tty_printf ("Not yet supported; "
165                   "use the gpgsm-gencert.sh script instead\n");
166       keytype = xstrdup ("RSA"); 
167       nbits = defbits; /* We need a dummy value.  */
168     }
169   else /* method == 3 */
170     {
171       tty_printf ("Not yet supported; "
172                   "use the gpgsm-gencert.sh script instead\n");
173       keytype = xstrdup ("card:foobar");
174       nbits = defbits; /* We need a dummy value.  */
175     }
176
177   /* Ask for the key usage.  */
178   tty_printf (_("Possible actions for a %s key:\n"), "RSA");
179   tty_printf (_("   (%d) sign, encrypt\n"), 1 );
180   tty_printf (_("   (%d) sign\n"), 2 );
181   tty_printf (_("   (%d) encrypt\n"), 3 );
182   do
183     {
184       answer = tty_get (_("Your selection? "));
185       tty_kill_prompt ();
186       selection = *answer? atoi (answer): 1;
187       xfree (answer);
188       switch (selection)
189         {
190         case 1: keyusage = "sign, encrypt"; break;
191         case 2: keyusage = "sign"; break;
192         case 3: keyusage = "encrypt"; break;
193         default: keyusage = NULL; break;
194         }
195     }
196   while (!keyusage);
197
198   /* Get the subject name.  */
199   answer = NULL;
200   do
201     {
202       size_t erroff, errlen;
203
204       xfree (answer);
205       answer = tty_get (_("Enter the X.509 subject name: "));
206       tty_kill_prompt ();
207       trim_spaces (answer);
208       if (!*answer)
209         tty_printf (_("No subject name given\n"));
210       else if ( (err = ksba_dn_teststr (answer, 0, &erroff, &errlen)) )
211         {
212           if (gpg_err_code (err) == GPG_ERR_UNKNOWN_NAME)
213             tty_printf (_("Invalid subject name label `%.*s'\n"),
214                         (int)errlen, answer+erroff);
215           else
216             {
217               /* TRANSLATORS: The 22 in the second string is the
218                  length of the first string up to the "%s".  Please
219                  adjust it do the length of your translation.  The
220                  second string is merely passed to atoi so you can
221                  drop everything after the number.  */
222               tty_printf (_("Invalid subject name `%s'\n"), answer);
223               tty_printf ("%*s^\n",
224                           atoi (_("22 translator: see "
225                                   "certreg-ui.c:gpgsm_gencertreq_tty"))
226                           + (int)erroff, "");
227             }
228           *answer = 0;
229         }
230     }
231   while (!*answer);
232   subject_name = answer;
233   answer = NULL;
234
235   /* Get the email addresses. */
236   tty_printf (_("Enter email addresses"));
237   tty_printf (_(" (end with an empty line):\n"));
238   ask_mb_lines (&mb_email, "Name-Email: ");
239
240   /* DNS names.  */
241   tty_printf (_("Enter DNS names"));
242   tty_printf (_(" (optional; end with an empty line):\n"));
243   ask_mb_lines (&mb_email, "Name-DNS: ");
244
245   /* URIs.  */
246   tty_printf (_("Enter URIs"));
247   tty_printf (_(" (optional; end with an empty line):\n"));
248   ask_mb_lines (&mb_email, "Name-URI: ");
249
250
251   /* Put it all together.  */
252   store_key_value_lf (&mb_result, "Key-Type: ", keytype);
253   {
254     char numbuf[30];
255     snprintf (numbuf, sizeof numbuf, "%u", nbits);
256     store_key_value_lf (&mb_result, "Key-Length: ", numbuf);
257   }
258   store_key_value_lf (&mb_result, "Key-Usage: ", keyusage);
259   store_key_value_lf (&mb_result, "Name-DN: ", subject_name);
260   if (keygrip)
261     store_key_value_lf (&mb_result, "Key-Grip: ", keygrip);
262   if (store_mb_lines (&mb_result, &mb_email))
263     goto mem_error;
264   if (store_mb_lines (&mb_result, &mb_dns))
265     goto mem_error;
266   if (store_mb_lines (&mb_result, &mb_uri))
267     goto mem_error;
268   put_membuf (&mb_result, "", 1);
269   result = get_membuf (&mb_result, NULL);
270   if (!result)
271     goto mem_error;
272
273   tty_printf (_("Parameters to be used for the certificate request:\n"));
274   for (s=result; (s2 = strchr (s, '\n')); s = s2+1, i++)
275     tty_printf ("    %.*s\n", (int)(s2-s), s);
276   tty_printf ("\n");
277
278
279   if (!tty_get_answer_is_yes ("Really create request? (y/N) "))
280      goto leave;
281
282   /* Now create a parameter file and generate the key.  */
283   fp = tmpfile ();
284   if (!fp)
285     {
286       log_error (_("error creating temporary file: %s\n"), strerror (errno));
287       goto leave;
288     }
289   fputs (result, fp);
290   rewind (fp);
291   tty_printf (_("Now creating certificate request.  "
292                 "This may take a while ...\n"));
293   {
294     int save_pem = ctrl->create_pem;
295     ctrl->create_pem = 1; /* Force creation of PEM. */
296     err = gpgsm_genkey (ctrl, -1, fp, output_fp);
297     ctrl->create_pem = save_pem;
298   }
299   if (!err)
300     tty_printf (_("Ready.  You should now send this request to your CA.\n"));
301
302
303   goto leave;
304  mem_error:
305   log_error (_("resource problem: out or core\n"));
306  leave:
307   if (fp)
308     fclose (fp);
309   xfree (keytype);         
310   xfree (subject_name);
311   xfree (keygrip);
312   xfree (get_membuf (&mb_email, NULL));
313   xfree (get_membuf (&mb_dns, NULL));
314   xfree (get_membuf (&mb_uri, NULL));
315   xfree (get_membuf (&mb_result, NULL));
316   xfree (result);
317 }