892 lines
25 KiB
C++
892 lines
25 KiB
C++
/* -*- c++ -*- */
|
|
/*
|
|
* Copyright 2023 jmfriedt.
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
// 240620: todo uncomment #idef AES for cryptography and #ifdef ECC for signature
|
|
|
|
// in m17_coder_impl.h: #define AES #define ECC
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gnuradio/io_signature.h>
|
|
#include "m17_coder_impl.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <unistd.h>
|
|
|
|
#include "m17.h"
|
|
|
|
#ifdef AES
|
|
#include "aes.h"
|
|
#endif
|
|
|
|
#ifdef ECC
|
|
#include "uECC.h"
|
|
#endif
|
|
|
|
namespace gr
|
|
{
|
|
namespace m17
|
|
{
|
|
|
|
m17_coder::sptr
|
|
m17_coder::make (std::string src_id, std::string dst_id, int mode,
|
|
int data, int encr_type, int encr_subtype, int can,
|
|
std::string meta, std::string key,
|
|
std::string priv_key, bool debug, bool signed_str, std::string seed)
|
|
{
|
|
return gnuradio::get_initial_sptr
|
|
(new
|
|
m17_coder_impl (src_id, dst_id, mode, data, encr_type, encr_subtype,
|
|
can, meta, key, priv_key, debug, signed_str, seed));
|
|
}
|
|
|
|
/*
|
|
* The private constructor
|
|
*/
|
|
m17_coder_impl::m17_coder_impl (std::string src_id, std::string dst_id,
|
|
int mode, int data, int encr_type,
|
|
int encr_subtype, int can,
|
|
std::string meta, std::string key,
|
|
std::string priv_key, bool debug,
|
|
bool signed_str, std::string seed):gr::block
|
|
("m17_coder", gr::io_signature:: make (1, 1, sizeof (char)),
|
|
gr::io_signature:: make (1, 1, sizeof (float))),
|
|
_mode (mode), _data (data), _encr_subtype (encr_subtype), _can (can), _meta (meta), _debug (debug),
|
|
_signed_str (signed_str)
|
|
{
|
|
set_encr_type(encr_type); // overwritten by set_seed()
|
|
set_type (mode, data, _encr_type, encr_subtype, can);
|
|
set_meta (meta); // depends on ^^^ encr_subtype
|
|
set_seed (seed); // depends on ^^^ encr_subtype
|
|
set_src_id (src_id);
|
|
set_dst_id (dst_id);
|
|
set_signed (signed_str);
|
|
set_debug (debug);
|
|
set_output_multiple (192);
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
_got_lsf = 0; //have we filled the LSF struct yet?
|
|
_fn = 0; //16-bit Frame Number (for the stream mode)
|
|
_finished = false;
|
|
#ifdef AES
|
|
srand (time (NULL)); //random number generator (for IV rand() seed value)
|
|
memset (_key, 0, 32 * sizeof (uint8_t));
|
|
memset (_iv, 0, 16 * sizeof (uint8_t));
|
|
#endif
|
|
if (_encr_type == ENCR_AES)
|
|
{
|
|
set_key (key); // read key
|
|
*((int32_t *) & _iv[0]) = (uint32_t) time (NULL) - (uint32_t) epoch; //timestamp
|
|
for (uint8_t i = 4; i < 4 + 10; i++)
|
|
_iv[i] = 0; //10 random bytes TODO: replace with a rand() or pass through an additional arg
|
|
}
|
|
message_port_register_in(pmt::mp("end_of_transmission"));
|
|
set_msg_handler(pmt::mp("end_of_transmission"), [this](const pmt::pmt_t& msg) { end_of_transmission(msg); });
|
|
}
|
|
|
|
void m17_coder_impl::end_of_transmission(const pmt::pmt_t& msg)
|
|
{_finished=true;
|
|
std::cout << "***** End of Transmission ********\n";
|
|
pmt::print(msg);
|
|
}
|
|
|
|
void m17_coder_impl::set_encr_type (int encr_type)
|
|
{
|
|
switch (encr_type)
|
|
{case 0:_encr_type=ENCR_NONE;break;
|
|
case 1:_encr_type=ENCR_SCRAM;break;
|
|
case 2:_encr_type=ENCR_AES;break;
|
|
case 3:_encr_type=ENCR_RES;break;
|
|
default:_encr_type=ENCR_NONE;
|
|
}
|
|
printf ("new encr type: %x -> ", _encr_type);
|
|
}
|
|
|
|
void m17_coder_impl::set_signed (bool signed_str)
|
|
{
|
|
_signed_str = signed_str;
|
|
if (_signed_str == true)
|
|
printf ("Signed\n");
|
|
else
|
|
printf ("Unsigned\n");
|
|
}
|
|
|
|
void m17_coder_impl::set_debug (bool debug)
|
|
{
|
|
_debug = debug;
|
|
if (_debug == true)
|
|
printf ("Debug true\n");
|
|
else
|
|
printf ("Debug false\n");
|
|
}
|
|
|
|
void m17_coder_impl::set_src_id (std::string src_id)
|
|
{
|
|
int length;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
_src_id[i] = 0;
|
|
}
|
|
if (src_id.length () > 9)
|
|
length = 9;
|
|
else
|
|
length = src_id.length ();
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
_src_id[i] = toupper (src_id.c_str ()[i]);
|
|
}
|
|
encode_callsign_bytes (_lsf.src, _src_id); // 6 byte ID <- 9 char callsign
|
|
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
}
|
|
|
|
void m17_coder_impl::set_dst_id (std::string dst_id)
|
|
{
|
|
int length;
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
_dst_id[i] = 0;
|
|
}
|
|
if (dst_id.length () > 9)
|
|
length = 9;
|
|
else
|
|
length = dst_id.length ();
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
_dst_id[i] = toupper (dst_id.c_str ()[i]);
|
|
}
|
|
encode_callsign_bytes (_lsf.dst, _dst_id); // 6 byte ID <- 9 char callsign
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
}
|
|
|
|
void m17_coder_impl::set_priv_key (std::string arg) // *UTF-8* encoded byte array
|
|
{
|
|
int length;
|
|
printf ("new private key: ");
|
|
length = arg.size ();
|
|
_priv_key_loaded = true;
|
|
int i = 0, j = 0;
|
|
while ((j < 32) && (i < length))
|
|
{
|
|
if ((unsigned int) arg.data ()[i] < 0xc2) // https://www.utf8-chartable.de/
|
|
{
|
|
_priv_key[j] = arg.data ()[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
_priv_key[j] =
|
|
(arg.data ()[i] - 0xc2) * 0x40 + arg.data ()[i + 1];
|
|
i += 2;
|
|
j++;
|
|
}
|
|
}
|
|
length = j; // index from 0 to length-1
|
|
printf ("%d bytes: ", length);
|
|
for (i = 0; i < length; i++)
|
|
printf ("%02X ", _priv_key[i]);
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
}
|
|
|
|
void m17_coder_impl::set_key (std::string arg) // *UTF-8* encoded byte array
|
|
{
|
|
int length;
|
|
printf ("new key: ");
|
|
length = arg.size ();
|
|
int i = 0, j = 0;
|
|
while ((j < 32) && (i < length))
|
|
{
|
|
if ((unsigned int) arg.data ()[i] < 0xc2) // https://www.utf8-chartable.de/
|
|
{
|
|
_key[j] = arg.data ()[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
_key[j] = (arg.data ()[i] - 0xc2) * 0x40 + arg.data ()[i + 1];
|
|
i += 2;
|
|
j++;
|
|
}
|
|
}
|
|
length = j; // index from 0 to length-1
|
|
printf ("%d bytes: ", length);
|
|
for (i = 0; i < length; i++)
|
|
printf ("%02X ", _key[i]);
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
}
|
|
|
|
void m17_coder_impl::set_seed (std::string arg) // *UTF-8* encoded byte array
|
|
{
|
|
int length;
|
|
printf ("new seed: ");
|
|
length = arg.size ();
|
|
int i = 0, j = 0;
|
|
while ((j < 3) && (i < length))
|
|
{
|
|
if ((unsigned int) arg.data ()[i] < 0xc2) // https://www.utf8-chartable.de/
|
|
{
|
|
_seed[j] = arg.data ()[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
_seed[j] = (arg.data ()[i] - 0xc2) * 0x40 + arg.data ()[i + 1];
|
|
i += 2;
|
|
j++;
|
|
}
|
|
}
|
|
length = j; // index from 0 to length-1
|
|
printf ("%d bytes: ", length);
|
|
for (i = 0; i < length; i++)
|
|
printf ("%02X ", _seed[i]);
|
|
printf ("\n");
|
|
fflush (stdout);
|
|
if(length<=2)
|
|
{
|
|
_scrambler_seed = _scrambler_seed >> 16;
|
|
fprintf(stderr, "Scrambler key: 0x%02X (8-bit)\n", _scrambler_seed);
|
|
}
|
|
else if(length<=4)
|
|
{
|
|
_scrambler_seed = _scrambler_seed >> 8;
|
|
fprintf(stderr, "Scrambler key: 0x%04X (16-bit)\n", _scrambler_seed);
|
|
}
|
|
else
|
|
fprintf(stderr, "Scrambler key: 0x%06X (24-bit)\n", _scrambler_seed);
|
|
|
|
_encr_type=ENCR_SCRAM; //Scrambler key was passed
|
|
}
|
|
|
|
void m17_coder_impl::set_meta (std::string meta) // either an ASCII string if encr_subtype==0 or *UTF-8* encoded byte array
|
|
{
|
|
int length;
|
|
printf ("new meta: ");
|
|
if (_encr_subtype == 0) // meta is \0-terminated string
|
|
{
|
|
if (meta.length () < 14)
|
|
length = meta.length ();
|
|
else
|
|
{
|
|
length = 14;
|
|
meta[length] = 0;
|
|
}
|
|
printf ("%s\n", meta.c_str ());
|
|
for (int i = 0; i < length; i++)
|
|
{
|
|
_lsf.meta[i] = meta[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
length = meta.size ();
|
|
int i = 0, j = 0;
|
|
while ((j < 14) && (i < length))
|
|
{
|
|
if ((unsigned int) meta.data ()[i] < 0xc2) // https://www.utf8-chartable.de/
|
|
{
|
|
_lsf.meta[j] = meta.data ()[i];
|
|
i++;
|
|
j++;
|
|
}
|
|
else
|
|
{
|
|
_lsf.meta[j] =
|
|
(meta.data ()[i] - 0xc2) * 0x40 + meta.data ()[i + 1];
|
|
i += 2;
|
|
j++;
|
|
}
|
|
}
|
|
length = j; // index from 0 to length-1
|
|
printf ("%d bytes: ", length);
|
|
for (i = 0; i < length; i++)
|
|
printf ("%02X ", _lsf.meta[i]);
|
|
printf ("\n");
|
|
}
|
|
fflush (stdout);
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
}
|
|
|
|
void m17_coder_impl::set_mode (int mode)
|
|
{
|
|
_mode = mode;
|
|
printf ("new mode: %x -> ", _mode);
|
|
set_type (_mode, _data, _encr_type, _encr_subtype, _can);
|
|
}
|
|
|
|
void m17_coder_impl::set_data (int data)
|
|
{
|
|
_data = data;
|
|
printf ("new data type: %x -> ", _data);
|
|
set_type (_mode, _data, _encr_type, _encr_subtype, _can);
|
|
}
|
|
|
|
void m17_coder_impl::set_encr_subtype (int encr_subtype)
|
|
{
|
|
_encr_subtype = encr_subtype;
|
|
printf ("new encr subtype: %x -> ", _encr_subtype);
|
|
set_type (_mode, _data, _encr_type, _encr_subtype, _can);
|
|
}
|
|
|
|
void m17_coder_impl::set_can (int can)
|
|
{
|
|
_can = can;
|
|
printf ("new CAN: %x -> ", _can);
|
|
set_type (_mode, _data, _encr_type, _encr_subtype, _can);
|
|
}
|
|
|
|
void m17_coder_impl::set_type (int mode, int data, encr_t encr_type,
|
|
int encr_subtype, int can)
|
|
{
|
|
short tmptype;
|
|
tmptype =
|
|
mode | (data << 1) | (encr_type << 3) | (encr_subtype << 5) | (can <<
|
|
7);
|
|
_lsf.type[0] = tmptype >> 8; // MSB
|
|
_lsf.type[1] = tmptype & 0xff; // LSB
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
printf ("Transmission type: 0x%02X%02X\n", _lsf.type[0], _lsf.type[1]);
|
|
fflush (stdout);
|
|
}
|
|
|
|
/*
|
|
* Our virtual destructor.
|
|
*/
|
|
m17_coder_impl::~m17_coder_impl ()
|
|
{
|
|
}
|
|
|
|
void
|
|
m17_coder_impl::forecast (int noutput_items,
|
|
gr_vector_int & ninput_items_required)
|
|
{
|
|
ninput_items_required[0] = noutput_items / 12; // 16 inputs -> 192 outputs
|
|
}
|
|
|
|
//scrambler pn sequence generation
|
|
void m17_coder_impl::scrambler_sequence_generator ()
|
|
{
|
|
int i = 0;
|
|
uint32_t lfsr, bit;
|
|
lfsr = _scrambler_seed;
|
|
|
|
//only set if not initially set (first run), it is possible (and observed) that the scrambler_subtype can
|
|
//change on subsequent passes if the current SEED for the LFSR falls below one of these thresholds
|
|
if (_scrambler_subtype == -1)
|
|
{
|
|
if (lfsr > 0 && lfsr <= 0xFF)
|
|
_scrambler_subtype = 0; // 8-bit key
|
|
else if (lfsr > 0xFF && lfsr <= 0xFFFF)
|
|
_scrambler_subtype = 1; //16-bit key
|
|
else if (lfsr > 0xFFFF && lfsr <= 0xFFFFFF)
|
|
_scrambler_subtype = 2; //24-bit key
|
|
else
|
|
_scrambler_subtype = 0; // 8-bit key (default)
|
|
}
|
|
|
|
//TODO: Set Frame Type based on scrambler_subtype value
|
|
if (_debug == true)
|
|
{
|
|
fprintf (stderr,
|
|
"\nScrambler Key: 0x%06X; Seed: 0x%06X; Subtype: %02d;",
|
|
_scrambler_seed, lfsr, _scrambler_subtype);
|
|
fprintf (stderr, "\n pN: ");
|
|
}
|
|
|
|
//run pN sequence with taps specified
|
|
for (i = 0; i < 128; i++)
|
|
{
|
|
//get feedback bit with specified taps, depending on the scrambler_subtype
|
|
if (_scrambler_subtype == 0)
|
|
bit = (lfsr >> 7) ^ (lfsr >> 5) ^ (lfsr >> 4) ^ (lfsr >> 3);
|
|
else if (_scrambler_subtype == 1)
|
|
bit = (lfsr >> 15) ^ (lfsr >> 14) ^ (lfsr >> 12) ^ (lfsr >> 3);
|
|
else if (_scrambler_subtype == 2)
|
|
bit = (lfsr >> 23) ^ (lfsr >> 22) ^ (lfsr >> 21) ^ (lfsr >> 16);
|
|
else
|
|
bit = 0; //should never get here, but just in case
|
|
|
|
bit &= 1; //truncate bit to 1 bit (required since I didn't do it above)
|
|
lfsr = (lfsr << 1) | bit; //shift LFSR left once and OR bit onto LFSR's LSB
|
|
lfsr &= 0xFFFFFF; //truncate lfsr to 24-bit (really doesn't matter)
|
|
_scrambler_pn[i] = bit;
|
|
|
|
}
|
|
//pack bit array into byte array for easy data XOR
|
|
pack_bit_array_into_byte_array (_scrambler_pn, _scr_bytes, 16);
|
|
|
|
//save scrambler seed for next round
|
|
_scrambler_seed = lfsr;
|
|
|
|
//truncate seed so subtype will continue to set properly on subsequent passes
|
|
if (_scrambler_subtype == 0)
|
|
_scrambler_seed &= 0xFF;
|
|
else if (_scrambler_subtype == 1)
|
|
_scrambler_seed &= 0xFFFF;
|
|
else if (_scrambler_subtype == 2)
|
|
_scrambler_seed &= 0xFFFFFF;
|
|
|
|
if (_debug == true)
|
|
{
|
|
//debug packed bytes
|
|
for (i = 0; i < 16; i++)
|
|
fprintf (stderr, " %02X", _scr_bytes[i]);
|
|
fprintf (stderr, "\n");
|
|
}
|
|
|
|
}
|
|
|
|
//convert a user string (as hex octets) into a uint8_t array for key
|
|
void m17_coder_impl::parse_raw_key_string (uint8_t * dest,
|
|
const char *inp)
|
|
{
|
|
uint16_t len = strlen (inp);
|
|
|
|
if (len == 0)
|
|
return; //return silently and pretend nothing happened
|
|
|
|
memset (dest, 0, len / 2); //one character represents half of a byte
|
|
|
|
if (!(len % 2)) //length even?
|
|
{
|
|
for (uint8_t i = 0; i < len; i += 2)
|
|
{
|
|
if (inp[i] >= 'a')
|
|
dest[i / 2] |= (inp[i] - 'a' + 10) * 0x10;
|
|
else if (inp[i] >= 'A')
|
|
dest[i / 2] |= (inp[i] - 'A' + 10) * 0x10;
|
|
else if (inp[i] >= '0')
|
|
dest[i / 2] |= (inp[i] - '0') * 0x10;
|
|
|
|
if (inp[i + 1] >= 'a')
|
|
dest[i / 2] |= inp[i + 1] - 'a' + 10;
|
|
else if (inp[i + 1] >= 'A')
|
|
dest[i / 2] |= inp[i + 1] - 'A' + 10;
|
|
else if (inp[i + 1] >= '0')
|
|
dest[i / 2] |= inp[i + 1] - '0';
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (inp[0] >= 'a')
|
|
dest[0] |= inp[0] - 'a' + 10;
|
|
else if (inp[0] >= 'A')
|
|
dest[0] |= inp[0] - 'A' + 10;
|
|
else if (inp[0] >= '0')
|
|
dest[0] |= inp[0] - '0';
|
|
|
|
for (uint8_t i = 1; i < len - 1; i += 2)
|
|
{
|
|
if (inp[i] >= 'a')
|
|
dest[i / 2 + 1] |= (inp[i] - 'a' + 10) * 0x10;
|
|
else if (inp[i] >= 'A')
|
|
dest[i / 2 + 1] |= (inp[i] - 'A' + 10) * 0x10;
|
|
else if (inp[i] >= '0')
|
|
dest[i / 2 + 1] |= (inp[i] - '0') * 0x10;
|
|
|
|
if (inp[i + 1] >= 'a')
|
|
dest[i / 2 + 1] |= inp[i + 1] - 'a' + 10;
|
|
else if (inp[i + 1] >= 'A')
|
|
dest[i / 2 + 1] |= inp[i + 1] - 'A' + 10;
|
|
else if (inp[i + 1] >= '0')
|
|
dest[i / 2 + 1] |= inp[i + 1] - '0';
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
m17_coder_impl::general_work (int noutput_items,
|
|
gr_vector_int & ninput_items,
|
|
gr_vector_const_void_star & input_items,
|
|
gr_vector_void_star & output_items)
|
|
{
|
|
const char *in = (const char *) input_items[0];
|
|
float *out = (float *) output_items[0];
|
|
int countin = 0;
|
|
uint32_t countout = 0;
|
|
|
|
uint8_t enc_bits[SYM_PER_PLD * 2]; //type-2 bits, unpacked
|
|
uint8_t rf_bits[SYM_PER_PLD * 2]; //type-4 bits, unpacked
|
|
uint8_t lich[6]; //48 bits packed raw, unencoded LICH
|
|
uint8_t lich_encoded[12]; //96 bits packed, encoded LICH
|
|
|
|
uint8_t data[16], next_data[16]; //raw payload, packed bits
|
|
|
|
while ((countout < (uint32_t) noutput_items) && (countin + 16 <= noutput_items))
|
|
{
|
|
if (!_got_lsf) //stream frames
|
|
{
|
|
//send LSF syncword
|
|
send_syncword (out, &countout, SYNC_LSF);
|
|
|
|
//encode LSF data
|
|
conv_encode_LSF (enc_bits, &_lsf);
|
|
|
|
//reorder bits
|
|
reorder_bits (rf_bits, enc_bits);
|
|
|
|
//randomize
|
|
randomize_bits (rf_bits);
|
|
|
|
//send LSF data
|
|
send_data (out, &countout, rf_bits);
|
|
|
|
//check the SIGNED STREAM flag
|
|
_signed_str = (_lsf.type[0] >> 3) & 1;
|
|
|
|
//set the flag
|
|
_got_lsf = 1;
|
|
}
|
|
|
|
if (_debug == true)
|
|
{
|
|
//destination set to "ALL"
|
|
memset (_next_lsf.dst, 0xFF, 6 * sizeof (uint8_t));
|
|
|
|
//source set to "N0CALL"
|
|
_next_lsf.src[0] = 0x00;
|
|
_next_lsf.src[1] = 0x00;
|
|
_next_lsf.src[2] = 0x4B;
|
|
_next_lsf.src[3] = 0x13;
|
|
_next_lsf.src[4] = 0xD1;
|
|
_next_lsf.src[5] = 0x06;
|
|
|
|
if (_encr_type == ENCR_AES) //AES ENC, 3200 voice
|
|
{
|
|
_next_lsf.type[0] = 0x03;
|
|
_next_lsf.type[1] = 0x95;
|
|
}
|
|
else if (_encr_type == ENCR_SCRAM) //Scrambler ENC, 3200 Voice
|
|
{
|
|
_next_lsf.type[0] = 0x00;
|
|
_next_lsf.type[1] = 0x00;
|
|
if (_scrambler_subtype == 0)
|
|
_next_lsf.type[1] = 0x0D;
|
|
else if (_scrambler_subtype == 1)
|
|
_next_lsf.type[1] = 0x2D;
|
|
else if (_scrambler_subtype == 2)
|
|
_next_lsf.type[1] = 0x4D;
|
|
}
|
|
else //no enc or subtype field, normal 3200 voice
|
|
{
|
|
_next_lsf.type[0] = 0x00;
|
|
_next_lsf.type[1] = 0x05;
|
|
}
|
|
|
|
//a signature key is loaded, OR this bit
|
|
if (_priv_key_loaded == true)
|
|
_next_lsf.type[0] |= 0x8;
|
|
|
|
_finished = false;
|
|
|
|
memset (next_data, 0, sizeof (next_data));
|
|
memcpy (data, next_data, sizeof (data));
|
|
// if (_fn == 60)
|
|
// _finished = true;
|
|
|
|
//debug sig with random payloads (don't play the audio)
|
|
for (uint8_t i = 0; i < 16; i++)
|
|
data[i] = 0x69; //rand() & 0xFF;
|
|
|
|
}
|
|
/*
|
|
//TODO: pass some of these through arguments?
|
|
//read data
|
|
dummy=fread(&(lsf.dst), 6, 1, stdin);
|
|
dummy=fread(&(lsf.src), 6, 1, stdin);
|
|
dummy=fread(&(lsf.type), 2, 1, stdin);
|
|
dummy=fread(&(lsf.meta), 14, 1, stdin);
|
|
dummy=fread(data, 16, 1, stdin);
|
|
*/
|
|
if (countin + 16 <= noutput_items)
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
data[i] = in[countin];
|
|
countin++;
|
|
}
|
|
}
|
|
|
|
//AES encryption enabled - use 112 bits of IV
|
|
if (_encr_type == ENCR_AES)
|
|
{
|
|
memcpy (&(_lsf.meta), _iv, 14);
|
|
_iv[14] = (_fn >> 8) & 0x7F;
|
|
_iv[15] = (_fn >> 0) & 0xFF;
|
|
|
|
//re-calculate LSF CRC with IV insertion
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
}
|
|
|
|
// while (_finished == false)
|
|
{
|
|
if (!_got_lsf)
|
|
{ //debug
|
|
//fprintf(stderr, "LSF\n");
|
|
|
|
//send LSF syncword
|
|
send_syncword (out, &countout, SYNC_LSF);
|
|
|
|
//encode LSF data
|
|
conv_encode_LSF (enc_bits, &_lsf);
|
|
|
|
//reorder bits
|
|
reorder_bits (rf_bits, enc_bits);
|
|
|
|
//randomize
|
|
randomize_bits (rf_bits);
|
|
|
|
//send LSF data
|
|
send_data (out, &countout, rf_bits);
|
|
|
|
//check the SIGNED STREAM flag
|
|
_signed_str = (_lsf.type[0] >> 3) & 1;
|
|
|
|
//set the flag
|
|
_got_lsf = 1;
|
|
}
|
|
|
|
if (_debug == true)
|
|
{
|
|
//destination set to "ALL"
|
|
memset (_next_lsf.dst, 0xFF, 6 * sizeof (uint8_t));
|
|
|
|
//source set to "N0CALL"
|
|
_next_lsf.src[0] = 0x00;
|
|
_next_lsf.src[1] = 0x00;
|
|
_next_lsf.src[2] = 0x4B;
|
|
_next_lsf.src[3] = 0x13;
|
|
_next_lsf.src[4] = 0xD1;
|
|
_next_lsf.src[5] = 0x06;
|
|
|
|
if (_encr_type == ENCR_AES) //AES ENC, 3200 voice
|
|
{
|
|
_next_lsf.type[0] = 0x03;
|
|
_next_lsf.type[1] = 0x95;
|
|
}
|
|
else if (_encr_type == ENCR_SCRAM) //Scrambler ENC, 3200 Voice
|
|
{
|
|
_next_lsf.type[0] = 0x00;
|
|
_next_lsf.type[1] = 0x00;
|
|
if (_scrambler_subtype == 0)
|
|
_next_lsf.type[1] = 0x0D;
|
|
else if (_scrambler_subtype == 1)
|
|
_next_lsf.type[1] = 0x2D;
|
|
else if (_scrambler_subtype == 2)
|
|
_next_lsf.type[1] = 0x4D;
|
|
}
|
|
else //no enc or subtype field, normal 3200 voice
|
|
{
|
|
_next_lsf.type[0] = 0x00;
|
|
_next_lsf.type[1] = 0x05;
|
|
}
|
|
|
|
//a signature key is loaded, OR this bit
|
|
if (_priv_key_loaded == true)
|
|
_next_lsf.type[0] |= 0x8;
|
|
|
|
_finished = false;
|
|
|
|
memset (next_data, 0, sizeof (next_data));
|
|
memcpy (data, next_data, sizeof (data));
|
|
if (_fn == 60)
|
|
_finished = 1;
|
|
|
|
//debug sig with random payloads (don't play the audio)
|
|
for (uint8_t i = 0; i < 16; i++)
|
|
data[i] = 0x69; //rand() & 0xFF;
|
|
|
|
} // end of debug==true
|
|
else
|
|
{
|
|
/*
|
|
//check if theres any more data
|
|
if(fread(&(next_lsf.dst), 6, 1, stdin)<1) finished=1;
|
|
if(fread(&(next_lsf.src), 6, 1, stdin)<1) finished=1;
|
|
if(fread(&(next_lsf.type), 2, 1, stdin)<1) finished=1;
|
|
if(fread(&(next_lsf.meta), 14, 1, stdin)<1) finished=1;
|
|
if(fread(next_data, 16, 1, stdin)<1) _finished=true;
|
|
*/
|
|
if (countin + 16 <= noutput_items)
|
|
{
|
|
for (int i = 0; i < 16; i++)
|
|
{
|
|
next_data[i] = in[countin];
|
|
countin++;
|
|
}
|
|
}
|
|
}
|
|
|
|
//AES
|
|
if (_encr_type == ENCR_AES)
|
|
{
|
|
memcpy (&(_next_lsf.meta), _iv, 14);
|
|
_iv[14] = (_fn >> 8) & 0x7F;
|
|
_iv[15] = (_fn >> 0) & 0xFF;
|
|
aes_ctr_bytewise_payload_crypt (_iv, _key, data, AES128); //hardcoded for now
|
|
}
|
|
|
|
//Scrambler
|
|
else if (_encr_type == ENCR_SCRAM)
|
|
{
|
|
scrambler_sequence_generator ();
|
|
for (uint8_t i = 0; i < 16; i++)
|
|
{
|
|
data[i] ^= _scr_bytes[i];
|
|
}
|
|
}
|
|
|
|
if (_finished == false)
|
|
{
|
|
send_syncword (out, &countout, SYNC_STR);
|
|
extract_LICH (lich, _lich_cnt, &_lsf);
|
|
encode_LICH (lich_encoded, lich);
|
|
unpack_LICH (enc_bits, lich_encoded);
|
|
conv_encode_stream_frame (&enc_bits[96], data, _fn);
|
|
reorder_bits (rf_bits, enc_bits);
|
|
randomize_bits (rf_bits);
|
|
send_data (out, &countout, rf_bits);
|
|
_fn = (_fn + 1) % 0x8000; //increment FN
|
|
_lich_cnt = (_lich_cnt + 1) % 6; //continue with next LICH_CNT
|
|
|
|
//update the stream digest if required
|
|
if (_signed_str)
|
|
{
|
|
for (uint8_t i = 0; i < sizeof (_digest); i++)
|
|
_digest[i] ^= data[i];
|
|
uint8_t tmp = _digest[0];
|
|
for (uint8_t i = 0; i < sizeof (_digest) - 1; i++)
|
|
_digest[i] = _digest[i + 1];
|
|
_digest[sizeof (_digest) - 1] = tmp;
|
|
}
|
|
|
|
//update LSF every 6 frames (superframe boundary)
|
|
if (_fn > 0 && _lich_cnt == 0)
|
|
{
|
|
_lsf = _next_lsf;
|
|
|
|
//calculate LSF CRC
|
|
uint16_t ccrc = LSF_CRC (&_lsf);
|
|
_lsf.crc[0] = ccrc >> 8;
|
|
_lsf.crc[1] = ccrc & 0xFF;
|
|
}
|
|
|
|
memcpy (data, next_data, 16);
|
|
}
|
|
else //send last frame(s)
|
|
{ printf("Sending last frame\n");
|
|
send_syncword (out, &countout, SYNC_STR);
|
|
extract_LICH (lich, _lich_cnt, &_lsf);
|
|
encode_LICH (lich_encoded, lich);
|
|
unpack_LICH (enc_bits, lich_encoded);
|
|
if (!_signed_str)
|
|
conv_encode_stream_frame (&enc_bits[96], data,
|
|
(_fn | 0x8000));
|
|
else
|
|
conv_encode_stream_frame (&enc_bits[96], data, _fn);
|
|
reorder_bits (rf_bits, enc_bits);
|
|
randomize_bits (rf_bits);
|
|
send_data (out, &countout, rf_bits);
|
|
_lich_cnt = (_lich_cnt + 1) % 6; //continue with next LICH_CNT
|
|
|
|
//if we are done, and the stream is signed, so we need to transmit the signature (4 frames)
|
|
if (_signed_str)
|
|
{
|
|
//update digest
|
|
for (uint8_t i = 0; i < sizeof (_digest); i++)
|
|
_digest[i] ^= data[i];
|
|
uint8_t tmp = _digest[0];
|
|
for (uint8_t i = 0; i < sizeof (_digest) - 1; i++)
|
|
_digest[i] = _digest[i + 1];
|
|
_digest[sizeof (_digest) - 1] = tmp;
|
|
|
|
//sign the digest
|
|
uECC_sign (_priv_key, _digest, sizeof (_digest),
|
|
_sig, _curve);
|
|
|
|
//4 frames with 512-bit signature
|
|
_fn = 0x7FFC; //signature has to start at 0x7FFC to end at 0x7FFF (0xFFFF with EoT marker set)
|
|
for (uint8_t i = 0; i < 4; i++)
|
|
{
|
|
send_syncword (out, &countout, SYNC_STR);
|
|
extract_LICH (lich, _lich_cnt, &_lsf);
|
|
encode_LICH (lich_encoded, lich);
|
|
unpack_LICH (enc_bits, lich_encoded);
|
|
conv_encode_stream_frame (&enc_bits[96],
|
|
&_sig[i * 16], _fn);
|
|
reorder_bits (rf_bits, enc_bits);
|
|
randomize_bits (rf_bits);
|
|
send_data (out, &countout, rf_bits);
|
|
_fn =
|
|
(_fn < 0x7FFE) ? _fn + 1 : (0x7FFF | 0x8000);
|
|
_lich_cnt = (_lich_cnt + 1) % 6; //continue with next LICH_CNT
|
|
}
|
|
if (_debug == true)
|
|
{
|
|
fprintf (stderr, "Signature: ");
|
|
for (uint8_t i = 0; i < sizeof (_sig); i++)
|
|
{
|
|
if (i == 16 || i == 32 || i == 48)
|
|
fprintf (stderr, "\n ");
|
|
fprintf (stderr, "%02X", _sig[i]);
|
|
}
|
|
|
|
fprintf (stderr, "\n");
|
|
}
|
|
}
|
|
//send EOT frame
|
|
send_eot (out, &countout);
|
|
//fprintf(stderr, "Stream has ended. Exiting.\n");
|
|
} // finished == true
|
|
} // finished == false
|
|
} // loop on input data
|
|
// Tell runtime system how many input items we consumed on
|
|
// each input stream.
|
|
consume_each (countin);
|
|
// printf(" noutput_items=%d countin=%d countout=%d\n",noutput_items,countin,countout);
|
|
// Tell runtime system how many output items we produced.
|
|
if (_finished==false)
|
|
return countout;
|
|
else {printf("Killing flowgraph\n"); return -1;} // https://lists.gnu.org/archive/html/discuss-gnuradio/2016-12/msg00206.html
|
|
// returning -1 (which is the magical value for "there's nothing coming anymore, you can shut down") would normally end a flow graph
|
|
}
|
|
|
|
} /* namespace m17 */
|
|
} /* namespace gr */
|