Poly1305

From Crypto++ Wiki
Jump to navigation Jump to search

Poly1305 is an AES-based MAC algorithm by D. J. Bernstein specified at Poly1305-AES: A state-of-the-art message-authentication code. Poly1305 computes a 16-byte authenticator of a message of any length, using a 16-byte nonce and a 32-byte secret key.

ChaCha20 combined with Poly1305 results in an authenticated encryption scheme. Crypto++ provides the authenticated encryption scheme using the ChaCha20Poly1305 class. Also see XChaCha20Poly1305, which is ChaCha20Poly1305 hardened against nonce misuse.

Sample Programs

There are two sample programs below. The first sample program below demonstrates a Poly1305 using filters (see pipelining). The second sample program uses HashTransformation member functions.

Pipelines

#include "cryptlib.h"
#include "poly1305.h"
#include "filters.h"
#include "osrng.h"
#include "aes.h"
#include "hex.h"

#include <iostream>
#include <iomanip>
#include <string>

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

    AutoSeededRandomPool prng;

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

    std::string plain = "Poly1305 Test";
    std::string mac, encoded;

    /*********************************\
    \*********************************/

    // Pretty print key
    encoded.clear();
    StringSource ss1(key, key.size(), true,
        new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource

    std::cout << "key: " << encoded << std::endl;
    std::cout << "plain: " << plain << std::endl;

    /*********************************\
    \*********************************/

    try
    {
        Poly1305<AES> poly1305(key, key.size());

        StringSource ss2(plain, true, 
            new HashFilter(poly1305,
                new StringSink(mac)
            ) // HashFilter      
        ); // StringSource
    }
    catch(const CryptoPP::Exception& e)
    {
        std::cerr << e.what() << std::endl;
        exit(1);
    }

    /*********************************\
    \*********************************/

    // Pretty print
    encoded.clear();
    StringSource ss3(mac, true,
        new HexEncoder(
            new StringSink(encoded)
        ) // HexEncoder
    ); // StringSource

    std::cout << "mac: " << encoded << std::endl;

    return 0;
}

A typical output is shown below. Note that each run will produce different results because the key is randomly generated.

$ ./test.exe 
key: 96D246CF3B7159C4E3A532E9DBF9B90061D7FF14977053BC227F9AB4AD74BA0D
plain: Poly1305 Test
mac: 1EA2F653C4E45C81732E72E09A53D8E2

To verify a Poly1305 on a message, use a HashVerificationFilter.

try
{
    Poly1305<AES> poly1305(key, key.size());
    const int flags = HashVerificationFilter::THROW_EXCEPTION | HashVerificationFilter::HASH_AT_END;
    
    StringSource(plain + mac, true, 
        new HashVerificationFilter(poly1305, NULL, flags)
    ); // StringSource

    std::cout << "Verified message" << std::endl;
}
catch(const CryptoPP::Exception& e)
{
    std::cerr << e.what() << std::endl;
    ...
}

We can tamper with a message as follows, which will cause the HashVerificationFilter to throw the exception, HashVerificationFilter: message hash or MAC not valid:

Poly1305 poly1305(key, key.size());

// Tamper with message
plain[0] ^= 0x01;

// Tamper with MAC
mac[0] ^= 0x01;

StringSource(plain + mac, true, 
    new HashVerificationFilter(poly1305, NULL, THROW_EXCEPTION | HASH_AT_END)
); // StringSource

Switching to another block cipher, such as Camellia, is as simple as the following:

Poly1305<Camellia> poly1305(key, key.size());

StringSource(plain, true, 
    new HashFilter(poly1305,
        new StringSink(mac)
    ) // HashFilter      
); // StringSource

HashTransformation

Below is an example of using HashTransformation member functions to calculate a Poly1305. Internally, pipelines do this for you.

#include "cryptlib.h"
#include "poly1305.h"
#include "files.h"
#include "aes.h"
#include "hex.h"

#include <string>
#include <iostream>

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

    const byte m[28] = {
      0x5,0x8,0xC,0xE,0x1,0xE,0x6,0x0,0x1,0x1,
      0x1,0x1,0x1,0x1,0x1,0x1,0x6,0x4,0x6,0x1,
      0x7,0x4,0x6,0x1,0x0,0x0,0x0,0x0
    };

    const byte k[32] = {
      0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,
      0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,
      0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,0x1,
      0x1,0x1
    };

    HexEncoder hex(new FileSink(std::cout));

    Poly1305<AES> poly1305(k, sizeof(k));
    poly1305.Update(m, sizeof(m));

    byte d[Poly1305<AES>::DIGESTSIZE];
    poly1305.Final(d);

    std::cout << "Message: ";
    hex.Put(m, sizeof(m));
    hex.MessageEnd();
    std::cout << std::endl;

    std::cout << "Mac: ";
    hex.Put(d, sizeof(d));
    hex.MessageEnd();
    std::cout << std::endl;

    return 0;
}

The program produces the expected output:

$ ./test.exe 
Message: 05080C0E010E06000101010101010101060406010704060100000000
Mac: 202A1A4DC096A54EC8E65FF59BDB752C

Downloads