op25/op25/gr-op25/lib/crypto.cc

287 lines
6.4 KiB
C++

#include "crypto.h"
#include <sstream>
#include <boost/format.hpp>
#include <iostream>
#include <stdio.h>
#include <memory.h>
extern "C" {
#include "des.h"
}
static unsigned long long swap_bytes(uint64_t l)
{
unsigned long long r;
unsigned char* pL = (unsigned char*)&l;
unsigned char* pR = (unsigned char*)&r;
for (int i = 0; i < sizeof(l); ++i)
pR[i] = pL[(sizeof(l) - 1) - i];
return r;
}
///////////////////////////////////////////////////////////////////////////////
/*
class null_algorithm : public crypto_algorithm // This is an algorithm skeleton (can be used for no encryption as pass-through)
{
private:
size_t m_generated_bits;
public:
null_algorithm()
: m_generated_bits(0)
{
}
const type_id id() const
{
return crypto_algorithm::NONE;
}
bool update(const struct CryptoState& state)
{
fprintf(stderr, "NULL:\t%d bits generated\n", m_generated_bits);
m_generated_bits = 0;
return true;
}
bool set_key(const crypto_algorithm::key_type& key)
{
return true;
}
uint64_t generate(size_t n)
{
m_generated_bits += n;
return 0;
}
};
*/
///////////////////////////////////////////////////////////////////////////////
class des_ofb : public crypto_algorithm
{
public:
unsigned long long m_key_des, m_next_iv, m_ks;
int m_ks_idx;
DES_KS m_ksDES;
int m_iterations;
uint16_t m_current_kid;
key_type m_default_key;
key_map_type m_key_map;
bool m_verbose;
public:
des_ofb()
: m_current_kid(-1)
{
memset(&m_ksDES, 0, sizeof(m_ksDES));
m_key_des = 0;
m_next_iv = 0;
m_ks_idx = 0;
m_ks = 0;
m_iterations = 0;
}
void set_logging(bool on)
{
m_verbose = on;
}
const type_id id() const
{
return crypto_algorithm::DES_OFB;
}
bool update(const struct CryptoState& state)
{
if (m_current_kid != state.kid)
{
if (m_key_map.empty())
{
// Nothing to do
}
else
{
key_map_type::iterator it = m_key_map.find(state.kid);
if (it != m_key_map.end())
{
set_key(it->second);
}
else if (!m_default_key.empty())
{
/*if (m_verbose) */fprintf(stderr, "Key 0x%04x not found in key map - using default key\n", state.kid);
set_key(m_default_key);
}
else
{
/*if (m_verbose) */fprintf(stderr, "Key 0x%04x not found in key map and no default key\n", state.kid);
}
}
m_current_kid = state.kid;
}
uint64_t iv = 0;
size_t n = std::min(sizeof(iv), state.mi.size());
memcpy(&iv, &state.mi[0], n);
set_iv(iv);
return (n == 8);
}
void set_key_map(const key_map_type& key_map)
{
m_key_map = key_map;
m_current_kid = -1; // To refresh on next update if it has changed
}
bool set_key(const crypto_algorithm::key_type& key)
{
const size_t valid_key_length = 8;
if (key.size() != valid_key_length)
{
if (m_verbose) fprintf(stderr, "DES:\tIncorrect key length of %lu (should be %lu)\n", key.size(), valid_key_length);
return false;
}
m_default_key = key;
memcpy(&m_key_des, &key[0], std::min(key.size(), sizeof(m_key_des)));
if (m_verbose)
{
std::stringstream ss;
for (int i = 0; i < valid_key_length; ++i)
ss << boost::format("%02X") % (int)key[i];
std::cerr << "DES:\tKey: " << ss.str() << std::endl;
}
deskey(m_ksDES, (unsigned char*)&m_key_des, 0); // 0: encrypt (for OFB mode)
return true;
}
void set_iv(uint64_t iv)
{
if (m_iterations > 0)
{
if (m_verbose) fprintf(stderr, "DES:\t%i bits used from %i iterations\n", m_ks_idx, m_iterations);
}
m_next_iv = iv;
m_ks_idx = 0;
m_iterations = 0;
m_ks = m_next_iv;
des(m_ksDES, (unsigned char*)&m_ks); // First initialisation
++m_iterations;
des(m_ksDES, (unsigned char*)&m_ks); // Throw out first iteration & prepare for second
++m_iterations;
generate(64); // Reserved 3 + first 5 of LC (3 left)
generate(3 * 8); // Use remaining 3 bytes for LC
}
uint64_t generate(size_t count) // 1..64
{
unsigned long long ullCurrent = swap_bytes(m_ks);
const int max_len = 64;
int pos = m_ks_idx % max_len;
m_ks_idx += count;
if ((pos + count) <= max_len) // Up to 64
{
if ((m_ks_idx % max_len) == 0)
{
des(m_ksDES, (unsigned char*)&m_ks); // Prepare for next iteration
++m_iterations;
}
unsigned long long result = (ullCurrent >> (((max_len - 1) - pos) - (count-1))) & ((count == max_len) ? (unsigned long long)-1 : ((1ULL << count) - 1));
return result;
}
// Over-flow 64-bit boundary (so all of rest of current will be used)
des(m_ksDES, (unsigned char*)&m_ks); // Compute second part
++m_iterations;
unsigned long long first = ullCurrent << pos; // RHS will be zeros
ullCurrent = swap_bytes(m_ks);
int remainder = count - (max_len - pos);
first >>= (((max_len - 1) - remainder) - ((max_len - 1) - pos));
unsigned long long next = (ullCurrent >> (((max_len - 1) - 0) - (remainder-1))) & ((1ULL << remainder) - 1);
return (first | next);
}
};
///////////////////////////////////////////////////////////////////////////////
crypto_module::crypto_module(bool verbose/* = true*/)
: d_verbose(verbose)
{
}
crypto_algorithm::sptr crypto_module::algorithm(crypto_algorithm::type_id algid)
{
if ((!d_current_algorithm && (algid == crypto_algorithm::NONE)) || // This line should be commented out if 'null_algorithm' is to be tested
(d_current_algorithm && (algid == d_current_algorithm->id())))
return d_current_algorithm;
switch (algid)
{
case crypto_algorithm::DES_OFB:
d_current_algorithm = crypto_algorithm::sptr(new des_ofb());
break;
//case crypto_algorithm::NONE:
// d_current_algorithm = crypto_algorithm::sptr(new null_algorithm());
// break;
default:
d_current_algorithm = crypto_algorithm::sptr();
};
if (d_current_algorithm)
{
d_current_algorithm->set_logging(logging_enabled());
if (!d_persistent_key_map.empty())
d_current_algorithm->set_key_map(d_persistent_key_map);
if (!d_persistent_key.empty())
d_current_algorithm->set_key(d_persistent_key);
}
return d_current_algorithm;
}
void crypto_module::set_key(const crypto_algorithm::key_type& key)
{
d_persistent_key = key;
if (d_current_algorithm)
d_current_algorithm->set_key(d_persistent_key);
}
void crypto_module::set_key_map(const crypto_algorithm::key_map_type& keys)
{
d_persistent_key_map = keys;
if (d_current_algorithm)
d_current_algorithm->set_key_map(d_persistent_key_map);
}
void crypto_module::set_logging(bool on/* = true*/)
{
d_verbose = on;
if (d_current_algorithm)
d_current_algorithm->set_logging(on);
}