Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Presentation of the XOR256 Encryption/Decryption method for the block ciphering case

0.00/5 (No votes)
8 Jun 2001 1  
An article presenting a new block ciphering method called XOR256

Theoretical Presentation

In this article I am presenting a new Block Ciphering encryption method which is an adaptation of some results I presented in a previous article with www.codeproject.com "Some Stream Cipher Encryption/Decryption Algorithms" for the Stream Ciphering case. Some of the advantages of this method are:

  • It is easy to understand and implement;
  • It can be applied for any block size;
  • It can be applied for a sufficient large range in key size;
  • The strength can be increased simply by increasing the number of rounds, or the block size, or the key size;
  • It is fast enough;
  • It is in my opinion enough secure;

It remain to see the opinions of other people especially about the security claim.

Details

This method has two variants, each one based on the a corresponding stream ciphering variant of the XOR256 method presented in my previous article for the stream ciphering case. The method for generating the seeds for the pseudo-random generators from the key is the same. The difference is that the sequences of pseudo-random generators are now calculated only once during the initialization phase and after that are considered as method constants and applied to any block. Let's denote by no_rounds the number of rounds, block_size the size of the blocks and no_random the number of pseudo-random generators (which can be determined from the key size by the formula no_random = (key_size+1)/2). Then the number of constants is 2*no_rounds*block_size for the first variant of the XOR256 block ciphering method and no_rounds*block_size*no_random for the second variant of the XOR256 block ciphering method. For example for blocks of size 8, keys of size 8 and 3 rounds we will have 48 method constants for the first variant and 96 method constants for the second variant, and for blocks of size 16, keys of size 16 and 3 rounds we will have 96 method constants for the first variant and 384 method constants for the second variant. You can see that the space of the constants is much larger than the key space or (seed space which is the same size), so that an decryption approach to determine the constants is less practical than an approach to determine the key. The constants are determined from the pseudo-random generators by:

for(k=0; k<no_rounds*block_size; k++)
{
    cxor[k] = random[0][k]^random[1][k]^...^random
        [no_random-1][k]
    c256[k] = (random[0][k]+random[1][k]+...+random
        [no_random-1][k])%256
}

for the First Variant and

for(k=0; k<no_rounds*block_size; k++)
    for(i=0; i<no_random; i++)
        c[k][i] = random[i][k]%256;

for the Second Variant.

The core of the method consists in applying the stream cipher XOR256 procedure a number of rounds scanning the block in succession in order and reverse order. For example for a three rounds encryption, first time the block is scanned directly, second time is scanned in reverse and last time is scanned again directly. For the first no_rounds-1 rounds the previous encrypted character is involved in the encryption. It is ensuring the dispersion of small differences in blocks. Otherwise if two plaintext blocks differ only in one place (one byte) then the cipher texts will also differ only in one place, and it could help the decryption attacks to easily identify similar blocks. In the particular case of the first character of the block for a direct scanning (or last character of the block for a reverse scanning) the previous encrypted character is considered 0. Let's denote by prev_cipher the result of the previous encryption round (for the first round it is the same as the plain text) and by cur_cipher the result of the current encryption round. We have for the block cipher XOR256 First Variant:

cur_cipher[k] = (cur_cipher[k-1]^prev_cipher[k]^cxor[k] + c256[k]) % 256

For the last round the previous unencrypted character is involved in the encryption. It is somewhat strengthening the method because to correctly decrypt a character you should have correctly decrypted the previous character. In the particular case of the first character in the block for a direct scanning (or last character in the block for a reverse scanning) the previous unencrypted character is considered 0. We have (a direct scanning of the block is assumed):

cur_cipher[k] = (prev_cipher[k-1]^prev_cipher[k]^cxor[k] + c256[k]) % 256

For the block cipher XOR256 Second Variant we have recursively (a direct scanning of the block is assumed):

//First pseudo-random generator:

    cur_cipher[k] = (cur_cipher[k-1]^prev_cipher[k]^c[k][0] + c[k][0]) % 256
//Second pseudo-random generator

    cur_cipher[k] = (cur_cipher[k]^c[k][1] + c[k][1]) % 256
...
//no_random-th, last pseudo-random generator

    cur_cipher[k] = (cur_cipher[k]^c[k][no_random-1] + c[k][no_random-1]) % 256

For the last round the previous unencrypted character is involved in the encryption:

