public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { Validate.AnArray(inputBuffer, inputOffset, inputCount); // AONT the message byte[] messageAONT = moAONT.Encode(inputBuffer, inputOffset, inputCount); // Get MAC of the output of the AONT byte[] macMessageAONT = moMAC.ComputeHash(messageAONT); // Encrypt the head of the AONT message using the Symmetric Cipher moCipher.BlockSize = macMessageAONT.Length * 8; moCipher.IV = macMessageAONT; byte[] cipheredHeadOfMessageAONT = moCipher.CreateEncryptor().TransformFinalBlock(messageAONT, 0, mKey.Length); // Replace the head of the AONT message with the encrypted head Buffer.BlockCopy(cipheredHeadOfMessageAONT, 0, messageAONT, 0, cipheredHeadOfMessageAONT.Length); // Copy the MAC and ciphered data to the ciphertext byte[] ciphertext = new byte[macMessageAONT.Length + messageAONT.Length]; Buffer.BlockCopy(macMessageAONT, 0, ciphertext, 0, macMessageAONT.Length); Buffer.BlockCopy(messageAONT, 0, ciphertext, macMessageAONT.Length, messageAONT.Length); return(ciphertext); }
public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { #region Validation Validate.AnArray(inputBuffer, inputOffset, inputCount); #endregion // Set the padding to always be 1 byte byte[] padding = new byte[] { 1 }; // Increase the padding if the input if AONT message length isn't enough to create 1 byte parts if (moAONT.outputLengthForInputLength(inputCount) < (2 * miTotalPairsCount)) { int paddingBytes = (2 * miTotalPairsCount) - moAONT.outputLengthForInputLength(inputCount); if (paddingBytes > 255) { throw new Exception("Unable to pad message as parameters have caused more than 255 padding bytes to be required"); } padding = new byte[paddingBytes]; for (int i = 0; i < paddingBytes; i++) { padding[i] = (byte)paddingBytes; } } // If the message length is odd, then set a flag to copy the last byte of AONT(m) straight to the ciphertext bool bMsgLengthOdd = false; if ((inputCount + padding.Length) % 2 != 0) { bMsgLengthOdd = true; } // Copy the message + padding byte[] message = new byte[inputCount + padding.Length]; Buffer.BlockCopy(inputBuffer, inputOffset, message, 0, inputCount); Buffer.BlockCopy(padding, 0, message, inputCount, padding.Length); // All Or Nothing Transform the message byte[] encodedMessage = moAONT.Encode(message); //string hex = BitConverter.ToString(encodedMessage); //Console.WriteLine("AONT Message: " + hex.Replace("-", "") + " (length = " + encodedMessage.Length + ")"); // For an odd length message we will copy the last byte straight to the ciphertext, so for the purpose of processing we ignore this byte int encodedMessageLength = bMsgLengthOdd ? encodedMessage.Length - 1 : encodedMessage.Length; // Determine the size of each message part (except for perhaps the last int partSize = getMaxPartSize(encodedMessageLength, 2 * miTotalPairsCount); // Generate the MAC seed byte[] seedMAC = new byte[miTotalPairsCount / 8]; (new RNGCryptoServiceProvider()).GetBytes(seedMAC); // Allocate space for a buffer to calculate the HMAC on byte[] bufferHMAC = new byte[seedMAC.Length + 1 + 2 * partSize]; // We will HMAC the HMAC seed, the index and the 2 parts // Copy the HMAC seed to the HMAC buffer Buffer.BlockCopy(seedMAC, 0, bufferHMAC, 0, seedMAC.Length); // Allocate space for the MAC bits byte[] bitsMAC = new byte[miTotalPairsCount / 8]; // Allocate space for the ciphertext = Length of Encrypt-then MAC + HMAC seed + MAC bits + encoded message length byte[] ciphertext = new byte[moMAC.HashSize / 8 + seedMAC.Length + bitsMAC.Length + encodedMessageLength + (bMsgLengthOdd?1:0)]; // For an odd length message copy the last encoded byte directly to the ciphertext if (bMsgLengthOdd) { ciphertext[ciphertext.Length - 1] = encodedMessage[encodedMessage.Length - 1]; } // Note where to write the swapped parts int ciphertextOffset = moMAC.HashSize / 8 + seedMAC.Length + bitsMAC.Length; // Offset into the encodedMessage int offset = 0; for (int i = 0; i < miTotalPairsCount; i++) { bufferHMAC[seedMAC.Length] = (byte)i; // Make a copy of the 2 parts in the correct order Buffer.BlockCopy(encodedMessage, offset, bufferHMAC, seedMAC.Length + 1, 2 * partSize); // Calculate the MAC of the 2 parts in the correct order byte[] oMACUnswapped = moMAC.ComputeHash(bufferHMAC); // Make a copy of the 2 parts in the swapped order Buffer.BlockCopy(encodedMessage, offset + partSize, bufferHMAC, seedMAC.Length + 1, partSize); Buffer.BlockCopy(encodedMessage, offset, bufferHMAC, seedMAC.Length + 1 + partSize, partSize); // Calculate the MAC of the 2 parts in the swapped order byte[] oMACSwapped = moMAC.ComputeHash(bufferHMAC); // Find the location of the first bit difference of the MACs bool found = false; for (byte j = 0; j < oMACUnswapped.Length; j++) { if (oMACSwapped[j] != oMACUnswapped[j]) { for (byte k = 0; k < 8; k++) { Boolean bUnSwappedBitSet = ((oMACUnswapped[j] & ((byte)1 << k)) > 0); Boolean bSwappedBitSet = ((oMACSwapped[j] & ((byte)1 << k)) > 0); if (bUnSwappedBitSet != bSwappedBitSet) { // Record the value of the bit from the unswapped MAC byte bitValue = bUnSwappedBitSet ? (byte)1 : (byte)0; bitsMAC[i / 8] |= (byte)(bitValue << (7 - (i % 8))); found = true; break; } } } if (found) { break; } } if (!found) { // Either the parts were identical, or we found a hash collision(!). We allow this case under the assumption that it won't happen // very often and that although it reduces the overall security, it won't reduce it much. } // Copy to ciphertext Buffer.BlockCopy(moRandOrd.Swap(encodedMessage, offset, 2 * partSize), 0, ciphertext, ciphertextOffset, 2 * partSize); ciphertextOffset += (2 * partSize); offset += (2 * partSize); partSize = getNextPartSize(encodedMessageLength, offset, (i + 1) * 2, 2 * miTotalPairsCount); // If the partSize changes we need to alter the size of bufferHMAC if (bufferHMAC.Length != (seedMAC.Length + 1 + 2 * partSize)) { bufferHMAC = new byte[seedMAC.Length + 1 + 2 * partSize]; } } // Prepend the HMAC seed Buffer.BlockCopy(seedMAC, 0, ciphertext, moMAC.HashSize / 8, seedMAC.Length); // Prepend the bits from the MACs Buffer.BlockCopy(bitsMAC, 0, ciphertext, moMAC.HashSize / 8 + seedMAC.Length, bitsMAC.Length); // Prepend Encrypt-then-MAC construction to the beginning of the ciphertext byte[] EtM = moMAC.ComputeHash(ciphertext, moMAC.HashSize / 8, ciphertext.Length - (moMAC.HashSize / 8)); Buffer.BlockCopy(EtM, 0, ciphertext, 0, EtM.Length); return(ciphertext); }