{***************************************************************************** The DEC team (see file NOTICE.txt) licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. A copy of this licence is found in the root directory of this project in the file LICENCE.txt or alternatively at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. *****************************************************************************} unit DECBaseClass; interface {$INCLUDE DECOptions.inc} uses {$IFDEF FPC} SysUtils, Classes, {$ELSE} System.SysUtils, System.Classes, {$ENDIF} Generics.Collections; type /// /// Class type for the base class from which all other DEC classes inherit /// in order to be able to create lists of classes, pick an entry of such a /// list and construct an object out of it /// TDECClass = class of TDECObject; /// /// Generic list of DEC classes with the identity as key /// TDECClassList = class(TDictionary) strict private /// /// Checks if a given class type has the same short class name as given /// /// /// Short class name, e.g. HEXL /// /// /// Class reference to check against /// /// /// true if the class reference is for the given short name /// function DoFindNameShort(const Name: string; const ClassType: TDECClass): Boolean; /// /// Checks if a given class type has the same long class name as given /// /// /// Long class name, e.g. TFormat_HEXL /// /// /// Class reference to check against /// /// /// true if the class reference is for the given long name /// function DoFindNameLong(const Name: string; const ClassType: TClass): Boolean; public /// /// Tries to find a class type by its name /// /// /// Name to look for in the list /// /// /// Returns the class type if found. if it could not be found a /// EDECClassNotRegisteredException will be thrown /// function ClassByName(const Name: string): TDECClass; /// /// Tries to find a class type by its numeric identity DEC assigned to it. /// Useful for file headers, so they can easily encode numerically which /// cipher class was being used. /// /// /// Identity to look for /// /// /// Returns the class type of the class with the specified identity value /// or throws an EDECClassNotRegisteredException exception if no class /// with the given identity has been found /// function ClassByIdentity(Identity: Int64): TDECClass; /// /// Returns a list of all classes registered in this list /// /// /// List where the registered classes shall be added to. The string is the /// long class name, the object the class reference. The list is being /// cleared first and when an uncreated list is given nothing is being done /// procedure GetClassList(List: TStrings); end; /// /// Parent class of all cryptography and hash implementations /// TDECObject = class(TInterfacedObject) public /// /// Overrideable but otherwise empty constructor (calls his parent /// constructor or course) /// constructor Create; virtual; /// /// This function creates a unique Signature for each class using the /// following naming scheme: /// /// 'Z' repeated n times (to fill space of 256 chars) + DEC ClassName /// /// The CRC32 of the generated Signature is used as our unique Identity /// /// Important Note: /// DEC 5.2 introduced a bug which breaks backward compatibility with /// DEC 5.1 by using String instead of AnsiString. This leads to different /// Identities when using Unicode capable Delphi Versions (Delphi 2009+). /// /// To restore the *wrong* behavior of DEC 5.2 enable the DEC52_IDENTITY option /// in the configuration file DECOptions.inc. /// /// With this and all future versions we will keep backward compatibility. /// class function Identity: Int64; {$IFDEF X86ASM} /// /// Override FreeInstance to fill allocated Object with zeros, that is /// safer for any access to invalid Pointers of released Objects. /// procedure FreeInstance; override; {$ENDIF X86ASM} /// /// Registers this class type in the list of DEC classes (ClassList). /// Trying to register an already registered class will raise an exception. /// /// /// List to which the own class type shall be added. This allows subclasses /// to have their own lists /// class procedure RegisterClass(ClassList : TDECClassList); /// /// Removes tthis class type from the list of registered DEC classes /// (ClassList). Trying to unregister a non registered class is a do nothing /// operation. /// /// /// List from which the own class type shall be removed. This allows /// subclasses to have their own lists /// class procedure UnregisterClass(ClassList : TDECClassList); /// /// Returns short Classname of any DEC derrived class. This is the part /// of the class name after the _ so for THash_RipeMD160 it will be RipeMD160. /// /// /// Complete class name /// /// /// Short class name /// class function GetShortClassNameFromName(const ClassName: string): string; /// /// Returns short Classname of any DEC derrived class type. This is the part /// of the class name after the _ so for THash_RipeMD160 it will be RipeMD160. /// /// /// Short class name or empty string if ClassType is nil. /// class function GetShortClassName: string; end; var /// /// default used for generating class identities /// IdentityBase: Int64 = $25844852; /// /// Size in bytes used for buffering data read from or written to a stream /// StreamBufferSize: Integer = 8192; {$IFDEF NEXTGEN} EmptyStr: string = ''; /// /// Pointer to an empty string. For non Nextgen platforms declared in SysUtils /// for backwards compatibility only. Here declared for NextGen only and /// should get replaced /// NullStr: PString = @EmptyStr; {$ENDIF} implementation uses DECUtil, DECCRC; resourcestring sClassNotRegistered = 'Class %s is not registered'; sWrongIdentity = 'Another class "%s" with the same identity as "%s" has already been registered'; constructor TDECObject.Create; begin inherited Create; end; class function TDECObject.Identity: Int64; var Signature: {$IFDEF DEC52_IDENTITY}string{$ELSE !DEC52_IDENTITY}RawByteString{$ENDIF !DEC52_IDENTITY}; begin {$IFDEF DEC52_IDENTITY} Signature := StringOfChar(#$5A, 256 - Length(ClassName)) + UpperCase(ClassName); {$IF CompilerVersion >= 24.0} Result := CRC32(IdentityBase, Signature[Low(Signature)], Length(Signature) * SizeOf(Signature[Low(Signature)])); {$ELSE} Result := CRC32(IdentityBase, Signature[Low(Signature)], Length(Signature) * SizeOf(Signature[1])); {$IFEND} {$ELSE !DEC52_IDENTITY} Signature := RawByteString(StringOfChar(#$5A, 256 - Length(ClassName)) + UpperCase(ClassName)); {$IF CompilerVersion >= 24.0} Result := CRC32(IdentityBase, Signature[Low(Signature)], Length(Signature) * SizeOf(Signature[Low(Signature)])); {$ELSE} Result := CRC32(IdentityBase, Signature[1], Length(Signature) * SizeOf(Signature[1])); {$IFEND} {$ENDIF !DEC52_IDENTITY} end; class procedure TDECObject.RegisterClass(ClassList : TDECClassList); begin ClassList.Add(Identity, self); end; {$IFDEF X86ASM} procedure TDECObject.FreeInstance; // Override FreeInstance to fill allocated Object with zeros, that is // safer for any access to invalid Pointers of released Objects asm PUSH EBX PUSH EDI MOV EBX,EAX CALL TObject.CleanupInstance MOV EAX,[EBX] CALL TObject.InstanceSize MOV ECX,EAX MOV EDI,EBX XOR EAX,EAX REP STOSB MOV EAX,EBX CALL System.@FreeMem POP EDI POP EBX end; {$ENDIF X86ASM} class procedure TDECObject.UnregisterClass(ClassList : TDECClassList); begin ClassList.Remove(Identity); end; class function TDECObject.GetShortClassName: string; begin Result := GetShortClassNameFromName(self.ClassName); end; class function TDECObject.GetShortClassNameFromName(const ClassName: string): string; var i: Integer; begin Result := ClassName; i := Pos('_', Result); if i > 0 then Delete(Result, 1, i); end; { TDECClassList } function TDECClassList.DoFindNameShort(const Name: string; const ClassType: TDECClass): Boolean; begin Result := CompareText(ClassType.GetShortClassName, Name) = 0; end; function TDECClassList.DoFindNameLong(const Name: string; const ClassType: TClass): Boolean; var s: string; begin s := Name; Result := CompareText(ClassType.ClassName, Name) = 0; end; function TDECClassList.ClassByIdentity(Identity: Int64): TDECClass; begin try Result := Items[Identity]; except On EListError do raise EDECClassNotRegisteredException.CreateResFmt(@sClassNotRegistered, [IntToHEX(Identity, 8)]); end; end; function TDECClassList.ClassByName(const Name: string): TDECClass; var FindNameShort : Boolean; Pair : TPair; begin Result := nil; if Length(Name) > 0 then begin FindNameShort := TDECClass.GetShortClassNameFromName(Name) = Name; for Pair in self do begin if FindNameShort then begin if DoFindNameShort(Name, Pair.Value) then begin result := Pair.Value; break; end; end else if DoFindNameLong(Name, Pair.Value) then begin result := Pair.Value; break; end; end; end; if Result = nil then raise EDECClassNotRegisteredException.CreateResFmt(@sClassNotRegistered, [Name]); end; procedure TDECClassList.GetClassList(List: TStrings); var Pair : TPair; begin if List <> nil then try List.BeginUpdate; List.Clear; for Pair in self do List.AddObject(Pair.Value.ClassName, TObject(Pair.Value)); finally List.EndUpdate; end; end; initialization finalization end.