crypto_gnutls.c 12.6 KB
Newer Older
1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 3 4 5
/* NetworkManager Wireless Applet -- Display wireless access points and allow user control
 *
 * Dan Williams <dcbw@redhat.com>
 *
6 7 8 9
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
15
 *
16 17 18 19
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301 USA.
20
 *
21
 * Copyright 2007 - 2015 Red Hat, Inc.
22 23
 */

24
#include "nm-default.h"
25

26
#include <gnutls/gnutls.h>
27
#include <gnutls/crypto.h>
28
#include <gnutls/x509.h>
29
#include <gnutls/pkcs12.h>
30 31 32

#include "crypto.h"

33 34
#define SALT_LEN 8

35
static gboolean initialized = FALSE;
36

37 38 39
gboolean
crypto_init (GError **error)
{
40 41 42 43 44 45 46 47 48 49
	if (initialized)
		return TRUE;

	if (gnutls_global_init() != 0) {
		gnutls_global_deinit();
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_INIT_FAILED,
		             "%s",
		             _("Failed to initialize the crypto engine."));
		return FALSE;
50
	}
51 52

	initialized = TRUE;
53 54 55 56 57 58 59 60 61 62 63 64
	return TRUE;
}

gboolean
crypto_md5_hash (const char *salt,
                 const gsize salt_len,
                 const char *password,
                 gsize password_len,
                 char *buffer,
                 gsize buflen,
                 GError **error)
{
65 66
	gnutls_hash_hd_t ctx;
	int err;
67 68 69 70 71 72
	int nkey = buflen;
	const gsize digest_len = 16;
	int count = 0;
	char digest[MD5_HASH_LEN];
	char *p = buffer;

73
	if (salt)
74
		g_return_val_if_fail (salt_len >= SALT_LEN, FALSE);
75

76 77 78 79 80
	g_return_val_if_fail (password != NULL, FALSE);
	g_return_val_if_fail (password_len > 0, FALSE);
	g_return_val_if_fail (buffer != NULL, FALSE);
	g_return_val_if_fail (buflen > 0, FALSE);

81
	if (gnutls_hash_get_len (GNUTLS_DIG_MD5) > MD5_HASH_LEN) {
82 83
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_MD5_INIT_FAILED,
84 85
		             _("Hash length too long (%d > %d)."),
		             gnutls_hash_get_len (GNUTLS_DIG_MD5), MD5_HASH_LEN);
86 87 88 89 90 91
		return FALSE;
	}

	while (nkey > 0) {
		int i = 0;

92 93 94 95
		err = gnutls_hash_init (&ctx, GNUTLS_DIG_MD5);
		if (err < 0)
			goto error;

96
		if (count++)
97 98
			gnutls_hash (ctx, digest, digest_len);
		gnutls_hash (ctx, password, password_len);
99
		if (salt)
100 101
			gnutls_hash (ctx, salt, SALT_LEN); /* Only use 8 bytes of salt */
		gnutls_hash_deinit (ctx, digest);
102

103 104 105 106 107 108 109 110
		while (nkey && (i < digest_len)) {
			*(p++) = digest[i++];
			nkey--;
		}
	}

	memset (digest, 0, sizeof (digest));
	return TRUE;
111 112 113 114 115 116 117
error:
	memset (digest, 0, sizeof (digest));
	g_set_error (error, NM_CRYPTO_ERROR,
	             NM_CRYPTO_ERR_MD5_INIT_FAILED,
	             _("Failed to initialize the MD5 engine: %s (%s)"),
	             gnutls_strerror_name (err), gnutls_strerror (err));
	return FALSE;
118 119 120 121 122
}

char *
crypto_decrypt (const char *cipher,
                int key_type,
123
                GByteArray *data,
124 125 126 127 128 129 130
                const char *iv,
                const gsize iv_len,
                const char *key,
                const gsize key_len,
                gsize *out_len,
                GError **error)
{
131 132 133
	gnutls_cipher_hd_t ctx;
	gnutls_datum_t key_dt, iv_dt;
	int err;
134
	int cipher_mech, i;
135 136
	char *output = NULL;
	gboolean success = FALSE;
137
	gsize pad_len, real_iv_len;
138

139
	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC)) {
140
		cipher_mech = GNUTLS_CIPHER_3DES_CBC;
141 142
		real_iv_len = SALT_LEN;
	} else if (!strcmp (cipher, CIPHER_DES_CBC)) {
143
		cipher_mech = GNUTLS_CIPHER_DES_CBC;
144
		real_iv_len = SALT_LEN;
145
	} else if (!strcmp (cipher, CIPHER_AES_CBC)) {
146
		cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
147
		real_iv_len = 16;
148
	} else {
149 150 151 152 153 154 155
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
		             _("Private key cipher '%s' was unknown."),
		             cipher);
		return NULL;
	}

156 157 158 159 160 161 162 163 164
	if (iv_len < real_iv_len) {
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_RAW_IV_INVALID,
		             _("Invalid IV length (must be at least %zd)."),
		             real_iv_len);
		return NULL;
	}

	output = g_malloc0 (data->len);
