I am encrypting and decrypting a string in .NET, using ECB cipher mode and Threefish symmetric block cipher, whose implementation I've attached to my project as a .dll Here's the link to .NET implementation

Key size is equal to block size and is 256 bits in my case.

The thing is, as far as I understand, the length of input string, plaintext, must be equal to the length of ciphertext. Or must it? For example, in my case, considering ASCII encoding, plaintext is divided into blocks with each block containing 32 characters, but there always are 12 additional characters of ciphertext for each block, as I've figured out! That is, the length of ciphertext = the length of initial text + 12*n, where n is the number of blocks of text, that is str.Length/32 (str - initial string, already padded for being a multiple of 32).

Is there an error in my code (below) or is my understanding valid only for the case of very simple block cipher, using only XOR operation for encryption, while for complex .NET encryption system this rule doesn't fulfill? If the latter is the case, than please explain me what actually makes these lengths different!!! Thank you in advance.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using SkeinFish;
using System.Security.Cryptography;
namespace ComputerSecurity_Threefish
{
    class Program
    {
        static void Main(string[] args)
        {
            string plainText = inputProperString(), decryptedText, cipherText;
            Threefish th = new Threefish();
            th.GenerateIV();
            th.GenerateKey();
            cipherText = EncryptWithThreefish(plainText, th);
            Console.WriteLine("\nThis is how your encrypted string looks like:\n" + cipherText + "\n\nNow it will be decrypted...");
            Console.WriteLine(cipherText.Length);
            decryptedText = DecryptWithThreefish(cipherText, th);
            Console.WriteLine("\nAnd here is your initial string decrypted:\n" + decryptedText);
            Console.Read();
        }
        public static string inputProperString()
        {
            Console.Write("Enter a string for encryption: ");
            string str = Console.ReadLine();
            int remainder = str.Length % 32;
            if (remainder != 0)
            {
                Console.WriteLine("\nYour string's length is not a multiple of 32, which is the equivalent of Threefish-256 blocksize for the length of ASCII string. The string will be padded with spaces.");
                for (int i = 0; i < 32 - remainder; i++)
                    str += " ";
            }
            return str;
        }
        public static string EncryptWithThreefish(string plainText, Threefish th)
        {
            MemoryStream memoryStream = new MemoryStream();

            ICryptoTransform threefishEncryptor = th.CreateEncryptor();

            CryptoStream cryptoStream = new CryptoStream(memoryStream, threefishEncryptor, CryptoStreamMode.Write);

            byte[] plainBytes = Encoding.ASCII.GetBytes(plainText);

            cryptoStream.Write(plainBytes, 0, plainBytes.Length);

            cryptoStream.FlushFinalBlock();

            byte[] cipherBytes = memoryStream.ToArray();

            memoryStream.Close();

            cryptoStream.Close();

            return Convert.ToBase64String(cipherBytes, 0, cipherBytes.Length);
        }
        public static string DecryptWithThreefish(string cipherText, Threefish th)
        {
            MemoryStream memoryStream = new MemoryStream();

            ICryptoTransform threefishDecryptor = th.CreateDecryptor();

            CryptoStream cryptoStream = new CryptoStream(memoryStream, threefishDecryptor, CryptoStreamMode.Write);

            string decryptedText = String.Empty;

            try
            {
                byte[] cipherBytes = Convert.FromBase64String(cipherText);

                cryptoStream.Write(cipherBytes, 0, cipherBytes.Length);

                cryptoStream.FlushFinalBlock();

                byte[] plainBytes = memoryStream.ToArray();

                decryptedText = Encoding.ASCII.GetString(plainBytes, 0, plainBytes.Length);
            }
            finally
            {
                memoryStream.Close();
                cryptoStream.Close();
            }

            return decryptedText;
        }

    }
}
1

There are 1 answers

1
Maarten Bodewes On

If the plaintext is dividable by the block size, then then a full block of padding is added for ECB mode encryption, if PKCS#7 padding is used. The block size of Threefish is 32, 64 or 128 bytes and PKCS#7 padding is the default in .NET. However base 64 expands the result with about 1/3rd plus some possible rounding up, so 12 base 64 characters sounds about right.

A block cipher usually does not apply XOR of the plaintext for encryption. A stream cipher, or a block cipher using a streaming mode such as CTR does however. That kind of encryption does require an IV (or at least a nonce), otherwise it fails catastrophically if the key is ever reused.

That all said, you really should not use ECB mode encryption and Threefish has not been fully specified for encryption. I would suggest at least CBC mode encryption and AES. An authenticated cipher such as GCM or EAX would be even better.