//First pseudo-random generator:

    cur_cipher[k] = (prev_cipher[k-1]^prev_cipher[k]^c[k][0] + c[k][0]) % 256
//Second pseudo-random generator

    cur_cipher[k] = (cur_cipher[k]^c[k][1] + c[k][1]) % 256
...
//no_random-th, last pseudo-random generator

    cur_cipher[k] = (cur_cipher[k]^c[k][no_random-1] + c[k][no_random-1]) % 256

Implementation

The Pseudo-Random Number generator is implemented in the CRand class and is identical to the one presented in my previous article . The common interface for the XOR256 classes is:

class IXOR256
{
public:
    IXOR256() : m_iBlockSize(0), m_iSize(0), m_pucChain0(NULL), 
        m_pucChain(NULL), m_pucTemp(NULL)
    {
    }

    virtual ~IXOR256()
    {
        if(m_pucChain0 != NULL)
            delete [] m_pucChain0;
        if(m_pucChain != NULL)
            delete [] m_pucChain;
        if(m_pucTemp != NULL)
            delete [] m_pucTemp;
    };

    enum { KEY_MAX = 217 };

    enum { ECB=0, CBC=1, CFB=2 };

    virtual bool Initialize(unsigned int iBlockSize, unsigned int iRounds, 
        unsigned char* pucChain, string const& rostrKey) = 0;
    virtual bool EncryptBlock(unsigned char* pucBlock) = 0;
    virtual bool DecryptBlock(unsigned char* pucBlock) = 0;
    bool Encrypt(unsigned char const* pucIn, unsigned char* pucOut, 
        size_t n, int iMode=ECB);
    bool Decrypt(unsigned char const* pucIn, unsigned char* pucOut, 
        size_t n, int iMode=ECB);
    //Auxiliary Functions:

    void ResetChain();

protected:
    //Auxiliary Functions:

    void Xor(unsigned char* pucBuff, unsigned char const* pucChain);

protected:
    int m_iBlockSize;
    int m_iRounds;
    int m_iSize;
    unsigned char* m_pucChain0;
    unsigned char* m_pucChain;
    unsigned char* m_pucTemp;
};

The method's variants interfaces are derived from the common interface. The interface for the First Variant is:

class CXOR256_0 : public IXOR256
{
public:
    CXOR256_0() : m_pucXOR(NULL), m_puc256(NULL)
    {
    }

    virtual ~CXOR256_0()
    {
        if(m_pucXOR != NULL)
            delete [] m_pucXOR;
        if(m_puc256 != NULL)
            delete [] m_puc256;
    };

    virtual bool Initialize(unsigned int iBlockSize, unsigned int iRounds, 
        unsigned char* pucChain, string const& rostrKey);
    virtual bool EncryptBlock(unsigned char* pucBlock);
    virtual bool DecryptBlock(unsigned char* pucBlock);
    void EncryptDirect(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void EncryptReverse(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void EncryptDirect1(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void EncryptReverse1(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void DecryptDirect(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void DecryptReverse(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void DecryptDirect1(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);
    void DecryptReverse1(unsigned char* pucBlock, unsigned char const* pucXOR, 
        unsigned char const* puc256);

private:
    unsigned char* m_pucXOR;
    unsigned char* m_puc256;
};

For the Second Variant the interface is:

class CXOR256_1 : public IXOR256
{
public:
    CXOR256_1() : m_pucXOR256(NULL)
    {
    }

    virtual ~CXOR256_1()
    {
        if(m_pucXOR256 != NULL)
            delete [] m_pucXOR256;
    };

    virtual bool Initialize(unsigned int iBlockSize, unsigned int iRounds, 
        unsigned char* pucChain, string const& rostrKey);
    virtual bool EncryptBlock(unsigned char* pucBlock);
    virtual bool DecryptBlock(unsigned char* pucBlock);
    void EncryptDirect(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void EncryptReverse(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void EncryptDirect1(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void EncryptReverse1(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void DecryptDirect(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void DecryptReverse(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void DecryptDirect1(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);
    void DecryptReverse1(unsigned char* pucBlock, 
        unsigned char const* pucXOR256);

private:
    unsigned char* m_pucXOR256;
};

The method's constants are calculated for a given key in the Initialize() method. For the First Variant:

bool CXOR256_0::Initialize(unsigned int iBlockSize, unsigned int iRounds, 
        unsigned char* pucChain, string const& rostrKey)
{
    if(0 == iBlockSize || 0 == iRounds)
        return false;
    m_iBlockSize = iBlockSize;
    m_iRounds = iRounds;
    //Limiting the key to the first 217 chars

    string ostrKey = rostrKey.substr(0, KEY_MAX);
    int iSize = ostrKey.size()>>1; //divided by 2

    if(0 == iSize)
        return false;
    bool bOdd = false;
    if(1 == (ostrKey.size()&1))
    {
        iSize++;
        bOdd = true;
    }
    //Create the Pseudo Random Generators

    CRand* paoRand = new CRand[iSize];
    if(true == bOdd)
        iSize--;
    //Generating the Seeds from Key

    int i, j;
    int iSum = 0;
    for(i=0; i<iSize; i++)
    {
        //Adding 1 to the character to avoid seed 0 because 

        //is cancelling the pseudo-random generator

        iSum += ostrKey[i<<1] + ((ostrKey[(i<<1)+1]+1)<<8) + 1;
        paoRand[i].SetSeed(iSum);
    }
    if(true == bOdd)
    {
        iSum += ostrKey[iSize<<1]+1;
        paoRand[iSize].SetSeed(iSum);
        iSize++;
    }
    //Initializing the Chain

    if(m_pucChain0 != NULL)
        delete [] m_pucChain0;
    if(m_pucChain != NULL)
        delete [] m_pucChain;
    m_pucChain0 = new unsigned char[m_iBlockSize];
    m_pucChain = new unsigned char[m_iBlockSize];
    memcpy(m_pucChain0, pucChain, m_iBlockSize);
    memcpy(m_pucChain, pucChain, m_iBlockSize);
    //Calculating the constants

    if(m_pucXOR != NULL)
        delete [] m_pucXOR;
    int iSize1 = m_iRounds*m_iBlockSize;
    m_pucXOR = new unsigned char[iSize1];
    if(m_puc256 != NULL)
        delete [] m_puc256;
    m_puc256 = new unsigned char[iSize1];
    for(i=0; i<iSize1; i++)
    {
        m_pucXOR[i] = 0;
        m_puc256[i] = 0;
        unsigned int uiRand;
        for(j=0; j<iSize; j++)
        {
            uiRand = paoRand[j].Rand();
            m_pucXOR[i] ^= uiRand;
            m_puc256[i] += uiRand;
        }
    }
    delete [] paoRand;
    m_pucTemp = new unsigned char[m_iBlockSize];
    return true;
}

For the Second Variant:

bool CXOR256_1::Initialize(unsigned int iBlockSize, unsigned int iRounds, 
        unsigned char* pucChain, string const& rostrKey)
{
    if(0 == iBlockSize || 0 == iRounds)
        return false;
    m_iBlockSize = iBlockSize;
    m_iRounds = iRounds;
    //Limiting the key to the first 217 chars

    string ostrKey = rostrKey.substr(0, KEY_MAX);
    int iSize = (ostrKey.size()+1)>>1; //divided by 2

    if(0 == iSize)
        return false;
    bool bOdd = false;
    if(1 == (ostrKey.size()&1))
    {
        iSize++;
        bOdd = true;
    }
    //Create the Pseudo Random Generators

    m_iSize = iSize;
    CRand* paoRand = new CRand[m_iSize];
    if(true == bOdd)
        iSize--;
    //Generating the Seeds from Key

    int i, j;
    int iSum = 0;
    for(i=0; i<iSize; i++)
    {
        //Adding 1 to the character to avoid seed 0 because is 

        //cancelling the pseudo-random generator

        iSum += ostrKey[i<<1] + ((ostrKey[(i<<1)+1]+1)<<8) + 1;
        paoRand[i].SetSeed(iSum);
    }
    if(true == bOdd)
    {
        iSum += ostrKey[iSize<<1]+1;
        paoRand[iSize].SetSeed(iSum);
        iSize++;
    }
    //Initializing the Chain

    if(m_pucChain0 != NULL)
        delete [] m_pucChain0;
    if(m_pucChain != NULL)
        delete [] m_pucChain;
    m_pucChain0 = new unsigned char[m_iBlockSize];
    m_pucChain = new unsigned char[m_iBlockSize];
    memcpy(m_pucChain0, pucChain, m_iBlockSize);
    memcpy(m_pucChain, pucChain, m_iBlockSize);
    //Calculating the constants

    if(m_pucXOR256 != NULL)
        delete [] m_pucXOR256;
    //The number of constants is given by  

    //Rounds * BlockSize * NoPseudoRandGen

    //NoPseudoRandGen = (KeySize+1)/2

    m_pucXOR256 = new unsigned char[m_iRounds*m_iBlockSize*m_iSize];
    unsigned char* pucXOR256 = &m_pucXOR256[0]; 
    for(i=0; i<m_iRounds*m_iBlockSize; i++)
        for(j=0; j<m_iSize; j++,pucXOR256++)
            *pucXOR256 = paoRand[j].Rand();
    delete [] paoRand;
    m_pucTemp = new unsigned char[m_iBlockSize];
    return true;
}

The code for one round encryption is in the methods EncryptDirect(), EncryptReverse(), EncryptDirect1() and EncryptReverse1() specialized for the cases of direct and respectively reverse block scanning and the consideration of the previous encrypted character or respectively previous unencrypted character. Direct block scanning considering the previous encrypted character, XOR256 First Variant:

void CXOR256_0::EncryptDirect(unsigned char* pucBlock, unsigned char const* pucXOR, 
    unsigned char const* puc256)
{
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++,pucXOR++,puc256++)
    {
        *pucBlock ^= ucPrev^*pucXOR;
        *pucBlock += *puc256;
        ucPrev = *pucBlock;
    }
}

Reverse block scanning considering the previous encrypted character, XOR256 First Variant:

void CXOR256_0::EncryptReverse(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--,pucXOR++,puc256++)
    {
        *pucBlock ^= ucPrev^*pucXOR;
        *pucBlock += *puc256;
        ucPrev = *pucBlock;
    }
}

Direct block scanning considering the previous unencrypted character, XOR256 First Variant:

void CXOR256_0::EncryptDirect1(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++,pucXOR++,puc256++)
    {
        ucTemp = *pucBlock;
        *pucBlock ^= ucPrev^*pucXOR;
        *pucBlock += *puc256;
        ucPrev = ucTemp;
    }
}

Reverse block scanning considering the previous unencrypted character, XOR256 First Variant:

void CXOR256_0::EncryptReverse1(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--,pucXOR++,puc256++)
    {
        ucTemp = *pucBlock;
        *pucBlock ^= ucPrev^*pucXOR;
        *pucBlock += *puc256;
        ucPrev = ucTemp;
    }
}

Direct block scanning considering the previous encrypted character, XOR256 Second Variant:

void CXOR256_1::EncryptDirect(unsigned char* pucBlock, 
                              unsigned char const* pucXOR256)
{
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++)
    {
        //First pseudo-random generator

        *pucBlock = (ucPrev ^ *pucBlock ^ *pucXOR256) + *pucXOR256;
        pucXOR256++;
        //The other m_iSize-1 pseudo-random generators

        for(int j=1; j<m_iSize; j++,pucXOR256++)
        {
            *pucBlock ^= *pucXOR256;
            *pucBlock += *pucXOR256;
        }
        ucPrev = *pucBlock;
    }
}

Reverse block scanning considering the previous encrypted character, XOR256 Second Variant:

void CXOR256_1::EncryptReverse(unsigned char* pucBlock, 
                               unsigned char const* pucXOR256)
{
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--)
    {
        //First pseudo-random generator

        *pucBlock = (ucPrev ^ *pucBlock ^ *pucXOR256) + *pucXOR256;
        pucXOR256++;
        //The other m_iSize-1 pseudo-random generators

        for(int j=1; j<m_iSize; j++,pucXOR256++)
        {
            *pucBlock ^= *pucXOR256;
            *pucBlock += *pucXOR256;
        }
        ucPrev = *pucBlock;
    }
}

Direct block scanning considering the previous unencrypted character, XOR256 Second Variant:

void CXOR256_1::EncryptDirect1(unsigned char* pucBlock, 
                               unsigned char const* pucXOR256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++)
    {
        ucTemp = *pucBlock;
        //First pseudo-random generator

        *pucBlock = (ucPrev ^ *pucBlock ^ *pucXOR256) + *pucXOR256;
        pucXOR256++;
        //The other m_iSize-1 pseudo-random generators

        for(int j=1; j<m_iSize; j++,pucXOR256++)
        {
            *pucBlock ^= *pucXOR256;
            *pucBlock += *pucXOR256;
        }
        ucPrev = ucTemp;
    }
}

Reverse block scanning considering the previous unencrypted character, XOR256 Second Variant:

void CXOR256_1::EncryptReverse1(unsigned char* pucBlock, 
                                unsigned char const* pucXOR256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--)
    {
        ucTemp = *pucBlock;
        //First pseudo-random generator

        *pucBlock = (ucPrev ^ *pucBlock ^ *pucXOR256) + *pucXOR256;
        pucXOR256++;
        //The other m_iSize-1 pseudo-random generators

        for(int j=1; j<m_iSize; j++,pucXOR256++)
        {
            *pucBlock ^= *pucXOR256;
            *pucBlock += *pucXOR256;
        }
        ucPrev = ucTemp;
    }
}

The above presented methods are called in the EncryptBlock() method which is doing the real block encryption. First Variant:

bool CXOR256_0::EncryptBlock(unsigned char* pucBlock)
{
    //Check First if is initialized

    if(0 == m_iBlockSize)
        return false;
    unsigned char const* pucXOR = m_pucXOR;
    unsigned char const* puc256 = m_puc256;
    bool bDir = true;
    for(int i=0; i<m_iRounds-1; i++,pucXOR+=m_iBlockSize,
        puc256+=m_iBlockSize)
    {
        if(true == bDir)
            EncryptDirect(pucBlock, pucXOR, puc256);
        else
            EncryptReverse(pucBlock, pucXOR, puc256);
        bDir = !bDir;
    }
    if(true == bDir)
        EncryptDirect1(pucBlock, pucXOR, puc256);
    else
        EncryptReverse1(pucBlock, pucXOR, puc256);
    return true;
}

Second Variant:

bool CXOR256_1::EncryptBlock(unsigned char* pucBlock)
{
    //Check First if is initialized

    if(0 == m_iBlockSize)
        return false;
    unsigned char const* pucXOR256 = m_pucXOR256;
    bool bDir = true;
    for(int i=0; i<m_iRounds-1; i++,pucXOR256+=m_iBlockSize*m_iSize)
    {
        if(true == bDir)
            EncryptDirect(pucBlock, pucXOR256);
        else
            EncryptReverse(pucBlock, pucXOR256);
        bDir = !bDir;
    }
    if(true == bDir)
        EncryptDirect1(pucBlock, pucXOR256);
    else
        EncryptReverse1(pucBlock, pucXOR256);
    return true;
}

The methods for the decryption case are corresponding to the above presented methods for the encryption case. Direct block scanning considering the previous encrypted character, XOR256 First Variant:

void CXOR256_0::DecryptDirect(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++,pucXOR++,puc256++)
    {
        ucTemp = *pucBlock;
        if(*puc256 <= *pucBlock)
            *pucBlock -= *puc256;
        else
            (*pucBlock += ~*puc256)++;
        *pucBlock ^= ucPrev^*pucXOR;
        ucPrev = ucTemp;
    }
}

Reverse block scanning considering the previous encrypted character, XOR256 First Variant:

void CXOR256_0::DecryptReverse(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--,pucXOR++,puc256++)
    {
        ucTemp = *pucBlock;
        if(*puc256 <= *pucBlock)
            *pucBlock -= *puc256;
        else
            (*pucBlock += ~*puc256)++;
        *pucBlock ^= ucPrev^*pucXOR;
        ucPrev = ucTemp;
    }
}

Direct block scanning considering the previous unencrypted character, XOR256 First Variant:

void CXOR256_0::DecryptDirect1(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucPrev = 0;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++,pucXOR++,puc256++)
    {
        if(*puc256 <= *pucBlock)
            *pucBlock -= *puc256;
        else
            (*pucBlock += ~*puc256)++;
        *pucBlock ^= ucPrev^*pucXOR;
        ucPrev = *pucBlock;
    }
}

Reverse block scanning considering the previous unencrypted character, XOR256 First Variant:

void CXOR256_0::DecryptReverse1(unsigned char* pucBlock, 
    unsigned char const* pucXOR, unsigned char const* puc256)
{
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--,pucXOR++,puc256++)
    {
        if(*puc256 <= *pucBlock)
            *pucBlock -= *puc256;
        else
            (*pucBlock += ~*puc256)++;
        *pucBlock ^= ucPrev^*pucXOR;
        ucPrev = *pucBlock;
    }
}

