Panama

From Crypto++ Wiki
Jump to: navigation, search

Panama is a hash function and stream cipher created by designed by Joan Daemen and Craig Clapp. Also see Fast Hashing and Stream Encryption with PANAMA.

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 PanamaCipher::Encryption and PanamaCipher::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 "panama.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::FileSink;
    using CryptoPP::HexEncoder;
    using CryptoPP::LittleEndian;
    using CryptoPP::SecByteBlock;
    using CryptoPP::PanamaCipher;
    using CryptoPP::AutoSeededRandomPool;

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

    SecByteBlock key(32), iv(32);
    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
    PanamaCipher<LittleEndian>::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;

    PanamaCipher<LittleEndian>::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: 21D2674AA5855E278988A6B430061EFDBF0A6CCB77CB96CD3071075D181EA880
IV: 9A26B80AFD983CFF419E1A96DB6E29436E898EC42AB1A512899E2E69CBC98134
Plain: My Plaintext!! My Dear plaintext!!
Cipher: 526DDA62E54800D32EC745DF4E6DCC5B6087B97EB5EB0E792E780E7A9730D71B269C
Recovered: My Plaintext!! My Dear plaintext!!

Resynchronizing

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

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

#include <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::FileSink;
    using CryptoPP::HexEncoder;
    using CryptoPP::LittleEndian;
    using CryptoPP::SecByteBlock;
    using CryptoPP::PanamaCipher;
    using CryptoPP::AutoSeededRandomPool;

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

    SecByteBlock key(32), iv(32);
    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
    PanamaCipher<LittleEndian>::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;

    // PanamaCipher<LittleEndian>::Decryption dec;
    // dec.SetKeyWithIV(key, key.size(), iv, iv.size());

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

    // Perform the decryption with the encryptor
    // 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, including the non-printable characters from encryption.

$ ./test.exe
Key: 6E49BDD41931B54CD3743385BFAD871DCB39701EC02D0515570023B2FB0858CE
IV: D61853812259423CBBE5DAED4BD1BA5AC00410D6ABB05D86B3CEB1D424F97AFB
Plain: My Plaintext!! My Dear plaintext!!
Cipher: E713CC1C26DDD06DDF5A91C3E50C69590EB4BB7C6E28720BAA8C90D4B10AD0052B55
Recovered: My Plaintext!! My Dear plaintext!!

Pipelines

You can also use stream ciphers in a Pipeline. Below is an example of Panama 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 "panama.h"
#include "osrng.h"
#include "files.h"
#include "hex.h"

#include <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::FileSink;
    using CryptoPP::StringSink;
    using CryptoPP::StringSource;
    using CryptoPP::HexEncoder;
    using CryptoPP::LittleEndian;
    using CryptoPP::SecByteBlock;
    using CryptoPP::PanamaCipher;
    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(32);
    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
    PanamaCipher<LittleEndian>::Encryption enc;    
    enc.SetKeyWithIV(key, key.size(), iv, iv.size());

    // Decryption object
    PanamaCipher<LittleEndian>::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: 069469720CBB5446F8B10AE531682F2963D6749FDCAD4BDB74473F9B0A743272
IV: BAB4B39C5490EB63AE7E2CF2A391834777B244F99FCC14AD08CE5AB193641ABA
Plain: My Plaintext!! My Dear plaintext!!
Cipher: AFFD59D4B978570C69468BF9D5256EC37923FB139D78C9237D17A9B6B945268B49C4
Recovered: My Plaintext!! My Dear plaintext!!

Hashing and MAC'ing

PanamaMAC is in the Weak namespace, so you must define CRYPTOPP_ENABLE_NAMESPACE_WEAK to a non-0 value.

#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1

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

#include <iostream>
#include <string>

int main()
{
    using CryptoPP::byte;
    using CryptoPP::FileSink;
    using CryptoPP::HexEncoder;
    using CryptoPP::LittleEndian;
    using CryptoPP::Weak::PanamaMAC;
    using CryptoPP::SecByteBlock;
    using CryptoPP::AutoSeededRandomPool;

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

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

    // MAC object
    PanamaMAC<LittleEndian> hash;    
    hash.SetKey(key, key.size());

    // Perform the hashing
    hash.Update((const byte*)plain.data(), plain.size());
    
    digest.resize(32);
    hash.Final((byte*)&digest[0]);

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

    std::cout << "Digest: ";
    encoder.Put((const byte*)digest.data(), digest.size());
    encoder.MessageEnd();
    std::cout << 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!!
Key: E478B999FBC215A7F8BBD38C3979D4BCE4BE8971A4AB88FD7430DA2B0BFF57D6
Digest: 46B8FD66152ACADD84F551EC40910213C405DAADF8DC35E7D40FEDE2FFC1C4AD