using System.Security.Cryptography;
Console.Write("Enter pass phrase (max 32 characters): ");
string passPhrase = Console.ReadLine() ?? "";
byte[] keyBytes = new byte[32];
Array.Copy(Encoding.UTF8.GetBytes(passPhrase), keyBytes, Math.Min(32, passPhrase.Length));
Console.Write("Enter ASCA message to process, enter 'quit' to exit: ");
string plainText = Console.ReadLine() ?? "";
if (plainText.ToUpper() == "QUIT" || plainText.ToUpper() == "'QUIT'") break;
var index = plainText.IndexOf("C:");
Console.Write("Invalid ASCA message entered.");
index = plainText.IndexOf(";", index + 2) + 1;
if (index == plainText.Length || plainText.Substring(index).StartsWith("OT:"))
associatedText = plainText;
index = plainText.IndexOf(";", index + 1) + 1;
associatedText = plainText.Substring(0, index);
plainText = plainText.Substring(index);
Console.WriteLine("------- Sender stuff -------");
byte[] senderNonce = RandomNumberGenerator.GetBytes(12);
byte[] ciphertext = Encrypt(plainText, keyBytes, senderNonce, associatedText, out byte[] authenticationTag);
WriteLine("Header & LMEX: ", associatedText.Substring(0, 77));
int associatedDataLength = associatedText.Length - 77;
WriteLine("Remaining Associated Data Length: ", associatedDataLength.ToString());
if (associatedDataLength > 0)
WriteLine("Associated Data: ", associatedText.Substring(77, associatedDataLength));
WriteLine("Authentication Tag: ", Convert.ToBase64String(authenticationTag));
WriteLine("Nonce: ", Convert.ToBase64String(senderNonce));
WriteLine("Cipher text: ", Convert.ToBase64String(ciphertext));
string manipulation = AskForManipulation();
switch (manipulation.Substring(0, 1).ToUpperInvariant())
associatedText = "X" + associatedText.Substring(1);
WriteLine("Manipulated Associated Data: ", associatedText);
ciphertext[0] = Encoding.UTF8.GetBytes("X")[0];
WriteLine("Manipulated Ciphertext: ", Convert.ToBase64String(ciphertext));
authenticationTag[0] = Encoding.UTF8.GetBytes("X")[0];
WriteLine("Manipulated AuthenticationTag: ", Convert.ToBase64String(authenticationTag));
byte[] messageToSend = Concat(Encoding.UTF8.GetBytes((associatedText.Substring(0, 77))),
Concat(BitConverter.GetBytes((ushort)associatedDataLength),
Concat(Encoding.UTF8.GetBytes(associatedDataLength > 0 ? associatedText.Substring(77, associatedDataLength) : ""),
Concat(authenticationTag,
WriteLine("Message to send: ", Convert.ToBase64String(messageToSend));
Console.WriteLine("------- Receiver stuff -------");
byte[] messageReceived = messageToSend;
var receivedHeaderLmex = SubArray(messageReceived, 0, 77);
WriteLine("Guard could inspect Header + LMEX: ", Encoding.UTF8.GetString(receivedHeaderLmex));
byte[] receivedAssociatedDataLength = SubArray(messageReceived, 77, 2);
ushort length = BitConverter.ToUInt16(receivedAssociatedDataLength);
var receivedAssociatedData = Concat(receivedHeaderLmex, SubArray(messageReceived, 77 + 2, associatedDataLength));
byte[] receivedAuthenticationTag = SubArray(messageReceived, 77 + 2 + associatedDataLength, 16);
byte[] receiverNonce = SubArray(messageReceived, 77 + 2 + associatedDataLength + 16, 12);
var receivedCipher = SubArray(messageReceived, 77 + 2 + associatedDataLength + 16 + 12, messageReceived.Length - (77 + 2 + associatedDataLength + 16 + 12));
WriteLine("Received Associated Data: ", Encoding.UTF8.GetString(receivedAssociatedData));
WriteLine("Received Authentication Tag: ", Convert.ToBase64String(receivedAuthenticationTag));
WriteLine("Received Nonce: ", Convert.ToBase64String(receiverNonce));
WriteLine("Received Cipher Text: ", Convert.ToBase64String(receivedCipher));
if (Decrypt(receivedCipher, keyBytes, receiverNonce, receivedAssociatedData, receivedAuthenticationTag, out string decryptedText))
WriteLine("Decrypted text: ", decryptedText);
associatedText = Encoding.UTF8.GetString(receivedAssociatedData);
WriteLine("Received ASCA message: ", associatedText + decryptedText);
private static string AskForManipulation()
while (!result.StartsWith("N")
&& !result.StartsWith("A")
&& !result.StartsWith("T")
&& !result.StartsWith("C"))
Console.Write("Manipulated the message? Use ");
Console.Write("(dditional data), ");
Console.Write("(yphered text) or ");
result = Console.ReadLine().ToUpperInvariant() ?? "";
static byte[] Encrypt(string plaintext, byte[] keyBytes, byte[] nonce, string associatedText, out byte[] authenticationTag)
byte[] plaintextBytes = Encoding.UTF8.GetBytes(plaintext);
byte[] associatedData = Encoding.UTF8.GetBytes(associatedText);
using (var chacha20 = new ChaCha20Poly1305(keyBytes))
encryptedText = new byte[plaintextBytes.Length];
authenticationTag = new byte[16];
chacha20.Encrypt(nonce, plaintextBytes, encryptedText, authenticationTag, associatedData);
static bool Decrypt(byte[] ciphertext, byte[] keyBytes, byte[] nonce, byte[] associatedData, byte[] authenticationTag, out string decryptedText)
byte[] decryptBytes = new byte[ciphertext.Length];
using (var chacha20 = new ChaCha20Poly1305(keyBytes))
chacha20.Decrypt(nonce, ciphertext, authenticationTag, decryptBytes, associatedData);
Console.WriteLine("Error: " + ex.Message);
decryptedText = Encoding.UTF8.GetString(decryptBytes);
static byte[] Concat(byte[] a, byte[] b)
byte[] output = new byte[a.Length + b.Length];
for (int i = 0; i < a.Length; i++)
for (int j = 0; j < b.Length; j++)
output[a.Length + j] = b[j];
static byte[] SubArray(byte[] data, int start, int length)
byte[] result = new byte[length];
Array.Copy(data, start, result, 0, length);
static void WriteLine(string label, string value)
Console.WriteLine(value);