device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm-util / crypto.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
3 /*
4  * Dan Williams <dcbw@redhat.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * 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  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301 USA.
20  *
21  * Copyright 2007 - 2011 Red Hat, Inc.
22  */
23
24 #include "nm-default.h"
25
26 #include <string.h>
27 #include <strings.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30
31 #include "crypto.h"
32
33 GQuark
34 _nm_crypto_error_quark (void)
35 {
36         static GQuark quark;
37
38         if (G_UNLIKELY (!quark))
39                 quark = g_quark_from_static_string ("nm-crypto-error-quark");
40         return quark;
41 }
42
43
44 #define PEM_RSA_KEY_BEGIN "-----BEGIN RSA PRIVATE KEY-----"
45 #define PEM_RSA_KEY_END   "-----END RSA PRIVATE KEY-----"
46
47 #define PEM_DSA_KEY_BEGIN "-----BEGIN DSA PRIVATE KEY-----"
48 #define PEM_DSA_KEY_END   "-----END DSA PRIVATE KEY-----"
49
50 #define PEM_CERT_BEGIN    "-----BEGIN CERTIFICATE-----"
51 #define PEM_CERT_END      "-----END CERTIFICATE-----"
52
53 #define PEM_PKCS8_ENC_KEY_BEGIN "-----BEGIN ENCRYPTED PRIVATE KEY-----"
54 #define PEM_PKCS8_ENC_KEY_END   "-----END ENCRYPTED PRIVATE KEY-----"
55
56 #define PEM_PKCS8_DEC_KEY_BEGIN "-----BEGIN PRIVATE KEY-----"
57 #define PEM_PKCS8_DEC_KEY_END   "-----END PRIVATE KEY-----"
58
59 static gboolean
60 find_tag (const char *tag,
61           const GByteArray *array,
62           gsize start_at,
63           gsize *out_pos)
64 {
65         gsize i, taglen;
66         gsize len = array->len - start_at;
67
68         g_return_val_if_fail (out_pos != NULL, FALSE);
69
70         taglen = strlen (tag);
71         if (len >= taglen) {
72                 for (i = 0; i < len - taglen + 1; i++) {
73                         if (memcmp (array->data + start_at + i, tag, taglen) == 0) {
74                                 *out_pos = start_at + i;
75                                 return TRUE;
76                         }
77                 }
78         }
79         return FALSE;
80 }
81
82 #define DEK_INFO_TAG "DEK-Info: "
83 #define PROC_TYPE_TAG "Proc-Type: "
84
85 static GByteArray *
86 parse_old_openssl_key_file (const GByteArray *contents,
87                             int key_type,
88                             char **out_cipher,
89                             char **out_iv,
90                             GError **error)
91 {
92         GByteArray *bindata = NULL;
93         char **lines = NULL;
94         char **ln = NULL;
95         gsize start = 0, end = 0;
96         GString *str = NULL;
97         int enc_tags = 0;
98         char *iv = NULL;
99         char *cipher = NULL;
100         unsigned char *tmp = NULL;
101         gsize tmp_len = 0;
102         const char *start_tag;
103         const char *end_tag;
104         guint8 save_end = 0;
105
106         switch (key_type) {
107         case NM_CRYPTO_KEY_TYPE_RSA:
108                 start_tag = PEM_RSA_KEY_BEGIN;
109                 end_tag = PEM_RSA_KEY_END;
110                 break;
111         case NM_CRYPTO_KEY_TYPE_DSA:
112                 start_tag = PEM_DSA_KEY_BEGIN;
113                 end_tag = PEM_DSA_KEY_END;
114                 break;
115         default:
116                 g_set_error (error, NM_CRYPTO_ERROR,
117                              NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
118                              "Unknown key type %d",
119                              key_type);
120                 g_assert_not_reached ();
121                 return NULL;
122         }
123
124         if (!find_tag (start_tag, contents, 0, &start))
125                 goto parse_error;
126
127         start += strlen (start_tag);
128         if (!find_tag (end_tag, contents, start, &end)) {
129                 g_set_error (error, NM_CRYPTO_ERROR,
130                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
131                              _("PEM key file had no end tag '%s'."),
132                              end_tag);
133                 goto parse_error;
134         }
135
136         save_end = contents->data[end];
137         contents->data[end] = '\0';
138         lines = g_strsplit ((const char *) (contents->data + start), "\n", 0);
139         contents->data[end] = save_end;
140
141         if (!lines || g_strv_length (lines) <= 1) {
142                 g_set_error (error, NM_CRYPTO_ERROR,
143                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
144                              _("Doesn't look like a PEM private key file."));
145                 goto parse_error;
146         }
147
148         str = g_string_new_len (NULL, end - start);
149         for (ln = lines; *ln; ln++) {
150                 char *p = *ln;
151
152                 /* Chug leading spaces */
153                 p = g_strstrip (p);
154                 if (!*p)
155                         continue;
156
157                 if (!strncmp (p, PROC_TYPE_TAG, strlen (PROC_TYPE_TAG))) {
158                         if (enc_tags++ != 0) {
159                                 g_set_error (error, NM_CRYPTO_ERROR,
160                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
161                                              _("Malformed PEM file: Proc-Type was not first tag."));
162                                 goto parse_error;
163                         }
164
165                         p += strlen (PROC_TYPE_TAG);
166                         if (strcmp (p, "4,ENCRYPTED")) {
167                                 g_set_error (error, NM_CRYPTO_ERROR,
168                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
169                                              _("Malformed PEM file: unknown Proc-Type tag '%s'."),
170                                              p);
171                                 goto parse_error;
172                         }
173                 } else if (!strncmp (p, DEK_INFO_TAG, strlen (DEK_INFO_TAG))) {
174                         char *comma;
175
176                         if (enc_tags++ != 1) {
177                                 g_set_error (error, NM_CRYPTO_ERROR,
178                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
179                                              _("Malformed PEM file: DEK-Info was not the second tag."));
180                                 goto parse_error;
181                         }
182
183                         p += strlen (DEK_INFO_TAG);
184
185                         /* Grab the IV first */
186                         comma = strchr (p, ',');
187                         if (!comma || (*(comma + 1) == '\0')) {
188                                 g_set_error (error, NM_CRYPTO_ERROR,
189                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
190                                              _("Malformed PEM file: no IV found in DEK-Info tag."));
191                                 goto parse_error;
192                         }
193                         *comma++ = '\0';
194                         if (!g_ascii_isxdigit (*comma)) {
195                                 g_set_error (error, NM_CRYPTO_ERROR,
196                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
197                                              _("Malformed PEM file: invalid format of IV in DEK-Info tag."));
198                                 goto parse_error;
199                         }
200                         iv = g_strdup (comma);
201
202                         /* Get the private key cipher */
203                         if (!strcasecmp (p, "DES-EDE3-CBC")) {
204                                 cipher = g_strdup (p);
205                         } else if (!strcasecmp (p, "DES-CBC")) {
206                                 cipher = g_strdup (p);
207                         } else if (!strcasecmp (p, "AES-128-CBC")) {
208                                 cipher = g_strdup (p);
209                         } else {
210                                 g_set_error (error, NM_CRYPTO_ERROR,
211                                              NM_CRYPTO_ERR_UNKNOWN_KEY_TYPE,
212                                              _("Malformed PEM file: unknown private key cipher '%s'."),
213                                              p);
214                                 goto parse_error;
215                         }
216                 } else {
217                         if ((enc_tags != 0) && (enc_tags != 2)) {
218                                 g_set_error (error, NM_CRYPTO_ERROR,
219                                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
220                                              "Malformed PEM file: both Proc-Type and DEK-Info tags are required.");
221                                 goto parse_error;
222                         }
223                         g_string_append (str, p);
224                 }
225         }
226
227         tmp = g_base64_decode (str->str, &tmp_len);
228         if (tmp == NULL || !tmp_len) {
229                 g_set_error (error, NM_CRYPTO_ERROR,
230                              NM_CRYPTO_ERR_DECODE_FAILED,
231                              _("Could not decode private key."));
232                 goto parse_error;
233         }
234         g_string_free (str, TRUE);
235
236         if (lines)
237                 g_strfreev (lines);
238
239         bindata = g_byte_array_sized_new (tmp_len);
240         g_byte_array_append (bindata, tmp, tmp_len);
241         g_free (tmp);
242
243         *out_iv = iv;
244         *out_cipher = cipher;
245         return bindata;
246
247 parse_error:
248         g_free (tmp);
249         g_free (cipher);
250         g_free (iv);
251         if (str)
252                 g_string_free (str, TRUE);
253         if (lines)
254                 g_strfreev (lines);
255         return NULL;
256 }
257
258 static GByteArray *
259 parse_pkcs8_key_file (const GByteArray *contents,
260                       gboolean *out_encrypted,
261                       GError **error)
262 {
263         GByteArray *key = NULL;
264         gsize start = 0, end = 0;
265         unsigned char *der = NULL;
266         guint8 save_end;
267         gsize length = 0;
268         const char *start_tag = NULL, *end_tag = NULL;
269         gboolean encrypted = FALSE;
270
271         /* Try encrypted first, decrypted next */
272         if (find_tag (PEM_PKCS8_ENC_KEY_BEGIN, contents, 0, &start)) {
273                 start_tag = PEM_PKCS8_ENC_KEY_BEGIN;
274                 end_tag = PEM_PKCS8_ENC_KEY_END;
275                 encrypted = TRUE;
276         } else if (find_tag (PEM_PKCS8_DEC_KEY_BEGIN, contents, 0, &start)) {
277                 start_tag = PEM_PKCS8_DEC_KEY_BEGIN;
278                 end_tag = PEM_PKCS8_DEC_KEY_END;
279                 encrypted = FALSE;
280         } else {
281                 g_set_error_literal (error, NM_CRYPTO_ERROR,
282                                      NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
283                                      _("Failed to find expected PKCS#8 start tag."));
284                 return NULL;
285         }
286
287         start += strlen (start_tag);
288         if (!find_tag (end_tag, contents, start, &end)) {
289                 g_set_error (error, NM_CRYPTO_ERROR,
290                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
291                              _("Failed to find expected PKCS#8 end tag '%s'."),
292                              end_tag);
293                 return NULL;
294         }
295
296         /* g_base64_decode() wants a NULL-terminated string */
297         save_end = contents->data[end];
298         contents->data[end] = '\0';
299         der = g_base64_decode ((const char *) (contents->data + start), &length);
300         contents->data[end] = save_end;
301
302         if (der && length) {
303                 key = g_byte_array_sized_new (length);
304                 g_byte_array_append (key, der, length);
305                 g_assert (key->len == length);
306                 *out_encrypted = encrypted;
307         } else {
308                 g_set_error_literal (error, NM_CRYPTO_ERROR,
309                                      NM_CRYPTO_ERR_DECODE_FAILED,
310                                      _("Failed to decode PKCS#8 private key."));
311         }
312
313         g_free (der);
314         return key;
315 }
316
317 static GByteArray *
318 file_to_g_byte_array (const char *filename, GError **error)
319 {
320         char *contents;
321         GByteArray *array = NULL;
322         gsize length = 0;
323
324         if (g_file_get_contents (filename, &contents, &length, error)) {
325                 array = g_byte_array_sized_new (length);
326                 g_byte_array_append (array, (guint8 *) contents, length);
327                 g_assert (array->len == length);
328                 g_free (contents);
329         }
330         return array;
331 }
332
333 /*
334  * Convert a hex string into bytes.
335  */
336 static char *
337 convert_iv (const char *src,
338             gsize *out_len,
339             GError **error)
340 {
341         int num;
342         int i;
343         char conv[3];
344         char *c;
345
346         g_return_val_if_fail (src != NULL, NULL);
347
348         num = strlen (src);
349         if (num % 2) {
350                 g_set_error (error, NM_CRYPTO_ERROR,
351                              NM_CRYPTO_ERR_RAW_IV_INVALID,
352                              _("IV must be an even number of bytes in length."));
353                 return NULL;
354         }
355
356         num /= 2;
357         c = g_malloc0 (num + 1);
358
359         conv[2] = '\0';
360         for (i = 0; i < num; i++) {
361                 conv[0] = src[(i * 2)];
362                 conv[1] = src[(i * 2) + 1];
363                 if (!g_ascii_isxdigit (conv[0]) || !g_ascii_isxdigit (conv[1])) {
364                         g_set_error (error, NM_CRYPTO_ERROR,
365                                      NM_CRYPTO_ERR_RAW_IV_INVALID,
366                                      _("IV contains non-hexadecimal digits."));
367                         goto error;
368                 }
369
370                 c[i] = strtol(conv, NULL, 16);
371         }
372         *out_len = num;
373         return c;
374
375 error:
376         g_free (c);
377         return NULL;
378 }
379
380 static char *
381 make_des_aes_key (const char *cipher,
382                   const char *salt,
383                   const gsize salt_len,
384                   const char *password,
385                   gsize *out_len,
386                   GError **error)
387 {
388         char *key;
389         guint32 digest_len;
390
391         g_return_val_if_fail (cipher != NULL, NULL);
392         g_return_val_if_fail (salt != NULL, NULL);
393         g_return_val_if_fail (salt_len >= 8, NULL);
394         g_return_val_if_fail (password != NULL, NULL);
395         g_return_val_if_fail (out_len != NULL, NULL);
396
397         if (!strcmp (cipher, "DES-EDE3-CBC"))
398                 digest_len = 24;
399         else if (!strcmp (cipher, "DES-CBC"))
400                 digest_len = 8;
401         else if (!strcmp (cipher, "AES-128-CBC"))
402                 digest_len = 16;
403         else {
404                 g_set_error (error, NM_CRYPTO_ERROR,
405                              NM_CRYPTO_ERR_UNKNOWN_CIPHER,
406                              _("Private key cipher '%s' was unknown."),
407                              cipher);
408                 return NULL;
409         }
410
411         if (password[0] == '\0')
412                 return NULL;
413
414         key = g_malloc0 (digest_len + 1);
415
416         if (!crypto_md5_hash (salt,
417                               salt_len,
418                               password,
419                               strlen (password),
420                               key,
421                               digest_len,
422                               error))
423                 goto error;
424
425         *out_len = digest_len;
426         return key;
427
428 error:
429         if (key) {
430                 /* Don't leak stale key material */
431                 memset (key, 0, digest_len);
432                 g_free (key);
433         }
434         return NULL;
435 }
436
437 static GByteArray *
438 decrypt_key (const char *cipher,
439              int key_type,
440              GByteArray *data,
441              const char *iv,
442              const char *password,
443              GError **error)
444 {
445         char *bin_iv = NULL;
446         gsize bin_iv_len = 0;
447         char *key = NULL;
448         gsize key_len = 0;
449         char *output = NULL;
450         gsize decrypted_len = 0;
451         GByteArray *decrypted = NULL;
452
453         g_return_val_if_fail (password != NULL, NULL);
454
455         bin_iv = convert_iv (iv, &bin_iv_len, error);
456         if (!bin_iv)
457                 return NULL;
458
459         /* Convert the password and IV into a DES or AES key */
460         key = make_des_aes_key (cipher, bin_iv, bin_iv_len, password, &key_len, error);
461         if (!key || !key_len)
462                 goto out;
463
464         output = crypto_decrypt (cipher, key_type,
465                                  data,
466                                  bin_iv, bin_iv_len,
467                                  key, key_len,
468                                  &decrypted_len,
469                                  error);
470         if (output && decrypted_len) {
471                 decrypted = g_byte_array_sized_new (decrypted_len);
472                 g_byte_array_append (decrypted, (guint8 *) output, decrypted_len);
473         }
474
475 out:
476         /* Don't leak stale key material */
477         if (key)
478                 memset (key, 0, key_len);
479         g_free (output);
480         g_free (key);
481         g_free (bin_iv);
482
483         return decrypted;
484 }
485
486 GByteArray *
487 crypto_decrypt_private_key_data (const GByteArray *contents,
488                                  const char *password,
489                                  NMCryptoKeyType *out_key_type,
490                                  GError **error)
491 {
492         GByteArray *decrypted = NULL;
493         NMCryptoKeyType key_type = NM_CRYPTO_KEY_TYPE_RSA;
494         GByteArray *data;
495         char *iv = NULL;
496         char *cipher = NULL;
497
498         g_return_val_if_fail (contents != NULL, NULL);
499         if (out_key_type)
500                 g_return_val_if_fail (*out_key_type == NM_CRYPTO_KEY_TYPE_UNKNOWN, NULL);
501
502         /* OpenSSL non-standard legacy PEM files */
503
504         /* Try RSA keys first */
505         data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
506         if (!data) {
507                 g_clear_error (error);
508
509                 /* DSA next */
510                 key_type = NM_CRYPTO_KEY_TYPE_DSA;
511                 data = parse_old_openssl_key_file (contents, key_type, &cipher, &iv, error);
512                 if (!data) {
513                         g_clear_error (error);
514                         g_set_error (error, NM_CRYPTO_ERROR,
515                                      NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
516                                      _("Unable to determine private key type."));
517                 }
518         }
519
520         if (data) {
521                 /* return the key type even if decryption failed */
522                 if (out_key_type)
523                         *out_key_type = key_type;
524
525                 if (password) {
526                         decrypted = decrypt_key (cipher,
527                                                  key_type,
528                                                  data,
529                                                  iv,
530                                                  password,
531                                                  error);
532                 }
533                 g_byte_array_free (data, TRUE);
534         }
535
536         g_free (cipher);
537         g_free (iv);
538
539         return decrypted;
540 }
541
542 GByteArray *
543 crypto_decrypt_private_key (const char *file,
544                             const char *password,
545                             NMCryptoKeyType *out_key_type,
546                             GError **error)
547 {
548         GByteArray *contents;
549         GByteArray *key = NULL;
550
551         contents = file_to_g_byte_array (file, error);
552         if (contents) {
553                 key = crypto_decrypt_private_key_data (contents, password, out_key_type, error);
554                 g_byte_array_free (contents, TRUE);
555         }
556         return key;
557 }
558
559 static GByteArray *
560 extract_pem_cert_data (GByteArray *contents, GError **error)
561 {
562         GByteArray *cert = NULL;
563         gsize start = 0, end = 0;
564         unsigned char *der = NULL;
565         guint8 save_end;
566         gsize length = 0;
567
568         if (!find_tag (PEM_CERT_BEGIN, contents, 0, &start)) {
569                 g_set_error (error, NM_CRYPTO_ERROR,
570                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
571                              _("PEM certificate had no start tag '%s'."),
572                              PEM_CERT_BEGIN);
573                 goto done;
574         }
575
576         start += strlen (PEM_CERT_BEGIN);
577         if (!find_tag (PEM_CERT_END, contents, start, &end)) {
578                 g_set_error (error, NM_CRYPTO_ERROR,
579                              NM_CRYPTO_ERR_FILE_FORMAT_INVALID,
580                              _("PEM certificate had no end tag '%s'."),
581                              PEM_CERT_END);
582                 goto done;
583         }
584
585         /* g_base64_decode() wants a NULL-terminated string */
586         save_end = contents->data[end];
587         contents->data[end] = '\0';
588         der = g_base64_decode ((const char *) (contents->data + start), &length);
589         contents->data[end] = save_end;
590
591         if (der && length) {
592                 cert = g_byte_array_sized_new (length);
593                 g_byte_array_append (cert, der, length);
594                 g_assert (cert->len == length);
595         } else {
596                 g_set_error (error, NM_CRYPTO_ERROR,
597                              NM_CRYPTO_ERR_DECODE_FAILED,
598                              _("Failed to decode certificate."));
599         }
600
601 done:
602         g_free (der);
603         return cert;
604 }
605
606 GByteArray *
607 crypto_load_and_verify_certificate (const char *file,
608                                     NMCryptoFileFormat *out_file_format,
609                                     GError **error)
610 {
611         GByteArray *array, *contents;
612
613         g_return_val_if_fail (file != NULL, NULL);
614         g_return_val_if_fail (out_file_format != NULL, NULL);
615         g_return_val_if_fail (*out_file_format == NM_CRYPTO_FILE_FORMAT_UNKNOWN, NULL);
616
617         contents = file_to_g_byte_array (file, error);
618         if (!contents)
619                 return NULL;
620
621         /* Check for PKCS#12 */
622         if (crypto_is_pkcs12_data (contents)) {
623                 *out_file_format = NM_CRYPTO_FILE_FORMAT_PKCS12;
624                 return contents;
625         }
626
627         /* Check for plain DER format */
628         if (contents->len > 2 && contents->data[0] == 0x30 && contents->data[1] == 0x82) {
629                 *out_file_format = crypto_verify_cert (contents->data, contents->len, error);
630         } else {
631                 array = extract_pem_cert_data (contents, error);
632                 if (!array) {
633                         g_byte_array_free (contents, TRUE);
634                         return NULL;
635                 }
636
637                 *out_file_format = crypto_verify_cert (array->data, array->len, error);
638                 g_byte_array_free (array, TRUE);
639         }
640
641         if (*out_file_format != NM_CRYPTO_FILE_FORMAT_X509) {
642                 g_byte_array_free (contents, TRUE);
643                 contents = NULL;
644         }
645
646         return contents;
647 }
648
649 gboolean
650 crypto_is_pkcs12_data (const GByteArray *data)
651 {
652         GError *error = NULL;
653         gboolean success;
654
655         g_return_val_if_fail (data != NULL, FALSE);
656
657         success = crypto_verify_pkcs12 (data, NULL, &error);
658         if (success == FALSE) {
659                 /* If the error was just a decryption error, then it's pkcs#12 */
660                 if (error) {
661                         if (g_error_matches (error, NM_CRYPTO_ERROR, NM_CRYPTO_ERR_CIPHER_DECRYPT_FAILED))
662                                 success = TRUE;
663                         g_error_free (error);
664                 }
665         }
666         return success;
667 }
668
669 gboolean
670 crypto_is_pkcs12_file (const char *file, GError **error)
671 {
672         GByteArray *contents;
673         gboolean success = FALSE;
674
675         g_return_val_if_fail (file != NULL, FALSE);
676
677         contents = file_to_g_byte_array (file, error);
678         if (contents) {
679                 success = crypto_is_pkcs12_data (contents);
680                 g_byte_array_free (contents, TRUE);
681         }
682         return success;
683 }
684
685 /* Verifies that a private key can be read, and if a password is given, that
686  * the private key can be decrypted with that password.
687  */
688 NMCryptoFileFormat
689 crypto_verify_private_key_data (const GByteArray *contents,
690                                 const char *password,
691                                 GError **error)
692 {
693         GByteArray *tmp;
694         NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
695         NMCryptoKeyType ktype = NM_CRYPTO_KEY_TYPE_UNKNOWN;
696         gboolean is_encrypted = FALSE;
697
698         g_return_val_if_fail (contents != NULL, FALSE);
699
700         /* Check for PKCS#12 first */
701         if (crypto_is_pkcs12_data (contents)) {
702                 if (!password || crypto_verify_pkcs12 (contents, password, error))
703                         format = NM_CRYPTO_FILE_FORMAT_PKCS12;
704         } else {
705                 /* Maybe it's PKCS#8 */
706                 tmp = parse_pkcs8_key_file (contents, &is_encrypted, error);
707                 if (tmp) {
708                         if (crypto_verify_pkcs8 (tmp, is_encrypted, password, error))
709                                 format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
710                 } else {
711                         g_clear_error (error);
712
713                         /* Or it's old-style OpenSSL */
714                         tmp = crypto_decrypt_private_key_data (contents, password, &ktype, error);
715                         if (tmp)
716                                 format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
717                         else if (!password && (ktype != NM_CRYPTO_KEY_TYPE_UNKNOWN))
718                                 format = NM_CRYPTO_FILE_FORMAT_RAW_KEY;
719                 }
720
721                 if (tmp) {
722                         /* Don't leave decrypted key data around */
723                         memset (tmp->data, 0, tmp->len);
724                         g_byte_array_free (tmp, TRUE);
725                 }
726         }
727
728         return format;
729 }
730
731 NMCryptoFileFormat
732 crypto_verify_private_key (const char *filename,
733                            const char *password,
734                            GError **error)
735 {
736         GByteArray *contents;
737         NMCryptoFileFormat format = NM_CRYPTO_FILE_FORMAT_UNKNOWN;
738
739         g_return_val_if_fail (filename != NULL, FALSE);
740
741         contents = file_to_g_byte_array (filename, error);
742         if (contents) {
743                 format = crypto_verify_private_key_data (contents, password, error);
744                 g_byte_array_free (contents, TRUE);
745         }
746         return format;
747 }