joelc
joelc

Reputation: 2761

3DES not producing consistent decryption for multiple iterations over same ciphertext

I've implemented a 3DES (TripleDES) decryption routine in my code and have noticed that it never produces the same cleartext when the supplied ciphertext is the same as the previous iteration. It seems non-deterministic and I'm sure it's something I'm doing wrong.

I've tried CBC (with a zeroed-out IV), ECB, multiple combinations of disposing and clearing. I cannot seem to get consistent output.

(for some reason the code formatting on the 'Code' and 'Output' below are not coming out right, sorry)

Code

using System;
using System.Security.Cryptography;
using System.Threading;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            while (true)
            {
                string key = "27F66D5244FF621EAA6F6120EDEB427F";
                string cipher = "C25C1D1197D31CAA87285D59A892047426D9182EC11353C051ADD6D0F072A6CB3436560B3071FC1FD11D9F7E74886742D9BEE0CFD1EA1064C213BB55278B2F12";

                Console.WriteLine("clear: " + byte_array_to_hex_string(
                    decrypt_3des(
                        hex_string_to_byte_array(cipher),
                        hex_string_to_byte_array(key)
                    ), true, true
                    ));

                Console.WriteLine("");
                Thread.Sleep(1000);
            }
        }

        static byte[] decrypt_3des(byte[] cipher, byte[] key)
        {
            if (cipher == null) return null;
            if (key == null) return null;
            int num_chunks = (cipher.Length) / 8;

            Console.WriteLine("Entering decrypt_3des");
            Console.WriteLine(" - cipher: " + byte_array_to_hex_string(cipher, true, true));
            Console.WriteLine(" - key: " + byte_array_to_hex_string(key, true, true));
            Console.WriteLine(" - cipher length: " + cipher.Length);
            Console.WriteLine(" - key length: " + key.Length);

            if ((cipher.Length % 8) != 0)
            {
                Console.WriteLine("cipher length not divisble by eight");
                return null;
            }

            if ((key.Length % 8) != 0)
            {
                Console.WriteLine("key length not divisible by eight");
                return null;
            }

            TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
            tdes.Key = key;
            tdes.Mode = CipherMode.ECB;
            tdes.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
            tdes.Padding = PaddingMode.Zeros;
            tdes.KeySize = (key.Length * 8);
            ICryptoTransform ct = tdes.CreateDecryptor();

            Console.WriteLine(" - IV: " + byte_array_to_hex_string(tdes.IV, true, true));
            byte[] clear = new byte[(cipher.Length)];
            clear = ct.TransformFinalBlock(cipher, 0, cipher.Length);

            Console.WriteLine(" - clear: " + byte_array_to_hex_string(clear, true, true));
            tdes.Clear();
            tdes.Dispose();
            ct.Dispose();
            return clear;
        }

        public static byte[] hex_string_to_byte_array(string hex)
        {
            int number_chars = hex.Length;
            byte[] bytes = new byte[number_chars / 2];
            for (int i = 0; i < number_chars; i += 2)
            {
                bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
            }
            return bytes;
        }

        public static string byte_array_to_hex_string(byte[] ba, bool remove_dashes, bool include_spaces)
        {
            string hex = BitConverter.ToString(ba);
            string hex_dashes = "";
            string hex_spaces = "";

            if (remove_dashes)
            {
                hex_dashes = hex.Replace("-", "");
            }
            else
            {
                return hex;
            }

            int hex_char_count = 0;
            if (include_spaces)
            {
                for (int i = (hex_dashes.Length - 1); i >= 0; i--)
                {
                    if (hex_char_count == 0)
                    {
                        hex_spaces += hex_dashes[i];
                        hex_char_count++;
                        continue;
                    }

                    if (hex_char_count % 16 == 0)
                    {
                        hex_spaces = (hex_dashes[i] + " " + hex_spaces);
                        hex_char_count++;
                        continue;
                    }

                    hex_spaces = hex_dashes[i] + hex_spaces;
                    hex_char_count++;
                }

                return hex_spaces;
            }
            else
            {
                return hex_dashes;
            }
        }
    }
}

Output

Entering decrypt_3des
 - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C
