wbokslag has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-tetra/+/33943 )
Change subject: Added first steps towards tetra crypto support ......................................................................
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 --- M src/Makefile A src/crypto/tetra_crypto.c A src/crypto/tetra_crypto.h 3 files changed, 424 insertions(+), 1 deletion(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-tetra refs/changes/43/33943/1
diff --git a/src/Makefile b/src/Makefile index 41250a9..9b12484 100644 --- a/src/Makefile +++ b/src/Makefile @@ -16,11 +16,14 @@ 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..c5e78e6 --- /dev/null +++ b/src/crypto/tetra_crypto.c @@ -0,0 +1,289 @@ +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +#include <osmocom/core/utils.h> +#include <tetra_mac_pdu.h> +#include <phy/tetra_burst.h> + +#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_SCK, "SCK" }, + { KEYTYPE_CCK, "CCK" }, + { KEYTYPE_GCK, "GCK" }, + { 0, NULL } +}; + +const char *tetra_get_key_type_name(int key_type) +{ + return get_value_string(tetra_key_types, key_type); +} + +static const struct value_string tetra_tea_types[] = { + { UNKNOWN, "UNKNOWN" }, + { TEA1, "TEA1" }, + { TEA2, "TEA2" }, + { TEA3, "TEA3" }, + { TEA4, "TEA4" }, + { TEA5, "TEA5" }, + { TEA6, "TEA6" }, + { TEA7, "TEA7" }, + { PROPRIETARY, "PROPRIETARY" }, + { 0, NULL } +}; + +const char *tetra_get_tea_type_name(int tea_type) +{ + if (tea_type > 7) { + return tetra_tea_types[PROPRIETARY].str; // proprietary + } else { + return get_value_string(tetra_tea_types, tea_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" }, + { NETWORK_CLASS_3G, "CLASS_3G" }, + { 0, NULL } +}; + +const char *tetra_get_security_class_name(uint8_t pdut) +{ + return get_value_string(tetra_security_classes, pdut); +} + +void tetra_crypto_init() +{ + /* Initialize tetra_crypto_state */ + tcs->mnc = -1; + tcs->mcc = -1; + tcs->network = 0; + tcs->cck_id = -1; + tcs->cck = 0; + tcs->hn = -1; + tcs->la = -1; + tcs->cc = -1; + + /* Initialize tetra_crypto_database */ + tcdb->num_keys = 0; + tcdb->num_nets = 0; + tcdb->num_ranges = 0; + tcdb->keys = calloc(TCDB_ALLOC_BLOCK_SIZE, sizeof(struct tetra_key)); + tcdb->nets = calloc(TCDB_ALLOC_BLOCK_SIZE, sizeof(struct network_info)); + tcdb->ranges = calloc(TCDB_ALLOC_BLOCK_SIZE, sizeof(struct address_range)); + if (!tcdb->keys || !tcdb->nets || !tcdb->ranges) { + 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 | KEYTYPE_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 network_info *network) +{ + static char pbuf[1024]; + snprintf(pbuf, sizeof(pbuf), "MCC %4d MNC %4d tea_type %d security_class %d", network->mcc, network->mnc, network->tea_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); + free(tcdb->ranges); + + /* Reset any lingering pointers */ + tetra_crypto_init(); +} + +int is_gssi(int addr) +{ + for (int i = 0; i < tcdb->num_ranges; i++) { + if (addr >= tcdb->ranges[i].start && addr < tcdb->ranges[i].end) { + return 1; + } + } + return 0; +} + +int is_issi(int addr) +{ + return !is_gssi(addr); +} + +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_tea_key(int addr) +{ + /* TETRA standard part 7 Clause 6.2: + Class 1: + Shall not use encryption + May use authentication + + Class 2: SCK Mode + Shall use SCK encryption + Shall use ESI with SCK + May use MGCK encryption + May use authentication + + Class 3: DCK Mode + Shall use authentication + Shall use DCK, CCK and/or MGCK encryption + Shall use ESI with CCK + + An address may be an: + - ISSI (individual), + - ASSI (not considered as of yet), + - GSSI (group) + + According to part 1 Clause 7.2.5: + The SSIs (ISSI, ASSI or GSSI) shall be allocated by the network operator. + As such, we do not know what SSI type we get. Also, we get an encrypted identity. + */ + + if (!tcs->network) { + return 0; + } + + if (tcs->network->security_class == NETWORK_CLASS_3G) { + /* This network uses DCKs. Check if individually addressed */ + if (is_issi(addr)) { + return get_key_by_addr(addr, KEYTYPE_DCK); + } + } + + if (is_gssi(addr)) { + /* Group addressed. Let's assume we use MGCKs on this network */ + /* FIXME: check if indeed MGCK used */ + /* FIXME: select MGCK if required */ + // return get_key_by_addr(addr, KEYTYPE_MGCK); + } + + // Individually addressed using CCK or SCK + return tcs->cck; +} + +void update_current_network(int mcc, int mnc) +{ + if (tcs->mnc != mcc || tcs->mcc != 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 network_info *network = &tcdb->nets[i]; + if (network->mnc == tcs->mnc && network->mcc == tcs->mcc) { + tcs->network = network; + break; + } + } + + } + + update_current_cck(); +} + +void update_current_cck() +{ + 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_SCK || key->key_type == KEYTYPE_CCK) { + // Best case: full SCK or CCK + tcs->cck = key; + printf("tetra_crypto: Set new current_cck %d (type: full)\n", i); + break; + } + } + } +} + +struct network_info *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..745f824 --- /dev/null +++ b/src/crypto/tetra_crypto.h @@ -0,0 +1,120 @@ +#ifndef TETRA_CRYPTO_H +#define TETRA_CRYPTO_H + +#include <inttypes.h> +#include <stdbool.h> + +#include "../tetra_prim.h" +#include "../tetra_tdma.h" +#include "../tetra_mac_pdu.h" + +#define TCDB_ALLOC_BLOCK_SIZE 1 + +enum tetra_key_type { + KEYTYPE_UNDEFINED = 0x00, + + /* Keytypes usable as ECK after applying TB5 */ + KEYTYPE_DCK = 0x01, + KEYTYPE_MGCK = 0x02, + KEYTYPE_SCK = 0x04, + KEYTYPE_CCK = 0x08, + + /* Can be combined with SCK/CCK to get MGCK */ + KEYTYPE_GCK = 0x20, +}; + +enum tetra_tea_type { + UNKNOWN = 0, + TEA1 = 1, /* KSG number value 0 */ + TEA2 = 2, /* KSG number value 1 */ + TEA3 = 3, /* KSG number value 2 */ + TEA4 = 4, /* KSG number value 3 */ + TEA5 = 5, /* KSG number value 4 */ + TEA6 = 6, /* KSG number value 5 */ + TEA7 = 7, /* KSG number value 6 */ + 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, + NETWORK_CLASS_3G = 4, /* Class 3 with GCK */ +}; + +struct network_info { + int mcc; + int mnc; + enum tetra_tea_type tea_type; /* KSG used in this network */ + enum tetra_security_class security_class; /* Security class this network employs */ +}; + +struct address_range { + int is_gssi_range; + int start; + int end; +}; + +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]; /* Depending on key type, only first part may be used */ + struct network_info *network_info; /* Network with which the key is associated */ +}; + +struct tetra_crypto_database { + int num_keys; + struct tetra_key *keys; + int num_nets; + struct network_info *nets; + int num_ranges; + struct address_range *ranges; +}; +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 id for key selection (from SYSINFO) */ + int hn; /* Hyperframe number for IV (from SYSINFO) */ + int la; /* location area for TB5 */ + int cn; /* carrier number for TB5 */ + int cc; /* colour code for TB5 */ + struct network_info *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(int key_type); +const char *tetra_get_tea_type_name(int tea_type); +const char *tetra_get_security_class_name(uint8_t pdut); + +/* Key loading / unloading */ +void tetra_crypto_init(); +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_tea_key(int addr); +struct network_info *get_network_info(int mcc, int mnc); +void update_current_network(int mcc, int mnc); +void update_current_cck(); + +int is_gssi(int addr); +int is_issi(int addr); + +char *dump_network_info(struct network_info *network); +char *dump_key(struct tetra_key *k); + +#endif /* TETRA_CRYPTO_H */