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
This commit is contained in:
wbokslag 2023-07-26 17:45:32 +02:00
parent affab933cf
commit eced97dd17
3 changed files with 344 additions and 1 deletions

View File

@ -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

233
src/crypto/tetra_crypto.c Normal file
View File

@ -0,0 +1,233 @@
#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_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;
}

107
src/crypto/tetra_crypto.h Normal file
View File

@ -0,0 +1,107 @@
#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 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 */