Direct block scanning considering the previous encrypted character, XOR256 Second Variant:

void CXOR256_1::DecryptDirect(unsigned char* pucBlock, 
                              unsigned char const* pucXOR256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    unsigned char const* pucXOR256_1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++)
    {
        pucXOR256 += m_iSize;
        pucXOR256_1 = pucXOR256 - 1;
        //The last m_iSize-1 pseudo-random generators

        ucTemp = *pucBlock;
        for(int j=m_iSize-1; j>0; j--,pucXOR256_1--)
        {
            if(*pucXOR256_1 <= *pucBlock)
                *pucBlock -= *pucXOR256_1;
            else
                (*pucBlock += ~(*pucXOR256_1))++;
            *pucBlock ^= *pucXOR256_1;
        }
        //First pseudo-random generator

        if(*pucXOR256_1 <= *pucBlock)
            *pucBlock -= *pucXOR256_1;
        else
            (*pucBlock += ~(*pucXOR256_1))++;
        *pucBlock ^= ucPrev ^ *pucXOR256_1;
        ucPrev = ucTemp;
    }
}

Reverse block scanning considering the previous encrypted character, Second Variant:

void CXOR256_1::DecryptReverse(unsigned char* pucBlock, 
                               unsigned char const* pucXOR256)
{
    unsigned char ucTemp;
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    unsigned char const* pucXOR256_1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--)
    {
        pucXOR256 += m_iSize;
        pucXOR256_1 = pucXOR256 - 1;
        //The last m_iSize-1 pseudo-random generators

        ucTemp = *pucBlock;
        for(int j=m_iSize-1; j>0; j--,pucXOR256_1--)
        {
            if(*pucXOR256_1 <= *pucBlock)
                *pucBlock -= *pucXOR256_1;
            else
                (*pucBlock += ~(*pucXOR256_1))++;
            *pucBlock ^= *pucXOR256_1;
        }
        //First pseudo-random generator

        if(*pucXOR256_1 <= *pucBlock)
            *pucBlock -= *pucXOR256_1;
        else
            (*pucBlock += ~(*pucXOR256_1))++;
        *pucBlock ^= ucPrev ^ *pucXOR256_1;
        ucPrev = ucTemp;
    }
}

