using System; using System.Collections.Generic; #if DEBUG using System.Diagnostics; #endif // #if DEBUG using System.Linq; using System.Text; using System.IO; using System.Security.Cryptography; namespace BroccoliProducts { /// /// Declaration of DESCrytography class /// public static class DESCrytography { ///////////////////////////////////////////////////////////// // Nested classes /// /// Declaration of BLOCK8BYTE class /// internal class BLOCK8BYTE { ///////////////////////////////////////////////////////// // Constants public const int BYTE_LENGTH = 8; ///////////////////////////////////////////////////////// // Attributes internal byte[] m_data = new byte[BYTE_LENGTH]; ///////////////////////////////////////////////////////// // Operations public void Reset() { // Reset bytes Array.Clear(m_data, 0, BYTE_LENGTH); } public void Set(BLOCK8BYTE Source) { // Copy source data to this this.Set(Source.m_data,0); } public void Set(byte[] buffer, int iOffset) { // Set contents by copying array Array.Copy(buffer, iOffset, m_data, 0, BYTE_LENGTH); } public void Xor(BLOCK8BYTE A, BLOCK8BYTE B) { // Set byte to A ^ B for (int iOffset = 0; iOffset < BYTE_LENGTH; iOffset++) m_data[iOffset] = Convert.ToByte( A.m_data[iOffset] ^ B.m_data[iOffset] ); } public void SetBit(int iByteOffset, int iBitOffset, bool bFlag) { // Compose mask byte mask = Convert.ToByte(1 << iBitOffset); if (((m_data[iByteOffset] & mask) == mask) != bFlag) m_data[iByteOffset] ^= mask; } public bool GetBit(int iByteOffset, int iBitOffset) { // call sibling function return ((this.m_data[iByteOffset] >> iBitOffset) & 0x01) == 0x01; } public void ShiftLeftWrapped( BLOCK8BYTE S, int iBitShift ) { // this shift is only applied to the first 32 bits, and parity bit is ignored // Declaration of local variables int iByteOffset = 0; bool bBit = false; // Copy byte and shift regardless for (iByteOffset = 0; iByteOffset < 4; iByteOffset++) m_data[iByteOffset] = Convert.ToByte((S.m_data[iByteOffset] << iBitShift) & 0xFF); // if shifting by 1... if (iBitShift == 1) { // repair bits on right of BYTE for (iByteOffset = 0; iByteOffset < 3; iByteOffset++) { // get repairing bit offsets bBit = S.GetBit( iByteOffset + 1, 7 ); this.SetBit(iByteOffset, 1, bBit); } // wrap around the final bit this.SetBit(3,1,S.GetBit(0,7)); } else if (iBitShift == 2) { // repair bits on right of BYTE for (iByteOffset = 0; iByteOffset < 3; iByteOffset++) { // get repairing bit offsets bBit = S.GetBit(iByteOffset + 1, 7); this.SetBit(iByteOffset, 2, bBit); bBit = S.GetBit(iByteOffset + 1, 6); this.SetBit(iByteOffset, 1, bBit); } // wrap around the final bit this.SetBit(3, 2, S.GetBit(0, 7)); this.SetBit(3, 1, S.GetBit(0, 6)); } #if DEBUG else Debug.Assert(false); #endif // #if DEBUG } } /// /// Declaration of KEY_SET class /// internal class KEY_SET { ///////////////////////////////////////////////////////// // Constants public const int KEY_COUNT = 17; ///////////////////////////////////////////////////////// // Attributes internal BLOCK8BYTE[] m_array; ///////////////////////////////////////////////////////// // Construction internal KEY_SET() { // Create array m_array = new BLOCK8BYTE[KEY_COUNT]; for (int i1 = 0; i1 < KEY_COUNT; i1++) m_array[i1] = new BLOCK8BYTE(); } ///////////////////////////////////////////////////////// // Operations public BLOCK8BYTE GetAt(int iArrayOffset) { return m_array[iArrayOffset]; } } /// /// Declaration of WORKING_SET class /// internal class WORKING_SET { ///////////////////////////////////////////////////////// // Attributes internal BLOCK8BYTE IP = new BLOCK8BYTE(); internal BLOCK8BYTE[] Ln = new BLOCK8BYTE[17]; internal BLOCK8BYTE[] Rn = new BLOCK8BYTE[17]; internal BLOCK8BYTE RnExpand = new BLOCK8BYTE(); internal BLOCK8BYTE XorBlock = new BLOCK8BYTE(); internal BLOCK8BYTE SBoxValues = new BLOCK8BYTE(); internal BLOCK8BYTE f = new BLOCK8BYTE(); internal BLOCK8BYTE X = new BLOCK8BYTE(); internal BLOCK8BYTE DataBlockIn = new BLOCK8BYTE(); internal BLOCK8BYTE DataBlockOut = new BLOCK8BYTE(); internal BLOCK8BYTE DecryptXorBlock = new BLOCK8BYTE(); ///////////////////////////////////////////////////////// // Construction internal WORKING_SET() { // Build the arrays for (int i1 = 0; i1 < 17; i1++) { Ln[i1] = new BLOCK8BYTE(); Rn[i1] = new BLOCK8BYTE(); } } ///////////////////////////////////////////////////////// // Operations internal void Scrub() { // Scrub data IP.Reset(); for (int i1 = 0; i1 < 17; i1++) { Ln[i1].Reset(); Rn[i1].Reset(); } RnExpand.Reset(); XorBlock.Reset(); SBoxValues.Reset(); f.Reset(); X.Reset(); DataBlockIn.Reset(); DataBlockOut.Reset(); DecryptXorBlock.Reset(); } } ///////////////////////////////////////////////////////////// // Constants public const int KEY_BYTE_LENGTH = 8; public const int BITS_PER_BYTE = 8; ///////////////////////////////////////////////////////////// #region DES Tables /* PERMUTED CHOICE 1 (PCl) */ private static byte[] bytePC1 = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4, }; /* PERMUTED CHOICE 2 (PC2) */ private static byte[] bytePC2 = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32, }; /* INITIAL PERMUTATION (IP) */ private static byte[] byteIP = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 }; /* REVERSE FINAL PERMUTATION (IP-1) */ private static byte[] byteRFP = { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25, }; /* E BIT-SELECTION TABLE */ private static byte[] byteE = { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 }; /* PERMUTATION FUNCTION P */ private static byte[] byteP = { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; // Schedule of left shifts for C and D blocks private static byte[] byteShifts = { 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 }; // S-Boxes private static byte[,] byteSBox = new byte[,] { {14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7}, { 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8}, { 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0}, {15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13}, {15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10}, {3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5}, {0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15}, {13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9}, {10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8}, {13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1}, {13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7}, {1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12}, {7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15}, {13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9}, {10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4}, {3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14}, {2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9}, {14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6}, {4, 2, 1, 11, 10, 13, 7, 8,15, 9, 12, 5, 6, 3, 0, 14}, {11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3}, {12, 1, 10, 15, 9, 2, 6, 8,0, 13, 3, 4, 14, 7, 5, 11}, {10, 15, 4, 2, 7, 12, 9, 5,6, 1, 13, 14, 0, 11, 3, 8}, {9, 14, 15, 5, 2, 8, 12, 3,7, 0, 4, 10, 1, 13, 11, 6}, {4, 3, 2, 12, 9, 5, 15, 10,11, 14, 1, 7, 6, 0, 8, 13}, {4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1}, {13, 0, 11, 7, 4, 9, 1, 10,14, 3, 5, 12, 2, 15, 8, 6}, {1, 4, 11, 13, 12, 3, 7, 14,10, 15, 6, 8, 0, 5, 9, 2}, {6, 11, 13, 8, 1, 4, 10, 7,9, 5, 0, 15, 14, 2, 3, 12}, {13, 2, 8, 4, 6, 15, 11, 1,10, 9, 3, 14, 5, 0, 12, 7}, {1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2}, {7, 11, 4, 1, 9, 12, 14, 2,0, 6, 10, 13, 15, 3, 5, 8}, {2, 1, 14, 7, 4, 10, 8, 13,15, 12, 9, 0, 3, 5, 6, 11} }; #endregion DES Tables ///////////////////////////////////////////////////////////// #region Static Operations - DES public static bool IsValidDESKey(byte[] Key) { // Shortcuts if (Key == null) return false; if (Key.Length != KEY_BYTE_LENGTH) return false; if (!IsStrongDESKey(Key)) return false; // Make sure end bits have odd parity for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) { // Add bits for this byte int iTotalBits = 0; byte Mask = 1; for (int iBitOffset = 0; iBitOffset < BITS_PER_BYTE; iBitOffset++) { if ((Key[iByteOffset] & Mask) != 0) iTotalBits++; Mask <<= 1; } // If the total bits is not odd... if ((iTotalBits % 2) != 1) return false; } // Return success return true; } public static bool IsStrongDESKey(byte[] Key) { // Compare by large integer UInt64 uiKey = BitConverter.ToUInt64(Key,0); // Find weak keys... if( (uiKey == 0x0000000000000000) || (uiKey == 0x00000000FFFFFFFF) || (uiKey == 0xE0E0E0E0F1F1F1F1) || (uiKey == 0x1F1F1F1F0E0E0E0E) ) return false; // Find semi-weak keys... if( (uiKey == 0x011F011F010E010E) || (uiKey == 0x1F011F010E010E01) || (uiKey == 0x01E001E001F101F1) || (uiKey == 0xE001E001F101F101) || (uiKey == 0x01FE01FE01FE01FE) || (uiKey == 0xFE01FE01FE01FE01) || (uiKey == 0x1FE01FE00EF10EF1) || (uiKey == 0xE01FE01FF10EF10E) || (uiKey == 0x1FFE1FFE0EFE0EFE) || (uiKey == 0xFE1FFE1FFE0EFE0E) || (uiKey == 0xE0FEE0FEF1FEF1FE) || (uiKey == 0xFEE0FEE0FEF1FEF1) ) return false; // Return success return true; } public static byte[] CreateDesKey(Random rnd) { // Declare return variable byte[] Ftmp = new byte[KEY_BYTE_LENGTH]; // Fill with random data rnd.NextBytes(Ftmp); // Make the key good Ftmp = MakeGoodDesKey(Ftmp); #if DEBUG Debug.Assert(IsValidDESKey(Ftmp)); #endif // #if DEBUG // Call sibling function return Ftmp; } public static byte[] MakeGoodDesKey(byte[] KeyIn) { #if DEBUG Debug.Assert(KeyIn != null); Debug.Assert(KeyIn.Length == KEY_BYTE_LENGTH); #endif // #if DEBUG // Declare return variable byte[] Ftmp = new byte[KEY_BYTE_LENGTH]; // Loop until key is good int iInc = 0; while (true) { // Start with the key data Array.Copy(KeyIn, Ftmp, KEY_BYTE_LENGTH); // Add the increment _incKey(Ftmp, iInc); // Make sure end bits have odd parity _modifyKeyParity(Ftmp); // If key is valid... if (IsValidDESKey(Ftmp)) break; // Move on iInc++; } // while-loop // Return variable return Ftmp; } public static void DES(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt) { // Shortcuts if (!IsValidDESKey(Key)) throw new Exception("Invalid DES key."); // Create the output buffer _createBufferOut(bufferIn.Length, ref bufferOut, bEncrypt); // Expand the keys into Kn KEY_SET[] Kn = new KEY_SET[1] { _expandKey(Key, 0) }; // Apply DES keys _desAlgorithm(bufferIn, ref bufferOut, Kn, bEncrypt); // If decrypting... if (!bEncrypt) _removePadding(ref bufferOut); } #endregion Static Operations - DES ///////////////////////////////////////////////////////////// #region Static Operations - TripleDES public static bool IsValidTripleDESKey(byte[] Key) { // Shortcuts if (Key == null) return false; if (Key.Length != (3*KEY_BYTE_LENGTH)) return false; // Check each part of the key byte[] SubKey = new byte[KEY_BYTE_LENGTH]; for( int iKeyLoop=0; iKeyLoop<3; iKeyLoop++ ) { // Get sub-key Array.Copy( Key, iKeyLoop*8, SubKey, 0, KEY_BYTE_LENGTH ); // Check this DES key if(!IsValidDESKey(SubKey)) return false; } // Keys must not be equal bool bAEqualsB = true; bool bAEqualsC = true; bool bBEqualsC = true; for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) { if (Key[iByteOffset] != Key[iByteOffset + KEY_BYTE_LENGTH]) bAEqualsB = false; if (Key[iByteOffset] != Key[iByteOffset + KEY_BYTE_LENGTH + KEY_BYTE_LENGTH]) bAEqualsC = false; if (Key[iByteOffset + KEY_BYTE_LENGTH] != Key[iByteOffset + KEY_BYTE_LENGTH + KEY_BYTE_LENGTH]) bBEqualsC = false; } if ((bAEqualsB) || (bAEqualsC) || (bBEqualsC)) return false; // Return success return true; } public static byte[] CreateTripleDesKey(Random rnd) { // Declare return variable byte[] Ftmp = new byte[KEY_BYTE_LENGTH * 3]; // Fill with random data rnd.NextBytes(Ftmp); // Make the key good Ftmp = MakeGoodTripleDesKey(Ftmp); // Check key is valid #if DEBUG Debug.Assert(IsValidTripleDESKey(Ftmp)); #endif // #if DEBUG // Call sibling function return Ftmp; } public static byte[] MakeGoodTripleDesKey(byte[] KeyIn) { // Declare return variable byte[] Ftmp = new byte[KEY_BYTE_LENGTH * 3]; // Declaration of local variables int iKey = 0; byte[] SubKey = new byte[KEY_BYTE_LENGTH]; // Loop through key modifications int iInc = 0; while (true) { // Start with the key Array.Copy(KeyIn, Ftmp, KEY_BYTE_LENGTH * 3); // Make sure each part of the key is valid for (iKey = 0; iKey < 3; iKey++) { // Get the sub-key Array.Copy(Ftmp, iKey * KEY_BYTE_LENGTH, SubKey, 0, KEY_BYTE_LENGTH); // Increment sub-key _incKey(SubKey, iInc * (iKey + 1)); // Make the parity valid _modifyKeyParity(SubKey); // Return to the Ftmp Array.Copy(SubKey, 0, Ftmp, iKey * KEY_BYTE_LENGTH, KEY_BYTE_LENGTH); } // Check the key if (IsValidTripleDESKey(Ftmp)) break; // Move on iInc++; } // while-loop // Return variable return Ftmp; } public static void TripleDES(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt) { // Shortcuts if (!IsValidTripleDESKey(Key)) throw new Exception("Invalid DES key."); // Create the output buffer _createBufferOut(bufferIn.Length, ref bufferOut, bEncrypt); // Expand the keys into Kn KEY_SET[] Kn = new KEY_SET[3] { _expandKey(Key, 0), _expandKey(Key, 8), _expandKey(Key, 16) }; // Apply DES keys _desAlgorithm(bufferIn, ref bufferOut, Kn, bEncrypt); // If decrypting... if (!bEncrypt) _removePadding(ref bufferOut); } #endregion Static Operations - TripleDES ///////////////////////////////////////////////////////////// #region Static Operations private static void _incKey(byte[] Key, int iInc) { #if DEBUG Debug.Assert(Key.Length == KEY_BYTE_LENGTH); #endif // #if DEBUG // shortcuts if (iInc == 0) return; // Add the increment int iCarry = iInc; for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) { int iTemp = Key[iByteOffset] + iCarry; iCarry = iTemp >> 8; Key[iByteOffset] = Convert.ToByte(iTemp & 0xFF); if (iCarry == 0) break; } } private static void _modifyKeyParity(byte[] Key) { #if DEBUG Debug.Assert(Key.Length == KEY_BYTE_LENGTH); #endif // #if DEBUG // Make sure end bits have odd parity for (int iByteOffset = 0; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) { // Add bits for this byte int iTotalBits = 0; byte Mask = 1; for (int iBitOffset = 0; iBitOffset < BITS_PER_BYTE; iBitOffset++) { if ((Key[iByteOffset] & Mask) != 0) iTotalBits++; Mask <<= 1; } // If the total bits is not odd... if ((iTotalBits % 2) != 1) { // Flip the first bit to retain odd parity Key[iByteOffset] ^= 0x01; } } } private static KEY_SET _expandKey(byte[] Key, int iOffset) { // // Expand an 8 byte DES key into a set of permuted keys // // Declare return variable KEY_SET Ftmp = new KEY_SET(); // Declaration of local variables int iTableOffset, iArrayOffset, iPermOffset, iByteOffset, iBitOffset; bool bBit; // Put key into an 8-bit block BLOCK8BYTE K = new BLOCK8BYTE(); K.Set(Key, iOffset); // Permutate Kp with PC1 BLOCK8BYTE Kp = new BLOCK8BYTE(); for (iArrayOffset = 0; iArrayOffset < bytePC1.Length; iArrayOffset++) { // Get permute offset iPermOffset = bytePC1[iArrayOffset]; iPermOffset--; // Get and set bit Kp.SetBit( _bitAddressToByteOffset(iArrayOffset, 7), _bitAddressToBitOffset(iArrayOffset, 7), K.GetBit( _bitAddressToByteOffset(iPermOffset, 8), _bitAddressToBitOffset(iPermOffset, 8) ) ); } // Create 17 blocks of C and D from Kp BLOCK8BYTE[] KpCn = new BLOCK8BYTE[17]; BLOCK8BYTE[] KpDn = new BLOCK8BYTE[17]; for (iArrayOffset = 0; iArrayOffset < 17; iArrayOffset++) { KpCn[iArrayOffset] = new BLOCK8BYTE(); KpDn[iArrayOffset] = new BLOCK8BYTE(); } for (iArrayOffset = 0; iArrayOffset < 32; iArrayOffset++) { // Set bit in KpCn iByteOffset = _bitAddressToByteOffset(iArrayOffset, 8); iBitOffset = _bitAddressToBitOffset(iArrayOffset, 8); bBit = Kp.GetBit(iByteOffset, iBitOffset); KpCn[0].SetBit(iByteOffset, iBitOffset, bBit); // Set bit in KpDn bBit = Kp.GetBit(iByteOffset + 4, iBitOffset); KpDn[0].SetBit(iByteOffset, iBitOffset, bBit); } for (iArrayOffset = 1; iArrayOffset < 17; iArrayOffset++) { // Shift left wrapped KpCn[iArrayOffset].ShiftLeftWrapped(KpCn[iArrayOffset - 1], byteShifts[iArrayOffset - 1]); KpDn[iArrayOffset].ShiftLeftWrapped(KpDn[iArrayOffset - 1], byteShifts[iArrayOffset - 1]); } // Create 17 keys Kn for (iArrayOffset = 0; iArrayOffset < 17; iArrayOffset++) { // Loop through the bits for (iTableOffset = 0; iTableOffset < 48; iTableOffset++) { // Get address if bit iPermOffset = bytePC2[iTableOffset]; iPermOffset--; // Convert to byte and bit offsets iByteOffset = _bitAddressToByteOffset(iPermOffset, 7); iBitOffset = _bitAddressToBitOffset(iPermOffset, 7); // Get bit if (iByteOffset < 4) bBit = KpCn[iArrayOffset].GetBit(iByteOffset, iBitOffset); else bBit = KpDn[iArrayOffset].GetBit(iByteOffset - 4, iBitOffset); // Set bit iByteOffset = _bitAddressToByteOffset(iTableOffset, 6); iBitOffset = _bitAddressToBitOffset(iTableOffset, 6); Ftmp.GetAt(iArrayOffset).SetBit(iByteOffset, iBitOffset, bBit); } } // Return variable return Ftmp; } private static void _createBufferOut( int iBufferInLength, ref byte[] bufferOut, bool bEncrypt ) { // // Create a buffer for the output, which may be trimmed later // // If encrypting... int iOutputLength; if (bEncrypt) { if ((iBufferInLength % KEY_BYTE_LENGTH) != 0) iOutputLength = ((iBufferInLength / KEY_BYTE_LENGTH) + 1) * KEY_BYTE_LENGTH; else iOutputLength = iBufferInLength + KEY_BYTE_LENGTH; } else { if (iBufferInLength < 8) throw new Exception("DES cypher-text must be at least 8 bytes."); if ((iBufferInLength % 8) != 0) throw new Exception("DES cypher-text must be a factor of 8 bytes in length."); iOutputLength = iBufferInLength; } // Create buffer if ((bufferOut == null) || (bufferOut.Length != iOutputLength)) bufferOut = new byte[iOutputLength]; else Array.Clear(bufferOut, 0, bufferOut.Length); } private static void _removePadding(ref byte[] bufferOut) { // // Remove the padding after decrypting // // Get the padding... byte Padding = bufferOut[bufferOut.Length - 1]; if ((Padding == 0) || (Padding > 8)) throw new Exception("Invalid padding on DES data."); // Confirm padding bool bPaddingOk = true; for (int iByteOffset = 1; iByteOffset < Padding; iByteOffset++) { if (bufferOut[bufferOut.Length - 1 - iByteOffset] != Padding) { bPaddingOk = false; break; } } if (bPaddingOk) { // Chop off the padding Array.Resize(ref bufferOut, bufferOut.Length - Padding); } else throw new Exception("Invalid padding on DES data."); } private static void _desAlgorithm(byte[] bufferIn, ref byte[] bufferOut, KEY_SET[] KeySets, bool bEncrypt) { // // Apply the DES algorithm to each block // // Declare a workset set of variables WORKING_SET workingSet = new WORKING_SET(); // encode/decode blocks int iBufferPos = 0; while (true) { // Check buffer position if (bEncrypt) { // If end of buffer... if (iBufferPos >= bufferOut.Length) break; // Calulate remaining bytes int iRemainder = (bufferIn.Length - iBufferPos); if (iRemainder >= 8) workingSet.DataBlockIn.Set(bufferIn, iBufferPos); else { // Copy part-block workingSet.DataBlockIn.Reset(); if (iRemainder > 0) Array.Copy(bufferIn, iBufferPos, workingSet.DataBlockIn.m_data, 0, iRemainder); // Get the padding byte byte Padding = Convert.ToByte(KEY_BYTE_LENGTH - iRemainder); // Add padding to block for (int iByteOffset = iRemainder; iByteOffset < KEY_BYTE_LENGTH; iByteOffset++) workingSet.DataBlockIn.m_data[iByteOffset] = Padding; } } else { // If end of buffer... if (iBufferPos >= bufferIn.Length) break; // Get the next block workingSet.DataBlockIn.Set(bufferIn, iBufferPos); } // if encrypting and not the first block... if ((bEncrypt) && (iBufferPos > 0)) { // Apply succession => XOR M with previous block workingSet.DataBlockIn.Xor(workingSet.DataBlockOut, workingSet.DataBlockIn); } // Apply the algorithm workingSet.DataBlockOut.Set(workingSet.DataBlockIn); _lowLevel_desAlgorithm(workingSet, KeySets, bEncrypt); // If decrypting... if (!bEncrypt) { // Retain the succession if (iBufferPos > 0) workingSet.DataBlockOut.Xor(workingSet.DecryptXorBlock, workingSet.DataBlockOut); // Retain the last block workingSet.DecryptXorBlock.Set(workingSet.DataBlockIn); } // Update buffer out Array.Copy(workingSet.DataBlockOut.m_data, 0, bufferOut, iBufferPos, 8); // Move on iBufferPos += 8; } // Scrub the working set workingSet.Scrub(); } private static void _lowLevel_desAlgorithm(WORKING_SET workingSet, KEY_SET[] KeySets, bool bEncrypt) { // // Apply 1 or 3 keys to a block of data // // Declaration of local variables int iTableOffset; int iArrayOffset; int iPermOffset; int iByteOffset; int iBitOffset; // Loop through keys for (int iKeySetOffset = 0; iKeySetOffset < KeySets.Length; iKeySetOffset++) { // Permute with byteIP workingSet.IP.Reset(); for (iTableOffset = 0; iTableOffset < byteIP.Length; iTableOffset++) { // Get perm offset iPermOffset = byteIP[iTableOffset]; iPermOffset--; // Get and set bit workingSet.IP.SetBit( _bitAddressToByteOffset(iTableOffset, 8), _bitAddressToBitOffset(iTableOffset, 8), workingSet.DataBlockOut.GetBit( _bitAddressToByteOffset(iPermOffset, 8), _bitAddressToBitOffset(iPermOffset, 8) ) ); } // Create Ln[0] and Rn[0] workingSet.Ln[0].Reset(); workingSet.Rn[0].Reset(); for (iArrayOffset = 0; iArrayOffset < 32; iArrayOffset++) { iByteOffset = _bitAddressToByteOffset(iArrayOffset, 8); iBitOffset = _bitAddressToBitOffset(iArrayOffset, 8); workingSet.Ln[0].SetBit(iByteOffset, iBitOffset, workingSet.IP.GetBit(iByteOffset, iBitOffset)); workingSet.Rn[0].SetBit(iByteOffset, iBitOffset, workingSet.IP.GetBit(iByteOffset + 4, iBitOffset)); } // Loop through 17 interations for (int iBlockOffset = 1; iBlockOffset < 17; iBlockOffset++) { // Get the array offset int iKeyOffset; if (bEncrypt != (iKeySetOffset == 1)) iKeyOffset = iBlockOffset; else iKeyOffset = 17 - iBlockOffset; // Set Ln[N] = Rn[N-1] workingSet.Ln[iBlockOffset].Set(workingSet.Rn[iBlockOffset - 1]); // Set Rn[N] = Ln[0] + f(R[N-1],K[N]) for (iTableOffset = 0; iTableOffset < byteE.Length; iTableOffset++) { // Get perm offset iPermOffset = byteE[iTableOffset]; iPermOffset--; // Get and set bit workingSet.RnExpand.SetBit( _bitAddressToByteOffset(iTableOffset, 6), _bitAddressToBitOffset(iTableOffset, 6), workingSet.Rn[iBlockOffset - 1].GetBit( _bitAddressToByteOffset(iPermOffset, 8), _bitAddressToBitOffset(iPermOffset, 8) ) ); } // XOR expanded block with K-block if (bEncrypt != (iKeySetOffset == 1)) workingSet.XorBlock.Xor(workingSet.RnExpand, KeySets[iKeySetOffset].GetAt(iKeyOffset)); else workingSet.XorBlock.Xor(workingSet.RnExpand, KeySets[KeySets.Length - 1 - iKeySetOffset].GetAt(iKeyOffset)); // Set S-Box values workingSet.SBoxValues.Reset(); for (iTableOffset = 0; iTableOffset < 8; iTableOffset++) { // Calculate m and n int m = ((workingSet.XorBlock.GetBit(iTableOffset, 7) ? 1 : 0) << 1) | (workingSet.XorBlock.GetBit(iTableOffset, 2) ? 1 : 0); int n = (workingSet.XorBlock.m_data[iTableOffset] >> 3) & 0x0F; // Get s-box value iPermOffset = byteSBox[(iTableOffset * 4) + m, n]; workingSet.SBoxValues.m_data[iTableOffset] = (byte)(iPermOffset << 4); } // Permute with P -> f workingSet.f.Reset(); for (iTableOffset = 0; iTableOffset < byteP.Length; iTableOffset++) { // Get perm offset iPermOffset = byteP[iTableOffset]; iPermOffset--; // Get and set bit workingSet.f.SetBit( _bitAddressToByteOffset(iTableOffset, 4), _bitAddressToBitOffset(iTableOffset, 4), workingSet.SBoxValues.GetBit( _bitAddressToByteOffset(iPermOffset, 4), _bitAddressToBitOffset(iPermOffset, 4) ) ); } // Rn[N] = Ln[N-1] ^ f workingSet.Rn[iBlockOffset].Reset(); for (iTableOffset = 0; iTableOffset < 8; iTableOffset++) { // Get Ln[N-1] -> A byte A = workingSet.Ln[iBlockOffset - 1].m_data[(iTableOffset >> 1)]; if ((iTableOffset % 2) == 0) A >>= 4; else A &= 0x0F; // Get f -> B byte B = Convert.ToByte(workingSet.f.m_data[iTableOffset] >> 4); // Update Rn[N] if ((iTableOffset % 2) == 0) workingSet.Rn[iBlockOffset].m_data[iTableOffset >> 1] |= Convert.ToByte((A ^ B) << 4); else workingSet.Rn[iBlockOffset].m_data[iTableOffset >> 1] |= Convert.ToByte(A ^ B); } } // X = R16 L16 workingSet.X.Reset(); for (iTableOffset = 0; iTableOffset < 4; iTableOffset++) { workingSet.X.m_data[iTableOffset] = workingSet.Rn[16].m_data[iTableOffset]; workingSet.X.m_data[iTableOffset + 4] = workingSet.Ln[16].m_data[iTableOffset]; } // C = X perm IP workingSet.DataBlockOut.Reset(); for (iTableOffset = 0; iTableOffset < byteRFP.Length; iTableOffset++) { // Get perm offset iPermOffset = byteRFP[iTableOffset]; iPermOffset--; // Get and set bit workingSet.DataBlockOut.SetBit( _bitAddressToByteOffset(iTableOffset, 8), _bitAddressToBitOffset(iTableOffset, 8), workingSet.X.GetBit( _bitAddressToByteOffset(iPermOffset, 8), _bitAddressToBitOffset(iPermOffset, 8) ) ); } } // key loop } #endregion Static Operations ///////////////////////////////////////////////////////////// // Helper Operations private static int _bitAddressToByteOffset(int iTableAddress, int iTableWidth) { int iFtmp = iTableAddress / iTableWidth; return iFtmp; } private static int _bitAddressToBitOffset(int iTableAddress, int iTableWidth) { int iFtmp = BITS_PER_BYTE - 1 - (iTableAddress % iTableWidth); return iFtmp; } ///////////////////////////////////////////////////////////// #region Debug Operations #if DEBUG #if !SILVERLIGHT private static void MicrosoftDESEncrypt(byte[] bufferIn, ref byte[] bufferOut, byte[] Key, bool bEncrypt, bool bDESMode) { // Declaration of key and IV byte[] bufferTemp = new byte[1024]; byte[] IV; if(bDESMode) IV = new byte[8]; else IV = new byte[8*3]; // Declare a crypto object ICryptoTransform crypto; if (bDESMode) { DESCryptoServiceProvider des = new DESCryptoServiceProvider(); des.Padding = PaddingMode.PKCS7; if (bEncrypt) crypto = des.CreateEncryptor(Key, IV); else crypto = des.CreateDecryptor(Key, IV); } else { TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider(); tripleDes.Padding = PaddingMode.PKCS7; if (bEncrypt) crypto = tripleDes.CreateEncryptor(Key, IV); else crypto = tripleDes.CreateDecryptor(Key, IV); } // a memory stream for the cyrpto using(MemoryStream ms = new MemoryStream()) { // Create a CryptoStream using the memory stream using (CryptoStream encStream = new CryptoStream(ms, crypto, CryptoStreamMode.Write)) { // Encrypt/decrypt and flush encStream.Write(bufferIn, 0, bufferIn.Length); encStream.Flush(); encStream.FlushFinalBlock(); encStream.Close(); // Get the data into a buffer bufferOut = ms.ToArray(); } } } #endif // #if !SILVERLIGHT #endif // #if DEBUG #if DEBUG #if !SILVERLIGHT public static void _assertBufferMatch(byte[] A, byte[] B) { // Compare outputs Debug.Assert(A.Length == B.Length); for (int iOffset = 0; iOffset < A.Length; iOffset++) Debug.Assert(A[iOffset] == B[iOffset]); } #endif // #if !SILVERLIGHT #endif // #if DEBUG #if DEBUG #if !SILVERLIGHT public static void Test() { // // This function encrypts and encrypts data using our DES algorithm, and // ensures the results are the same as the Microsoft algorithm with default padding settings. // // Declaration of local variables Random rnd = new Random(1); byte[] DesKey, Des3Key; byte[] plainText = null, cypherText = null, plainText2 = null, msCypherText = null, msPlainText2 = null; // Compare the DES algorithm with the Microsoft algorithm for (int iTest = 0; iTest < 100*1000; iTest++) { // Dump progress if ((iTest % 200) == 0) Trace.TraceInformation("Test {0}", iTest); // Generate test data DesKey = DESCrytography.CreateDesKey(rnd); Des3Key = DESCrytography.CreateTripleDesKey(rnd); int iLength = rnd.Next(0, 256); if ((plainText == null) || (plainText.Length != iLength)) plainText = new byte[iLength]; rnd.NextBytes(plainText); // DES Test { // Encrypt using our algorithm DESCrytography.DES(plainText, ref cypherText, DesKey, true); // Decrypt using our algorithm DESCrytography.DES(cypherText, ref plainText2, DesKey, false); // Compare outputs _assertBufferMatch(plainText,plainText2); // Encrypt using Microsoft algorithm MicrosoftDESEncrypt(plainText, ref msCypherText, DesKey, true, true); // Decrypt using Microsoft algorithm MicrosoftDESEncrypt(msCypherText, ref msPlainText2, DesKey, false, true); // Compare outputs _assertBufferMatch(plainText, msPlainText2); // Make sure Microsoft and our algorithms are the same _assertBufferMatch(cypherText, msCypherText); } // TripleDES Test { // Encrypt using our algorithm DESCrytography.TripleDES(plainText, ref cypherText, Des3Key, true); // Decrypt using our algorithm DESCrytography.TripleDES(cypherText, ref plainText2, Des3Key, false); // Compare outputs _assertBufferMatch(plainText, plainText2); // Encrypt using Microsoft algorithm MicrosoftDESEncrypt(plainText, ref msCypherText, Des3Key, true, false); // Decrypt using Microsoft algorithm MicrosoftDESEncrypt(msCypherText, ref msPlainText2, Des3Key, false, false); // Compare outputs _assertBufferMatch(plainText, msPlainText2); // Make sure Microsoft and our algorithms are the same _assertBufferMatch(cypherText, msCypherText); } } // for-loop } #endif // #if !SILVERLIGHT #endif // #if DEBUG #endregion Debug Operations } }