[gnome-keyring] gcr: Verify PKCS#12 MAC



commit 75053b66d6943205bbe2ed280f8691625aab87c2
Author: Stef Walter <stefw collabora co uk>
Date:   Mon Sep 12 12:20:19 2011 +0200

    gcr: Verify PKCS#12 MAC
    
     * This also has the nice effect of sanifying the prompting for unlock
       passwords for PKCS#12 file, since it's done by the parser before
       looking inside the various parts of the file.

 egg/egg-symkey.c |  140 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 egg/egg-symkey.h |   22 +++++++-
 gcr/gcr-parser.c |   96 +++++++++++++++++++++++++++++++++++--
 3 files changed, 250 insertions(+), 8 deletions(-)
---
diff --git a/egg/egg-symkey.c b/egg/egg-symkey.c
index 45b0fe6..90c67a3 100644
--- a/egg/egg-symkey.c
+++ b/egg/egg-symkey.c
@@ -51,6 +51,8 @@ static GQuark OID_PKCS12_PBE_2DES_SHA1;
 static GQuark OID_PKCS12_PBE_RC2_128_SHA1;
 static GQuark OID_PKCS12_PBE_RC2_40_SHA1;
 
+static GQuark OID_SHA1;
+
 static void
 init_quarks (void)
 {
@@ -83,7 +85,9 @@ init_quarks (void)
 		QUARK (OID_PKCS12_PBE_2DES_SHA1, "1.2.840.113549.1.12.1.4");
 		QUARK (OID_PKCS12_PBE_RC2_128_SHA1, "1.2.840.113549.1.12.1.5");
 		QUARK (OID_PKCS12_PBE_RC2_40_SHA1, "1.2.840.113549.1.12.1.6");
-		
+
+		QUARK (OID_SHA1, "1.3.14.3.2.26");
+
 		#undef QUARK
 		
 		g_once_init_leave (&quarks_inited, 1);
@@ -478,6 +482,43 @@ egg_symkey_generate_pkcs12 (int cipher_algo, int hash_algo, const gchar *passwor
 	return ret;
 }
 
+gboolean
+egg_symkey_generate_pkcs12_mac (int hash_algo,
+                                const gchar *password,
+                                gssize n_password,
+                                const guchar *salt,
+                                gsize n_salt,
+                                int iterations,
+                                guchar **key)
+{
+	gsize n_key;
+	gboolean ret = TRUE;
+
+	g_return_val_if_fail (hash_algo, FALSE);
+	g_return_val_if_fail (iterations > 0, FALSE);
+
+	n_key = gcry_md_get_algo_dlen (hash_algo);
+
+	if (password && !g_utf8_validate (password, n_password, NULL)) {
+		g_warning ("invalid non-UTF8 password");
+		g_return_val_if_reached (FALSE);
+	}
+
+	/* Generate us an key */
+	if (key) {
+		*key = egg_secure_alloc (n_key);
+		g_return_val_if_fail (*key != NULL, FALSE);
+		ret = generate_pkcs12 (hash_algo, 3, password, n_password, salt, n_salt,
+		                       iterations, *key, n_key);
+	}
+
+	/* Cleanup in case of failure */
+	if (!key)
+		egg_secure_free (key ? *key : NULL);
+
+	return ret;
+}
+
 static gboolean
 generate_pbkdf2 (int hash_algo, const gchar *password, gsize n_password,
 		 const guchar *salt, gsize n_salt, guint iterations,
@@ -966,6 +1007,75 @@ done:
 	return ret;
 }
 
+static gboolean
+read_mac_pkcs12_pbe (int hash_algo,
+                     const gchar *password,
+                     gsize n_password,
+                     const guchar *data,
+                     gsize n_data,
+                     gcry_md_hd_t *mdh,
+                     gsize *digest_len)
+{
+	GNode *asn = NULL;
+	gcry_error_t gcry;
+	gboolean ret;
+	gsize n_key;
+	const guchar *salt;
+	gsize n_salt;
+	gulong iterations;
+	guchar *key = NULL;
+
+	g_return_val_if_fail (hash_algo != 0, FALSE);
+	g_return_val_if_fail (mdh != NULL, FALSE);
+	g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+	*mdh = NULL;
+	ret = FALSE;
+
+	/* Check if we can use this algorithm */
+	if (gcry_md_algo_info (hash_algo, GCRYCTL_TEST_ALGO, NULL, 0) != 0)
+		goto done;
+
+	asn = egg_asn1x_create_and_decode (pkix_asn1_tab, "pkcs-12-MacData", data, n_data);
+	if (!asn)
+		goto done;
+
+	salt = egg_asn1x_get_raw_value (egg_asn1x_node (asn, "macSalt", NULL), &n_salt);
+	if (!salt)
+		goto done;
+	if (!egg_asn1x_get_integer_as_ulong (egg_asn1x_node (asn, "iterations", NULL), &iterations))
+		goto done;
+
+	n_key = gcry_md_get_algo_dlen (hash_algo);
+
+	/* Generate IV and key using salt read above */
+	if (!egg_symkey_generate_pkcs12_mac (hash_algo, password, n_password,
+	                                     salt, n_salt, iterations, &key))
+		goto done;
+
+	gcry = gcry_md_open (mdh, hash_algo, GCRY_MD_FLAG_HMAC);
+	if (gcry != 0) {
+		g_warning ("couldn't create mac digest: %s", gcry_strerror (gcry));
+		goto done;
+	}
+
+	if (digest_len)
+		*digest_len = n_key;
+	gcry_md_setkey (*mdh, key, n_key);
+
+	ret = TRUE;
+
+done:
+	if (ret != TRUE && *mdh) {
+		gcry_md_close (*mdh);
+		*mdh = NULL;
+	}
+
+	egg_secure_free (key);
+	egg_asn1x_destroy (asn);
+	return ret;
+}
+
 gboolean
 egg_symkey_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_password,
                         const guchar *data, gsize n_data, gcry_cipher_hd_t *cih)
