aboutsummaryrefslogtreecommitdiffstats
path: root/tests/mm_auth/mm_auth_test.c
diff options
context:
space:
mode:
Diffstat (limited to 'tests/mm_auth/mm_auth_test.c')
-rw-r--r--tests/mm_auth/mm_auth_test.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/tests/mm_auth/mm_auth_test.c b/tests/mm_auth/mm_auth_test.c
new file mode 100644
index 00000000..b8777a8c
--- /dev/null
+++ b/tests/mm_auth/mm_auth_test.c
@@ -0,0 +1,340 @@
+#include <stdbool.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+
+#include <openbsc/debug.h>
+#include <openbsc/gsm_data.h>
+#include <openbsc/gsm_subscriber.h>
+#include <openbsc/auth.h>
+
+#define min(A,B) ((A)>(B)? (B) : (A))
+
+static char *auth_tuple_str(struct gsm_auth_tuple *atuple)
+{
+ static char buf[256];
+ char *pos = buf;
+ int len = sizeof(buf);
+ int l;
+
+#define print2buf(FMT, args...) do {\
+ l = snprintf(pos, len, FMT, ## args); \
+ pos += l;\
+ len -= l;\
+ } while (0)
+
+ print2buf("gsm_auth_tuple {\n");
+ print2buf(" .use_count = %d\n", atuple->use_count);
+ print2buf(" .key_seq = %d\n", atuple->key_seq);
+ print2buf(" .rand = %s\n", osmo_hexdump(atuple->vec.rand, sizeof(atuple->vec.rand)));
+ print2buf(" .sres = %s\n", osmo_hexdump(atuple->vec.sres, sizeof(atuple->vec.sres)));
+ print2buf(" .kc = %s\n", osmo_hexdump(atuple->vec.kc, sizeof(atuple->vec.kc)));
+ print2buf("}\n");
+#undef print2buf
+
+ return buf;
+}
+
+static bool auth_tuple_is(struct gsm_auth_tuple *atuple,
+ const char *expect_str)
+{
+ int l, l1, l2;
+ int i;
+ char *tuple_str = auth_tuple_str(atuple);
+ bool same = (strcmp(expect_str, tuple_str) == 0);
+ if (!same) {
+ l1 = strlen(expect_str);
+ l2 = strlen(tuple_str);
+ printf("Expected %d:\n%s\nGot %d:\n%s\n",
+ l1, expect_str, l2, tuple_str);
+ l = min(l1, l2);
+ for (i = 0; i < l; i++) {
+ if (expect_str[i] != tuple_str[i]) {
+ printf("Difference at pos %d"
+ " (%c 0x%0x != %c 0x%0x)\n",
+ i, expect_str[i], expect_str[i],
+ tuple_str[i], tuple_str[i]);
+ break;
+ }
+ }
+ }
+ return same;
+}
+
+/* override, requires '-Wl,--wrap=db_get_authinfo_for_subscr' */
+int __real_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr);
+
+int test_get_authinfo_rc = 0;
+struct gsm_auth_info test_auth_info = {0};
+struct gsm_auth_info default_auth_info = {
+ .auth_algo = AUTH_ALGO_COMP128v1,
+ .a3a8_ki_len = 16,
+ .a3a8_ki = { 0 }
+};
+
+int __wrap_db_get_authinfo_for_subscr(struct gsm_auth_info *ainfo,
+ struct gsm_subscriber *subscr)
+{
+ *ainfo = test_auth_info;
+ printf("wrapped: db_get_authinfo_for_subscr(): rc = %d\n", test_get_authinfo_rc);
+ return test_get_authinfo_rc;
+}
+
+/* override, requires '-Wl,--wrap=db_get_lastauthtuple_for_subscr' */
+int __real_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr);
+
+int test_get_lastauthtuple_rc = 0;
+struct gsm_auth_tuple test_last_auth_tuple = { 0 };
+struct gsm_auth_tuple default_auth_tuple = { 0 };
+
+int __wrap_db_get_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ *atuple = test_last_auth_tuple;
+ printf("wrapped: db_get_lastauthtuple_for_subscr(): rc = %d\n", test_get_lastauthtuple_rc);
+ return test_get_lastauthtuple_rc;
+}
+
+/* override, requires '-Wl,--wrap=db_sync_lastauthtuple_for_subscr' */
+int __real_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr);
+int test_sync_lastauthtuple_rc = 0;
+int __wrap_db_sync_lastauthtuple_for_subscr(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr)
+{
+ test_last_auth_tuple = *atuple;
+ printf("wrapped: db_sync_lastauthtuple_for_subscr(): rc = %d\n", test_sync_lastauthtuple_rc);
+ return test_sync_lastauthtuple_rc;
+}
+
+int auth_get_tuple_for_subscr_verbose(struct gsm_auth_tuple *atuple,
+ struct gsm_subscriber *subscr,
+ int key_seq)
+{
+ int auth_action;
+ auth_action = auth_get_tuple_for_subscr(atuple, subscr, key_seq);
+ printf("auth_get_tuple_for_subscr(key_seq=%d) --> auth_action == %s\n",
+ key_seq, auth_action_str(auth_action));
+ return auth_action;
+}
+
+/* override libssl RAND_bytes() to get testable crypto results */
+int RAND_bytes(uint8_t *rand, int len)
+{
+ memset(rand, 23, len);
+ return 1;
+}
+
+static void test_error()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq = 0;
+
+ printf("\n* test_error()\n");
+
+ /* any error (except -ENOENT) */
+ test_get_authinfo_rc = -EIO;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_ERROR);
+}
+
+static void test_auth_not_avail()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq = 0;
+
+ printf("\n* test_auth_not_avail()\n");
+
+ /* no entry */
+ test_get_authinfo_rc = -ENOENT;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_NOT_AVAIL);
+}
+
+static void test_auth_then_ciph1()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_then_ciph1()\n");
+
+ /* Ki entry, but no auth tuple negotiated yet */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = -ENOENT;
+ key_seq = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 0\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+
+ /* With a different last saved key_seq stored in the out-arg of
+ * db_get_lastauthtuple_for_subscr() by coincidence, expect absolutely
+ * the same as above. */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = -ENOENT;
+ key_seq = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 0\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_then_ciph2()
+{
+ int auth_action;
+
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_then_ciph2()\n");
+
+ /* Ki entry, auth tuple negotiated, but invalid incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 2;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ key_seq = GSM_KEY_SEQ_INVAL;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 3\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+
+ /* Change the last saved key_seq, expect last_auth_tuple.key_seq + 1 */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ key_seq = GSM_KEY_SEQ_INVAL;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 4\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_reuse()
+{
+ int auth_action;
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_reuse()\n");
+
+ /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = key_seq = 3;
+ test_last_auth_tuple.use_count = 1;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 2\n"
+ " .key_seq = 3\n"
+ " .rand = 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \n"
+ " .sres = 00 00 00 00 \n"
+ " .kc = 00 00 00 00 00 00 00 00 \n"
+ "}\n"
+ ));
+}
+
+static void test_auth_reuse_key_seq_mismatch()
+{
+ int auth_action;
+ struct gsm_auth_tuple atuple = {0};
+ struct gsm_subscriber subscr = {0};
+ int key_seq;
+
+ printf("\n* test_auth_reuse_key_seq_mismatch()\n");
+
+ /* Ki entry, auth tuple negotiated, valid+matching incoming key_seq */
+ test_auth_info = default_auth_info;
+ test_last_auth_tuple = default_auth_tuple;
+ test_last_auth_tuple.key_seq = 3;
+ key_seq = 4;
+ test_last_auth_tuple.use_count = 1;
+ test_get_authinfo_rc = 0;
+ test_get_lastauthtuple_rc = 0;
+ auth_action = auth_get_tuple_for_subscr_verbose(&atuple, &subscr,
+ key_seq);
+ OSMO_ASSERT(auth_action == AUTH_DO_AUTH_THEN_CIPH);
+ OSMO_ASSERT(auth_tuple_is(&atuple,
+ "gsm_auth_tuple {\n"
+ " .use_count = 1\n"
+ " .key_seq = 4\n"
+ " .rand = 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 17 \n"
+ " .sres = a1 ab c6 90 \n"
+ " .kc = 0f 27 ed f3 ac 97 ac 00 \n"
+ "}\n"
+ ));
+}
+
+int main(void)
+{
+ osmo_init_logging(&log_info);
+ log_set_log_level(osmo_stderr_target, LOGL_INFO);
+
+ test_error();
+ test_auth_not_avail();
+ test_auth_then_ciph1();
+ test_auth_then_ciph2();
+ test_auth_reuse();
+ test_auth_reuse_key_seq_mismatch();
+ return 0;
+}