auth_milenage/osmo-auc-gen: compute OPC in case only OP is known
diff --git a/include/osmocom/crypt/auth.h b/include/osmocom/crypt/auth.h
index 30e16e8..67b3200 100644
--- a/include/osmocom/crypt/auth.h
+++ b/include/osmocom/crypt/auth.h
@@ -33,6 +33,7 @@
 			uint8_t k[16];
 			uint8_t amf[2];
 			uint64_t sqn;
+			int opc_is_op;
 		} umts;
 		struct {
 			uint8_t ki[16];
diff --git a/src/gsm/auth_milenage.c b/src/gsm/auth_milenage.c
index 2a9ba33..5b2787d 100644
--- a/src/gsm/auth_milenage.c
+++ b/src/gsm/auth_milenage.c
@@ -83,10 +83,21 @@
 				 const uint8_t *_rand)
 {
 	uint8_t sqn_out[6];
+	uint8_t gen_opc[16];
+	uint8_t *opc;
 	int rc;
 
-	rc = milenage_auts(aud->u.umts.opc, aud->u.umts.k,
-			   rand_auts, auts, sqn_out);
+	/* Check if we only know OP and compute OPC if required */
+	if (aud->type == OSMO_AUTH_TYPE_UMTS && aud->u.umts.opc_is_op) {
+		rc = milenage_opc_gen(gen_opc, aud->u.umts.k,
+				      aud->u.umts.opc);
+		if (rc < 0)
+			return rc;
+		opc = gen_opc;
+	} else
+		opc = aud->u.umts.opc;
+
+	rc = milenage_auts(opc, aud->u.umts.k, rand_auts, auts, sqn_out);
 	if (rc < 0)
 		return rc;
 
diff --git a/utils/osmo-auc-gen.c b/utils/osmo-auc-gen.c
index 93f126f..a3c4dc2 100644
--- a/utils/osmo-auc-gen.c
+++ b/utils/osmo-auc-gen.c
@@ -59,6 +59,7 @@
 		"-a  --algorithm\tSpecify name of the algorithm\n"
 		"-k  --key\tSpecify Ki / K\n"
 		"-o  --opc\tSpecify OPC (only for 3G)\n"
+		"-O  --op\tSpecify OP (only for 3G)\n"
 		"-a  --amf\tSpecify AMF (only for 3G)\n"
 		"-s  --sqn\tSpecify SQN (only for 3G)\n"
 		"-r  --rand\tSpecify random value\n");
@@ -84,6 +85,7 @@
 			{ "algorithm", 1, 0, 'a' },
 			{ "key", 1, 0, 'k' },
 			{ "opc", 1, 0, 'o' },
+			{ "op", 1, 0, 'O' },
 			{ "amf", 1, 0, 'f' },
 			{ "sqn", 1, 0, 's' },
 			{ "rand", 1, 0, 'r' },
@@ -133,6 +135,16 @@
 			}
 			rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
 					   sizeof(test_aud.u.umts.opc));
+			test_aud.u.umts.opc_is_op = 0;
+			break;
+		case 'O':
+			if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {
+				fprintf(stderr, "Only UMTS has OP\n");
+				exit(2);
+			}
+			rc = osmo_hexparse(optarg, test_aud.u.umts.opc,
+					   sizeof(test_aud.u.umts.opc));
+			test_aud.u.umts.opc_is_op = 1;
 			break;
 		case 'f':
 			if (test_aud.type != OSMO_AUTH_TYPE_UMTS) {