TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags
[wireshark-sm.git] / wsutil / rsa.c
blob22afe08f0b9efcd99dbd5f2723475aff5c4668d1
1 /* rsa.c
3 * Functions for RSA private key reading and use
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 2007 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "config.h"
13 #define WS_LOG_DOMAIN LOG_DOMAIN_WSUTIL
15 #include "rsa.h"
16 #include "filesystem.h"
17 #include "file_util.h"
18 #include <errno.h>
19 #include <wsutil/wslog.h>
22 #ifdef HAVE_LIBGNUTLS
24 #include <gnutls/abstract.h>
25 #include <gnutls/pkcs12.h>
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)
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 int i;
37 gcry_mpi_t rsa_params[RSA_PARS];
38 *err = NULL;
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;
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 = ws_strdup_printf("can't convert m rsa param to int (size %d)", rsa_datum[i].size);
59 return NULL;
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)
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. */
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]);
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;
83 for (i=0; i< 6; i++)
84 gcry_mpi_release(rsa_params[i]);
85 return rsa_priv_key;
88 gnutls_x509_privkey_t
89 rsa_load_pem_key(FILE *fp, char **err)
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 conversion function does not exist.
95 gnutls_x509_privkey_t priv_key;
96 gnutls_datum_t key;
97 ws_statb64 statbuf;
98 int ret;
99 unsigned bytes;
100 *err = NULL;
102 if (ws_fstat64(ws_fileno(fp), &statbuf) == -1) {
103 *err = ws_strdup_printf("can't ws_fstat64 file: %s", g_strerror(errno));
104 return NULL;
106 if (S_ISDIR(statbuf.st_mode)) {
107 *err = g_strdup("file is a directory");
108 errno = EISDIR;
109 return NULL;
111 if (S_ISFIFO(statbuf.st_mode)) {
112 *err = g_strdup("file is a named pipe");
113 errno = EINVAL;
114 return NULL;
116 if (!S_ISREG(statbuf.st_mode)) {
117 *err = g_strdup("file is not a regular file");
118 errno = EINVAL;
119 return NULL;
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 = (unsigned) fread(key.data, 1, key.size, fp);
126 if (bytes < key.size) {
127 if (bytes == 0 && ferror(fp)) {
128 *err = ws_strdup_printf("can't read from file %d bytes, got error %s",
129 key.size, g_strerror(errno));
130 } else {
131 *err = ws_strdup_printf("can't read from file %d bytes, got %d",
132 key.size, bytes);
134 g_free(key.data);
135 return NULL;
138 /* init private key data*/
139 gnutls_x509_privkey_init(&priv_key);
141 /* import PEM data*/
142 if ((ret = gnutls_x509_privkey_import(priv_key, &key, GNUTLS_X509_FMT_PEM)) != GNUTLS_E_SUCCESS) {
143 *err = ws_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;
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;
156 g_free(key.data);
158 return priv_key;
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>";
175 gnutls_x509_privkey_t
176 rsa_load_pkcs12(FILE *fp, const char *cert_passwd, char **err)
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;
185 gnutls_pkcs12_t rsa_p12 = NULL;
187 gnutls_x509_privkey_t priv_key = NULL;
188 *err = NULL;
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;
204 data.size -= rest;
205 if (!feof(fp)) {
206 *err = g_strdup("Error during certificate reading.");
207 g_free(data.data);
208 return NULL;
211 ret = gnutls_pkcs12_init(&rsa_p12);
212 if (ret < 0) {
213 *err = ws_strdup_printf("gnutls_pkcs12_init(&st_p12) - %s", gnutls_strerror(ret));
214 g_free(data.data);
215 return NULL;
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 = ws_strdup_printf("could not load PKCS#12 in DER or PEM format: %s", gnutls_strerror(ret));
226 g_free(data.data);
227 if (ret < 0) {
228 gnutls_pkcs12_deinit(rsa_p12);
229 return NULL;
232 ws_debug("grsa_privkey_to_sexp: PKCS#12 imported");
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;
238 ret = gnutls_pkcs12_bag_init(&bag);
239 if (ret < 0) {
240 *err = ws_strdup_printf("gnutls_pkcs12_bag_init failed: %s",
241 gnutls_strerror(ret));
242 goto done;
245 ret = gnutls_pkcs12_get_bag(rsa_p12, i, bag);
246 if (ret < 0) {
247 *err = ws_strdup_printf("gnutls_pkcs12_get_bag failed: %s",
248 gnutls_strerror(ret));
249 goto done;
252 for (j=0; j<gnutls_pkcs12_bag_get_count(bag); j++) {
254 ret = gnutls_pkcs12_bag_get_type(bag, j);
255 if (ret < 0) {
256 *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
257 gnutls_strerror(ret));
258 goto done;
260 bag_type = (gnutls_pkcs12_bag_type_t)ret;
261 if (bag_type >= GNUTLS_BAG_UNKNOWN) {
262 *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
263 ret);
264 goto done;
266 ws_debug("Bag %d/%d: %s", 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 = ws_strdup_printf("gnutls_pkcs12_bag_get_type failed: %s",
273 gnutls_strerror(ret));
274 goto done;
276 bag_type = (gnutls_pkcs12_bag_type_t)ret;
277 if (bag_type >= GNUTLS_BAG_UNKNOWN) {
278 *err = ws_strdup_printf("gnutls_pkcs12_bag_get_type returned unknown bag type %u",
279 ret);
280 goto done;
282 ws_debug("Bag %d/%d decrypted: %s", i, j, BAGTYPE(bag_type));
286 ret = gnutls_pkcs12_bag_get_data(bag, j, &data);
287 if (ret < 0) {
288 *err = ws_strdup_printf("gnutls_pkcs12_bag_get_data failed: %s",
289 gnutls_strerror(ret));
290 goto done;
293 switch (bag_type) {
295 case GNUTLS_BAG_PKCS8_KEY:
296 case GNUTLS_BAG_PKCS8_ENCRYPTED_KEY:
298 gnutls_x509_privkey_t rsa_pkey;
300 ret = gnutls_x509_privkey_init(&rsa_pkey);
301 if (ret < 0) {
302 *err = ws_strdup_printf("gnutls_x509_privkey_init failed: %s", gnutls_strerror(ret));
303 goto done;
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 = ws_strdup_printf("Can not decrypt private key - %s", gnutls_strerror(ret));
309 gnutls_x509_privkey_deinit(rsa_pkey);
310 goto done;
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;
319 /* Private key found, return it. */
320 priv_key = rsa_pkey;
321 goto done;
324 default: ;
326 } /* j */
328 gnutls_pkcs12_bag_deinit(bag);
329 bag = NULL;
330 } /* i */
332 done:
333 if (bag) {
334 gnutls_pkcs12_bag_deinit(bag);
336 if (!priv_key) {
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.
342 if (*err == NULL)
343 *err = g_strdup("no PKCS8 key found");
345 gnutls_pkcs12_deinit(rsa_p12);
347 return priv_key;
350 void
351 rsa_private_key_free(void * key)
353 gcry_sexp_release((gcry_sexp_t) key);
356 #else /* ! defined(HAVE_LIBGNUTLS) */
358 void
359 rsa_private_key_free(void * key _U_)
363 #endif /* HAVE_LIBGNUTLS */
366 * Editor modelines - https://www.wireshark.org/tools/modelines.html
368 * Local variables:
369 * c-basic-offset: 4
370 * tab-width: 8
371 * indent-tabs-mode: nil
372 * End:
374 * vi: set shiftwidth=4 tabstop=8 expandtab:
375 * :indentSize=4:tabSize=8:noTabs=true: