laforge has submitted this change. (
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
M src/tetra-rx.c
M src/tetra_common.h
5 files changed, 403 insertions(+), 1 deletion(-)
Approvals:
Jenkins Builder: Verified
laforge: Looks good to me, approved
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..5313459
--- /dev/null
+++ b/src/crypto/tetra_crypto.c
@@ -0,0 +1,252 @@
+/* Cryptography related helper, key management and wrapper functions */
+/*
+ * Copyright (C) 2023 Midnight Blue B.V.
+ *
+ * Author: Wouter Bokslag <w.bokslag [ ] midnightblue [ ] nl>
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+
+#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;
+
+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_state_init(struct tetra_crypto_state *tcs)
+{
+ /* Initialize tetra_crypto_state */
+ tcs = talloc_zero(NULL, struct 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;
+}
+
+void tetra_crypto_db_init(void)
+{
+ /* Initialize tetra_crypto_database */
+ tcdb->num_keys = 0;
+ tcdb->num_nets = 0;
+ tcdb->keys = talloc_zero_array(NULL, struct tetra_key, TCDB_ALLOC_BLOCK_SIZE);
+ tcdb->nets = talloc_zero_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;
+}
+
+struct tetra_key *get_key_by_addr(struct tetra_crypto_state *tcs, 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(struct tetra_crypto_state *tcs, int addr)
+{
+ /* TETRA standard part 7 Clause 6.2:
+ --------------------------------------------
+ Auth Encr GCK DCK
+ Class 1: ? - - -
+ Class 2: ? + ? -
+ Class 3: + + ? +
+ --------------------------------------------
+ */
+
+ if (!tcs->network)
+ /* No tetra_netinfo from the db set for this 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(struct tetra_crypto_state *tcs, 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;
+ }
+ }
+
+ // (Try to) select new CCK/SCK
+ update_current_cck(tcs);
+}
+
+void update_current_cck(struct tetra_crypto_state *tcs)
+{
+ 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..a1801cc
--- /dev/null
+++ b/src/crypto/tetra_crypto.h
@@ -0,0 +1,130 @@
+/* Cryptography related helper, key management and wrapper functions */
+/*
+ * Copyright (C) 2023 Midnight Blue B.V.
+ *
+ * Author: Wouter Bokslag <w.bokslag [ ] midnightblue [ ] nl>
+ *
+ * SPDX-License-Identifier: AGPL-3.0+
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * See the COPYING file in the main directory for details.
+ */
+
+
+#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) */
+};
+
+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_state_init(struct tetra_crypto_state *tcs);
+void tetra_crypto_db_init(void);
+int load_keystore(char *filename);
+
+/* 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(struct tetra_crypto_state *tcs, int addr);
+struct tetra_netinfo *get_network_info(int mcc, int mnc);
+void update_current_network(struct tetra_crypto_state *tcs, int mcc, int mnc);
+void update_current_cck(struct tetra_crypto_state *tcs);
+
+char *dump_network_info(struct tetra_netinfo *network);
+char *dump_key(struct tetra_key *k);
+
+#endif /* TETRA_CRYPTO_H */
diff --git a/src/tetra-rx.c b/src/tetra-rx.c
index ed4d252..2fe95b5 100644
--- a/src/tetra-rx.c
+++ b/src/tetra-rx.c
@@ -30,6 +30,7 @@
#include <osmocom/core/talloc.h>
#include "tetra_common.h"
+#include "crypto/tetra_crypto.h"
#include <phy/tetra_burst.h>
#include <phy/tetra_burst_sync.h>
#include "tetra_gsmtap.h"
@@ -43,8 +44,11 @@
struct tetra_rx_state *trs;
struct tetra_mac_state *tms;
+ /* Initialize tetra mac state and crypto state */
tms = talloc_zero(tetra_tall_ctx, struct tetra_mac_state);
tetra_mac_state_init(tms);
+ tms->tcs = talloc_zero(NULL, struct tetra_crypto_state);
+ tetra_crypto_state_init(tms->tcs);
trs = talloc_zero(tetra_tall_ctx, struct tetra_rx_state);
trs->burst_cb_priv = tms;
diff --git a/src/tetra_common.h b/src/tetra_common.h
index 3296a3f..3a96ecd 100644
--- a/src/tetra_common.h
+++ b/src/tetra_common.h
@@ -55,6 +55,8 @@
} cur_burst;
struct tetra_si_decoded last_sid;
+ struct tetra_crypto_state *tcs; /* contains all state relevant to encryption */
+
char *dumpdir; /* Where to save traffic channel dump */
int ssi; /* SSI */
int tsn; /* Timeslon number */
--
To view, visit
https://gerrit.osmocom.org/c/osmo-tetra/+/33943
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-tetra
Gerrit-Branch: master
Gerrit-Change-Id: I63bc712630ae5dbaa049c129d456f7aef5bda863
Gerrit-Change-Number: 33943
Gerrit-PatchSet: 6
Gerrit-Owner: wbokslag <w.bokslag(a)midnightblue.nl>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: merged