Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions examples/encryption_keys.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"keys": [
{
"keyid": "0x0001",
"algid": "0x84",
"key": "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
},
{
"keyid": "0x0002",
"algid": "0x81",
"key": "0123456789ABCDEF"
},
{
"keyid": "0x1b50",
"algid": "0xaa",
"key": "0123456789"
}
]
}
3 changes: 3 additions & 0 deletions lib/op25_repeater/lib/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ list(APPEND op25_repeater_sources
p25_framer.cc
p25p1_fdma.cc
p25_crypt_algs.cc
p25_crypt_adp.cc
p25_crypt_aes.cc
p25_crypt_des.cc
p25p1_voice_encode.cc
p25p1_voice_decode.cc
p25p2_framer.cc
Expand Down
46 changes: 46 additions & 0 deletions lib/op25_repeater/lib/p25_crypt.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/* -*- c++ -*- */
/*
* Copyright 2025 Graham J. Norbury
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/

#ifndef P25_CRYPT_H
#define P25_CRYPT_H

#include <stdint.h>
#include <vector>

enum algid_type : uint8_t {
ALG_UNENCRYPTED = 0x80,
ALG_DES_OFB = 0x81,
ALG_AES_256 = 0x84,
ALG_ADP_RC4 = 0xAA
};

enum frame_type { FT_UNK = 0, FT_LDU1, FT_LDU2, FT_2V, FT_4V_0, FT_4V_1, FT_4V_2, FT_4V_3 };
enum protocol_type { PT_UNK = 0, PT_P25_PHASE1, PT_P25_PHASE2 };

typedef std::vector<uint8_t> packed_codeword;

struct key_info {
key_info() : algid(0), key() {}
key_info(uint8_t a, const std::vector<uint8_t> &k) : algid(a), key(k) {}
uint8_t algid;
std::vector<uint8_t> key;
};

#endif /* P25_CRYPT_H */
149 changes: 149 additions & 0 deletions lib/op25_repeater/lib/p25_crypt_adp.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/* -*- c++ -*- */
/*
* Copyright 2025 Graham J. Norbury
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "p25_crypt_adp.h"

// constructor
p25_crypt_adp::p25_crypt_adp(log_ts& logger, int debug, int msgq_id) :
p25_crypt_alg(logger, debug, msgq_id) {

fprintf(stderr, "%s p25_crypt_adp::p25_crypt_adp: loading ADP RC4 module\n", logts.get(d_msgq_id));
}

// destructor
p25_crypt_adp::~p25_crypt_adp() {

}

// prepare routine entry point
bool
p25_crypt_adp::prepare(uint16_t keyid, protocol_type pr_type, uint8_t *MI) {
d_pr_type = pr_type;
memcpy(d_mi, MI, sizeof(d_mi));

d_key_iter = d_keys.find(keyid);
if (d_key_iter == d_keys.end()) {
if (d_debug >= 10) {
fprintf(stderr, "%s p25_crypt_adp::prepare: keyid[0x%x] not found\n", logts.get(d_msgq_id), keyid);
}
return false;
}
if (d_debug >= 10) {
fprintf(stderr, "%s p25_crypt_adp::prepare: keyid[0x%x] found\n", logts.get(d_msgq_id), keyid);
}
d_position = 0;
d_pr_type = pr_type;

// Find key value from keyid and set up to create keystream
uint8_t adp_key[13], S[256], K[256];
uint32_t i, j, k;
std::vector<uint8_t>::const_iterator kval_iter = d_key_iter->second.key.begin();
for (i = 0; i < (uint32_t)std::max(5-(int)(d_key_iter->second.key.size()), 0); i++) {
adp_key[i] = 0; // pad with leading 0 if supplied key too short
}
for ( ; i < 5; i++) {
adp_key[i] = *kval_iter++; // copy up to 5 bytes into key array
}

j = 0;
for (i = 5; i < 13; ++i) {
adp_key[i] = d_mi[i - 5]; // append MI bytes
}

for (i = 0; i < 256; ++i) {
K[i] = adp_key[i % 13];
}

for (i = 0; i < 256; ++i) {
S[i] = i;
}

for (i = 0; i < 256; ++i) {
j = (j + S[i] + K[i]) & 0xFF;
adp_swap(S, i, j);
}

i = j = 0;

for (k = 0; k < 469; ++k) {
i = (i + 1) & 0xFF;
j = (j + S[i]) & 0xFF;
adp_swap(S, i, j);
d_keystream[k] = S[(S[i] + S[j]) & 0xFF];
}

return true;
}

// process routine entry point
bool
p25_crypt_adp::process(packed_codeword& PCW, frame_type fr_type, int voice_subframe) {
if (d_key_iter == d_keys.end())
return false;

bool rc = true;
size_t offset = 256;
switch (fr_type) {
case FT_LDU1:
offset = 0;
break;
case FT_LDU2:
offset = 101;
break;
case FT_4V_0:
offset += 7 * voice_subframe;
break;
case FT_4V_1:
offset += 7 * (voice_subframe + 4);
break;
case FT_4V_2:
offset += 7 * (voice_subframe + 8);
break;
case FT_4V_3:
offset += 7 * (voice_subframe + 12);
break;
case FT_2V:
offset += 7 * (voice_subframe + 16);
break;
default:
rc = false;
break;
}
if (d_pr_type == PT_P25_PHASE1) {
//FDMA
offset += (d_position * 11) + 267 + ((d_position < 8) ? 0 : 2); // voice only; skip LCW and LSD
d_position = (d_position + 1) % 9;
for (int j = 0; j < 11; ++j) {
PCW[j] = d_keystream[j + offset] ^ PCW[j];
}
} else if (d_pr_type == PT_P25_PHASE2) {
//TDMA
for (int j = 0; j < 7; ++j) {
PCW[j] = d_keystream[j + offset] ^ PCW[j];
}
PCW[6] &= 0x80; // mask everything except the MSB of the final codeword
}

return rc;
}
59 changes: 59 additions & 0 deletions lib/op25_repeater/lib/p25_crypt_adp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/* -*- c++ -*- */
/*
* Copyright 2025 Graham J. Norbury
*
* This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* This software 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this software; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/

#ifndef P25_CRYPT_ADP_H
#define P25_CRYPT_ADP_H

#include <gnuradio/msg_queue.h>
#include "log_ts.h"
#include "p25_crypt.h"
#include "p25_crypt_alg.h"

// ADP RC4 decryption algorithm
class p25_crypt_adp : public p25_crypt_alg
{
private:
protocol_type d_pr_type;
uint8_t d_mi[9];
uint8_t d_keystream[469];
uint32_t d_position;

public:
virtual bool prepare(uint16_t keyid, protocol_type pr_type, uint8_t *MI);
virtual bool process(packed_codeword& PCW, frame_type fr_type, int voice_subframe);

p25_crypt_adp(log_ts& logger, int debug, int msgq_id);
~p25_crypt_adp();

inline uint8_t key(uint16_t keyid, uint8_t algid, const std::vector<uint8_t> &key) {
if (algid != ALG_ADP_RC4)
return 0;
p25_crypt_alg::key(keyid, algid, key);
return algid;
}

inline void adp_swap(uint8_t *S, uint32_t i, uint32_t j) {
uint8_t temp = S[i];
S[i] = S[j];
S[j] = temp;
}
};

#endif /* P25_CRYPT_ADP_H */
Loading