Skip to content

Instantly share code, notes, and snippets.

@sblmnl
Last active July 19, 2022 10:12
Show Gist options
  • Save sblmnl/222c786ddebd00e2dbae7ab361fb2618 to your computer and use it in GitHub Desktop.
Save sblmnl/222c786ddebd00e2dbae7ab361fb2618 to your computer and use it in GitHub Desktop.
Known plaintext attack against the Chug encryption algorithm
using ChugSharp;
using ChugSharp.PaddingAlgorithms;
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
struct Permutation
{
public byte Key { get; set; }
public byte[] Data { get; set; }
public static Permutation Create(byte[] ciphertext, byte[] plaintext, byte initialN)
{
int key = (ciphertext[0] - initialN) % 256;
return new Permutation()
{
Key = key < 0
? (byte)(256 + key)
: (byte)key,
Data = plaintext
};
}
}
static class Extensions
{
public static IEnumerable<Permutation> DetectKnownPlaintext(this IEnumerable<Permutation> permutations, byte[] knownPlaintext)
{
bool Contains(byte[] haystack, byte[] needle)
{
for (int i = 0; i < haystack.Length; i++)
{
if (haystack[i] == needle[0])
{
bool match = true;
for (int j = 1; j < needle.Length; j++)
{
if (i + j >= haystack.Length || haystack[i + j] != needle[j])
{
match = false;
break;
}
}
if (match)
return true;
}
}
return false;
}
if (knownPlaintext is null)
throw new ArgumentNullException(nameof(knownPlaintext));
foreach (var permutation in permutations)
{
if (Contains(permutation.Data, knownPlaintext))
{
yield return permutation;
}
}
}
public static IEnumerable<Permutation> TryUnpadPermutations(this IEnumerable<Permutation> permutations, IChugPaddingAlgorithm padding)
{
foreach (var permutation in permutations)
{
var unpadded = new Permutation() { Key = permutation.Key };
try
{
unpadded.Data = padding.Unpad(permutation.Data);
}
catch
{
continue;
}
yield return unpadded;
}
}
}
class Program
{
static IEnumerable<Permutation> GetPermutations(byte[] ciphertext, int keySize)
{
byte[] Decrypt(int initialN)
{
if (ciphertext is null)
throw new ArgumentNullException(nameof(ciphertext));
if (ciphertext.Length == 0)
throw new ArgumentException("The ciphertext cannot be empty!");
if (keySize < 0)
throw new ArgumentOutOfRangeException(nameof(keySize));
if (initialN < 0 || initialN > 255)
throw new ArgumentOutOfRangeException(nameof(initialN));
byte[] plaintext = new byte[ciphertext.Length];
int n = initialN;
for (int i = 0; i < ciphertext.Length; i++)
{
if (i != 0 && i % keySize == 0)
n += initialN;
switch (i % 2)
{
case 0:
plaintext[i] = (byte)((ciphertext[i] + n) % 256);
break;
case 1:
plaintext[i] = (byte)((ciphertext[i] - n) % 256);
break;
}
}
return plaintext;
}
for (int i = 0; i < 256; i++)
{
yield return Permutation.Create(ciphertext, Decrypt(i), (byte)i);
}
}
static void Main()
{
// Create a random key and plaintext
byte[] key = new byte[2];
byte[] plaintext = new byte[16];
byte[] knownPlaintext = new byte[plaintext.Length / 4];
RandomNumberGenerator.Fill(key);
RandomNumberGenerator.Fill(plaintext);
for (int i = 0; i < knownPlaintext.Length; i++)
knownPlaintext[i] = plaintext[i];
// Encrypt w/ length prefixed random padding using a block size of 32 bytes
byte[] ciphertext = new Chug(true).Encrypt(plaintext, key);
Console.WriteLine("[info]");
Console.WriteLine();
Console.WriteLine("key\t: {0}", BitConverter.ToString(key));
Console.WriteLine("pt\t: {0}", BitConverter.ToString(plaintext));
Console.WriteLine("kpt\t: {0}", BitConverter.ToString(knownPlaintext));
Console.WriteLine("ct\t: {0}", BitConverter.ToString(ciphertext));
Console.WriteLine();
Console.WriteLine("[messages]");
Console.WriteLine();
// Decrypt all permutations of the ciphertext w/ the key length
// then detect the permutations that contain the known plaintext
// and attempt to unpad them.
foreach (var permutation in GetPermutations(ciphertext, key.Length)
.DetectKnownPlaintext(knownPlaintext)
.TryUnpadPermutations(new LengthPrefixedRandomPadding(32)))
{
Console.WriteLine("key: {0} - msg: {1}",
permutation.Key.ToString("X2"),
BitConverter.ToString(permutation.Data));
}
}
}
@sblmnl
Copy link
Author

sblmnl commented Oct 20, 2021

This is what a successful decryption looks like:

[info]

key     : 10-5F
pt      : 69-01-9A-F9-0F-FB-DB-5E-A8-D5-D4-0E-C4-28-9E-41
kpt     : 69-01-9A-F9
ct      : C0-59-D4-99-BF-77-1B-6E-6F-48-11-18-2B-5F-77-BE-A2-C8-84-0F-AA-60-27-12-A5-D8-82-60-23-C9-AE-31

[messages]

key: 71 - msg: 69-01-9A-F9-0F-FB-DB-5E-A8-D5-D4-0E-C4-28-9E-41

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment