504d8a5ef99c58b5ef959ac24dc227d3209a74be
[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+
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     size_t                    len;
183
184     gnutls_pkcs12_t       rsa_p12  = NULL;
185
186     gnutls_x509_privkey_t     priv_key = NULL;
187     *err = NULL;
188
189     rest = 4096;
190     data.data = (unsigned char *)g_malloc(rest);
191     data.size = rest;
192     p = data.data;
193     while ((len = fread(p, 1, rest, fp)) > 0) {
194         p += len;
195         rest -= (int) len;
196         if (!rest) {
197             rest = 1024;
198             data.data = (unsigned char *)g_realloc(data.data, data.size + rest);
199             p = data.data + data.size;
200             data.size += rest;
201         }
202     }
203     data.size -= rest;
204     if (!feof(fp)) {
205         *err = g_strdup("Error during certificate reading.");
206         g_free(data.data);
207         return NULL;
208     }
209
210     ret = gnutls_pkcs12_init(&rsa_p12);
211     if (ret < 0) {
212         *err = g_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret));
213         g_free(data.data);
214         return NULL;
215     }
216
217     /* load PKCS#12 in DER or PEM format */
218     ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_DER, 0);
219     if (ret < 0) {
220         ret = gnutls_pkcs12_import(rsa_p12, &data, GNUTLS_X509_FMT_PEM, 0);
221         if (ret < 0) {
222             *err = g_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret));
223         }
224     }
225     g_free(data.data);
226     if (ret < 0) {
227         gnutls_pkcs12_deinit(rsa_p12);
228         return NULL;
229     }
230
231     g_log(NULL, G_LOG_LEVEL_INFO, "rsa_privkey_to_sexp: PKCS#12 imported\n");
232
233     /* TODO: Use gnutls_pkcs12_simple_parse, since 3.1.0 (August 2012) */
234     for (i=0; ; i++) {
235         gnutls_pkcs12_bag_t       bag;
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             gnutls_pkcs12_bag_deinit(bag);
250             goto done;
251         }
252
253         for (j=0; j<gnutls_pkcs12_bag_get_count(bag); j++) {
254
255             ret = gnutls_pkcs12_bag_get_type(bag, j);
256             if (ret < 0) {
257                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
258                                        gnutls_strerror(ret));
259                 gnutls_pkcs12_bag_deinit(bag);
260                 goto done;
261             }
262             bag_type = (gnutls_pkcs12_bag_type_t)ret;
263             if (bag_type >= GNUTLS_BAG_UNKNOWN) {
264                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
265                                        ret);
266                 gnutls_pkcs12_bag_deinit(bag);
267                 goto done;
268             }
269             g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d: %s\n", i, j, BAGTYPE(bag_type));
270             if (bag_type == GNUTLS_BAG_ENCRYPTED) {
271                 ret = gnutls_pkcs12_bag_decrypt(bag, cert_passwd);
272                 if (ret == 0) {
273                     ret = gnutls_pkcs12_bag_get_type(bag, j);
274                     if (ret < 0) {
275                         *err = g_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
276                                                gnutls_strerror(ret));
277                         gnutls_pkcs12_bag_deinit(bag);
278                         goto done;
279                     }
280                     bag_type = (gnutls_pkcs12_bag_type_t)ret;
281                     if (bag_type >= GNUTLS_BAG_UNKNOWN) {
282                         *err = g_strdup_printf("gnutls_pkcs12_bag_get_type returnd unknown bag type %u",
283                                                ret);
284                         gnutls_pkcs12_bag_deinit(bag);
285                         goto done;
286                     }
287                     g_log(NULL, G_LOG_LEVEL_INFO, "Bag %d/%d decrypted: %s\n", i, j, BAGTYPE(bag_type));
288                 }
289             }
290
291             ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
292             if (ret < 0) {
293                 *err = g_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
294                                        gnutls_strerror(ret));
295                 gnutls_pkcs12_bag_deinit(bag);
296                 goto done;
297             }
298
299             switch (bag_type) {
300
301                 case GNUTLS_BAG_PKCS8_KEY:
302                 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
303                 {
304                     gnutls_x509_privkey_t rsa_pkey;
305
306                     ret = gnutls_x509_privkey_init(&rsa_pkey);
307                     if (ret < 0) {
308                         *err = g_strdup_printf("gnutls_x509_privkey_init failed: %s", gnutls_strerror(ret));
309                         gnutls_pkcs12_bag_deinit(bag);
310                         goto done;
311                     }
312                     ret = gnutls_x509_privkey_import_pkcs8(rsa_pkey, &data, GNUTLS_X509_FMT_DER, cert_passwd,
313                             (bag_type==GNUTLS_BAG_PKCS8_KEY) ? GNUTLS_PKCS_PLAIN : 0);
314                     if (ret < 0) {
315                         *err = g_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret));
316                         gnutls_x509_privkey_deinit(rsa_pkey);
317                         gnutls_pkcs12_bag_deinit(bag);
318                         goto done;
319                     }
320
321                     if (gnutls_x509_privkey_get_pk_algorithm(rsa_pkey) != GNUTLS_PK_RSA) {
322                         *err = g_strdup("private key public key algorithm isn't RSA");
323                         gnutls_x509_privkey_deinit(rsa_pkey);
324                         gnutls_pkcs12_bag_deinit(bag);
325                         goto done;
326                     }
327
328                     /* Private key found, return it. */
329                     priv_key = rsa_pkey;
330                     goto done;
331                     break;
332                 }
333
334                 default: ;
335             }
336             gnutls_pkcs12_bag_deinit(bag);
337             bag = NULL;
338         }  /* j */
339     }  /* i */
340
341 done:
342     if (!priv_key) {
343         /*
344          * We failed.  If we didn't fail with an error, we failed because
345          * we found no PKCS8 key and fell out of the loop; report that
346          * error.
347          */
348         if (*err == NULL)
349             *err = g_strdup("no PKCS8 key found");
350     }
351     gnutls_pkcs12_deinit(rsa_p12);
352
353     return priv_key;
354 }
355
356 void
357 rsa_private_key_free(gpointer key)
358 {
359     gcry_sexp_release((gcry_sexp_t) key);
360 }
361
362 #else /* ! defined(HAVE_LIBGNUTLS) */
363
364 void
365 rsa_private_key_free(gpointer key _U_)
366 {
367 }
368
369 #endif /* HAVE_LIBGNUTLS */
370
371 /*
372  * Editor modelines  -  http://www.wireshark.org/tools/modelines.html
373  *
374  * Local variables:
375  * c-basic-offset: 4
376  * tab-width: 8
377  * indent-tabs-mode: nil
378  * End:
379  *
380  * vi: set shiftwidth=4 tabstop=8 expandtab:
381  * :indentSize=4:tabSize=8:noTabs=true:
382  */