* More than one certificate can be used.
*
* The encryption is done with an pseudo random generator.
- * The portability (over the hardware architecture) is guaranteed if the
- * pseudo random generator is portable.
+ *
+ * The portability (over byte order) is guaranteed if the
+ * pseudo random generator is portable (nomalizing big/little endian)
+ * and a 64 bit integer arithmetic is available.
*
*/
/**
RplEnigma::RplEnigma(RplRandom* random) :
m_random(random),
m_ownsRandom(false),
- m_byteSeeds(),
- m_ixSeed(0)
+ m_secrets(),
+ m_randomCalls(0),
+ m_randomSource("4711")
{
+ m_randomSource.reserve(8096);
if (random == NULL){
m_random = new RplRandom();
m_ownsRandom = true;
/**
* @brief Destructor.
*/
-RplEnigma::~RplEnigma();
+RplEnigma::~RplEnigma(){
+ if (m_ownsRandom){
+ delete m_random;
+ m_random = NULL;
+ }
+}
/**
* @brief Reads a certificate.
return rc;
}
+inline void buildBooster(QByteArray& booster, const char* charSet){
+ int size = 256;
+ booster.fill(0, size);
+ int ix = 0;
+ char cc;
+ while( (cc = *charSet++) != '\0'){
+ booster[cc] = ++ix;
+ }
+ booster[0] = ix;
+}
+
+/**
+ * @brief Encodes a string in place with a given character set.
+ *
+ * If a character of data is not included in the character set
+ * it remains unchanged
+ *
+ * @param data IN: data to encode<br>
+ * OUT: data encoded
+ * @param size length of <code>data</code>. If 0
+ * @param charSet a string containing all valid characters
+ * @param booster a performance booster. Once built it can be shared
+ * between all calls of encodeChar() and decodeChar()
+ * with the same character set<br>
+ * IN: "": not initialized otherwise: ready for work
+ * OUT: ready for work
+ */
+void RplEnigma::encode(char* data, int size, const char* charSet, QByteArray& booster){
+ if (booster.length() == 0){
+ buildBooster(booster, charSet);
+ }
+ int lengthOfCharSet = booster.at(0);
+ for (int ii = 0; ii < size; ii++){
+ char cc = *data;
+ int ix = booster.at(cc);
+ if (ix != 0){
+ ix = (ix + nextInt(lengthOfCharSet)) % lengthOfCharSet;
+ *data++ = charSet[ix];
+ }
+ }
+}
+/**
+ * @brief Decodes a string in place with a given character set.
+ *
+ * If a character of data is not included in the character set
+ * it remains unchanged
+ *
+ * @param data IN: data to decode<br>
+ * OUT: data decoded
+ * @param size length of <code>data</code>. If 0
+ * @param charSet a string containing all valid characters
+ * @param booster a performance booster. Once built it can be shared
+ * between all calls of encodeChar() and decodeChar()
+ * with the same character set<br>
+ * IN: "": not initialized otherwise: ready for work
+ * OUT: ready for work
+ */
+void RplEnigma::decode(char* data, int size, const char* charSet, QByteArray& booster){
+ if (booster.length() == 0){
+ buildBooster(booster, charSet);
+ }
+ int lengthOfCharSet = booster.at(0);
+ for (int ii = 0; ii < size; ii++){
+ char cc = *data;
+ int ix = booster.at(cc);
+ if (ix != 0){
+ ix = (lengthOfCharSet + ix - nextInt(lengthOfCharSet)) % lengthOfCharSet;
+ *data++ = charSet[ix];
+ }
+ }
+}
/**
- * @brief Encodes one character.
+ * @brief Encodes or decode a byte array.
+ *
+ * The encoding method is symetric. Therefore it can encode and decode.
*
- * @param byte value to encode
- * @param mode affects the possible values of the result
- * @return the encoded character
+ * @param data IN: data to encode/decoded<br>
+ * OUT: data encoded/decoded
*/
-char RplEnigma::encodeChar(char byte, mode_t mode = MODE_STANDARD){
- char rc = 0;
+void RplEnigma::change(QByteArray& data){
+ int randomLength = m_randomSource.length();
+ for (int ix = data.length() - 1; ix >= 0; ix--){
+ char item = data.at(ix);
+ item = (item ^ nextInt(0xff) ^ m_randomSource.at(ix % randomLength));
+ data[ix] = item;
+ }
+}
- switch(mode){
- case MODE_DECIMAL:
+/**
+ * @brief Adds a random source given by an byte array.
+ *
+ * A random source can be the binary form of an certificate,
+ * a salt, a password or similar.
+ *
+ * @param byteSecret a byte sequence which influences the random generation
+ */
+void RplEnigma::addByteSecret(QByteArray byteSecret){
+ // we expand it to a multiple of 64 bit:
+ int oldSize = byteSecret.length();
+ int newSize = (oldSize + 7) / 8 * 8;
+ int ix;
+ if (newSize > oldSize){
+ byteSecret.resize(newSize);
+ int sum = 0;
+ int start = oldSize > 8 ? oldSize - 8 : 0;
+ for (ix = start; ix < oldSize; ix++){
+ sum += ix + byteSecret.at(ix);
+ }
+ for (ix = oldSize; ix < newSize; ix--){
+ sum += ix + 7;
+ byteSecret[ix] = (char) (sum + byteSecret.at(ix-1));
+ }
+ }
+ int count = newSize / 8;
+ secret_t* secret = new secret_t();
+ secret->m_count = count;
+ secret->m_list = new u_int64_t[count];
+ m_secrets.append(secret);
+ for (ix = 0; ix < count; ix++){
+ u_int64_t value = 0;
+ for (int ix2 = 0; ix2 < 8; ix2++)
+ value = (value << 8) + byteSecret.at(ix * 8 + ix2);
+ secret->m_list[ix] = value;
+ }
+ QByteArray value;
+ QCryptographicHash hash(QCryptographicHash::Md5);
+ RplRandom rand;
+ hash.addData(m_randomSource.constData(), 4);
+ for (ix = 0; ix < byteSecret.length(); ix++){
+ hash.addData(byteSecret.constData() + ix, 1);
+ QByteArray current = hash.result();
+ int ix2 = rand.nextInt(0, m_randomSource.length() - 1);
+ m_randomSource[ix2] = (m_randomSource.at(ix2) ^ current.at(0));
+ m_randomSource.insert(0, current);
+ }
+}
- break;
- case MODE_HEXADECIMAL:
- case MODE_ALPHA_NUMERIC:
- case MODE_ASCII:
- case MODE_CHAR255:
- case MODE_FILENAME:
- break;
- case MODE_STANDARD:
- default:
- rc =
- ;
- break;
+/**
+ * @brief Returns the next random integer
+ * @param maxValue
+ * @return
+ */
+int RplEnigma::nextInt(int maxValue){
+ u_int64_t seed = 0;
+ QList<secret_t*>::const_iterator it;
+ int ixSecret = m_randomCalls++;
+ int ix = ixSecret;
+ for (it = m_secrets.constBegin(); it != m_secrets.constEnd(); ++it){
+ secret_t* secret = *it;
+ seed |= ((secret->m_list[ixSecret % secret->m_count]) >> (ix % 8));
}
+ m_random->xorSeed(seed);
+ int rc = m_random->nextInt(0, maxValue);
return rc;
}
-void RplEnigma::encode(QByteArray& value, mode_t mode = MODE_STANDARD);
-void RplEnigma::addByteSeed(const QByteArray& byteSeed);
-void RplEnigma::setSeed(u_int64_t seed);
- protected:
- QList<QByteArray> m_byteSeeds;
- ///> each call of setSeed sets this value to 0.
- int m_ixSeed;
- };
+
+/**
+ * @brief Sets the pseudo random to a define state.
+ *
+ * @param seed the initial state
+ */
+void RplEnigma::setSeed(u_int64_t seed){
+ m_random->setSeed(seed);
+ m_randomCalls = 0;
+}
#define RPLENIGMA_HPP
class RplEnigma {
- public:
- typedef enum {
- MODE_STANDARD = 1, ///< no limitation
- MODE_DECIMAL, ///< a decimal will be encrypted by an decimal
- MODE_HEXADECIMAL, ///< a hexadecimal will be encrypted by an hexadecimal
- MODE_ALPHA_NUMERIC, ///< [a-zA-Z0-9_] remains in this class
- MODE_FILENAME, ///< [a-zA-Z0-9_!^$%=+-#~.] remains in this class
- MODE_ASCII, ///< chr(32)..chr(127) remains in this class
- MODE_CHAR255 ///< chr(32)..chr(255) remains in this class
- } mode_t;
- public:
- RplEnigma(RplRandom* random = NULL);
- virtual ~RplEnigma();
- public:
- QByteArray readCertificate();
- char encodeChar(char byte, mode_t mode = MODE_STANDARD);
- void encode(QByteArray& value, mode_t mode = MODE_STANDARD);
- void addByteSeed(const QByteArray& byteSeed);
- void setSeed(u_int64_t seed);
- protected:
- ///> a pseudo random generator
- RplRandom* m_random;
- ///> true: m_random must be destroyed (in the destructor).
- bool m_ownsRandom;
- QList<QByteArray> m_byteSeeds;
- ///> each call of setSeed sets this value to 0.
- int m_ixSeed;
- };
+public:
+ static const char* SET_32_127; ///< ' ' .. chr(127)
+ static const char* SET_DECIMALS; ///< '0'..'9'
+ static const char* SET_HEXDIGITS; ///< '0'..'9' 'a'..'f'
+ static const char* SET_ALPHANUM; ///< '0'..'9' 'A'..'Z' a'..'z' '_'
+ static const char* SET_FILENAME; ///< '0'..'9' 'A'..'Z' z'..'z' '_'
+ static const char* SET_32_255; ///< ' ' .. chr(255)
+protected:
+ typedef struct {
+ int m_count;
+ u_int64_t* m_list;
+ } secret_t;
+public:
+ RplEnigma(RplRandom* random = NULL);
+ virtual ~RplEnigma();
+public:
+ void encode(char* data, int size, const char* charSet, QByteArray& cache);
+ void decode(char* data, int size, const char* charSet, QByteArray& cache);
+ void change(QByteArray& data);
+ void addByteSecret(QByteArray byteSeed);
+ void setSeed(u_int64_t seed);
+ QByteArray readCertificate(const char* filename);
+protected:
+ int nextInt(int maxValue);
+
+protected:
+ ///> a pseudo random generator
+ RplRandom* m_random;
+ ///> true: m_random must be destroyed (in the destructor).
+ bool m_ownsRandom;
+ ///> This values will be mixed with <code>m_random</code>' seed
+ QList<secret_t*> m_secrets;
+ ///> each call of setSeed sets this value to 0.
+ int m_randomCalls;
+ ///> a byte sequence derived from the secrets
+ QByteArray m_randomSource;
+};
#endif // RPLENIGMA_HPP
* @return the next random number.
*/
u_int64_t RplRandom::nextInt64(){
- // Knuth recommands (64-Bit):
+ // Donald Knuth recommands (for 64-Bit):
m_seed = m_seed * 6364136223846793005L + 1442695040888963407L;
return m_seed;
}
void RplRandom::setSeed(u_int64_t seed){
m_seed = seed;
}
+/**
+ * @brief Modifies the seed.
+ *
+ * @param seed the XOR operand.
+ */
+void RplRandom::xorSeed(u_int64_t seed){
+ m_seed ^= seed;
+}
/**
* @brief nextByte returns the next random byte.
* @param maxChar all characters of the result are lower or equal than this value
* @return a random string
*/
-QByteArray RplRandom::nextString(int length = 8, char minChar = ' ', char maxChar = 127){
+QByteArray RplRandom::nextString(int length, char minChar, char maxChar){
QByteArray rc;
rc.resize(length);
for (int ii = 0; ii < length; ii++){
- rc[ii] = minChar + nextInt(maxChar + 1);
+ rc[ii] = nextInt(minChar, maxChar);
}
return rc;
}
* @param charSet a string with all allowed characters
* @return a random string with characters from the given set
*/
-QByteArray RplRandom::nextString(int length = 8, char* charSet){
+QByteArray RplRandom::nextString(int length, char* charSet){
QByteArray rc;
rc.resize(length);
int ubound = strlen(charSet) - 1;
}
return rc;
}
+