libosmogsm: Support authentication with 256-bit K and/or OP/OPc

3GPP TS 33.102 Section 6.3.7 states that K can be 128 or 256 bits,
while our 'struct osmo_sub_auth_data' had a fixed-size 128bit field.

This means we cannot use our auth_core for algorithms with larger
key sizes, such as TUAK.  Let's introduce osmo_sub_auth_data2 for
larger (and variable) sized K and OP[c].

K and OP[c] can even have different sizes in TUAK, where OP[c] is
always 256bit, but K can be 128 or 256 bits.  So we need separate
length fields for K and OP[c].

I'm adding backwards-compatibility API wrappers, so old applications
just continue to work as they always did.

However, I'm not adding compatibility wrappers for the plug-in API
that can be used to register additional authentication implementations
at runtime.  We don't know of any user of that API outside of
libosmocore, so the function signatures of the 'struct osmo_auth_impl'
are modified in an incompatible way.

Change-Id: Ie775fedba4a3fa12314c0f7c8a369662ef6a40df
diff --git a/TODO-RELEASE b/TODO-RELEASE
index ebda4c5..d082ee3 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -13,3 +13,5 @@
 libosmocore ADD     new API osmo_io_*()
 libosmocoding	ADD	new gsm0503_tch_hr_decode2() public API, previous API
 			gsm0503_tch_hr_decode() marked as deprecated
+libosmogsm	ADD	new osmo_sub_auth_data2 / osmo_auth_gen_vec2 / osmo_auth_gen_vec_auts2
+libosmogsm	MODIFY	osmo_auth_impl callback function signature change. No known external users
diff --git a/include/osmocom/crypt/auth.h b/include/osmocom/crypt/auth.h
index d872f64..2833ed3 100644
--- a/include/osmocom/crypt/auth.h
+++ b/include/osmocom/crypt/auth.h
@@ -40,6 +40,30 @@
 #define OSMO_AUTH_ALG_XOR OSMO_AUTH_ALG_XOR_3G
 
 /*! permanent (secret) subscriber auth data */
+struct osmo_sub_auth_data2 {
+	enum osmo_sub_auth_type type;
+	enum osmo_auth_algo algo;
+	union {
+		struct {
+			/* See 3GPP TS 33.102 Section 9.3.7 Length of authentication parameters */
+			uint8_t opc[32]; /*!< operator invariant value */
+			uint8_t opc_len; /*!< OPc length (in bytes): 16 or 32 */
+			uint8_t k[32];	/*!< secret key of the subscriber */
+			uint8_t k_len;	/*!< K length (in bytes): 16 or 32 */
+			uint8_t amf[2];
+			uint64_t sqn;	/*!< sequence number (in: prev sqn; out: used sqn) */
+			int opc_is_op;	/*!< is the OPC field OPC (0) or OP (1) ? */
+			unsigned int ind_bitlen; /*!< nr of bits not in SEQ, only SQN */
+			unsigned int ind; /*!< which IND slot to use an SQN from */
+			uint64_t sqn_ms; /*!< sqn from AUTS (output value only) */
+		} umts;
+		struct {
+			uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES];	/*!< secret key */
+		} gsm;
+	} u;
+};
+
+/* deprecated older structure without support for 32-byte K/OP[c] */
 struct osmo_sub_auth_data {
 	enum osmo_sub_auth_type type;
 	enum osmo_auth_algo algo;
@@ -58,7 +82,7 @@
 			uint8_t ki[OSMO_A5_MAX_KEY_LEN_BYTES];	/*!< secret key */
 		} gsm;
 	} u;
