chmodseven

Encryption Functions

Jan 7th, 2021 (edited)
108
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 20.02 KB | None | 0 0
  1. using System;
  2. using System.Security.Cryptography;
  3. using System.Text;
  4.  
  5. // ReSharper disable UnusedMember.Global
  6.  
  7. #region MIT LICENSE
  8.  
  9. /*
  10.     License: The MIT License (MIT)
  11.     Copyright (C) 2021 Shannon Rowe
  12.    
  13.     Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
  14.     documentation files (the "Software"), to deal in the Software without restriction, including without limitation
  15.     the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
  16.     and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  17.  
  18.     The above copyright notice and this permission notice shall be included in all copies or substantial portions of
  19.     the Software.
  20.    
  21.     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
  22.     TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  23.     THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  24.     CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  25.     DEALINGS IN THE SOFTWARE.
  26. */
  27.  
  28. #endregion
  29.  
  30. namespace Tesseraction.Functions
  31. {
  32.     public static class EncryptionFunctions
  33.     {
  34.         private const CipherMode EncryptionCipherMode = CipherMode.CBC;
  35.         private const PaddingMode EncryptionPaddingMode = PaddingMode.PKCS7;
  36.         private const int EncryptionKeySize = 256;
  37.         private const CipherMode RandomKeyCipherMode = CipherMode.CBC;
  38.         private const PaddingMode RandomKeyPaddingMode = PaddingMode.PKCS7;
  39.         private const int RandomKeySize = 192;
  40.         private const int EncryptionKeyLength = 24;
  41.         private const int EncryptionIvLength = 16;
  42.         private const int Md5ChecksumLength = 16;
  43.  
  44.         // Note: these salts should be changed for every application using these functions
  45.  
  46.         private static readonly int [] saltOrder = {9, 3, 5, 1, 4, 0, 7, 8, 2, 6};
  47.         private static readonly int saltOrderLength = saltOrder.Length;
  48.         private const int SaltXor = 234;
  49.  
  50.         // The minimum recommended iterations is normally 1000, but we set this lower to save on load time and GC cost,
  51.         // since we're not really that desperate to protect against hackers who would figure this out easily anyway
  52.         // Having more than this improves security but creates a spike of processing and GC, so not really worth it here
  53.  
  54.         private const int Iterations = 100;
  55.  
  56.         public static byte [] GenerateRandomEncryptionKey ()
  57.         {
  58.             using (RijndaelManaged rijndael = new RijndaelManaged ())
  59.             {
  60.                 rijndael.Mode = RandomKeyCipherMode;
  61.                 rijndael.Padding = RandomKeyPaddingMode;
  62.                 rijndael.KeySize = RandomKeySize;
  63.  
  64.                 rijndael.GenerateKey ();
  65.                 if (rijndael.Key.Length == EncryptionKeyLength)
  66.                 {
  67.                     return rijndael.Key;
  68.                 }
  69.  
  70.                 throw new CryptographicException ();
  71.             }
  72.         }
  73.  
  74.         public static byte [] GenerateRandomEncryptionIv ()
  75.         {
  76.             using (RijndaelManaged rijndael = new RijndaelManaged ())
  77.             {
  78.                 rijndael.Mode = RandomKeyCipherMode;
  79.                 rijndael.Padding = RandomKeyPaddingMode;
  80.                 rijndael.KeySize = RandomKeySize;
  81.  
  82.                 rijndael.GenerateIV ();
  83.                 if (rijndael.IV.Length == EncryptionIvLength)
  84.                 {
  85.                     return rijndael.IV;
  86.                 }
  87.  
  88.                 throw new CryptographicException ();
  89.             }
  90.         }
  91.  
  92.         private static uint Fnv1AHash (string stringToHash)
  93.         {
  94.             if (stringToHash == null)
  95.             {
  96.                 return 0;
  97.             }
  98.  
  99.             uint hash = 2166136261;
  100.             foreach (char c in stringToHash)
  101.             {
  102.                 hash = (hash ^ c) * 16777619;
  103.             }
  104.  
  105.             return hash;
  106.         }
  107.  
  108.         public static byte [] GeneratePasswordEncryptionKey (string password)
  109.         {
  110.             if (string.IsNullOrEmpty (password))
  111.             {
  112.                 throw new ArgumentNullException (nameof (password));
  113.             }
  114.  
  115.             // Construct an additional XOR from the hashcode of the password string
  116.             // Note: GetHashCode is not reliably consistent between builds or machines and should NEVER be used for encryption,
  117.             // so we use an implementation of FNV1a hash instead
  118.             int passwordHashXor = (int) (Fnv1AHash (password) % 256);
  119.  
  120.             // Construct the salt from bytes from dependent script, modified by order and XORs
  121.             byte [] salt = new byte [saltOrderLength];
  122.             for (int i = 0; i < saltOrderLength; i++)
  123.             {
  124.                 // Modify each byte by the XORs, so that the true salt is only ever officially in memory here briefly
  125.                 salt [i] = (byte) (ByteFunctions.XorSaltBytes [saltOrder [i]] ^ SaltXor ^ passwordHashXor);
  126.             }
  127.  
  128.             // Derive key based on password, salt, and iterations
  129.             Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes (password, salt, Iterations);
  130.  
  131.             // Generate the key as the required number of bytes
  132.             byte [] keyBytes = deriveBytes.GetBytes (EncryptionKeyLength);
  133.             if (keyBytes.Length == EncryptionKeyLength)
  134.             {
  135.                 return keyBytes;
  136.             }
  137.  
  138.             throw new ArgumentException (nameof (password));
  139.         }
  140.  
  141.         // ReSharper disable once UnusedMember.Global
  142.         public static byte [] ScatterPassword (string password, int scatterKey)
  143.         {
  144.             if (string.IsNullOrEmpty (password))
  145.             {
  146.                 throw new ArgumentNullException (nameof (password));
  147.             }
  148.  
  149.             // Get the relevant scatter list and convert the string to a char array
  150.             char [] scatterList = CreateScatterList (scatterKey);
  151.             char [] passwordChars = password.ToCharArray ();
  152.  
  153.             int passwordCharsLength = passwordChars.Length;
  154.             byte [] scatteredPassword = new byte [passwordCharsLength];
  155.  
  156.             for (int i = 0; i < passwordCharsLength; i++)
  157.             {
  158.                 // Start at a particular formula-staggered index and then search forward until finding a match
  159.                 // This will give a reasonable (but consistent) chance of having the same character referred by more than one index
  160.                 int currentIndex = ((i * scatterKey * SaltXor) ^ scatterKey ^ passwordCharsLength ^ SaltXor) % 256;
  161.                 bool success = false;
  162.                 int failureCheck = 0;
  163.  
  164.                 // Scan through all positions only once to make sure we don't get into an infinite loop
  165.                 while (failureCheck < 256)
  166.                 {
  167.                     if (scatterList [currentIndex] == passwordChars [i])
  168.                     {
  169.                         success = true;
  170.                         break;
  171.                     }
  172.  
  173.                     currentIndex++;
  174.                     if (currentIndex >= 256)
  175.                     {
  176.                         currentIndex = 0;
  177.                     }
  178.  
  179.                     // Increment failure check to avoid getting stuck in an infinite loop
  180.                     failureCheck++;
  181.                 }
  182.  
  183.                 // Populate the current character and then increment to next char
  184.                 if (success)
  185.                 {
  186.                     scatteredPassword [i] = (byte) currentIndex;
  187.                 }
  188.                 else
  189.                 {
  190.                     throw new ArgumentException (nameof (password));
  191.                 }
  192.             }
  193.  
  194.             return scatteredPassword;
  195.         }
  196.  
  197.         public static string RecoverPassword (byte [] passwordBytes, int scatterKey)
  198.         {
  199.             if (passwordBytes == null)
  200.             {
  201.                 throw new ArgumentNullException (nameof (passwordBytes));
  202.             }
  203.  
  204.             // Get the relevant scatter list and prepare a new StringBuilder
  205.             char [] scatterList = CreateScatterList (scatterKey);
  206.             StringBuilder passwordString = new StringBuilder ();
  207.  
  208.             // Loop through all of the bytes and convert them back to chars via their index references
  209.             int passwordBytesLength = passwordBytes.Length;
  210.             for (int i = 0; i < passwordBytesLength; i++)
  211.             {
  212.                 passwordString.Append (scatterList [passwordBytes [i]]);
  213.             }
  214.  
  215.             return passwordString.ToString ();
  216.         }
  217.  
  218.         public static byte [] EncryptBytes (byte [] bytesToEncrypt, byte [] encryptionKey)
  219.         {
  220.             if (bytesToEncrypt == null)
  221.             {
  222.                 throw new ArgumentNullException (nameof (bytesToEncrypt));
  223.             }
  224.  
  225.             if (encryptionKey == null)
  226.             {
  227.                 throw new ArgumentNullException (nameof (encryptionKey));
  228.             }
  229.  
  230.             if (encryptionKey.Length != EncryptionKeyLength)
  231.             {
  232.                 throw new ArgumentException (nameof (encryptionKey));
  233.             }
  234.  
  235.             // Generate and prepend a new IV every time
  236.             byte [] encryptionIv = GenerateRandomEncryptionIv ();
  237.             if (encryptionIv == null)
  238.             {
  239.                 throw new NullReferenceException ();
  240.             }
  241.  
  242.             if (encryptionIv.Length != EncryptionIvLength)
  243.             {
  244.                 throw new ArgumentException (nameof (encryptionKey));
  245.             }
  246.  
  247.             byte [] encryptedBytes = PerformEncryption (bytesToEncrypt, encryptionKey, encryptionIv);
  248.             if (encryptedBytes != null)
  249.             {
  250.                 return ByteFunctions.CombineBytes (encryptionIv, encryptedBytes);
  251.             }
  252.  
  253.             throw new NullReferenceException ();
  254.         }
  255.  
  256.         public static byte [] DecryptBytes (byte [] bytesToDecryptWithIv, byte [] encryptionKey)
  257.         {
  258.             if (bytesToDecryptWithIv == null)
  259.             {
  260.                 throw new ArgumentNullException (nameof (bytesToDecryptWithIv));
  261.             }
  262.  
  263.             if (bytesToDecryptWithIv.Length <= EncryptionIvLength)
  264.             {
  265.                 throw new ArgumentException (nameof (bytesToDecryptWithIv));
  266.             }
  267.  
  268.             if (encryptionKey == null)
  269.             {
  270.                 throw new ArgumentNullException (nameof (encryptionKey));
  271.             }
  272.  
  273.             if (encryptionKey.Length != EncryptionKeyLength)
  274.             {
  275.                 throw new ArgumentException (nameof (encryptionKey));
  276.             }
  277.  
  278.             ByteFunctions.SplitBytes (bytesToDecryptWithIv, EncryptionIvLength, out byte [] encryptionIv, out byte [] bytesToDecryptWithoutIv);
  279.             if (encryptionIv == null)
  280.             {
  281.                 throw new NullReferenceException ();
  282.             }
  283.  
  284.             if (encryptionIv.Length != EncryptionIvLength)
  285.             {
  286.                 throw new ArgumentException (nameof (encryptionKey));
  287.             }
  288.  
  289.             if (bytesToDecryptWithoutIv == null)
  290.             {
  291.                 throw new NullReferenceException ();
  292.             }
  293.  
  294.             if (bytesToDecryptWithoutIv.Length != bytesToDecryptWithIv.Length - EncryptionIvLength)
  295.             {
  296.                 throw new ArgumentException (nameof (bytesToDecryptWithIv));
  297.             }
  298.  
  299.             byte [] decryptedBytes = PerformDecryption (bytesToDecryptWithoutIv, encryptionKey, encryptionIv);
  300.             if (decryptedBytes != null)
  301.             {
  302.                 return decryptedBytes;
  303.             }
  304.  
  305.             throw new NullReferenceException ();
  306.         }
  307.  
  308.         public static string EncryptString (string stringToEncrypt, byte [] encryptionKey)
  309.         {
  310.             if (string.IsNullOrEmpty (stringToEncrypt))
  311.             {
  312.                 throw new ArgumentNullException (nameof (stringToEncrypt));
  313.             }
  314.  
  315.             if (encryptionKey == null)
  316.             {
  317.                 throw new ArgumentNullException (nameof (encryptionKey));
  318.             }
  319.  
  320.             if (encryptionKey.Length != EncryptionKeyLength)
  321.             {
  322.                 throw new ArgumentException (nameof (encryptionKey));
  323.             }
  324.  
  325.             // Turn original full Unicode string into bytes
  326.             byte [] bytesToEncrypt = ByteFunctions.ConvertStringToBytes (stringToEncrypt, Encoding.Unicode);
  327.             if (bytesToEncrypt == null)
  328.             {
  329.                 throw new NullReferenceException ();
  330.             }
  331.  
  332.             // Encrypt bytes using Rijndael, which will also prepend the IV
  333.             byte [] encryptedBytes = EncryptBytes (bytesToEncrypt, encryptionKey);
  334.             if (encryptedBytes != null)
  335.             {
  336.                 return ByteFunctions.ConvertBytesToBase64String (encryptedBytes);
  337.             }
  338.  
  339.             throw new NullReferenceException ();
  340.         }
  341.  
  342.         public static string DecryptString (string stringToDecrypt, byte [] encryptionKey)
  343.         {
  344.             if (string.IsNullOrEmpty (stringToDecrypt))
  345.             {
  346.                 throw new ArgumentNullException (nameof (stringToDecrypt));
  347.             }
  348.  
  349.             if (encryptionKey == null)
  350.             {
  351.                 throw new ArgumentNullException (nameof (encryptionKey));
  352.             }
  353.  
  354.             if (encryptionKey.Length != EncryptionKeyLength)
  355.             {
  356.                 throw new ArgumentException (nameof (encryptionKey));
  357.             }
  358.  
  359.             // Turn the encrypted Base64 string back into bytes
  360.             byte [] bytesToDecrypt = ByteFunctions.ConvertBase64StringToBytes (stringToDecrypt);
  361.             if (bytesToDecrypt == null)
  362.             {
  363.                 throw new NullReferenceException ();
  364.             }
  365.  
  366.             // Decrypt bytes using Rijndael, which will also extract the IV
  367.             byte [] decryptedBytes = DecryptBytes (bytesToDecrypt, encryptionKey);
  368.             if (decryptedBytes != null)
  369.             {
  370.                 return ByteFunctions.ConvertBytesToString (decryptedBytes, Encoding.Unicode);
  371.             }
  372.  
  373.             throw new NullReferenceException ();
  374.         }
  375.  
  376.         public static byte [] GetMd5Checksum (byte [] bytes)
  377.         {
  378.             if (bytes == null)
  379.             {
  380.                 throw new ArgumentNullException (nameof (bytes));
  381.             }
  382.  
  383.             using (MD5 md5Checksum = MD5.Create ())
  384.             {
  385.                 byte [] md5ChecksumBytes = md5Checksum.ComputeHash (bytes);
  386.                 if (md5ChecksumBytes.Length != Md5ChecksumLength)
  387.                 {
  388.                     throw new ArgumentException (nameof (bytes));
  389.                 }
  390.  
  391.                 return md5ChecksumBytes;
  392.             }
  393.         }
  394.  
  395.         public static int GetStringHashCode (string stringValue)
  396.         {
  397.             unchecked
  398.             {
  399.                 int hash1 = (5381 << 16) + 5381;
  400.                 int hash2 = hash1;
  401.  
  402.                 int stringValueLength = stringValue.Length;
  403.                 for (int i = 0; i < stringValueLength; i += 2)
  404.                 {
  405.                     hash1 = ((hash1 << 5) + hash1) ^ stringValue [i];
  406.                     if (i == stringValue.Length - 1)
  407.                     {
  408.                         break;
  409.                     }
  410.  
  411.                     hash2 = ((hash2 << 5) + hash2) ^ stringValue [i + 1];
  412.                 }
  413.  
  414.                 return hash1 + hash2 * 1566083941;
  415.             }
  416.         }
  417.  
  418.         private static byte [] PerformEncryption (byte [] bytesToEncrypt, byte [] encryptionKey, byte [] encryptionIv)
  419.         {
  420.             if (bytesToEncrypt == null)
  421.             {
  422.                 throw new ArgumentNullException (nameof (bytesToEncrypt));
  423.             }
  424.  
  425.             if (encryptionKey == null)
  426.             {
  427.                 throw new ArgumentNullException (nameof (encryptionKey));
  428.             }
  429.  
  430.             if (encryptionIv == null)
  431.             {
  432.                 throw new ArgumentNullException (nameof (encryptionIv));
  433.             }
  434.  
  435.             if (encryptionKey.Length != EncryptionKeyLength)
  436.             {
  437.                 throw new ArgumentException (nameof (encryptionKey));
  438.             }
  439.  
  440.             if (encryptionIv.Length != EncryptionIvLength)
  441.             {
  442.                 throw new ArgumentException (nameof (encryptionIv));
  443.             }
  444.  
  445.             using (RijndaelManaged rijndael = new RijndaelManaged ())
  446.             {
  447.                 rijndael.Mode = EncryptionCipherMode;
  448.                 rijndael.Padding = EncryptionPaddingMode;
  449.                 rijndael.KeySize = EncryptionKeySize;
  450.  
  451.                 ICryptoTransform cryptoTransform = rijndael.CreateEncryptor (encryptionKey, encryptionIv);
  452.                 return cryptoTransform.TransformFinalBlock (bytesToEncrypt, 0, bytesToEncrypt.Length);
  453.             }
  454.         }
  455.  
  456.         private static byte [] PerformDecryption (byte [] bytesToDecrypt, byte [] encryptionKey, byte [] encryptionIv)
  457.         {
  458.             if (bytesToDecrypt == null)
  459.             {
  460.                 throw new ArgumentNullException (nameof (bytesToDecrypt));
  461.             }
  462.  
  463.             if (encryptionKey == null)
  464.             {
  465.                 throw new ArgumentNullException (nameof (encryptionKey));
  466.             }
  467.  
  468.             if (encryptionIv == null)
  469.             {
  470.                 throw new ArgumentNullException (nameof (encryptionIv));
  471.             }
  472.  
  473.             if (encryptionKey.Length != EncryptionKeyLength)
  474.             {
  475.                 throw new ArgumentException (nameof (encryptionKey));
  476.             }
  477.  
  478.             if (encryptionIv.Length != EncryptionIvLength)
  479.             {
  480.                 throw new ArgumentException (nameof (encryptionIv));
  481.             }
  482.  
  483.             using (RijndaelManaged rijndael = new RijndaelManaged ())
  484.             {
  485.                 rijndael.Mode = EncryptionCipherMode;
  486.                 rijndael.Padding = EncryptionPaddingMode;
  487.                 rijndael.KeySize = EncryptionKeySize;
  488.  
  489.                 ICryptoTransform cryptoTransform = rijndael.CreateDecryptor (encryptionKey, encryptionIv);
  490.                 return cryptoTransform.TransformFinalBlock (bytesToDecrypt, 0, bytesToDecrypt.Length);
  491.             }
  492.         }
  493.  
  494.         private static char [] CreateScatterList (int scatterKey)
  495.         {
  496.             char [] scatterList = new char [256];
  497.             int currentChar = 32;
  498.  
  499.             // Fill up full 256 array with all of the printable ASCII characters between 32 and 126
  500.             for (int i = 0; i < 256; i++)
  501.             {
  502.                 // Find the first spot that hasn't been used yet after applying scatter
  503.                 int newIndex = ((i * i) ^ scatterKey ^ currentChar) % 256;
  504.                 bool success = false;
  505.                 int failureCheck = 0;
  506.  
  507.                 // Scan through all positions only once to make sure we don't get into an infinite loop
  508.                 while (failureCheck < 256)
  509.                 {
  510.                     if (scatterList [newIndex] == default (char))
  511.                     {
  512.                         success = true;
  513.                         break;
  514.                     }
  515.  
  516.                     newIndex++;
  517.                     if (newIndex >= 256)
  518.                     {
  519.                         newIndex = 0;
  520.                     }
  521.  
  522.                     // Increment failure check to avoid getting stuck in an infinite loop
  523.                     failureCheck++;
  524.                 }
  525.  
  526.                 // Populate the current character and then increment to next char
  527.                 if (success)
  528.                 {
  529.                     scatterList [newIndex] = (char) currentChar;
  530.                     currentChar++;
  531.                     if (currentChar >= 127)
  532.                     {
  533.                         currentChar = 32;
  534.                     }
  535.                 }
  536.                 else
  537.                 {
  538.                     throw new ArgumentOutOfRangeException ();
  539.                 }
  540.             }
  541.  
  542.             return scatterList;
  543.         }
  544.     }
  545. }
Add Comment
Please, Sign In to add comment