@@ -1030,3 +1140,31 @@ egg_symkey_read_cipher (GQuark oid_scheme, const gchar *password, gsize n_passwo
 	
     	return ret;
 }
+
+gboolean
+egg_symkey_read_mac (GQuark oid_scheme,
+                     const gchar *password,
+                     gsize n_password,
+                     const guchar *data,
+                     gsize n_data,
+                     gcry_md_hd_t *mdh,
+                     gsize *digest_len)
+{
+	gboolean ret = FALSE;
+
+	g_return_val_if_fail (oid_scheme != 0, FALSE);
+	g_return_val_if_fail (mdh != NULL, FALSE);
+	g_return_val_if_fail (data != NULL && n_data != 0, FALSE);
+
+	init_quarks ();
+
+	/* PKCS#12 MAC with SHA-1 */
+	if (oid_scheme == OID_SHA1)
+		ret = read_mac_pkcs12_pbe (GCRY_MD_SHA1, password, n_password,
+		                           data, n_data, mdh, digest_len);
+
+	if (ret == FALSE)
+		g_message ("unsupported or invalid mac: %s", g_quark_to_string (oid_scheme));
+
+	return ret;
+}
diff --git a/egg/egg-symkey.h b/egg/egg-symkey.h
index 4231708..eceb802 100644
--- a/egg/egg-symkey.h
+++ b/egg/egg-symkey.h
@@ -47,15 +47,23 @@ gboolean                 egg_symkey_generate_pbe                (int cipher_algo
                                                                  guchar **iv);
 
 gboolean                 egg_symkey_generate_pkcs12             (int cipher_algo, 
-                                                                 int hash_algo, 
+                                                                 int hash_algo,
                                                                  const gchar *password,
                                                                  gssize n_password,
-                                                                 const guchar *salt, 
+                                                                 const guchar *salt,
                                                                  gsize n_salt,
-                                                                 int iterations, 
+                                                                 int iterations,
                                                                  guchar **key, 
                                                                  guchar **iv);
 
+gboolean                 egg_symkey_generate_pkcs12_mac         (int hash_algo,
+                                                                 const gchar *password,
+                                                                 gssize n_password,
+                                                                 const guchar *salt,
+                                                                 gsize n_salt,
+                                                                 int iterations,
+                                                                 guchar **key);
+
 gboolean                 egg_symkey_generate_pbkdf2             (int cipher_algo, 
                                                                  int hash_algo, 
                                                                  const gchar *password,
@@ -73,4 +81,12 @@ gboolean                 egg_symkey_read_cipher                 (GQuark oid_sche
                                                                  gsize n_data, 
                                                                  gcry_cipher_hd_t *cih);
 
+gboolean                 egg_symkey_read_mac                    (GQuark oid_scheme,
+                                                                 const gchar *password,
+                                                                 gsize n_password,
+                                                                 const guchar *data,
+                                                                 gsize n_data,
+                                                                 gcry_md_hd_t *mdh,
+                                                                 gsize *digest_len);
+
 #endif /* EGG_SYMKEY_H_ */
diff --git a/gcr/gcr-parser.c b/gcr/gcr-parser.c
index 9d80268..1f93c1f 100644
--- a/gcr/gcr-parser.c
+++ b/gcr/gcr-parser.c
@@ -1136,6 +1136,94 @@ done:
 }
 
 static gint
