Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- using System;
- using System.Security.Cryptography;
- using System.Text;
- // ReSharper disable UnusedMember.Global
- #region MIT LICENSE
- /*
- License: The MIT License (MIT)
- Copyright (C) 2021 Shannon Rowe
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation
- the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
- and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of
- the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
- TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
- CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- DEALINGS IN THE SOFTWARE.
- */
- #endregion
- namespace Tesseraction.Functions
- {
- public static class EncryptionFunctions
- {
- private const CipherMode EncryptionCipherMode = CipherMode.CBC;
- private const PaddingMode EncryptionPaddingMode = PaddingMode.PKCS7;
- private const int EncryptionKeySize = 256;
- private const CipherMode RandomKeyCipherMode = CipherMode.CBC;
- private const PaddingMode RandomKeyPaddingMode = PaddingMode.PKCS7;
- private const int RandomKeySize = 192;
- private const int EncryptionKeyLength = 24;
- private const int EncryptionIvLength = 16;
- private const int Md5ChecksumLength = 16;
- // Note: these salts should be changed for every application using these functions
- private static readonly int [] saltOrder = {9, 3, 5, 1, 4, 0, 7, 8, 2, 6};
- private static readonly int saltOrderLength = saltOrder.Length;
- private const int SaltXor = 234;
- // The minimum recommended iterations is normally 1000, but we set this lower to save on load time and GC cost,
- // since we're not really that desperate to protect against hackers who would figure this out easily anyway
- // Having more than this improves security but creates a spike of processing and GC, so not really worth it here
- private const int Iterations = 100;
- public static byte [] GenerateRandomEncryptionKey ()
- {
- using (RijndaelManaged rijndael = new RijndaelManaged ())
- {
- rijndael.Mode = RandomKeyCipherMode;
- rijndael.Padding = RandomKeyPaddingMode;
- rijndael.KeySize = RandomKeySize;
- rijndael.GenerateKey ();
- if (rijndael.Key.Length == EncryptionKeyLength)
- {
- return rijndael.Key;
- }
- throw new CryptographicException ();
- }
- }
- public static byte [] GenerateRandomEncryptionIv ()
- {
- using (RijndaelManaged rijndael = new RijndaelManaged ())
- {
- rijndael.Mode = RandomKeyCipherMode;
- rijndael.Padding = RandomKeyPaddingMode;
- rijndael.KeySize = RandomKeySize;
- rijndael.GenerateIV ();
- if (rijndael.IV.Length == EncryptionIvLength)
- {
- return rijndael.IV;
- }
- throw new CryptographicException ();
- }
- }
- private static uint Fnv1AHash (string stringToHash)
- {
- if (stringToHash == null)
- {
- return 0;
- }
- uint hash = 2166136261;
- foreach (char c in stringToHash)
- {
- hash = (hash ^ c) * 16777619;
- }
- return hash;
- }
- public static byte [] GeneratePasswordEncryptionKey (string password)
- {
- if (string.IsNullOrEmpty (password))
- {
- throw new ArgumentNullException (nameof (password));
- }
- // Construct an additional XOR from the hashcode of the password string
- // Note: GetHashCode is not reliably consistent between builds or machines and should NEVER be used for encryption,
- // so we use an implementation of FNV1a hash instead
- int passwordHashXor = (int) (Fnv1AHash (password) % 256);
- // Construct the salt from bytes from dependent script, modified by order and XORs
- byte [] salt = new byte [saltOrderLength];
- for (int i = 0; i < saltOrderLength; i++)
- {
- // Modify each byte by the XORs, so that the true salt is only ever officially in memory here briefly
- salt [i] = (byte) (ByteFunctions.XorSaltBytes [saltOrder [i]] ^ SaltXor ^ passwordHashXor);
- }
- // Derive key based on password, salt, and iterations
- Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes (password, salt, Iterations);
- // Generate the key as the required number of bytes
- byte [] keyBytes = deriveBytes.GetBytes (EncryptionKeyLength);
- if (keyBytes.Length == EncryptionKeyLength)
- {
- return keyBytes;
- }
- throw new ArgumentException (nameof (password));
- }
- // ReSharper disable once UnusedMember.Global
- public static byte [] ScatterPassword (string password, int scatterKey)
- {
- if (string.IsNullOrEmpty (password))
- {
- throw new ArgumentNullException (nameof (password));
- }
- // Get the relevant scatter list and convert the string to a char array
- char [] scatterList = CreateScatterList (scatterKey);
- char [] passwordChars = password.ToCharArray ();
- int passwordCharsLength = passwordChars.Length;
- byte [] scatteredPassword = new byte [passwordCharsLength];
- for (int i = 0; i < passwordCharsLength; i++)
- {
- // Start at a particular formula-staggered index and then search forward until finding a match
- // This will give a reasonable (but consistent) chance of having the same character referred by more than one index
- int currentIndex = ((i * scatterKey * SaltXor) ^ scatterKey ^ passwordCharsLength ^ SaltXor) % 256;
- bool success = false;
- int failureCheck = 0;
- // Scan through all positions only once to make sure we don't get into an infinite loop
- while (failureCheck < 256)
- {
- if (scatterList [currentIndex] == passwordChars [i])
- {
- success = true;
- break;
- }
- currentIndex++;
- if (currentIndex >= 256)
- {
- currentIndex = 0;
- }
- // Increment failure check to avoid getting stuck in an infinite loop
- failureCheck++;
- }
- // Populate the current character and then increment to next char
- if (success)
- {
- scatteredPassword [i] = (byte) currentIndex;
- }
- else
- {
- throw new ArgumentException (nameof (password));
- }
- }
- return scatteredPassword;
- }
- public static string RecoverPassword (byte [] passwordBytes, int scatterKey)
- {
- if (passwordBytes == null)
- {
- throw new ArgumentNullException (nameof (passwordBytes));
- }
- // Get the relevant scatter list and prepare a new StringBuilder
- char [] scatterList = CreateScatterList (scatterKey);
- StringBuilder passwordString = new StringBuilder ();
- // Loop through all of the bytes and convert them back to chars via their index references
- int passwordBytesLength = passwordBytes.Length;
- for (int i = 0; i < passwordBytesLength; i++)
- {
- passwordString.Append (scatterList [passwordBytes [i]]);
- }
- return passwordString.ToString ();
- }
- public static byte [] EncryptBytes (byte [] bytesToEncrypt, byte [] encryptionKey)
- {
- if (bytesToEncrypt == null)
- {
- throw new ArgumentNullException (nameof (bytesToEncrypt));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- // Generate and prepend a new IV every time
- byte [] encryptionIv = GenerateRandomEncryptionIv ();
- if (encryptionIv == null)
- {
- throw new NullReferenceException ();
- }
- if (encryptionIv.Length != EncryptionIvLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- byte [] encryptedBytes = PerformEncryption (bytesToEncrypt, encryptionKey, encryptionIv);
- if (encryptedBytes != null)
- {
- return ByteFunctions.CombineBytes (encryptionIv, encryptedBytes);
- }
- throw new NullReferenceException ();
- }
- public static byte [] DecryptBytes (byte [] bytesToDecryptWithIv, byte [] encryptionKey)
- {
- if (bytesToDecryptWithIv == null)
- {
- throw new ArgumentNullException (nameof (bytesToDecryptWithIv));
- }
- if (bytesToDecryptWithIv.Length <= EncryptionIvLength)
- {
- throw new ArgumentException (nameof (bytesToDecryptWithIv));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- ByteFunctions.SplitBytes (bytesToDecryptWithIv, EncryptionIvLength, out byte [] encryptionIv, out byte [] bytesToDecryptWithoutIv);
- if (encryptionIv == null)
- {
- throw new NullReferenceException ();
- }
- if (encryptionIv.Length != EncryptionIvLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- if (bytesToDecryptWithoutIv == null)
- {
- throw new NullReferenceException ();
- }
- if (bytesToDecryptWithoutIv.Length != bytesToDecryptWithIv.Length - EncryptionIvLength)
- {
- throw new ArgumentException (nameof (bytesToDecryptWithIv));
- }
- byte [] decryptedBytes = PerformDecryption (bytesToDecryptWithoutIv, encryptionKey, encryptionIv);
- if (decryptedBytes != null)
- {
- return decryptedBytes;
- }
- throw new NullReferenceException ();
- }
- public static string EncryptString (string stringToEncrypt, byte [] encryptionKey)
- {
- if (string.IsNullOrEmpty (stringToEncrypt))
- {
- throw new ArgumentNullException (nameof (stringToEncrypt));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- // Turn original full Unicode string into bytes
- byte [] bytesToEncrypt = ByteFunctions.ConvertStringToBytes (stringToEncrypt, Encoding.Unicode);
- if (bytesToEncrypt == null)
- {
- throw new NullReferenceException ();
- }
- // Encrypt bytes using Rijndael, which will also prepend the IV
- byte [] encryptedBytes = EncryptBytes (bytesToEncrypt, encryptionKey);
- if (encryptedBytes != null)
- {
- return ByteFunctions.ConvertBytesToBase64String (encryptedBytes);
- }
- throw new NullReferenceException ();
- }
- public static string DecryptString (string stringToDecrypt, byte [] encryptionKey)
- {
- if (string.IsNullOrEmpty (stringToDecrypt))
- {
- throw new ArgumentNullException (nameof (stringToDecrypt));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- // Turn the encrypted Base64 string back into bytes
- byte [] bytesToDecrypt = ByteFunctions.ConvertBase64StringToBytes (stringToDecrypt);
- if (bytesToDecrypt == null)
- {
- throw new NullReferenceException ();
- }
- // Decrypt bytes using Rijndael, which will also extract the IV
- byte [] decryptedBytes = DecryptBytes (bytesToDecrypt, encryptionKey);
- if (decryptedBytes != null)
- {
- return ByteFunctions.ConvertBytesToString (decryptedBytes, Encoding.Unicode);
- }
- throw new NullReferenceException ();
- }
- public static byte [] GetMd5Checksum (byte [] bytes)
- {
- if (bytes == null)
- {
- throw new ArgumentNullException (nameof (bytes));
- }
- using (MD5 md5Checksum = MD5.Create ())
- {
- byte [] md5ChecksumBytes = md5Checksum.ComputeHash (bytes);
- if (md5ChecksumBytes.Length != Md5ChecksumLength)
- {
- throw new ArgumentException (nameof (bytes));
- }
- return md5ChecksumBytes;
- }
- }
- public static int GetStringHashCode (string stringValue)
- {
- unchecked
- {
- int hash1 = (5381 << 16) + 5381;
- int hash2 = hash1;
- int stringValueLength = stringValue.Length;
- for (int i = 0; i < stringValueLength; i += 2)
- {
- hash1 = ((hash1 << 5) + hash1) ^ stringValue [i];
- if (i == stringValue.Length - 1)
- {
- break;
- }
- hash2 = ((hash2 << 5) + hash2) ^ stringValue [i + 1];
- }
- return hash1 + hash2 * 1566083941;
- }
- }
- private static byte [] PerformEncryption (byte [] bytesToEncrypt, byte [] encryptionKey, byte [] encryptionIv)
- {
- if (bytesToEncrypt == null)
- {
- throw new ArgumentNullException (nameof (bytesToEncrypt));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionIv == null)
- {
- throw new ArgumentNullException (nameof (encryptionIv));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- if (encryptionIv.Length != EncryptionIvLength)
- {
- throw new ArgumentException (nameof (encryptionIv));
- }
- using (RijndaelManaged rijndael = new RijndaelManaged ())
- {
- rijndael.Mode = EncryptionCipherMode;
- rijndael.Padding = EncryptionPaddingMode;
- rijndael.KeySize = EncryptionKeySize;
- ICryptoTransform cryptoTransform = rijndael.CreateEncryptor (encryptionKey, encryptionIv);
- return cryptoTransform.TransformFinalBlock (bytesToEncrypt, 0, bytesToEncrypt.Length);
- }
- }
- private static byte [] PerformDecryption (byte [] bytesToDecrypt, byte [] encryptionKey, byte [] encryptionIv)
- {
- if (bytesToDecrypt == null)
- {
- throw new ArgumentNullException (nameof (bytesToDecrypt));
- }
- if (encryptionKey == null)
- {
- throw new ArgumentNullException (nameof (encryptionKey));
- }
- if (encryptionIv == null)
- {
- throw new ArgumentNullException (nameof (encryptionIv));
- }
- if (encryptionKey.Length != EncryptionKeyLength)
- {
- throw new ArgumentException (nameof (encryptionKey));
- }
- if (encryptionIv.Length != EncryptionIvLength)
- {
- throw new ArgumentException (nameof (encryptionIv));
- }
- using (RijndaelManaged rijndael = new RijndaelManaged ())
- {
- rijndael.Mode = EncryptionCipherMode;
- rijndael.Padding = EncryptionPaddingMode;
- rijndael.KeySize = EncryptionKeySize;
- ICryptoTransform cryptoTransform = rijndael.CreateDecryptor (encryptionKey, encryptionIv);
- return cryptoTransform.TransformFinalBlock (bytesToDecrypt, 0, bytesToDecrypt.Length);
- }
- }
- private static char [] CreateScatterList (int scatterKey)
- {
- char [] scatterList = new char [256];
- int currentChar = 32;
- // Fill up full 256 array with all of the printable ASCII characters between 32 and 126
- for (int i = 0; i < 256; i++)
- {
- // Find the first spot that hasn't been used yet after applying scatter
- int newIndex = ((i * i) ^ scatterKey ^ currentChar) % 256;
- bool success = false;
- int failureCheck = 0;
- // Scan through all positions only once to make sure we don't get into an infinite loop
- while (failureCheck < 256)
- {
- if (scatterList [newIndex] == default (char))
- {
- success = true;
- break;
- }
- newIndex++;
- if (newIndex >= 256)
- {
- newIndex = 0;
- }
- // Increment failure check to avoid getting stuck in an infinite loop
- failureCheck++;
- }
- // Populate the current character and then increment to next char
- if (success)
- {
- scatterList [newIndex] = (char) currentChar;
- currentChar++;
- if (currentChar >= 127)
- {
- currentChar = 32;
- }
- }
- else
- {
- throw new ArgumentOutOfRangeException ();
- }
- }
- return scatterList;
- }
- }
- }
Add Comment
Please, Sign In to add comment