[fpc-pascal] TBlowfishDecryptStream flaw

Jorge Aldo G. de F. Junior jagfj80 at gmail.com
Sun Mar 6 14:02:05 CET 2011


Check those two programs

======= Program A =====

Program A;

Uses
	Classes,
	BlowFish;

Var
	lSource,
	lIntermediate : TMemoryStream;
	lEncrypter : TBlowfishEncryptStream;


Begin
	lSource := TMemoryStream.Create;
	lSource.LoadFromFile('test7.pas');
	WriteLn(lSource.Size);

	lIntermediate := TMemoryStream.Create;	
	lEncrypter := TBlowfishEncryptStream.Create('123', lIntermediate);
	lEncrypter.CopyFrom(lSource, lSource.Size);
	lEncrypter.Free;
	
	WriteLn(lIntermediate.Size);
	
	// SendStreamViaSocket(lIntermediate); { Lets pretend this function
automatically checks the size of the stream }
	
	lIntermediate.Free;
	lSource.Free;

End.

====== EOF =====

====== Program B =====

Program B;

Uses
	Classes,
	Blowfish;

Var
	lIntermediate,
	lDestination : TMemoryStream;
	lDecrypter : TBlowfishDecryptStream;
	
Begin

	lIntermediate := TMemoryStream.Create;
	lDestination := TMemoryStream.Create;
	
	// ReceiveStreamViaSocket(lIntermediate);  { Lets pretend this
function can check the socket for the size of the last UDP packet }

	WriteLn(lIntermediate.Size);
	lIntermediate.Seek(0, soFromBeginning);

	lDecrypter := TBlowfishDecryptStream.Create('123', lIntermediate);
	lDestination.CopyFrom(lDecrypter, lIntermediate.Size); { Read how much ??? }
	lDecrypter.Free;
	
	WriteLn(lDestination.Size);
	
	lDestination.SaveToFile('test7.txt');
	
	lSource.Free;
	lIntermediate.Free;
	lDestination.Free;
End.

===== EOF =====

The problem consists that, if lIntermediate.Size is not equal to
lSource.Size, i will not know how much bytes to "copyfrom" from the
decrypter.

For one, if a 10 bytes lSource generates a 20 bytes lIntermediate, and
no clear rule show what are the mathematical relationship of those
sizes,
then asking 20 bytes (as lIntermediate.Size will show) will yield
wrong results (Worse if the result at lIntermediate is smaller than
the original lSource.Size).

But, if TBlowfishDecryptStream worked backwards (the create parameters
being the key and the DESTINATION stream, not the source) then this
would makes sense.

The problem ends up to be that by having a source parameter instead of
a destination paramater this makes you have to read out bytes from the
decrypter (you push out instead of pushing in), while
at the same time you cannot know how many bytes you really have to
read by just looking at the decrypter current state.

Thats a set of mutually exclusive assumptions.

Now this :

===== Program C ====

Program B;

Uses
	Classes,
	Blowfish;

Var
	lIntermediate,
	lDestination : TMemoryStream;
	lDecrypter : TBlowfishDecryptStream;
	
Begin

	lIntermediate := TMemoryStream.Create;
	lDestination := TMemoryStream.Create;
	
	// ReceiveStreamViaSocket(lIntermediate);  { Lets pretend this
function can check the socket for the size of the last UDP packet }

	WriteLn(lIntermediate.Size);
	lIntermediate.Seek(0, soFromBeginning);

	lDecrypter := TBlowfishDecryptStream.Create('123', lDestination);
	lDecrypter.CopyFrom(lIntermediate, lIntermediate.Size);
	lDecrypter.Free;
	
	WriteLn(lDestination.Size);
	
	lDestination.SaveToFile('test7.txt');
	
	lSource.Free;
	lIntermediate.Free;
	lDestination.Free;
End.

=== EOF ===

This would work better, cause now you just need to know how much bytes
to push into the decrypter, something that is readily available at the
lIntermediate stream.

While i bet that the blowfish algorithm has an input to output size
relationship of one to one (as most stream cyphers), this is not a
good choice for other algorithms wich could yield different output
sizes for a given input...

2011/3/6 Michael Van Canneyt <michael at freepascal.org>:
>
>
> On Sat, 5 Mar 2011, Jorge Aldo G. de F. Junior wrote:
>
>> i have a problem with TBlowfishDecryptStream :
>
> [snip]
>>
>> The problem now is that it eats chars at the end of the generated
>> lBuffer (tested by taking a tmemorystream thru tblowfishencryptstream,
>> then taking the result to this tblowfishdecryptstream, than back to a
>> tmemorystream and comparing the results, the original tmemorystream
>> shows to be bigger than the resulting tmemorystream)...
>>
>> Basically i need :
>>
>> 1 - That TBlowfishEncryptStream guarantees that the same ammount of
>> bytes feed to it, ends up at the output of it
>
> That should be the case. See below for an explanation why you get different
> results.
>
>> 2 - That TBlowfishDecryptStream simply allows us to use Size property
>> (Well, sometimes i receive a tmemorystream in an unrelated part of the
>> code so i cannot read the original size the buffer had before
>> encrypting)...
>
> This is impossible by definition.
>
> You should always design your algorithms so they don't need size.
> Compression and encryption streams cannot have size, since they do not know
> the size of the input data.
>
> There are only 3 streams that have guaranteed size:
> - Memory stream
> - File stream
> - String stream (a variant of memory stream)
> All other streams do not have size.
>
> Your second mistake is that the output of the BlowFish encryption stream is
> only fully known when it is destroyed. You should FIRST destroy the blowfish
> encryptor/decryptor stream. Only then, all characters that are in the
> internal encryption buffer are written to the output buffer.
>
> Alternatively, call Flush() to write the remains of the internal buffer to
> the output buffer. Note that after a call to flush(), no more data can be
> written to the stream, because the output will then be wrong.
>
> Michael.
> _______________________________________________
> fpc-pascal maillist  -  fpc-pascal at lists.freepascal.org
> http://lists.freepascal.org/mailman/listinfo/fpc-pascal
>



More information about the fpc-pascal mailing list