ChaCha20Poly1305

From Crypto++ Wiki
Jump to navigation Jump to search
ChaCha20Poly1305
Documentation
#include <cryptopp/chachapoly.h>

ChaCha20Poly1305 is an authenticated encryption scheme that combines ChaChaTLS and Poly1305. The scheme uses the IETF versions of the ciphers because it is defined in RFC 8439, ChaCha20 and Poly1305 for IETF Protocols. The scheme is somewhat unique because it pairs a stream cipher with an authenticator (most authenticated encryption modes pair a block cipher).

Crypto++ added ChaCha20Poly1305 to the library version 8.1. Also see Issue 724 and Commit 315e54e63d58.

ChaCha20 AVX2 implementation had a bug related to carries. On occasion the tail of the cipher text would be incorrect. The bug appeared to be relatively rare when the cpu had AVX2, and it did not appear in other implementations like SSE or NEON. The bug was fixed in Crypto++ 8.6. Also see Issue 1069 and Commit 20962baf4440.

A related page is XChaCha20Poly1305.

Example

The example below uses the test vector from RFC 8439, section 2.8.

#include "cryptlib.h"
#include "chachapoly.h"
#include "filters.h"
#include "files.h"
#include "hex.h"

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

    const byte pt[] = {
        0x4c,0x61,0x64,0x69,0x65,0x73,0x20,0x61,0x6e,0x64,0x20,0x47,0x65,0x6e,0x74,0x6c,
        0x65,0x6d,0x65,0x6e,0x20,0x6f,0x66,0x20,0x74,0x68,0x65,0x20,0x63,0x6c,0x61,0x73,
        0x73,0x20,0x6f,0x66,0x20,0x27,0x39,0x39,0x3a,0x20,0x49,0x66,0x20,0x49,0x20,0x63,
        0x6f,0x75,0x6c,0x64,0x20,0x6f,0x66,0x66,0x65,0x72,0x20,0x79,0x6f,0x75,0x20,0x6f,
        0x6e,0x6c,0x79,0x20,0x6f,0x6e,0x65,0x20,0x74,0x69,0x70,0x20,0x66,0x6f,0x72,0x20,
        0x74,0x68,0x65,0x20,0x66,0x75,0x74,0x75,0x72,0x65,0x2c,0x20,0x73,0x75,0x6e,0x73,
        0x63,0x72,0x65,0x65,0x6e,0x20,0x77,0x6f,0x75,0x6c,0x64,0x20,0x62,0x65,0x20,0x69,
        0x74,0x2e
    };

    const byte aad[] = {
        0x50,0x51,0x52,0x53,0xc0,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7
    };

    const byte key[] = {
        0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8a,0x8b,0x8c,0x8d,0x8e,0x8f,
        0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0x9b,0x9c,0x9d,0x9e,0x9f
    };

    const byte iv[] = {
        0x07,0x00,0x00,0x00,                      // Common
        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47   // IV
    };

    byte ct[sizeof(pt)], rt[sizeof(ct)], mac[16];

    ChaCha20Poly1305::Encryption enc;
    enc.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
    enc.EncryptAndAuthenticate(ct, mac, sizeof(mac), iv, sizeof(iv), aad, sizeof(aad), pt, sizeof(pt));

    std::cout << "Plain: ";
    StringSource(pt, sizeof(pt), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << "\n" << std::endl;

    std::cout << "Cipher: ";
    StringSource(ct, sizeof(ct), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << std::endl;

    std::cout << "MAC: ";
    StringSource(mac, sizeof(mac), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << "\n" << std::endl;

    ChaCha20Poly1305::Decryption dec;
    dec.SetKeyWithIV(key, sizeof(key), iv, sizeof(iv));
    dec.DecryptAndVerify(rt, mac, sizeof(mac), iv, sizeof(iv), aad, sizeof(aad), ct, sizeof(ct));

    std::cout << "Recover: ";
    StringSource(rt, sizeof(rt), true, new HexEncoder(new FileSink(std::cout)));
    std::cout << "\n" << std::endl;

    return 0;
}

Running the code results in the following output.

$ ./test.exe
Plain: 4C616469657320616E642047656E746C656D656E206F662074686520636C617373206F662
02739393A204966204920636F756C64206F6666657220796F75206F6E6C79206F6E6520746970206
66F7220746865206675747572652C2073756E73637265656E20776F756C642062652069742E

Cipher: D31A8D34648E60DB7B86AFBC53EF7EC2A4ADED51296E08FEA9E2B5A736EE62D63DBEA45E
8CA9671282FAFB69DA92728B1A71DE0A9E060B2905D6A5B67ECD3B3692DDBD7F2D778B8C9803AEE3
28091B58FAB324E4FAD675945585808B4831D7BC3FF4DEF08E4B7A9DE576D26586CEC64B6116
MAC: 1AE10B594F09E26A7E902ECBD0600691

Recover: 4C616469657320616E642047656E746C656D656E206F662074686520636C617373206F6
6202739393A204966204920636F756C64206F6666657220796F75206F6E6C79206F6E65207469702
0666F7220746865206675747572652C2073756E73637265656E20776F756C642062652069742E

Benchmarks

ChaCha20Poly1305 benchmarks are respectable, and will likely exceed exceed non-AES accelerated authentication encryption modes of operations. The comparison below is from a Skylake Core-i5 6400 @ 2.7 GHz. AES and polynomial multiply are accelerated with AES-NI, and ChaCha is accelerated with AVX.

Algorithm MB/s Cpb
AES/GCM 2855 0.90
AES/CCM 847 3.04
AES/EAX 844 3.05
ChaCha20/Poly1305 659 3.91

As another example, here are the results from a PowerMac G5 @ 2.0 GHz. AES and polynomial multiply are not accelerated, and ChaCha is accelerated with Altivec.

Algorithm MB/s Cpb
AES/GCM 43 44.3
AES/CCM 28 68.5
AES/EAX 28 68.5
ChaCha20/Poly1305 80 23.87

As a final example, here are the results from a LeMake HiKey ARMv8 @ 1.2 GHz. AES and polynomial multiply are accelerated with crypto extensions, and ChaCha is accelerated with ASIMD (NEON).

Algorithm MB/s Cpb
AES/GCM 291 3.93
AES/CCM 169 6.77
AES/EAX 166 6.88
ChaCha20/Poly1305 96 11.98