aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authorHarald Welte <laforge@osmocom.org>2021-03-19 13:19:18 +0100
committerlaforge <laforge@osmocom.org>2021-03-20 11:00:27 +0000
commit860210622438225bbb2f34f4def602c93adfb032 (patch)
tree886aa6552a884c5d5bb4c6501b4a7cb42853204e /utils
parenta7eb735b8ddc1dfd3872756820ae752ba4728822 (diff)
utils: osmo-aka-verify to verify UMTS AKA (SIM side)
This new utility implements the UMTS AKA procedures of the SIM side. It can be used to manually verify the correctness of authentication tuples received from the network. Change-Id: I497747fbf09f633dcd7c592bd9af7fca9a820645
Diffstat (limited to 'utils')
-rw-r--r--utils/Makefile.am4
-rw-r--r--utils/osmo-aka-verify.c243
2 files changed, 246 insertions, 1 deletions
diff --git a/utils/Makefile.am b/utils/Makefile.am
index 4e54a78b..3f2b13f6 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -8,12 +8,14 @@ LDADD = $(top_builddir)/src/libosmocore.la $(top_builddir)/src/gsm/libosmogsm.la
if ENABLE_UTILITIES
EXTRA_DIST = conv_gen.py conv_codes_gsm.py
-bin_PROGRAMS += osmo-arfcn osmo-auc-gen osmo-config-merge
+bin_PROGRAMS += osmo-arfcn osmo-auc-gen osmo-config-merge osmo-aka-verify
osmo_arfcn_SOURCES = osmo-arfcn.c
osmo_auc_gen_SOURCES = osmo-auc-gen.c
+osmo_aka_verify_SOURCES = osmo-aka-verify.c
+
osmo_config_merge_SOURCES = osmo-config-merge.c
osmo_config_merge_LDADD = $(LDADD) $(TALLOC_LIBS)
osmo_config_merge_CFLAGS = $(TALLOC_CFLAGS)
diff --git a/utils/osmo-aka-verify.c b/utils/osmo-aka-verify.c
new file mode 100644
index 00000000..5134d798
--- /dev/null
+++ b/utils/osmo-aka-verify.c
@@ -0,0 +1,243 @@
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <getopt.h>
+
+#include <osmocom/core/utils.h>
+#include <osmocom/core/bit64gen.h>
+
+/* Utility program for implementing the SIM-side procedures of 3GPP Authentication and Key Agreement
+ * as specified by 3GPP TS 33.102 Section 6.3.3
+ *
+ * (C) 2021 by Harald Welte <laforge@gnumonks.org>
+ * Milenage library code used from libosmocore, which inherited it from wpa_supplicant
+ */
+
+/* FIXME: libosmogsm implements those, but doesn't declare them */
+int milenage_f1(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
+ const uint8_t *sqn, const uint8_t *amf, uint8_t *mac_a, uint8_t *mac_s);
+int milenage_f2345(const uint8_t *opc, const uint8_t *k, const uint8_t *_rand,
+ uint8_t *res, uint8_t *ck, uint8_t *ik, uint8_t *ak, uint8_t *akstar);
+
+static int milenage_check(const uint8_t *opc, const uint8_t *k, const uint8_t *sqn, const uint8_t *_rand,
+ const uint8_t *autn, uint8_t *ik, uint8_t *ck, uint8_t *res, size_t *res_len,
+ uint8_t *auts)
+{
+ int i;
+ uint8_t xmac[8], ak[6], rx_sqn_bin[6];
+ unsigned long long rx_sqn;
+ const uint8_t *amf;
+
+ printf("=== Static SIM parameters:\n");
+ printf("Milenage SIM K: %s\n", osmo_hexdump_nospc(k, 16));
+ printf("Milenage SIM OPc: %s\n", osmo_hexdump_nospc(opc, 16));
+ printf("Milenage SIM SQN: %s\n", osmo_hexdump_nospc(sqn, 6));
+ printf("\n");
+
+ printf("=== Authentication Tuple as received from Network:\n");
+ printf("Milenage Input RAND: %s\n", osmo_hexdump_nospc(_rand, 16));
+ printf("Milenage Input AUTN: %s\n", osmo_hexdump_nospc(autn, 16));
+ printf("\tAUTN(+)AK: %s\n", osmo_hexdump_nospc(autn, 6));
+ printf("\tAMF: %s\n", osmo_hexdump_nospc(autn+6, 2));
+ printf("\tMAC: %s\n", osmo_hexdump_nospc(autn+8, 8));
+ printf("\n");
+
+ if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL))
+ return -1;
+
+ *res_len = 8;
+ printf("Milenage f2-Computed RES: %s\n", osmo_hexdump_nospc(res, *res_len));
+ printf("Milenage f3-Computed CK: %s\n", osmo_hexdump_nospc(ck, 16));
+ printf("Milenage f4-Computed IK: %s\n", osmo_hexdump_nospc(ik, 16));
+ printf("Milenage f5-Computed AK: %s\n", osmo_hexdump_nospc(ak, 6));
+
+ /* AUTN = (SQN ^ AK) || AMF || MAC */
+ for (i = 0; i < 6; i++)
+ rx_sqn_bin[i] = autn[i] ^ ak[i];
+ rx_sqn = osmo_load64be_ext(rx_sqn_bin, 6);
+ printf("Milenage Computed SQN: %s (%llu)\n", osmo_hexdump_nospc(rx_sqn_bin, 6), rx_sqn);
+
+ if (memcmp(rx_sqn_bin, sqn, 6) <= 0) {
+ printf("Milenage: RX-SQN differs from SIM SQN: Re-Sync!\n");
+ uint8_t auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */
+ if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak))
+ return -1;
+ printf("Milenage Computed AK*: %s", osmo_hexdump_nospc(ak, 6));
+ for (i = 0; i < 6; i++)
+ auts[i] = sqn[i] ^ ak[i];
+ if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6))
+ return -1;
+ printf("Milenage AUTS: %s\n", osmo_hexdump_nospc(auts, 14));
+ return -2;
+ }
+
+ amf = autn + 6;
+ if (milenage_f1(opc, k, _rand, rx_sqn_bin, amf, xmac, NULL))
+ return -1;
+
+ printf("Milenage f1-Computed XMAC: %s\n", osmo_hexdump_nospc(xmac, 8));
+
+ if (memcmp(xmac, autn + 8, 8) != 0) {
+ fprintf(stderr, "Milenage: MAC mismatch!\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void help()
+{
+ printf( "Static SIM card parameters:\n"
+ "-k --key\tSpecify Ki / K\n"
+ "-o --opc\tSpecify OPC\n"
+ "-O --op\tSpecify OP\n"
+ "-f --amf\tSpecify AMF\n"
+ "-s --sqn\tSpecify SQN\n"
+ "\n"
+ "Authentication Tuple by network:\n"
+ //"-i --ind\tSpecify IND slot for new SQN after AUTS\n"
+ //"-l --ind-len\tSpecify IND bit length (default=5)\n"
+ "-r --rand\tSpecify RAND random value\n"
+ "-A --autn\tSpecify AUTN authentication nonce\n"
+ );
+}
+
+static uint8_t g_k[16];
+static uint8_t g_opc[16];
+static uint8_t g_rand[16];
+static uint8_t g_autn[16];
+static uint8_t g_amf[16];
+static unsigned long long g_sqn;
+
+
+static int handle_options(int argc, char **argv)
+{
+ int rc, option_index;
+ bool rand_is_set = false;
+ bool autn_is_set = false;
+ bool sqn_is_set = false;
+ bool k_is_set = false;
+ bool opc_is_set = false;
+ bool amf_is_set = false;
+ bool opc_is_op = false;
+
+ while (1) {
+ int c;
+ static struct option long_options[] = {
+ { "key", 1, 0, 'k' },
+ { "opc", 1, 0, 'o' },
+ { "op", 1, 0, 'O' },
+ { "amf", 1, 0, 'f' },
+ { "sqn", 1, 0, 's' },
+ { "rand", 1, 0, 'r' },
+ { "autn", 1, 0, 'A' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ rc = 0;
+
+ c = getopt_long(argc, argv, "k:o:O:f:s:r:A:h", long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'k':
+ rc = osmo_hexparse(optarg, g_k, sizeof(g_k));
+ k_is_set = true;
+ break;
+ case 'o':
+ rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
+ opc_is_op = false;
+ opc_is_set = true;
+ break;
+ case 'O':
+ rc = osmo_hexparse(optarg, g_opc, sizeof(g_opc));
+ opc_is_op = true;
+ opc_is_set = true;
+ break;
+ case 'A':
+ rc = osmo_hexparse(optarg, g_autn, sizeof(g_autn));
+ autn_is_set = true;
+ break;
+ case 'f':
+ rc = osmo_hexparse(optarg, g_amf, sizeof(g_amf));
+ amf_is_set = true;
+ break;
+ case 's':
+ g_sqn = strtoull(optarg, 0, 10);
+ sqn_is_set = true;
+ break;
+ case 'r':
+ rc = osmo_hexparse(optarg, g_rand, sizeof(g_rand));
+ rand_is_set = true;
+ break;
+ case 'h':
+ help();
+ exit(0);
+ default:
+ help();
+ exit(1);
+ }
+
+ if (rc < 0) {
+ help();
+ fprintf(stderr, "\nError parsing argument of option `%c'\n", c);
+ exit(2);
+ }
+ }
+
+ if (!k_is_set || !opc_is_set || !autn_is_set || !rand_is_set) {
+ fprintf(stderr, "Error: K, OP[c], AUTN and RAND are mandatory arguments\n");
+ fprintf(stderr, "\n");
+ help();
+ exit(2);
+ }
+
+ if (!sqn_is_set)
+ printf("Warning: You may want to specify SQN\n");
+
+ if (!amf_is_set)
+ printf("Warning: You may want to specify AMF\n");
+
+ if (opc_is_op) {
+ /* FIXME */
+ }
+
+ return 0;
+}
+
+
+
+int main(int argc, char **argv)
+{
+ printf("osmo-aka-check (C) 2021 by Harald Welte\n");
+ printf("This is FREE SOFTWARE with ABSOLUTELY NO WARRANTY\n\n");
+
+ handle_options(argc, argv);
+
+ printf("\n");
+
+ uint8_t ck[16];
+ uint8_t ik[16];
+ uint8_t res[16];
+ size_t res_len;
+ uint8_t auts[14];
+ uint8_t sqn_bin[6];
+ int rc;
+
+ osmo_store64be_ext(g_sqn, sqn_bin, 6);
+
+ rc = milenage_check(g_opc, g_k, sqn_bin, g_rand, g_autn, ck, ik, res, &res_len, auts);
+
+ if (rc < 0) {
+ fprintf(stderr, "Authentication FAILED!\n");
+ exit(1);
+ } else {
+ printf("Authentication SUCCEEDED\n");
+ exit(0);
+ }
+}