Rabbit

From Crypto++ Wiki
Jump to navigation Jump to search
Rabbit
Documentation
#include <cryptopp/rabbit.h>

Rabbit is a stream cipher by by Martin Boesgaard, Mette Vesterager, Thomas Pedersen, Jesper Christiansen and Ove Scavenius. The ciphers use a 128-bit key, and an optional 64-bit initialization vector (IV). The cipher is part of eSTREAM portfolio Phase 3 (final) for Profile 1 (software). The reference materials and source files for the Crypto++ implementation are available at The eSTREAM Project | Rabbit.

Rabbit takes an optional IV which lead to two Crypto++ classes for the cipher. The first class is Rabbit which provides the cipher without and IV. The second class is RabbitWithIV and it provides the cipher with an IV. The two classes were necessary because Crypto++ is not setup for optional IVs. The code below uses RabbitWithIV because it is a more secure configuration and helps avoid key reuse vulnerabilities.

Crypto++ provides all stream ciphers from eSTREAM Phase 3 for Profile 1. The ciphers are ChaCha, HC-128/256, Rabbit, Salsa20 and Sosemanuk. The IETF's version of ChaCha is specified in RFC 7539, ChaCha20 and Poly1305 for IETF Protocols and available as ChaChaTLS.

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.

Key and IV sizes

The first sample program prints Rabbit's and RabbitWithIV key and iv sizes.

int main()
{
    using namespace CryptoPP;

    Rabbit::Encryption enc;
    std::cout << "key length: " << enc.DefaultKeyLength() << std::endl;
    std::cout << "key length (min): " << enc.MinKeyLength () << std::endl;
    std::cout << "key length (max): " << enc.MaxKeyLength () << std::endl;
    std::cout << "iv size: " << enc.IVSize() << std::endl;

    return 0;
}

A typical output is shown below.

$ ./test.exe
key length: 16
key length (min): 16
key length (max): 16
iv size: 0

And switching to RabbitWithIV results in the following.

$ ./test.exe
key length: 16
key length (min): 16
key length (max): 16
iv size: 8

Encryption and Decryption

The following example shows you how to use RabbitWithIV::Encryption and RabbitWithIV::Decryption. &cipher[0] may look odd, but its how to get the non-const pointer from a std::string.

#include "cryptlib.h"
#include "secblock.h"
#include "rabbit.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include <iostream>
#include <string>

