title: DCPCrypt (Delphi) + ZLib / "data error" when decompressing decrypted data
body: <p>I designed a system that stores blocks of data, sent compressed and encrypted by multiple clients. </p>
  
  <p>When trying to fetch stored data blocks (that I need to decompress and decrypt) on a specific computer, a ZLib "data error" exception is raised during the data decompression. It seems clear that the stream has not been properly decrypted so the input stream is not a valid ZLib stream, leading to a such issue. </p>
  
  <p>After some research I discovered that for an identical block of source data that is also stored, this problematic block contained totaly different data, so it's quite clear that there is an issue in the encryption or the compression algorithm. </p>
  
  <p>However, the fact that I cannot easily reproduce the problem means that if there is an error it's something far from being obvious. So I'm posting the code here, hoping someone will spot something I didn't see.</p>
  
  <p>First, the data block <code>AStream</code> is compressed :</p>
  
  <pre><code>function CompressStream(AStream: TMemoryStream;
    ACompressionLevel:
    TCompressionLevel): Boolean;
  var
    LTempStream: TMemoryStream;
    LCompressedStream: TCompressionStream;
  begin
    LTempStream := TMemoryStream.create;
    try
      try
        AStream.Seek(0, soBeginning);
        LCompressedStream := TCompressionStream.Create(ACompressionLevel, LTempStream);
        try
          try
            LCompressedStream.CopyFrom(AStream, AStream.Size);
          finally
            LCompressedStream.free;
          end;
        finally
          AStream.Clear;
          AStream.CopyFrom(LTempStream, 0);
          AStream.Seek(0, soBeginning);
          Result := True;
        end;
      finally
        LTempStream.free;
      end;
    except
      Result := False;
    end;
  end;
  </code></pre>
  
  <p>Then, it's encrypted :</p>
  
  <pre><code>function EncryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
  var
    LModifiedStream: TMemoryStream;
    LRijndaelCipher: TDCP_rijndael;
  begin
     try
       LRijndaelCipher := TDCP_rijndael.Create(nil);
       LModifiedStream := TMemoryStream.Create;
       InitCipherWithKey(LRijndaelCipher, AParameters);
       try
          AStream.Seek(0, soBeginning);
          LRijndaelCipher.EncryptStream(AStream, LModifiedStream, AStream.Size);
          TMemoryStream(AStream).Clear;
          AStream.CopyFrom(LModifiedStream, 0);
          Result := True;
       finally
          LRijndaelCipher.Burn;
          LRijndaelCipher.Free;
          LModifiedStream.Free;
       end;
     except
       Result := False;
     end;
  end;
  </code></pre>
  
  <p>...and stored somewhere.</p>
  
  <p>When fetching a data block, it's first decrypted :</p>
  
  <pre><code>function DecryptStream(AStream: TMemoryStream; const AParameters: AnsiString): Boolean;
  var
    LModifiedStream: TMemoryStream;
    LRijndaelCipher: TDCP_rijndael;
  begin
     try
       LRijndaelCipher := TDCP_rijndael.Create(nil);
       LModifiedStream := TMemoryStream.Create;
       InitCipherWithKey(LRijndaelCipher, AParameters);
       try
          AStream.Seek(0, soBeginning);
          LRijndaelCipher.DecryptStream(AStream, LModifiedStream, AStream.Size);
          TMemoryStream(AStream).Clear;
          AStream.CopyFrom(LModifiedStream, 0);
          Result := True;
       finally
          LRijndaelCipher.Burn;
          LRijndaelCipher.Free;
          LModifiedStream.Free;
       end;
     except
       Result := False;
     end;
  end;
  </code></pre>
  
  <p>Then uncompressed :</p>
  
  <pre><code>function DecompressStream(AStream: TMemoryStream): Boolean;
  var
    LTempStream: TMemoryStream;
    LCompressedStream: TDecompressionStream;
  begin
    LTempStream := TMemoryStream.create;
    try
      try
        AStream.Seek(0, soBeginning);
        LCompressedStream := TDecompressionStream.Create(AStream);
        try
          try
            LTempStream.CopyFrom(LCompressedStream, LCompressedStream.size);
          finally
            LCompressedStream.Free;
          end;
        finally
          AStream.Clear;
          AStream.CopyFrom(LTempStream, 0);
          AStream.Seek(0, soBeginning);
          Result := True;
        end;
      finally
        LTempStream.free;
      end;
    except
      Result := False;
    end;
  end;
  </code></pre>
  
  <p>The cipher is initialized using the <code>InitCipherWithKey()</code> method. It is designed to convert a MD5 hash into it's binary representation, contained in the <code>LMD5Hash</code> variable (yes, the array is 64 bytes long but only the first 16 will be used by the cipher, since I call <code>Init()</code> with the 128 value (which means a 128-bit/16 bytes key length) : </p>
  
  <pre><code>procedure InitCipherWithKey(ACipher: TDCP_cipher; const AKey: AnsiString);
  var
    LMD5Hash: array [0..63] of Byte;
    S: AnsiString;
  begin
    //We use a 128 bit key
    ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
    S := AKey;
    HexToBin(PAnsiChar(S), LMD5Hash, Length(LMD5Hash) -1);
    ACipher.Init(LMD5Hash[0], 128, nil);
    ZeroMemory(@LMD5Hash, SizeOf(LMD5Hash));
  end;
  </code></pre>
  
  <p>Thanks in advance.</p>