[gnome-keyring] gcr: Verify PKCS#12 MAC
- From: Stefan Walter <stefw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-keyring] gcr: Verify PKCS#12 MAC
- Date: Mon, 12 Sep 2011 10:43:45 +0000 (UTC)
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]