165

166 167 168 169
	key_dt.data = (unsigned char *) key;
	key_dt.size = key_len;
	iv_dt.data = (unsigned char *) iv;
	iv_dt.size = iv_len;
170

171 172
	err = gnutls_cipher_init (&ctx, cipher_mech, &key_dt, &iv_dt);
	if (err < 0) {
173
		g_set_error (error, NM_CRYPTO_ERROR,
174 175 176
		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
		             _("Failed to initialize the decryption cipher context: %s (%s)"),
		             gnutls_strerror_name (err), gnutls_strerror (err));
177 178 179
		goto out;
	}

180 181
	err = gnutls_cipher_decrypt2 (ctx, data->data, data->len, output, data->len);
	if (err < 0) {
182 183
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
184 185
		             _("Failed to decrypt the private key: %s (%s)"),
		             gnutls_strerror_name (err), gnutls_strerror (err));
186 187
		goto out;
	}
188 189
	pad_len = output[data->len - 1];

190 191 192 193 194 195 196 197
	/* Check if the padding at the end of the decrypted data is valid */
	if (pad_len == 0 || pad_len > real_iv_len) {
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
		             _("Failed to decrypt the private key: unexpected padding length."));
		goto out;
	}

198 199 200 201 202 203 204 205 206 207 208
	/* Validate tail padding; last byte is the padding size, and all pad bytes
	 * should contain the padding size.
	 */
	for (i = 1; i <= pad_len; ++i) {
		if (output[data->len - i] != pad_len) {
			g_set_error (error, NM_CRYPTO_ERROR,
			             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
			             _("Failed to decrypt the private key."));
			goto out;
		}
	}
209

210
	*out_len = data->len - pad_len;
211 212 213 214 215 216
	success = TRUE;

out:
	if (!success) {
		if (output) {
			/* Don't expose key material */
217
			memset (output, 0, data->len);
218 219 220 221
			g_free (output);
			output = NULL;
		}
	}
222
	gnutls_cipher_deinit (ctx);
223 224 225
	return output;
}

226 227 228 229 230 231 232 233 234 235
char *
crypto_encrypt (const char *cipher,
                const GByteArray *data,
                const char *iv,
                const gsize iv_len,
                const char *key,
                gsize key_len,
                gsize *out_len,
                GError **error)
{
236 237 238
	gnutls_cipher_hd_t ctx;
	gnutls_datum_t key_dt, iv_dt;
	int err;
239 240 241 242 243 244 245
	int cipher_mech;
	char *output = NULL;
	gboolean success = FALSE;
	gsize padded_buf_len, pad_len, output_len;
	char *padded_buf = NULL;
	guint32 i;

246
	if (!strcmp (cipher, CIPHER_DES_EDE3_CBC))
247
		cipher_mech = GNUTLS_CIPHER_3DES_CBC;
248
	else if (!strcmp (cipher, CIPHER_AES_CBC))
249
		cipher_mech = GNUTLS_CIPHER_AES_128_CBC;
250
	else {
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_UNKNOWN_CIPHER,
		             _("Private key cipher '%s' was unknown."),
		             cipher);
		return NULL;
	}

	/* If data->len % ivlen == 0, then we add another complete block
	 * onto the end so that the decrypter knows there's padding.
	 */
	pad_len = iv_len - (data->len % iv_len);
	output_len = padded_buf_len = data->len + pad_len;
	padded_buf = g_malloc0 (padded_buf_len);

	memcpy (padded_buf, data->data, data->len);
	for (i = 0; i < pad_len; i++)
		padded_buf[data->len + i] = (guint8) (pad_len & 0xFF);

	output = g_malloc0 (output_len);

271 272 273 274
	key_dt.data = (unsigned char *) key;
	key_dt.size = key_len;
	iv_dt.data = (unsigned char *) iv;
	iv_dt.size = iv_len;
275

276 277
	err = gnutls_cipher_init (&ctx, cipher_mech, &key_dt, &iv_dt);
	if (err < 0) {
278
		g_set_error (error, NM_CRYPTO_ERROR,
279 280 281
		             NM_CRYPTO_ERR_CIPHER_INIT_FAILED,
		             _("Failed to initialize the encryption cipher context: %s (%s)"),
		             gnutls_strerror_name (err), gnutls_strerror (err));
282 283 284
		goto out;
	}

285 286
	err = gnutls_cipher_encrypt2 (ctx, padded_buf, padded_buf_len, output, output_len);
	if (err < 0) {
287 288
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
289 290
		             _("Failed to encrypt the data: %s (%s)"),
		             gnutls_strerror_name (err), gnutls_strerror (err));
291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		goto out;
	}

	*out_len = output_len;
	success = TRUE;

out:
	if (padded_buf) {
		memset (padded_buf, 0, padded_buf_len);
		g_free (padded_buf);
		padded_buf = NULL;
	}

	if (!success) {
		if (output) {
			/* Don't expose key material */
			memset (output, 0, output_len);
			g_free (output);
			output = NULL;
		}
	}
312
	gnutls_cipher_deinit (ctx);
