[PATCH] libosmocore[master]: osmo_auth_gen_vec: UMTS auth: fix SQN increment

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

Neels Hofmeyr gerrit-no-reply at lists.osmocom.org
Tue Mar 14 02:15:39 UTC 2017


Review at  https://gerrit.osmocom.org/2049

osmo_auth_gen_vec: UMTS auth: fix SQN increment

So far we incremented SQN by 1, which doesn't match the procedures described in
3GPP TS 33.102. An IND separates a non-significant part of SQN from SEQ, and
the more significant SEQ needs to be incremented.

In OsmoHLR we furthermore want to use the "exception" suggested in annex C.3.4,
so that each HLR's client has a fixed i index. In other words, we will not
assign i cyclically, but increment SEQ for each auth vector consumer.

Add 'ind' and 'i' to the osmo_sub_auth_data.u.umts structure and increment SQN
accordingly.

Add a comment explaining the details.

Because 'ind' is still passed as zero, the milenage_test does not change its
behavior, which is a feature I want to clearly show in this patch. The test
will be expanded for the newly implemented SQN scheme in a subsequent patch.

The verbose output of osmo-auc-gen with invoked with --auts parameter about
SQN.MS and the next SQN needs adjustment. No longer show the next SQN, as it is
not relevant. Show only the current SQN used for tuple generation, and show
SQN.MS -- because we're still passing IND == 0, we can rely on single
increments and know SQN.MS is sqn - 1. With nonzero IND, we would not actually
know SQN.MS from the API, because we'll only be told what the next sensible SQN
is.

Change-Id: Ibc97e1736a797ffcbf8c1f7d41c5c4518f4e41bf
---
M include/osmocom/crypt/auth.h
M src/gsm/auth_milenage.c
M tests/auth/milenage_test.c
M utils/osmo-auc-gen.c
4 files changed, 72 insertions(+), 12 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/49/2049/1

diff --git a/include/osmocom/crypt/auth.h b/include/osmocom/crypt/auth.h
index 7c6072b..a9e252d 100644
--- a/include/osmocom/crypt/auth.h
+++ b/include/osmocom/crypt/auth.h
@@ -39,6 +39,8 @@
 			uint8_t amf[2];
 			uint64_t sqn;	/*!< sequence number */
 			int opc_is_op;	/*!< is the OPC field OPC (0) or OP (1) ? */
+			unsigned int ind; /*!< nr of bits not in SEQ, only SQN */
+			unsigned int i; /*!< SQN slot, i.e. (SEQ << IND) + i */
 		} umts;
 		struct {
 			uint8_t ki[16];	/*!< secret key */
diff --git a/src/gsm/auth_milenage.c b/src/gsm/auth_milenage.c
index e180762..626bac4 100644
--- a/src/gsm/auth_milenage.c
+++ b/src/gsm/auth_milenage.c
@@ -32,10 +32,66 @@
 	size_t res_len = sizeof(vec->res);
 	uint64_t next_sqn;
 	uint8_t sqn[6];
+	uint64_t ind_mask;
+	uint64_t seq_1;
 	int rc;
 
+	/* Determine next SQN, according to 3GPP TS 33.102:
+	 * SQN consists of SEQ and a lower significant part of IND bits:
+	 *
+	 * |----------SEQ------------|
+	 * |------------------------SQN-----------|
+	 *                           |<---------->|<-- IND
+	 *
+	 * The IND part is used as "slots": e.g. a given HLR client will always
+	 * get the same IND part, called i here, with incrementing SEQ. In the
+	 * USIM, each IND slot enforces that its SEQ are used in ascending
+	 * order -- as long as that constraint is satisfied, the SQN may jump
+	 * forwards and backwards. For example, for IND == 5, asking the USIM
+	 * for SQN = 32, 64, 33 is allowed, because 32 and 64 are SEQ || (i ==
+	 * 0), and though 33 is below 64, it is i == 1 and allowed. Not allowed
+	 * would be 32, 96, 64, because 64 would go backwards after 96, both
+	 * being i == 0.
+	 *
+	 * From the last used SQN, we want to increment SEQ + 1, and then pick
+	 * the matching IND part.
+	 *
+	 * IND is suggested in TS 33.102 as 5. SQN is 48 bits long. If IND is
+	 * passed too large here, the algorithms will break down. But at which
+	 * point should we return an error? A sane limit seems to be IND == 10,
+	 * but to protect against failure, limiting IND to 28 is enough, 28
+	 * being the number of bits to choose for the delta in 33.102, which is
+	 * discussed to still require 2^15 > 32000 authentications to wrap the
+	 * SQN back to the start.
+	 *
+	 * Note that if a caller with i == 1 generates N vectors, the SQN
+	 * stored after this will reflect SEQ + N. If then another caller with
+	 * i == 2 generates another N vectors, this will then use SEQ + N
+	 * onwards and end up with SEQ + N + N. In other words, most of each
+	 * SEQ's IND slots will remain unused. When looking at SQN being 48
+	 * bits wide, after dropping IND (say 5) bits from it, we will still
+	 * have a sequence range of 2^43 = 8.8e12, eight trillion sequences,
+	 * which is large enough to not bother further. With the maximum IND of
+	 * 28 enforced below, we still get more than 1 million sequences, which
+	 * is also sufficiently large.
+	 *
+	 * An IND of zero may be passed from legacy callers that are not aware
+	 * of the IND extension. For these, below algorithm works out as
+	 * before, simply incrementing SQN by 1. Assume that i is also 0 then.
+	 */
+
+	if (aud->u.umts.ind > 28)
+		return -2;
+
+	seq_1 = 1LL << aud->u.umts.ind;
+	ind_mask = ~(seq_1 - 1);
+
+	/* the i index must not affect the SEQ part */
+	if (aud->u.umts.i > seq_1)
+		return -3;
+
 	/* keep the incremented SQN local until gsm_milenage() succeeded. */
-	next_sqn = aud->u.umts.sqn + 1;
+	next_sqn = ((aud->u.umts.sqn + seq_1) & ind_mask) + aud->u.umts.i;
 
 	osmo_store64be_ext(next_sqn, sqn, 6);
 	milenage_generate(aud->u.umts.opc, aud->u.umts.amf, aud->u.umts.k,
@@ -78,6 +134,8 @@
 	if (rc < 0)
 		return rc;
 
+	/* Update our "largest used SQN" from the USIM -- milenage_gen_vec()
+	 * below will increment SQN. */
 	aud->u.umts.sqn = osmo_load64be_ext(sqn_out, 6) >> 16;
 
 	return milenage_gen_vec(vec, aud, _rand);
diff --git a/tests/auth/milenage_test.c b/tests/auth/milenage_test.c
index 405da65..f24a9ef 100644
--- a/tests/auth/milenage_test.c
+++ b/tests/auth/milenage_test.c
@@ -37,6 +37,8 @@
 			 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f },
 		.amf = { 0x00, 0x00 },
 		.sqn = 0x21,
+		.ind = 0,
+		.i = 0,
 	},
 };
 
