]> feistymeow.org Git - feisty_meow.git/commitdiff
added twofish encryption
authorFred T. Hamster <fred@feistymeow.org>
Mon, 16 Mar 2026 02:34:50 +0000 (22:34 -0400)
committerFred T. Hamster <fred@feistymeow.org>
Mon, 16 Mar 2026 02:34:50 +0000 (22:34 -0400)
based on cryptical envelopment, and it has no foulups, unlike blowfish.

nucleus/library/crypto/blowfish_crypto.cpp
nucleus/library/crypto/blowfish_crypto.h
nucleus/library/crypto/cryptical_envelopment.cpp
nucleus/library/crypto/cryptical_envelopment.h
nucleus/library/crypto/makefile
nucleus/library/crypto/twofish_crypto.cpp [new file with mode: 0644]
nucleus/library/crypto/twofish_crypto.h [new file with mode: 0644]
nucleus/library/tests_crypto/makefile
nucleus/library/tests_crypto/test_blowfish_crypto.cpp
nucleus/library/tests_crypto/test_twofish_crypto.cpp [new file with mode: 0644]

index 3c0596d6df75cf66c12178313ae0ea685536bef7..02ce5008047d11c6c699642b97ca9c730e66b300 100644 (file)
@@ -21,7 +21,6 @@
 #include <mathematics/chaos.h>
 #include <structures/static_memory_gremlin.h>
 
-#include <openssl/blowfish.h>
 #include <openssl/err.h>
 #include <openssl/evp.h>
 
@@ -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();
index f69923f126581bd032c452f3ed89ec265d501a72..8cedb32b4a5a327a005b67f7fc5b405f339dbf4e 100644 (file)
@@ -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();
index e717d1be7600a1ba808c65f9a55b277ff9e4e4f9..838c37dd8ec93658c6280798d9cb01b0324249bf 100644 (file)
@@ -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) {
index 9b07a443bfc9621d495e940ca416cdf1b567af24..899c6ab3c560261dc38686f5a3645548d45e7779 100644 (file)
@@ -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;
index 06b612c4439385a4b25c2fce4f3b72fe69b64b42..4ba15f86d038392dad0577019d8f7433d463a0e2 100644 (file)
@@ -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 (file)
index 0000000..ea9ad79
--- /dev/null
@@ -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 <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.
+
+
diff --git a/nucleus/library/crypto/twofish_crypto.h b/nucleus/library/crypto/twofish_crypto.h
new file mode 100644 (file)
index 0000000..381c8dd
--- /dev/null
@@ -0,0 +1,104 @@
+#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
+
index e0854cbbacc87bfe9045d0e4bd1ba8afb6c0dcd3..58b9ff7c38d9fb449c39f92a26abc23b736aea37 100644 (file)
@@ -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
index 23f622c0f837a3ca25710d15f659128ceb52085e..3baff5d760c504e74f05666242d425c1b1357f11 100644 (file)
@@ -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 (file)
index 0000000..055eb65
--- /dev/null
@@ -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 <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, )
+
+