CryptoSys Home > API > Using the AEAD functions for authenticated encryption with additional data

Using the AEAD functions for authenticated encryption with additional data


This page expands on the details about using authenticated encryption in the manual and gives some examples.

Introduction | One-off authenticated encryption | One-off authenticated decryption | Incremental mode | Contact us

Introduction

Authenticated Encryption with Associated Data (AEAD) provides both authenticated encryption (confidentiality and authentication) and the ability to check the integrity and authentication of additional authenticated data (AAD) that is sent in the clear.

RFC5116 An Interface and Algorithms for Authenticated Encryption defines algorithms for Authenticated Encryption with Associated Data (AEAD) and defines a uniform interface for such algorithms. CryptoSys API v5.1 provides the algorithms AEAD_AES_128_GCM, AEAD_AES_256_GCM and AEAD_CHACHA20_POLY1305.

One-off authenticated encryption

Authenticated encryption: There are four inputs for authenticated encryption: the secret key, a nonce (sometimes called the initialization vector (IV)), the plaintext itself, and optional additional authentication data (AAD). The nonce and AAD are passed in the clear. There are two outputs: the ciphertext, which is exactly the same length as the plaintext, and an authentication tag (the "tag").

† Some authors use the term "initialization vector" (IV) to mean the entire nonce value. Others use "IV" to mean a unique variable part to be combined with a "fixed" part to make the full nonce value. If we use the term "IV" in relation to AEAD we mean the full 12-octet nonce value.

The Aead.Encrypt method is unusual in the library in that it outputs two values: the ciphertext itself is returned as a byte array, and the tag is passed by reference using the C# "out" keyword.

In this example, we encrypyt a 54-byte packet using GCM-AES-128. The test vectors are decoded from hex to byte arrays.

// IEEE P802.1 MACsec 2.4.1 54-byte Packet Encryption Using GCM-AES-128
byte[] pt, ct, key, nonce, tag, aad;
key = Cnv.FromHex("071b113b 0ca743fe cccf3d05 1f737382");
nonce = Cnv.FromHex("f0761e8d cd3d0001 76d457ed");
aad = Cnv.FromHex("e20106d7 cd0df076 1e8dcd3d 88e54c2a 76d457ed");
pt = Cnv.FromHex("08000f10 11121314 15161718 191a1b1c 1d1e1f20 21222324 25262728 292a2b2c 2d2e2f30 31323334 0004");
tag = new byte[0];    // Do this to avoid "before it has been assigned a value" error
ct = Aead.Encrypt(out tag, pt, key, nonce, aad, Aead.Algorithm.Aes_128_Gcm);
Console.WriteLine("C: " + Cnv.ToHex(ct));
Console.WriteLine("T: " + Cnv.ToHex(tag));
C: 13B4C72B389DC5018E72A171DD85A5D3752274D3A019FBCAED09A425CD9B2E1C9B72EEE7C9DE7D52B3F3
T: D6A5284F4A6D3FE22A5D6C2B960494C3

Another example showing ChaCha20-Poly1305 used in an IKEv2 packet.

// ipsecme-chacha20-poly1305 Appendix B IKEv2 Example
byte[] pt, ct, key, nonce, tag, aad;
key = Cnv.FromHex("808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9f");
nonce = Cnv.FromHex("a0 a1 a2 a3 10 11 12 13 14 15 16 17");
aad = Cnv.FromHex("c0c1c2c3c4c5c6c7d0d1d2d3d4d5d6d72e202500000000090000004529000029");
pt = Cnv.FromHex("00 00 00 0c 00 00 40 01 00 00 00 0a 00");
tag = new byte[0]; 
ct = Aead.Encrypt(out tag, pt, key, nonce, aad, Aead.Algorithm.Chacha20_Poly1305);
Console.WriteLine("C: " + Cnv.ToHex(ct));
Console.WriteLine("T: " + Cnv.ToHex(tag));
C: 610394701F8D017F7C12924889
T: 6B71BFE25236EFD7CDC67066906315B2

VBA/VB6: For examples of authenticated encryption using VBA/VB6, see AEAD_Encrypt.

One-off authenticated decryption

Authenticated decryption: The authenticated decryption operation has five inputs: the secret key, a nonce, the ciphertext itself, the additional authentication data (AAD), which can be zero-length; and the tag. The nonce and AAD are passed in the clear. There is one output: the plaintext, which is exactly the same length as the plaintext. If the inputs are not authentic, the function returns a "FAIL" error code.

The Aead.Decrypt method either returns the decrypted plaintext as a byte array or an empty array if there was an error. Call General.ErrorCode() immediately afterwards to check the actual error: AUTH_FAIL_ERROR indicates the authentication failed.

