identification: Add hash() method
authorTobias Brunner <tobias@strongswan.org>
Thu, 11 Jun 2015 15:38:46 +0000 (17:38 +0200)
committerTobias Brunner <tobias@strongswan.org>
Thu, 6 Aug 2015 15:22:32 +0000 (17:22 +0200)
Compared to hashing the encoding we can ignore string types of RDNs when
hashing DNs, making hash() compatible to equals() that does the same.

Fixes #991.

src/libstrongswan/tests/suites/test_identification.c
src/libstrongswan/utils/identification.c
src/libstrongswan/utils/identification.h

index de00e4a..ff14ba8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Tobias Brunner
+ * Copyright (C) 2013-2015 Tobias Brunner
  * Copyright (C) 2009 Martin Willi
  * Hochschule fuer Technik Rapperswil
  *
@@ -727,6 +727,88 @@ START_TEST(test_matches_empty_reverse)
 END_TEST
 
 /*******************************************************************************
+ * identification hashing
+ */
+
+static bool id_hash_equals(char *str, char *b_str)
+{
+       identification_t *a, *b;
+       bool success = FALSE;
+
+       a = identification_create_from_string(str);
+       b = identification_create_from_string(b_str ?: str);
+       success = a->hash(a, 0) == b->hash(b, 0);
+       a->destroy(a);
+       b->destroy(b);
+       return success;
+}
+
+START_TEST(test_hash)
+{
+       ck_assert(id_hash_equals("moon@strongswan.org", NULL));
+       ck_assert(id_hash_equals("vpn.strongswan.org", NULL));
+       ck_assert(id_hash_equals("192.168.1.1", NULL));
+       ck_assert(id_hash_equals("C=CH", NULL));
+
+       ck_assert(!id_hash_equals("moon@strongswan.org", "sun@strongswan.org"));
+       ck_assert(!id_hash_equals("vpn.strongswan.org", "*.strongswan.org"));
+       ck_assert(!id_hash_equals("192.168.1.1", "192.168.1.2"));
+       ck_assert(!id_hash_equals("C=CH", "C=DE"));
+       ck_assert(!id_hash_equals("fqdn:strongswan.org", "keyid:strongswan.org"));
+}
+END_TEST
+
+START_TEST(test_hash_any)
+{
+       ck_assert(id_hash_equals("%any", NULL));
+       ck_assert(id_hash_equals("%any", "0.0.0.0"));
+       ck_assert(id_hash_equals("%any", "*"));
+       ck_assert(id_hash_equals("%any", ""));
+
+       ck_assert(!id_hash_equals("%any", "any"));
+}
+END_TEST
+
+START_TEST(test_hash_dn)
+{
+       identification_t *a, *b;
+
+       /* same DN (C=CH, O=strongSwan), different RDN type (PRINTABLESTRING vs.
+        * UTF8STRING) */
+       a = identification_create_from_data(chunk_from_chars(
+                       0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+                       0x55, 0x04, 0x06, 0x13, 0x02, 0x43, 0x48, 0x31,
+                       0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a,
+                       0x13, 0x0a, 0x73, 0x74, 0x72, 0x6f, 0x6e, 0x67,
+                       0x53, 0x77, 0x61, 0x6e));
+       b = identification_create_from_data(chunk_from_chars(
+                       0x30, 0x22, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
+                       0x55, 0x04, 0x06, 0x0c, 0x02, 0x43, 0x48, 0x31,
+                       0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x0a,
+                       0x0c, 0x0a, 0x73, 0x74, 0x72, 0x6f, 0x6e, 0x67,
+                       0x53, 0x77, 0x61, 0x6e));
+       ck_assert_int_eq(a->hash(a, 0), b->hash(b, 0));
+       ck_assert(a->equals(a, b));
+       a->destroy(a);
+       b->destroy(b);
+}
+END_TEST
+
+START_TEST(test_hash_inc)
+{
+       identification_t *a;
+
+       a = identification_create_from_string("vpn.strongswan.org");
+       ck_assert(a->hash(a, 0) != a->hash(a, 1));
+       a->destroy(a);
+
+       a = identification_create_from_string("C=CH, O=strongSwan");
+       ck_assert(a->hash(a, 0) != a->hash(a, 1));
+       a->destroy(a);
+}
+END_TEST
+
+/*******************************************************************************
  * identification part enumeration
  */
 
