From: Fred T. Hamster Date: Mon, 16 Mar 2026 02:34:50 +0000 (-0400) Subject: added twofish encryption X-Git-Url: https://feistymeow.org/gitweb/?a=commitdiff_plain;h=refs%2Fheads%2Fdev;p=feisty_meow.git added twofish encryption based on cryptical envelopment, and it has no foulups, unlike blowfish. --- diff --git a/nucleus/library/crypto/blowfish_crypto.cpp b/nucleus/library/crypto/blowfish_crypto.cpp index 3c0596d6..02ce5008 100644 --- a/nucleus/library/crypto/blowfish_crypto.cpp +++ b/nucleus/library/crypto/blowfish_crypto.cpp @@ -21,7 +21,6 @@ #include #include -#include #include #include @@ -86,10 +85,11 @@ namespace crypto { */ 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(); @@ -106,10 +106,11 @@ blowfish_crypto::blowfish_crypto(int key_size) } 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(); diff --git a/nucleus/library/crypto/blowfish_crypto.h b/nucleus/library/crypto/blowfish_crypto.h index f69923f1..8cedb32b 100644 --- a/nucleus/library/crypto/blowfish_crypto.h +++ b/nucleus/library/crypto/blowfish_crypto.h @@ -49,6 +49,10 @@ public: 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(); diff --git a/nucleus/library/crypto/cryptical_envelopment.cpp b/nucleus/library/crypto/cryptical_envelopment.cpp index e717d1be..838c37dd 100644 --- a/nucleus/library/crypto/cryptical_envelopment.cpp +++ b/nucleus/library/crypto/cryptical_envelopment.cpp @@ -63,66 +63,45 @@ const int FUDGE = 1024; 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) @@ -143,15 +122,40 @@ cryptical_envelopment::~cryptical_envelopment() 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; @@ -163,45 +167,78 @@ cryptical_envelopment &cryptical_envelopment::operator = (const cryptical_envelo 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"); @@ -211,7 +248,7 @@ const byte_array &cryptical_envelopment::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; @@ -235,10 +272,10 @@ LOG(a_sprintf(" encrypting %d bytes", source.length())); 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) { diff --git a/nucleus/library/crypto/cryptical_envelopment.h b/nucleus/library/crypto/cryptical_envelopment.h index 9b07a443..899c6ab3 100644 --- a/nucleus/library/crypto/cryptical_envelopment.h +++ b/nucleus/library/crypto/cryptical_envelopment.h @@ -28,34 +28,34 @@ namespace crypto { 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; diff --git a/nucleus/library/crypto/makefile b/nucleus/library/crypto/makefile index 06b612c4..4ba15f86 100644 --- a/nucleus/library/crypto/makefile +++ b/nucleus/library/crypto/makefile @@ -4,7 +4,7 @@ include cpp/variables.def 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 diff --git a/nucleus/library/crypto/twofish_crypto.cpp b/nucleus/library/crypto/twofish_crypto.cpp new file mode 100644 index 00000000..ea9ad793 --- /dev/null +++ b/nucleus/library/crypto/twofish_crypto.cpp @@ -0,0 +1,372 @@ +/* +* 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 +#include +#include +#include +#include +#include +#include + +//#include +#include +#include + +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("<>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("< +#include + +#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 + diff --git a/nucleus/library/tests_crypto/makefile b/nucleus/library/tests_crypto/makefile index e0854cbb..58b9ff7c 100644 --- a/nucleus/library/tests_crypto/makefile +++ b/nucleus/library/tests_crypto/makefile @@ -2,7 +2,7 @@ include cpp/variables.def 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 diff --git a/nucleus/library/tests_crypto/test_blowfish_crypto.cpp b/nucleus/library/tests_crypto/test_blowfish_crypto.cpp index 23f622c0..3baff5d7 100644 --- a/nucleus/library/tests_crypto/test_blowfish_crypto.cpp +++ b/nucleus/library/tests_crypto/test_blowfish_crypto.cpp @@ -58,7 +58,7 @@ const int ITERATIONS = 80; // number of test runs in our testing threads. 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; ////////////// diff --git a/nucleus/library/tests_crypto/test_twofish_crypto.cpp b/nucleus/library/tests_crypto/test_twofish_crypto.cpp new file mode 100644 index 00000000..055eb65c --- /dev/null +++ b/nucleus/library/tests_crypto/test_twofish_crypto.cpp @@ -0,0 +1,219 @@ +/* +* 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +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(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, ) + +