trash hexdump removed.
[silc.git] / lib / silcapputil / silcapputil.c
1 /*
2
3   silcapputil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2006 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_silc_encode_identifier(username, hostname, realname,
46                                            email, 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                               SilcPublicKey *return_public_key,
64                               SilcPrivateKey *return_private_key,
65                               SilcBool interactive)
66 {
67   SilcRng rng;
68   char line[256];
69   char *pkfile = pub_filename ? strdup(pub_filename) : NULL;
70   char *prvfile = prv_filename ? strdup(prv_filename) : NULL;
71   char *alg = pkcs_name ? strdup(pkcs_name) : NULL;
72   char *identifier = pub_identifier ? strdup(pub_identifier) : NULL;
73   char *pass = passphrase ? strdup(passphrase) : NULL;
74
75   if (interactive && (!alg || !pub_filename || !prv_filename))
76     printf("\
77 New pair of keys will be created.  Please, answer to following questions.\n\
78 ");
79
80   if (!alg) {
81     if (interactive) {
82       while (!alg) {
83         alg = silc_get_input("PKCS name (l to list names) [rsa]: ", FALSE);
84         if (!alg)
85           alg = strdup("rsa");
86
87         if (*alg == 'l' || *alg == 'L') {
88           char *list = silc_pkcs_get_supported();
89           printf("%s\n", list);
90           silc_free(list);
91           silc_free(alg);
92           alg = NULL;
93         }
94       }
95     } else {
96       alg = strdup("rsa");
97     }
98   }
99
100   if (!silc_pkcs_find_algorithm(alg, NULL)) {
101     fprintf(stderr, "Unknown PKCS algorithm `%s' or crypto library"
102             "is not initialized", alg);
103     return FALSE;
104   }
105
106   if (!key_len_bits) {
107     if (interactive) {
108       char *length = NULL;
109       length = silc_get_input("Key length in key_len_bits [2048]: ", FALSE);
110       if (length)
111         key_len_bits = atoi(length);
112       silc_free(length);
113     }
114     if (!key_len_bits)
115       key_len_bits = 2048;
116   }
117
118   if (!identifier) {
119     char *def = silc_create_pk_identifier();
120
121     if (interactive) {
122       memset(line, 0, sizeof(line));
123       if (def)
124         snprintf(line, sizeof(line), "Identifier [%s]: ", def);
125       else
126         snprintf(line, sizeof(line),
127                "Identifier (eg. UN=jon, HN=jon.dummy.com, "
128                "RN=Jon Johnson, E=jon@dummy.com): ");
129
130       while (!identifier) {
131         identifier = silc_get_input(line, FALSE);
132         if (!identifier && def)
133           identifier = strdup(def);
134       }
135     } else {
136       if (!def) {
137         fprintf(stderr, "Could not create public key identifier: %s\n",
138                 strerror(errno));
139         return FALSE;
140       }
141       identifier = strdup(def);
142     }
143
144     silc_free(def);
145   }
146
147   rng = silc_rng_alloc();
148   silc_rng_init(rng);
149   silc_rng_global_init(rng);
150
151   if (!pkfile) {
152     if (interactive) {
153       memset(line, 0, sizeof(line));
154       snprintf(line, sizeof(line), "Public key filename [public_key.pub]: ");
155       pkfile = silc_get_input(line, FALSE);
156     }
157     if (!pkfile)
158       pkfile = strdup("public_key.pub");
159   }
160
161   if (!prvfile) {
162     if (interactive) {
163       memset(line, 0, sizeof(line));
164       snprintf(line, sizeof(line), "Private key filename [private_key.prv]: ");
165       prvfile = silc_get_input(line, FALSE);
166     }
167     if (!prvfile)
168       prvfile = strdup("private_key.prv");
169   }
170
171   if (!pass) {
172     while (TRUE) {
173       char *pass2 = NULL;
174       pass = silc_get_input("Private key passphrase: ", TRUE);
175       if (!pass) {
176         pass = strdup("");
177         break;
178       } else {
179         SilcBool match;
180         printf("\n");
181         pass2 = silc_get_input("Retype private key passphrase: ", TRUE);
182         if (!pass2)
183           pass2 = strdup("");
184         match = !strcmp(pass, pass2);
185         silc_free(pass2);
186         if (match)
187           break;
188         fprintf(stderr, "\nPassphrases do not match\n\n");
189       }
190     }
191   }
192
193   /* Generate keys */
194   if (!silc_pkcs_silc_generate_key(alg, "pkcs1-no-oid", key_len_bits,
195                                    identifier, rng, return_public_key,
196                                    return_private_key))
197     return FALSE;
198
199   /* Save public key into file */
200   silc_pkcs_save_public_key(pkfile, *return_public_key, SILC_PKCS_FILE_BASE64);
201
202   /* Save private key into file */
203   silc_pkcs_save_private_key(prvfile, *return_private_key,
204                              (const unsigned char *)pass, strlen(pass),
205                              SILC_PKCS_FILE_BIN, rng);
206
207   printf("Public key has been saved into `%s'.\n", pkfile);
208   printf("Private key has been saved into `%s'.\n", prvfile);
209   if (interactive) {
210     printf("Press <Enter> to continue...\n");
211     getchar();
212   }
213
214   silc_rng_free(rng);
215   silc_free(alg);
216   silc_free(pkfile);
217   silc_free(prvfile);
218   silc_free(identifier);
219   memset(pass, 0, strlen(pass));
220   silc_free(pass);
221
222   return TRUE;
223 }
224
225 /* Load key pair */
226
227 SilcBool silc_load_key_pair(const char *pub_filename,
228                             const char *prv_filename,
229                             const char *passphrase,
230                             SilcPublicKey *return_public_key,
231                             SilcPrivateKey *return_private_key)
232 {
233   char *pass = passphrase ? strdup(passphrase) : NULL;
234
235   SILC_LOG_DEBUG(("Loading public and private keys"));
236
237   if (!silc_pkcs_load_public_key(pub_filename, return_public_key)) {
238     if (pass)
239       memset(pass, 0, strlen(pass));
240     silc_free(pass);
241     return FALSE;
242   }
243
244   if (!pass) {
245     pass = silc_get_input("Private key passphrase: ", TRUE);
246     if (!pass)
247       pass = strdup("");
248   }
249
250   if (!silc_pkcs_load_private_key(prv_filename,
251                                   (const unsigned char *)pass, strlen(pass),
252                                   return_private_key)) {
253     memset(pass, 0, strlen(pass));
254     silc_free(pass);
255     return FALSE;
256   }
257
258   memset(pass, 0, strlen(pass));
259   silc_free(pass);
260   return TRUE;
261 }
262
263 /* Dump public key into stdout */
264
265 SilcBool silc_show_public_key(const char *pub_filename)
266 {
267   SilcPublicKey public_key;
268   SilcSILCPublicKey silc_pubkey;
269   SilcPublicKeyIdentifier ident;
270   char *fingerprint, *babbleprint;
271   unsigned char *pk;
272   SilcUInt32 pk_len;
273   SilcUInt32 key_len = 0;
274
275   if (!silc_pkcs_load_public_key((char *)pub_filename, &public_key)) {
276     fprintf(stderr, "Could not load public key file `%s'\n", pub_filename);
277     return FALSE;
278   }
279
280   silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key);
281   if (!silc_pubkey) {
282     silc_pkcs_public_key_free(public_key);
283     return FALSE;
284   }
285
286   ident = &silc_pubkey->identifier;
287   key_len = silc_pkcs_public_key_get_len(public_key);
288   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
289   if (!pk) {
290     silc_pkcs_public_key_free(public_key);
291     return FALSE;
292   }
293   fingerprint = silc_hash_fingerprint(NULL, pk, pk_len);
294   babbleprint = silc_hash_babbleprint(NULL, pk, pk_len);
295
296   printf("Public key file    : %s\n", pub_filename);
297   printf("Algorithm          : %s\n", silc_pkcs_get_name(public_key));
298   if (key_len)
299     printf("Key length (bits)  : %d\n", (unsigned int)key_len);
300   if (ident->realname)
301     printf("Real name          : %s\n", ident->realname);
302   if (ident->username)
303     printf("Username           : %s\n", ident->username);
304   if (ident->host)
305     printf("Hostname           : %s\n", ident->host);
306   if (ident->email)
307     printf("Email              : %s\n", ident->email);
308   if (ident->org)
309     printf("Organization       : %s\n", ident->org);
310   if (ident->country)
311     printf("Country            : %s\n", ident->country);
312   printf("Fingerprint (SHA1) : %s\n", fingerprint);
313   printf("Babbleprint (SHA1) : %s\n", babbleprint);
314
315   fflush(stdout);
316
317   silc_free(fingerprint);
318   silc_free(babbleprint);
319   silc_free(pk);
320   silc_pkcs_public_key_free(public_key);
321
322   return TRUE;
323 }
324
325 /* Change private key passphrase */
326
327 SilcBool silc_change_private_key_passphrase(const char *prv_filename,
328                                             const char *old_passphrase,
329                                             const char *new_passphrase)
330 {
331   SilcPrivateKey private_key;
332   char *pass;
333   SilcRng rng;
334
335   pass = old_passphrase ? strdup(old_passphrase) : NULL;
336   if (!pass) {
337     pass = silc_get_input("Old passphrase: ", TRUE);
338     if (!pass)
339       pass = strdup("");
340   }
341
342   if (!silc_pkcs_load_private_key(prv_filename,
343                                   (const unsigned char *)pass, strlen(pass),
344                                   &private_key)) {
345     memset(pass, 0, strlen(pass));
346     silc_free(pass);
347     fprintf(stderr, "Could not load private key `%s' file\n", prv_filename);
348     return FALSE;
349   }
350
351   memset(pass, 0, strlen(pass));
352   silc_free(pass);
353
354   pass = new_passphrase ? strdup(new_passphrase) : NULL;
355   if (!pass) {
356     char *pass2 = NULL;
357     fprintf(stdout, "\n");
358     pass = silc_get_input("New passphrase: ", TRUE);
359     if (!pass) {
360       pass = strdup("");
361     } else {
362       while (TRUE) {
363         printf("\n");
364         pass2 = silc_get_input("Retype new passphrase: ", TRUE);
365         if (!pass2)
366           pass2 = strdup("");
367         if (!strcmp(pass, pass2))
368           break;
369         fprintf(stderr, "\nPassphrases do not match");
370       }
371       silc_free(pass2);
372     }
373   }
374
375   rng = silc_rng_alloc();
376   silc_rng_init(rng);
377
378   silc_pkcs_save_private_key((char *)prv_filename, private_key,
379                              (unsigned char *)pass, strlen(pass),
380                              SILC_PKCS_FILE_BIN, rng);
381
382   fprintf(stdout, "\nPassphrase changed\n");
383
384   memset(pass, 0, strlen(pass));
385   silc_free(pass);
386
387   silc_pkcs_private_key_free(private_key);
388   silc_rng_free(rng);
389
390   return TRUE;
391 }
392
393 /* Checks that the 'identifier' string is valid identifier string
394    and does not contain any unassigned or prohibited character.  This
395    function is used to check for valid nicknames, channel names,
396    server names, usernames, hostnames, service names, algorithm names,
397    other security property names, and SILC Public Key name. */
398
399 unsigned char *silc_identifier_check(const unsigned char *identifier,
400                                      SilcUInt32 identifier_len,
401                                      SilcStringEncoding identifier_encoding,
402                                      SilcUInt32 max_allowed_length,
403                                      SilcUInt32 *out_len)
404 {
405   unsigned char *utf8s;
406   SilcUInt32 utf8s_len;
407   SilcStringprepStatus status;
408
409   if (!identifier || !identifier_len)
410     return NULL;
411
412   if (max_allowed_length && identifier_len > max_allowed_length)
413     return NULL;
414
415   status = silc_stringprep(identifier, identifier_len,
416                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
417                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
418   if (status != SILC_STRINGPREP_OK) {
419     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
420     return NULL;
421   }
422
423   if (out_len)
424     *out_len = utf8s_len;
425
426   return utf8s;
427 }
428
429 /* Same as above but does not allocate memory, just checks the
430    validity of the string. */
431
432 SilcBool silc_identifier_verify(const unsigned char *identifier,
433                                 SilcUInt32 identifier_len,
434                                 SilcStringEncoding identifier_encoding,
435                                 SilcUInt32 max_allowed_length)
436 {
437   SilcStringprepStatus status;
438
439   if (!identifier || !identifier_len)
440     return FALSE;
441
442   if (max_allowed_length && identifier_len > max_allowed_length)
443     return FALSE;
444
445   status = silc_stringprep(identifier, identifier_len,
446                            identifier_encoding, SILC_IDENTIFIER_PREP, 0,
447                            NULL, NULL, SILC_STRING_UTF8);
448   if (status != SILC_STRINGPREP_OK) {
449     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
450     return FALSE;
451   }
452
453   return TRUE;
454 }
455
456 unsigned char *silc_channel_name_check(const unsigned char *identifier,
457                                        SilcUInt32 identifier_len,
458                                        SilcStringEncoding identifier_encoding,
459                                        SilcUInt32 max_allowed_length,
460                                        SilcUInt32 *out_len)
461 {
462   unsigned char *utf8s;
463   SilcUInt32 utf8s_len;
464   SilcStringprepStatus status;
465
466   if (!identifier || !identifier_len)
467     return NULL;
468
469   if (max_allowed_length && identifier_len > max_allowed_length)
470     return NULL;
471
472   status = silc_stringprep(identifier, identifier_len,
473                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
474                            &utf8s, &utf8s_len, SILC_STRING_UTF8);
475   if (status != SILC_STRINGPREP_OK) {
476     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
477     return NULL;
478   }
479
480   if (out_len)
481     *out_len = utf8s_len;
482
483   return utf8s;
484 }
485
486 /* Same as above but does not allocate memory, just checks the
487    validity of the string. */
488
489 SilcBool silc_channel_name_verify(const unsigned char *identifier,
490                                   SilcUInt32 identifier_len,
491                                   SilcStringEncoding identifier_encoding,
492                                   SilcUInt32 max_allowed_length)
493 {
494   SilcStringprepStatus status;
495
496   if (!identifier || !identifier_len)
497     return FALSE;
498
499   if (max_allowed_length && identifier_len > max_allowed_length)
500     return FALSE;
501
502   status = silc_stringprep(identifier, identifier_len,
503                            identifier_encoding, SILC_IDENTIFIER_CH_PREP, 0,
504                            NULL, NULL, SILC_STRING_UTF8);
505   if (status != SILC_STRINGPREP_OK) {
506     SILC_LOG_DEBUG(("silc_stringprep() status error %d", status));
507     return FALSE;
508   }
509
510   return TRUE;
511 }