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