Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bouncy Castle CTS Mode for Blowfish Engine not working as expected

Perhaps my expectations are wrong. I am not an cryptography expert, I'm just a simple user. I have exhaustively tried to make this work with no success so far.

Background information:

I'm trying to port a Legacy Encryption from Delphi Encryption Compendium which is using Blowfish Engine (TCipher_Blowfish_)with CTS operation mode (cmCTS). The private key is hashed by RipeMD256(THash_RipeMD256).

Problems:

The input plain text array of bytes needs to be the same size of CIPHER_BLOCK. As far as I can tell it shouldn't.

From Wikipedia:

In cryptography, ciphertext stealing (CTS) is a general method of using a block cipher mode of operation that allows for processing of messages that are not evenly divisible into blocks without resulting in any expansion of the ciphertext, at the cost of slightly increased complexity.

The output is not the same as the old routine:

I'm using:

  • Same IV
  • Same Password
  • Same input plain text

The legacy application is using ANSI String, the new one uses Unicode, so for every input string I've called Encoding.ASCII.GetBytes("plainText"), Encoding.ASCII.GetBytes("privatepassword").

The private password bytes is then hashed by RipeMD256, I've checked the output bytes and they are the same.

I can confirm the problem is specific in the Bouncy Clastle (operation mode or missing configuration/step) because I've downloaded a second library Blowfish.cs and using an input of 8 bytes (same size as the cipher block) and using the Encrypt_CBC(bytes[]) with the same IV results in the same output as the legacy format.

This is the sketch of the code i'm using for both Blowfish.cs and Bouncy Castle:

Delphi Encryption Compendium

var 
  IV: Array [0..7] of Byte (1,2,3,4,5,6,7,8);
  Key: String = '12345678';
with TCipher_Blowfish.Create('', nil) do
begin
  try
    InitKey(Key, @IV); //Key is auto hashed using RIPE256 here;
    Result:= CodeString('12345678', paEncode, -1); //Output bytes is later encoded as MIME64 here, the result is the hash.
  finally
    Free;  
  end;
end;

Blofish.cs

var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
Blowfish b = new BlowFish(hashOfPrivateKey);

b.IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};

var input = Encoding.ASCII.GetBytes("12345678");
var output = b.Encrypt_CBC(input);

I assume that CTS and CBC will always have the same result if the input is 8 bits length. Is this just lucky/coincidence or is fundamentally truth?

Bouncy Castle

IBufferedCipher inCipher = CipherUtilities.GetCipher("BLOWFISH/CTS");
var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
var key = new KeyParameter(hashOfPrivateKey);
var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8};
var cipherParams = new ParametersWithIV(key, IV); 
inCipher.Init(true, cipherParams);
var input = Encoding.ASCII.GetBytes("12345678");

//try one: direct with DoFinal
var output = inCipher.DoFinal(input);
// output bytes different from expected

inCipher.Reset();

//try two: ProcessBytes then DoFinal
var outBytes = new byte[input.Length];
var res = inCipher.ProcessBytes(input, 0, input.Length, outBytes, 0);
var r = inCipher.DoFinal(outBytes, res);
// outBytes bytes different from expected

As I said, I'm comparing CBC with CTS based on the assumption that given a 8 bytes input, the output will be the same. I cannot forward the implementation with Bouncy Castle if even with the same input the output is not the same.

I Don't Know:

  • If the CTS Mode used in Delphi Encryption Compendium uses CBC along with CTS. I Couldn't find documented anywhere.
  • The difference between calling just DoFinal() and ProcessBytes() then DoFinal() in Bouncy Castle, I imagine that is required when the input block is larger than engine block size, in this case they are the same size.
  • If Delphi Encryption Compendium is correct/wrong or If Bouncy Castle is correct/wrong. I don't have enough knowledge in cryptography to understand the implementation, otherwise I wouldn't ask a question here (I need guidance).
like image 617
EProgrammerNotFound Avatar asked Feb 15 '17 12:02

EProgrammerNotFound


1 Answers

I assume that CTS and CBC will always have the same result if the input is 8 bits length. Is this just lucky/coincidence or is fundamentally truth?

No, this is a false statement.

Here is the quote from Wikipedia:

Ciphertext stealing for CBC mode doesn't necessarily require the plaintext to be longer than one block. In the case where the plaintext is one block long or less, the Initialization vector (IV) can act as the prior block of ciphertext.

So even for your case of 8-byte input, CTS algorithm comes into play and affects the output. Basically your statement about CTS and CBS equality could be reversed:

CTS and CBC will always have the same result up to last two blocks.

You could verify it with the following sample:

