using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ExtensionMethods;
using System.Text.RegularExpressions;
namespace CryptanalysisCore
{
public class Vigenere : Cipher
{
public const int KeyLengthAttack = 1;
public const int BruteForce = 0;
public const int MaxKeyLength = 20;
private Random random = new Random();
public Vigenere()
: base()
{
ExceptionText = "Klíč musí být dlouhý alespoň dva znaky a musí být sloužen pouze z malých písmen anglické abecedy.";
}
public override string Decrypt(string ciphertext, string key)
{
base.Decrypt(ciphertext, key);
char[] opentext = new char[ciphertext.Length];
int keyIndex = 0;
for (int i = 0; i < opentext.Length; i++)
{
char currCipherChar = ciphertext[i];
if (currCipherChar == ' ')
{
opentext[i] = ' ';
}
else
{
opentext[i] = Analyse.MoveBackCharacter(ciphertext[i], key[keyIndex % key.Length]);
keyIndex++;
}
}
return new string(opentext).ToLower();
}
protected override bool IsKeyValid(string key)
{
return key.Length >= 2 && Regex.IsMatch(key, @"^[a-z\?]+$");
}
public override string Encrypt(string opentext, string key)
{
base.Encrypt(opentext, key);
char[] ciphertext = new char[opentext.Length];
int opentextIndex = 0;
for (int i = 0; i < ciphertext.Length; i++)
{
char currOpenChar = opentext[i];
if (currOpenChar == ' ')
{
ciphertext[i] = ' ';
}
else
{
ciphertext[i] = Analyse.MoveCharacter(opentext[i], key[opentextIndex % key.Length]);
opentextIndex++;
}
}
return new string(ciphertext).ToUpper();
}
public override string RandomKey()
{
int length = random.Next(5, MaxKeyLength);
return Text.RandomString(length);
}
protected override void SetCrackMethods()
{
CrackMethods = new CrackMethod[] { BruteForceAttack, TestKeyLengthAttack };
}
///
/// Crackovací metoda, která se nejprve pokusí zjistit délku klíče
/// a následně se pokusí prolomit šifru standardně.
///
///
///
///
private List TestKeyLengthAttack(string ciphertext, Storage.Languages language)
{
KeyLength keyLength = new KeyLength();
var keysLength = keyLength.GetKeyLength(ciphertext).Take(6).ToArray();
if (keysLength.Length == 0)
keysLength = new int[MaxKeyLength - 2].Fill(x => x + 2);
return BruteForceAttack(ciphertext, language, keysLength);
}
private List BruteForceAttack(string ciphertext, Storage.Languages language, int[] keysLength)
{
ciphertext = Analyse.NormalizeText(ciphertext, Analyse.TextTypes.WithoutSpacesLower);
Caesar caesar = new Caesar();
Dictionary crackedKeys = new Dictionary();
foreach(int keyLength in keysLength)
{
char[][] splitedChars = new char[keyLength][];
for (int i = 0; i < keyLength; i++)
splitedChars[i] = GetNthChars(ciphertext, keyLength, i);
char[] crackKeys = new char[keyLength];
for (int i = 0; i < keyLength; i++)
{
try
{
crackKeys[i] = caesar.Crack(Analyse.NormalizeText(new string(splitedChars[i]), Analyse.TextTypes.WithoutSpacesUpper), Caesar.TriangleID, language).First()[0];
}
catch (Exceptions.MatchNotFound)
{
crackKeys[i] = '?';
}
}
crackedKeys[keyLength] = new string(crackKeys);
}
var possKeys = crackedKeys.Where(x => x.Value.Length > 0).Select(x => x.Value).ToArray();
Dictionary simIndexes = new Dictionary();
for (int i = 0; i < possKeys.Length; i++)
{
string openttext = Decrypt(ciphertext, possKeys[i]);
double simIndex = Analyse.SimilarityIndex(openttext, Storage.GetLangChar(language));
simIndexes[possKeys[i]] = simIndex;
}
var result = simIndexes.OrderBy(x => x.Value).Select(x => x.Key).ToList();
return result;
}
private List BruteForceAttack(string ciphertext, Storage.Languages language)
{
int[] keysLength = new int[MaxKeyLength - 2].Fill(x => x + 2);
return BruteForceAttack(ciphertext, language, keysLength);
}
/************* BRUTE FORCE ATTACK **************/
private char[] GetNthChars(string ciphertext, int n, int move)
{
if (ciphertext.Length < n)
return new char[0];
int length = (int)Math.Ceiling((double)(ciphertext.Length - move) / (double)n);
char[] chars = new char[length];
int count = 0;
for (int i = 0; i < ciphertext.Length - move; i += n)
chars[count++] = ciphertext[i + move];
return chars;
}
/******************* KONEC *********************/
public int[] GetKey(string ciphertext)
{
KeyLength keyLength = new KeyLength();
return keyLength.GetKeyLength(ciphertext).Where(x => x < 30).Where(x => x > 4).Take(1).ToArray();
}
public override string ToString()
{
return "Vigenere";
}
}
}