Status of GEA issues in OsmoSGSN

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/osmocom-net-gprs@lists.osmocom.org/.

Harald Welte laforge at gnumonks.org
Tue May 31 12:54:41 UTC 2016


Dear all,

Deater has been investigating which issues currently prevent us from
using GEA in OsmoSGSN.  His mail has been sitting in my inbox too long,
and hence I'm forwarding his results here to the mailing list for
further discussion / follow-up.

Regards,
	Harald
-- 
- Harald Welte <laforge at gnumonks.org>           http://laforge.gnumonks.org/
============================================================================
"Privacy in residential applications is a desirable marketing option."
                                                  (ETSI EN 300 175-7 Ch. A6)
-------------- next part --------------
GEA issues (May 2016):
***********************

libosmo-crypt-gea12/src/osmocom.c
---------------------------------

GEA1_stream() and GEA2_stream() operate with bits and not bytes. This
is how they have to be used:

static int gea1_run(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
		     enum gprs_cipher_direction direction)
{
	uint8_t dir;
	uint8_t bits_input[32];
	uint8_t bits_kc[64];
	int i;

	// get 32 input bits
  
	for(i = 0; i < 32; i++)
	{
		bits_input[i] = (BYTE)((iv >> i) & 1);
	}
  
	// get 64 key bits
  
	for(i = 0; i < 64; i++)
	{
	#if 0 // reverse Kc byte order ("reverse" compared to Calypso test code)
		bits_kc[i] = (BYTE)((kc >> i) & 1);
	#else // "normal" Kc byte order (whatever "normal" is)
		bits_kc[i] = (BYTE)((kc >> (((7 - (i / 8)) * 8) + (i % 8))) & 1);
	#endif
	}

	if (direction == GPRS_CIPH_MS2SGSN)
		dir = DIRECTION_UPLINK;
	else
		dir = DIRECTION_DOWNLINK;

	GEA1_stream(bits_kc, bits_input, dir, len, out);

	return 0;
}

static int gea2_run(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
		     enum gprs_cipher_direction direction)
{
	uint8_t dir;
	uint8_t bits_input[32];
	uint8_t bits_kc[64];
	int i;

	// get 32 input bits
  
	for(i = 0; i < 32; i++)
	{
		bits_input[i] = (BYTE)((iv >> i) & 1);
	}
  
	// get 64 key bits
  
	for(i = 0; i < 64; i++)
	{
	#if 0 // reverse Kc byte order ("reverse" compared to Calypso test code)
		bits_kc[i] = (BYTE)((kc >> i) & 1);
	#else // "normal" Kc byte order (whatever "normal" is)
		bits_kc[i] = (BYTE)((kc >> (((7 - (i / 8)) * 8) + (i % 8))) & 1);
	#endif
	}

	if (direction == GPRS_CIPH_MS2SGSN)
		dir = DIRECTION_UPLINK;
	else
		dir = DIRECTION_DOWNLINK;

	GEA2_stream(bits_kc, bits_input, dir, len, out);

	return 0;
}


libosmo-crypt-a53/src/osmocom.c
-------------------------------

The length of the key is in bits and not in bytes:

static int gea3_run(uint8_t *out, uint16_t len, uint64_t kc, uint32_t iv,
		     enum gprs_cipher_direction direction)
{
	uint8_t dir;

	if (direction == GPRS_CIPH_MS2SGSN)
		dir = 0;
	else
		dir = 1;

	GEA3((uint8_t *)&kc, sizeof(kc) * 8, iv, dir, out, len);

	return 0;
}


openbsc/openbsc/src/gprs/gprs_llc.c
-----------------------------------