// RFC7739 ChaCha20_Poly1305 Appendix A.5
byte[] pt, ct, key, nonce, tag, aad;
key = Cnv.FromHex("1c9240a5eb55d38af333888604f6b5f0473917c1402b80099dca5cbc207075c0");
ct = Cnv.FromHex("64a0861575861af460f062c79be643bd5e805cfd345cf389f108670ac76c8cb24c6cfc18755d43eea09ee94e382d26b0bdb7b73c321b0100d4f03b7f355894cf332f830e710b97ce98c8a84abd0b948114ad176e008d33bd60f982b1ff37c8559797a06ef4f0ef61c186324e2b3506383606907b6a7c02b0f9f6157b53c867e4b9166c767b804d46a59b5216cde7a4e99040c5a40433225ee282a1b0a06c523eaf4534d7f83fa1155b0047718cbc546a0d072b04b3564eea1b422273f548271a0bb2316053fa76991955ebd63159434ecebb4e466dae5a1073a6727627097a1049e617d91d361094fa68f0ff77987130305beaba2eda04df997b714d6c6f2c29a6ad5cb4022b02709b");
nonce = Cnv.FromHex("000000000102030405060708");
aad = Cnv.FromHex("f33388860000000000004e91");
tag = Cnv.FromHex("eead9d67890cbb22392336fea1851f38");
pt = Aead.Decrypt(ct, key, nonce, aad, tag, Aead.Algorithm.Chacha20_Poly1305);
Console.WriteLine("P:" + Cnv.ToHex(pt));
// This is UTF-8-encoded text, so display it
string Str = System.Text.Encoding.UTF8.GetString(pt);
Console.WriteLine(Str);
P:496E7465726E65742D4472616674732061726520647261667420646F63756D656E74732076616C
696420666F722061206D6178696D756D206F6620736978206D6F6E74687320616E64206D61792062
6520757064617465642C207265706C616365642C206F72206F62736F6C65746564206279206F7468
657220646F63756D656E747320617420616E792074696D652E20497420697320696E617070726F70
726961746520746F2075736520496E7465726E65742D447261667473206173207265666572656E63
65206D6174657269616C206F7220746F2063697465207468656D206F74686572207468616E206173
202FE2809C776F726B20696E2070726F67726573732E2FE2809D
Internet-Drafts are draft documents valid for a maximum of six months and may be
 updated, replaced, or obsoleted by other documents at any time. It is inappropr
iate to use Internet-Drafts as reference material or to cite them other than as
/"work in progress./"

An example that fails to authenticate

// NIST GCMVS CAVS 14.0 GCM Decrypt with keysize 256 = FAIL
byte[] pt, ct, key, nonce, tag, aad;
key = Cnv.FromHex("9a0343f850a6427120f764789ffec6d237447b898fbf51d2182f065d3861497d");
ct = Cnv.FromHex("e93165935ac18e3a2845d15fe31a9286");
nonce = Cnv.FromHex("3deef6f453dd70d92143adcd");
aad = Cnv.FromHex("dbb8226a624520863db6897017b2a4f8");
tag = Cnv.FromHex("f5fc50d18766bc3d9e16dd136d45816b");
pt = Aead.Decrypt(ct, key, nonce, aad, tag, Aead.Algorithm.Chacha20_Poly1305);
Console.WriteLine("P='" + Cnv.ToHex(pt) + "'");
// Get last error
int n = General.ErrorCode();
Console.WriteLine("ErrorCode=" + n + ": " + General.ErrorLookup(n));
P=''
ErrorCode=40: Authentication failed (AUTH_FAIL_ERROR)

VBA/VB6: For examples of authenticated decryption using VBA/VB6, see AEAD_Decrypt.

Incremental mode

If you have a large amount of data to process, use the incremental mode and pass the data in chunks. The correct sequence is shown the diagram opposite and in the pseudocode below. Calling a function out of sequence returns a MISUSE_ERROR.

Correct sequence for AEAD incremental functions

* denotes zero or more repeats.

Incremental encryption pseudocode

InitKey(key)
{ For each packet do:
   SetNonce(nonce)
   { AddAAD(aad-chunk) }*
   StartEncrypt()
   { ciphertext-chunk <-- Update(plaintext-chunk) }*
   tag <-- FinishEncrypt()
}*
Destroy()

Incremental decryption pseudocode

InitKey(key)
{ For each packet do:
   SetNonce(nonce)
   { AddAAD(aad-chunk) }*
   StartDecrypt(tag-to-check)
   { plaintext-chunk <-- Update(ciphertext-chunk) }*
   (OK|FAIL) <-- FinishDecrypt()
}*
Destroy()

Notes on incremental mode:

A trivial example showing the principles.

