rsa.c: strerror -> g_strerror
[metze/wireshark/wip.git] / wsutil / rsa.c
1 /* rsa.c
2  *
3  * Functions for RSA private key reading and use
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 2007 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22  */
23
24 #include "config.h"
25
26 #include "rsa.h"
27 #include <glib.h>
28 #include "filesystem.h"
29 #include "file_util.h"
30 #include "log.h"
31 #include <errno.h>
32
33
34 #ifdef HAVE_LIBGNUTLS
35
36 #include <gnutls/abstract.h>
37 #include <gnutls/pkcs12.h>
38
39 /* RSA private key file processing {{{ */
40 #define RSA_PARS 6
41 gcry_sexp_t
42 rsa_privkey_to_sexp(gnutls_x509_privkey_t priv_key, char **err)
43 {
44     gnutls_datum_t rsa_datum[RSA_PARS]; /* m, e, d, p, q, u */
45     size_t         tmp_size;
46     gcry_error_t   gret;
47     gcry_sexp_t    rsa_priv_key = NULL;
48     gint           i;
49     gcry_mpi_t     rsa_params[RSA_PARS];
50     *err = NULL;
51
52     /* RSA get parameter */
53     if (gnutls_x509_privkey_export_rsa_raw(priv_key,
54                 &rsa_datum[0],
55                 &rsa_datum[1],
56                 &rsa_datum[2],
57                 &rsa_datum[3],
58                 &rsa_datum[4],
59                 &rsa_datum[5])  != 0) {
60         *err = g_strdup("can't export rsa param (is a rsa private key file ?!?)");
61         return NULL;
62     }
63
64     /* convert each rsa parameter to mpi format*/
65     for(i=0; i<RSA_PARS; i++) {
66         gret = gcry_mpi_scan(&rsa_params[i], GCRYMPI_FMT_USG, rsa_datum[i].data, rsa_datum[i].size,&tmp_size);
67         /* these buffers were allocated by gnutls_x509_privkey_export_rsa_raw() */
68         g_free(rsa_datum[i].data);
69         if (gret != 0) {
70             *err = g_strdup_printf("can't convert m rsa param to int (size %d)", rsa_datum[i].size);
71             return NULL;
72         }
73     }
74
75     /* libgcrypt expects p < q, and gnutls might not return it as such, depending on gnutls version and its crypto backend */
76     if (gcry_mpi_cmp(rsa_params[3], rsa_params[4]) > 0)
77     {
78         /* p, q = q, p */
79         gcry_mpi_swap(rsa_params[3], rsa_params[4]);
80         /* due to swapping p and q, u = p^-1 mod p which happens to be needed. */
81     }
82     /* libgcrypt expects u = p^-1 mod q (for OpenPGP), but the u parameter
83      * says u = q^-1 mod p. Recompute u = p^-1 mod q. Do this unconditionally as
84      * at least GnuTLS 2.12.23 computes an invalid value. */
85     gcry_mpi_invm(rsa_params[5], rsa_params[3], rsa_params[4]);
86
87     if  (gcry_sexp_build( &rsa_priv_key, NULL,
88                 "(private-key(rsa((n%m)(e%m)(d%m)(p%m)(q%m)(u%m))))", rsa_params[0],
89                 rsa_params[1], rsa_params[2], rsa_params[3], rsa_params[4],
90                 rsa_params[5]) != 0) {
91         *err = g_strdup("can't build rsa private key s-exp");
92         return NULL;
93     }
94
95     for (i=0; i< 6; i++)
96         gcry_mpi_release(rsa_params[i]);
97     return rsa_priv_key;
98 }
99
100 gnutls_x509_privkey_t
101 rsa_load_pem_key(FILE *fp, char **err)
102 {
103     /* gnutls makes our work much harder, since we have to work internally with
104      * s-exp formatted data, but PEM loader exports only in "gnutls_datum_t"
105      * format, and a datum -> s-exp convertion function does not exist.
106      */
107     gnutls_x509_privkey_t priv_key;
108     gnutls_datum_t        key;
109     ws_statb64            statbuf;
110     gint                  ret;
111     guint                 bytes;
112     *err = NULL;
113
114     if (ws_fstat64(ws_fileno(fp), &statbuf) == -1) {
115         *err = g_strdup_printf("can't ws_fstat64 file: %s", g_strerror(errno));
116         return NULL;
117     }
118     if (S_ISDIR(statbuf.st_mode)) {
119         *err = g_strdup("file is a directory");
120         errno = EISDIR;
121         return NULL;
122     }
123     if (S_ISFIFO(statbuf.st_mode)) {
124         *err = g_strdup("file is a named pipe");
125         errno = EINVAL;
126         return NULL;
127     }
128     if (!S_ISREG(statbuf.st_mode)) {
129         *err = g_strdup("file is not a regular file");
130         errno = EINVAL;
131         return NULL;
132     }
133     /* XXX - check for a too-big size */
134     /* load all file contents into a datum buffer*/
135     key.data = (unsigned char *)g_malloc((size_t)statbuf.st_size);
136     key.size = (int)statbuf.st_size;
137     bytes = (guint) fread(key.data, 1, key.size, fp);
138     if (bytes < key.size) {
139         if (bytes == 0 && ferror(fp)) {
140             *err = g_strdup_printf("can't read from file %d bytes, got error %s",
141                     key.size, g_strerror(errno));
142         } else {
143             *err = g_strdup_printf("can't read from file %d bytes, got %d",
144                     key.size, bytes);
145         }
146         g_free(key.data);
147         return NULL;
148     }
149
150     /* init private key data*/
151     gnutls_x509_privkey_init(&priv_key);
152
153     /* import PEM data*/
154     if ((ret = gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) {
155         *err = g_strdup_printf("can't import pem data: %s", gnutls_strerror(ret));
156         g_free(key.data);
157         gnutls_x509_privkey_deinit(priv_key);
158         return NULL;
159     }
160
161     if (gnutls_x509_privkey_get_pk_algorithm(priv_key) != GNUTLS_PK_RSA) {
162         *err = g_strdup("private key public key algorithm isn't RSA");
163         g_free(key.data);
164         gnutls_x509_privkey_deinit(priv_key);
165         return NULL;
166     }
167
168     g_free(key.data);
169
170     return priv_key;
171 }
172
173 static const char *
174 BAGTYPE(gnutls_pkcs12_bag_type_t x) {
175     switch (x) {
176         case GNUTLS_BAG_EMPTY:               return "Empty";
177         case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY: return "PKCS#8 Encrypted key";
178         case GNUTLS_BAG_PKCS8_KEY:           return "PKCS#8 Key";
179         case GNUTLS_BAG_CERTIFICATE:         return "Certificate";
180         case GNUTLS_BAG_CRL:                 return "CRL";
181         case GNUTLS_BAG_ENCRYPTED:           return "Encrypted";
182         case GNUTLS_BAG_UNKNOWN:             return "Unknown";
183         default:                             return "<undefined>";
184     }
185 }
186
187 gnutls_x509_privkey_t
188 rsa_load_pkcs12(FILE *fp, const gchar *cert_passwd, char **err)
189 {
190     int                       i, j, ret;
191     int                       rest;
192     unsigned char            *p;
193     gnutls_datum_t            data;
194     gnutls_pkcs12_bag_t       bag = NULL;
195     gnutls_pkcs12_bag_type_t  bag_type;
196     size_t                    len;
197
198     gnutls_pkcs12_t       rsa_p12  = NULL;
199     gnutls_x509_privkey_t rsa_pkey = NULL;
200
201     gnutls_x509_privkey_t     priv_key = NULL;
202     *err = NULL;
203
204     rest = 4096;
205     data.data = (unsigned char *)g_malloc(rest);
206     data.size = rest;
207     p = data.data;
208     while ((len = fread(p, 1, rest, fp)) > 0) {
209         p += len;
210         rest -= (int) len;
211         if (!rest) {
212             rest = 1024;
213             data.data = (unsigned char *)g_realloc(data.data, data.size + rest);
214             p = data.data + data.size;
215             data.size += rest;
216         }
217     }
218     data.size -= rest;
219     if (!feof(fp)) {
220         *err = g_strdup("Error during certificate reading.");
221         g_free(data.data);
222         return NULL;
223     }
224
225     ret = gnutls_pkcs12_init(&rsa_p12);
226     if (ret < 0) {
227         *err = g_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret));
228         g_free(data.data);
229         return NULL;
230     }
231
232     /* load PKCS#12 in DER or PEM format */
233     ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_DER, 0);
234     if (ret < 0) {
235         ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_PEM, 0);
236         if (ret < 0) {
237             *err = g_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret));
238         }
239     }
240     g_free(data.data);
241     if (ret < 0) {
242         return NULL;
243     }
244
245     g_log(NULL, G_LOG_LEVEL_INFO, "rsa_privkey_to_sexp: PKCS#12 imported\n");
246
247     /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */
248     for (i=0; ; i++) {
249
250         ret = gnutls_pkcs12_bag_init(&bag);
251         if (ret < 0) {
252             *err = g_strdup_printf("gnutls_pkcs12_bag_init failed: %s",
253                                    gnutls_strerror(ret));
254             break;
255         }
256
257         ret = gnutls_pkcs12_get_bag(rsa_p12, i, bag);
258         if (ret < 0) {
259             *err = g_strdup_printf("gnutls_pkcs12_get_bag failed: %s",
260                                    gnutls_strerror(ret));
261             break;
262         }
263
264         for (j=0; j<gnutls_pkcs12_bag_get_count(bag); j++) {
265
266             ret = gnutls_pkcs12_bag_get_type(bag, j);
267             if (ret < 0) {
268                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
269                                        gnutls_strerror(ret));
270                 goto done;
271             }
272             bag_type = (gnutls_pkcs12_bag_type_t)ret;
273             if (bag_type >= GNUTLS_BAG_UNKNOWN) {
274                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
275                                        ret);
276                 goto done;
277             }
278             g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d: %s\n", i, j, BAGTYPE(bag_type));
279             if (bag_type == GNUTLS_BAG_ENCRYPTED) {
280                 ret = gnutls_pkcs12_bag_decrypt(bag, cert_passwd);
281                 if (ret == 0) {
282                     ret = gnutls_pkcs12_bag_get_type(bag, j);
283                     if (ret < 0) {
284                         *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
285                                                gnutls_strerror(ret));
286                         goto done;
287                     }
288                     bag_type = (gnutls_pkcs12_bag_type_t)ret;
289                     if (bag_type >= GNUTLS_BAG_UNKNOWN) {
290                         *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
291                                                ret);
292                         goto done;
293                     }
294                     g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d decrypted: %s\n", i, j, BAGTYPE(bag_type));
295                 }
296             }
297
298             ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
299             if (ret < 0) {
300                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
301                                        gnutls_strerror(ret));
302                 goto done;
303             }
304
305             switch (bag_type) {
306
307                 case GNUTLS_BAG_PKCS8_KEY:
308                 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
309
310                     ret = gnutls_x509_privkey_init(&rsa_pkey);
311                     if (ret < 0) {
312                         *err = g_strdup_printf("gnutls_x509_privkey_init(&rsa_pkey) - %s", gnutls_strerror(ret));
313                         goto done;
314                     }
315                     ret = gnutls_x509_privkey_import_pkcs8(rsa_pkey, &data, GNUTLS_X509_FMT_DER, cert_passwd,
316                             (bag_type==GNUTLS_BAG_PKCS8_KEY) ? GNUTLS_PKCS_PLAIN : 0);
317                     if (ret < 0) {
318                         *err = g_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret));
319                         goto done;
320                     }
321
322                     if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey) != GNUTLS_PK_RSA) {
323                         *err = g_strdup("private key public key algorithm isn't RSA");
324                         goto done;
325                     }
326
327                     /* Private key found, return it. */
328                     priv_key = rsa_pkey;
329                     goto done;
330                     break;
331
332                 default: ;
333             }
334         }  /* j */
335         if (bag) { gnutls_pkcs12_bag_deinit(bag); bag = NULL; }
336     }  /* i */
337
338 done:
339     if (!priv_key && rsa_pkey)
340         gnutls_x509_privkey_deinit(rsa_pkey);
341     if (bag)
342         gnutls_pkcs12_bag_deinit(bag);
343
344     return priv_key;
345 }
346
347 void
348 rsa_private_key_free(gpointer key)
349 {
350     gcry_sexp_release((gcry_sexp_t) key);
351 }
352
353 #else /* ! defined(HAVE_LIBGNUTLS) */
354
355 void
356 rsa_private_key_free(gpointer key _U_)
357 {
358 }
359
360 #endif /* HAVE_LIBGNUTLS */
361
362 /*
363  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
364  *
365  * Local variables:
366  * c-basic-offset: 4
367  * tab-width: 8
368  * indent-tabs-mode: nil
369  * End:
370  *
371  * vi: set shiftwidth=4 tabstop=8 expandtab:
372  * :indentSize=4:tabSize=8:noTabs=true:
373  */