Frame encryption in gprs_llc_tx_ui() doesn't work:

	/* encrypt information field + FCS, if needed! */
	if (lle->llme->algo != GPRS_ALGO_GEA0) {
		uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
		uint16_t crypt_len = (fcs + 3) - (llch + 3);
		uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
		uint32_t iv;
		int rc, i;
		uint64_t kc = *(uint64_t *)&lle->llme->kc;

		/* Compute the 'Input' Paraemeter */
		iv = gprs_cipher_gen_input_ui(iov_ui, sapi, nu, oc);

		/* Compute the keystream that we need to XOR with the data */
		rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
				     kc, iv, GPRS_CIPH_SGSN2MS);
		if (rc < 0) {
			LOGP(DLLC, LOGL_ERROR, "Error crypting UI frame: %d\n", rc);
			msgb_free(msg);
			return rc;
		}

		/* Mark frame as encrypted */
		#if 1 // Dieter: recalculate checksum after properly setting the flag
		llch[2] |= 0x02;
		fcs_calc = gprs_llc_fcs(llch, fcs - llch);
		fcs[0] = fcs_calc & 0xff;
		fcs[1] = (fcs_calc >> 8) & 0xff;
		fcs[2] = (fcs_calc >> 16) & 0xff;
		#endif

		/* XOR the cipher output with the information field + FCS */
		for (i = 0; i < crypt_len; i++)
			*(llch + 3 + i) ^= cipher_out[i];

		/* Mark frame as encrypted */
		#if 0 // Dieter: This won't work
		ctrl[1] |= 0x02;
		#endif
	}

Frame decryption in gprs_llc_rcvmsg() doesn't work:

	/* decrypt information field + FCS, if needed! */
	if (llhp.is_encrypted) {
		uint32_t iov_ui = 0; /* FIXME: randomly select for TLLI */
		uint16_t crypt_len = llhp.data_len + 3;
		uint8_t cipher_out[GSM0464_CIPH_MAX_BLOCK];
		uint32_t iv;
		uint64_t kc = *(uint64_t *)&lle->llme->kc;
		int rc, i;

		if (lle->llme->algo == GPRS_ALGO_GEA0) {
			LOGP(DLLC, LOGL_NOTICE, "encrypted frame for LLC that "
				"has no KC/Algo! Dropping.\n");
			return 0;
		}

		iv = gprs_cipher_gen_input_ui(iov_ui, lle->sapi, llhp.seq_tx,
						lle->oc_ui_recv);
		rc = gprs_cipher_run(cipher_out, crypt_len, lle->llme->algo,
				     kc, iv, GPRS_CIPH_MS2SGSN);
		if (rc < 0) {
			LOGP(DLLC, LOGL_ERROR, "Error decrypting frame: %d\n",
			     rc);
			return rc;
		}

		/* XOR the cipher output with the information field + FCS */
		for (i = 0; i < crypt_len; i++)
			*(llhp.data + i) ^= cipher_out[i];

		#if 1 // Dieter: FCS is encrypted, gprs_llc_hdr_parse() should be called again
		llhp.fcs = *(llhp.data + crypt_len - 3);
		llhp.fcs |= *(llhp.data + crypt_len - 2) << 8;
		llhp.fcs |= *(llhp.data + crypt_len - 1) << 16;
		#endif
	} else {
		if (lle->llme->algo != GPRS_ALGO_GEA0) {
			LOGP(DLLC, LOGL_NOTICE, "unencrypted frame for LLC "
				"that is supposed to be encrypted. Dropping.\n");
			return 0;
		}
	}


Open issues:
############

openbsc/openbsc/src/gprs/gprs_llc.c
-----------------------------------

When creating an LLME "on the fly" in lle_for_rx_by_tlli_sapi() kc and algo
of the LLME have to be set to the ones used for the subscriber.


In general:
-----------

Management of GEA algorithm and KC for a subscriber. The algorithm has to
be selected according to the capabilities of the MS. Some affected functions
are gprs_llgmm_assign() and gsm48_tx_gmm_auth_ciph_req(). 


Handling of IOV (initialisation vector), currently most of the time IOV is
set to 0, but gprs_llgmm_reset() and gprs_llgmm_reset_oldmsg() use a random
IOV and would probably cause problems when called.


Using uint64_t as the type for Kc in the GEA libraries is not a good choice,
uint8_t[8] would be better and cause less troubles (e.g. endian issues and
incompatibility with the test vectors, where the order of Kc has to be
reversed). Attention: this change might require to adjust the byte order of
Kc in the GEA libraries.


More information about the osmocom-net-gprs mailing list