doublecmd/src/ucryptproc.pas
2015-10-23 19:27:05 +00:00

243 lines
6.9 KiB
ObjectPascal

{
Double Commander
-------------------------------------------------------------------------
This unit contains Encrypt/Decrypt classes and functions.
Copyright (C) 2009 Koblov Alexander (Alexx2000@mail.ru)
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
}
unit uCryptProc;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, DCClassesUtf8;
type
{ TPasswordStore }
TPasswordStore = class(TIniFileEx)
private
FMasterKey,
FMasterKeyHash: AnsiString;
public
constructor Create(const AFileName: String); reintroduce;
public
function HasMasterKey: Boolean;
function CheckMasterKey: Boolean;
function WritePassword(Prefix, Name, Connection: String; const Password: AnsiString): Boolean;
function ReadPassword(Prefix, Name, Connection: String; out Password: AnsiString): Boolean;
function DeletePassword(Prefix, Name, Connection: String): Boolean;
end;
{ EEncryptDecryptFailed }
EEncryptDecryptFailed = class(Exception)
public
constructor Create; reintroduce;
end;
function Encode(MasterKey, Data: AnsiString): AnsiString;
function Decode(MasterKey, Data: AnsiString): AnsiString;
procedure InitPasswordStore;
var
PasswordStore: TPasswordStore = nil;
implementation
uses
LCLProc, LCLType, Base64, BlowFish, md5, uShowMsg, uGlobsPaths, uLng, uDebug;
type
TBlowFishKeyRec = record
dwSize: LongWord;
case Boolean of
True: (bBlowFishKey: TBlowFishKey);
False: (cBlowFishKey: array [0..SizeOf(TBlowFishKey)] of AnsiChar);
end;
function Encode(MasterKey, Data: AnsiString): AnsiString;
var
BlowFishKeyRec: TBlowFishKeyRec;
StringStream: TStringStream = nil;
Base64EncodingStream: TBase64EncodingStream = nil;
BlowFishEncryptStream: TBlowFishEncryptStream = nil;
begin
Result:= EmptyStr;
BlowFishKeyRec.cBlowFishKey:= MasterKey;
BlowFishKeyRec.dwSize:= Length(MasterKey);
try
StringStream:= TStringStream.Create(EmptyStr);
Base64EncodingStream:= TBase64EncodingStream.Create(StringStream);
BlowFishEncryptStream:= TBlowFishEncryptStream.Create(BlowFishKeyRec.bBlowFishKey, BlowFishKeyRec.dwSize, Base64EncodingStream);
BlowFishEncryptStream.Write(PAnsiChar(Data)^, Length(Data));
BlowFishEncryptStream.Flush;
Base64EncodingStream.Flush;
Result:= StringStream.DataString;
finally
FreeThenNil(BlowFishEncryptStream);
FreeThenNil(Base64EncodingStream);
FreeThenNil(StringStream);
end;
end;
function Decode(MasterKey, Data: AnsiString): AnsiString;
var
BlowFishKeyRec: TBlowFishKeyRec;
StringStream: TStringStream = nil;
Base64DecodingStream: TBase64DecodingStream = nil;
BlowFishDeCryptStream: TBlowFishDeCryptStream = nil;
begin
Result:= EmptyStr;
BlowFishKeyRec.cBlowFishKey:= MasterKey;
BlowFishKeyRec.dwSize:= Length(MasterKey);
try
StringStream:= TStringStream.Create(Data);
Base64DecodingStream:= TBase64DecodingStream.Create(StringStream);
SetLength(Result, Base64DecodingStream.Size);
BlowFishDeCryptStream:= TBlowFishDeCryptStream.Create(BlowFishKeyRec.bBlowFishKey, BlowFishKeyRec.dwSize, Base64DecodingStream);
BlowFishDeCryptStream.Read(PAnsiChar(Result)^, Base64DecodingStream.Size);
finally
FreeThenNil(BlowFishDeCryptStream);
FreeThenNil(Base64DecodingStream);
FreeThenNil(StringStream);
end;
end;
{ TPasswordStore }
constructor TPasswordStore.Create(const AFileName: String);
begin
inherited Create(AFileName);
CacheUpdates:= False;
if ReadOnly then DCDebug('Read only password store!');
FMasterKeyHash:= ReadString('General', 'MasterKey', EmptyStr);
end;
function TPasswordStore.HasMasterKey: Boolean;
begin
Result:= (Length(FMasterKey) <> 0);
end;
function TPasswordStore.CheckMasterKey: Boolean;
var
MasterKey,
MasterKeyHash: AnsiString;
begin
Result:= False;
if Length(FMasterKey) <> 0 then Exit(True);
if not ShowInputQuery(rsMsgMasterPassword, rsMsgMasterPasswordEnter, True, MasterKey) then
Exit;
if Length(MasterKey) = 0 then Exit;
MasterKeyHash:= MD5Print(MD5String(MasterKey));
MasterKeyHash:= Encode(MasterKey, MasterKeyHash);
if FMasterKeyHash = EmptyStr then
begin
FMasterKeyHash:= MasterKeyHash;
FMasterKey:= MasterKey;
WriteString('General', 'MasterKey', FMasterKeyHash);
Result:= True;
end
else if SameText(FMasterKeyHash, MasterKeyHash) then
begin
FMasterKey:= MasterKey;
Result:= True;
end
else
begin
ShowMessageBox('Wrong password!'#13'Please try again!', 'Error!', MB_OK or MB_ICONERROR);
end;
end;
function TPasswordStore.WritePassword(Prefix, Name, Connection: String;
const Password: AnsiString): Boolean;
var
Data: AnsiString;
begin
Result:= False;
if ReadOnly then Exit;
if CheckMasterKey = False then Exit;
Data:= Encode(FMasterKey, Password);
if Length(Data) = 0 then raise EEncryptDecryptFailed.Create;
try
WriteString(Prefix + '_' + Name, Connection, Data);
except
Exit;
end;
Result:= True;
end;
function TPasswordStore.ReadPassword(Prefix, Name, Connection: String;
out Password: AnsiString): Boolean;
var
Data: AnsiString = '';
begin
Result:= False;
if CheckMasterKey = False then Exit;
Data:= ReadString(Prefix + '_' + Name, Connection, Data);
if Length(Data) = 0 then Exit;
Password:= Decode(FMasterKey, Data);
if Length(Password) = 0 then raise EEncryptDecryptFailed.Create;
Result:= True;
end;
function TPasswordStore.DeletePassword(Prefix, Name, Connection: String): Boolean;
begin
Result:= not ReadOnly;
if Result then
try
DeleteKey(Prefix + '_' + Name, Connection);
except
Result:= False;
end;
end;
procedure InitPasswordStore;
var
AFileName: String;
begin
AFileName := gpCfgDir + 'pwd.ini';
try
PasswordStore:= TPasswordStore.Create(AFileName);
except
DCDebug('Can not create secure password store!');
end;
end;
{ EEncryptDecryptFailed }
constructor EEncryptDecryptFailed.Create;
begin
inherited Create('Encrypt/Decrypt failed');
end;
finalization
FreeThenNil(PasswordStore);
end.