{***************************************************************************** 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. *****************************************************************************} /// /// Utility functions /// unit DECUtil; interface {$INCLUDE DECOptions.inc} uses {$IFDEF FPC} SysUtils, Classes; {$ELSE} System.SysUtils, System.Classes; {$ENDIF} type // Exception Classes /// /// Base exception class for all DEC specific exceptions, /// EDECException = class(Exception) public {$IFDEF FMXTranslateableExceptions} /// /// Creates the exception instance and makes the exception message translateable /// via Firemonkey's TLang translation mechanism. Normal ressource strings /// are not translated in the same way on mobile platforms as they are on /// Win32/Win64. /// /// /// String with a failure message to be output or logged /// constructor Create(const Msg: string); reintroduce; overload; /// /// Creates the exception instance and makes the exception message translateable /// via Firemonkey's TLang translation mechanism. Normal ressource strings /// are not translated in the same way on mobile platforms as they are on /// Win32/Win64. /// /// /// String with a failure message to be output or logged /// /// /// Array with values for the parameters specified in the format string /// constructor CreateFmt(const Msg: string; const Args: array of const); reintroduce; overload; {$ENDIF} end; /// /// Exception class used when reporting that a class searched in a list is /// not contained in that list, e.g. when searching for a non existant /// formatting class. /// EDECClassNotRegisteredException = class(EDECException); /// /// Exception class for reporting formatting related exceptions /// EDECFormatException = class(EDECException); /// /// Exception class for reporting exceptions related to hash functions /// EDECHashException = class(EDECException); /// /// Exception class for reporting encryption/decryption caused exceptions /// EDECCipherException = class(EDECException); /// /// Exception class for reporting the use of abstract things which cannot /// be called directly /// EDECAbstractError = class(EDECException) /// /// Create the exception using a meaningfull error message /// constructor Create(ClassName: string); overload; end; /// /// Reason for calling the progress event /// TDECProgressState = (Started, Processing, Finished {, Error}); /// /// Event type used by several hash- and cipher methods to display their /// progress. It can be implemented as regular method, procedure and as /// anonymous method, means: in place. /// /// /// Number of bytes to process. For files this is usually the file size. For /// streams this can be less than the stream size if the stream is not being /// processed from the beginning. /// /// /// Position within size in byte. For streams this may be a position /// relative to the starting position for processing. /// TDECProgressEvent = reference to procedure(Size, Pos: Int64; State: TDECProgressState); // Byte Ordering /// /// Reverses all bits in the passed value, 1111 0000 will be 0000 1111 afterwards /// /// /// Value who's bits are to be reversed /// /// /// Representation of Source but with all bits reversed /// function ReverseBits(Source: UInt32): UInt32; overload; /// /// Reverses all bits in the passed value, 1111 0000 will be 0000 1111 afterwards /// /// /// Value who's bits are to be reversed /// /// /// Representation of Source but with all bits reversed /// function ReverseBits(Source: UInt8): UInt8; overload; /// /// Reverses the order of the bytes contained in the buffer passed in. /// e.g. 1 2 3 will be 3 2 1 afterwards /// /// /// Buffer who's contents is to be reversed. /// /// /// Size of the passed buffer in byte /// procedure SwapBytes(var Buffer; Size: Integer); /// /// Reverses the byte order of the passed variable /// /// /// value who's byte order shall be reversed /// /// /// value of the passed vallue with reversed byte order /// function SwapUInt32(Source: UInt32): UInt32; /// /// Reverses the byte order for all entries of a passed array of UInt32 values /// /// /// Data with a layout like an array of UInt32 values for which the byte order /// of all entries shall be reversed /// /// /// In this variable the reversed values will be stored. Layout is like an /// array of UInt32 values /// /// /// Number of values to be reversed /// procedure SwapUInt32Buffer(const Source; var Dest; Count: Integer); /// /// Reverses the byte order of an Int64 value /// /// /// Value who's byte order shall be reversed /// /// /// Representation of the passed value after reversing its byte order /// function SwapInt64(Source: Int64): Int64; /// /// Reverses the byte order for all entries of a passed array of Int64 values /// /// /// Data with a layout like an array of Int64 values for which the byte order /// of all entries shall be reversed /// /// /// In this variable the reversed values will be stored. Layout is like an /// array of Int64 values /// /// /// Number of values to be reversed /// procedure SwapInt64Buffer(const Source; var Dest; Count: Integer); /// /// XORs the contents of two passed buffers and stores the result into a 3rd one /// /// /// One source buffer of bytes to be XORed /// /// /// The other source buffer of bytes to be XORed. Buffer size must be equal /// or bigger than Left /// /// /// Buffer size in byte. /// /// /// Buffer where the result is to be stored in. Must be of equal or bigger /// size than Left /// procedure XORBuffers(const Left, Right; Size: Integer; var Dest); // Buffer and Data Protection /// /// Fills a given buffer with zeros in a secure way /// /// /// Buffer to be zeroed. In case of TBytes to be passed as Buf[0] /// /// /// Buffer size in byte /// procedure ProtectBuffer(var Buffer; Size: NativeUInt); /// /// Fills a given stream with zeros in a secure way /// /// /// Stream to be zeroed. /// /// /// Number of bytes of that stream to be zeroed. Starting point is Stream.Position /// procedure ProtectStream(Stream: TStream; SizeToProtect: Int64 = 0); /// /// Fills a given byte array with zeros in a secure way and then empties the /// buffer. /// /// /// Byte array to be zeroed. The length of the passed buffer is 0 afterwards! /// procedure ProtectBytes(var Source: TBytes); /// /// Overwrites the string's contents in a secure way and returns an empty string. /// /// /// String to be safely overwritten /// procedure ProtectString(var Source: string); overload; /// /// Overwrites the string's contents in a secure way and returns an empty string. /// /// /// String to be safely overwritten /// procedure ProtectString(var Source: RawByteString); overload; {$IFDEF ANSISTRINGSUPPORTED} /// /// Overwrites the string's contents in a secure way and returns an empty string. /// /// /// String to be safely overwritten /// procedure ProtectString(var Source: AnsiString); overload; {$ENDIF} {$IFNDEF NEXTGEN} /// /// Overwrites the string's contents in a secure way and returns an empty string. /// /// /// String to be safely overwritten /// procedure ProtectString(var Source: WideString); overload; {$ENDIF} // Byte/String conversion /// /// Converts a byte array to a RawByteString /// /// /// Byte array to be converted into a string. An empty byte array is allowed /// and results in an empty string. /// /// /// RawByteString with the same length as Source and all bytes copied over. /// No conversion of any sort is being applied to the bytes. /// /// /// Not easily replaced by some RTL function as none for TBytes to RawByteString /// seems to exist /// function BytesToRawString(const Source: TBytes): RawByteString; implementation {$IFDEF FMXTranslateableExceptions} uses FMX.Types, {$ELSE} uses {$ENDIF} DECUtilRawByteStringHelper, DECTypes; const { TODO : Prüfen warum das eine Konstante ist, die gleich vom Ressourcestring benutzt wird. Weil es keine Ressorcestrings bei FMX gibt? } cAbstractError = 'Abstract Error: %s is not implemented'; resourcestring sAbstractError = cAbstractError; const // Bit Lookup Table - see 'Bit Twiddling Hacks' by Sean Eron Anderson // http://graphics.stanford.edu/~seander/bithacks.html ReverseBitLookupTable256: array[0..255] of Byte = ($00, $80, $40, $C0, $20, $A0, $60, $E0, $10, $90, $50, $D0, $30, $B0, $70, $F0, $08, $88, $48, $C8, $28, $A8, $68, $E8, $18, $98, $58, $D8, $38, $B8, $78, $F8, $04, $84, $44, $C4, $24, $A4, $64, $E4, $14, $94, $54, $D4, $34, $B4, $74, $F4, $0C, $8C, $4C, $CC, $2C, $AC, $6C, $EC, $1C, $9C, $5C, $DC, $3C, $BC, $7C, $FC, $02, $82, $42, $C2, $22, $A2, $62, $E2, $12, $92, $52, $D2, $32, $B2, $72, $F2, $0A, $8A, $4A, $CA, $2A, $AA, $6A, $EA, $1A, $9A, $5A, $DA, $3A, $BA, $7A, $FA, $06, $86, $46, $C6, $26, $A6, $66, $E6, $16, $96, $56, $D6, $36, $B6, $76, $F6, $0E, $8E, $4E, $CE, $2E, $AE, $6E, $EE, $1E, $9E, $5E, $DE, $3E, $BE, $7E, $FE, $01, $81, $41, $C1, $21, $A1, $61, $E1, $11, $91, $51, $D1, $31, $B1, $71, $F1, $09, $89, $49, $C9, $29, $A9, $69, $E9, $19, $99, $59, $D9, $39, $B9, $79, $F9, $05, $85, $45, $C5, $25, $A5, $65, $E5, $15, $95, $55, $D5, $35, $B5, $75, $F5, $0D, $8D, $4D, $CD, $2D, $AD, $6D, $ED, $1D, $9D, $5D, $DD, $3D, $BD, $7D, $FD, $03, $83, $43, $C3, $23, $A3, $63, $E3, $13, $93, $53, $D3, $33, $B3, $73, $F3, $0B, $8B, $4B, $CB, $2B, $AB, $6B, $EB, $1B, $9B, $5B, $DB, $3B, $BB, $7B, $FB, $07, $87, $47, $C7, $27, $A7, $67, $E7, $17, $97, $57, $D7, $37, $B7, $77, $F7, $0F, $8F, $4F, $CF, $2F, $AF, $6F, $EF, $1F, $9F, $5F, $DF, $3F, $BF, $7F, $FF); function ReverseBits(Source: UInt32): UInt32; begin Result := (ReverseBitLookupTable256[Source and $FF] shl 24) or (ReverseBitLookupTable256[(Source shr 8) and $FF] shl 16) or (ReverseBitLookupTable256[(Source shr 16) and $FF] shl 8) or (ReverseBitLookupTable256[(Source shr 24) and $FF]); end; function ReverseBits(Source: UInt8): UInt8; begin Result := ReverseBitLookupTable256[Source]; end; procedure SwapBytes(var Buffer; Size: Integer); {$IFDEF X86ASM} asm CMP EDX,1 JLE @@3 AND EAX,EAX JZ @@3 PUSH EBX MOV ECX,EDX LEA EDX,[EAX + ECX - 1] SHR ECX,1 @@1: MOV BL,[EAX] XCHG BL,[EDX] DEC EDX MOV [EAX],BL INC EAX DEC ECX JNZ @@1 @@2: POP EBX @@3: end; {$ELSE !X86ASM} var T: Byte; P, Q: PByte; i: Integer; begin P := @Buffer; Inc(P, Size - 1); Q := @Buffer; for i := 0 to Size div 2 - 1 do // using P/Q comparison with 'while' breaks some compilers begin T := Q^; Q^ := P^; P^ := T; Dec(P); Inc(Q); end; end; {$ENDIF !X86ASM} function SwapUInt32(Source: UInt32): UInt32; {$IF defined(X86ASM) or defined(X64ASM)} asm {$IFDEF X64ASM} MOV EAX, ECX {$ENDIF X64ASM} BSWAP EAX end; {$ELSE PUREPASCAL} begin Result := Source shl 24 or Source shr 24 or Source shl 8 and $00FF0000 or Source shr 8 and $0000FF00; end; {$IFEND PUREPASCAL} procedure SwapUInt32Buffer(const Source; var Dest; Count: Integer); {$IFDEF X86ASM} asm TEST ECX,ECX JLE @Exit PUSH EDI SUB EAX,4 SUB EDX,4 @@1: MOV EDI,[EAX + ECX * 4] BSWAP EDI MOV [EDX + ECX * 4],EDI DEC ECX JNZ @@1 POP EDI @Exit: end; {$ELSE !X86ASM} var i: Integer; T: UInt32; begin for i := 0 to Count - 1 do begin T := TUInt32Array(Source)[i]; TUInt32Array(Dest)[i] := (T shl 24) or (T shr 24) or ((T shl 8) and $00FF0000) or ((T shr 8) and $0000FF00); end; end; {$ENDIF !X86ASM} function SwapInt64(Source: Int64): Int64; {$IFDEF X86ASM} asm MOV EDX,Source.DWord[0] MOV EAX,Source.DWord[4] BSWAP EDX BSWAP EAX end; {$ELSE !X86ASM} var L, H: Cardinal; begin L := Int64Rec(Source).Lo; H := Int64Rec(Source).Hi; L := L shl 24 or L shr 24 or L shl 8 and $00FF0000 or L shr 8 and $0000FF00; H := H shl 24 or H shr 24 or H shl 8 and $00FF0000 or H shr 8 and $0000FF00; Int64Rec(Result).Hi := L; Int64Rec(Result).Lo := H; end; {$ENDIF !X86ASM} procedure SwapInt64Buffer(const Source; var Dest; Count: Integer); {$IFDEF X86ASM} asm TEST ECX,ECX JLE @Exit PUSH ESI PUSH EDI LEA ESI,[EAX + ECX * 8] LEA EDI,[EDX + ECX * 8] NEG ECX @@1: MOV EAX,[ESI + ECX * 8] MOV EDX,[ESI + ECX * 8 + 4] BSWAP EAX BSWAP EDX MOV [EDI + ECX * 8 + 4],EAX MOV [EDI + ECX * 8],EDX INC ECX JNZ @@1 POP EDI POP ESI @Exit: end; {$ELSE !X86ASM} var H, L: Cardinal; i: Integer; begin for i := 0 to Count - 1 do begin H := TUInt32Array(Source)[i * 2 ]; L := TUInt32Array(Source)[i * 2 + 1]; TUInt32Array(Dest)[i * 2 ] := L shl 24 or L shr 24 or L shl 8 and $00FF0000 or L shr 8 and $0000FF00; TUInt32Array(Dest)[i * 2 + 1] := H shl 24 or H shr 24 or H shl 8 and $00FF0000 or H shr 8 and $0000FF00; end; end; {$ENDIF !X86ASM} procedure XORBuffers(const Left, Right; Size: Integer; var Dest); // Dest^ = Source1^ xor Source2^ // Buffers must have the same size! {$IFDEF X86ASM} asm AND ECX,ECX JZ @@5 PUSH ESI PUSH EDI MOV ESI,EAX MOV EDI,Dest @@1: TEST ECX,3 JNZ @@3 @@2: SUB ECX,4 JL @@4 MOV EAX,[ESI + ECX] XOR EAX,[EDX + ECX] MOV [EDI + ECX],EAX JMP @@2 @@3: DEC ECX MOV AL,[ESI + ECX] XOR AL,[EDX + ECX] MOV [EDI + ECX],AL JMP @@1 @@4: POP EDI POP ESI @@5: end; {$ELSE !X86ASM} var P, Q, D: PByte; i: Integer; begin P := @Left; Q := @Right; D := @Dest; for i := 0 to Size - 1 do begin D^ := P^ xor Q^; Inc(P); Inc(Q); Inc(D); end; end; {$ENDIF !X86ASM} const WipeCount = 4; WipeBytes: array[0..WipeCount - 1] of Byte = ( $55, // 0101 0101 $AA, // 1010 1010 $FF, // 1111 1111 $00 // 0000 0000 ); procedure ProtectBuffer(var Buffer; Size: NativeUInt); var Count: Integer; begin if Size > 0 then begin for Count := 0 to WipeCount - 1 do FillChar(Buffer, Size, WipeBytes[Count]); end; end; procedure ProtectStream(Stream: TStream; SizeToProtect: Int64 = 0); const BufferSize = 512; var Buffer: string; Count, Bytes, Size: Integer; Position: Integer; begin Position := Stream.Position; Size := Stream.Size; if SizeToProtect <= 0 then begin SizeToProtect := Size; Position := 0; end else begin Dec(Size, Position); if SizeToProtect > Size then SizeToProtect := Size; end; SetLength(Buffer, BufferSize); for Count := 0 to WipeCount -1 do begin Stream.Position := Position; Size := SizeToProtect; {$IF CompilerVersion >= 24.0} FillChar(Buffer[Low(Buffer)], BufferSize, WipeBytes[Count]); {$ELSE} FillChar(Buffer[1], BufferSize, WipeBytes[Count]); {$IFEND} while Size > 0 do begin Bytes := Size; if Bytes > BufferSize then Bytes := BufferSize; {$IF CompilerVersion >= 24.0} Stream.Write(Buffer[Low(Buffer)], Bytes); {$ELSE} Stream.Write(Buffer[1], Bytes); {$IFEND} Dec(Size, Bytes); end; end; end; procedure ProtectBytes(var Source: TBytes); begin if (Source <> nil) and (Length(Source) > 0) then begin ProtectBuffer(Source[0], Length(Source)); SetLength(Source, 0); end; end; procedure ProtectString(var Source: string); begin if Length(Source) > 0 then begin System.UniqueString(Source); {$IF CompilerVersion >= 24.0} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[Low(Source)])); {$ELSE} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[1])); {$IFEND} Source := ''; end; end; procedure ProtectString(var Source: RawByteString); begin if Length(Source) > 0 then begin // UniqueString(Source); cannot be called with a RawByteString as there is // no overload for it, so we need to call our own one. DECUtilRawByteStringHelper.UniqueString(Source); {$IF CompilerVersion >= 24.0} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[Low(Source)])); {$ELSE} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[1])); {$IFEND} Source := ''; end; end; {$IFNDEF NEXTGEN} procedure ProtectString(var Source: AnsiString); overload; begin if Length(Source) > 0 then begin System.UniqueString(Source); {$IF CompilerVersion >= 24.0} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[Low(Source)])); {$ELSE} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[1])); {$IFEND} Source := ''; end; end; procedure ProtectString(var Source: WideString); overload; begin if Length(Source) > 0 then begin System.UniqueString(Source); // for OS <> Win, WideString is not RefCounted on Win {$IF CompilerVersion >= 24.0} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[Low(Source)])); {$ELSE} ProtectBuffer(Pointer(Source)^, Length(Source) * SizeOf(Source[1])); {$IFEND} Source := ''; end; end; {$ENDIF} function BytesToRawString(const Source: TBytes): RawByteString; begin SetLength(Result, Length(Source)); if Length(Source) > 0 then begin // determine lowest string index for handling of ZeroBasedStrings {$IF CompilerVersion >= 24.0} Move(Source[0], Result[Low(result)], Length(Source)); {$ELSE} Move(Source[0], Result[1], Length(Source)); {$IFEND} end; end; { EDECException } {$IFDEF FMXTranslateableExceptions} constructor EDECException.Create(const Msg: string); begin inherited Create(Translate(msg)); end; constructor EDECException.CreateFmt(const Msg: string; const Args: array of const); begin inherited Create(Format(Translate(Msg), Args)); end; constructor EDECAbstractError.Create(ClassName: string); begin inherited Create(Format(Translate(sAbstractError), [ClassName])); end; {$ELSE} constructor EDECAbstractError.Create(ClassName: string); begin inherited CreateResFmt(@sAbstractError, [ClassName]); end; {$ENDIF} end.