lazarus-ccr/components/cryptini/latest_stable/ucryptini.pas
gbamber 1a04f8d1d9 Cryptini Update
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@5693 8e941d3f-bd1b-0410-a28a-d453659cc2b4
2017-01-23 16:41:19 +00:00

1484 lines
52 KiB
ObjectPascal

unit ucryptini;
{$mode objfpc}{$H+}
{$DEFINE USE_DCPCRYPT} // Delete this if you don't have the DCrypt library
// and CryptINI will use built-in BASE64 instead
// (but turn all project debugging checks off!)
// DCrypt can be downloaded via OnlinePackageManager
// And is vastly preferable to BASE64
{$IF FPC_VERSION = 3} // inifiles.pp changed in fpc v3.1.? (17 Jan 2016)
{$IF FPC_RELEASE > 0}
{$IF FPC_PATCH > 0}
{$DEFINE FPC311}
{$ENDIF}
{$ENDIF}
{$ENDIF}
{ 'Paranoid' version of TIniFile
Copyright (C) 2016 Gordon Bamber minesadorada@charcodelvcalle.com
Base 64 code by David Barton
This library is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published by
the Free Software Foundation; either version 2 of the License, or (at your
option) any later version with the following modification:
As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent modules,and
to copy and distribute the resulting executable under terms of your choice,
provided that you also meet, for each linked independent module, the terms
and conditions of the license of that module. An independent module is a
module which is not derived from or based on this library. If you modify
this library, you may extend this exception to your version of the library,
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your 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 Library General Public License
for more details.
You should have received a copy of the GNU Library General Public License
along with this library; if not, write to the Free Software Foundation,
Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
PURPOSE
=======
This is a TiniFile implementation that is resistant to tampering.
In normal (PlainTextMode = FALSE) mode, any calls to Write values are
accompanied by an embedded MD5 hash value (and also reversed then Base64/IDEA Encrypted)
This is invisible in normal use (i.e. read methods return normal results)
but there are added methods to internally verify any entries.
It also is able to write a standard ident section containing various
details including authorship and copyright. A single function allows
you to check on app startup whether this section has been altered.
It also includes a useful 'First Run' functionality.
It's intended purpose is to store information that cannot be easily altered
in a text editor (such as HiScores etc) by a weekend scripter.
The WriteInteger method is the most secure as it double-encrypts as well as
embedding an MD5Hash value as a checksum. Very handy to save scores etc.
It is paired with ReadInteger and VerifyInteger
DISCLAIMER
==========
This unit does not claim to pass any security tests nor be used in
any environment where a moderate-level hacker could circumvent it.
ENCRYPTION
==========
By Default CryptINI uses the DCPCrypt package for string encryption.
The mode is IDEA cipher with an MD5 hash for the key. The EncryptINI
and DecryptINI methods use DCPCrypt RC4 Cipher (With SHA hash)
There is a $DEFINE USE_DCPCRYPT directive at the top of this file.
If this DEFINE is commented out, then CryptINI will default
to BASE64 string encryption, which is weaker. Encrypt/DecryptINI methods
will be unavailable.
You can then delete any requirement for dcpcrypt in the project inspector.
FREEPASCAL VERSIONS
===================
Starting in FPC V3.1.1 there are additional options in TINIFile which
are implemented in TCryptINI if the FPC Version >= 3.1.1 is detected.
USE AND EXAMPLE CODE
====================
** You can hard-code an Ident Section in your INI file
** and check if it has been altered
** Typical Form.Create()
procedure TForm1.FormCreate(Sender: TObject);
const
C_KEYPHRASE = 'I do like to be beside the seaside'; // Move this to the top of the unit
Begin
...other code
// Initialise the encrypted config file
INI := TCryptIniFile.Create(ChangeFileExt(Application.EXEName, '.cfg'));
// First ever run. INI is absent
If INI.IsVirgin then INI.WriteIdent('minesadorada','(c)2016','minesadorada@charcodelvalle.com','Creative Commons',TRUE);l
if NOT INI.VerifyIdent('5b319674f5cb55f3ed1e404e33c25868') then // I got this from the INI file
ShowMessage('This is not a genuine copy of ' + Application.Title + '!')
else INI.Deflower; // If not Deflowered then the default ini is used.
// After first run, use the encrypted version
If NOT INI.IsVirgin then
begin
INI.DecryptINI(TRUE,C_KEYPHRASE);
// Check the unencrypted version..
if NOT INI.VerifyIdent('5b319674f5cb55f3ed1e404e33c25868') then // I got this from the INI file
ShowMessage('This is not a genuine copy of ' + Application.Title + '!');
end;
INI.KeyPhrase:=C_KEYPHRASE; // for subsequent read/writes
...other code
end;
procedure Tmainform.FormDestroy(Sender: TObject);
const
C_KEYPHRASE = 'I do like to be beside the seaside'; // Move this to the top of the unit
Begin
...other code
INI.EncryptINI(TRUE,C_KEYPHRASE);
...other code
end;
** Has the Ident been tampered with? Put this in Form.Create
// Use the MD5 value from the INI file (use your own!)
If INI.VerifyIdent('92abf0deecbb25c435bff507a396d92a') then
ShowMessage('Ident verified OK') // do nothing
else
ShowMessage('Ident failed verification'); // Warning message/exit
** Test for first run
If INI.IsVirgin then // note that doing the test Deflowers the app by default
ShowMessage('First time run of this app');
** Toggle to normal UnEncrypted INI use
(by default it is set to FALSE)
INI.PlainTextMode:=TRUE;
INI.WriteString('MySection', 'String', 'MyString'); // just writes normally
** When PlainTextMode = FALSE (default),Write<anytype> encrypts the value
and prefixes the encrypted value with an MD5Hash
INI.WriteInteger('MySection', 'Integer',1000);
** Note WriteInteger adds a '+' (INTEGER_MARKER) to the written Key
This is so that CryptINI can deal with Integers specially
Using CryptINI in either mode, this is invisible in use
(i.e. don't add a + to the ident for any methods)
** When PlainTextMode = FALSE (default), use these convenient methods if you like:
INI.ReadUnencryptedString
INI.WriteUnencryptedString
INI.ReadUnEncryptedInteger
INI.WriteUnencryptedInteger
** Store 'First Run' status using these methods:
INI.IsVirgin
INI.Deflower
INI.ReFlower (useful for testing)
** You can encrypt and decrypt the entire INI file usine EncryptINI and DecryptINI
This uses a different cipher and hash method, so is extra-secure
cryptINI methods and properties an only work with an unencrypted INI file
So; Decrypt on startup, and Encrypt before exit.
By default the routines use the KeyPhrase property, but you can override this
in the method call along with whether to delete the "old" file and what
file extension to use for the encrypted file.
** Original IniFiles methods and properties
Just use them as normal. Note: you can mix-and-match Plaintext and Encrypted
in the same INI file. Just toggle the property PlainTextMode before Writing/Reading
}
interface
uses
Forms, SysUtils, LazUTF8, LazFileUtils,StrUtils, INIFiles, md5, LCLVersion,
Classes,fileinfo,SyncObjs
// DEBUGGING for ShowMessage:,Dialogs
// ,Dialogs
{$IFDEF USE_DCPCRYPT},DCPidea, DCPmd5,DCPrc4, DCPsha1{$ENDIF}
;
const
// Fixed Ident section values for Read/Write/Verify Ident methods
// Change the values here if you like.
IDENT_SECTION = 'ProgramInfo';
IDENT_APPNAME = 'App name';
IDENT_APPVERSION = 'App version';
IDENT_EXE = 'App Path';
IDENT_AUTHOR = 'Author';
IDENT_COPYRIGHT = 'Copyright';
IDENT_LICENSE = 'License';
IDENT_CONTACT = 'Contact';
IDENT_LCLVERSION = 'LCL Version';
IDENT_FPCVERSION = 'FPC Version';
IDENT_TARGET = 'Target System';
IDENT_LASTCOMPILED = 'Last Compiled';
IDENT_FIRSTRUN = 'FirstRun';
IDENT_MD5HASH = 'MD5Hash';
// You can change these Consts here if you like
INTEGER_MARKER = '+'; // Appended to Crypted Integer Idents
// so that ReadSectionValues,ValueExists and DeleteKey methods will work
DEFAULT_KEYPHRASE = 'Choose a better keyphrase than this!'; // If none specified
{$IFDEF WINDOWS}
DEFAULT_EXT = '.enc';
{$ELSE}
DEFAULT_EXT = 'enc';
{$ENDIF}
{$IFNDEF USE_DCPCRYPT}
// Base64 array of bytes. CHANGE AT YOUR PERIL!
B64: array[0..63] of byte = (65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 48, 49, 50, 51, 52, 53,
54, 55, 56, 57, 43, 47);
{$ENDIF}
C_VERSION = '0.1.3';
{
VERSION HISTORY
===============
V 0.0.1: Initial alpha release by (c)2016 minesadorada@charcodelvalle.com (GB)
V 0.0.2: Improved Read/Write/Verify integer values (GB)
V 0.0.3: Section functions added. Integer marker added. (GB)
V 0.0.4: Compatibility test for fpc 3 additions to TIniFiles (GB)
V 0.0.5: Added DCPCrypt units and conditional DEFINES. Added Encrypt/Decrypt INI (GB)
V 0.0.6: ReadBinaryStream and WriteBinaryStream added (GB)
V 0.0.7: ReadSection debugged (GB)
V 0.0.8: Bugfix: FPC Version {DEFINE FPC3} corrected (GB)
V 0.0.9: Added SplitKeyValue procedure. Other fixes and improvements (GB)
V 0.1.0: Added Program Version info to Ident section
V 0.1.2: Added thread-safe TLockCryptINIFile class
for thread-safe operation (GB)
Credit: "Fungus" at http://forum.lazarus.freepascal.org/
V 0.1.3: BugFix: WriteIdent method without Version Info now covered by meaningful error message.
V 0.1.4: ??
}
type
TCryptIniFile = class(TIniFile)
private
// Md5 Stuff
fMyDigest: TMDDigest; // defined in md5 unit
fMD5String: string;
// For Read/write Integers
fKey: longint;
fKeyPhrase: string;
// Encryption on/off
fPlainText: boolean;
fSectionHashing: boolean;
fVersion: string;
{$IFDEF USE_DCPCRYPT}
DCP_idea1: TDCP_idea;
DCP_md5_1: TDCP_md5;
{$ENDIF}
fHashType:String;
fCipherType:String;
// Functions to convert values
function EncodeString(const sValue: string): string;
function DecodeString(const sValue: string): string;
// Functions to convert Integers only
function EncodeInteger(const iValue: longint): longint;
function DecodeInteger(const iValue: longint): longint;
// Either..
procedure SetKeyPhrase(AValue: string); // Sets fKey from a string (Preferrred method)
Procedure SetKey(aValue:Integer); // Also sets Keyphrase (weaker)
public
{$IFDEF FPC311}
constructor Create(const AFileName: string; AOptions: TIniFileOptions = []);
override;
{$ELSE}
constructor Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);override;
{$ENDIF}
destructor Destroy; override;
// Overridden TINIFile methods
function ReadInteger(const Section, Ident: string; Default: longint): longint;
override;
procedure WriteInteger(const Section, Ident: string; Value: longint); override;
function ReadString(const Section, Ident, Default: string): string; override;
procedure WriteString(const Section, Ident, Value: string); override;
procedure ReadSection(const Section: string; Strings: TStrings); override;
function ValueExists(const Section, Ident: string): Boolean; override;
procedure DeleteKey(const Section, Ident: String); override;
{$IFDEF FPC311}
procedure ReadSectionValues(const Section: string; Strings: TStrings;
AOptions: TSectionValuesOptions = []); override;
{$ELSE}
procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
{$ENDIF}
function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer; override;
procedure WriteBinaryStream(const Section, Name: string; Value: TStream); override;
// Added functions
// Compares TestValue's value and MD5Hash with the INI file's version
function VerifyString(const Section, Ident, TestValue: string): boolean;
function VerifyBool(const Section, Ident: string; TestValue: boolean): boolean;
function VerifyInt64(const Section, Ident: string; TestValue: int64): boolean;
function VerifyFloat(const Section, Ident: string; TestValue: double): boolean;
function VerifyDate(const Section, Ident: string; TestValue: TDateTime): boolean;
function VerifyTime(const Section, Ident: string; TestValue: TDateTime): boolean;
function VerifyDateTime(const Section, Ident: string; TestValue: TDateTime): boolean;
// VerifyInteger is more strict
function VerifyInteger(const Section, Ident: string; TestValue: longint): boolean;
// New functionality. Write a whole section in one go.
procedure WriteSectionValues(const Section: string; Strings: TStrings);
// If SectionHashing = TRUE then MakeSectionHash is called for every write
// Best practioe is to do it only after a bunch of writes
procedure MakeSectionHash(const Section: string; WriteToINI: boolean = True);
function VerifySectionHash(const Section, TestMD5Hash: string): boolean;
// Separates key=value into Skey and sValue
function SplitKeyValue(Const sValueEntry:String;var sKey,sValue:String):Boolean;
// Sets property PlainTextMode to FALSE temporarily to Read/Write normally
// I included these functions for simplicity. They work when PlainTextMode=TRUE or FALSE
function ReadUnencryptedString(const Section, Ident, Default: string): string;
procedure WriteUnencryptedString(const Section, Ident, Value: string);
function ReadUnEncryptedInteger(const Section, Ident: string;
Default: longint): longint;
procedure WriteUnencryptedInteger(const Section, Ident: string; Value: longint);
// Writes a standard Ident section with MD5 Hash
// This can be done in Form.Create() on program first run
// Or can be done during development, then distribute the prewritten INI file with the exe
procedure WriteIdent(const sAuthor, sCopyright, sLicense, sContact: string; Force: boolean=False);
// Read the MD5Hash from the INI file and pass it as a parameter
// ANY tampering with the [ProgramInfo] ident section info returns a FALSE
// Note not all Key/Values are verified (i.e. Appname etc.)
function VerifyIdent(const sMD5Hash: string): boolean;
{$IFDEF USE_DCPCRYPT}
// Encrypt and Decrypt the whole file in one go. Very secure via RC4 cipher.
// You can call:
// EncryptINI; (use DEFAULT_KEYPHRASE as the passkey, and delete the old file)
// EncryptINI(FALSE); (don't delete old ini file)
// EncryptINI(TRUE,'bananas'); (delete old file; use "bananas" as the passkey)
// EncryptINI(TRUE,'bananas','.xxx'); (delete old file; use bananas as the passkey, and save as an .xxx file)
// bSuccess:=EncryptINI(optional parameters); (returns FALSE if anything goes wrong)
function EncryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
function DecryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
{$ENDIF}
// Use to detect or set 'First Run' status
function IsVirgin: boolean;
procedure Deflower;
procedure Reflower;
// By Default, PlainTextMode = FALSE (Encrypted mode)
property PlainTextMode: boolean read fPlainText write fPlainText;
property MD5Hash: string read fMD5String write fMD5String;
// Either..
property KeyPhrase: string read fKeyPhrase write SetKeyPhrase; // also sets Key (preferred property)
// or..
property Key: longint read fKey write SetKey; // Also sets KeyPhrase (weaker)
// If SectionHashing=TRUE then a Section hash is written each time any value
// is written. This enables VerifySection.
property SectionHashing: boolean read fSectionHashing write fSectionHashing;
property HashType:String read fHashType; // Info
property CipherType:String read fCipherType; // Info
property CryptINIVersion: string read fVersion; // Info
property FileName; //inherited
{$IFNDEF FPC311}
// Reflect changes in fpc 3x
property EscapeLineFeeds;
Property CaseSensitive;
Property StripQuotes;
{$ENDIF}
end;
Type
TLockCryptINIFile = class(TCryptIniFile)
private
FLock: TCriticalSection;
public
destructor Destroy; override;
// Call Lock after creation
procedure Lock;
// Call Unlock before destruction
procedure Unlock;
end;
resourcestring
rsNotYetInitia = 'Not yet initialised';
rsNothingWasEn = 'Nothing was Entered!';
rsUnknown = 'unknown';
implementation
{TLockCryptINIFile}
destructor TLockCryptINIFile.Destroy;
begin
FreeAndNil(FLock);
inherited;
end;
procedure TLockCryptINIFile.Lock;
begin
if FLock = nil then FLock:= TCriticalSection.Create;
FLock.Acquire;
end;
procedure TLockCryptINIFile.Unlock;
begin
if FLock <> nil then FLock.Release;
end;
{TCryptINIFile}
{$IFDEF FPC311}
constructor TCryptIniFile.Create(const AFileName: string;
AOptions: TIniFileOptions = []);
{
TIniFileOption = (ifoStripComments, // Strip comments when reading file
ifoStripInvalid, // Strip invalid lines when reading file.
ifoEscapeLineFeeds, // Escape linefeeds when reading file.
ifoCaseSensitive, // Use Case sensitive section/key names
ifoStripQuotes, // Strip quotes when reading string values.
ifoFormatSettingsActive, // Use format settings when writing date/float etc.
ifoWriteStringBoolean // Write booleans as string
);
}
begin
// AOptions := AOptions And NOT [ifoWriteStringBoolean];
inherited Create(AFileName, AOptions);
{$ELSE}
constructor TCryptIniFile.Create(const AFileName: string; AEscapeLineFeeds : Boolean = False);
begin
inherited Create(AFileName,AEscapeLineFeeds);
{$ENDIF}
CacheUpdates := False;
fPlainText := False;
fSectionHashing := True;
fVersion := C_VERSION;
fMD5String := rsNotYetInitia;
fKey := 300554; // Random default key in case user doesn't set one
fKeyPhrase := DEFAULT_KEYPHRASE;
{$IFDEF USE_DCPCRYPT}
DCP_idea1:=TDCP_idea.Create(Nil);
DCP_md5_1:=TDCP_md5.Create(Nil);
If DCP_idea1.SelfTest then
DCP_Idea1.Burn; { Clear all information }
DCP_Idea1.InitStr(fKeyPhrase,TDCP_md5);
fCipherType:=DCP_Idea1.GetAlgorithm;
fHashType:=DCP_md5_1.GetAlgorithm;
{$ELSE}
fHashType:='MD5';
fCiperType:='BASE64';
{$ENDIF}
end;
// ***************************************************************************//
destructor TCryptIniFile.Destroy;
begin
// Anything to free here?
{$IFDEF USE_DCPCRYPT}
DCP_idea1.Burn; // Clear key info from memory
DCP_idea1.Free;
DCP_md5_1.Burn; // Clear key info from memory
DCP_md5_1.Free;
{$ENDIF}
inherited Destroy;
end;
// ***************************************************************************//
// Start of internal routines
// ***************************************************************************//
{$IFNDEF USE_DCPCRYPT} // Fallback routines
function B64Encode(pInput: pointer; pOutput: pointer; Size: longint): longint;
var
i, iptr, optr: longint;
Input, Output: PByteArray;
begin
Input := PByteArray(pInput);
Output := PByteArray(pOutput);
iptr := 0;
optr := 0;
for i := 1 to (Size div 3) do
begin
Output^[optr + 0] := B64[Input^[iptr] shr 2];
Output^[optr + 1] := B64[((Input^[iptr] and 3) shl 4) + (Input^[iptr + 1] shr 4)];
Output^[optr + 2] := B64[((Input^[iptr + 1] and 15) shl 2) + (Input^[iptr + 2] shr 6)];
Output^[optr + 3] := B64[Input^[iptr + 2] and 63];
Inc(optr, 4);
Inc(iptr, 3);
end;
case (Size mod 3) of
1:
begin
Output^[optr + 0] := B64[Input^[iptr] shr 2];
Output^[optr + 1] := B64[(Input^[iptr] and 3) shl 4];
Output^[optr + 2] := byte('=');
Output^[optr + 3] := byte('=');
end;
2:
begin
Output^[optr + 0] := B64[Input^[iptr] shr 2];
Output^[optr + 1] := B64[((Input^[iptr] and 3) shl 4) + (Input^[iptr + 1] shr 4)];
Output^[optr + 2] := B64[(Input^[iptr + 1] and 15) shl 2];
Output^[optr + 3] := byte('=');
end;
end;
Result := ((Size + 2) div 3) * 4;
end;
// ***************************************************************************//
function Base64Encode(const Value: ansistring): ansistring;
begin
SetLength(Result, ((Length(Value) + 2) div 3) * 4);
B64Encode(@Value[1], @Result[1], Length(Value));
end;
// ***************************************************************************//
function B64Decode(pInput: pointer; pOutput: pointer; Size: longint): longint;
var
i, j, iptr, optr: longint;
Temp: array[0..3] of byte;
Input, Output: PByteArray;
begin
Input := PByteArray(pInput);
Output := PByteArray(pOutput);
iptr := 0;
optr := 0;
Result := 0;
for i := 1 to (Size div 4) do
begin
for j := 0 to 3 do
begin
case Input^[iptr] of
65..90: Temp[j] := Input^[iptr] - Ord('A');
97..122: Temp[j] := Input^[iptr] - Ord('a') + 26;
48..57: Temp[j] := Input^[iptr] - Ord('0') + 52;
43: Temp[j] := 62;
47: Temp[j] := 63;
61: Temp[j] := $FF;
end;
Inc(iptr);
end;
Output^[optr] := (Temp[0] shl 2) or (Temp[1] shr 4);
Result := optr + 1;
if (Temp[2] <> $FF) and (Temp[3] = $FF) then
begin
Output^[optr + 1] := (Temp[1] shl 4) or (Temp[2] shr 2);
Result := optr + 2;
Inc(optr);
end
else if (Temp[2] <> $FF) then
begin
try
Output^[optr + 1] := (Temp[1] shl 4) or (Temp[2] shr 2);
Output^[optr + 2] := (Temp[2] shl 6) or Temp[3]; // Throws error
Result := optr + 3;
Inc(optr, 2);
except
On E: Exception do
Continue;
end;
end;
Inc(optr);
end;
end;
// ***************************************************************************//
function Base64Decode(const Value: ansistring): string;
begin
SetLength(Result, (Length(Value) div 4) * 3);
SetLength(Result, B64Decode(@Value[1], @Result[1], Length(Value)));
end;
{$ENDIF}
// ***************************************************************************//
function CharToBool(AChar: char): boolean;
begin
Result := (Achar = '1');
end;
// ***************************************************************************//
function BoolToChar(ABool: boolean): char;
begin
if ABool then
Result := '1'
else
Result := '0';
end;
// ***************************************************************************//
// End of internal routines
// ***************************************************************************//
Procedure TCryptIniFile.SetKey(aValue:Integer); // Also sets KeyPhrase
begin
fKey:=AValue;
{$IFNDEF USE_DCPCRYPT}
SetKeyPhrase(fKey); // VERY WEAK!
{$ENDIF}
end;
// ***************************************************************************//
procedure TCryptIniFile.SetKeyPhrase(AValue: string);
// Hash the phrase to a standard 32 chars
// Then sum the ASCII values to seed the fKey (it never exceeds LongInt)
// Difficult to reverse-engineer even if you have the KeyPhrase
var
aDigest: TMDDigest; // defined in md5 unit
iCount: integer;
lTry: longint;
begin
{$IFDEF USE_DCPCRYPT}
// Use the string directly
fKeyPhrase:=AValue;
if fKeyPhrase = '' then
fKeyPhrase := DEFAULT_KEYPHRASE; // don't allow empty string
DCP_Idea1.Burn; { Clear all stored key information }
DCP_Idea1.InitStr(fKeyPhrase,TDCP_md5);
Exit; // bail out
{$ENDIF}
lTry := 0;
// If it's already a number, just use it. (unless it's zero!)
if ((TryStrToInt(AValue, lTry)) and (lTry <> 0)) then
fKey := lTry
else // It's a string
begin
if AValue = '' then
AValue := rsNothingWasEn; // don't allow empty string
aDigest := MDString(AValue, MD_VERSION_5);
fKeyPhrase := MDPrint(aDigest); // 32 chars
// Mess it up to make a big enough number (96 chars)
fKeyPhrase := fKeyPhrase + ReverseString(fKeyPhrase) + fKeyPhrase;
fKey := 0;
for iCount := 1 to (Length(fKeyPhrase)) do
begin
fKey := fKey + Ord(fKeyPhrase[iCount]);
end;
// Make the number bigger
fKey := fKey * 7;
end;
end;
// ***************************************************************************//
function TCryptIniFile.EncodeString(const sValue: string): string;
// Muddle the String up and encode it in IDEA or Base64
var
s: string;
begin
s := ReverseString(sValue);
{$IFDEF USE_DCPCRYPT}
DCP_Idea1.Reset;{ Reset any stored chaining information }
Result:=DCP_Idea1.EncryptString(s);
{$ELSE}
Result := Base64Encode(s);
{$ENDIF}
end;
// ***************************************************************************//
function TCryptIniFile.DecodeString(const sValue: string): string;
// Decode the IDEA or Base64 then Unmuddle
var
s: string;
begin
{$IFDEF USE_DCPCRYPT}
DCP_Idea1.Reset;{ Reset any stored chaining information }
s:=DCP_Idea1.DecryptString(sValue);
{$ELSE}
s := Base64Decode(sValue);
{$ENDIF}
Result := ReverseString(s);
end;
// ***************************************************************************//
function TCryptIniFile.EncodeInteger(const iValue: longint): longint;
begin
//DEBUG: ShowMessageFmt('EncodeInteger value = %d',[iValue]);
// B64Encode(@iValue,@Result,SizeOf(iValue)); deprecated
Result := iValue xor fKey;
// Already IDEA or BASE64 encoded after writing
//DEBUG: ShowMessageFmt('EncodeInteger Result = %d',[Result]);
end;
// ***************************************************************************//
function TCryptIniFile.DecodeInteger(const iValue: longint): longint;
begin
// B64Decode(@iValue,@Result,SizeOf(iValue)); deprecated
// Already IDEA or BASE64 decoded before reading
Result := iValue xor fKey;
end;
// ***************************************************************************//
procedure TCryptIniFile.Deflower;
// Called by IsVirgin to set FirstRun status to FALSE
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText;
fPlainText := True;
WriteBool(IDENT_SECTION, IDENT_FIRSTRUN, False);
fPlainText := tempPlainText;
end;
// ***************************************************************************//
procedure TCryptIniFile.Reflower;
// Set FirstRun status to TRUE
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText;
fPlainText := True;
WriteBool(IDENT_SECTION, IDENT_FIRSTRUN, True);
fPlainText := tempPlainText;
end;
// ***************************************************************************//
function TCryptIniFile.IsVirgin: boolean;
// On FirstRun returns TRUE
// Then calls Deflower so subsequent runs will return FALSE
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText;
fPlainText := True;
Result := ReadBool(IDENT_SECTION, IDENT_FIRSTRUN, True);
if Result = True then
Deflower;
fPlainText := tempPlainText;
end;
// ***************************************************************************//
function TCryptIniFile.VerifyIdent(const sMD5Hash: string): boolean;
// The sMD5Hash has been previously generated by WriteIdent
// and stored in INI file. Get it from there.
var
s: string;
tempPlainText: boolean;
begin
Result := False;
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
// Make a string of all the authorship entries
s := ReadString(IDENT_SECTION, IDENT_AUTHOR, rsUnknown);
s := s + ReadString(IDENT_SECTION, IDENT_COPYRIGHT, rsUnknown);
s := s + ReadString(IDENT_SECTION, IDENT_LICENSE, rsUnknown);
s := s + ReadString(IDENT_SECTION, IDENT_CONTACT, rsUnknown);
// Make up an MD5 hash from the INI values read
fMyDigest := MDString(s, MD_VERSION_5);
fMD5String := MDPrint(fMyDigest);
// Compare it with the value passed in the parameter
if fMD5String = sMD5Hash then
Result := True;
fPlainText := tempPlainText; // Restore old value
end;
// ***************************************************************************//
procedure TCryptIniFile.WriteIdent(const sAuthor, sCopyright, sLicense, sContact: string; Force: boolean=False);
// This proc will only write anything if IsVirgin (First Run status) is TRUE
// Or the INI file is empty or altered
var
s: string;
tempPlainText: boolean;
VInfo:TFileVersionInfo;
Version:TProgramVersion;
begin
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
if ((ReadBool(IDENT_SECTION, IDENT_FIRSTRUN, True) = True) OR (Force = True)) then
begin // IsVirgin=TRUE so write the section in plaintext
WriteString(IDENT_SECTION, IDENT_AUTHOR, sAuthor);
WriteString(IDENT_SECTION, IDENT_COPYRIGHT, sCopyright);
WriteString(IDENT_SECTION, IDENT_LICENSE, sLicense);
WriteString(IDENT_SECTION, IDENT_CONTACT, sContact);
WriteString(IDENT_SECTION, IDENT_LCLVERSION, lcl_version);
WriteString(IDENT_SECTION, IDENT_FPCVERSION, {$I %FPCVERSION%});
s:={$I %FPCTARGETCPU%}+' - '+{$I %FPCTARGETOS%};
WriteString(IDENT_SECTION, IDENT_TARGET, s);
// If you don't need this you can delete 'Forms' from the Uses clause
WriteString(IDENT_SECTION, IDENT_APPNAME,Application.Title);
s:='0.0.0.0'; // Default
VInfo:=TFileVersionInfo.Create(Application);
TRY
TRY
VInfo.ReadFileInfo;
If fileinfo.GetProgramVersion(Version) then
s:=ProgramversionToStr(Version);
finally
VInfo.free;
end;
Except
Raise EResNotFound.Create('TCryptINIFile.WriteIdent' + LineEnding + 'Please include Version Info in Project/options to use this function');
end;
WriteString(IDENT_SECTION,IDENT_APPVERSION,s);
WriteString(IDENT_SECTION, IDENT_EXE, ParamStrUTF8(0));
WriteBool(IDENT_SECTION,IDENT_FIRSTRUN,TRUE);
s:={$I %DATE%} + ' at ' + {$I %TIME%};
WriteString(IDENT_SECTION, IDENT_LASTCOMPILED, s);
// Make up the MD5Hash of all the authorship entries
s := sAuthor + sCopyright + sLicense + sContact;
fMyDigest := MDString(s, MD_VERSION_5);
fMD5String := MDPrint(fMyDigest);
WriteString(IDENT_SECTION, IDENT_MD5HASH, fMD5String);
UpdateFile;
end;
fPlainText := tempPlainText; // Restore old value
end;
// ***************************************************************************//
procedure TCryptIniFile.WriteUnencryptedString(const Section, Ident, Value: string);
var
tempPlainText: boolean;
// Temporarily sets PlainTextMode = TRUE then restores previous status
begin
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
WriteString(Section, Ident, Value);
fPlainText := tempPlainText; // Restore old value
end;
// ***************************************************************************//
function TCryptIniFile.ReadUnencryptedString(
const Section, Ident, Default: string): string;
// Temporarily sets PlainTextMode = TRUE then restores previous status
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
Result := ReadString(Section, Ident, Default);
fPlainText := tempPlainText; // Restore old value
end;
// ***************************************************************************//
function TCryptIniFile.ReadUnEncryptedInteger(const Section, Ident: string;
Default: longint): longint;
// Temporarily sets PlainTextMode = TRUE then restores previous status
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
Result := ReadInteger(Section, Ident, Default);
fPlainText := tempPlainText; // Restore old value
end;
// ***************************************************************************//
procedure TCryptIniFile.WriteUnencryptedInteger(const Section, Ident: string;
Value: longint);
// Temporarily sets PlainTextMode = TRUE then restores previous status
var
tempPlainText: boolean;
begin
tempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
WriteInteger(Section, Ident, Value);
fPlainText := tempPlainText; // Restore old value
end;
(******************************************************************************)
// Subclass of TIniFiles to enable obfuscation of entries
(******************************************************************************)
function TCryptIniFile.ReadInteger(const Section, Ident: string;
Default: longint): longint;
// Unobfuscates the number if PlainTextMode=FALSE
var
s: string;
iTemp: longint;
begin
if fPlainText = True then
begin
// Read result using TiniFile
Result := inherited ReadInteger(Section, Ident, Default);
Exit;
end;
Result := Default; // If all else fails..
s := ReadString(Section, Ident + INTEGER_MARKER, IntToStr(Default));
//DEBUG: ShowMessage(s);
if s = IntToStr(Default) then
Exit // nothing there - return Default value
else
begin
if not TryStrToInt(s, iTemp) then
Exit; // Error - return Default value
Result := DecodeInteger(iTemp); // Un-Scramble it
end;
end;
(******************************************************************************)
procedure TCryptIniFile.WriteInteger(const Section, Ident: string; Value: longint);
// Obfuscates the number if PlainTextMode=FALSE
begin
if fPlainText = False then
begin
Value := EncodeInteger(Value); // Scramble it
inherited WriteInteger(Section, Ident + INTEGER_MARKER, Value); // Write+ using TiniFile
end
else inherited WriteInteger(Section, Ident, Value); // Write using TiniFile
if SectionHashing = True then
MakeSectionHash(Section, True);
UpdateFile;
end;
(******************************************************************************)
function TCryptIniFile.ReadString(const Section, Ident, Default: string): string;
// Behaviour depends on PlainTextMode = FALSE/TRUE
var
s: string;
begin
fMD5String:='';
// Read value via TIniFile
s := inherited ReadString(Section, Ident, Default);
Result := s;
if fPlainText = True then // Use unencrypted version
Exit;
if (s = Default) then Exit;
If (Length(s) < 32) then Exit;
fMD5String := LeftStr(s, 32); // Grab the MD5 string and store (unencoded)
s := RightStr(s, Length(s) - 32); // Use the rest
// Deal with Default value
//if (s = EncodeString(Default)) then
// Result:=Default
//else
Result := DecodeString(s); // Un-Encrypt it
end;
(******************************************************************************)
function TCryptIniFile.ValueExists(const Section, Ident: string): Boolean;
// In Encrypted mode Integers have a special marker
Var s:String;
begin
Result:= inherited ValueExists(Section,Ident); //Normal result
if fPlainText = False then
begin
If Ident = '' then // bail out
begin
Result:=FALSE;
exit;
end;
// Test against both Ident and (Ident + INTEGER_MARKER)
s:=Ident;
s := s + INTEGER_MARKER; // Add INTEGER_MARKER
Result:=Result OR inherited ValueExists(Section,s);
end;
end;
(******************************************************************************)
procedure TCryptIniFile.DeleteKey(const Section, Ident: String);
// In Encrypted mode Integers have a special marker
Var s:String;
begin
If Ident = '' then exit; // Bail out
inherited DeleteKey(Section,Ident); //Normal behaviour
// Ident might have an INTEGER_MARKER so preceding code didn't delete it
// So delete (Ident + INTEGER_MARKER) if its there
s:=Ident; // Make a copy
// DEBUG: ShowMessage(s);
s := s + INTEGER_MARKER; // Add INTEGER_MARKER
inherited DeleteKey(Section,s);
If SectionHashing then MakeSectionHash(Section,TRUE);
UpdateFile;
end;
(******************************************************************************)
procedure TCryptIniFile.WriteString(const Section, Ident, Value: string);
// Writes MD5Digest string + BASE64 or IDEA string
// Behaviour depends on PlainTextMode = FALSE/TRUE
var
s: string;
begin
if fPlainText = True then // Write regular string
begin
// Use regular TiniFile
inherited WriteString(Section, Ident, Value);
UpdateFile;
Exit;
end;
s := EncodeString(Value); // Encode the string (IDEA/BASE64)
fMyDigest := MDString(s, MD_VERSION_5);
fMD5String := MDPrint(fMyDigest); // Get the MD5Hash of the encoded string
s := fMD5String + s; // Prefix it to the encoded string
inherited WriteString(Section, Ident, s); // Write the result using TiniFile
if SectionHashing = True then
MakeSectionHash(Section, True);
UpdateFile;
end;
(******************************************************************************)
Function TCryptIniFile.SplitKeyValue(Const sValueEntry:String;var sKey,sValue:String):Boolean;
Var iEqualsPos:Integer;
begin
Result:=FALSE;
If Length(sValueEntry) < 4 then exit;
iEqualsPos := Pos('=', sValueEntry);
if (iEqualsPos >= 2) then
begin
sKey := LeftStr(sValueEntry, iEqualsPos - 1);
sValue := RightStr(sValueEntry, Length(sValueEntry) - iEqualsPos);
Result:=TRUE;
end;
end;
(******************************************************************************)
procedure TCryptIniFile.ReadSection(const Section: string; Strings: TStrings);
// If PlainText = FALSE then
// 1. Remove any INTEGER_MARKER
// 2. Remove MD5Hash entry
Var
TempStrings:TStrings;
iCount,iMD5HashEntry: integer;
s: string;
begin
inherited ReadSection(Section, Strings); // Use regular TiniFile
if fPlainText = True then exit;// Return faithful copy
// Deal with PlainText = FALSE
iMD5HashEntry := -1; // dummy value
TempStrings := TStringList.Create; // Use a copy
TempStrings.BeginUpdate;
try
// Make a Copy
TempStrings.Clear;
TempStrings.AddStrings(Strings); // contains encrypted versions
for iCount := 0 to Pred(TempStrings.Count) do
begin
// Do not retrieve MD5Hash entry
if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
begin
s := TempStrings[iCount];
If RightStr(s,1) = INTEGER_MARKER then
TempStrings[iCount]:=LeftStr(s,Length(s)-1);
end
else
iMD5HashEntry := iCount;
end;
finally
TempStrings.EndUpdate;
// Copy decoded versions back into the calling StringList
if (iMD5HashEntry >= 0) then
TempStrings.Delete(iMD5HashEntry); // Dont return the MD5Hash entry
Strings.Clear;
Strings.AddStrings(TempStrings);
TempStrings.Free;
end;
end;
procedure TCryptIniFile.WriteSectionValues(const Section: string; Strings: TStrings);
// Writes either normal or Encrypted values depending on property PlainTextMode
// If SectionHashing = TRUE, also writes the MD5Hash entry at the end (for VerifySection method)
var
iCount, iTemp: integer;
s, sKey, sValue: string;
f: double;
TempSectionHashing: boolean;
begin
TempSectionHashing := fSectionHashing; // Preserve property
fSectionHashing := False; // Write section without on-the-fly hashing
for iCount := 0 to Strings.Count - 1 do
begin
s := Strings[iCount];
// Split key and value
If NOT SplitKeyValue(s,sKey,sValue) then continue;
if TryStrToInt(sValue, iTemp) = True then
begin
WriteInteger(Section, sKey, iTemp);
Continue;
end;
if ((LowerCase(sValue) = 'true') or (LowerCase(sValue) = 'false')) then
begin
if (LowerCase(sValue) = 'true') then
WriteBool(Section, sKey, True)
else
WriteBool(Section, sKey, False);
Continue;
end;
if TryStrToFloat(sValue, f) = True then
begin
WriteFloat(Section, sKey, f);
Continue;
end;
// All other values
WriteString(Section, sKey, sValue);
end;
// Now is the time to write the MD5Hash entry
if TempSectionHashing = True then
begin
fSectionHashing := True;
MakeSectionHash(Section, True);
end;
UpdateFile;
fSectionHashing := TempSectionHashing; // Restore property
end;
// ***************************************************************************//
{$IFDEF FPC311}
// TSectionValuesOption = (svoIncludeComments,svoIncludeInvalid, svoIncludeQuotes);
procedure TCryptIniFile.ReadSectionValues(const Section: string;
Strings: TStrings; AOptions: TSectionValuesOptions = []);
{$ELSE}
procedure TCryptIniFile.ReadSectionValues(const Section: string; Strings: TStrings);
{$ENDIF}
var
TempStrings: TStrings;
iCount, iMD5HashEntry: integer;
s, sKey, sValue: string;
iTemp: longint;
begin
{$IFDEF FPC311}
inherited ReadSectionValues(Section, Strings, AOptions);
{$ELSE}
inherited ReadSectionValues(Section, Strings);
{$ENDIF}
if fPlainText = True then // Use regular method
begin
// Use regular TiniFile
UpdateFile;
Exit;
end;
iMD5HashEntry := -1; // dummy value
TempStrings := TStringList.Create; // Use a copy
TempStrings.BeginUpdate;
try
// Make a Copy
TempStrings.Clear;
TempStrings.AddStrings(Strings); // contains encrypted versions
// Iterate through the list
for iCount := 0 to Pred(TempStrings.Count) do
begin
// Do not retrieve MD5Hash entry
if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
begin
s := TempStrings[iCount];
// Split key and value
If NOT SplitKeyValue(s,sKey,sValue) then Exit;
// Deal with Integers, which are IDEA or BASE64 encoded then Encrypted
if RightStr(sKey, 1) = INTEGER_MARKER then // It's an Integer value
begin
s := DecodeString(RightStr(sValue, Length(sValue) - 32));
// Decode (IDEA/BASE64) to a string
if TryStrToInt(s, iTemp) then
begin
iTemp := DecodeInteger(iTemp); // Decrypt the integer
s := IntToStr(iTemp); // String value of decoded integer
end;
sKey := LeftStr(sKey, Length(sKey) - 1); // Trim off the INTEGER_MARKER
end
else
begin
// All but Integers are simply IDEA or BASE64 encoded
s := DecodeString(RightStr(sValue, Length(sValue) - 32));
end;
// Replace encoded version with decoded version
TempStrings[iCount] := sKey + '=' + s;
end
else
iMD5HashEntry := iCount;
// Grab the sKey and sValue (we only want to decrypt the Value)
end;
finally
TempStrings.EndUpdate;
// Copy decoded versions back into the calling StringList
if (iMD5HashEntry >= 0) then
TempStrings.Delete(iMD5HashEntry); // Dont return the MD5Hash entry
Strings.Clear;
Strings.AddStrings(TempStrings);
TempStrings.Free;
end;
end;
// ***************************************************************************//
function TCryptIniFile.ReadBinaryStream(const Section, Name: string; Value: TStream): Integer;
// Force normal TINIFile mode
Var
tempPlainTextMode:Boolean;
tempSectionHashing:Boolean;
begin
tempPlainTextMode:=PlainTextMode;
tempSectionHashing:=SectionHashing;
PlainTextMode:=TRUE;
SectionHashing:=FALSE;
Result:=inherited ReadBinaryStream(Section, Name,Value);
PlainTextMode:=tempPlainTextMode;
SectionHashing:=tempSectionHashing;
end;
// ***************************************************************************//
procedure TCryptIniFile.WriteBinaryStream(const Section, Name: string; Value: TStream);
// Force normal TINIFile mode
Var
tempPlainTextMode:Boolean;
tempSectionHashing:Boolean;
begin
tempPlainTextMode:=PlainTextMode;
tempSectionHashing:=SectionHashing;
PlainTextMode:=TRUE;
SectionHashing:=FALSE;
inherited WriteBinaryStream(Section, Name,Value);
PlainTextMode:=tempPlainTextMode;
SectionHashing:=tempSectionHashing;
end;
// ***************************************************************************//
// ***************************************************************************//
procedure TCryptIniFile.MakeSectionHash(const Section: string; WriteToINI: boolean);
// VerifySection uses WriteToINI=FALSE
var
TempStrings: TStrings;
iCount: integer;
s: string;
tempPlainText: boolean;
begin
if fSectionHashing = False then Exit;
tempPlainText:=fPlainText; // Store value
TempStrings := TStringList.Create;
TempStrings.BeginUpdate;
try
s := '';
ReadSectionValues(Section, TempStrings);
for iCount := 0 to Pred(TempStrings.Count) do
// Ignore the MD5Hash entry
if (Pos(IDENT_MD5HASH, TempStrings[iCount]) = 0) then
s := s + TempStrings[iCount];
//DEBUG: else ShowMessage('Found ' + IDENT_MD5HASH);
fMyDigest := MDString(s, MD_VERSION_5);
fMD5String := MDPrint(fMyDigest);
//DEBUG: ShowMessage(fMD5String);
TempPlainText := fPlainText; // Store old value
fPlainText := True; // Set PlainTextMode ON
// Default WriteToINI = TRUE
if WriteToINI then
WriteString(Section, IDENT_MD5HASH, fMD5String);
fPlainText := tempPlainText; // Restore old value
finally
TempStrings.EndUpdate;
TempStrings.Free;
end;
end;
function TCryptIniFile.VerifySectionHash(const Section, TestMD5Hash: string): boolean;
begin
Result := False;
if fSectionHashing = False then Exit;
{
if ((fSectionHashing = False) or (fPlainText = True)) then
Exit;
}
fMD5String := '';
// Rehash, but without writing in order to set fMD5String correctly
MakeSectionHash(Section, False);
//DEBUG: ShowMessage(fMD5String);
//DEBUG: ShowMessage(TestMD5Hash);
if (fMD5String = TestMD5Hash) then
Result := True;
end;
function TCryptIniFile.VerifyString(const Section, Ident, TestValue: string): boolean;
// Matches both value and Hash
var
sCorrect, sCorrectMD5: string;
begin
Result := False;
if fPlainText = True then
Exit;
// Fetch the MD5 hash of the TestValue string
sCorrect := EncodeString(TestValue);
fMyDigest := MDString(sCorrect, MD_VERSION_5);
sCorrectMD5 := MDPrint(fMyDigest);
// Read the MD5 hash of the INI string (sets fMD5String) and get the value;
fMD5String := '';
sCorrect := ReadString(Section, Ident, '');
// DEBUG
{
ShowMessageFmt('sCorrectMD5=%s, fMD5String=%s, sCorrect=%s, TestValue=%s',
[sCorrectMD5,fMD5String,sCorrect,TestValue]);
}
if ((sCorrectMD5 = fMD5String) and (sCorrect = TestValue)) then
Result := True;
end;
// ***************************************************************************//
function TCryptIniFile.VerifyInteger(const Section, Ident: string;
TestValue: longint): boolean;
// Matches both value and Hash
var
iCorrect: longint;
sCorrect, sCorrectMD5: string;
begin
Result := False;
if fPlainText = True then
Exit;
iCorrect:=0;
if TestValue <> 0 then
iCorrect := EncodeInteger(TestValue); // Use fKey to scramble it
// Encode it as a string
sCorrect := EncodeString(IntToStr(iCorrect));
// Make an MD5 Hash of it (sets fMD5String)
fMyDigest := MDString(sCorrect, MD_VERSION_5);
sCorrectMD5 := MDPrint(fMyDigest);
// Use ReadInteger to get the fMD5String of the INI file entry
// And read the value
iCorrect := ReadInteger(Section, Ident, 9999); // Automatically uses fKey to unscramble
// Value and MD5Hash match?
if ((sCorrectMD5 = fMD5String) and (iCorrect = TestValue)) then
Result := True;
end;
// ***************************************************************************//
function TCryptIniFile.VerifyBool(const Section, Ident: string;
TestValue: boolean): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, BoolToChar(TestValue));
end;
// ***************************************************************************//
function TCryptIniFile.VerifyInt64(const Section, Ident: string;
TestValue: int64): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, IntToStr(TestValue));
end;
// ***************************************************************************//
function TCryptIniFile.VerifyFloat(const Section, Ident: string;
TestValue: double): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, FloatToStr(TestValue));
end;
// ***************************************************************************//
function TCryptIniFile.VerifyDate(const Section, Ident: string;
TestValue: TDateTime): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, DateToStr(TestValue));
end;
// ***************************************************************************//
function TCryptIniFile.VerifyTime(const Section, Ident: string;
TestValue: TDateTime): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, TimeToStr(TestValue));
end;
// ***************************************************************************//
function TCryptIniFile.VerifyDateTime(const Section, Ident: string;
TestValue: TDateTime): boolean;
begin
Result := False;
if fPlainText = True then
Exit;
Result := VerifyString(Section, Ident, DateTimeToStr(TestValue));
end;
// ***************************************************************************//
{$IFDEF USE_DCPCRYPT}
// TODO? use Stream property (Filename='')
Function TCryptIniFile.EncryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
var
Cipher: TDCP_rc4;
Source,Dest: TFileStream;
begin
CacheUpdates:=FALSE;
UpdateFile;
Result:=FALSE;
If NOT FileExistsUTF8(Filename) then exit;
try
If sPassword = '' then sPassword:=fKeyPhrase;
Source := TFileStream.Create(Filename, fmOpenRead);
// INI.Filename, PATH SOURCE OF FILE
Dest := TFileStream.Create(ChangeFileExt(Filename, NewExt), fmCreate);
// ChangeFileExt(INI.Filename, NewExt), PATH DESTINATION OF FILE
Cipher := TDCP_rc4.Create(Nil);
Cipher.InitStr(sPassword, TDCP_sha1);
// initialize the cipher with a hash of the passphrase
Cipher.EncryptStream(Source, Dest, Source.Size);
// encrypt the contents of the file
Cipher.Burn;
Cipher.Free;
finally
Dest.Free;
Source.Free;
Result:=TRUE;
If DeleteOld then Result:=Result AND DeleteFile(Filename);
end;
end;
// ***************************************************************************//
Function TCryptIniFile.DecryptINI(DeleteOld:Boolean = TRUE;sPassword: string = DEFAULT_KEYPHRASE;NewExt:String = DEFAULT_EXT):Boolean;
var
Cipher: TDCP_rc4;
Source,Dest: TFileStream;
begin
Result:=FALSE;
If NOT FileExistsUTF8(ChangeFileExt(Filename, NewExt)) then exit;
try
If sPassword = '' then sPassword:=fKeyPhrase;
Source := TFileStream.Create(ChangeFileExt(Filename, NewExt), fmOpenRead);
// ChangeFileExt(INI.Filename, NewExt), PATH SOURCE OF FILE
Dest := TFileStream.Create(Filename, fmCreate);
// INI.Filename, PATH DESTINATION OF FILE
Cipher := TDCP_rc4.Create(Nil);
Cipher.InitStr(sPassword, TDCP_sha1);
// initialize the cipher with a hash of the passphrase
Cipher.DecryptStream(Source, Dest, Source.Size);
// decrypt the contents of the file
Cipher.Burn;
Cipher.Free;
finally
Dest.Free;
Source.Free;
Result:=TRUE;
If DeleteOld then Result:=Result AND DeleteFile(ChangeFileExt(Filename, NewExt));
// Re-initialise Decrypted INI from disk
Try
{$IFDEF FPC311}
Create(Filename,Options);
{$else}
Create(Filename,EscapeLineFeeds);
{$endif}
CacheUpdates:=FALSE;
Except
// Eat exception
Result:=FALSE;
end;
end;
end;
{$ENDIF}
// ***************************************************************************//
// ENDOFFILE
// ***************************************************************************//
end.