Added SILC Server library.
[silc.git] / lib / silcutil / silcapputil.c
1 /*
2
3   silcapputil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2005 Pekka Riikonen
8
9   This program is free software; you can redistribute it and/or modify
10   it under the terms of the GNU General Public License as published by
11   the Free Software Foundation; version 2 of the License.
12
13   This program is distributed in the hope that it will be useful,
14   but WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16   GNU General Public License for more details.
17
18 */
19 /* $Id$ */
20
21 #include "silc.h"
22
23 static char *silc_create_pk_identifier(void)
24 {
25   char *username = NULL, *realname = NULL;
26   char *hostname, email[256];
27   char *ident;
28
29   /* Get realname */
30   realname = silc_get_real_name();
31
32   /* Get hostname */
33   hostname = silc_net_localhost();
34   if (!hostname)
35     return NULL;
36
37   /* Get username (mandatory) */
38   username = silc_get_username();
39   if (!username)
40     return NULL;
41
42   /* Create default email address, whether it is right or not */
43   snprintf(email, sizeof(email), "%s@%s", username, hostname);
44
45   ident = silc_pkcs_encode_identifier(username, hostname, realname, email,
46                                       NULL, NULL);
47   if (realname)
48     silc_free(realname);
49   silc_free(hostname);
50   silc_free(username);
51
52   return ident;
53 }
54
55 /* Generate key pair */
56
57 SilcBool silc_create_key_pair(const char *pkcs_name,
58                           SilcUInt32 key_len_bits,
59                           const char *pub_filename,
60                           const char *prv_filename,
61                           const char *pub_identifier,
62                           const char *passphrase,
63                           SilcPKCS *return_pkcs,
64                           SilcPublicKey *return_public_key,
65                           SilcPrivateKey *return_private_key,
66                           SilcBool interactive)
67 {
68   SilcPKCS pkcs;
69   SilcPublicKey pub_key;
70   SilcPrivateKey prv_key;
71   SilcRng rng;
72   unsigned char *key;
73   SilcUInt32 key_len;
74   char line[256];
75   char *pkfile = pub_filename ? strdup(pub_filename) : NULL;
76   char *prvfile = prv_filename ? strdup(prv_filename) : NULL;
77   char *alg = pkcs_name ? strdup(pkcs_name) : NULL;
78   char *identifier = pub_identifier ? strdup(pub_identifier) : NULL;
79   char *pass = passphrase ? strdup(passphrase) : NULL;
80
81   if (interactive && (!alg || !pub_filename || !prv_filename))
82     printf("\
83 New pair of keys will be created.  Please, answer to following questions.\n\
84 ");
85
86   if (!alg) {
87     if (interactive) {
88       while (!alg) {
89         alg = silc_get_input("PKCS name (l to list names) [rsa]: ", FALSE);
90         if (!alg)
91           alg = strdup("rsa");
92
93         if (*alg == 'l' || *alg == 'L') {
94           char *list = silc_pkcs_get_supported();
95           printf("%s\n", list);
96           silc_free(list);
97           silc_free(alg);
98           alg = NULL;
99         }
100       }
101     } else {
102       alg = strdup("rsa");
103     }
104   }
105
106   if (!silc_pkcs_is_supported(alg)) {
107     fprintf(stderr, "Unknown PKCS algorithm `%s' or crypto library"
108             "is not initialized", alg);
109     return FALSE;
110   }
111
112   if (!key_len_bits) {
113     if (interactive) {
114       char *length = NULL;
115       length = silc_get_input("Key length in key_len_bits [2048]: ", FALSE);
116       if (length)
117         key_len_bits = atoi(length);
118       silc_free(length);
119     }
120     if (!key_len_bits)
121       key_len_bits = 2048;
122   }
123
124   if (!identifier) {
125     char *def = silc_create_pk_identifier();
126
127     if (interactive) {
128       memset(line, 0, sizeof(line));
129       if (def)
130         snprintf(line, sizeof(line), "Identifier [%s]: ", def);
131       else
132         snprintf(line, sizeof(line),
133                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
134                "RN=Jon Johnson, E=jon@dummy.com): ");
135
136       while (!identifier) {
137         identifier = silc_get_input(line, FALSE);
138         if (!identifier && def)
139           identifier = strdup(def);
140       }
141     } else {
142       if (!def) {
143         fprintf(stderr, "Could not create public key identifier: %s\n",
144                 strerror(errno));
145         return FALSE;
146       }
147       identifier = strdup(def);
148     }
149
150     silc_free(def);
151   }
152
153   rng = silc_rng_alloc();
154   silc_rng_init(rng);
155   silc_rng_global_init(rng);
156
157   if (!pkfile) {
158     if (interactive) {
159       memset(line, 0, sizeof(line));
160       snprintf(line, sizeof(line), "Public key filename [public_key.pub]: ");
161       pkfile = silc_get_input(line, FALSE);
162     }
163     if (!pkfile)
164       pkfile = strdup("public_key.pub");
165   }
166
167   if (!prvfile) {
168     if (interactive) {
169       memset(line, 0, sizeof(line));
170       snprintf(line, sizeof(line), "Private key filename [private_key.prv]: ");
171       prvfile = silc_get_input(line, FALSE);
172     }
173     if (!prvfile)
174       prvfile = strdup("private_key.prv");
175   }
176
177   if (!pass) {
178     while (TRUE) {
179       char *pass2 = NULL;
180       pass = silc_get_input("Private key passphrase: ", TRUE);
181       if (!pass) {
182         pass = strdup("");
183         break;
184       } else {
185         SilcBool match;
186         printf("\n");
187         pass2 = silc_get_input("Retype private key passphrase: ", TRUE);
188         if (!pass2)
189           pass2 = strdup("");
190         match = !strcmp(pass, pass2);
191         silc_free(pass2);
192         if (match)
193           break;
194         fprintf(stderr, "\nPassphrases do not match\n\n");
195       }
196     }
197   }
198
199   /* Generate keys */
200   silc_pkcs_alloc(alg, SILC_PKCS_SILC, &pkcs);
201   silc_pkcs_generate_key(pkcs, key_len_bits, rng);
202
203   /* Save public key into file */
204   key = silc_pkcs_get_public_key(pkcs, &key_len);
205   pub_key = silc_pkcs_public_key_alloc(silc_pkcs_get_name(pkcs),
206                                        identifier, key, key_len);
207   silc_pkcs_save_public_key(pkfile, pub_key, SILC_PKCS_FILE_PEM);
208   if (return_public_key)
209     *return_public_key = pub_key;
210   else
211     silc_pkcs_public_key_free(pub_key);
212   memset(key, 0, key_len);
213   silc_free(key);
214
215   /* Save private key into file */
216   key = silc_pkcs_get_private_key(pkcs, &key_len);
217   prv_key = silc_pkcs_private_key_alloc(silc_pkcs_get_name(pkcs),
218                                         key, key_len);
219   silc_pkcs_save_private_key(prvfile, prv_key,
220                              (unsigned char *)pass, strlen(pass),
221                              SILC_PKCS_FILE_BIN);
222   if (return_private_key)
223     *return_private_key = prv_key;
224   else
225     silc_pkcs_private_key_free(prv_key);
226   memset(key, 0, key_len);
227   silc_free(key);
228
229   printf("Public key has been saved into `%s'.\n", pkfile);
230   printf("Private key has been saved into `%s'.\n", prvfile);
231   if (interactive) {
232     printf("Press <Enter> to continue...\n");
233     getchar();
234   }
235
236   if (return_pkcs)
237     *return_pkcs = pkcs;
238   else
239     silc_pkcs_free(pkcs);
240
241   silc_rng_free(rng);
242   silc_free(alg);
243   silc_free(pkfile);
244   silc_free(prvfile);
245   silc_free(identifier);
246   memset(pass, 0, strlen(pass));
247   silc_free(pass);
248
249   return TRUE;
250 }
251
252 /* Load key pair */
253
254 SilcBool silc_load_key_pair(const char *pub_filename,
255                         const char *prv_filename,
256                         const char *passphrase,
257                         SilcPKCS *return_pkcs,
258                         SilcPublicKey *return_public_key,
259                         SilcPrivateKey *return_private_key)
260 {
261   char *pass = passphrase ? strdup(passphrase) : NULL;
262
263   SILC_LOG_DEBUG(("Loading public and private keys"));
264
265   if (silc_pkcs_load_public_key((char *)pub_filename, return_public_key,
266                                 SILC_PKCS_FILE_PEM) == FALSE)
267     if (silc_pkcs_load_public_key((char *)pub_filename, return_public_key,
268                                   SILC_PKCS_FILE_BIN) == FALSE) {
269       if (pass)
270         memset(pass, 0, strlen(pass));
271       silc_free(pass);
272       return FALSE;
273     }
274
275   if (!pass) {
276     pass = silc_get_input("Private key passphrase: ", TRUE);
277     if (!pass)
278       pass = strdup("");
279   }
280
281   if (silc_pkcs_load_private_key((char *)prv_filename, return_private_key,
282                                  (unsigned char *)pass, strlen(pass),
283                                  SILC_PKCS_FILE_BIN) == FALSE)
284     if (silc_pkcs_load_private_key((char *)prv_filename, return_private_key,
285                                    (unsigned char *)pass, strlen(pass),
286                                    SILC_PKCS_FILE_PEM) == FALSE) {
287       memset(pass, 0, strlen(pass));
288       silc_free(pass);
289       return FALSE;
290     }
291
292   if (return_pkcs) {
293     silc_pkcs_alloc((*return_public_key)->name, SILC_PKCS_SILC, return_pkcs);
294     silc_pkcs_public_key_set(*return_pkcs, *return_public_key);
295     silc_pkcs_private_key_set(*return_pkcs, *return_private_key);
296   }
297
298   memset(pass, 0, strlen(pass));
299   silc_free(pass);
300   return TRUE;
301 }
302
303 /* Dump public key into stdout */
304
305 SilcBool silc_show_public_key(const char *pub_filename)
306 {
307   SilcPublicKey public_key;
308   SilcPublicKeyIdentifier ident;
309   char *fingerprint, *babbleprint;
310   unsigned char *pk;
311   SilcUInt32 pk_len;
312   SilcPKCS pkcs;
313   SilcUInt32 key_len = 0;
314
315   if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
316                                 SILC_PKCS_FILE_PEM) == FALSE)
317     if (silc_pkcs_load_public_key((char *)pub_filename, &public_key,
318                                   SILC_PKCS_FILE_BIN) == FALSE) {
319       fprintf(stderr, "Could not load public key file `%s'\n", pub_filename);
320       return FALSE;
321     }
322
323   ident = silc_pkcs_decode_identifier(public_key->identifier);
324
325   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
326   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
327   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
328
329   if (silc_pkcs_alloc(public_key->name, SILC_PKCS_SILC, &pkcs)) {
330     key_len = silc_pkcs_public_key_set(pkcs, public_key);
331     silc_pkcs_free(pkcs);
332   }
333
334   printf("Public key file    : %s\n", pub_filename);
335   printf("Algorithm          : %s\n", public_key->name);
336   if (key_len)
337     printf("Key length (bits)  : %d\n", (unsigned int)key_len);
338   if (ident->realname)
339     printf("Real name          : %s\n", ident->realname);
340   if (ident->username)
341     printf("Username           : %s\n", ident->username);
342   if (ident->host)
343     printf("Hostname           : %s\n", ident->host);
344   if (ident->email)
345     printf("Email              : %s\n", ident->email);
346   if (ident->org)
347     printf("Organization       : %s\n", ident->org);
348   if (ident->country)
349     printf("Country            : %s\n", ident->country);
350   printf("Fingerprint (SHA1) : %s\n", fingerprint);
351   printf("Babbleprint (SHA1) : %s\n", babbleprint);
352
353   fflush(stdout);
354
355   silc_free(fingerprint);
356   silc_free(babbleprint);
357   silc_free(pk);
358   silc_pkcs_public_key_free(public_key);
359   silc_pkcs_free_identifier(ident);
360
361   return TRUE;
362 }
363
364 /* Change private key passphrase */
365
366 SilcBool silc_change_private_key_passphrase(const char *prv_filename,
367                                         const char *old_passphrase,
368                                         const char *new_passphrase)
369 {
370   SilcPrivateKey private_key;
371   SilcBool base64 = FALSE;
372   char *pass;
373
374   pass = old_passphrase ? strdup(old_passphrase) : NULL;
375   if (!pass) {
376     pass = silc_get_input("Old passphrase: ", TRUE);
377     if (!pass)
378       pass = strdup("");
379   }
380
381   if (silc_pkcs_load_private_key((char *)prv_filename, &private_key,
382                                  (unsigned char *)pass, strlen(pass),
383                                  SILC_PKCS_FILE_BIN) == FALSE) {
384     base64 = TRUE;
385     if (silc_pkcs_load_private_key((char *)prv_filename, &private_key,
386                                    (unsigned char *)pass, strlen(pass),
387                                    SILC_PKCS_FILE_PEM) == FALSE) {
388       memset(pass, 0, strlen(pass));
389       silc_free(pass);
390       fprintf(stderr, "Could not load private key `%s' file\n", prv_filename);
391       return FALSE;
392     }
393   }
394
395   memset(pass, 0, strlen(pass));
396   silc_free(pass);
397
398   pass = new_passphrase ? strdup(new_passphrase) : NULL;
399   if (!pass) {
400     char *pass2 = NULL;
401     fprintf(stdout, "\n");
402     pass = silc_get_input("New passphrase: ", TRUE);
403     if (!pass) {
404       pass = strdup("");
405     } else {
406       while (TRUE) {
407         printf("\n");
408         pass2 = silc_get_input("Retype new passphrase: ", TRUE);
409         if (!pass2)
410           pass2 = strdup("");
411         if (!strcmp(pass, pass2))
412           break;
413         fprintf(stderr, "\nPassphrases do not match");
414       }
415       silc_free(pass2);
416     }
417   }
418
419   silc_pkcs_save_private_key((char *)prv_filename, private_key,
420                              (unsigned char *)pass, strlen(pass),
421                              base64 ? SILC_PKCS_FILE_PEM : SILC_PKCS_FILE_BIN);
422
423   fprintf(stdout, "\nPassphrase changed\n");
424
425   memset(pass, 0, strlen(pass));
426   silc_free(pass);
427
428   silc_pkcs_private_key_free(private_key);
429   return TRUE;
430 }