From 8aca7f92319af5e5e1da3b7c3126f94eb1761f19 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Mon, 13 Jul 2026 18:00:26 +0200 Subject: [PATCH] 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. --- src/libstrongswan/tests/suites/test_chunk.c | 39 +++++++++++++++------ src/libstrongswan/utils/chunk.c | 23 ++++++++++-- src/libstrongswan/utils/chunk.h | 19 ++++++++-- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_chunk.c b/src/libstrongswan/tests/suites/test_chunk.c index d3e1bd7f6..016e8f340 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 c8153f4f0..12d710082 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 62f7155d2..e0417cf36 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.