based on cryptical envelopment, and it has no foulups, unlike blowfish.
#include <mathematics/chaos.h>
#include <structures/static_memory_gremlin.h>
-#include <openssl/blowfish.h>
#include <openssl/err.h>
#include <openssl/evp.h>
*/
blowfish_crypto::blowfish_crypto(int key_size)
-: cryptical_envelopment(key_size, EVP_bf_cbc())
+: cryptical_envelopment(EVP_bf_cbc())
///_key_size(key_size),
/// _key(new byte_array)
{
+ set_key(key_size);
/*
FUNCDEF("ctor(int)");
static_ssl_initializer();
}
blowfish_crypto::blowfish_crypto(const byte_array &key, int key_size)
-: cryptical_envelopment(key, key_size, EVP_bf_cbc())
+: cryptical_envelopment(EVP_bf_cbc())
//: _key_size(key_size),
// _key(new byte_array(key))
{
+ set_key(key, key_size);
/*
FUNCDEF("ctor(byte_array,int)");
static_ssl_initializer();
DEFINE_CLASS_NAME("blowfish_crypto");
+ // blowfish relevant values for appropriate key sizes.
+ virtual int minimum_key_size() const { return 32; }
+ virtual int maximum_key_size() const { return 448; }
+
/// int key_size() const; // returns the size of our key, in bits.
/// static int minimum_key_size();
ERR_error_string(ERR_get_error(), NULL_POINTER)
#ifdef DEBUG_CRYPTICAL_ENVELOPMENT
-
-/// // this macro checks on the validity of the key sizes (in bits).
-/// #define DISCUSS_KEY_SIZE(key_size) \
-/// if (key_size < minimum_key_size()) { \
-/// deadly_error(static_class_name(), func, \
-/// a_sprintf("key size (%d bits) is less than minimum key size %d.", \
-/// key_size, minimum_key_size())); \
-/// } \
-/// if (key_size > maximum_key_size()) { \
-/// deadly_error(static_class_name(), func, \
-/// a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
-/// key_size, maximum_key_size())); \
-/// }
-
- // this macro checks that the key in the byte array has enough bytes for
- // the key size bits.
- #define DISCUSS_PROVIDED_KEY(key_size, key) \
- if (key.length() * BITS_PER_BYTE < key_size) { \
- deadly_error(static_class_name(), func, \
- a_sprintf("key array length (%d) is less than required by key size " \
- "(%d bits).", key.length(), key_size)); \
- }
+ // only cause a program stop if we're in debugging mode.
+//hmmm: that's pretty loose, really; if they have a key size error, how can we keep going and just pretend that's okay? we should not.
+ #define ERROR_BAILOUT(a, b, c) deadly_error(a, b, c)
#else
- #define DISCUSS_PROVIDED_KEY(key_size, key)
-//// #define DISCUSS_KEY_SIZE(key_size)
+ #define ERROR_BAILOUT(a, b, c)
#endif
-cryptical_envelopment::cryptical_envelopment(int key_size, const EVP_CIPHER *cipher_type)
-: _key_size(key_size),
- _key(new byte_array),
- _cipher_type(cipher_type)
-{
- FUNCDEF("ctor(int)");
- static_ssl_initializer();
-//// LOG("prior to key size discuss");
-//// DISCUSS_KEY_SIZE(key_size);
-//// if (key_size < minimum_key_size())
-//// _key_size = minimum_key_size();
-//// if (key_size > maximum_key_size())
-//// _key_size = maximum_key_size();
- LOG("prior to generate key");
- generate_key(_key_size, *_key);
- LOG("after generate key");
-}
+// this macro checks on the validity of the key sizes (in bits).
+#define DISCUSS_KEY_SIZE(key_size) \
+ if (key_size < minimum_key_size()) { \
+ ERROR_BAILOUT(static_class_name(), func, \
+ a_sprintf("key size (%d bits) is less than minimum key size %d.", \
+ key_size, minimum_key_size())); \
+ return false; \
+ } \
+ if (key_size > maximum_key_size()) { \
+ ERROR_BAILOUT(static_class_name(), func, \
+ a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
+ key_size, maximum_key_size())); \
+ return false; \
+ }
+
+// this macro checks that the key in the byte array has enough bytes for
+// the key size bits.
+#define DISCUSS_PROVIDED_KEY(key_size, key) \
+ if (key.length() * BITS_PER_BYTE < key_size) { \
+ ERROR_BAILOUT(static_class_name(), func, \
+ a_sprintf("key array length (%d) is less than required by key size " \
+ "(%d bits).", key.length(), key_size)); \
+ return false; \
+ }
-cryptical_envelopment::cryptical_envelopment(const byte_array &key, int key_size, const EVP_CIPHER *cipher_type)
-: _key_size(key_size),
- _key(new byte_array(key)),
+cryptical_envelopment::cryptical_envelopment(const EVP_CIPHER *cipher_type)
+: _key_size(0),
+ _key(new byte_array()),
_cipher_type(cipher_type)
{
- FUNCDEF("ctor(byte_array,int)");
+ FUNCDEF("ctor(int)");
static_ssl_initializer();
- // any problems with the key provided are horrid. they will yield a
- // non-working blowfish object.
-//// LOG("prior to key size discuss");
-//// DISCUSS_KEY_SIZE(key_size);
- LOG("prior to provided key discuss");
- DISCUSS_PROVIDED_KEY(key_size, key);
- LOG("prior to ssl static init");
- LOG("after ssl static init");
}
cryptical_envelopment::cryptical_envelopment(const cryptical_envelopment &to_copy)
LOG("after key whack");
}
+bool cryptical_envelopment::set_key(int key_size)
+{
+ FUNCDEF("set_key(int)");
+ DISCUSS_KEY_SIZE(key_size);
+/// if (key_size < minimum_key_size())
+/// _key_size = minimum_key_size();
+/// if (key_size > maximum_key_size())
+/// _key_size = maximum_key_size();
+ LOG("prior to generate key");
+ _key_size = key_size;
+/// WHACK(_key); // clear any existing key.
+/// _key = new byte_array();
+ bool to_return = generate_key(_key_size, *_key);
+ LOG("after generate key");
+ return to_return;
+}
+
+/*
+bool cryptical_envelopment::set_key(const byte_array &key, int key_size)
+{
+ FUNCDEF("set_key(byte_array,int)");
+//// static_ssl_initializer();
+ DISCUSS_KEY_SIZE(key_size);
+ DISCUSS_PROVIDED_KEY(key_size, key);
+ _key_size = key_size;
+ _key = new byte_array(key);
+ return true;
+}
+*/
+
int cryptical_envelopment::key_size() const { return _key_size; }
const byte_array &cryptical_envelopment::get_key() const { return *_key; }
-/////hmmm: these are not right for all encryption types.
-///int cryptical_envelopment::minimum_key_size() { return 64; }
-///
-///int cryptical_envelopment::maximum_key_size() { return 448; }
-
cryptical_envelopment &cryptical_envelopment::operator = (const cryptical_envelopment &to_copy)
{
if (this == &to_copy) return *this;
bool cryptical_envelopment::set_key(const byte_array &new_key, int key_size)
{
- FUNCDEF("set_key");
- if (!new_key.length()) return false;
-//// DISCUSS_KEY_SIZE(key_size);
+ FUNCDEF("set_key(byte_array,int)");
+// if (!new_key.length()) return false;
+ DISCUSS_KEY_SIZE(key_size);
DISCUSS_PROVIDED_KEY(key_size, new_key);
-//// if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
-//// return false;
- if (new_key.length() * BITS_PER_BYTE < key_size) return false;
+// if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
+// return false;
+// if (new_key.length() * BITS_PER_BYTE < key_size) return false;
+//// if (!_key)
+//// _key = new byte_array();
_key_size = key_size;
*_key = new_key;
return true;
}
-void cryptical_envelopment::generate_key(int size, byte_array &new_key)
+//hmmm: move someplace more useful.
+#define BYTE_TO_BINARY_PATTERN "%c%c%c%c%c%c%c%c"
+#define BYTE_TO_BINARY(byte) \
+ ((byte) & 0x80 ? '1' : '0'), \
+ ((byte) & 0x40 ? '1' : '0'), \
+ ((byte) & 0x20 ? '1' : '0'), \
+ ((byte) & 0x10 ? '1' : '0'), \
+ ((byte) & 0x08 ? '1' : '0'), \
+ ((byte) & 0x04 ? '1' : '0'), \
+ ((byte) & 0x02 ? '1' : '0'), \
+ ((byte) & 0x01 ? '1' : '0')
+
+bool cryptical_envelopment::generate_key(int size, byte_array &new_key)
{
FUNCDEF("generate_key");
static_ssl_initializer();
-//// DISCUSS_KEY_SIZE(size);
-//// if (size < minimum_key_size())
-//// size = minimum_key_size();
-//// else if (size > maximum_key_size())
-//// size = maximum_key_size();
+//// if (!_key)
+//// _key = new byte_array();
+ DISCUSS_KEY_SIZE(size);
+// if (size < minimum_key_size())
+// size = minimum_key_size();
+// else if (size > maximum_key_size())
+// size = maximum_key_size();
int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed.
if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion.
new_key.reset(bytes);
for (int i = 0; i < bytes; i++)
new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
+//code that we thought might be necessary but which doesn't help blowfish not suck.
+//#define WIPE_UNUSED_BITS
+#ifdef WIPE_UNUSED_BITS
+ // clear the bits that cannot be non-zero for a key whose bits are not evenly divisible by 8.
+ int bits_to_wipe = BITS_PER_BYTE - (size % BITS_PER_BYTE);
+ALWAYS_LOG(a_sprintf("saying we need to zap %d bits from result.", bits_to_wipe));
+ abyte mask = 0xFF;
//hmmm: if we leave non-zero stuff in the last byte, that's not quite right!
// also a question of endian-ness of where to zap those bits. argh!
- // clear the bits that cannot be non-zero for a key whose bits are not evenly divisible by 8.
+ if (bits_to_wipe) {
+ // rotate our mask to cover the number of bits we want to actually use.
+ mask <<= bits_to_wipe;
+ALWAYS_LOG(a_sprintf("mask now: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(mask)));
+ new_key[bytes - 1] &= mask;
+ALWAYS_LOG(a_sprintf("last byte now: " BYTE_TO_BINARY_PATTERN, BYTE_TO_BINARY(new_key[bytes - 1])));
+ }
+#endif //wipe.
+ return true;
}
SAFE_STATIC(mutex, __vector_init_lock, )
-//hmmm: this seems like a bad security situation, because we are using a single process for
-// creating init vectors, so it's super easy to guess these (by reading the code, for
-// example). is this still secure, given that the keys are not known to an attacker?
+/*
+hmmm: this seems like a bad security situation, because we are using a single algorithm for
+ creating init vectors, so it's super easy to guess these (by reading the code, for
+ example). is this still secure, given that the keys are not known to an attacker?
+*/
const byte_array &cryptical_envelopment::init_vector()
{
FUNCDEF("init_vector");
static bool initted = false;
if (!initted) {
LOG(">> actually creating init_vector >>");
-//hmmm: we're okay with the wrap-around on the byte type (going negative) if the init vector length is longer than 214?
+//hmmm: are we okay with the wrap-around on the byte type (going negative) if the init vector length is longer than 214?
for (int i = 0; i < to_return.length(); i++)
to_return[i] = abyte(214 - i);
initted = true;
EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
EVP_CIPHER_CTX_init(session);
-//new rules!
-//EVP_EncryptInit to set the cipher, but leave key and IV null and unset
-//EVP_CIPHER_CTX_set_key_length and EVP_CTRL_AEAD_SET_IVLEN
-//EVP_EncryptInit again. This time leave cipher null, because you've already set it, and set the key and IV.
+ //new rules!
+ // EVP_EncryptInit to set the cipher, but leave key and IV null and unset
+ // EVP_CIPHER_CTX_set_key_length and EVP_CTRL_AEAD_SET_IVLEN
+ // EVP_EncryptInit again. This time leave cipher null, because you've already set it, and set the key and IV.
int initret = EVP_EncryptInit_ex(session, _cipher_type, NULL_POINTER, NULL_POINTER, NULL_POINTER);
if (!initret) {
class cryptical_envelopment : public virtual basis::root_object
{
public:
- cryptical_envelopment(int key_size, const EVP_CIPHER *cipher_type);
- //!< this will create a new random key of the "key_size", in bits.
- /*!< the valid sizes for keys depend on the algorithm in question. */
-
- cryptical_envelopment(const basis::byte_array &key, int key_size, const EVP_CIPHER *cipher_type);
- //!< uses a pre-existing "key" for encryption.
-
+ cryptical_envelopment(const EVP_CIPHER *cipher_type);
+ //!< constructor requires the type of encryption to use.
cryptical_envelopment(const cryptical_envelopment &to_copy); //!< copy constructor.
virtual ~cryptical_envelopment();
cryptical_envelopment &operator = (const cryptical_envelopment &to_copy);
+ bool set_key(int key_size);
+ //!< this will create a new random key of the "key_size", in bits.
+ /*!< the valid sizes for keys depend on the algorithm in question. */
+
+ bool set_key(const basis::byte_array &new_key, int key_size);
+ //!< sets the encryption key to "new_key" with a "key_size" in bits.
+
DEFINE_CLASS_NAME("cryptical_envelopment");
int key_size() const; // returns the size of our key, in bits.
-/// static int minimum_key_size();
-/// //!< returns the minimum key size (in bits) supported here.
-/// static int maximum_key_size();
-/// //!< returns the maximum key size (in bits) supported here.
+ // derived classes must provide the min and max for key sizes.
+ virtual int minimum_key_size() const = 0;
+ //!< returns the minimum key size (in bits) supported here.
+ virtual int maximum_key_size() const = 0;
+ //!< returns the maximum key size (in bits) supported here.
const basis::byte_array &get_key() const; //!< returns our current key.
- bool set_key(const basis::byte_array &new_key, int key_size);
- //!< sets the encryption key to "new_key" with a "key_size" in bits.
-
- static void generate_key(int size, basis::byte_array &new_key);
+ bool generate_key(int size, basis::byte_array &new_key);
//!< creates a "new_key" of the "size" (in bits) specified.
bool encrypt(const basis::byte_array &source, basis::byte_array &target) const;
TYPE = library
PROJECT = crypto
-SOURCE = blowfish_crypto.cpp cryptical_envelopment.cpp old_school_rsa_crypto.cpp ssl_init.cpp
+SOURCE = blowfish_crypto.cpp cryptical_envelopment.cpp old_school_rsa_crypto.cpp ssl_init.cpp twofish_crypto.cpp
USE_SSL = t
TARGETS = crypto.lib
--- /dev/null
+/*
+* Name : twofish encryption
+* Author : Chris Koeritz
+*****
+* Copyright (c) 2005-$now By Author. This program 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 2 of
+* the License or (at your option) any later version. This is online at:
+* http://www.fsf.org/copyleft/gpl.html
+* Please send any updates to: fred@gruntose.com
+*/
+
+#include "twofish_crypto.h"
+#include "ssl_init.h"
+
+#include <basis/astring.h>
+#include <basis/functions.h>
+#include <basis/mutex.h>
+#include <loggers/critical_events.h>
+#include <loggers/program_wide_logger.h>
+#include <mathematics/chaos.h>
+#include <structures/static_memory_gremlin.h>
+
+//#include <openssl/twofish.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+
+using namespace basis;
+using namespace loggers;
+using namespace mathematics;
+using namespace structures;
+
+namespace crypto {
+
+////const int FUDGE = 128;
+ // extra space for the cipher's block size. twofish is only 8 bytes for
+ // the cipher block size, but we ensure there will definitely be no
+ // problems.
+
+//#undef set_key
+ // get rid of a macro we don't want.
+
+//#define DEBUG_TWOFISH
+ // uncomment for noisier version.
+#undef ALWAYS_LOG
+#define ALWAYS_LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
+#ifdef DEBUG_TWOFISH
+ #undef LOG
+ #define LOG(t) CLASS_EMERGENCY_LOG(program_wide_logger::get(), t)
+#else
+ #undef LOG
+ #define LOG(t)
+#endif
+
+// helpful macro for the error string of last failure.
+//#define GET_SSL_ERROR() \
+// ERR_error_string(ERR_get_error(), NULL_POINTER)
+
+/*
+#ifdef DEBUG_TWOFISH
+ // this macro checks on the validity of the key sizes (in bits).
+ #define DISCUSS_KEY_SIZE(key_size) \
+ if (key_size < minimum_key_size()) { \
+ deadly_error(static_class_name(), func, \
+ a_sprintf("key size (%d bits) is less than minimum key size %d.", \
+ key_size, minimum_key_size())); \
+ } \
+ if (key_size > maximum_key_size()) { \
+ deadly_error(static_class_name(), func, \
+ a_sprintf("key size (%d bits) is greater than maximum key size %d.", \
+ key_size, maximum_key_size())); \
+ }
+
+ // this macro checks that the key in the byte array has enough bytes for
+ // the key size bits.
+ #define DISCUSS_PROVIDED_KEY(key_size, key) \
+ if (key.length() * BITS_PER_BYTE < key_size) { \
+ deadly_error(static_class_name(), func, \
+ a_sprintf("key array length (%d) is less than required by key size " \
+ "(%d bits).", key.length(), key_size)); \
+ }
+#else
+ #define DISCUSS_PROVIDED_KEY(key_size, key)
+ #define DISCUSS_KEY_SIZE(key_size)
+#endif
+*/
+
+twofish_crypto::twofish_crypto(int key_size)
+: cryptical_envelopment(EVP_aes_256_cbc())
+///_key_size(key_size),
+/// _key(new byte_array)
+{
+ set_key(key_size);
+/*
+ FUNCDEF("ctor(int)");
+ static_ssl_initializer();
+ LOG("prior to key size discuss");
+ DISCUSS_KEY_SIZE(key_size);
+ if (key_size < minimum_key_size())
+ _key_size = minimum_key_size();
+ if (key_size > maximum_key_size())
+ _key_size = maximum_key_size();
+ LOG("prior to generate key");
+ generate_key(_key_size, *_key);
+ LOG("after generate key");
+*/
+}
+
+twofish_crypto::twofish_crypto(const byte_array &key, int key_size)
+: cryptical_envelopment(EVP_aes_256_cbc())
+//: _key_size(key_size),
+// _key(new byte_array(key))
+{
+ set_key(key, key_size);
+/*
+ FUNCDEF("ctor(byte_array,int)");
+ static_ssl_initializer();
+ // any problems with the key provided are horrid. they will yield a
+ // non-working twofish object.
+ LOG("prior to key size discuss");
+ DISCUSS_KEY_SIZE(key_size);
+ LOG("prior to provided key discuss");
+ DISCUSS_PROVIDED_KEY(key_size, key);
+ LOG("prior to ssl static init");
+ LOG("after ssl static init");
+*/
+}
+
+twofish_crypto::twofish_crypto(const twofish_crypto &to_copy)
+: root_object(),
+ cryptical_envelopment(*this)
+/// _key_size(to_copy._key_size),
+/// _key(new byte_array(*to_copy._key))
+{
+/// FUNCDEF("copy ctor");
+/// static_ssl_initializer();
+/// LOG("after ssl static init");
+}
+
+twofish_crypto::~twofish_crypto()
+{
+/*
+ FUNCDEF("destructor");
+ LOG("prior to key whack");
+ WHACK(_key);
+ LOG("after key whack");
+*/
+}
+
+/*
+int twofish_crypto::key_size() const { return _key_size; }
+
+const byte_array &twofish_crypto::get_key() const { return *_key; }
+
+int twofish_crypto::minimum_key_size() { return 64; }
+
+int twofish_crypto::maximum_key_size() { return 448; }
+*/
+
+twofish_crypto &twofish_crypto::operator = (const twofish_crypto &to_copy)
+{
+ if (this == &to_copy) return *this;
+ *((cryptical_envelopment *)this) = *((cryptical_envelopment *)&to_copy);
+//hmmm: is that the best way to do this?
+
+// _key_size = to_copy._key_size;
+// *_key = *to_copy._key;
+ return *this;
+}
+
+/*
+bool twofish_crypto::set_key(const byte_array &new_key, int key_size)
+{
+ FUNCDEF("set_key");
+ if (!new_key.length()) return false;
+ DISCUSS_KEY_SIZE(key_size);
+ DISCUSS_PROVIDED_KEY(key_size, new_key);
+ if ( (key_size < minimum_key_size()) || (key_size > maximum_key_size()) )
+ return false;
+ if (new_key.length() * BITS_PER_BYTE < key_size) return false;
+ _key_size = key_size;
+ *_key = new_key;
+ return true;
+}
+
+void twofish_crypto::generate_key(int size, byte_array &new_key)
+{
+ FUNCDEF("generate_key");
+ static_ssl_initializer();
+ DISCUSS_KEY_SIZE(size);
+ if (size < minimum_key_size())
+ size = minimum_key_size();
+ else if (size > maximum_key_size())
+ size = maximum_key_size();
+ int bytes = size / BITS_PER_BYTE; // calculate the number of bytes needed.
+ if (size % BITS_PER_BYTE) bytes++; // add one for non-integral portion.
+ new_key.reset(bytes);
+ for (int i = 0; i < bytes; i++)
+ new_key[i] = static_ssl_initializer().randomizer().inclusive(0, 255);
+}
+
+SAFE_STATIC(mutex, __vector_init_lock, )
+
+const byte_array &twofish_crypto::init_vector()
+{
+ FUNCDEF("init_vector");
+ static_ssl_initializer();
+ auto_synchronizer locking(__vector_init_lock());
+ static byte_array to_return(EVP_MAX_IV_LENGTH);
+ static bool initted = false;
+ if (!initted) {
+ LOG("actually doing init");
+ for (int i = 0; i < to_return.length(); i++)
+ to_return[i] = abyte(214 - i);
+ initted = true;
+ LOG("finished init process");
+ }
+ return to_return;
+}
+
+bool twofish_crypto::encrypt(const byte_array &source,
+ byte_array &target) const
+{
+ FUNCDEF("encrypt");
+ALWAYS_LOG(">>encrypt>>");
+ target.reset();
+ if (!_key->length() || !source.length()) return false;
+ bool to_return = true;
+
+LOG(a_sprintf(" encrypting %d bytes", source.length()));
+
+ // initialize an encoding session.
+ EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
+ EVP_CIPHER_CTX_init(session);
+
+//new rules!
+//EVP_EncryptInit to set the cipher, but leave key and IV null and unset
+//EVP_CIPHER_CTX_set_key_length and EVP_CTRL_AEAD_SET_IVLEN
+//EVP_EncryptInit again. This time leave cipher null, because you've already set it, and set the key and IV.
+
+ int initret = EVP_EncryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, NULL_POINTER, NULL_POINTER);
+ if (!initret) {
+ // zero means a failure of the initialization.
+ ALWAYS_LOG(a_sprintf("failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
+ exit(1);
+ }
+ LOG(a_sprintf(" calling set key len with key size of %d", _key_size));
+ // new fancy footwork needed to keep openssl from blowing up and claiming we didn't set the key.
+//hmmm: check returns on these setters?
+ EVP_CIPHER_CTX_set_key_length(session, _key_size);
+ EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
+ // and round and round we go...
+ initret = EVP_EncryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
+ if (!initret) {
+ // zero means a failure of the initialization.
+ ALWAYS_LOG(a_sprintf("second phase failure in calling EVP_EncryptInit_ex, with error %s", GET_SSL_ERROR()));
+ exit(1);
+ }
+
+ // allocate temporary space for encrypted data.
+ byte_array encoded(source.length() + FUDGE);
+
+ // encrypt the entire source buffer.
+ int encoded_len = 0;
+ int enc_ret = EVP_EncryptUpdate(session, encoded.access(), &encoded_len,
+ source.observe(), source.length());
+ if (enc_ret != 1) {
+ deadly_error(class_name(), func, a_sprintf("encryption failed, "
+ "result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
+ to_return = false;
+ } else {
+ // chop any extra space off.
+ LOG(a_sprintf(" chopping extra bytes %d to %d.", encoded_len, encoded.last()));
+ encoded.zap(encoded_len, encoded.last());
+ target = encoded;
+ }
+
+ // only add padding if we succeeded with the encryption.
+ if (enc_ret == 1) {
+ // finalize the encryption.
+ encoded.reset(FUDGE); // reinflate for padding.
+ int pad_len = 0;
+ enc_ret = EVP_EncryptFinal_ex(session, encoded.access(), &pad_len);
+ if (enc_ret != 1) {
+ deadly_error(class_name(), func, a_sprintf("finalizing encryption "
+ "failed, result=%d with error=%s.", enc_ret, GET_SSL_ERROR()));
+ to_return = false;
+ } else {
+ LOG(a_sprintf(" padding added %d bytes.", pad_len));
+ encoded.zap(pad_len, encoded.last());
+ target += encoded;
+ }
+ }
+
+ EVP_CIPHER_CTX_cleanup(session);
+ EVP_CIPHER_CTX_free(session);
+ALWAYS_LOG("<<encrypt<<");
+ return to_return;
+}
+
+bool twofish_crypto::decrypt(const byte_array &source,
+ byte_array &target) const
+{
+ FUNCDEF("decrypt");
+ALWAYS_LOG(">>decrypt>>");
+ target.reset();
+ if (!_key->length() || !source.length()) return false;
+ bool to_return = true;
+ EVP_CIPHER_CTX *session = EVP_CIPHER_CTX_new();
+ EVP_CIPHER_CTX_init(session);
+ LOG(a_sprintf(" using key size with %d bits.", _key_size));
+///NOOOOOOO BITS_PER_BYTE * _key->length()));
+ int initret = EVP_DecryptInit_ex(session, EVP_bf_cbc(), NULL_POINTER, NULL_POINTER, NULL_POINTER);
+ if (!initret) {
+ // zero means a failure of the initialization.
+//hmmm: below approach is called a deadly error. use that instead, throughout.
+ ALWAYS_LOG(a_sprintf("failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
+ exit(1);
+ }
+ // more fancy fupwork.
+//hmmm: check returns on these setters?
+ EVP_CIPHER_CTX_set_key_length(session, _key_size);
+ EVP_CIPHER_CTX_ctrl(session, EVP_CTRL_AEAD_SET_IVLEN, init_vector().length(), NULL);
+ initret = EVP_DecryptInit_ex(session, NULL_POINTER, NULL_POINTER, _key->observe(), init_vector().observe());
+ if (!initret) {
+ // zero means a failure of the initialization.
+ ALWAYS_LOG(a_sprintf("second phase failure in calling EVP_DecryptInit_ex, with error %s", GET_SSL_ERROR()));
+ exit(1);
+ }
+
+ // allocate enough space for decoded bytes.
+ byte_array decoded(source.length() + FUDGE);
+
+ int decoded_len = 0;
+ int dec_ret = EVP_DecryptUpdate(session, decoded.access(), &decoded_len,
+ source.observe(), source.length());
+ if (dec_ret != 1) {
+ deadly_error(class_name(), func, a_sprintf("decryption failed with error=%s", GET_SSL_ERROR()));
+ to_return = false;
+ } else {
+ LOG(a_sprintf(" first part decrypted size in bytes is %d.", decoded_len));
+ decoded.zap(decoded_len, decoded.last());
+ target = decoded;
+ }
+
+ // only process padding if the first part of decryption succeeded.
+ if (dec_ret == 1) {
+ decoded.reset(FUDGE); // reinflate for padding.
+ int pad_len = 0;
+ dec_ret = EVP_DecryptFinal_ex(session, decoded.access(), &pad_len);
+ if (dec_ret != 1) {
+ deadly_error(class_name(), func, a_sprintf("finalizing decryption "
+ "failed, result=%d, padlen=%d, target had %d bytes, error=%s.", dec_ret,
+ pad_len, target.length(), GET_SSL_ERROR()));
+ to_return = false;
+ } else {
+ LOG(a_sprintf(" padding added %d bytes.", pad_len));
+ decoded.zap(pad_len, decoded.last());
+ target += decoded;
+ }
+ }
+
+ EVP_CIPHER_CTX_cleanup(session);
+ EVP_CIPHER_CTX_free(session);
+ALWAYS_LOG("<<decrypt<<");
+ return to_return;
+}
+*/
+
+} //namespace.
+
+
--- /dev/null
+#ifndef TWOFISH_CRYPTO_CLASS
+#define TWOFISH_CRYPTO_CLASS
+
+/*****************************************************************************\
+* *
+* Name : twofish encryption *
+* Author : Chris Koeritz *
+* *
+*******************************************************************************
+* Copyright (c) 2005-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+\*****************************************************************************/
+
+#include <basis/byte_array.h>
+#include <basis/contracts.h>
+
+#include "cryptical_envelopment.h"
+
+namespace crypto {
+
+//! Provides TwoFish encryption on byte_arrays using the OpenSSL package.
+
+class twofish_crypto : public cryptical_envelopment
+//: public virtual basis::root_object
+{
+public:
+ twofish_crypto(int key_size);
+ //!< this will create a new random key of the "key_size", in bits.
+ /*!< the valid sizes are from 64 bits to 448 bits (we are forcing a
+ higher minimum than the published algorithm because we have found smaller
+ keys to be unreliable during decryption. keys of 168 bits and larger
+ should be very secure. it is said that if a billion computers each tried
+ a billion keys a second, then a 168 bit key would take 10 * 10^24 years
+ to break (using brute force). this is essentially unbreakable since the
+ age of the universe is only 10 * 10^9 years so far. */
+
+ twofish_crypto(const basis::byte_array &key, int key_size);
+ //!< uses a pre-existing "key".
+
+ twofish_crypto(const twofish_crypto &to_copy); //!< copy constructor.
+
+ virtual ~twofish_crypto();
+
+ twofish_crypto &operator = (const twofish_crypto &to_copy);
+
+ DEFINE_CLASS_NAME("twofish_crypto");
+
+ // twofish relevant values for appropriate key sizes.
+ virtual int minimum_key_size() const { return 92; }
+ /*
+ note that the lower bound above was discovered for openssl library through experimentation,
+ and is not officially documented for the twofish algorithm anywhere we can find (yet).
+ really contradictory results happened with lower key sizes, where many encrypt/decrypt cycles
+ would succeed on the same key length but then would trigger an 'invalid key length' error.
+ not the kind of deterministic behavior we might expect.
+ */
+ virtual int maximum_key_size() const { return 256; }
+
+/// int key_size() const; // returns the size of our key, in bits.
+
+/// static int minimum_key_size();
+/// //!< returns the minimum key size (in bits) supported here.
+/// static int maximum_key_size();
+/// //!< returns the maximum key size (in bits) supported here.
+
+/// const basis::byte_array &get_key() const; //!< returns our current key.
+
+/// bool set_key(const basis::byte_array &new_key, int key_size);
+ //!< sets the encryption key to "new_key".
+
+/// static void generate_key(int size, basis::byte_array &new_key);
+ //!< creates a "new_key" of the "size" (in bits) specified.
+
+/// bool encrypt(const basis::byte_array &source, basis::byte_array &target) const;
+ //!< encrypts the "source" array into the "target" array.
+
+/// bool decrypt(const basis::byte_array &source, basis::byte_array &target) const;
+ //!< decrypts the "target" array from the encrypted "source" array.
+
+ // seldom-needed methods...
+
+/// static const basis::byte_array &init_vector();
+ //!< returns the initialization vector that is used by this class.
+ /*!< decryption of chunks that were encrypted by this class will require
+ the same init vector as this function returns. this is mainly provided
+ for third-party applications that want to be able to decrypt interoperably
+ with this class. if you are creating such an application but for some
+ reason cannot run this class in order to invoke this method, the vector
+ is created by the algorithm in this class's implementation file
+ (currently named twofish_crypto.cpp). */
+
+private:
+/// int _key_size; //!< number of bits in the key.
+/// basis::byte_array *_key; //!< our secret key.
+};
+
+} //namespace.
+
+#endif
+
PROJECT = tests_crypto
TYPE = test
-TARGETS = test_blowfish_crypto.exe test_old_school_rsa_crypto.exe
+TARGETS = test_blowfish_crypto.exe test_old_school_rsa_crypto.exe test_twofish_crypto.exe
LOCAL_LIBS_USED = unit_test crypto application processes loggers configuration textual timely \
filesystem structures basis
USE_SSL = t
const int MAX_STRING = 20000; // largest chunk that we'll try to encrypt.
// some constants snagged from older version of blowfish_crypto class...
-const int blowfish_crypto_minimum_key_size_in_bits = 64;
+const int blowfish_crypto_minimum_key_size_in_bits = 32;
const int blowfish_crypto_maximum_key_size_in_bits = 448;
//////////////
--- /dev/null
+/*
+* Name : test twofish encryption
+* Author : Chris Koeritz
+* Purpose: Exercises the BlowFish encryption methods in the crypto library.
+**
+* Copyright (c) 2005-$now By Author. This program 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 2 of *
+* the License or (at your option) any later version. This is online at: *
+* http://www.fsf.org/copyleft/gpl.html *
+* Please send any updates to: fred@gruntose.com *
+*/
+
+#include <application/hoople_main.h>
+#include <basis/byte_array.h>
+#include <basis/astring.h>
+#include <crypto/twofish_crypto.h>
+#include <mathematics/chaos.h>
+#include <processes/ethread.h>
+#include <processes/thread_cabinet.h>
+#include <structures/static_memory_gremlin.h>
+#include <structures/unique_id.h>
+#include <textual/byte_formatter.h>
+#include <textual/string_manipulation.h>
+#include <timely/time_control.h>
+#include <timely/time_stamp.h>
+#include <unit_test/unit_base.h>
+
+#include <stdio.h>
+#include <string.h>
+
+using namespace application;
+using namespace basis;
+using namespace crypto;
+using namespace filesystem;
+using namespace loggers;
+using namespace mathematics;
+using namespace processes;
+using namespace structures;
+using namespace textual;
+using namespace timely;
+using namespace unit_test;
+
+#define LOG(to_print) EMERGENCY_LOG(program_wide_logger::get(), astring(to_print))
+
+#define DEBUG_TWOFISH
+ // uncomment for noisier run.
+
+//const int TEST_RUNS_PER_KEY = 42; // encryption test cycles done on each key.
+const int TEST_RUNS_PER_KEY = 420; // encryption test cycles done on each key.
+
+const int THREAD_COUNT = 10; // number of threads testing twofish at once.
+
+//const int ITERATIONS = 64; // number of test runs in our testing threads.
+const int ITERATIONS = 4200; // number of test runs in our testing threads.
+
+const int MAX_STRING = 64 * KILOBYTE; // largest chunk that we'll try to encrypt.
+
+// some constants snagged from older version of twofish_crypto class...
+const int twofish_crypto_minimum_key_size_in_bits = 92;
+const int twofish_crypto_maximum_key_size_in_bits = 256;
+/*
+ Q: is twofish truly requiring 128/192/256 key sizes only?
+ A: no, schneier says any key size up to 256 bits.
+ Q: can twofish really support *any* key length up to 256?
+ A: well, not in openssl. we are getting invalid key length complaints
+ with keys below 92 bits. this was calculated by testing for quite
+ a while with only those size keys, and 92 seems reliable as a minimum.
+*/
+
+//////////////
+
+class test_twofish; // forward.
+
+class twofish_thread : public ethread
+{
+public:
+ twofish_thread(test_twofish &parent) : ethread(), _parent(parent) {}
+
+ void perform_activity(void *ptr);
+ // try out random twofish keys on randomly chosen chunks of the fodder.
+
+private:
+ test_twofish &_parent;
+};
+
+//////////////
+
+class test_twofish : virtual public unit_base, virtual public application_shell
+{
+public:
+ test_twofish()
+ : _fodder(string_manipulation::make_random_name(MAX_STRING + 1, MAX_STRING + 1)) {}
+ DEFINE_CLASS_NAME("test_twofish");
+
+ int execute();
+
+private:
+ astring _fodder; // chunks taken from this are encrypted and decrypted.
+ time_stamp _program_start; // the time at which we started executing.
+ thread_cabinet _threads; // manages our testing threads.
+ friend class twofish_thread; // bad practice, but saves time in test app.
+};
+
+int test_twofish::execute()
+{
+ FUNCDEF("execute");
+#ifdef DEBUG_TWOFISH
+ LOG("starting twofish test...");
+#endif
+ int left = THREAD_COUNT;
+ while (left--) {
+#ifdef DEBUG_TWOFISH
+ LOG(a_sprintf("twofish thread %d starting...", left));
+#endif
+ _threads.add_thread(new twofish_thread(*this), true, NULL_POINTER);
+ }
+
+#ifdef DEBUG_TWOFISH
+ LOG("started all threads...");
+#endif
+
+ while (_threads.threads()) {
+#ifdef DEBUG_TWOFISH
+ LOG("periodic debris cleaning.");
+#endif
+ _threads.clean_debris();
+ time_control::sleep_ms(1000);
+ }
+
+ int duration = int(time_stamp().value() - _program_start.value());
+ LOG(a_sprintf("duration for %d keys and encrypt/decrypt=%d ms,",
+ ITERATIONS * TEST_RUNS_PER_KEY * THREAD_COUNT, duration));
+ LOG(a_sprintf("that comes to %d ms per cycle.", int(double(duration
+ / TEST_RUNS_PER_KEY / ITERATIONS / THREAD_COUNT))));
+
+ return final_report();
+}
+
+//////////////
+
+#undef UNIT_BASE_THIS_OBJECT
+#define UNIT_BASE_THIS_OBJECT (*dynamic_cast<unit_base *>(application_shell::single_instance()))
+
+void twofish_thread::perform_activity(void *)
+{
+ FUNCDEF("perform_activity");
+ int left = ITERATIONS;
+ while (left--) {
+ time_stamp key_start;
+ twofish_crypto bc(_parent.randomizer().inclusive(twofish_crypto_minimum_key_size_in_bits,
+ twofish_crypto_maximum_key_size_in_bits));
+#ifdef DEBUG_TWOFISH
+ LOG(a_sprintf("%d bit key has:", bc.key_size()));
+ astring dumped_key = byte_formatter::text_dump(bc.get_key());
+ LOG(a_sprintf("%s", dumped_key.s()));
+#endif
+ int key_dur = int(time_stamp().value() - key_start.value());
+#ifdef DEBUG_TWOFISH
+ LOG(a_sprintf(" key generation took %d ms", key_dur));
+#endif
+
+ for (int i = 0; i < TEST_RUNS_PER_KEY; i++) {
+/// byte_array key;
+/// byte_array iv;
+LOG(a_sprintf("test run %d on this key.", i+1));
+ int string_start = _parent.randomizer().inclusive(0, MAX_STRING - 1);
+ int string_end = _parent.randomizer().inclusive(0, MAX_STRING - 1);
+ flip_increasing(string_start, string_end);
+ astring ranstring = _parent._fodder.substring(string_start, string_end);
+#ifdef DEBUG_TWOFISH
+// LOG(a_sprintf("encoding %s", ranstring.s()));
+// LOG(a_sprintf("string length encoded: %d", ranstring.length()));
+#endif
+
+ byte_array target;
+ time_stamp test_start;
+ bool worked = bc.encrypt(byte_array(ranstring.length() + 1, (abyte*)ranstring.s()), target);
+ int enc_durat = int(time_stamp().value() - test_start.value());
+ ASSERT_TRUE(worked, "phase 1 should not fail to encrypt the string");
+
+ byte_array recovered;
+ test_start.reset();
+ worked = bc.decrypt(target, recovered);
+ int dec_durat = int(time_stamp().value() - test_start.value());
+ ASSERT_TRUE(worked, "phase 1 should not fail to decrypt the string");
+#ifdef DEBUG_TWOFISH
+ astring jammer_piece = a_sprintf("--\noriginal has %d chars, recovered has %d chars",
+ ranstring.length(), recovered.length() - 1);
+#endif
+
+ astring teddro = (char *)recovered.observe();
+#ifdef DEBUG_TWOFISH
+// LOG(a_sprintf("decoded %s", teddro.s()));
+#endif
+
+#ifdef DEBUG_TWOFISH
+ if (teddro != ranstring) {
+ LOG(a_sprintf("error!\toriginal has %d chars, recovered has %d chars",
+ ranstring.length(), recovered.length() - 1));
+ LOG(a_sprintf("\tencoded %s", ranstring.s()));
+ LOG(a_sprintf("\tdecoded %s", teddro.s()));
+ }
+#endif
+ ASSERT_EQUAL(teddro, ranstring, "should not fail to regenerate the original string");
+
+#ifdef DEBUG_TWOFISH
+ LOG(a_sprintf("%s\nencrypt %d ms, decrypt %d ms, data %d bytes",
+ jammer_piece.s(), enc_durat, dec_durat, string_end - string_start + 1));
+#endif
+ time_control::sleep_ms(0); // take a rest.
+ }
+ time_control::sleep_ms(0); // take a rest.
+ }
+}
+
+HOOPLE_MAIN(test_twofish, )
+
+