milenage: Add function to compute OPC from OP and K
diff --git a/src/gsm/milenage/milenage.c b/src/gsm/milenage/milenage.c
index cc4e95c..b43f986 100644
--- a/src/gsm/milenage/milenage.c
+++ b/src/gsm/milenage/milenage.c
@@ -327,3 +327,18 @@
 
 	return 0;
 }
+
+int milenage_opc_gen(u8 *opc, const u8 *k, const u8 *op)
+{
+	int i;
+
+	/* Encrypt OP using K */
+	if (aes_128_encrypt_block(k, op, opc))
+		return -1;
+
+	/* XOR the resulting Ek(OP) with OP */
+	for (i = 0; i < 16; i++)
+		opc[i] = opc[i] ^ op[i];
+
+	return 0;
+}
diff --git a/src/gsm/milenage/milenage.h b/src/gsm/milenage/milenage.h
index d5054d6..a91e946 100644
--- a/src/gsm/milenage/milenage.h
+++ b/src/gsm/milenage/milenage.h
@@ -30,4 +30,6 @@
 int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand,
 		   u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar);
 
+int milenage_opc_gen(u8 *opc, const u8 *k, const u8 *op);
+
 #endif /* MILENAGE_H */
diff --git a/tests/auth/milenage_test.c b/tests/auth/milenage_test.c
index da7c800..7c996f0 100644
--- a/tests/auth/milenage_test.c
+++ b/tests/auth/milenage_test.c
@@ -37,6 +37,24 @@
 	},
 };
 
+static int opc_test(const struct osmo_sub_auth_data *aud)
+{
+	int rc;
+	uint8_t opc[16];
+#if 0
+	const uint8_t op[16] = { 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+				 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };
+#else
+	const uint8_t op[16] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0 };
+#endif
+
+	rc = milenage_opc_gen(opc, aud->u.umts.k, op);
+
+	printf("OP:\t%s\n", osmo_hexdump(op, sizeof(op)));
+	printf("OPC:\t%s\n", osmo_hexdump(opc, sizeof(opc)));
+	return rc;
+}
+
 int main(int argc, char **argv)
 {
 	struct osmo_auth_vector _vec;
@@ -73,6 +91,8 @@
 		printf("AUTS success: SEQ.MS = %lu\n", test_aud.u.umts.sqn);
 	}
 
+	opc_test(&test_aud);
+
 	exit(0);
 
 }