Direct block scanning considering the previous unencrypted character, Second Variant:

void CXOR256_1::DecryptDirect1(unsigned char* pucBlock, 
                               unsigned char const* pucXOR256)
{
    unsigned char ucPrev = 0;
    unsigned char const* pucXOR256_1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock++)
    {
        pucXOR256 += m_iSize;
        pucXOR256_1 = pucXOR256 - 1;
        //The last m_iSize-1 pseudo-random generators

        for(int j=m_iSize-1; j>0; j--,pucXOR256_1--)
        {
            if(*pucXOR256_1 <= *pucBlock)
                *pucBlock -= *pucXOR256_1;
            else
                (*pucBlock += ~(*pucXOR256_1))++;
            *pucBlock ^= *pucXOR256_1;
        }
        //First pseudo-random generator

        if(*pucXOR256_1 <= *pucBlock)
            *pucBlock -= *pucXOR256_1;
        else
            (*pucBlock += ~(*pucXOR256_1))++;
        *pucBlock ^= ucPrev ^ *pucXOR256_1;
        ucPrev = *pucBlock;
    }
}

Reverse block scanning considering the previous unencrypted character, Second Variant:

void CXOR256_1::DecryptReverse1(unsigned char* pucBlock, 
                                unsigned char const* pucXOR256)
{
    unsigned char ucPrev = 0;
    pucBlock += m_iBlockSize-1;
    unsigned char const* pucXOR256_1;
    for(int i=0; i<m_iBlockSize; i++,pucBlock--)
    {
        pucXOR256 += m_iSize;
        pucXOR256_1 = pucXOR256 - 1;
        //The last m_iSize-1 pseudo-random generators

        for(int j=m_iSize-1; j>0; j--,pucXOR256_1--)
        {
            if(*pucXOR256_1 <= *pucBlock)
                *pucBlock -= *pucXOR256_1;
            else
                (*pucBlock += ~(*pucXOR256_1))++;
            *pucBlock ^= *pucXOR256_1;
        }
        //First pseudo-random generator

        if(*pucXOR256_1 <= *pucBlock)
            *pucBlock -= *pucXOR256_1;
        else
            (*pucBlock += ~(*pucXOR256_1))++;
        *pucBlock ^= ucPrev ^ *pucXOR256_1;
        ucPrev = *pucBlock;
    }
}

