There is a problem with the default code page and streams
Original Reporter info from Mantis: RedOctober
-
Reporter name: Kevin Morris
Original Reporter info from Mantis: RedOctober
- Reporter name: Kevin Morris
Description:
The problem is that Lazarus is expecting codepage UTF8 but the DefaultCodePage in Windows is windows-1252.
In the Lazarus forum, I recieved enough help that we were able to determine that the problem was the default code page.
This problem affects EncodeStringBase64 and DecodeStringBase64 when either deals with anything other than characters a..A, z..Z and 0..9.
Once the Base64 messes up a string encode, then the subsequent actions you may want to do to the MIME string are messed up too, namely: BlowFish encryption.
Steps to reproduce:
See my example project. UPDATE: My example project is too big to be uploaded. Which is strange bc I was able to upload this exact project .zip to the Lazarus forum website. Anyway, see my example project in the "Additional Information memo field. Just below this paragraph, is the way I was told to cure this problem. Please note that I never enc_utf8.Free bc that causes a memory access error. I just leave it, so I hope my Lazarus application frees that memory when the app is closed. If it doesn't, then I've just created a memory leak.
uses
LazUTF8
...
// Global var
var
enc_utf8: TEncoding;
...
procedure SetDefaultEncoding; // I call this in my main form's FormCreate
begin
if not Assigned(enc_utf8) then
begin
TEncoding.FreeEncodings;
enc_utf8 := TMBCSEncoding.Create(DefaultSystemCodePage); // Do not manually enc_utf8.Free; It will be freed by another object.
end;
end;
Additional information:
.
{
Built with Lazarus 1.9 and Fpc 3.1.1
When clicking Button2 the error I get is:
[Debugger Exception Notification]
Project Base64_Blowfish_Stream_Bug raised exception class 'EReadError' with message:
Stream read error
At address 10003AFA1
[Ignore this exception type]
[Break] [Continue]
}
unit Unit1;
{$mode objfpc}{$H+}
interface
uses
LazUTF8, Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls,
Base64, Blowfish;
type
{ TForm1 }
TForm1 = class(TForm)
Button1: TButton;
Button2: TButton;
GroupBox1: TGroupBox;
GroupBox2: TGroupBox;
LabeledEdit1: TLabeledEdit;
LabeledEdit2: TLabeledEdit;
LabeledEdit3: TLabeledEdit;
LabeledEdit4: TLabeledEdit;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure FormClose(Sender: TObject; var CloseAction: TCloseAction);
private
mime: String;
in_s, out_s: String;
enc_utf8: TEncoding;
function EncryptItB64(const pwd, plain_text: String): String;
function DecryptItB64(const pwd, encrypted_str_b64: String; var decrypted_str: String): Boolean;
public
end;
const
master_pwd = '12345678';
var
Form1: TForm1;
implementation
{$R *.lfm}
{ TForm1 }
procedure TForm1.Button1Click(Sender: TObject);
begin
in_s := LabeledEdit1.Text;
mime := EncodeStringBase64(in_s);
ShowMessage('mime: ' + mime);
out_s := DecodeStringBase64(mime);
ShowMessage('out_s: ' + out_s);
LabeledEdit2.Text := out_s;
end;
procedure TForm1.FormClose(Sender: TObject; var CloseAction: TCloseAction);
begin
//if Assigned(enc) then
//enc.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
try
if not Assigned(enc_utf8) then
begin
TEncoding.FreeEncodings;
enc_utf8 := TMBCSEncoding.Create(DefaultSystemCodePage); // Do not manually enc_utf8.Free; It will be freed by another object.
end;
in_s := LabeledEdit3.Text;
mime := EncryptItB64(master_pwd, in_s);
ShowMessage('mime: ' + mime);
if DecryptItB64(master_pwd, mime, out_s) then
ShowMessage('out_s: ' + out_s)
else
ShowMessage('Nope');
LabeledEdit4.Text := out_s;
finally
end;
end;
function TForm1.EncryptItB64(const pwd, plain_text: String): String;
var
bfsh: TBlowFishEncryptStream;
strm: TStringStream;
byt_str: RawByteString;
begin
Result := '';
byt_str := '';
if Length(plain_text) = 0 then
Exit;
strm := nil;
bfsh := nil;
try
strm := TStringStream.Create('');
bfsh := TBlowfishEncryptStream.Create(pwd, strm);
bfsh.WriteAnsiString(plain_text);
bfsh.Flush;
strm.Seek(0, soFromBeginning);
byt_str := strm.Datastring;
Result := EncodeStringBase64(byt_str);
finally
bfsh.Free; // <--- MUST be freed in THIS ORDER!
strm.free;
end;
end;
function TForm1.DecryptItB64(const pwd, encrypted_str_b64: String; var decrypted_str: String): Boolean;
var
bfsh: TBlowFishDecryptStream;
strm: TStringStream;
byt_str: RawByteString;
begin
Result := False;
byt_str := '';
decrypted_str := '';
if Length(encrypted_str_b64) = 0 then
Exit;
strm := nil;
bfsh := nil;
try
try
byt_str := DecodeStringBase64(encrypted_str_b64);
strm := TStringStream.Create(byt_str);
strm.Seek(0, soFromBeginning);
bfsh := TBlowfishDecryptStream.Create(pwd, strm);
decrypted_str := bfsh.ReadAnsiString;
Result := True;
except
on E: Exception do
begin
decrypted_str := '';
Result := False;
end;
end;
finally
bfsh.Free; // <--- MUST be freed in THIS ORDER!
strm.Free;
end;
end;
end.