XChaCha20Poly1305

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

XChaCha20Poly1305 is a modified version of ChaCha20Poly1305 created by Scott Arciszewski that is hardened against nonce misuse. Crypto++ provides the algorithm by way of the XChaCha20Poly1305 class. XChaCha20Poly1305 only offers a 32-byte key with a 24-byte nonce and 20 rounds. Also see Issue 727, XChaCha20 support and draft-arciszewski-xchacha, XChaCha: eXtended-nonce ChaCha and AEAD_XChaCha20_Poly1305.

Crypto++ added XChaCha20Poly1305 to the library version 8.1. Also see Issue 724 and Commit 76b47204dfc4.

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 ChaCha20Poly1305.

Example

The example below uses a test vector from the Botan library.

#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[] = {
        0x40,0x41,0x42,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,
        0x50,0x51,0x52,0x53,0x54,0x55,0x56,0x58
    };

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

    XChaCha20Poly1305::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;

    XChaCha20Poly1305::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: 65032F227E672AAF3D102E073E4F1C386ABAB35EE19A50DEB91A4AE3AEB7C687BF89E39F
C459675DEB350C69BB5B7B372BEA9B0561A5046C7AEEDCD2456FAA4A301A137209D61DA415499A44
CBF6D867E019831B89BEA709A3A706026C910C523F814911E77B826AF350A9FD2A93097FA84B
MAC: 7EA2404B69A2A6182BE9992CE53701BF

Recover: 4C616469657320616E642047656E746C656D656E206F662074686520636C617373206F6
6202739393A204966204920636F756C64206F6666657220796F75206F6E6C79206F6E65207469702
0666F7220746865206675747572652C2073756E73637265656E20776F756C642062652069742E

Benchmarks

XChaCha20Poly1305 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
XChaCha20/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
XChaCha20/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
XChaCha20/Poly1305 96 11.98