wbokslag has uploaded this change for review. (
https://gerrit.osmocom.org/c/osmo-tetra/+/34001 )
Change subject: Added stub for decryption and full keystream application functions
......................................................................
Added stub for decryption and full keystream application functions
Added stub functions for keystream generation and identity decryption
Also added implementations for mac resource decryption, voice
decryption and identity decryption.
Change-Id: I4e6147f206ad6046f32e08015ec9721b64382ca1
---
M src/crypto/tetra_crypto.c
M src/crypto/tetra_crypto.h
2 files changed, 179 insertions(+), 9 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-tetra refs/changes/01/34001/1
diff --git a/src/crypto/tetra_crypto.c b/src/crypto/tetra_crypto.c
index 9515a25..d5edde7 100644
--- a/src/crypto/tetra_crypto.c
+++ b/src/crypto/tetra_crypto.c
@@ -150,19 +150,176 @@
return ((tm->tn - 1) | (tm->fn << 2) | (tm->mn << 7) | ((hn &
0x7FFF) << 13) | (dir << 28));
}
-int decrypt_identity(struct tetra_crypto_state *tcs, struct tetra_addr *addr)
+void tb5_stub(uint8_t *cn, uint8_t *la, uint8_t *cc, uint8_t *key, uint8_t *eck)
{
- return 0;
+ /* FIXME: implement */
+ memset(eck, 0, 10);
}
-int decrypt_mac_element(struct tetra_crypto_state *tcs, struct tetra_tmvsap_prim *tmvp,
struct tetra_key *key, int l1_len, int tmpdu_offset)
+void ta61_stub(uint8_t *key, uint8_t *esi, uint8_t *ssi)
{
- return 0;
+ /* FIXME: implement */
+ memcpy(ssi, esi, 3);
}
-int decrypt_voice_timeslot(struct tetra_crypto_state *tcs, struct tetra_tdma_time
*tdma_time, int16_t *type1_block)
+void tea1_stub(uint32_t iv, uint8_t *eck, uint32_t num_bytes, uint8_t *ks)
{
- return 0;
+ /* FIXME: implement */
+ memset(ks, 0, num_bytes);
+}
+
+void tea2_stub(uint32_t iv, uint8_t *eck, uint32_t num_bytes, uint8_t *ks)
+{
+ /* FIXME: implement */
+ memset(ks, 0, num_bytes);
+}
+
+void tea3_stub(uint32_t iv, uint8_t *eck, uint32_t num_bytes, uint8_t *ks)
+{
+ /* FIXME: implement */
+ memset(ks, 0, num_bytes);
+}
+
+static bool generate_keystream(struct tetra_crypto_state *tcs, struct tetra_key *key,
struct tetra_tdma_time *t, uint16_t hn, int num_bits, uint8_t *ks_out)
+{
+ if (!key)
+ return false;
+
+ /* Construct IV and prepare buf for bytewise keystream */
+ int num_bytes = (num_bits + 7) / 8;
+ uint8_t ks_bytes[num_bytes];
+ uint32_t iv = tea_build_iv(t, hn, 0);
+
+ /* Compute ECK from net info and CK */
+ if (tcs->cn < 0 || tcs->la < 0 || tcs->cc < 0) {
+ /* Missing data for TB5 */
+ memset(ks_out, 0, num_bytes);
+ return false;
+ }
+
+ uint8_t eck[10];
+ uint8_t cn[2] = {(tcs->cn >> 8) & 0xFF, tcs->cn & 0xFF};
+ uint8_t la[2] = {(tcs->la >> 8) & 0xFF, tcs->la & 0xFF};
+ uint8_t cc[1] = {tcs->cc & 0xFF};
+ tb5_stub(cn, la, cc, key->key, eck);
+
+ switch (key->network_info->ksg_type) {
+ case KSG_TEA1:
+ tea1_stub(iv, eck, num_bytes, ks_bytes);
+ break;
+ case KSG_TEA2:
+ tea2_stub(iv, eck, num_bytes, ks_bytes);
+ break;
+ case KSG_TEA3:
+ tea3_stub(iv, eck, num_bytes, ks_bytes);
+ break;
+ default:
+ fprintf(stderr, "tetra_crypto: KSG type %d not supported\n",
key->network_info->ksg_type);
+ return false;
+ }
+
+ for (int i = 0; i < num_bytes; i++)
+ for (int j = 0; j < 8; j++)
+ ks_out[i * 8 + j] = (ks_bytes[i] >> (7-j)) & 1;
+
+ /* Expand keystream bytes into ubit format */
+ for (int i = 0; i < num_bits; i++)
+ ks_out[i] = (ks_bytes[num_bits / 8] >> (7-(num_bits % 8))) & 1;
+
+ return true;
+}
+
+bool decrypt_identity(struct tetra_crypto_state *tcs, struct tetra_addr *addr)
+{
+ /* TODO FIXME maybe extend tetra_addr to contain is_encrypted bool */
+ if (!tcs->cck)
+ return false;
+
+ // Perform in-place decryption
+ uint8_t ab_addr[3] = {addr->ssi >> 16, addr->ssi >> 8, addr->ssi};
+ ta61_stub(tcs->cck->key, ab_addr, ab_addr);
+ addr->ssi = (ab_addr[0] << 16) | (ab_addr[1] << 8) | ab_addr[2];
+ return true;
+}
+
+bool decrypt_mac_element(struct tetra_crypto_state *tcs, struct tetra_tmvsap_prim *tmvp,
struct tetra_key *key, int l1_len, int tmpdu_offset)
+{
+ if (!key || l1_len - tmpdu_offset <= 0)
+ return false;
+
+ if (tcs->cn < 0 || tcs->la < 0 || tcs->cc < 0) {
+ printf("tetra_crypto: can't compute TB5 due to incomplete network info (carr
%d la %d cc %d)\n",
+ tcs->cn, tcs->la, tcs->cc);
+ return false;
+ }
+
+ struct tetra_tdma_time *tdma_time = &tmvp->u.unitdata.tdma_time;
+
+ // Compute keystream offset to apply
+ /* TODO FIXME maybe we can rework channel type setting in lower mac, and
+ avoid using TETRA_LC_UNKNOWN below */
+ uint32_t ks_skip_bits = 0;
+ if (tmvp->u.unitdata.blk_num == BLK_2 && (
+ tmvp->u.unitdata.lchan == TETRA_LC_SCH_HD ||
+ tmvp->u.unitdata.lchan == TETRA_LC_UNKNOWN)) {
+ ks_skip_bits = 216;
+ printf("tetra_crypto: 2nd half slot; skipping bits\n");
+ }
+
+ // Compute keystream
+ struct msgb *msg = tmvp->oph.msg;
+ int ct_len = l1_len - tmpdu_offset;
+ int ks_num_bits = ks_skip_bits + ct_len;
+ uint8_t *ct_start = msg->l1h + tmpdu_offset;
+ uint8_t ks[ks_num_bits];
+
+ if (!generate_keystream(tcs, key, tdma_time, tcs->hn, ks_num_bits, ks)) {
+ printf("tetra_crypto: addr %8d -> key %4d, time %5d/%s, tmpdu offset %d,
FAILED\n",
+ key->addr, key->index, tcs->hn, tetra_tdma_time_dump(tdma_time),
tmpdu_offset);
+ return false;
+ }
+
+ // Apply keystream
+ for (int i = 0; i < ct_len; i++)
+ ct_start[i] = ct_start[i] ^ ks[i + ks_skip_bits];
+
+ printf("tetra_crypto: addr %8d -> key %4d, time %5d/%s, tmpdu offset %d,
decrypting %d bits\n",
+ key->addr, key->index, tcs->hn, tetra_tdma_time_dump(tdma_time), tmpdu_offset,
ct_len);
+
+ return true;
+}
+
+bool decrypt_voice_timeslot(struct tetra_crypto_state *tcs, struct tetra_tdma_time
*tdma_time, int16_t *type1_block)
+{
+ /* TODO FIXME implement proper key selection for voice blocks */
+ struct tetra_key *key = tcs->cck;
+ if (!key)
+ return false;
+
+ if (tcs->cn < 0 || tcs->la < 0 || tcs->cc < 0) {
+ printf("tetra_crypto: can't compute TB5 due to incomplete network info (carr
%d la %d cc %d)\n",
+ tcs->cn, tcs->la, tcs->cc);
+ return false;
+ }
+
+ // Compute keystream
+ int ks_num_bits = 137*2; // two half slots of voice
+ uint8_t ks[ks_num_bits];
+ if (!generate_keystream(tcs, key, tdma_time, tcs->hn, ks_num_bits, ks)) {
+ printf("tetra_crypto: addr %8d -> key %4d, time %5d/%s, traffic
FAILED\n",
+ key->addr, key->index, tcs->hn, tetra_tdma_time_dump(tdma_time));
+ return false;
+ }
+
+ // Apply keystream
+ for (int i = 0; i < 137; i++) {
+ type1_block[i + 1] = type1_block[i + 1] ^ ks[i];
+ type1_block[i + 139] = type1_block[i + 139] ^ ks[i + 137];
+ }
+
+ printf("tetra_crypto: addr %8d -> key %4d, time %5d/%s, decrypted
voice\n",
+ key->addr, key->index, tcs->hn, tetra_tdma_time_dump(tdma_time));
+ return true;
}
int load_keystore(char *tetra_keyfile)
diff --git a/src/crypto/tetra_crypto.h b/src/crypto/tetra_crypto.h
index e50a45e..ace4c04 100644
--- a/src/crypto/tetra_crypto.h
+++ b/src/crypto/tetra_crypto.h
@@ -111,9 +111,9 @@
/* 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_crypto_state *tcs, struct tetra_addr *addr);
-int decrypt_mac_element(struct tetra_crypto_state *tcs, struct tetra_tmvsap_prim *tmvp,
struct tetra_key *key, int l1_len, int tmpdu_offset);
-int decrypt_voice_timeslot(struct tetra_crypto_state *tcs, struct tetra_tdma_time
*tdma_time, int16_t *type1_bits);
+bool decrypt_identity(struct tetra_crypto_state *tcs, struct tetra_addr *addr);
+bool decrypt_mac_element(struct tetra_crypto_state *tcs, struct tetra_tmvsap_prim *tmvp,
struct tetra_key *key, int l1_len, int tmpdu_offset);
+bool decrypt_voice_timeslot(struct tetra_crypto_state *tcs, struct tetra_tdma_time
*tdma_time, int16_t *type1_bits);
/* Key selection and crypto state management */
struct tetra_netinfo *get_network_info(int mcc, int mnc);
--
To view, visit
https://gerrit.osmocom.org/c/osmo-tetra/+/34001
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: osmo-tetra
Gerrit-Branch: master
Gerrit-Change-Id: I4e6147f206ad6046f32e08015ec9721b64382ca1
Gerrit-Change-Number: 34001
Gerrit-PatchSet: 1
Gerrit-Owner: wbokslag <w.bokslag(a)midnightblue.nl>
Gerrit-MessageType: newchange