Commit 8aca7f9231 for strongswan.org

commit 8aca7f92319af5e5e1da3b7c3126f94eb1761f19
Author: Tobias Brunner <tobias@strongswan.org>
Date:   Mon Jul 13 18:00:26 2026 +0200

    chunk: Add utility that compares the prefix of two chunks

    Unlike chunk_compare() this first compares the prefix of two nonces,
    then falls back to comparing the length.  This is basically intended to
    compare nonces as specified in RFC 7296:

       "Lowest" means an octet-by-octet comparison (instead of, for instance,
       comparing the nonces as large integers).  In other words, start by
       comparing the first octet; if they're equal, move to the next octet,
       and so on.  If you reach the end of one nonce, that nonce is the
       lower one.

diff --git a/src/libstrongswan/tests/suites/test_chunk.c b/src/libstrongswan/tests/suites/test_chunk.c
index d3e1bd7f65..016e8f3400 100644
--- a/src/libstrongswan/tests/suites/test_chunk.c
+++ b/src/libstrongswan/tests/suites/test_chunk.c
@@ -93,20 +93,24 @@ END_TEST

 static struct {
 	int result;
+	int result_prefix;
 	chunk_t a;
 	chunk_t b;
 } compare_data[] = {
-	{ 0, { NULL, 0 }, { NULL, 0 }},
-	{ 0, chunk_from_chars(0x00), chunk_from_chars(0x00)},
-	{-1, chunk_from_chars(0x00), chunk_from_chars(0x01)},
-	{ 1, chunk_from_chars(0x01), chunk_from_chars(0x00)},
-	{ 0, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x00)},
-	{-1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x01)},
-	{ 1, chunk_from_chars(0x00, 0x01), chunk_from_chars(0x00, 0x00)},
-	{-1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x01, 0x00)},
-	{ 1, chunk_from_chars(0x01, 0x00), chunk_from_chars(0x00, 0x00)},
-	{-1, chunk_from_chars(0xff), chunk_from_chars(0x00, 0x00)},
-	{ 1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0xff)},
+	{ 0,  0, { NULL, 0 }, { NULL, 0 }},
+	{ 0,  0, chunk_from_chars(0x00), chunk_from_chars(0x00)},
+	{-1, -1, chunk_from_chars(0x00), chunk_from_chars(0x01)},
+	{ 1,  1, chunk_from_chars(0x01), chunk_from_chars(0x00)},
+	{ 0,  0, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x00)},
+	{-1, -1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x00, 0x01)},
+	{ 1,  1, chunk_from_chars(0x00, 0x01), chunk_from_chars(0x00, 0x00)},
+	{-1, -1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0x01, 0x00)},
+	{ 1,  1, chunk_from_chars(0x01, 0x00), chunk_from_chars(0x00, 0x00)},
+	{-1,  1, chunk_from_chars(0xff), chunk_from_chars(0x00, 0x00)},
+	{ 1, -1, chunk_from_chars(0x00, 0x00), chunk_from_chars(0xff)},
+	{-1, -1, chunk_from_chars(0x01), chunk_from_chars(0xff, 0x00)},
+	{-1, -1, chunk_from_chars(0x01), chunk_from_chars(0x01, 0x00)},
+	{ 1,  1, chunk_from_chars(0x01, 0x00), chunk_from_chars(0x01)},
 };

 START_TEST(test_compare)
@@ -121,6 +125,18 @@ START_TEST(test_compare)
 }
 END_TEST

+START_TEST(test_compare_prefix)
+{
+	int result, expected;
+
+	result = chunk_compare_prefix(compare_data[_i].a, compare_data[_i].b);
+	expected = compare_data[_i].result_prefix;
+	ck_assert((result == 0 && expected == 0) ||
+			  (result < 0 && expected < 0) ||
+			  (result > 0 && expected > 0));
+}
+END_TEST
+
 /*******************************************************************************
  * clear
  */
@@ -1200,6 +1216,7 @@ Suite *chunk_suite_create()

 	tc = tcase_create("chunk_compare");
 	tcase_add_loop_test(tc, test_compare, 0, countof(compare_data));
+	tcase_add_loop_test(tc, test_compare_prefix, 0, countof(compare_data));
 	suite_add_tcase(s, tc);

 	tc = tcase_create("clear");
diff --git a/src/libstrongswan/utils/chunk.c b/src/libstrongswan/utils/chunk.c
index c8153f4f0c..12d710082f 100644
--- a/src/libstrongswan/utils/chunk.c
+++ b/src/libstrongswan/utils/chunk.c
@@ -816,15 +816,34 @@ chunk_t chunk_to_dec(chunk_t chunk, char *buf)
 int chunk_compare(chunk_t a, chunk_t b)
 {
 	int compare_len = a.len - b.len;
-	int len = (compare_len < 0)? a.len : b.len;
+	int len = (compare_len < 0) ? a.len : b.len;

 	if (compare_len != 0 || len == 0)
 	{
 		return compare_len;
 	}
 	return memcmp(a.ptr, b.ptr, len);
-};
+}

+/**
+ * Described in header.
+ */
+int chunk_compare_prefix(chunk_t a, chunk_t b)
+{
+	int cmp, compare_len = a.len - b.len;
+	int len = (compare_len < 0) ? a.len : b.len;
+
+	if (!len)
+	{
+		return compare_len;
+	}
+	cmp = memcmp(a.ptr, b.ptr, len);
+	if (!cmp)
+	{
+		return compare_len;
+	}
+	return cmp;
+}

 /**
  * Described in header.
diff --git a/src/libstrongswan/utils/chunk.h b/src/libstrongswan/utils/chunk.h
index 62f7155d28..e0417cf367 100644
--- a/src/libstrongswan/utils/chunk.h
+++ b/src/libstrongswan/utils/chunk.h
@@ -331,11 +331,26 @@ static inline chunk_t chunk_skip_zero(chunk_t chunk)
 chunk_t chunk_copy_pad(chunk_t dst, chunk_t src, u_char chr);

 /**
- *  Compare two chunks, returns zero if a equals b
- *  or negative/positive if a is small/greater than b
+ * Compare two chunks, returns zero if they are equal or negative/positive if
+ * a is smaller/greater than b.
+ *
+ * Note that if the chunks are not of equal length, their prefix is not
+ * compared, only their lengths.
  */
 int chunk_compare(chunk_t a, chunk_t b);

+/**
+ * Compare two chunks, returns zero if they are equal or negative/positive if
+ * a is smaller/greater than b.
+ *
+ * Unlike chunk_compare(), this first compares the two chunks' prefix and only
+ * then falls back to comparing their lengths (shorter is "lower") if the
+ * prefix is equal.
+ *
+ * This can e.g. be used to compare nonces.
+ */
+int chunk_compare_prefix(chunk_t a, chunk_t b);
+
 /**
  * Compare two chunks for equality,
  * NULL chunks are never equal.