Poly1305
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