// Transform an input block into an output block. public static int TransformBlock(CryptoAPITransform transform, byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { int blockSize = transform.blockSize; byte[] iv = transform.iv; IntPtr state = transform.state; int offset = outputOffset; byte[] tempBuffer = transform.tempBuffer; int tempSize = transform.tempSize; int index; // Process all of the data in the input. We need to keep // the last two blocks for the finalization process. while (inputCount >= blockSize) { // If the temporary buffer is full, then flush a block // through the cipher in CBC mode. if (tempSize > blockSize) { // Decrypt the ciphertext block and XOR with the IV. CryptoMethods.Decrypt(state, tempBuffer, blockSize, tempBuffer, 0); for (index = blockSize - 1; index >= 0; --index) { tempBuffer[index] ^= iv[index]; } // Copy the original ciphertext to the IV. Array.Copy(tempBuffer, blockSize, iv, 0, blockSize); // Copy the plaintext into place. Array.Copy(tempBuffer, 0, outputBuffer, offset, blockSize); // Advance to the next output block. offset += blockSize; // Shift the second block down to the first position. Array.Copy(tempBuffer, blockSize * 2, tempBuffer, blockSize, blockSize); tempSize -= blockSize; } // Copy the next block into the temporary buffer. Array.Copy(inputBuffer, inputOffset, tempBuffer, tempSize + blockSize, blockSize); inputOffset += blockSize; inputCount -= blockSize; tempSize += blockSize; } transform.tempSize = tempSize; // Finished. return(offset - outputOffset); }
// Transform an input block into an output block. public static int TransformBlock(CryptoAPITransform transform, byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { int blockSize = transform.blockSize; byte[] iv = transform.iv; IntPtr state = transform.state; byte[] tempBuffer = transform.tempBuffer; int offset = outputOffset; int index; bool needPadding = (transform.padding != PaddingMode.None); // Process a left-over block from last time. if (transform.tempSize > 0 && inputCount > 0) { // Decrypt the ciphertext to get the plaintext. CryptoMethods.Decrypt(state, tempBuffer, 0, outputBuffer, offset); // Advance to the next block and clear the temporary block. offset += blockSize; transform.tempSize = 0; for (index = blockSize - 1; index >= 0; --index) { tempBuffer[index] = (byte)0x00; } } // Process all of the blocks in the input, minus one. // If we don't need padding, then process all of the blocks. while ((needPadding && inputCount > blockSize) || (!needPadding && inputCount >= blockSize)) { // Decrypt the ciphertext to get the plaintext. CryptoMethods.Decrypt(state, inputBuffer, inputOffset, outputBuffer, offset); // Advance to the next block. inputOffset += blockSize; inputCount -= blockSize; offset += blockSize; } // Save the last block for next time. if (needPadding && inputCount > 0) { Array.Copy(inputBuffer, inputOffset, tempBuffer, 0, inputCount); transform.tempSize = inputCount; } // Finished. return(offset - outputOffset); }
// Transform the final input block. public static byte[] TransformFinalBlock(CryptoAPITransform transform, byte[] inputBuffer, int inputOffset, int inputCount) { int blockSize = transform.blockSize; byte[] iv = transform.iv; IntPtr state = transform.state; int offset; byte[] tempBuffer = transform.tempBuffer; byte[] outputBuffer; int tempSize; int index; // Allocate the output buffer. outputBuffer = new byte [inputCount + transform.tempSize]; // Process as many full blocks as possible. index = inputCount - (inputCount % blockSize); offset = TransformBlock(transform, inputBuffer, inputOffset, index, outputBuffer, 0); inputOffset += index; inputCount -= index; // Flush the first block if we need the extra space. tempSize = transform.tempSize; if (tempSize > blockSize && inputCount > 0) { // Decrypt the ciphertext block and XOR with the IV. CryptoMethods.Decrypt(state, tempBuffer, blockSize, tempBuffer, 0); for (index = blockSize - 1; index >= 0; --index) { tempBuffer[index] ^= iv[index]; } // Copy the original ciphertext to the IV. Array.Copy(tempBuffer, blockSize, iv, 0, blockSize); // Copy the plaintext into place. Array.Copy(tempBuffer, 0, outputBuffer, offset, blockSize); // Advance to the next output block. offset += blockSize; // Shift the second block down to the first position. Array.Copy(tempBuffer, blockSize * 2, tempBuffer, blockSize, blockSize); tempSize -= blockSize; } // Copy the remainder of the data into the temporary buffer. Array.Copy(inputBuffer, inputOffset, tempBuffer, tempSize + blockSize, inputCount); tempSize += inputCount; // "Applied Cryptography" describes Cipher Text Stealing // as taking two blocks to generate the short end-point. // If we less than one block, then use CFB instead. if (tempSize < blockSize) { // Decrypt the single block in CFB mode. CryptoMethods.Encrypt(transform.state2, iv, 0, iv, 0); for (index = 0; index < tempSize; ++index) { outputBuffer[offset + index] = (byte)(iv[index] ^ tempBuffer[index + blockSize]); } } else { // Decrypt the second last ciphertext block. CryptoMethods.Decrypt(state, tempBuffer, blockSize, tempBuffer, blockSize); // Rebuild the ciphertext for the last block. for (index = inputCount; index < blockSize; ++index) { tempBuffer[blockSize * 2 + index] = tempBuffer[blockSize + index]; } // Get the last plaintext block from the second // last ciphertext block. for (index = inputCount - 1; index >= 0; --index) { outputBuffer[offset + blockSize + index] = (byte)(tempBuffer[blockSize + index] ^ tempBuffer[blockSize * 2 + index]); } // Decrypt the last ciphertext block that we rebuilt. CryptoMethods.Decrypt(state, tempBuffer, blockSize * 2, tempBuffer, 0); // XOR the block with the IV to get the second // last plaintext block. for (index = blockSize - 1; index >= 0; --index) { outputBuffer[offset + index] = (byte)(iv[index] ^ tempBuffer[index]); } } // Finished. return(outputBuffer); }
// Transform the final input block. public static byte[] TransformFinalBlock(CryptoAPITransform transform, byte[] inputBuffer, int inputOffset, int inputCount) { int blockSize = transform.blockSize; IntPtr state = transform.state; byte[] tempBuffer = transform.tempBuffer; byte[] outputBuffer; int offset, index, pad; // Allocate a temporary output buffer. outputBuffer = new byte [inputCount + blockSize]; // Push the remaining bytes through the decryptor. The // final block will end up in "transform.tempBuffer". offset = TransformBlock(transform, inputBuffer, inputOffset, inputCount, outputBuffer, 0); // Decrypt the final block in "tempBuffer". if (transform.tempSize > 0) { // Decrypt the ciphertext to get the plaintext. CryptoMethods.Decrypt(state, tempBuffer, 0, tempBuffer, 0); // Remove padding. if (transform.padding == PaddingMode.PKCS7) { // Use PKCS #7 padding. pad = tempBuffer[blockSize - 1]; if (pad == 0 || pad > blockSize) { pad = blockSize; } Array.Copy(tempBuffer, 0, outputBuffer, offset, blockSize - pad); offset += blockSize - pad; pad = 0; } else if (transform.padding == PaddingMode.Zeros) { // Strip zeroes from the end of the block. index = blockSize; while (index > 0 && tempBuffer[index - 1] == 0) { --index; } Array.Copy(tempBuffer, 0, outputBuffer, offset, index); offset += index; } else { // No padding, so return the whole block. Array.Copy(tempBuffer, 0, outputBuffer, offset, blockSize); offset += blockSize; } } // Reduce the output buffer size to the final length. if (offset != outputBuffer.Length) { byte[] newout = new byte [offset]; if (offset != 0) { Array.Copy(outputBuffer, 0, newout, 0, offset); } Array.Clear(outputBuffer, 0, outputBuffer.Length); outputBuffer = newout; } // Finished. return(outputBuffer); }
// Transform an input block into an output block. public static int TransformBlock(CryptoAPITransform transform, byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) { int blockSize = transform.blockSize; byte[] iv = transform.iv; IntPtr state = transform.state; byte[] tempBuffer = transform.tempBuffer; int offset = outputOffset; int index; // Process a left-over block from last time. if (transform.tempSize > 0 && inputCount > 0) { // Decrypt the ciphertext to get the plaintext // xor'ed with the IV. CryptoMethods.Decrypt(state, tempBuffer, blockSize, tempBuffer, 0); // XOR the IV with the temporary buffer to get plaintext. for (index = blockSize - 1; index >= 0; --index) { outputBuffer[offset + index] = (byte)(iv[index] ^ tempBuffer[index]); } // Copy the original ciphertext to the IV. Array.Copy(tempBuffer, blockSize, iv, 0, blockSize); // Advance to the next block and clear the temporary block. offset += blockSize; transform.tempSize = 0; for (index = 2 * blockSize - 1; index >= blockSize; --index) { tempBuffer[index] = (byte)0x00; } } // Process all of the blocks in the input, minus one. while (inputCount > blockSize) { // Decrypt the ciphertext to get the plaintext // xor'ed with the IV. CryptoMethods.Decrypt(state, inputBuffer, inputOffset, tempBuffer, 0); // XOR the IV with the temporary buffer to get plaintext. for (index = blockSize - 1; index >= 0; --index) { outputBuffer[offset + index] = (byte)(iv[index] ^ tempBuffer[index]); } // Copy the original ciphertext to the IV. Array.Copy(inputBuffer, inputOffset, iv, 0, blockSize); // Advance to the next block. inputOffset += blockSize; inputCount -= blockSize; offset += blockSize; } // Save the last block for next time. if (inputCount > 0) { Array.Copy(inputBuffer, inputOffset, tempBuffer, blockSize, inputCount); transform.tempSize = inputCount; } // Clear the temporary buffer to protect sensitive data. for (index = blockSize - 1; index >= 0; --index) { tempBuffer[index] = (byte)0x00; } // Finished. return(offset - outputOffset); }