FD1EA1064 C213BB55278B2F12
 - key: 27F66D5244FF621E AA6F6120EDEB427F
 - cipher length: 64
 - key length: 16
 - IV: 10F99E2232BEC7F4
 - clear: BF3CBF923E6C0E19 DBBB64E0FCBBFBB9 4CA3D68F117BE51F 0C1294CEC85470B6 6A799CC5D914F427 30D0C47BC9E6340C BD8E4C2E
6E5819ED 23125066DBA83477
clear: BF3CBF923E6C0E19 DBBB64E0FCBBFBB9 4CA3D68F117BE51F 0C1294CEC85470B6 6A799CC5D914F427 30D0C47BC9E6340C BD8E4C2E6E5
819ED 23125066DBA83477

Entering decrypt_3des
 - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C
FD1EA1064 C213BB55278B2F12
 - key: 27F66D5244FF621E AA6F6120EDEB427F
 - cipher length: 64
 - key length: 16
 - IV: 32B9C802333CFBB4
 - clear: A878F23C3AF5E724 2ACB3458F8D68E68 9BE4F6C007F24FD2 9AF4CE98239A4F2C 5710633D88266AFD 77EF88A7B09485D9 F07C33D7
08832E90 77382A7A51A532C1
clear: A878F23C3AF5E724 2ACB3458F8D68E68 9BE4F6C007F24FD2 9AF4CE98239A4F2C 5710633D88266AFD 77EF88A7B09485D9 F07C33D7088
32E90 77382A7A51A532C1

Entering decrypt_3des
 - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C
FD1EA1064 C213BB55278B2F12
 - key: 27F66D5244FF621E AA6F6120EDEB427F
 - cipher length: 64
 - key length: 16
 - IV: 2FCB1A9F5B502E1B
 - clear: 80B7302520ACA111 223BF99421EFDA2C 60CABCB3C632A61C 3422552A07B582B7 E50E44E38DEDC300 DD9A6EB5CF3C0A63 E512FAC0
C04D3EC5 EEC551C3E845BF80
clear: 80B7302520ACA111 223BF99421EFDA2C 60CABCB3C632A61C 3422552A07B582B7 E50E44E38DEDC300 DD9A6EB5CF3C0A63 E512FAC0C04
D3EC5 EEC551C3E845BF80

Entering decrypt_3des
 - cipher: C25C1D1197D31CAA 87285D59A8920474 26D9182EC11353C0 51ADD6D0F072A6CB 3436560B3071FC1F D11D9F7E74886742 D9BEE0C
FD1EA1064 C213BB55278B2F12
 - key: 27F66D5244FF621E AA6F6120EDEB427F
 - cipher length: 64
 - key length: 16
 - IV: 355096FE3CA1E9A1
 - clear: E4EDCDC436745A51 42FE9D0C71933080 6DFCAACE07FBCC22 0F5C9AF9082C8264 870362982FF90CA4 665E312FC1A8D264 DDBB27B7
3B30840E 6030D791CD849A3D
clear: E4EDCDC436745A51 42FE9D0C71933080 6DFCAACE07FBCC22 0F5C9AF9082C8264 870362982FF90CA4 665E312FC1A8D264 DDBB27B73B3
0840E 6030D791CD849A3D

Upvotes: 2

Views: 562

Answers (2)

sga101
sga101

Reputation: 1904

Setting the key size results in a new random key being created. If you do this before you set the key and the IV, you will get consistent results.

Upvotes: 5

joelc
joelc

Reputation: 2761

I can't explain why, but replacing it with the code below worked. I'll dive into it more and if I figure it out I'll comment.

    static byte[] decrypt_3des(byte[] cipher, byte[] key)
    {
        byte[] clear;
        TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider();
        tdes.Key = key;
        tdes.Mode = CipherMode.CBC;
        tdes.Padding = PaddingMode.None;
        tdes.IV = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

        try
        {
            ICryptoTransform ic = tdes.CreateDecryptor();
            clear = ic.TransformFinalBlock(cipher, 0, cipher.Length);
        }
        finally
        {
            tdes.Clear();
        }
        return clear;
    }

Upvotes: 0

Related Questions