int main()
{
    using namespace CryptoPP;

    AutoSeededRandomPool prng;
    HexEncoder encoder(new FileSink(std::cout));
    std::string plain("Rabbit stream cipher test"), cipher, recover;

    SecByteBlock key(16), iv(8);
    prng.GenerateBlock(key, key.size());
    prng.GenerateBlock(iv, iv.size());

    std::cout << "Key: ";
    encoder.Put((const byte*)key.data(), key.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "IV: ";
    encoder.Put((const byte*)iv.data(), iv.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    // Encryption object
    RabbitWithIV::Encryption enc;    
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Perform the encryption
    cipher.resize(plain.size());
    enc.ProcessData((byte*)&cipher[0], (const byte*)plain.data(), plain.size());

    std::cout << "Plain: " << plain << std::endl;

    std::cout << "Cipher: ";
    encoder.Put((const byte*)cipher.data(), cipher.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    RabbitWithIV::Decryption dec;
    dec.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Perform the decryption
    recover.resize(cipher.size());
    dec.ProcessData((byte*)&recover[0], (const byte*)cipher.data(), cipher.size());

    std::cout << "Recovered: " << recover << std::endl;

    return 0;
}

A typical output is shown below.

$ ./test.exe
Key: 23C2731E8B5469FD8DABB5BC592A0F3A
IV: 712906405EF03201
Plain: Rabbit stream cipher test
Cipher: 1AE2D4EDCF9B6063B00FD6FDA0B223ADED157E77031CF0440B
Recovered: Rabbit stream cipher test

Resynchronizing

The Rabbit cipher is self-inverting so you can use the encryption object for decryption (and vice versa). The cipher holds internal state and is resynchronizable. If you want to reuse an encryption or decryption object then you should set the IV with Resynchronize.

#include "cryptlib.h"
#include "secblock.h"
#include "rabbit.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include <iostream>
#include <string>

int main()
{
    using namespace CryptoPP;

    AutoSeededRandomPool prng;
    HexEncoder encoder(new FileSink(std::cout));
    std::string plain("Rabbit stream cipher test"), cipher, recover;

    SecByteBlock key(16), iv(8);
    prng.GenerateBlock(key, key.size());
    prng.GenerateBlock(iv, iv.size());

    std::cout << "Key: ";
    encoder.Put((const byte*)key.data(), key.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "IV: ";
    encoder.Put((const byte*)iv.data(), iv.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    // Encryption object
    RabbitWithIV::Encryption enc;    
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Perform the encryption
    cipher.resize(plain.size());
    enc.ProcessData((byte*)&cipher[0], (const byte*)plain.data(), plain.size());

    std::cout << "Plain: " << plain << std::endl;

    std::cout << "Cipher: ";
    encoder.Put((const byte*)cipher.data(), cipher.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    // RabbitWithIV::Decryption dec;
    // dec.SetKeyWithIV(key, key.size(), iv, iv.size());

    std::cout << "Self inverting: " << enc.IsSelfInverting() << std::endl;
    std::cout << "Resynchronizable: " << enc.IsResynchronizable() << std::endl;

    enc.Resynchronize(iv, iv.size());

    // Perform the decryption
    // recover.resize(cipher.size());
    // dec.ProcessData((byte*)&recover[0], (const byte*)cipher.data(), cipher.size());

    // Perform the decryption with the encryptor
    recover.resize(cipher.size());
    enc.ProcessData((byte*)&recover[0], (const byte*)cipher.data(), cipher.size());

    std::cout << "Recovered: " << recover << std::endl;

    return 0;
}

A typical output is shown below.

$ ./test.exe
Key: 0EA30464E88F321047ACCCFED2AC18F0
IV: CCEBA07AE8B1FBE6
Plain: Rabbit stream cipher test
Cipher: 747ED2055DA707D9A04F717F28F8A010DD8A84F31EE3262745
Self inverting: 1
Resynchronizable: 1
Recovered: Rabbit stream cipher test

The following C++11 program demonstrates resynchronizing without the additional operations like printing a key or iv. The library was built with CXXFLAGS="-DNDEBUG -g2 -O3 -std=c++11.

#include "cryptlib.h"
#include "rabbit.h"

#include <iostream>
#include <array>
#include <cstdint>

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

    const uint8_t rabbitKey[16] = "012345678901234";
    const uint8_t rabbitIV[8] = "0123456";

    RabbitWithIV::Encryption enc;
    RabbitWithIV::Decryption dec;
    enc.SetKeyWithIV(rabbitKey, 16, rabbitIV, 8);
    dec.SetKeyWithIV(rabbitKey, 16, rabbitIV, 8);

    std::array<byte, 3> origin = { 1,2,3 };
    std::array<byte, 3> encrpyt;
    enc.ProcessData(encrpyt.data(), origin.data(), origin.size());

    std::array<byte, 3> decrypt;
    dec.ProcessData(decrypt.data(), encrpyt.data(), encrpyt.size());

    dec.Resynchronize(rabbitIV, sizeof(rabbitIV));
    dec.ProcessData(decrypt.data(), encrpyt.data(), encrpyt.size());

    dec.Resynchronize(rabbitIV, sizeof(rabbitIV));
    dec.ProcessData(decrypt.data(), encrpyt.data(), encrpyt.size());
    
    std::cout << (int)decrypt[0] << " " << (int)decrypt[1] << " " << (int)decrypt[2] << std::endl;
    
    return 0;
}

It produces the following result.

$ g++ -DNDEBUG -g2 -O3 -std=c++11 test.cxx -o test.exe ./libcryptopp.a
$ ./test.exe
1 2 3

Pipelines

You can also use stream ciphers in a Pipeline. Below is an example of Rabbit participating in a pipeline. Internally, StreamTransformationFilter calls ProcessData on the incoming data stream. The filter also buffers output if there is no attached transformation or sink.

#include "cryptlib.h"
#include "secblock.h"
#include "filters.h"
#include "rabbit.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include <iostream>
#include <string>

int main()
{
    using namespace CryptoPP;

    AutoSeededRandomPool prng;
    HexEncoder encoder(new FileSink(std::cout));
    std::string plain("Rabbit stream cipher test"), cipher, recover;

    SecByteBlock key(16), iv(16);
    prng.GenerateBlock(key, key.size());
    prng.GenerateBlock(iv, iv.size());

    std::cout << "Key: ";
    encoder.Put(key.data(), key.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "IV: ";
    encoder.Put(iv.data(), iv.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    // Encryption object
    RabbitWithIV::Encryption enc;    
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Decryption object
    RabbitWithIV::Decryption dec;    
    dec.SetKeyWithIV(key, key.size(), iv, iv.size());

    StringSource ss1(plain, true, new StreamTransformationFilter(enc, new StringSink(cipher)));
    StringSource ss2(cipher, true, new StreamTransformationFilter(dec, new StringSink(recover)));

    std::cout << "Plain: " << plain << std::endl;

    std::cout << "Cipher: ";
    encoder.Put((const byte*)cipher.data(), cipher.size());
    encoder.MessageEnd();
    std::cout << std::endl;

    std::cout << "Recovered: " << recover << std::endl;

    return 0;
}

The program produces the expected output:

$ ./test.exe
Key: BD9E9C4E91C9B2858741C46AA91A11DE
IV: 2374EC9C1026B41C
Plain: Rabbit stream cipher test
Cipher: 81FDD1CEF6549AA55032B45197B22F0A4A043B59BE7084CA02
Recovered: Rabbit stream cipher test