Last active
November 12, 2022 23:34
-
-
Save Guiorgy/da837e4844d0006fce53800a031c5639 to your computer and use it in GitHub Desktop.
A simple implementation of the Vigenere Cipher in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// #define VERBOSE // define for debug logs | |
// By deafult the alphabet only includes Lowercase Latin letters and the space character | |
#define INCLUDE_UPPER // define to include Uppercase Latin letters | |
#define INCLUDE_NUMBERS // define to include numbers | |
#define INCLUDE_SYMBOLS // define to include other valid ASCII characters | |
// #define INCLUDE_GEORGIAN // define to include Georgian letters | |
#define IGNORE_INVALID_CHARACTERS // define to discard invalid characters without stopping the algorithm | |
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using static System.Console; | |
namespace VigenereCipher | |
{ | |
public class Program | |
{ | |
public static void Main(string[] args) | |
{ | |
const string key = "This is a key!"; | |
const string message = "A super secret message!"; | |
string ciphertext = EncryptVigenere(message, key); | |
Console.WriteLine($"Cyphertext: {ciphertext}"); | |
string decrypted = DecryptVigenere(ciphertext, key); | |
Console.WriteLine($"Decrypted: {decrypted}"); | |
} | |
#region Algorithm | |
static readonly int AlphabetLength; | |
static Dictionary<char, byte> Encoder = new Dictionary<char, byte>(); | |
static Dictionary<byte, char> Decoder = new Dictionary<byte, char>(); | |
static Program() { | |
#if VERBOSE | |
Console.WriteLine($".NET Runtime version: {typeof(string).Assembly.ImageRuntimeVersion}"); | |
Console.WriteLine(); | |
#endif | |
byte code = 0; | |
// Default | |
Encoder.Add(' ', code); | |
Decoder.Add(code++, ' '); | |
for (char c = 'a'; c <= 'z'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
// Uppercase letters | |
#if INCLUDE_UPPER | |
for (char c = 'A'; c <= 'Z'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
#endif | |
// Numbers | |
#if INCLUDE_NUMBERS | |
for (char c = '0'; c <= '9'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
#endif | |
// Other valid ASCII characters | |
#if INCLUDE_SYMBOLS | |
for (char c = '!'; c <= '/'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
for (char c = ':'; c <= '@'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
for (char c = '['; c <= '`'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
for (char c = '{'; c <= '~'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
#endif | |
// Georgian | |
#if INCLUDE_GEORGIAN | |
for (char c = 'ა'; c <= 'ჰ'; ++c) { | |
Encoder.Add(c, code); | |
Decoder.Add(code++, c); | |
} | |
#endif | |
AlphabetLength = Encoder.Count(); | |
#if VERBOSE | |
Console.WriteLine("----- Encoding -----"); | |
foreach (var e in Encoder) Console.WriteLine($"{e.Key}: {e.Value}"); | |
Console.WriteLine(); | |
Console.WriteLine("----- Decoding -----"); | |
foreach (var d in Decoder) Console.WriteLine($"{d.Key}: {d.Value}"); | |
Console.WriteLine(); | |
#endif | |
} | |
public static string EncryptVigenere(string message, string key) { | |
#if VERBOSE | |
Console.WriteLine("Encrypting (Vigenere):"); | |
Console.WriteLine($"\tMessage: {message}"); | |
Console.WriteLine($"\tKey: {key}"); | |
#endif | |
#if !INCLUDE_UPPER | |
message = message.ToLower(); | |
key = key.ToLower(); | |
#endif | |
#if IGNORE_INVALID_CHARACTERS | |
message = string.Concat(message.Where(c => Encoder.ContainsKey(c))); | |
if (message.Length == 0) return ""; | |
key = string.Concat(key.Where(c => Encoder.ContainsKey(c))); | |
if (key.Length == 0) return "Invalid key!"; | |
#else | |
if (message.Any(c => !Encoder.ContainsKey(c))) return "Invalid character in message!"; | |
if (key.Any(c => !Encoder.ContainsKey(c))) return "Invalid character in key!"; | |
#endif | |
#if (VERBOSE && (!INCLUDE_UPPER || IGNORE_INVALID_CHARACTERS)) | |
Console.WriteLine("Sanitized arguments:"); | |
Console.WriteLine($"\tMessage: {message}"); | |
Console.WriteLine($"\tKey: {key}"); | |
#endif | |
#if VERBOSE | |
Console.WriteLine(); | |
#endif | |
StringBuilder ciphertext = new StringBuilder(message.Length); | |
int k = 0; | |
foreach (var m in message) { | |
#if VERBOSE | |
Console.Write($"{m}({Encoder[m]}) + {key[k]}({Encoder[key[k]]}) mod {AlphabetLength} = "); | |
Console.WriteLine($"{Decoder[(byte)((Encoder[m] + Encoder[key[k]]) % AlphabetLength)]}({(Encoder[m] + Encoder[key[k]]) % AlphabetLength})"); | |
#endif | |
ciphertext.Append(Decoder[(byte)((Encoder[m] + Encoder[key[k++]]) % AlphabetLength)]); | |
if (k == key.Length) k = 0; | |
} | |
#if VERBOSE | |
Console.WriteLine(); | |
#endif | |
return ciphertext.ToString(); | |
} | |
public static string DecryptVigenere(string ciphertext, string key) { | |
#if VERBOSE | |
Console.WriteLine("Decrypting (Vigenere):"); | |
Console.WriteLine($"\tCiphertext: {ciphertext}"); | |
Console.WriteLine($"\tKey: {key}"); | |
#endif | |
#if !INCLUDE_UPPER | |
ciphertext = ciphertext.ToLower(); | |
key = key.ToLower(); | |
#endif | |
#if IGNORE_INVALID_CHARACTERS | |
ciphertext = string.Concat(ciphertext.Where(c => Encoder.ContainsKey(c))); | |
if (ciphertext.Length == 0) return ""; | |
key = string.Concat(key.Where(c => Encoder.ContainsKey(c))); | |
if (key.Length == 0) return "Invalid key!"; | |
#else | |
if (ciphertext.Any(c => !Encoder.ContainsKey(c))) return "Invalid character in ciphertext!"; | |
if (key.Any(c => !Encoder.ContainsKey(c))) return "Invalid character in key!"; | |
#endif | |
#if (VERBOSE && (!INCLUDE_UPPER || IGNORE_INVALID_CHARACTERS)) | |
Console.WriteLine("Sanitized arguments:"); | |
Console.WriteLine($"\tCiphertext: {ciphertext}"); | |
Console.WriteLine($"\tKey: {key}"); | |
#endif | |
#if VERBOSE | |
Console.WriteLine(); | |
#endif | |
StringBuilder decrypted = new StringBuilder(ciphertext.Length); | |
int k = 0; | |
foreach (var c in ciphertext) { | |
#if VERBOSE | |
Console.Write($"{c}({Encoder[c]}) - {key[k]}({Encoder[key[k]]}) mod {AlphabetLength} = "); | |
Console.WriteLine($"{Decoder[(byte)((AlphabetLength + Encoder[c] - Encoder[key[k]]) % AlphabetLength)]}({(AlphabetLength + Encoder[c] - Encoder[key[k]]) % AlphabetLength})"); | |
#endif | |
decrypted.Append(Decoder[(byte)((AlphabetLength + Encoder[c] - Encoder[key[k++]]) % AlphabetLength)]); | |
if (k == key.Length) k = 0; | |
} | |
#if VERBOSE | |
Console.WriteLine(); | |
#endif | |
return decrypted.ToString(); | |
} | |
#endregion | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment