Iu: add UEA encryption

Add vty 'encryption uea 0 1 2', defaults to 'encryption uea 0' to yield
previous behavior.

If any UEA above 0 is enabled, include the UEA key in the Iu Security
Mode Command.

I noticed that only the code bit in st_iu_security_cmd_on_enter()
affects the test. The same code in gsm48_gmm_authorize() seems to be
dead code? But applying the patch there as well just to be safe.

We cannot yet verify the chosen UEA to match a configured UEA level,
because the iu_client.c does not send us message details with the
RANAP_IU_EVENT_SECURITY_MODE_COMPLETE.
Also we cannot yet send the set of configured UEA to the hNodeB, since,
again, iu_client.c does not provide the proper API for it.
The proper solution here is to completely dissolve iu_client.c and do
all Iu handling in osmo-sgsn itself -- see OS#5487.

Related: SYS#5516
Related: I1a7c3b156830058c43f15f55883ea301d2d01d5f (osmo-ttcn3-hacks)
Change-Id: I27e8e0078c45426bf227bb44aac82a4875d18d0f
diff --git a/src/sgsn/gprs_gmm.c b/src/sgsn/gprs_gmm.c
index dfe477c..c2bf7a3 100644
--- a/src/sgsn/gprs_gmm.c
+++ b/src/sgsn/gprs_gmm.c
@@ -41,6 +41,7 @@
 #include <osmocom/core/utils.h>
 #include <osmocom/core/tdef.h>
 #include <osmocom/crypt/auth.h>
+#include <osmocom/crypt/utran_cipher.h>
 #include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
 
 #include <osmocom/gprs/gprs_bssgp.h>
@@ -916,7 +917,13 @@
 	/* The MS is authorized */
 #ifdef BUILD_IU
 	if (ctx->ran_type == MM_CTX_T_UTRAN_Iu && !ctx->iu.ue_ctx->integrity_active) {
-		rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key);
+		/* Is any encryption above UEA0 enabled? */
+		bool send_ck = sgsn->cfg.uea_encryption_mask > (1 << OSMO_UTRAN_UEA0);
+		LOGMMCTXP(LOGL_DEBUG, ctx, "Iu Security Mode Command: %s encryption key (UEA encryption mask = 0x%x)\n",
+			  send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask);
+		/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
+		 * is not possible in the iu_client API. See OS#5487. */
+		rc = ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
 		ctx->iu.new_key = 0;
 		return rc;
 	}
diff --git a/src/sgsn/gprs_gmm_attach.c b/src/sgsn/gprs_gmm_attach.c
index 629cc53..59417df 100644
--- a/src/sgsn/gprs_gmm_attach.c
+++ b/src/sgsn/gprs_gmm_attach.c
@@ -1,4 +1,5 @@
 #include <osmocom/core/tdef.h>
+#include <osmocom/crypt/utran_cipher.h>
 
 #include <osmocom/sgsn/gprs_gmm_attach.h>
 
@@ -257,6 +258,7 @@
 {
 #ifdef BUILD_IU
 	struct sgsn_mm_ctx *ctx = fi->priv;
+	bool send_ck;
 
 	/* TODO: shouldn't this set always? not only when the integrity_active? */
 	if (ctx->iu.ue_ctx->integrity_active) {
@@ -264,7 +266,14 @@
 		return;
 	}
 
-	ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, 0, ctx->iu.new_key);
+	/* Is any encryption above UEA0 enabled? */
+	send_ck = sgsn->cfg.uea_encryption_mask > (1 << OSMO_UTRAN_UEA0);
+	LOGMMCTXP(LOGL_DEBUG, ctx, "Iu Security Mode Command: %s encryption key (UEA encryption mask = 0x%x)\n",
+		  send_ck ? "sending" : "not sending", sgsn->cfg.uea_encryption_mask);
+
+	/* FIXME: we should send the set of allowed UEA, as in ranap_new_msg_sec_mod_cmd2(). However, this
+	 * is not possible in the iu_client API. See OS#5487. */
+	ranap_iu_tx_sec_mode_cmd(ctx->iu.ue_ctx, &ctx->auth_triplet.vec, send_ck, ctx->iu.new_key);
 	ctx->iu.new_key = 0;
 #endif
 }
diff --git a/src/sgsn/gprs_ranap.c b/src/sgsn/gprs_ranap.c
index ead20f7..ab3a968 100644
--- a/src/sgsn/gprs_ranap.c
+++ b/src/sgsn/gprs_ranap.c
@@ -145,6 +145,11 @@
 		rc = 0;
 		break;
 	case RANAP_IU_EVENT_SECURITY_MODE_COMPLETE:
+		/* FIXME: verify that a permitted UEA level was chosen. Compare how osmo-msc does it in
+		 * msc_a_ran_dec_from_msc_i(), case RAN_MSG_CIPHER_MODE_COMPLETE.
+		 * We should dissolve iu_client.c, it was a design mistake when first implementing Iu support. osmo-msc
+		 * has moved away from it a long time ago.
+		 */
 		/* Continue authentication here */
 		mm->iu.ue_ctx->integrity_active = 1;
 		ranap_iu_tx_common_id(mm->iu.ue_ctx, mm->imsi);
diff --git a/src/sgsn/sgsn_vty.c b/src/sgsn/sgsn_vty.c
index 56a2d78..a394c41 100644
--- a/src/sgsn/sgsn_vty.c
+++ b/src/sgsn/sgsn_vty.c
@@ -47,6 +47,7 @@
 #include <osmocom/vty/vty.h>
 #include <osmocom/vty/misc.h>
 #include <osmocom/crypt/gprs_cipher.h>
+#include <osmocom/crypt/utran_cipher.h>
 #include <osmocom/abis/ipa.h>
 
 #include <osmocom/gprs/gprs_bssgp.h>
@@ -775,9 +776,11 @@
 	return CMD_SUCCESS;
 }
 
+#define ENCRYPTION_STR "Set encryption algorithms for SGSN\n"
+
 DEFUN(cfg_encrypt2, cfg_encrypt2_cmd,
 	"encryption gea <0-4> [<0-4>] [<0-4>] [<0-4>] [<0-4>]",
-	"Set encryption algorithms for SGSN\n"
+	ENCRYPTION_STR
 	"GPRS Encryption Algorithm\n"
 	"GEAn Algorithm Number\n"
 	"GEAn Algorithm Number\n"
@@ -835,6 +838,23 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_encryption_uea, cfg_encryption_uea_cmd,
+      "encryption uea <0-2> [<0-2>] [<0-2>]",
+      ENCRYPTION_STR
+      "UTRAN (3G) encryption algorithms to allow: 0 = UEA0 (no encryption), 1 = UEA1, 2 = UEA2.\n"
+      "UEAn Algorithm Number\n"
+      "UEAn Algorithm Number\n"
+      "UEAn Algorithm Number\n")
+{
+	unsigned int i;
+
+	g_cfg->uea_encryption_mask = 0;
+	for (i = 0; i < argc; i++)
+		g_cfg->uea_encryption_mask |= (1 << atoi(argv[i]));
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_auth_policy, cfg_auth_policy_cmd,
 	"auth-policy (accept-all|closed|acl-only|remote)",
 	"Configure the Authorization policy of the SGSN. This setting determines which subscribers are"
@@ -1732,6 +1752,7 @@
 	/* order matters here: ensure we attempt to parse our new command first! */
 	install_element(SGSN_NODE, &cfg_encrypt2_cmd);
 	install_element(SGSN_NODE, &cfg_encrypt_cmd);
+	install_element(SGSN_NODE, &cfg_encryption_uea_cmd);
 
 	install_element(SGSN_NODE, &cfg_gsup_ipa_name_cmd);
 	install_element(SGSN_NODE, &cfg_gsup_remote_ip_cmd);
@@ -1784,6 +1805,7 @@
 	OSMO_ASSERT(g_cfg);
 
 	g_cfg->gea_encryption_mask = 0x1; /* support GEA0 by default unless specific encryption config exists */
+	g_cfg->uea_encryption_mask = (1 << OSMO_UTRAN_UEA0); /* support UEA0 by default unless specific encryption config exists */
 
 	rc = vty_read_config_file(config_file, NULL);
 	if (rc < 0) {