delphimvcframework/sources/MVCFramework.Crypt.Utils.pas
2024-01-02 17:04:27 +01:00

152 lines
4.2 KiB
ObjectPascal

// ***************************************************************************
//
// Delphi MVC Framework
//
// Copyright (c) 2010-2024 Daniele Teti and the DMVCFramework Team
//
// https://github.com/danieleteti/delphimvcframework
//
// Some code in this file is from Thijs van Dien (https://stackoverflow.com/users/1163893/thijs-van-dien)
//
// ***************************************************************************
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License 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 MVCFramework.Crypt.Utils;
interface
uses
System.SysUtils, System.Math, IdHMAC, IdHMACSHA1, IdGlobal;
type
TIdHMACClass = class of TIdHMAC;
function PBKDF2(const Password: TBytes; const Salt: TBytes; const IterationsCount: Integer;
const KeyLengthInBytes: Integer; PRFC: TIdHMACClass = nil): TBytes;
function BytesToHexString(const _B: TIdBytes): string; overload;
function BytesToHexString(const _B: TBytes): string; overload;
procedure MVCCryptInit;
implementation
uses
IdSSLOpenSSL, MVCFramework.Commons;
{
PBKDF2 algorithm implementation from
https://stackoverflow.com/a/20134669/6825479
and then slighly modified. Copyright, if any, is from the original owner.
}
// Modeled after http://www.di-mgt.com.au/cryptoKDFs.html#PKCS5
function PBKDF2(const Password: TBytes; const Salt: TBytes; const IterationsCount: Integer;
const KeyLengthInBytes: Integer; PRFC: TIdHMACClass = nil): TBytes;
var
PRF: TIdHMAC;
D: Integer;
I: Int32;
F: TIdBytes;
U: TIdBytes;
J: Integer;
T: TIdBytes;
lPassword, lSalt: TIdBytes;
function _ConcatenateBytes(const _B1: TIdBytes; const _B2: TIdBytes): TIdBytes; inline;
begin
SetLength(Result, Length(_B1) + Length(_B2));
if Length(_B1) > 0 then
Move(_B1[low(_B1)], Result[low(Result)], Length(_B1));
if Length(_B2) > 0 then
Move(_B2[low(_B2)], Result[low(Result) + Length(_B1)], Length(_B2));
end;
function _INT_32_BE(const _I: Int32): TIdBytes; inline;
begin
Result := TIdBytes.Create(_I shr 24, _I shr 16, _I shr 8, _I);
end;
procedure _XorBytes(var _B1: TIdBytes; const _B2: TIdBytes); inline;
var
_I: Integer;
begin
for _I := low(_B1) to high(_B1) do
_B1[_I] := _B1[_I] xor _B2[_I];
end;
begin
if not Assigned(PRFC) then
PRFC := TIdHMACSHA1;
try
PRF := PRFC.Create;
except
on E: Exception do
begin
if E.Message.Contains('is not available') then
raise EMVCException.Create(HTTP_STATUS.InternalServerError,
E.Message +
' [HINT: May be OpenSSL is not been loaded yet. Did you invoked MVCCryptInit?]');
raise EMVCException.Create(HTTP_STATUS.InternalServerError, E.Message);
end;
end;
try
{
Conversion TBytes -> TidBytes as Remy Lebeau says
https://stackoverflow.com/a/18854367/6825479
}
lPassword := TIdBytes(Password);
lSalt := TIdBytes(Salt);
D := Ceil(KeyLengthInBytes / PRF.HashSize);
PRF.Key := lPassword;
for I := 1 to D do
begin
F := PRF.HashValue(_ConcatenateBytes(lSalt, _INT_32_BE(I)));
U := Copy(F);
for J := 2 to IterationsCount do
begin
U := PRF.HashValue(U);
_XorBytes(F, U);
end;
T := _ConcatenateBytes(T, F);
end;
Result := TBytes(Copy(T, low(T), KeyLengthInBytes));
finally
PRF.Free;
end;
end;
function BytesToHexString(const _B: TIdBytes): string; overload;
var
_I: Integer;
begin
for _I := low(_B) to high(_B) do
Result := Result + IntToHex(_B[_I], 2);
end;
function BytesToHexString(const _B: TBytes): string; overload;
begin
Result := BytesToHexString(TIdBytes(_B));
end;
procedure MVCCryptInit;
begin
if not LoadOpenSSLLibrary then
begin
raise EMVCException.Create('Cannot load OpenSSL');
end;
end;
end.