static byte[] EncryptData(byte[] input, string algorithm)
{
    IBufferedCipher inCipher = CipherUtilities.GetCipher(algorithm);
    var hashOfPrivateKey = HashValue(Encoding.ASCII.GetBytes("12345678"));
    var key = new KeyParameter(hashOfPrivateKey);
    var IV = new byte[8] { 1, 2, 3, 4, 5, 6, 7, 8 };
    var cipherParams = new ParametersWithIV(key, IV);
    inCipher.Init(true, cipherParams);

    return inCipher.DoFinal(input);
}

static void Main(string[] args)
{
    var data = Encoding.ASCII.GetBytes("0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF");
    var ctsResult = EncryptData(data, "BLOWFISH/CTS");
    var cbcResult = EncryptData(data, "BLOWFISH/CBC");
    var equalPartLength = data.Length - 2 * 8;
    var equal = ctsResult.Take(equalPartLength).SequenceEqual(cbcResult.Take(equalPartLength));
}

So this is basically the answer to your main question. You should not expect the same output for CTS and CBC on 8-byte input.

Here are answers (I hope) to your other questions:

If the CTS Mode used in Delphi Encryption Compendium uses CBC along with CTS. I Couldn't find documented anywhere.

I haven't found any documentation for CTS mode in Delphi Encryption Compendium either, however there are such comments in the source code:

cmCTSx = double CBC, with CFS8 padding of truncated final block

Modes cmCTSx, cmCFSx, cmCFS8 are proprietary modes developed by me. These modes works such as cmCBCx, cmCFBx, cmCFB8 but with double XOR'ing of the inputstream into Feedback register.

So seems like CTS mode is implemented in custom way in Delphi Encryption Compendium which will not be compatible with standard implementation by Bouncy Castle.

The difference between calling just DoFinal() and ProcessBytes() then DoFinal() in Bouncy Castle, I imagine that is required when the input block is larger than engine block size, in this case they are the same size.

Calls pair ProcessBytes() / DoFinal() is required if you encrypt data sequentially. It could be required for example if huge data is streamed. However if you have a routine that takes whole byte array for encryption, you could just call following convenient overload of DoFinal() once:

var encryptedData = inCipher.DoFinal(plainText);

This DoFinal() overload will calculate the size of output buffer and make required calls of ProcessBytes() and DoFinal() under the hood.

If Delphi Encryption Compendium is correct/wrong or If Bouncy Castle is correct/wrong. I don't have enough knowledge in cryptography to understand the implementation, otherwise I wouldn't ask a question here (I need guidance).

Let's sum up here:

  1. You should not expect the same output for CTS and CBC for 8-byte input.
  2. Seems like Delphi Encryption Compendium uses custom algorithm for CTS. Since Bouncy Castle is implemented according to the standards, these libraries will produce different results. If your new application is not required to support encrypted data produced with legacy Delphi application, then you could just use Bouncy Castle and be OK. In other case, you should use the same custom CTS algorithm that Delphi Encryption Compendium uses, which will require port of its sources to C#, unfortunately.

UPDATE

(More details on Delphi Encryption Compendium implementation in version 3.0)

Here is a CTS encoding code from DEC version 3.0:

S := @Source;
D := @Dest;

// ...

begin
    while DataSize >= FBufSize do
    begin
        XORBuffers(S, FFeedback, FBufSize, D);
        Encode(D);
        XORBuffers(D, FFeedback, FBufSize, FFeedback);
        Inc(S, FBufSize);
        Inc(D, FBufSize);
        Dec(DataSize, FBufSize);
    end;
    if DataSize > 0 then
    begin
        Move(FFeedback^, FBuffer^, FBufSize);
        Encode(FBuffer);
        XORBuffers(S, FBuffer, DataSize, D);
        XORBuffers(FBuffer, FFeedback, FBufSize, FFeedback);
    end;
end;

Here we see double XOR'ing that was mentioned in DEC documentation. Basically this code implements the following algorithm:

C[i] = Encrypt( P[i] xor F[i-1] )
F[i] = F[i-1] xor C[i]
F[0] = IV

while standard algorithm would be:

C[i] = Encrypt( P[i] xor C[i-1] )
C[0] = IV

The step F[i] = F[i-1] xor C[i] is DEC author invention and makes the encryption results differ. The handling of last two blocks that is crucial for CTS mode is also implemented not by a standard.

Here is a comment from DEC v 3.0 ReadMe.txt that describes why the author added such modification:

cmCTS Mode, XOR's the Data before and now after the encryption. This has better Securityeffect when using a InitVector, the Output is secure when a bad InitVector is used, ca 1% Speed lossed

It's a very common mistake when authors of security libraries try to make underlying algorithms 'more secure' by such naive modifications. In many cases such changes have a reverse effect and decrease protection strength. Another drawback of course is that encrypted data could not be decrypted by other libraries implemented according to the standard, like in your case.

like image 93
CodeFuller Avatar answered Nov 05 '22 20:11

CodeFuller