From eced97dd17580a738f0757fcf4b70774e76d4198 Mon Sep 17 00:00:00 2001 From: wbokslag Date: Wed, 26 Jul 2023 17:45:32 +0200 Subject: [PATCH] Added first steps towards tetra crypto support A crypto folder has been added containing only a single c and h file at the moment. These files contain structs and high level functionality pertaining to TETRA crypto support which can be added in future patches. Change-Id: I63bc712630ae5dbaa049c129d456f7aef5bda863 --- src/Makefile | 5 +- src/crypto/tetra_crypto.c | 233 ++++++++++++++++++++++++++++++++++++++ src/crypto/tetra_crypto.h | 107 +++++++++++++++++ 3 files changed, 344 insertions(+), 1 deletion(-) create mode 100644 src/crypto/tetra_crypto.c create mode 100644 src/crypto/tetra_crypto.h diff --git a/src/Makefile b/src/Makefile index 41250a9..9b12484 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,11 +16,14 @@ libosmo-tetra-phy.a: phy/tetra_burst_sync.o phy/tetra_burst.o libosmo-tetra-mac.a: lower_mac/tetra_conv_enc.o lower_mac/tch_reordering.o tetra_tdma.o lower_mac/tetra_scramb.o lower_mac/tetra_rm3014.o lower_mac/tetra_interleave.o lower_mac/crc_simple.o tetra_common.o lower_mac/viterbi.o lower_mac/viterbi_cch.o lower_mac/viterbi_tch.o lower_mac/tetra_lower_mac.o tetra_upper_mac.o tetra_mac_pdu.o tetra_llc_pdu.o tetra_llc.o tetra_mle_pdu.o tetra_mle.o tetra_mm_pdu.o tetra_cmce_pdu.o tetra_sndcp_pdu.o tetra_gsmtap.o tuntap.o $(AR) r $@ $^ +libosmo-tetra-crypto.a: crypto/tetra_crypto.o + $(AR) r $@ $^ + float_to_bits: float_to_bits.o crc_test: crc_test.o tetra_common.o libosmo-tetra-mac.a -tetra-rx: tetra-rx.o libosmo-tetra-phy.a libosmo-tetra-mac.a +tetra-rx: tetra-rx.o libosmo-tetra-phy.a libosmo-tetra-mac.a libosmo-tetra-crypto.a conv_enc_test: conv_enc_test.o testpdu.o libosmo-tetra-phy.a libosmo-tetra-mac.a diff --git a/src/crypto/tetra_crypto.c b/src/crypto/tetra_crypto.c new file mode 100644 index 0000000..9f80a49 --- /dev/null +++ b/src/crypto/tetra_crypto.c @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "tetra_crypto.h" + +struct tetra_crypto_database _tcdb, *tcdb = &_tcdb; +struct tetra_crypto_state _tcs, *tcs = &_tcs; + +static const struct value_string tetra_key_types[] = { + { KEYTYPE_UNDEFINED, "UNDEFINED" }, + { KEYTYPE_DCK, "DCK" }, + { KEYTYPE_MGCK, "MGCK" }, + { KEYTYPE_CCK_SCK, "CCK/SCK" }, + { KEYTYPE_GCK, "GCK" }, + { 0, NULL } +}; + +const char *tetra_get_key_type_name(enum tetra_key_type key_type) +{ + return get_value_string(tetra_key_types, key_type); +} + +static const struct value_string tetra_tea_types[] = { + { UNKNOWN, "UNKNOWN" }, + { KSG_TEA1, "TEA1" }, + { KSG_TEA2, "TEA2" }, + { KSG_TEA3, "TEA3" }, + { KSG_TEA4, "TEA4" }, + { KSG_TEA5, "TEA5" }, + { KSG_TEA6, "TEA6" }, + { KSG_TEA7, "TEA7" }, + { KSG_PROPRIETARY, "PROPRIETARY" }, + { 0, NULL } +}; + +const char *tetra_get_ksg_type_name(enum tetra_ksg_type ksg_type) +{ + if (ksg_type >= KSG_PROPRIETARY) + return tetra_tea_types[KSG_PROPRIETARY].str; + else + return get_value_string(tetra_tea_types, ksg_type); +} + +static const struct value_string tetra_security_classes[] = { + { NETWORK_CLASS_UNDEFINED, "CLASS_UNDEFINED" }, + { NETWORK_CLASS_1, "CLASS_1" }, + { NETWORK_CLASS_2, "CLASS_2" }, + { NETWORK_CLASS_3, "CLASS_3" }, + { 0, NULL } +}; + +const char *tetra_get_security_class_name(uint8_t pdut) +{ + return get_value_string(tetra_security_classes, pdut); +} + +void tetra_crypto_init(void) +{ + /* Initialize tetra_crypto_state */ + tcs->mnc = -1; + tcs->mcc = -1; + tcs->cck_id = -1; + tcs->hn = -1; + tcs->la = -1; + tcs->cc = -1; + tcs->cck = 0; + tcs->network = 0; + + /* Initialize tetra_crypto_database */ + tcdb->num_keys = 0; + tcdb->num_nets = 0; + tcdb->keys = talloc_array(NULL, struct tetra_key, TCDB_ALLOC_BLOCK_SIZE); + tcdb->nets = talloc_array(NULL, struct tetra_netinfo, TCDB_ALLOC_BLOCK_SIZE); + + if (!tcdb->keys || !tcdb->nets) { + fprintf(stderr, "couldn't allocate memory for tetra_crypto_database\n"); + exit(1); + } +} + +char *dump_key(struct tetra_key *k) +{ + static char pbuf[1024]; + + int c = snprintf(pbuf, sizeof(pbuf), "MCC %4d MNC %4d key_type %02X", + k->mcc, k->mnc, k->key_type); + + if (k->key_type & (KEYTYPE_DCK | KEYTYPE_MGCK)) + c += snprintf(pbuf + c, sizeof(pbuf) - c, " addr: %8d", k->addr); + + if (k->key_type & (KEYTYPE_CCK_SCK)) + c += snprintf(pbuf + c, sizeof(pbuf) - c, " key_num: %4d", k->key_num); + + c += snprintf(pbuf + c, sizeof(pbuf) - c, ": "); + for (int i = 0; i < 10; i++) + snprintf(pbuf + c + 2*i, sizeof(pbuf)-c-2*i, "%02X", k->key[i]); + + return pbuf; +} + +char *dump_network_info(struct tetra_netinfo *network) +{ + static char pbuf[1024]; + snprintf(pbuf, sizeof(pbuf), "MCC %4d MNC %4d ksg_type %d security_class %d", network->mcc, network->mnc, network->ksg_type, network->security_class); + return pbuf; +} + +uint32_t tea_build_iv(struct tetra_tdma_time *tm, uint16_t hn, uint8_t dir) +{ + assert(1 <= tm->tn && tm->tn <= 4); + assert(1 <= tm->fn && tm->fn <= 18); + assert(1 <= tm->mn && tm->mn <= 60); + assert(0 <= tm->hn && tm->hn <= 0xFFFF); + assert(0 <= dir && dir <= 1); // 0 = downlink, 1 = uplink + return ((tm->tn - 1) | (tm->fn << 2) | (tm->mn << 7) | ((hn & 0x7FFF) << 13) | (dir << 28)); +} + +int decrypt_identity(struct tetra_addr *addr) +{ + return 0; +} + +int decrypt_mac_element(struct tetra_tmvsap_prim *tmvp, struct tetra_key *key, int l1_len, int tmpdu_offset) +{ + return 0; +} + +int decrypt_voice_timeslot(struct tetra_tdma_time *tdma_time, int16_t *type1_block) +{ + return 0; +} + +int load_keystore(char *tetra_keyfile) +{ + return 0; +} + +void unload_keystore(void) +{ + /* Free database and set ptr to null */ + free(tcdb->keys); + free(tcdb->nets); + + /* Reset any lingering pointers */ + tetra_crypto_init(); +} + +struct tetra_key *get_key_by_addr(int addr, enum tetra_key_type key_type) +{ + for (int i = 0; i < tcdb->num_keys; i++) { + struct tetra_key *key = &tcdb->keys[i]; + if (key->mnc == tcs->mnc && + key->mcc == tcs->mcc && + key->addr == addr && + (key->key_type & key_type)) { + return key; + } + } + return 0; +} + +struct tetra_key *get_ksg_key(int addr) +{ + /* TETRA standard part 7 Clause 6.2: + -------------------------------------------- + Auth Encr GCK DCK + Class 1: ? - - - + Class 2: ? + ? - + Class 3: + + ? + + -------------------------------------------- + */ + + if (!tcs->network) + return 0; + + /* FIXME: add support for ISSI/GSSI range definitions and GCK bindings */ + /* FIXME: add support for ISSI-bound DCK keys in class 3 networks */ + + return tcs->cck; +} + +void update_current_network(int mcc, int mnc) +{ + // Update globals + tcs->mcc = mcc; + tcs->mnc = mnc; + + // Network changed, update reference to current network + tcs->network = 0; + for (int i = 0; i < tcdb->num_nets; i++) { + struct tetra_netinfo *network = &tcdb->nets[i]; + if (network->mnc == tcs->mnc && network->mcc == tcs->mcc) { + tcs->network = network; + break; + } + } + + update_current_cck(); +} + +void update_current_cck(void) +{ + printf("\ntetra_crypto: update_current_cck invoked cck %d mcc %d mnc %d\n", tcs->cck_id, tcs->mcc, tcs->mnc); + tcs->cck = 0; + + for (int i = 0; i < tcdb->num_keys; i++) { + struct tetra_key *key = &tcdb->keys[i]; + /* TODO FIXME consider selecting CCK or SCK key type based on network config */ + if (key->mcc == tcs->mcc && key->mnc == tcs->mnc && key->key_num == tcs->cck_id) { + if (key->key_type == KEYTYPE_CCK_SCK) { + tcs->cck = key; + printf("tetra_crypto: Set new current_cck %d (type: full)\n", i); + break; + } + } + } +} + +struct tetra_netinfo *get_network_info(int mcc, int mnc) +{ + for (int i = 0; i < tcdb->num_nets; i++) { + if (tcdb->nets[i].mcc == mcc && tcdb->nets[i].mnc == mnc) + return &tcdb->nets[i]; + } + return 0; +} diff --git a/src/crypto/tetra_crypto.h b/src/crypto/tetra_crypto.h new file mode 100644 index 0000000..2aabbc4 --- /dev/null +++ b/src/crypto/tetra_crypto.h @@ -0,0 +1,107 @@ +#ifndef TETRA_CRYPTO_H +#define TETRA_CRYPTO_H + +#include +#include + +#include "../tetra_prim.h" +#include "../tetra_tdma.h" +#include "../tetra_mac_pdu.h" + +#define TCDB_ALLOC_BLOCK_SIZE 16 + +enum tetra_key_type { + KEYTYPE_UNDEFINED = 0x00, + + /* Keytypes usable as ECK after applying TB5 */ + KEYTYPE_DCK = 0x01, + KEYTYPE_MGCK = 0x02, + KEYTYPE_CCK_SCK = 0x04, /* SCK in class 2, CCK in class 3 networks */ + + /* Can be combined with SCK/CCK to get MGCK */ + KEYTYPE_GCK = 0x20, +}; + +enum tetra_ksg_type { + UNKNOWN = 0, + KSG_TEA1 = 1, /* KSG number value 0 */ + KSG_TEA2 = 2, /* KSG number value 1 */ + KSG_TEA3 = 3, /* KSG number value 2 */ + KSG_TEA4 = 4, /* KSG number value 3 */ + KSG_TEA5 = 5, /* KSG number value 4 */ + KSG_TEA6 = 6, /* KSG number value 5 */ + KSG_TEA7 = 7, /* KSG number value 6 */ + KSG_PROPRIETARY = 8, /* KSG number values 9-15 */ +}; + +enum tetra_security_class { + NETWORK_CLASS_UNDEFINED = 0, + NETWORK_CLASS_1 = 1, + NETWORK_CLASS_2 = 2, + NETWORK_CLASS_3 = 3, +}; + +struct tetra_netinfo { + uint32_t mcc; + uint32_t mnc; + enum tetra_ksg_type ksg_type; /* KSG used in this network */ + enum tetra_security_class security_class; /* Security class this network employs */ +}; + +struct tetra_key { + uint32_t index; /* position in key list */ + uint32_t mnc; + uint32_t mcc; + enum tetra_key_type key_type; + uint32_t key_num; /* key_vn or whatever key numbering system is relevant for this key type */ + uint32_t addr; /* ISSI, GSSI, or whatever address is relevant for this key type */ + uint8_t key[16]; /* Currently keys are 80 bits, but we may want to support 128-bit keys as well */ + struct tetra_netinfo *network_info; /* Network with which the key is associated */ +}; + +struct tetra_crypto_database { + uint32_t num_keys; + struct tetra_key *keys; + uint32_t num_nets; + struct tetra_netinfo *nets; +}; +extern struct tetra_crypto_database *tcdb; + +struct tetra_crypto_state { + int mnc; /* Network info for selecting keys */ + int mcc; /* Network info for selecting keys */ + int cck_id; /* CCK or SCK id used on network (from SYSINFO) */ + int hn; /* Hyperframe number for IV (from SYSINFO) */ + int la; /* location area for TB5 */ + int cn; /* carrier number for TB5. WARNING: only set correctly if tuned to main control channel */ + int cc; /* colour code for TB5 */ + struct tetra_netinfo *network; /* pointer to network info struct loaded from file */ + struct tetra_key *cck; /* pointer to CCK or SCK for this network and version (from SYSINFO) */ +}; +extern struct tetra_crypto_state *tcs; + +const char *tetra_get_key_type_name(enum tetra_key_type); +const char *tetra_get_ksg_type_name(enum tetra_ksg_type); +const char *tetra_get_security_class_name(uint8_t pdut); + +/* Key loading / unloading */ +void tetra_crypto_init(void); +int load_keystore(char *filename); +void unload_keystore(void); + +/* Keystream generation and decryption functions */ +uint32_t tea_build_iv(struct tetra_tdma_time *tm, uint16_t hn, uint8_t dir); +int decrypt_identity(struct tetra_addr *addr); +int decrypt_mac_element(struct tetra_tmvsap_prim *tmvp, struct tetra_key *key, int l1_len, int tmpdu_offset); +int decrypt_voice_timeslot(struct tetra_tdma_time *tdma_time, int16_t *type1_bits); + +/* Key selection and crypto state management */ +struct tetra_key *get_ksg_key(int addr); +struct tetra_netinfo *get_network_info(int mcc, int mnc); +void update_current_network(int mcc, int mnc); +void update_current_cck(void); + +char *dump_network_info(struct tetra_netinfo *network); +char *dump_key(struct tetra_key *k); + +#endif /* TETRA_CRYPTO_H */