-};
+} OSMO_DEPRECATED_OUTSIDE("Use osmo_sub_auth_data2 instead");
 
 /* data structure describing a computed auth vector, generated by AuC */
 struct osmo_auth_vector {
@@ -67,7 +91,7 @@
 	uint8_t ck[OSMO_A5_MAX_KEY_LEN_BYTES];		/*!< ciphering key */
 	uint8_t ik[OSMO_A5_MAX_KEY_LEN_BYTES];		/*!< integrity key */
 	uint8_t res[16];	/*!< authentication result */
-	uint8_t res_len;	/*!< length (in bytes) of res */
+	uint8_t res_len;	/*!< length (in bytes) of res: 8..16 bytes */
 	uint8_t kc[8];		/*!< Kc for GSM encryption (A5) */
 	uint8_t sres[4];	/*!< authentication result for GSM */
 	uint32_t auth_types;	/*!< bitmask of OSMO_AUTH_TYPE_* */
@@ -82,22 +106,32 @@
 
 	/*! callback for generate authentication vectors */
 	int (*gen_vec)(struct osmo_auth_vector *vec,
-			struct osmo_sub_auth_data *aud,
+			struct osmo_sub_auth_data2 *aud,
 			const uint8_t *_rand);
 
-	/* callback for generationg auth vectors + re-sync */
+	/*! callback for generating auth vectors + re-sync */
 	int (*gen_vec_auts)(struct osmo_auth_vector *vec,
-			    struct osmo_sub_auth_data *aud,
+			    struct osmo_sub_auth_data2 *aud,
 			    const uint8_t *auts, const uint8_t *rand_auts,
 			    const uint8_t *_rand);
 };
 
 int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
-		      struct osmo_sub_auth_data *aud, const uint8_t *_rand);
+		      struct osmo_sub_auth_data *aud, const uint8_t *_rand)
+	OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec2 instead");
+
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+		       struct osmo_sub_auth_data2 *aud, const uint8_t *_rand);
 
 int osmo_auth_gen_vec_auts(struct osmo_auth_vector *vec,
 			   struct osmo_sub_auth_data *aud,
 			   const uint8_t *auts, const uint8_t *rand_auts,
+			   const uint8_t *_rand)
+	OSMO_DEPRECATED_OUTSIDE("Use osmo_auth_gen_vec_auts2 instead");
+
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+			   struct osmo_sub_auth_data2 *aud,
+			   const uint8_t *auts, const uint8_t *rand_auts,
 			   const uint8_t *_rand);
 
 int osmo_auth_register(struct osmo_auth_impl *impl);
