Salsa20

From Crypto++ Wiki
Jump to navigation Jump to search

Salsa20 is a stream cipher by Daniel J. Bernstein and part of eSTREAM portfolio Phase 3 (final) for Profile 1 (software). The 20-round stream cipher Salsa20/20 is consistently faster than AES and is recommended by the designer for typical cryptographic applications. The reduced-round ciphers Salsa20/12 and Salsa20/8 are among the fastest 256-bit stream ciphers available and are recommended for applications where speed is more important than confidence. Also see The Salsa20 family of stream ciphers.

Crypto++ provides all stream ciphers from eSTREAM Phase 3 for Profile 1. The ciphers are HC-128/256, Rabbit, Salsa20 and Sosemanuk. Additionally the library provides Bernstein's ChaCha.

Salsa20 provides a 8, 12 or 20 rounds. The default number of rounds is 20. The sample program below shows you how to change the number of rounds.

If you are used to working in languages like Jave 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 understand why you should consider using a block cipher and mode like CCM, GCM, or EAX.

Key and IV sizes

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

int main()
{
    using namespace CryptoPP;

    Salsa20::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: 32
key length (min): 16
key length (max): 32
iv size: 8

Encryption and Decryption

The following example shows you how to use Salsa20::Encryption and Salsa20::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 "salsa.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("Salsa20 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
    Salsa20::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;

    Salsa20::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, including the non-printable characters from encryption.

$ ./test.exe
Key: 9692FFCDAD67D6D1FE4575147AD3514F
IV: 4C7D97FDFEC42D8F
Plain: Salsa20 stream cipher test
Cipher: 0E5DC8BE2D07E868E7880D6607EBF13B7CCCB74804D8247DE279
Recovered: Salsa20 stream cipher test

Resynchronizing

The Salsa family is both self-inverting and resynchronizable so you can use the encryption object for decryption, too.

#include "cryptlib.h"
#include "secblock.h"
#include "salsa.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("Salsa20 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
    Salsa20::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;

    // Salsa20::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: 0C7300E07D8E607A0AD3781C9D4E46DA
IV: CE1A0D2126033712
Plain: Salsa20 stream cipher test
Cipher: B45F2EAB9F591B3F959DB9CFB6B6BAE92CE43A05D35A06322761
Self inverting: 1
Resynchronizable: 1
Recovered: Salsa20 stream cipher test

Pipelines

You can also use stream ciphers in a Pipeline. Below is an example of Salsa20 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 "salsa.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("Salsa20 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(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
    Salsa20::Encryption enc;    
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Decryption object
    Salsa20::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: 611F2AB2A64EE564691A67742B7B7ECB
IV: B9214F705114FDEB
Plain: Salsa20 stream cipher test
Cipher: 3F8FDDB34834DCA55D3C14B227C5C0C8C17461994A16E92F569A
Recovered: Salsa20 stream cipher test

Rounds

If you need to set the number of rounds you have two choices. First, you can use SetKeyWithRounds and then call Resynchronize to set the IV. Second, you can use NameValuePairs and SetKey. Below is an example of the second method.

SecByteBlock key(16), iv(8);
...

AlgorithmParameters params = MakeParameters("Rounds", 12);
    MakeParameters("IV", ConstByteArrayParameter(iv, iv.size()));

Salsa20::Encryption salsa;
salsa.SetKey(key, key.size(), params);