Salsa20

From Crypto++ Wiki
Jump to: navigation, search

Salsa20 is a stream cipher by Daniel J. Bernstein and part of the eSTREAM portfolio. 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 salsafamily-20071225.pdf The Salsa20 family of stream ciphers.

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.

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 <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::Salsa20;
    using CryptoPP::SecByteBlock;
    using CryptoPP::AutoSeededRandomPool;

    AutoSeededRandomPool prng;
    std::string plain("My Plaintext!! My Dear plaintext!!"), cipher, recover;

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

    // 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: " << cipher << 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
Plain: My Plaintext!! My Dear plaintext!!
Cipher: '"▒ç]▒▒ѱ▒▒Ȏ▒▒T▒▒▒▒▒▒­▒&
                               ▒q▒▒▒
Recovered: My Plaintext!! My Dear plaintext!!

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 <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::Salsa20;
    using CryptoPP::SecByteBlock;
    using CryptoPP::AutoSeededRandomPool;

    AutoSeededRandomPool prng;
    std::string plain("My Plaintext!! My Dear plaintext!!"), cipher, recover;

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

    // 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: " << cipher << 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 decryptor
    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, including the non-printable characters from encryption.

$ ./test.exe
Plain: My Plaintext!! My Dear plaintext!!
Cipher: p▒3▒?▒▒I▒.D▒>▒▒▒▒Dx$E▒5▒▒p▒6▒[▒▒
Self inverting: 1
Resynchronizable: 1
Recovered: My Plaintext!! My Dear plaintext!!

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 CryptoPP::byte;
    using CryptoPP::Salsa20;
    using CryptoPP::FileSink;
    using CryptoPP::StringSink;
    using CryptoPP::StringSource;
    using CryptoPP::HexEncoder;
    using CryptoPP::SecByteBlock;
    using CryptoPP::AutoSeededRandomPool;
    using CryptoPP::StreamTransformationFilter;

    AutoSeededRandomPool prng;
    HexEncoder encoder(new FileSink(std::cout));
    std::string plain("My Plaintext!! My Dear plaintext!!"), cipher, recover;

    SecByteBlock key(32), 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: 16FE53114067E535C712A99781C18E8246CECEF354D334304B7B580F2B1DA495
IV: 105BC9A388E0D226
Plain: My Plaintext!! My Dear plaintext!!
Cipher: 19D12B9C2621A2E48FF3B14634530CA3EA7FBDB2FF48FBDC86FDDA1129B9A7B9992E
Recovered: My Plaintext!! My Dear plaintext!!