@@ -79,6 +81,9 @@
 #endif
 	memset(vec, 0, sizeof(*vec));
 
+	/* With IND == 0, this is the legacy mode of incrementing SQN by 1.
+	 * sqn == 0x21 == 33, so the SQN used to generate the vector is
+	 * sqn + 1 == 34. */
 	rc = osmo_auth_gen_vec(vec, &test_aud, _rand);
 	if (rc < 0) {
 		fprintf(stderr, "error generating auth vector\n");
@@ -90,6 +95,7 @@
 	const uint8_t auts[14] = { 0x87, 0x11, 0xa0, 0xec, 0x9e, 0x16, 0x37, 0xdf,
 			     0x17, 0xf8, 0x0b, 0x38, 0x4e, 0xe4 };
 
+	/* Invoking with IND == 0, the next SQN after 31 is 32. */
 	rc = osmo_auth_gen_vec_auts(vec, &test_aud, auts, _rand, _rand);
 	if (rc < 0) {
 		printf("AUTS failed\n");
diff --git a/utils/osmo-auc-gen.c b/utils/osmo-auc-gen.c
index f0cfa79..237f076 100644
--- a/utils/osmo-auc-gen.c
+++ b/utils/osmo-auc-gen.c
@@ -260,21 +260,15 @@
 	else
 		dump_auth_vec(vec);
 
-	/* Print SQN from AUTS. It makes sense to print actually three SQN
-	 * to clarify:
-	 * After recovering SQN.MS from AUTS, milenage_gen_vec_auts() does:
-	 *   aud->u.umts.sqn = 1 + (osmo_load64be_ext(sqn_out, 6) >> 16);
-	 * Then calls milenage_gen_vec(), which, after it is done, does:
-	 *   aud->u.umts.sqn++;
+	/* Print SQN from AUTS. It makes sense to print actually two SQN to
+	 * clarify: after recovering SQN.MS from AUTS, milenage_gen_vec() does
+	 * aud->u.umts.sqn++, so to show SQN.MS we need to undo the ++.
 	 */
 	if (auts_is_set)
 		printf("AUTS success: SQN.MS = %" PRIu64
-		       ", generated vector with SQN = %" PRIu64
-		       ", next SQN = %" PRIu64 "\n",
-		       test_aud.u.umts.sqn - 2,
+		       ", generated vector with SQN = %" PRIu64 "\n",
 		       test_aud.u.umts.sqn - 1,
-		       test_aud.u.umts.sqn
-		       );
+		       test_aud.u.umts.sqn);
 
 	exit(0);
 }

-- 
To view, visit https://gerrit.osmocom.org/2049
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibc97e1736a797ffcbf8c1f7d41c5c4518f4e41bf
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofmeyr at sysmocom.de>



More information about the gerrit-log mailing list