@@ -851,6 +933,13 @@ Suite *identification_suite_create()
        tcase_add_loop_test(tc, test_matches_empty_reverse, ID_ANY, ID_KEY_ID + 1);
        suite_add_tcase(s, tc);
 
+       tc = tcase_create("hash");
+       tcase_add_test(tc, test_hash);
+       tcase_add_test(tc, test_hash_any);
+       tcase_add_test(tc, test_hash_dn);
+       tcase_add_test(tc, test_hash_inc);
+       suite_add_tcase(s, tc);
+
        tc = tcase_create("part enumeration");
        tcase_add_test(tc, test_parts);
        suite_add_tcase(s, tc);
index b69adf3..cd3f1ce 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009-2012 Tobias Brunner
+ * Copyright (C) 2009-2015 Tobias Brunner
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -579,6 +579,19 @@ METHOD(identification_t, contains_wildcards_memchr, bool,
        return memchr(this->encoded.ptr, '*', this->encoded.len) != NULL;
 }
 
+METHOD(identification_t, hash_binary, u_int,
+       private_identification_t *this, u_int inc)
+{
+       u_int hash;
+
+       hash = chunk_hash_inc(chunk_from_thing(this->type), inc);
+       if (this->type != ID_ANY)
+       {
+               hash = chunk_hash_inc(this->encoded, hash);
+       }
+       return hash;
+}
+
 METHOD(identification_t, equals_binary, bool,
        private_identification_t *this, identification_t *other)
 {
@@ -687,6 +700,24 @@ METHOD(identification_t, equals_dn, bool,
        return compare_dn(this->encoded, other->get_encoding(other), NULL);
 }
 
+METHOD(identification_t, hash_dn, u_int,
+       private_identification_t *this, u_int inc)
+{
+       enumerator_t *rdns;
+       chunk_t oid, data;
+       u_char type;
+       u_int hash;
+
+       hash = chunk_hash_inc(chunk_from_thing(this->type), inc);
+       rdns = create_rdn_enumerator(this->encoded);
+       while (rdns->enumerate(rdns, &oid, &type, &data))
+       {
+               hash = chunk_hash_inc(data, chunk_hash_inc(oid, hash));
+       }
+       rdns->destroy(rdns);
+       return hash;
+}
+
 METHOD(identification_t, equals_strcasecmp,  bool,
        private_identification_t *this, identification_t *other)
 {
@@ -903,6 +934,7 @@ static private_identification_t *identification_create(id_type_t type)
        switch (type)
        {
                case ID_ANY:
+                       this->public.hash = _hash_binary;
                        this->public.matches = _matches_any;
                        this->public.equals = _equals_binary;
                        this->public.contains_wildcards = return_true;
@@ -910,16 +942,19 @@ static private_identification_t *identification_create(id_type_t type)
                case ID_FQDN:
                case ID_RFC822_ADDR:
                case ID_USER_ID:
+                       this->public.hash = _hash_binary;
                        this->public.matches = _matches_string;
                        this->public.equals = _equals_strcasecmp;
                        this->public.contains_wildcards = _contains_wildcards_memchr;
                        break;
                case ID_DER_ASN1_DN:
+                       this->public.hash = _hash_dn;
                        this->public.equals = _equals_dn;
                        this->public.matches = _matches_dn;
                        this->public.contains_wildcards = _contains_wildcards_dn;
                        break;
                default:
+                       this->public.hash = _hash_binary;
                        this->public.equals = _equals_binary;
                        this->public.matches = _matches_binary;
                        this->public.contains_wildcards = return_false;
index e6a9fe1..a172f64 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Tobias Brunner
+ * Copyright (C) 2009-2015 Tobias Brunner
  * Copyright (C) 2005-2009 Martin Willi
  * Copyright (C) 2005 Jan Hutter
  * Hochschule fuer Technik Rapperswil
@@ -219,6 +219,14 @@ struct identification_t {
        id_type_t (*get_type) (identification_t *this);
 
        /**
+        * Create a hash value for this identification_t object.
+        *
+        * @param inc           optional value for incremental hashing
+        * @return                      hash value
+        */
+       u_int (*hash) (identification_t *this, u_int inc);
+
+       /**
         * Check if two identification_t objects are equal.
         *
         * @param other         other identification_t object