Length of encrypted output under 3DES in CBC cipher mode

S

Sathyaish

I have the following scenario:

Algorithm: 3DES
Cipher Mode: CBC
Key Size: 128-bit
Block Size: 64 bit
IV: 0x0000000000000000 (an eight byte array of zeros)

The results I get using .NET with the following routine are:

1. EncryptUnicode: takes a unicode string in plain text and encrypts
it, returning a unicode string.

2. DecryptUnicode: takes an encrypted unicode string as input and
decrypts it, returning plain text in a unicode string.

3. EncryptBase64: takes a plain text in a unicode string and encrypts
it, returning a base64 string.

4. DecryptBase64: takes a base64 string and decrypts it, returns a
plain text unicode string.


The source code of the routines is given at the end of this post.

I am using C#. When I take a plain text such as "hello" and put it
through EncryptBase64, and then put the output of EncryptBase64 to
DecryptBase64, I get back the plain old string, for e.g "hello". So,
this part works ok.

However, when I do the same with the other set of methods, i.e
EncryptUnicode and DecryptUnicode, it doesn't work. The call to
EncryptUnicode gives me back some string. I assume that that is the
encrypted string. When I call DecryptUnicode with that string, it gives
me an exception saying, "Bad Data".

So, my first question is:

1. Does 3DES encryption in CBC mode work only in base64 strings?

You might say, "One set of your methods work, i.e the base64 ones. Why
are you being so pedantic. Just go ahead and use them. If it works,
it's good." And you'd be right in saying that. Only, my problem is that
some other machine on another platform, probably, is going to do the
encryption using 3DES in CBC (we'll share a IV and key), and I'll have
to decrypt them.


2. My second question pertains to the length of the output in
encryption process. I found that if I give a string of a length that is
less than a multiple of eight, the encrypted output is of the length
that is equal to the the nearest, higher multiple of eight. This is
true for the EncryptUnicode method. I understand that this might be
because 3DES works with a block size of 64-bits, i.e works with data in
chunks of 64-bits, and thus because of chaining the block (the
initialization vector), it might require some memory.

We'll get to the base64 methods later. So, my second question is:

Is it not possible to give 3DES in CBC mode a X length string, where X
is a multiple of 8, and get back exactly X bytes in the output as the
encrypted string? My finding says it is NOT possible. However, the
other end which, in my case, is going to do the encryption says that,
for example, a 16-byte plain text that they are encrypting will spit
out a 16-byte encrypted output.

Here're my findings (please ignore the base64 part for now). Look at
the plain text length and the encrypted text lengths in the case of
Unicode string encryption.


Enter plain text string [Press Q to quit]: a
______________________________________________

Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 1 8
Base64 1 12
--------------------------------------------------------------------------------



Enter plain text string [Press Q to quit]: ab
______________________________________________

Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 2 8
Base64 2 12
--------------------------------------------------------------------------------



Enter plain text string [Press Q to quit]: abcdefg
_____________________________________________
Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 7 8
Base64 7 12
--------------------------------------------------------------------------------



Enter plain text string [Press Q to quit]: abcdefgh
______________________________________________

Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 8 16
Base64 8 24
--------------------------------------------------------------------------------



Enter plain text string [Press Q to quit]: abcdefghijklmno
______________________________________________

Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 15 16
Base64 15 24
--------------------------------------------------------------------------------



Enter plain text string [Press Q to quit]: abcdefghijklmnop
______________________________________________

Encoding Length (PT) Length (ET)
--------------------------------------------------------------------------------

Unicode 16 24
Base64 16 32
--------------------------------------------------------------------------------





CODE FOR THE FOUR METHODS MENTIONED ABOVE

public static string EncryptUnicode(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;

ICryptoTransform c = p.CreateEncryptor();
byte[] bClear = asc.GetBytes(s);
byte[] bEncrypted = c.TransformFinalBlock(bClear, 0, bClear.Length);
return asc.GetString(bEncrypted);
}

public static string DecryptUnicode(string s, byte[] bkey, byte[]
bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateDecryptor();
byte[] bEncrypted = asc.GetBytes(s);
byte[] bClear = c.TransformFinalBlock(bEncrypted, 0,
bEncrypted.Length);
return asc.GetString(bClear);
}

public static string EncryptBase64(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;

ICryptoTransform c = p.CreateEncryptor();
byte[] bClear = asc.GetBytes(s);
byte[] bEncrypted = c.TransformFinalBlock(bClear, 0, bClear.Length);
return Convert.ToBase64String(bEncrypted);
}

public static string DecryptBase64(string s, byte[] bkey, byte[] bIV)
{
ASCIIEncoding asc = new ASCIIEncoding();
TripleDESCryptoServiceProvider p = new
TripleDESCryptoServiceProvider();
p.Mode = CipherMode.CBC;
p.Key = bkey;
p.IV = bIV;
ICryptoTransform c = p.CreateDecryptor();
byte[] bEncrypted = Convert.FromBase64String(s);
byte[] bClear = c.TransformFinalBlock(bEncrypted, 0,
bEncrypted.Length);
return asc.GetString(bClear);
}
 
J

Joseph Ashwood

I'm coming from the crypto side, I don't know the peculiarities of .NET.

Sathyaish said:
Algorithm: 3DES
Cipher Mode: CBC
The source code of the routines is given at the end of this post.

Honestly, save yourself a lot of time, and effort in debugging, add a
standard MAC to the end of the data, this will detect tampering far better
than formatting, and your routines will be format agnostic, they accept a
series of octets, they output a series of octets.

Additionally, you forget the important factor in answering the length of the
encrypted output, which method of termination is being used? I suspect this
is a big part of your problem.
1. Does 3DES encryption in CBC mode work only in base64 strings?

No. 3DES works on 64-bits of data packed into 64-bits of storage. Passing it
base64 encoded data will simply result in wasted space.
Is it not possible to give 3DES in CBC mode a X length string, where X
is a multiple of 8, and get back exactly X bytes in the output as the
encrypted string?

It is a matter of the termination method in use. Most likely it is using
PKCS-style termination which requires a minimum of 1 byte, and a maximum of
BLOCK_LENGTH (8-bytes in this case).
My finding says it is NOT possible. However, the
other end which, in my case, is going to do the encryption says that,
for example, a 16-byte plain text that they are encrypting will spit
out a 16-byte encrypted output.

You need to check your settings for termination, in this case a padding
method, your decryptor is not trimming the padding off like it should.
ASCIIEncoding asc = new ASCIIEncoding(); ....
byte[] bEncrypted = c.TransformFinalBlock(bClear, 0, bClear.Length);
return asc.GetString(bEncrypted);

That is most likely a problem. bEncrypted is a random series of bits, it
will not be ASCII compatible, and any semi-intelligent mapping to ASCII will
cause corruption in the value. In order to put it in an ASCII string you
will have to format it somehow.
Joe
 
Top