The Chacha20 Cipher is a stream cipher which uses a 256-bit key and a 64-bit nonce [paper]. Currently AES has a virtual monopoly on secret key encryption. There would be major problems, though, if this was cracked. Along with this AES has been shown to be weak around cache-collision attacks. Google thus propose ChaCha20 as an alternative, and actively use it within TLS connections. Currently it is three times faster than software-enabled AES, and is not sensitive to timing attacks. It operates by creating a key stream which is then X-ORed with the plaintext. It has been standardised with RFC 7539: [Download code].
ChaCha20Poly1305 with Bouncy Castle and C# |
Coding
First we create a folder named "bc_chacha", and then go into that folder.We can create a Dotnet console project for .NET 8.0 with:
dotnet new console --framework net8.0
This produces a Csproject file of:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> </Project>
We then add the latest Bouncy Castle library:
dotnet add package BouncyCastle.Cryptography --version 2.2.1
The following is the coding:
namespace ChaCha20 { using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Engines; using Org.BouncyCastle.Crypto.Modes; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; class Program { static void Main(string[] args) { var msg="Hello"; var add=""; var iv="000102030405060708090A0B"; var size=256; if (args.Length >0) msg=args[0]; if (args.Length >1) add=args[1]; if (args.Length >2) iv=args[2]; if (args.Length >3) size=Convert.ToInt32(args[3]); try { var plainTextData=System.Text.Encoding.UTF8.GetBytes(msg); ChaCha20Poly1305 cipher = new ChaCha20Poly1305(); int macSize = 128; byte[] nonce = new byte[12]; Array.Copy(Convert.FromHexString(iv), nonce, 12); byte[] associatedText =System.Text.Encoding.UTF8.GetBytes(add); CipherKeyGenerator keyGen = new CipherKeyGenerator(); keyGen.Init(new KeyGenerationParameters(new SecureRandom(), size)); KeyParameter keyParam = keyGen.GenerateKeyParameter(); AeadParameters keyParamAead = new AeadParameters(keyParam, macSize, nonce, associatedText); cipher.Init(true,keyParamAead); int outputSize = cipher.GetOutputSize(plainTextData.Length); byte[] cipherTextData = new byte[outputSize]; int result = cipher.ProcessBytes(plainTextData, 0, plainTextData.Length, cipherTextData, 0); cipher.DoFinal(cipherTextData, result); var rtn = cipherTextData; // Decrypt cipher.Init(false,keyParamAead); outputSize = cipher.GetOutputSize(cipherTextData.Length); plainTextData = new byte[outputSize]; result = cipher.ProcessBytes(cipherTextData, 0, cipherTextData.Length,plainTextData, 0); cipher.DoFinal(plainTextData, result); var pln=plainTextData; Console.WriteLine("=== ChaCha20 Cipher =="); Console.WriteLine("Message:\t\t{0}",msg); Console.WriteLine("IV:\t\t\t{0}",iv); Console.WriteLine("Key:\t\t\t{0} [{1}]",Convert.ToHexString(keyParam.GetKey()),Convert.ToBase64String(keyParam.GetKey())); Console.WriteLine("Additional data:\t{0}",add); Console.WriteLine("\nCipher (hex):\t\t{0}",Convert.ToHexString(rtn)); Console.WriteLine("Cipher (Base64):\t{0}",Convert.ToBase64String(rtn)); Console.WriteLine("\nPlain:\t\t\t{0}",System.Text.Encoding.UTF8.GetString(pln)); } catch (Exception e) { Console.WriteLine("Error: {0}",e.Message); } } } }
A sample run is:
=== ChaCha20 Cipher == Message: Hello 123 IV: 000102030405060708090A0B Key: BDA76FC850EB20F69B43BF914E7424C73EE70B1602F42CC7BC06274C36E0955E [vadvyFDrIPabQ7+RTnQkxz7nCxYC9CzHvAYnTDbglV4=] Additional data: test Cipher (hex): CF17501A29BE70346F399B51875967CEA7AD3EF8A984899EC1 Cipher (Base64): zxdQGim+cDRvOZtRh1lnzqetPviphImewQ== Plain: Hello 123
Notice that the cipher stream is the same length as the plaintext stream.