CTR Mode

From Crypto++ Wiki
Jump to navigation Jump to search
Counter Mode
Documentation
#include <cryptopp/modes.h>

CTR mode is counter mode and uses a counter rather than a random or unpredictable initialization vector. The counter has additional properties, including an unique value and initial counter block. The mode does not require padding the plain text to the block size of the cipher. CTR mode was standardized in 2001 by NIST in SP 800-38A.

Crypto++ offers several modes of operation, including ECB, CBC, OFB, CFB, CBC-CTS, CTR, XTS, CCM, EAX, GCM and OCB.

Crypto++ does not provide a way to retrieve the current IV or counter used for encryption or decryption. If you need the current IV or counter then you need to manage it yourself. Some ciphers allow you to seek a number of bytes or blocks in the stream.

If you are used to working in languages like Java or libraries like OpenSSL, then you might want to visit the Init-Update-Final wiki page. Crypto++ provides the transformation model, but its not obvious because its often shrouded behind Pipelines.

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 consider using an algorithm or mode like CCM, GCM, EAX or ChaCha20Poly1305.

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. The 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

There are three sample programs for CTR mode. The samples use filters in a pipeline). Pipelining is a high level abstraction and it handles buffering input, buffering output and padding for you. The first sample shows the basic use of a pipeline. The second sample shows how to change padding. The third shows how to manually insert into a filter.

If you are benchmarking then you may want to visit Benchmarks | Sample Program . It shows you how to use StreamTransformation and its ProcessString method to process multiple blocks at a time. ProcessString eventually calls BlockTransformation and ProcessBlock. Calling a cipher's ProcessString or ProcessBlock eventually call ProcessAndXorBlock or AdvancedProcessBlocks, and they are the lowest level API you can use.

Pipelines

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 ss1( 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 ss2( 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 ss3( 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

Non-pipeline

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());

Seeking

The following program seeks in the AES/CTR stream. Using a Crypto++ Pipeline is a tad bit awkward because Discard or Skip on a Source does not work as expected. You have to Pump data into "nothing" under the current implementation. Also see Skip'ing on a Source does not work as expected on Stack Overflow.

Below is an example of using AES/CTR and seeking in the stream. It needs to perform a "two part" seek. First, it discards bytes on the Source called cipher. Second, it seeks in the keystream on the encryption object called enc to synchronize the counter. Once the seek is performed, the remainder of the cipher text is decrypted by calling PumpAll(), which pumps the remainder of the data through the pipeline.

#include "modes.h"
#include "aes.h"
using namespace CryptoPP;
    
int main(int argc, char* argv[])
{
    string plain = "Now is the time for all good men to come to the aide of their country";
    	
    byte key[AES::DEFAULT_KEYLENGTH] = {0};
    byte nonce[AES::BLOCKSIZE] = {0};
    	
    CTR_Mode<AES>::Encryption enc;
    enc.SetKeyWithIV(key, sizeof(key), nonce, sizeof(nonce));
    	
    string cipher;
    StringSource ss1(plain, true, new StreamTransformationFilter(enc, new StringSink(cipher)));
    
    for(size_t i=0; i<cipher.size(); i++)
    {	
        CTR_Mode<AES>::Decryption dec;
        dec.SetKeyWithIV(key, sizeof(key), nonce, sizeof(nonce));
    		
        StringSource ss2(cipher, false);
        ss2.Pump(i);
        dec.Seek(i);
    		
        string recover;
        StreamTransformationFilter stf(dec, new StringSink(recover));
    
        // Attach the decryption filter after seeking
        ss2.Attach(new Redirector(stf));
        ss2.PumpAll();
    		
        cout << i << ": " << recover << endl;
    }
    
    return 0;
}

Here is the result:

$ ./test.exe 
 0: Now is the time for all good men to come to the aide of their country
 1: ow is the time for all good men to come to the aide of their country
 2: w is the time for all good men to come to the aide of their country
 3:  is the time for all good men to come to the aide of their country
 4: is the time for all good men to come to the aide of their country
 5: s the time for all good men to come to the aide of their country
 6:  the time for all good men to come to the aide of their country
 7: the time for all good men to come to the aide of their country
 8: he time for all good men to come to the aide of their country
 9: e time for all good men to come to the aide of their country
10:  time for all good men to come to the aide of their country
...
58: eir country
59: ir country
60: r country
61:  country
62: country
63: ountry
64: untry
65: ntry
66: try
67: ry
68: y

External Cipher

Counter mode uses the forward transformation for both encryption and decryption. The keystream is generated by encrypting the IV/counter, and then the keystream is XOR'd with the plaintext or ciphertext. If you are using an external cipher, then you should use the encryptor in both directions. Also see AES/CTR Decryption stops unexpectedly using Crypto++ on Stack Overflow.

#include "cryptlib.h"
#include "secblock.h"
#include "filters.h"
#include "modes.h"
#include "aes.h"

#include <iostream>
#include <string>

int main (int argc, char* argv[])
{
    using namespace CryptoPP;

    SecByteBlock key(16), iv(16);
    std::memset(key, 0xff, key.size());
    std::memset( iv, 0x88,  iv.size());

    std::string message, encrypted, decrypted;
    message = "Now is the time for all good men "
        "to come to the aide of their country.";

    AES::Encryption aesEncryption(key, key.size());
    CTR_Mode_ExternalCipher::Encryption ctrEncryption(aesEncryption, iv);

    StreamTransformationFilter stfEncryptor(
        ctrEncryption, new StringSink(encrypted));

    stfEncryptor.Put((const byte*)&message[0], message.size());
    stfEncryptor.MessageEnd();

    AES::Encryption aesDecryption(key, key.size());
    CTR_Mode_ExternalCipher::Decryption ctrDecryption(aesDecryption, iv);

    StreamTransformationFilter stfDecryptor(
        ctrDecryption, new StringSink(decrypted));

    stfDecryptor.Put((const byte*)&encrypted[0], encrypted.size());
    stfDecryptor.MessageEnd();

    std::cout << "Message: " << message << std::endl;
    std::cout << "Recovered: " << decrypted << std::endl;

    return 0;
}

Running the program results in the following.

$ ./test.exe
Message: Now is the time for all good men to come to the aide of their country.
Recovered: Now is the time for all good men to come to the aide of their country.

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

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

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

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

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