/// <summary> /// This method decrypts the specified data using the algorithm. This method should be used in checking the /// inputted password from the client, encrypted and passed in the authentication request packet. /// </summary> /// <param name="input">The inputted buffer to be decrypted.</param> public unsafe void Decrypt(byte[] input) { for (int index = 0; index < input.Length; index++) { // Is the algorithm done decrypting data? if (input[index] == 0) { fixed(byte *ptr = input) NativeFunctionCalls.memset(ptr + index, 0, input.Length - index); return; } // Decrypt the byte at the current offset: byte position = _keyBuffer[input[index] * 2]; if (position > 0x80) { // Entered using the "SHIFT" key: position = (byte)(_keyBuffer[input[index] * 2] - 0x80); input[index] = _decryptionSubstitutionBox[position]; } else { input[index] = _decryptionSubstitutionBox[position]; if (input[index] >= 0x41 && input[index] <= 90) { input[index] = (byte)(input[index] + 0x20); } } } }
/// <summary> /// This method generates keys for the algorithm. It should be used before the encryption and decryption /// methods are used to initialize the key vector. This method is automatically called by the constructor. /// </summary> /// <param name="initializationVector">The initialization vector used to generate keys.</param> public void GenerateKeys(byte[] initializationVector) { // Initialize Cipher Buffers: _keyBuffer = new uint[KEY_SIZE]; _substitutionBuffer = new uint[SUBSTITUTION_SIZE]; fixed(uint *keyBufferPtr = _keyBuffer) NativeFunctionCalls.memcpy((byte *)keyBufferPtr, initializationVector, KEY_SIZE * sizeof(uint)); // Generate the substitution box: _substitutionBuffer[0] = 0xB7E15163; for (int index = 1; index < SUBSTITUTION_SIZE; index++) { _substitutionBuffer[index] = _substitutionBuffer[index - 1] + 0x9E3779B9; } // Generate Key & Final Substitution Box: uint substitutionIndex = 0, keyIndex = 0, x = 0, y = 0; for (int loopControlIndex = 0; loopControlIndex < 3 * SUBSTITUTION_SIZE; loopControlIndex++) { _substitutionBuffer[substitutionIndex] = RotateLeft(_substitutionBuffer[substitutionIndex] + x + y, 3); x = _substitutionBuffer[substitutionIndex]; substitutionIndex = (substitutionIndex + 1) % SUBSTITUTION_SIZE; _keyBuffer[keyIndex] = RotateLeft(_keyBuffer[keyIndex] + x + y, (int)(x + y)); y = _keyBuffer[keyIndex]; keyIndex = (keyIndex + 1) % KEY_SIZE; } }
/// <summary> /// This function sends a packet to the client using the client's remote socket defined above. The /// packet is encrypted using the client's selected cipher algorithm and sent through the client's remote /// socket. If the server has a footer, it will write that footer to the end of the packet. /// </summary> /// <param name="packet">The packet being encrypted and sent to the client.</param> public void Send(byte[] packet) { try { lock (SendLock) // Locked to prevent two packets from being encrypted and sent at the same time. { // Add the footer to the end of the packet: if (Server.FooterLength > 0) fixed(byte *packetPtr = packet) NativeFunctionCalls.memcpy(packetPtr + packet.Length - 8, Server.Footer, Server.FooterLength); // Encrypt the packet and attempt to send it to the client: byte[] encryptedPacket = Cipher != null?Cipher.Encrypt(packet, packet.Length) : packet; Socket.Send(encryptedPacket); } } catch (SocketException e) { // Was the connection issue a problem on our side or the client's side? if (e.SocketErrorCode != SocketError.Disconnecting && e.SocketErrorCode != SocketError.NotConnected && e.SocketErrorCode != SocketError.ConnectionReset && e.SocketErrorCode != SocketError.ConnectionAborted && e.SocketErrorCode != SocketError.Shutdown) { Console.WriteLine(e); } } }
/// <summary> /// This method accepts the initialization key vector from the server's key exchange with the client and generates /// an encryption initialization vector, used in generating the key vector and in encrypting data from the server. /// </summary> /// <param name="iv">The encryption initialization vector from key exchange.</param> public void SetEncryptionIV(byte[] iv) { // Error check before creating the initialization vector: if (iv == null || iv.Length != BF_BLOCK_SIZE) { return; } // Copy the key exchange's generated iv to the encryption iv and byte swap: NativeFunctionCalls.memcpy(_encryptionIV, iv, BF_BLOCK_SIZE); _encryptionIncrementor = 0; }
/// <summary> /// Blowfish is a keyed, symmetric block cipher, designed in 1993 by Bruce Schneier and included in a large /// number of cipher suites and encryption products. Blowfish provides a good encryption rate in software and no /// effective cryptanalysis of it has been found to date. However, the Advanced Encryption Standard now receives /// more attention. Schneier designed Blowfish as a general-purpose algorithm, intended as an alternative to the /// aging DES and free of the problems and constraints associated with other algorithms. Schneier has stated that, /// "Blowfish is unpatented, and will remain so in all countries. The algorithm is hereby placed in the public /// domain, and can be freely used by anyone." The implementation type in use is CFB64. /// </summary> public BlowfishCipher() { _encryptionLock = new object(); _decryptionLock = new object(); _encryptionIncrementor = 0; _decryptionIncrementor = 0; _encryptionIV = (byte *)NativeFunctionCalls.malloc(BF_BLOCK_SIZE); _decryptionIV = (byte *)NativeFunctionCalls.malloc(BF_BLOCK_SIZE); NativeFunctionCalls.memset(_encryptionIV, 0, BF_BLOCK_SIZE); NativeFunctionCalls.memset(_decryptionIV, 0, BF_BLOCK_SIZE); KeySchedule(InitialKey); }