byte[] ct, key, nonce, tag;
byte[] aad1, aad2;
byte[] pt1, pt2, pt3;
key = Cnv.FromHex("071b113b 0ca743fe cccf3d05 1f737382");
nonce = Cnv.FromHex("f0761e8d cd3d0001 76d457ed");
aad1 = Cnv.FromHex("e20106d7 cd0df076");
aad2 = Cnv.FromHex("1e8dcd3d 88e54c2a 76d457ed");
pt1 = Cnv.FromHex("08000f10 11121314 15161718 191a1b1c");
pt2 = Cnv.FromHex("1d1e1f20 21222324 25262728 292a2b2c 2d2e2f30 31323334");
pt3 = Cnv.FromHex("0004");

Aead oAead = Aead.Instance();
oAead.InitKey(key, Aead.Algorithm.Aes_128_Gcm);
oAead.SetNonce(nonce);
oAead.AddAAD(aad1);
oAead.AddAAD(aad2);
oAead.StartEncrypt();
ct = oAead.Update(pt1);
Console.WriteLine("CT-chunk: " + Cnv.ToHex(ct));
ct = oAead.Update(pt2);
Console.WriteLine("CT-chunk: " + Cnv.ToHex(ct));
ct = oAead.Update(pt3);
Console.WriteLine("CT-chunk: " + Cnv.ToHex(ct));
tag = oAead.FinishEncrypt();
Console.WriteLine("T: " + Cnv.ToHex(tag));
oAead.Dispose();
CT-chunk: 13B4C72B389DC5018E72A171DD85A5D3
CT-chunk: 752274D3A019FBCAED09A425CD9B2E1C9B72EEE7C9DE7D52
CT-chunk: B3F3
T: D6A5284F4A6D3FE22A5D6C2B960494C3

Encrypting the maximum number of plaintext bytes (P_MAX) using ChaCha20-Poly1305 (this may take some time!). Note that the value of P_MAX in RFC5116 is wrong.

// Use ChaCha20-Poly1305 to encrypt 274877906880 repetitions of the zero byte (0x00)
const long p_max = 274877906880;  // 2^38 - 64
const int BLOCK_LEN = 1024 * 1024;
long nblocks = p_max / BLOCK_LEN;
long nodd = p_max - (nblocks * BLOCK_LEN);
long totlen = 0;
Console.WriteLine("About to process {0} bytes", p_max);
Console.WriteLine(" as {0} blocks of {1} bytes + {2} odd bytes...", 
    nblocks, BLOCK_LEN, nodd);

byte[] key, nonce, tag, aad;
key = Cnv.FromHex("808182838485868788898A8B8C8D8E8F909192939495969798999A9B9C9D9E9F");
nonce = Cnv.FromHex("07000000deadbeefcafe0001");
aad = Cnv.FromHex("50515253C0C1C2C3C4C5C6C7");
byte[] inblock = new byte[BLOCK_LEN];
byte[] outblock = new byte[0];

Aead oAead = Aead.Instance();
oAead.InitKey(key, Aead.Algorithm.Chacha20_Poly1305);
oAead.SetNonce(nonce);
oAead.AddAAD(aad);
oAead.StartEncrypt();
// Process the P_MAX bytes in chunks
for (long ib = 0; ib < nblocks; ib++)
{
    outblock = oAead.Update(inblock);
    totlen += BLOCK_LEN;
    if (ib % 1024 == 0) Console.Write(".");
}
if (nodd > 0)
{
    byte[] lastblock = new byte[(int)nodd];
    outblock = oAead.Update(lastblock);
    totlen += nodd;
}
tag = oAead.FinishEncrypt();

Console.WriteLine("\nProcessed {0} bytes", totlen);
Console.WriteLine("Tag=" + Cnv.ToHex(tag));
// Get the final 64 bytes of ciphertext
byte[] final64 = new byte[64];
Console.WriteLine("last block length=" + outblock.Length);
Array.Copy(outblock, outblock.Length - 64, final64, 0, 64);
Console.WriteLine("Last 64 bytes in ciphertext:");
Console.WriteLine("C[{0}..{1}]:", p_max - 64, p_max - 1);
Console.WriteLine(Cnv.ToHex(final64));
About to process 274877906880 bytes
 as 262143 blocks of 1048576 bytes + 1048512 odd bytes...
................................................................................
................................................................................
................................................................................
................
Processed 274877906880 bytes
Tag=66AC036904B31ADA3920F244F5F45C13
last block length=1048512
Last 64 bytes in ciphertext:
C[274877906816..274877906879]:
67A6B44108D6CA9B8151E9343C979B162F03FE0FC149CB9F2D30D6AB9DBACC9D9FFA326A48AA34A5
861E1B790207C31BDAC7EECC164E9FE413CF78571EE9DE7F

VBA/VB6: For examples of authenticated encryption in incremental mode using VBA/VB6, see AEAD_InitKey.

Contact

For more information, please send us a message.

This page last updated 17 June 2020

[Go to top]