diff --git a/src/gsm/auth_comp128v1.c b/src/gsm/auth_comp128v1.c
index ba3cf60..a314dc2 100644
--- a/src/gsm/auth_comp128v1.c
+++ b/src/gsm/auth_comp128v1.c
@@ -27,7 +27,7 @@
  */
 
 static int c128v1_gen_vec(struct osmo_auth_vector *vec,
-			  struct osmo_sub_auth_data *aud,
+			  struct osmo_sub_auth_data2 *aud,
 			  const uint8_t *_rand)
 {
 	comp128v1(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
diff --git a/src/gsm/auth_comp128v23.c b/src/gsm/auth_comp128v23.c
index 91fb259..697858a 100644
--- a/src/gsm/auth_comp128v23.c
+++ b/src/gsm/auth_comp128v23.c
@@ -29,7 +29,7 @@
  */
 
 static int c128v2_gen_vec(struct osmo_auth_vector *vec,
-			  struct osmo_sub_auth_data *aud,
+			  struct osmo_sub_auth_data2 *aud,
 			  const uint8_t *_rand)
 {
 	comp128v2(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
@@ -46,7 +46,7 @@
 };
 
 static int c128v3_gen_vec(struct osmo_auth_vector *vec,
-			  struct osmo_sub_auth_data *aud,
+			  struct osmo_sub_auth_data2 *aud,
 			  const uint8_t *_rand)
 {
 	comp128v3(aud->u.gsm.ki, _rand, vec->sres, vec->kc);
diff --git a/src/gsm/auth_core.c b/src/gsm/auth_core.c
index ce6ba7d..421ecee 100644
--- a/src/gsm/auth_core.c
+++ b/src/gsm/auth_core.c
@@ -1,4 +1,4 @@
-/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org>
+/* (C) 2010-2023 by Harald Welte <laforge@gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -36,6 +36,35 @@
 
 static LLIST_HEAD(osmo_auths);
 
+/* generate auth_data2 from auth_data (for legacy API/ABI compatibility */
+static int auth_data2auth_data2(struct osmo_sub_auth_data2 *out, const struct osmo_sub_auth_data *in)
+{
+	out->type = in->type;
+	out->algo = in->algo;
+	switch (in->type) {
+	case OSMO_AUTH_TYPE_NONE:
+		return 0;
+	case OSMO_AUTH_TYPE_GSM:
+		memcpy(out->u.gsm.ki, in->u.gsm.ki, sizeof(out->u.gsm.ki));
+		break;
+	case OSMO_AUTH_TYPE_UMTS:
+		memcpy(out->u.umts.opc, in->u.umts.opc, sizeof(in->u.umts.opc));
+		out->u.umts.opc_len = sizeof(in->u.umts.opc);
+		memcpy(out->u.umts.k, in->u.umts.k, sizeof(in->u.umts.k));
+		out->u.umts.k_len = sizeof(in->u.umts.k);
+		memcpy(out->u.umts.amf, in->u.umts.amf, sizeof(out->u.umts.amf));
+		out->u.umts.sqn = in->u.umts.sqn;
+		out->u.umts.opc_is_op = in->u.umts.opc_is_op;
+		out->u.umts.ind_bitlen = in->u.umts.ind_bitlen;
+		out->u.umts.ind = in->u.umts.ind;
+		out->u.umts.sqn_ms = in->u.umts.sqn_ms;
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
 static struct osmo_auth_impl *selected_auths[_OSMO_AUTH_ALG_NUM];
 
 /*! Register an authentication algorithm implementation with the core
@@ -149,9 +178,9 @@
  * by the AUC via HLR and VLR to the MSC which will then be able to
  * invoke authentication with the MS
  */
-int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
-		      struct osmo_sub_auth_data *aud,
-		      const uint8_t *_rand)
+int osmo_auth_gen_vec2(struct osmo_auth_vector *vec,
+		       struct osmo_sub_auth_data2 *aud,
+		       const uint8_t *_rand)
 {
 	struct osmo_auth_impl *impl = selected_auths[aud->algo];
 	int rc;
@@ -168,6 +197,71 @@
 	return 0;
 }
 
+/*! Generate authentication vector
+ *  \param[out] vec Generated authentication vector
+ *  \param[in] aud Subscriber-specific key material
+ *  \param[in] _rand Random challenge to be used
+ *  \returns 0 on success, negative error on failure
+ *
+ * This function performs the core cryptographic function of the AUC,
+ * computing authentication triples/quintuples based on the permanent
+ * subscriber data and a random value.  The result is what is forwarded
+ * by the AUC via HLR and VLR to the MSC which will then be able to
+ * invoke authentication with the MS
+ */
+int osmo_auth_gen_vec(struct osmo_auth_vector *vec,
+		      struct osmo_sub_auth_data *aud,
+		      const uint8_t *_rand)
+{
+	struct osmo_sub_auth_data2 aud2;
+	int rc;
+
+	rc = auth_data2auth_data2(&aud2, aud);
+	if (rc < 0)
+		return rc;
+
+	rc = osmo_auth_gen_vec2(vec, &aud2, _rand);
+	if (aud->type == OSMO_AUTH_TYPE_UMTS)
+		aud->u.umts.sqn = aud2.u.umts.sqn;
+
+	return rc;
+}
+
+/*! Generate authentication vector and re-sync sequence
+ *  \param[out] vec Generated authentication vector
+ *  \param[in] aud Subscriber-specific key material
+ *  \param[in] auts AUTS value sent by the SIM/MS
+ *  \param[in] rand_auts RAND value sent by the SIM/MS
+ *  \param[in] _rand Random challenge to be used to generate vector
+ *  \returns 0 on success, negative error on failure
+ *
+ * This function performs a special variant of the core cryptographic
+ * function of the AUC: computing authentication triples/quintuples
+ * based on the permanent subscriber data, a random value as well as the
+ * AUTS and RAND values returned by the SIM/MS.  This special variant is
+ * needed if the sequence numbers between MS and AUC have for some
+ * reason become different.
+ */
+int osmo_auth_gen_vec_auts2(struct osmo_auth_vector *vec,
+			    struct osmo_sub_auth_data2 *aud,
+			    const uint8_t *auts, const uint8_t *rand_auts,
+			    const uint8_t *_rand)
+{
+	struct osmo_auth_impl *impl = selected_auths[aud->algo];
+	int rc;
+
+	if (!impl || !impl->gen_vec_auts)
+		return -ENOENT;
+
+	rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+	if (rc < 0)
+		return rc;
+
+	memcpy(vec->rand, _rand, sizeof(vec->rand));
+
+	return 0;
+}
+
 /*! Generate authentication vector and re-sync sequence
  *  \param[out] vec Generated authentication vector
  *  \param[in] aud Subscriber-specific key material
@@ -188,19 +282,20 @@
 			   const uint8_t *auts, const uint8_t *rand_auts,
 			   const uint8_t *_rand)
 {
-	struct osmo_auth_impl *impl = selected_auths[aud->algo];
+	struct osmo_sub_auth_data2 aud2;
 	int rc;
 
-	if (!impl || !impl->gen_vec_auts)
-		return -ENOENT;
-
-	rc = impl->gen_vec_auts(vec, aud, auts, rand_auts, _rand);
+	rc = auth_data2auth_data2(&aud2, aud);
 	if (rc < 0)
 		return rc;
 
-	memcpy(vec->rand, _rand, sizeof(vec->rand));
+	rc = osmo_auth_gen_vec_auts2(vec, &aud2, auts, rand_auts, _rand);
+	if (aud->type == OSMO_AUTH_TYPE_UMTS) {
+		aud->u.umts.sqn = aud2.u.umts.sqn;
+		aud->u.umts.sqn_ms = aud2.u.umts.sqn_ms;
+	}
 
-	return 0;
+	return rc;
 }
 
 static const struct value_string auth_alg_vals[] = {
diff --git a/src/gsm/auth_milenage.c b/src/gsm/auth_milenage.c
index 4a430b3..36fbb06 100644
--- a/src/gsm/auth_milenage.c
+++ b/src/gsm/auth_milenage.c
@@ -28,7 +28,7 @@
  *  @{
  */
 
-static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data *aud, uint8_t *gen_opc)
+static const uint8_t *gen_opc_if_needed(const struct osmo_sub_auth_data2 *aud, uint8_t *gen_opc)
 {
 	int rc;
 
@@ -43,7 +43,7 @@
 }
 
 static int milenage_gen_vec(struct osmo_auth_vector *vec,
-			    struct osmo_sub_auth_data *aud,
+			    struct osmo_sub_auth_data2 *aud,
 			    const uint8_t *_rand)
 {
 	size_t res_len = sizeof(vec->res);
@@ -141,7 +141,7 @@
 }
 
 static int milenage_gen_vec_auts(struct osmo_auth_vector *vec,
-				 struct osmo_sub_auth_data *aud,
+				 struct osmo_sub_auth_data2 *aud,
 				 const uint8_t *auts, const uint8_t *rand_auts,
 				 const uint8_t *_rand)
 {
diff --git a/src/gsm/auth_xor.c b/src/gsm/auth_xor.c
index 81076bd..3552568 100644
--- a/src/gsm/auth_xor.c
+++ b/src/gsm/auth_xor.c
@@ -43,7 +43,7 @@
 
 /* 3GPP TS 34.108, section 8.1.2.1 */
 static int xor_gen_vec(struct osmo_auth_vector *vec,
-		       struct osmo_sub_auth_data *aud,
+		       struct osmo_sub_auth_data2 *aud,
 		       const uint8_t *_rand)
 {
 	uint8_t xdout[16], cdout[8];
@@ -125,7 +125,7 @@
 
 /* 3GPP TS 34.108, section 8.1.2.2 */
 static int xor_gen_vec_auts(struct osmo_auth_vector *vec,
-			    struct osmo_sub_auth_data *aud,
+			    struct osmo_sub_auth_data2 *aud,
 			    const uint8_t *auts,
 			    const uint8_t *rand_auts,
 			    const uint8_t *_rand)
diff --git a/src/gsm/auth_xor_2g.c b/src/gsm/auth_xor_2g.c
index 9efe5cc..1a96b4a 100644
--- a/src/gsm/auth_xor_2g.c
+++ b/src/gsm/auth_xor_2g.c
@@ -43,8 +43,8 @@
 
 /* GSM XOR-2G algorithm as specified in Annex 4 (A.4.1.2) of 3GPP TS 51.010-1. */
 static int xor2g_gen_vec(struct osmo_auth_vector *vec,
-		       struct osmo_sub_auth_data *aud,
-		       const uint8_t *_rand)
+			 struct osmo_sub_auth_data2 *aud,
+			 const uint8_t *_rand)
 {
 	uint8_t res1[16];
 
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 40f3e80..e03e014 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -578,7 +578,9 @@
 osmo_auth_alg_name;
 osmo_auth_alg_parse;
 osmo_auth_gen_vec;
+osmo_auth_gen_vec2;
 osmo_auth_gen_vec_auts;
+osmo_auth_gen_vec_auts2;
 osmo_auth_3g_from_2g;
 osmo_auth_load;
 osmo_auth_register;