aboutsummaryrefslogtreecommitdiffstats
path: root/src/libmsc
diff options
context:
space:
mode:
authorNeels Hofmeyr <neels@hofmeyr.de>2021-06-09 22:26:11 +0200
committerNeels Hofmeyr <neels@hofmeyr.de>2021-06-24 01:27:47 +0200
commit6ce2edcac1b457243583b541f22da2a05c9452b7 (patch)
tree5513bf8fb9102a147028c88b0e5c381799243e06 /src/libmsc
parenta7f8020bfa4ba472439bb4a55ca756c2f616eb9f (diff)
support A5/4 in Cipher Mode Command
Diffstat (limited to 'src/libmsc')
-rw-r--r--src/libmsc/msc_a.c10
-rw-r--r--src/libmsc/ran_msg_a.c26
2 files changed, 33 insertions, 3 deletions
diff --git a/src/libmsc/msc_a.c b/src/libmsc/msc_a.c
index 05030d307..4c38c562d 100644
--- a/src/libmsc/msc_a.c
+++ b/src/libmsc/msc_a.c
@@ -292,6 +292,14 @@ int msc_a_vlr_set_cipher_mode(void *_msc_a, bool umts_aka, bool retrieve_imeisv)
return msc_a_ran_enc_ciphering(msc_a, umts_aka, retrieve_imeisv);
}
+static uint8_t filter_a5(uint8_t a5_mask, bool umts_aka)
+{
+ /* With GSM AKA: allow A5/0, 1, 3 = 0b00001011 = 0xb.
+ * UMTS aka: allow A5/0, 1, 3, 4 = 0b00011011 = 0x1b.
+ */
+ return a5_mask & (umts_aka ? 0x1b : 0x0b);
+}
+
static int msc_a_ran_enc_ciphering(struct msc_a *msc_a, bool umts_aka, bool retrieve_imeisv)
{
struct gsm_network *net;
@@ -321,7 +329,7 @@ static int msc_a_ran_enc_ciphering(struct msc_a *msc_a, bool umts_aka, bool retr
.geran = {
.umts_aka = umts_aka,
.retrieve_imeisv = retrieve_imeisv,
- .a5_encryption_mask = net->a5_encryption_mask,
+ .a5_encryption_mask = filter_a5(net->a5_encryption_mask, umts_aka),
/* for ran_a.c to store the GERAN key that is actually used */
.chosen_key = &msc_a->geran_encr,
diff --git a/src/libmsc/ran_msg_a.c b/src/libmsc/ran_msg_a.c
index 61e5024b9..106c2dff0 100644
--- a/src/libmsc/ran_msg_a.c
+++ b/src/libmsc/ran_msg_a.c
@@ -25,6 +25,7 @@
#include <osmocom/core/byteswap.h>
#include <osmocom/crypt/auth.h>
+#include <osmocom/crypt/kdf.h>
#include <osmocom/gsm/tlv.h>
#include <osmocom/gsm/gsm0808.h>
@@ -1019,6 +1020,9 @@ static int a5_n_to_gsm0808_chosen_enc_alg(uint8_t *dst, int a5_n)
case 3:
*dst = GSM0808_ALG_ID_A5_3;
return 0;
+ case 4:
+ *dst = GSM0808_ALG_ID_A5_4;
+ return 0;
default:
return -ENOTSUP;
}
@@ -1078,21 +1082,39 @@ static struct msgb *ran_a_make_cipher_mode_command(struct osmo_fsm_inst *fi, con
/* In case of UMTS AKA, the Kc for ciphering must be derived from the 3G auth
* tokens. vec->kc was calculated from the GSM algorithm and is not
* necessarily a match for the UMTS AKA tokens. */
- if (cm->geran.umts_aka)
+ if (cm->geran.umts_aka) {
+ int i;
osmo_auth_c3(ei->key, cm->vec->ck, cm->vec->ik);
- else
+
+ for (i = 0; i < ei->perm_algo_len; i++) {
+ if (ei->perm_algo[i] != GSM0808_ALG_ID_A5_4)
+ continue;
+ /* A5/4 is included, so need to generate Kc128 */
+ osmo_kdf_kc128(cm->vec->ck, cm->vec->ik, cmc.kc128);
+ cmc.kc128_present = true;
+ break;
+ }
+ } else {
memcpy(ei->key, cm->vec->kc, sizeof(cm->vec->kc));
+ }
ei->key_len = sizeof(cm->vec->kc);
/* Store chosen GERAN key where the caller asked it to be stored.
* alg_id remains unknown until we receive a Cipher Mode Complete from the BSC */
if (cm->geran.chosen_key) {
+ *cm->geran.chosen_key = (struct geran_encr){0};
+
if (ei->key_len > sizeof(cm->geran.chosen_key->key)) {
LOG_RAN_A_ENC(fi, LOGL_ERROR, "Chosen key is larger than I can store\n");
return NULL;
}
memcpy(cm->geran.chosen_key->key, ei->key, ei->key_len);
cm->geran.chosen_key->key_len = ei->key_len;
+
+ if (cmc.kc128_present) {
+ memcpy(cm->geran.chosen_key->kc128, cmc.kc128, 16);
+ cm->geran.chosen_key->kc128_present = true;
+ }
}
LOG_RAN_A_ENC(fi, LOGL_DEBUG, "Tx BSSMAP CIPHER MODE COMMAND to BSC, %u ciphers (%s) key %s\n",