+verify_pkcs12_safe (GcrParser *self,
+                    GNode *asn,
+                    gconstpointer content,
+                    gsize n_content)
+{
+	PasswordState pstate = PASSWORD_STATE_INIT;
+	const gchar *password;
+	gcry_md_hd_t mdh = NULL;
+	const guchar *mac_digest;
+	gsize mac_len;
+	guchar *digest = NULL;
+	gsize n_digest;
+	GQuark algorithm;
+	GNode *mac_data;
+	gconstpointer params;
+	gsize n_params;
+	int ret, r;
+
+	ret = GCR_ERROR_FAILURE;
+
+	/*
+	 * The MAC is optional (and outside the encryption no less). I wonder
+	 * what the designers (ha) of PKCS#12 were trying to achieve
+	 */
+
+	mac_data = egg_asn1x_node (asn, "macData", NULL);
+	if (mac_data == NULL)
+		return SUCCESS;
+
+	algorithm = egg_asn1x_get_oid_as_quark (egg_asn1x_node (mac_data, "mac",
+	                                        "digestAlgorithm", "algorithm", NULL));
+	if (!algorithm)
+		goto done;
+
+	params = egg_asn1x_get_raw_element (mac_data, &n_params);
+	if (!params)
+		goto done;
+
+	digest = egg_asn1x_get_string_as_raw (egg_asn1x_node (mac_data, "mac", "digest", NULL), NULL, &n_digest);
+	if (!digest)
+		goto done;
+
+	/* Loop to try different passwords */
+	for (;;) {
+		g_assert (mdh == NULL);
+
+		r = enum_next_password (self, &pstate, &password);
+		if (r != SUCCESS) {
+			ret = r;
+			goto done;
+		}
+
+		/* Parse the encryption stuff into a cipher. */
+		if (!egg_symkey_read_mac (algorithm, password, -1, params, n_params,
+		                          &mdh, &mac_len)) {
+			ret = GCR_ERROR_FAILURE;
+			goto done;
+		}
+
+		/* If not the right length, then that's really broken */
+		if (mac_len != n_digest) {
+			r = GCR_ERROR_FAILURE;
+
+		} else {
+			gcry_md_write (mdh, content, n_content);
+			mac_digest = gcry_md_read (mdh, 0);
+			g_return_val_if_fail (mac_digest, GCR_ERROR_FAILURE);
+			r = memcmp (mac_digest, digest, n_digest) == 0 ? SUCCESS : GCR_ERROR_LOCKED;
+		}
+
+		gcry_md_close (mdh);
+		mdh = NULL;
+
+		if (r != GCR_ERROR_LOCKED) {
+			ret = r;
+			break;
+		}
+	}
+
+done:
+	if (mdh)
+		gcry_md_close (mdh);
+	g_free (digest);
+	return ret;
+
+}
+
+static gint
 parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
 {
 	GNode *asn = NULL;
@@ -1152,7 +1240,7 @@ parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
 	if (!asn)
 		goto done;
 
-	ret = GCR_ERROR_FAILURE;
+	parsing_begin (self, 0, data, n_data);
 
 	oid = egg_asn1x_get_oid_as_quark (egg_asn1x_node (asn, "authSafe", "contentType", NULL));
 	if (!oid)
@@ -1176,9 +1264,9 @@ parse_der_pkcs12 (GcrParser *self, const guchar *data, gsize n_data)
 	if (!content)
 		goto done;
 
-	parsing_begin (self, 0, data, n_data);
-
-	ret = handle_pkcs12_safe (self, content, n_content);
+	ret = verify_pkcs12_safe (self, asn, content, n_content);
+	if (ret == SUCCESS)
+		ret = handle_pkcs12_safe (self, content, n_content);
 
 	parsing_end (self);
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]