CTR Mode

From Crypto++ Wiki
Jump to: navigation, search

CTR is counter mode. CTR mode was standardized in 2001 by NIST in SP 800-38A. CTR mode uses a counter rather than a traditional IV. The counter has additional properties, including a nonce and initial counter block. The mode does not require padding the plain text to the block size of the cipher.

Note: if your project is using encryption alone to secure your data, encryption alone is usually not enough. Please take a moment to read Authenticated Encryption and understand why you should prefer to use CCM, GCM, or EAX over other modes, such as CBC or CTR.

Counter Increment

Broadly speaking, NIST specifies two types of counters. First is a counter which is made up of a nonce and counter. The nonce is random, and the remaining bytes are counter bytes (which are incremented). For example, a 16 byte block cipher might use the high 8 bytes as a nonce, and the low 8 bytes as a counter. Second is a counter block, where all bytes are counter bytes and can be incremented as carries are generated. For example, in a 16 byte block cipher, all 16 bytes are counter bytes.

Crypto++ uses the second method, which means the entire byte block is treated as counter bytes. Counter mode is declared in modes.h:

class CTR_ModePolicy : public ModePolicyCommonTemplate<AdditiveCipherAbstractPolicy>
{
public:
    bool CipherIsRandomAccess() const {return true;}
    IV_Requirement IVRequirement() const {return RANDOM_IV;}
    static const char * StaticAlgorithmName() {return "CTR";}

protected:
    virtual void IncrementCounterBy256();
    ...
};

The implementation increments the entire counter block. modes.cpp offers IncrementCounterBy256:

void CTR_ModePolicy::IncrementCounterBy256()
{
    IncrementCounterByOne(m_counterArray, BlockSize()-1);
}

IncrementCounterByOne is located in misc.h, and it performs:

inline void IncrementCounterByOne(byte *inout, unsigned int s)
{
    for (int i=s-1, carry=1; i>=0 && carry; i--)
        carry = !++inout[i];
}

Sample Program

The sample program below demonstrates AES in CTR mode using filters (see pipelining). Though the key is declared on the stack, a SecByteBlock is used to ensure the sensitive material is zeroized. Similar could be used for both plain text and recovered text.

AutoSeededRandomPool prng;

SecByteBlock key(AES::DEFAULT_KEYLENGTH);
prng.GenerateBlock( key, key.size() );

byte ctr[ AES::BLOCKSIZE ];
prng.GenerateBlock( ctr, sizeof(ctr) );

string plain = "CTR Mode Test";
string cipher, encoded, recovered;

/*********************************\
\*********************************/

try
{
    cout << "plain text: " << plain << endl;

    CTR_Mode< AES >::Encryption e;
    e.SetKeyWithIV( key, key.size(), ctr );

    // The StreamTransformationFilter adds padding
    //  as required. ECB and CBC Mode must be padded
    //  to the block size of the cipher. CTR does not.
    StringSource( plain, true, 
        new StreamTransformationFilter( e,
            new StringSink( cipher )
        ) // StreamTransformationFilter      
    ); // StringSource
}
catch( CryptoPP::Exception& e )
{
    cerr << e.what() << endl;
    exit(1);
}

/*********************************\
\*********************************/

// Pretty print cipher text
StringSource( cipher, true,
    new HexEncoder(
        new StringSink( encoded )
    ) // HexEncoder
); // StringSource
cout << "cipher text: " << encoded << endl;

/*********************************\
\*********************************/

try
{
    CTR_Mode< AES >::Decryption d;
    d.SetKeyWithIV( key, key.size(), ctr );

    // The StreamTransformationFilter removes
    //  padding as required.
    StringSource( cipher, true, 
        new StreamTransformationFilter( d,
            new StringSink( recovered )
        ) // StreamTransformationFilter
    ); // StringSource

    cout << "recovered text: " << recovered << endl;
}
catch( CryptoPP::Exception& e )
{
    cerr << e.what() << endl;
    exit(1);
}

A typical output is shown below. Note that each run will produce different results because the key and initial counter block are randomly generated.

$ ./Driver.exe 
key: F534FC7F0565A8CF1629F01DB31AE3CA
counter: A4D16CBC010DACAA2E54FA676B57A345
plain text: CTR Mode Test
cipher text: 12455EDB41020E6D751F207EE6
recovered text: CTR Mode Test

To manually insert bytes into the filter, perform multiple Puts. Though Get is used below, a StringSink could easily be attached and save the administrivia.

const size_t SIZE = 16 * 4;
string plain(SIZE, 0x00);

for(size_t i = 0; i < plain.size(); i++)
    plain[i] = 'A' + (i%26);
...

CTR_Mode < AES >::Encryption encryption(key, sizeof(key), iv);
StreamTransformationFilter encryptor(encryption, NULL);

for(size_t j = 0; j < plain.size(); j++)
    encryptor.Put((byte)plain[j]);

encryptor.MessageEnd();
size_t ready = encryptor.MaxRetrievable();

string cipher(ready, 0x00);
encryptor.Get((byte*) &cipher[0], cipher.size());

Downloads

IDEA-CTR-Filter.zip - Demonstrates encryption and decryption using IDEA in CTR mode with filters

Blowfish-CTR-Filter.zip - Demonstrates encryption and decryption using Blowfish in CTR mode with filters

AES-CTR-Filter.zip - Demonstrates encryption and decryption using AES in CTR mode with filters

Camellia-CTR-Filter.zip - Demonstrates encryption and decryption using Camellia in CTR mode with filters