313 314 315
	return output;
}

316
NMCryptoFileFormat
317 318 319 320
crypto_verify_cert (const unsigned char *data,
                    gsize len,
                    GError **error)
{
321
	gnutls_x509_crt_t der;
322
	gnutls_datum_t dt;
323 324
	int err;

325
	err = gnutls_x509_crt_init (&der);
326 327 328 329 330
	if (err < 0) {
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
		             _("Error initializing certificate data: %s"),
		             gnutls_strerror (err));
331
		return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
332 333
	}

334
	/* Try DER first */
335 336
	dt.data = (unsigned char *) data;
	dt.size = len;
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_DER);
	if (err == GNUTLS_E_SUCCESS) {
		gnutls_x509_crt_deinit (der);
		return NM_CRYPTO_FILE_FORMAT_X509;
	}

	/* And PEM next */
	err = gnutls_x509_crt_import (der, &dt, GNUTLS_X509_FMT_PEM);
	gnutls_x509_crt_deinit (der);
	if (err == GNUTLS_E_SUCCESS)
		return NM_CRYPTO_FILE_FORMAT_X509;

	g_set_error (error, NM_CRYPTO_ERROR,
	             NM_CRYPTO_ERR_CERT_FORMAT_INVALID,
	             _("Couldn't decode certificate: %s"),
	             gnutls_strerror (err));
	return NM_CRYPTO_FILE_FORMAT_UNKNOWN;
}

gboolean
crypto_verify_pkcs12 (const GByteArray *data,
                      const char *password,
                      GError **error)
{
	gnutls_pkcs12_t p12;
362
	gnutls_datum_t dt;
363 364
	gboolean success = FALSE;
	int err;
365

366 367 368 369 370 371
	g_return_val_if_fail (data != NULL, FALSE);

	dt.data = (unsigned char *) data->data;
	dt.size = data->len;

	err = gnutls_pkcs12_init (&p12);
372 373
	if (err < 0) {
		g_set_error (error, NM_CRYPTO_ERROR,
374 375
		             NM_CRYPTO_ERR_DECODE_FAILED,
		             _("Couldn't initialize PKCS#12 decoder: %s"),
376 377 378 379
		             gnutls_strerror (err));
		return FALSE;
	}

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
	/* DER first */
	err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_DER, 0);
	if (err < 0) {
		/* PEM next */
		err = gnutls_pkcs12_import (p12, &dt, GNUTLS_X509_FMT_PEM, 0);
		if (err < 0) {
			g_set_error (error, NM_CRYPTO_ERROR,
			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
			             _("Couldn't decode PKCS#12 file: %s"),
			             gnutls_strerror (err));
			goto out;
		}
	}

	err = gnutls_pkcs12_verify_mac (p12, password);
	if (err == GNUTLS_E_SUCCESS)
		success = TRUE;
	else {
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED,
		             _("Couldn't verify PKCS#12 file: %s"),
		             gnutls_strerror (err));
	}

out:
	gnutls_pkcs12_deinit (p12);
	return success;
407 408
}

409 410 411 412 413 414 415
gboolean
crypto_verify_pkcs8 (const GByteArray *data,
                     gboolean is_encrypted,
                     const char *password,
                     GError **error)
{
	gnutls_x509_privkey_t p8;
416
	gnutls_datum_t dt;
417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
	int err;

	g_return_val_if_fail (data != NULL, FALSE);

	dt.data = (unsigned char *) data->data;
	dt.size = data->len;

	err = gnutls_x509_privkey_init (&p8);
	if (err < 0) {
		g_set_error (error, NM_CRYPTO_ERROR,
		             NM_CRYPTO_ERR_DECODE_FAILED,
		             _("Couldn't initialize PKCS#8 decoder: %s"),
		             gnutls_strerror (err));
		return FALSE;
	}

	err = gnutls_x509_privkey_import_pkcs8 (p8,
	                                        &dt,
	                                        GNUTLS_X509_FMT_DER,
	                                        is_encrypted ? password : NULL,
	                                        is_encrypted ? 0 : GNUTLS_PKCS_PLAIN);
	gnutls_x509_privkey_deinit (p8);

	if (err < 0) {
		if (err == GNUTLS_E_UNKNOWN_CIPHER_TYPE) {
			/* HACK: gnutls doesn't support all the cipher types that openssl
			 * can use with PKCS#8, so if we encounter one, we have to assume
			 * the given password works.  gnutls needs to unsuckify, apparently.
			 * Specifically, by default openssl uses pbeWithMD5AndDES-CBC
			 * which gnutls does not support.
			 */
		} else {
			g_set_error (error, NM_CRYPTO_ERROR,
450 451 452
			             NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
			             _("Couldn't decode PKCS#8 file: %s"),
			             gnutls_strerror (err));
453 454 455 456 457 458 459
			return FALSE;
		}
	}

	return TRUE;
}

460 461 462
gboolean
crypto_randomize (void *buffer, gsize buffer_len, GError **error)
{
463
	gnutls_rnd (GNUTLS_RND_RANDOM, buffer, buffer_len);
464 465
	return TRUE;
}