Block Decryption, First Variant:

bool CXOR256_0::DecryptBlock(unsigned char* pucBlock)
{
    //Check First if is initialized

    if(0 == m_iBlockSize)
        return false;
    unsigned char const* pucXOR = m_pucXOR+(m_iRounds-1)*m_iBlockSize;
    unsigned char const* puc256 = m_puc256+(m_iRounds-1)*m_iBlockSize;
    bool bDir;  
    if(1 == m_iRounds%2)
    {
        DecryptDirect1(pucBlock, pucXOR, puc256);
        bDir = false;
    }
    else
    {
        DecryptReverse1(pucBlock, pucXOR, puc256);
        bDir = true;
    }
    pucXOR-=m_iBlockSize;
    puc256-=m_iBlockSize;
    for(int i=0; i<m_iRounds-1; i++,pucXOR-=m_iBlockSize,
        puc256-=m_iBlockSize)
    {
        if(true == bDir)
            DecryptDirect(pucBlock, pucXOR, puc256);
        else
            DecryptReverse(pucBlock, pucXOR, puc256);
        bDir = !bDir;
    }
    return true;
}

Block Decryption, Second Variant:

bool CXOR256_1::Decrypt(unsigned char* pucBlock)
{
    //Check First if is initialized

    if(0 == m_iBlockSize)
        return false;
    unsigned char const* pucXOR256 = 
        m_pucXOR256+(m_iRounds-1)*m_iBlockSize*m_iSize;
    bool bDir;  
    if(1 == m_iRounds%2)
    {
        DecryptDirect1(pucBlock, pucXOR256);
        bDir = false;
    }
    else
    {
        DecryptReverse1(pucBlock, pucXOR256);
        bDir = true;
    }
    pucXOR256-=m_iBlockSize*m_iSize;
    for(int i=0; i<m_iRounds-1; i++,pucXOR256-=m_iBlockSize*m_iSize)
    {
        if(true == bDir)
            DecryptDirect(pucBlock, pucXOR256);
        else
            DecryptReverse(pucBlock, pucXOR256);
        bDir = !bDir;
    }
    return true;
}

