auth: Add support for XOR test A3A8 algo (and vty commands)

Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
diff --git a/openbsc/src/auth.c b/openbsc/src/auth.c
index 5a54da0..f57b668 100644
--- a/openbsc/src/auth.c
+++ b/openbsc/src/auth.c
@@ -31,6 +31,26 @@
 
 
 static int
+_use_xor(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
+{
+	int i, l = ainfo->a3a8_ki_len;
+
+	if ((l > A38_XOR_MAX_KEY_LEN) || (l < A38_XOR_MIN_KEY_LEN)) {
+		DEBUGP(DMM, "Invalid XOR key (len=%d) %s",
+			ainfo->a3a8_ki_len,
+			hexdump(ainfo->a3a8_ki, ainfo->a3a8_ki_len));
+		return -1;
+	}
+
+	for (i=0; i<4; i++)
+		atuple->sres[i] = atuple->rand[i] ^ ainfo->a3a8_ki[i];
+	for (i=8; i<12; i++)
+		atuple->kc[i-4] = atuple->rand[i] ^ ainfo->a3a8_ki[i];
+
+	return 0;
+}
+
+static int
 _use_comp128_v1(struct gsm_auth_info *ainfo, struct gsm_auth_tuple *atuple)
 {
 	if (ainfo->a3a8_ki_len != A38_COMP128_KEY_LEN) {
@@ -85,6 +105,11 @@
 		case AUTH_ALGO_NONE:
 			return 0;
 
+		case AUTH_ALGO_XOR:
+			if (_use_xor(&ainfo, atuple))
+				return 0;
+			break;
+
 		case AUTH_ALGO_COMP128v1:
 			if (_use_comp128_v1(&ainfo, atuple))
 				return 0;
diff --git a/openbsc/src/vty_interface_layer3.c b/openbsc/src/vty_interface_layer3.c
index b5af0ab..7c32c05 100644
--- a/openbsc/src/vty_interface_layer3.c
+++ b/openbsc/src/vty_interface_layer3.c
@@ -442,9 +442,10 @@
 	return CMD_SUCCESS;
 }
 
-#define A3A8_ALG_TYPES "(none|comp128v1)"
+#define A3A8_ALG_TYPES "(none|xor|comp128v1)"
 #define A3A8_ALG_HELP 			\
 	"Use No A3A8 algorithm\n"	\
+	"Use XOR algorithm\n"		\
 	"Use COMP128v1 algorithm\n"
 
 DEFUN(ena_subscr_a3a8,
@@ -457,9 +458,9 @@
 	struct gsm_subscriber *subscr =
 			get_subscr_by_argv(gsmnet, argv[0], argv[1]);
 	const char *alg_str = argv[2];
-	const char *ki_str = argv[3];
+	const char *ki_str = argc == 4 ? argv[3] : NULL;
 	struct gsm_auth_info ainfo;
-	int rc;
+	int rc, minlen, maxlen;
 
 	if (!subscr) {
 		vty_out(vty, "%% No subscriber found for %s %s%s",
@@ -468,23 +469,35 @@
 	}
 
 	if (!strcasecmp(alg_str, "none")) {
-		/* Just erase */
-		rc = db_sync_authinfo_for_subscr(NULL, subscr);
+		ainfo.auth_algo = AUTH_ALGO_NONE;
+		minlen = maxlen = 0;
+	} else if (!strcasecmp(alg_str, "xor")) {
+		ainfo.auth_algo = AUTH_ALGO_XOR;
+		minlen = A38_XOR_MIN_KEY_LEN;
+		maxlen = A38_XOR_MAX_KEY_LEN;
 	} else if (!strcasecmp(alg_str, "comp128v1")) {
-		/* Parse hex string Ki */
-		rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
-		if (rc != 16)
-			return CMD_WARNING;
-
-		/* Set the infos */
 		ainfo.auth_algo = AUTH_ALGO_COMP128v1;
-		ainfo.a3a8_ki_len = rc;
-		rc = db_sync_authinfo_for_subscr(&ainfo, subscr);
+		minlen = maxlen = A38_COMP128_KEY_LEN;
 	} else {
 		/* Unknown method */
 		return CMD_WARNING;
 	}
 
+	if (ki_str) {
+		rc = hexparse(ki_str, ainfo.a3a8_ki, sizeof(ainfo.a3a8_ki));
+		if ((rc > maxlen) || (rc < minlen))
+			return CMD_WARNING;
+		ainfo.a3a8_ki_len = rc;
+	} else {
+		ainfo.a3a8_ki_len = 0;
+		if (minlen)
+			return CMD_WARNING;
+	}
+
+	rc = db_sync_authinfo_for_subscr(
+		ainfo.auth_algo == AUTH_ALGO_NONE ? NULL : &ainfo,
+		subscr);
+
 	/* the last tuple probably invalid with the new auth settings */
 	db_sync_lastauthtuple_for_subscr(NULL, subscr);