Skip to content

ChaCha20

Peter edited this page May 7, 2020 · 24 revisions

Introduction

It is a refinement of the Salsa20 algorithm, and uses a 256-bit key. I do recommend it as a more secure variant over Salsa20.

Parameters

  • Key - secret key/passphrase using 256-bit

🔑 Secret field. The original algorithm also specified 128-bit keys.

  • Nonce - a unique non-repeating number

📢 Public field. Bitness depends on the cipher variant. Does not need to be random, can be sequential.

  • Counter - identifies a block to encipher/decipher (random access)

📢 Public field. Bitness depends on the cipher variant.

  • Rounds - tradeoff between security and speed

📢 Public field. (recommended tradeoffs: 8 = speed, 12 = balanced, 20 = security). Implementation can handle 2^32.

Variants

Currently there are three variants of ChaCha20 (as of 2020)

  • ChaCha20 (256-bit key, 64-bit nonce, 64-bit counter)

    🛈 First original version released by D. J. Bernstein (implemented)

  • IETF ChaCha20 (256-bit key, 96-bit nonce, 32-bit counter)

    🛈 IETF slightly tweaked version as RFC 7539 for networking (implemented)

  • XChaCha20 (256-bit key, 192-bit nonce, 64-bit counter)

    🛈 Another version released after original by D. J. Bernstein

Usage

Encryption (ChaCha20 using RFC 7539)

// Multiple ways to initialize ChaCha.Create("ChaCha12") or new ChaCha8
using(var chacha = SymmetricAlgorithm.Create("ChaCha20Rfc7539"))
{
 using(var ms = new MemoryStream())
 using(var cse = new CryptoStream(ms, chacha.CreateEncryptor(myKey, myNonce), CryptoStreamMode.Write))
 {
   byte[] data = Encoding.UTF8.GetBytes("SecretMessage");
   cse.Write(data, 0, data.Length);
   
   // Make sure to flush any remaining buffer content
   // alternatively call Close()
   if(!cse.HasFlushedFinalBlock)
      cse.FlushFinalBlock();
   
   byte[] encryptedData = ms.ToArray();
 }
}

Decryption (ChaCha20 original paper)

using(var chacha = new ChaCha20())
{
 using(var ms = new MemoryStream())
 using(var csd = new CryptoStream(ms, chacha.CreateDecryptor(myKey, myNonce), CryptoStreamMode.Read))
 {
   byte[] encryptedData = ...;
   csd.Read(encryptedData, 0, encryptedData.Length);
   
   // Make sure to flush any remaining buffer content
   // alternatively call Close()
   if(!csd.HasFlushedFinalBlock)
      csd.FlushFinalBlock();
   
   byte[] originalData = ms.ToArray();
 }
}

Multiple ways to initialize

// Creating object directly
ChaCha8 chacha = new ChaCha8();
// Calling base class Create method (only chacha family)
ChaCha chacha = ChaCha.Create("ChaCha12");
// Using SymmetricAlgorithm.Create (any symmetric alg.)
SymmetricAlgorithm chacha = SymmetricAlgorithm.Create("ChaCha8Rfc7539");

Safety

  1. Always try to use non-predictable randomly chosen key to improve security
  2. Remember that (key, nonce) pair must be unique; same nonce cannot be reused with same key and vice versa
  3. The nonce is short and thus generating it randomly can create possible collisions. It is recommended to increment the previous nonce instead of generating a random nonce every time a new stream is required. (general rule: 128-bit numbers and higher have very low to non-existent collision chance. Example)
  4. A single given pair of a (key, nonce) allows to safely de/encrypt only up to 256GB and 1ZiB (2^70 bytes) for IETF ChaCha20 and Original ChaCha20 respectively.

Considerations

  • Salsa20 may be slightly faster on some platforms due to different core function.
  • Encrypted ciphertext has same length as plaintext; (use it as advantage to pre-allocate buffers in advance, some other algorithms may require you padding the output)

Clone this wiki locally