Tee Encrypt() and Decrypt() methods for larger blocks of text are common and implemented in the IXOR256 abstract class. The size of the large block should be a multiple of the block size. Three operation modes are implemented: Electronic Code Book (ECB), Cipher Block Chaining (CBC) and Cipher Feedback Block (CFB) modes are implemented. In ECB mode if the same block is encrypted twice with the same key, the resulting cipher text blocks are the same. In CBC Mode a cipher text block is obtained by first xoring the plaintext block with the previous cipher text block, and encrypting the resulting value. In CFB mode a cipher text block is obtained by encrypting the previous cipher text block and xoring the resulting value with the plaintext. Large block encryption:

bool IXOR256::Encrypt(unsigned char const* pucIn, 
                      unsigned char* pucOut, size_t n, int iMode)
{
    //Check First if is initialized. n should be > 0 

    //and multiple of m_iBlockSize

    if(0==m_iBlockSize || 0==n || n%m_iBlockSize!=0)
        return false;
    unsigned int ui;
    unsigned char const* pucCurrentIn;
    unsigned char* pucCurrentOut;
    if(iMode == CBC) //CBC mode, using the Chain

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            Xor(pucCurrentOut, m_pucChain);
            Encrypt(pucCurrentOut);
            memcpy(m_pucChain, pucCurrentOut, m_iBlockSize);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    else if(iMode == CFB) //CFB mode, using the Chain

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            Encrypt(m_pucChain);
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            Xor(pucCurrentOut, m_pucChain);
            memcpy(m_pucChain, pucCurrentOut, m_iBlockSize);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    else //ECB mode, not using the Chain

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            Encrypt(pucCurrentOut);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    return true;
}

Large block decryption:

bool IXOR256::Decrypt(unsigned char const* pucIn, 
                      unsigned char* pucOut, size_t n, int iMode)
{
    //Check First if is initialized. n should be > 0 

    //and multiple of m_iBlockSize

    if(0==m_iBlockSize || 0==n || n%m_iBlockSize!=0)
        return false;
    unsigned int ui;
    unsigned char const* pucCurrentIn;
    unsigned char* pucCurrentOut;
    if(iMode == CBC) //CBC mode, using the Chain

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            memcpy(m_pucTemp, pucCurrentOut, m_iBlockSize);
            Decrypt(pucCurrentOut);
            Xor(pucCurrentOut, m_pucChain);
            memcpy(m_pucChain, m_pucTemp, m_iBlockSize);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    else if(iMode == CFB) //CFB mode, using the Chain, not using Decrypt()

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            Encrypt(m_pucChain);
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            memcpy(m_pucTemp, pucCurrentOut, m_iBlockSize);
            Xor(pucCurrentOut, m_pucChain);
            memcpy(m_pucChain, m_pucTemp, m_iBlockSize);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    else //ECB mode, not using the Chain

    {
        for(ui=0,pucCurrentIn=pucIn,pucCurrentOut=pucOut; 
            ui<n/m_iBlockSize; ui++)
        {
            memcpy(pucCurrentOut, pucCurrentIn, m_iBlockSize);
            Decrypt(pucCurrentOut);
            pucCurrentIn += m_iBlockSize;
            pucCurrentOut += m_iBlockSize;
        }
    }
    return true;
}

Some testing code showing how to use the CXOR256 class is presented bellow. One block testing three rounds:

CXOR256_1 oXOR256_1;

oXOR256_1.Initialize(8, 3, (unsigned char*)"\0\0\0\0\0\0\0\0","xxxxxxxx");

char szData1[] = "aaaaaaaa";
oXOR256_1.Encrypt((unsigned char*)szData1);
oXOR256_1.Decrypt((unsigned char*)szData1);

char szData2[] = "aaaaaaab";
oXOR256_1.Encrypt((unsigned char*)szData2);
oXOR256_1.Decrypt((unsigned char*)szData2);

oXOR256_1.Initialize(8, 3, (unsigned char*)"\0\0\0\0\0\0\0\0","xxxxxxxy");

char szData3[] = "aaaaaaaa";
oXOR256_1.Encrypt((unsigned char*)szData3);
oXOR256_1.Decrypt((unsigned char*)szData3);

char szData4[] = "aaaaaaab";
oXOR256_1.Encrypt((unsigned char*)szData4);
oXOR256_1.Decrypt((unsigned char*)szData4);

Larger block testing three rounds:

CXOR256_1 oXOR256_1;
oXOR256_1.Initialize(8, 3, (unsigned char*)"\0\0\0\0\0\0\0\0","xxxxxyyyyy");
char szDataIn1[25] = "ababababababababcccccccc";
char szDataIn[25];
szDataIn[24] = 0;
char szDataOut[25];
szDataOut[24] = 0;

//Test ECB
strcpy(szDataIn, szDataIn1);
memset(szDataOut, 0, 24);
oXOR256_1.Encrypt((unsigned char*)szDataIn, 
    (unsigned char*)szDataOut, 24, IXOR256::ECB);
memset(szDataIn, 0, 24);
oXOR256_1.Decrypt((unsigned char*)szDataOut, 
    (unsigned char*)szDataIn, 24, IXOR256::ECB);

//Test CBC
oXOR256_1.ResetChain();
strcpy(szDataIn, szDataIn1);
memset(szDataOut, 0, 24);
oXOR256_1.Encrypt((unsigned char*)szDataIn, 
    (unsigned char*)szDataOut, 24, IXOR256::CBC);
memset(szDataIn, 0, 24);
oXOR256_1.ResetChain();
oXOR256_1.Decrypt((unsigned char*)szDataOut, 
    (unsigned char*)szDataIn, 24, IXOR256::CBC);

//Test CFB
oXOR256_1.ResetChain();
strcpy(szDataIn, szDataIn1);
memset(szDataOut, 0, 24);
oXOR256_1.Encrypt((unsigned char*)szDataIn, 
    (unsigned char*)szDataOut, 24, IXOR256::CFB);
memset(szDataIn, 0, 24);
oXOR256_1.ResetChain();
oXOR256_1.Decrypt((unsigned char*)szDataOut, 
    (unsigned char*)szDataIn, 24, IXOR256::CFB);

Final Thoughts

Some analysis of the mathematical complexity of decrypting the XOR256 method is given in my previous article, but it was done for the stream ciphering case which is equivalent to applying just one round of the block ciphering. I really don't know how could I approach a decryption for many rounds without increasing the mathematical complexity of the problem to unpractical values. I am interested in any opinions and new ideas about how to attack this encryption method. In the project XOR256.zip attached to this article a test program of the XOR256 encryption/decryption method is implemented. All the source files of the presented classes are included in the project.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here