880 lines
30 KiB
ObjectPascal
880 lines
30 KiB
ObjectPascal
{*****************************************************************************
|
|
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.
|
|
*****************************************************************************}
|
|
|
|
/// <summary>
|
|
/// Unit containing all the KDF, MGF, HMAC and PBKDF2 algorithms
|
|
/// </summary>
|
|
unit DECHashAuthentication;
|
|
|
|
interface
|
|
|
|
uses
|
|
System.SysUtils, DECHashBase;
|
|
|
|
type
|
|
/// <summary>
|
|
/// Meta class for all the hashing classes in order to support the
|
|
/// registration mechanism
|
|
/// </summary>
|
|
TDECHashAuthenticationClass = class of TDECHashAuthentication;
|
|
|
|
/// <summary>
|
|
/// Type of the KDF variant
|
|
/// </summary>
|
|
TKDFType = (ktKDF1, ktKDF2, ktKDF3);
|
|
|
|
/// <summary>
|
|
/// Class containing all the KDF, MGF, HMAC and PBKDF2 algorithms
|
|
/// </summary>
|
|
TDECHashAuthentication = class(TDECHash)
|
|
strict private
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// IEEE P1363 Working Group, ISO 18033-2:2004
|
|
/// This is either KDF1 or KDF2 depending on KDFType
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size in bytes of the source data passed.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Start value for pseudo random number generator
|
|
/// </param>
|
|
/// <param name="SeedSize">
|
|
/// Size of the seed in byte.
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <param name="KDFType">
|
|
/// Type of the algorithm: 1 = KDF1, 2 = KDF2 and 3 = KDF 3
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key.
|
|
/// </returns>
|
|
class function KDFInternal(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer; KDFType: TKDFType): TBytes; inline;
|
|
public
|
|
/// <summary>
|
|
/// Detects whether the given hash class is one particularily suited
|
|
/// for storing hashes of passwords
|
|
/// </summary>
|
|
/// <returns>
|
|
/// true if it's a hash class specifically designed to store password
|
|
/// hashes, false for ordinary hash algorithms.
|
|
/// </returns>
|
|
class function IsPasswordHash: Boolean; override;
|
|
|
|
// mask generation
|
|
|
|
/// <summary>
|
|
/// Mask generation: generates an output based on the data given which is
|
|
/// similar to a hash function but in contrast does not have a fixed output
|
|
/// length. Use of a MGF is desirable in cases where a fixed-size hash
|
|
/// would be inadequate. Examples include generating padding, producing
|
|
/// one time pads or keystreams in symmetric key encryption, and yielding
|
|
/// outputs for pseudorandom number generators.
|
|
/// Indexed Mask generation function, IEEE P1363 working group
|
|
/// equal to KDF1 except without seed. RFC 2437 PKCS #1
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Data from which to generate a mask from
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size of the input data in bytes
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the returned mask in bytes
|
|
/// </param>
|
|
/// <returns>
|
|
/// Mask such that one cannot determine the data which had been given to
|
|
/// generate this mask from.
|
|
/// </returns>
|
|
class function MGF1(const Data; DataSize, MaskSize: Integer): TBytes; overload;
|
|
/// <summary>
|
|
/// Mask generation: generates an output based on the data given which is
|
|
/// similar to a hash function but incontrast does not have a fixed output
|
|
/// length. Use of a MGF is desirable in cases where a fixed-size hash
|
|
/// would be inadequate. Examples include generating padding, producing
|
|
/// one time pads or keystreams in symmetric key encryption, and yielding
|
|
/// outputs for pseudorandom number generators
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Data from which to generate a mask from
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the returned mask in bytes
|
|
/// </param>
|
|
/// <returns>
|
|
/// Mask such that one cannot determine the data which had been given to
|
|
/// generate this mask from.
|
|
/// </returns>
|
|
class function MGF1(const Data: TBytes; MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// IEEE P1363 Working Group, ISO 18033-2:2004
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size in bytes of the source data passed.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="SeedSize">
|
|
/// Size of the seed/salt in byte.
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// In earlier versions there was an optional format parameter. This has
|
|
/// been removed as this is a base class. The method might not have
|
|
/// returned a result with the MaskSize specified, as the formatting might
|
|
/// have had to alter this. This would have been illogical.
|
|
/// </remarks>
|
|
class function KDF1(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// IEEE P1363 Working Group, ISO 18033-2:2004
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
class function KDF1(const Data, Seed: TBytes; MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// IEEE P1363 Working Group, ISO 18033-2:2004
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size in bytes of the source data passed.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="SeedSize">
|
|
/// Size of the seed/salt in byte.
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// In earlier versions there was an optional format parameter. This has
|
|
/// been removed as this is a base class. The method might not have
|
|
/// returned a result with the MaskSize specified, as the formatting might
|
|
/// have had to alter this. This would have been illogical.
|
|
/// </remarks>
|
|
class function KDF2(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// IEEE P1363 Working Group, ISO 18033-2:2004
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Start value for pseudo random number generator
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
class function KDF2(const Data, Seed: TBytes; MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size in bytes of the source data passed.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="SeedSize">
|
|
/// Size of the seed/salt in byte.
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
/// <remarks>
|
|
/// In earlier versions there was an optional format parameter. This has
|
|
/// been removed as this is a base class. The method might not have
|
|
/// returned a result with the MaskSize specified, as the formatting might
|
|
/// have had to alter this. This would have been illogical.
|
|
/// </remarks>
|
|
class function KDF3(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
class function KDF3(const Data, Seed: TBytes; MaskSize: Integer): TBytes; overload;
|
|
|
|
// DEC's own KDF + MGF
|
|
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys. The alrorithm
|
|
/// implemented by this method does not follow any official standard.
|
|
/// </summary>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size in bytes of the source data passed.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="SeedSize">
|
|
/// Size of the seed/salt in byte.
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <param name="Index">
|
|
/// Optional parameter: can be used to specify a different default value
|
|
/// for the index variable used in the algorithm.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
class function KDFx(const Data; DataSize: Integer; const Seed; SeedSize, MaskSize: Integer; Index: UInt32 = 1): TBytes; overload;
|
|
/// <summary>
|
|
/// Key deviation algorithm to derrive keys from other keys.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This variant of the algorithm does not follow an official standard.
|
|
/// It has been created by the original author of DEC.
|
|
/// </remarks>
|
|
/// <param name="Data">
|
|
/// Source data from which the new key shall be derrived.
|
|
/// </param>
|
|
/// <param name="Seed">
|
|
/// Salt value
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the generated output in byte
|
|
/// </param>
|
|
/// <param name="Index">
|
|
/// Optional parameter: can be used to specify a different default value
|
|
/// for the index variable used in the algorithm.
|
|
/// </param>
|
|
/// <returns>
|
|
/// Returns the new derrived key with the length specified in MaskSize.
|
|
/// </returns>
|
|
class function KDFx(const Data, Seed: TBytes; MaskSize: Integer; Index: UInt32 = 1): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Mask generation: generates an output based on the data given which is
|
|
/// similar to a hash function but incontrast does not have a fixed output
|
|
/// length. Use of a MGF is desirable in cases where a fixed-size hash
|
|
/// would be inadequate. Examples include generating padding, producing
|
|
/// one time pads or keystreams in symmetric key encryption, and yielding
|
|
/// outputs for pseudorandom number generators.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This variant of the algorithm does not follow an official standard.
|
|
/// It has been created by the original author of DEC.
|
|
/// </remarks>
|
|
/// <param name="Data">
|
|
/// Data from which to generate a mask from
|
|
/// </param>
|
|
/// <param name="DataSize">
|
|
/// Size of the passed data in bytes
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the returned mask in bytes
|
|
/// </param>
|
|
/// <param name="Index">
|
|
/// Looks like this is a salt applied to each byte of output data?
|
|
{ TODO : Clarify this parameter }
|
|
/// </param>
|
|
/// <returns>
|
|
/// Mask such that one cannot determine the data which had been given to
|
|
/// generate this mask from.
|
|
/// </returns>
|
|
class function MGFx(const Data; DataSize, MaskSize: Integer; Index: UInt32 = 1): TBytes; overload;
|
|
/// <summary>
|
|
/// Mask generation: generates an output based on the data given which is
|
|
/// similar to a hash function but incontrast does not have a fixed output
|
|
/// length. Use of a MGF is desirable in cases where a fixed-size hash
|
|
/// would be inadequate. Examples include generating padding, producing
|
|
/// one time pads or keystreams in symmetric key encryption, and yielding
|
|
/// outputs for pseudorandom number generators.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// This variant of the algorithm does not follow an official standard.
|
|
/// It has been created by the original author of DEC.
|
|
/// </remarks>
|
|
/// <param name="Data">
|
|
/// Data from which to generate a mask from
|
|
/// </param>
|
|
/// <param name="MaskSize">
|
|
/// Size of the returned mask in bytes
|
|
/// </param>
|
|
/// <param name="Index">
|
|
/// Looks like this is a salt applied to each byte of output data?
|
|
{ TODO : Clarify this parameter }
|
|
/// </param>
|
|
/// <returns>
|
|
/// Mask such that one cannot determine the data which had been given to
|
|
/// generate this mask from.
|
|
/// </returns>
|
|
class function MGFx(const Data: TBytes; MaskSize: Integer; Index: UInt32 = 1): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// HMAC according to rfc2202: hash message authentication code allow to
|
|
/// verify both the data integrity and the authenticity of a message.
|
|
/// </summary>
|
|
/// <param name="Key">
|
|
/// This is the secret key which shall not be transmitted over the line.
|
|
/// The sender uses this key to create the resulting HMAC, transmits the
|
|
/// text and the HMAC over the line and the receiver recalculates the HMAC
|
|
/// based on his copy of the secret key. If his calculated HMAC equals the
|
|
/// transfered HMAC value the message has not been tampered.
|
|
/// </param>
|
|
/// <param name="Text">
|
|
/// Text over which to calculate the HMAC
|
|
/// </param>
|
|
/// <returns>
|
|
/// Calculated HMAC
|
|
/// </returns>
|
|
class function HMAC(const Key, Text: TBytes): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// HMAC according to rfc2202: hash message authentication code allow to
|
|
/// verify both the data integrity and the authenticity of a message.
|
|
/// </summary>
|
|
/// <param name="Key">
|
|
/// This is the secret key which shall not be transmitted over the line.
|
|
/// The sender uses this key to create the resulting HMAC, transmits the
|
|
/// text and the HMAC over the line and the receiver recalculates the HMAC
|
|
/// based on his copy of the secret key. If his calculated HMAC equals the
|
|
/// transfered HMAC value the message has not been tampered.
|
|
/// </param>
|
|
/// <param name="Text">
|
|
/// Text over which to calculate the HMAC
|
|
/// </param>
|
|
/// <returns>
|
|
/// Calculated HMAC
|
|
/// </returns>
|
|
class function HMAC(const Key, Text: RawByteString): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Password based key deviation function 2
|
|
/// RFC 2898, PKCS #5.
|
|
/// This can be used to create a login sheme by storing the output,
|
|
/// number of iterations and the salt. When the user enters a password
|
|
/// this calculation is done using the same parameters as stored for his
|
|
/// user account and comparing the output.
|
|
/// </summary>
|
|
/// <param name="Password">
|
|
/// Password to create the deviation from
|
|
/// </param>
|
|
/// <param name="Salt">
|
|
/// Salt used to modify the password
|
|
/// </param>
|
|
/// <param name="Iterations">
|
|
/// Number of iterations to perform
|
|
/// </param>
|
|
/// <param name="KeyLength">
|
|
/// Length of the resulting key in byte
|
|
/// </param>
|
|
class function PBKDF2(const Password, Salt: TBytes; Iterations: Integer; KeyLength: Integer): TBytes; overload;
|
|
|
|
/// <summary>
|
|
/// Password based key deviation function 2
|
|
/// RFC 2898, PKCS #5.
|
|
/// This can be used to create a login sheme by storing the output,
|
|
/// number of iterations and the salt. When the user enters a password
|
|
/// this calculation is done using the same parameters as stored for his
|
|
/// user account and comparing the output.
|
|
/// </summary>
|
|
/// <param name="Password">
|
|
/// Password to create the deviation from
|
|
/// </param>
|
|
/// <param name="Salt">
|
|
/// Salt used to modify the password
|
|
/// </param>
|
|
/// <param name="Iterations">
|
|
/// Number of iterations to perform
|
|
/// </param>
|
|
/// <param name="KeyLength">
|
|
/// Length of the resulting key in byte
|
|
/// </param>
|
|
class function PBKDF2(const Password, Salt: RawByteString; Iterations: Integer; KeyLength: Integer): TBytes; overload;
|
|
end;
|
|
|
|
/// <summary>
|
|
/// All hash classes with hash algorithms specially developed for password
|
|
/// hashing should inherit from this class in order to be able to distinguish
|
|
/// those from normal hash algorithms not really meant to be used for password
|
|
/// hashing.
|
|
/// </summary>
|
|
TDECPasswordHash = class(TDECHashAuthentication);
|
|
|
|
{$IF CompilerVersion < 28.0}
|
|
/// <summary>
|
|
/// Class helper for implementing array concatenation which is not available
|
|
/// in Delphi XE6 or lower.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// SHall be removed as soon as the minimum supported version is XE7 or higher.
|
|
/// </remarks>
|
|
TArrHelper = class
|
|
class procedure AppendArrays<T>(var A: TArray<T>; const B: TArray<T>);
|
|
end;
|
|
{$IFEND}
|
|
|
|
implementation
|
|
|
|
uses
|
|
DECUtil;
|
|
|
|
class function TDECHashAuthentication.IsPasswordHash: Boolean;
|
|
begin
|
|
Result := self.InheritsFrom(TDECPasswordHash);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDFInternal(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer; KDFType: TKDFType): TBytes;
|
|
var
|
|
I, n,
|
|
Rounds, DigestBytes : Integer;
|
|
Dest : PByteArray;
|
|
Count : UInt32;
|
|
HashInstance : TDECHashAuthentication;
|
|
begin
|
|
SetLength(Result, 0);
|
|
DigestBytes := DigestSize;
|
|
Assert(MaskSize >= 0);
|
|
Assert(DataSize >= 0);
|
|
Assert(SeedSize >= 0);
|
|
Assert(DigestBytes >= 0);
|
|
|
|
HashInstance := TDECHashAuthenticationClass(self).Create;
|
|
try
|
|
Rounds := (MaskSize + DigestBytes - 1) div DigestBytes;
|
|
SetLength(Result, Rounds * DigestBytes);
|
|
Dest := @Result[0];
|
|
|
|
|
|
if (KDFType = ktKDF2) then
|
|
n := 1
|
|
else
|
|
n := 0;
|
|
|
|
for I := 0 to Rounds-1 do
|
|
begin
|
|
Count := SwapUInt32(n);
|
|
HashInstance.Init;
|
|
|
|
if (KDFType = ktKDF3) then
|
|
begin
|
|
HashInstance.Calc(Count, SizeOf(Count));
|
|
HashInstance.Calc(Data, DataSize);
|
|
end
|
|
else
|
|
begin
|
|
HashInstance.Calc(Data, DataSize);
|
|
HashInstance.Calc(Count, SizeOf(Count));
|
|
end;
|
|
|
|
HashInstance.Calc(Seed, SeedSize);
|
|
HashInstance.Done;
|
|
Move(HashInstance.Digest[0], Dest[(I) * DigestBytes], DigestBytes);
|
|
|
|
inc(n);
|
|
end;
|
|
|
|
SetLength(Result, MaskSize);
|
|
finally
|
|
HashInstance.Free;
|
|
end;
|
|
end;
|
|
|
|
class function TDECHashAuthentication.MGF1(const Data; DataSize, MaskSize: Integer): TBytes;
|
|
begin
|
|
Result := KDF1(Data, DataSize, NullStr, 0, MaskSize);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.MGF1(const Data: TBytes; MaskSize: Integer): TBytes;
|
|
begin
|
|
Result := KDFInternal(Data[0], Length(Data), NullStr, 0, MaskSize, ktKDF1);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF1(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes;
|
|
begin
|
|
Result := KDFInternal(Data, DataSize, Seed, SeedSize, MaskSize, ktKDF1);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF1(const Data, Seed: TBytes;
|
|
MaskSize: Integer): TBytes;
|
|
begin
|
|
if (length(Seed) > 0) then
|
|
Result := KDFInternal(Data[0], length(Data), Seed[0], length(Seed), MaskSize, ktKDF1)
|
|
else
|
|
Result := KDFInternal(Data[0], length(Data), NullStr, 0, MaskSize, ktKDF1);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF2(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes;
|
|
begin
|
|
Result := KDFInternal(Data, DataSize, Seed, SeedSize, MaskSize, ktKDF2);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF2(const Data, Seed: TBytes; MaskSize: Integer): TBytes;
|
|
begin
|
|
if (length(Seed) > 0) then
|
|
Result := KDFInternal(Data[0], Length(Data), Seed[0], Length(Seed), MaskSize, ktKDF2)
|
|
else
|
|
Result := KDFInternal(Data[0], Length(Data), NullStr, 0, MaskSize, ktKDF2);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF3(const Data; DataSize: Integer; const Seed;
|
|
SeedSize, MaskSize: Integer): TBytes;
|
|
begin
|
|
Result := KDFInternal(Data, DataSize, Seed, SeedSize, MaskSize, ktKDF3);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDF3(const Data, Seed: TBytes; MaskSize: Integer): TBytes;
|
|
begin
|
|
if (length(Seed) > 0) then
|
|
Result := KDFInternal(Data[0], Length(Data), Seed[0], Length(Seed), MaskSize, ktKDF3)
|
|
else
|
|
Result := KDFInternal(Data[0], Length(Data), NullStr, 0, MaskSize, ktKDF3);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDFx(const Data; DataSize: Integer; const Seed; SeedSize, MaskSize: Integer; Index: UInt32 = 1): TBytes;
|
|
// DEC's own KDF, even stronger
|
|
var
|
|
I, J : Integer;
|
|
Count : UInt32;
|
|
R : Byte;
|
|
HashInstance : TDECHashAuthentication;
|
|
begin
|
|
Assert(MaskSize >= 0);
|
|
Assert(DataSize >= 0);
|
|
Assert(SeedSize >= 0);
|
|
Assert(DigestSize > 0);
|
|
|
|
SetLength(Result, MaskSize);
|
|
Index := SwapUInt32(Index);
|
|
|
|
HashInstance := TDECHashAuthenticationClass(self).Create;
|
|
try
|
|
for I := 0 to MaskSize - 1 do
|
|
begin
|
|
HashInstance.Init;
|
|
|
|
Count := SwapUInt32(I);
|
|
HashInstance.Calc(Count, SizeOf(Count));
|
|
HashInstance.Calc(Result[0], I);
|
|
|
|
HashInstance.Calc(Index, SizeOf(Index));
|
|
|
|
Count := SwapUInt32(SeedSize);
|
|
HashInstance.Calc(Count, SizeOf(Count));
|
|
HashInstance.Calc(Seed, SeedSize);
|
|
|
|
Count := SwapUInt32(DataSize);
|
|
HashInstance.Calc(Count, SizeOf(Count));
|
|
HashInstance.Calc(Data, DataSize);
|
|
|
|
HashInstance.Done;
|
|
|
|
R := 0;
|
|
|
|
for J := 0 to DigestSize - 1 do
|
|
R := R xor HashInstance.Digest[J];
|
|
|
|
Result[I] := R;
|
|
end;
|
|
finally
|
|
HashInstance.Free;
|
|
end;
|
|
end;
|
|
|
|
class function TDECHashAuthentication.KDFx(const Data, Seed: TBytes; MaskSize: Integer; Index: UInt32 = 1): TBytes;
|
|
begin
|
|
if (length(Seed) > 0) then
|
|
Result := KDFx(Data[0], Length(Data), Seed[0], Length(Seed), MaskSize, Index)
|
|
else
|
|
Result := KDFx(Data[0], Length(Data), NullStr, Length(Seed), MaskSize, Index)
|
|
end;
|
|
|
|
class function TDECHashAuthentication.MGFx(const Data; DataSize, MaskSize: Integer; Index: UInt32 = 1): TBytes;
|
|
begin
|
|
Result := KDFx(Data, DataSize, NullStr, 0, MaskSize, Index);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.MGFx(const Data: TBytes; MaskSize: Integer; Index: UInt32 = 1): TBytes;
|
|
begin
|
|
Result := KDFx(Data[0], Length(Data), NullStr, 0, MaskSize, Index);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.HMAC(const Key, Text: RawByteString): TBytes;
|
|
begin
|
|
result := HMAC(BytesOf(Key), BytesOf(Text));
|
|
end;
|
|
|
|
class function TDECHashAuthentication.HMAC(const Key, Text: TBytes): TBytes;
|
|
const
|
|
CONST_UINT_OF_0x36 = $3636363636363636;
|
|
CONST_UINT_OF_0x5C = $5C5C5C5C5C5C5C5C;
|
|
var
|
|
HashInstance: TDECHashAuthentication;
|
|
InnerKeyPad, OuterKeyPad: array of Byte;
|
|
I, KeyLength, BlockSize, DigestLength: Integer;
|
|
begin
|
|
HashInstance := TDECHashAuthenticationClass(self).Create;
|
|
try
|
|
BlockSize := HashInstance.BlockSize; // 64 for sha1, ...
|
|
DigestLength := HashInstance.DigestSize;
|
|
KeyLength := Length(Key);
|
|
|
|
SetLength(InnerKeyPad, BlockSize);
|
|
SetLength(OuterKeyPad, BlockSize);
|
|
|
|
I := 0;
|
|
|
|
if KeyLength > BlockSize then
|
|
begin
|
|
Result := HashInstance.CalcBytes(Key);
|
|
KeyLength := DigestLength;
|
|
end
|
|
else
|
|
Result := Key;
|
|
|
|
while I <= KeyLength - SizeOf(NativeUInt) do
|
|
begin
|
|
PNativeUInt(@InnerKeyPad[I])^ := PNativeUInt(@Result[I])^ xor NativeUInt(CONST_UINT_OF_0x36);
|
|
PNativeUInt(@OuterKeyPad[I])^ := PNativeUInt(@Result[I])^ xor NativeUInt(CONST_UINT_OF_0x5C);
|
|
Inc(I, SizeOf(NativeUInt));
|
|
end;
|
|
|
|
while I < KeyLength do
|
|
begin
|
|
InnerKeyPad[I] := Result[I] xor $36;
|
|
OuterKeyPad[I] := Result[I] xor $5C;
|
|
Inc(I);
|
|
end;
|
|
|
|
while I <= BlockSize - SizeOf(NativeUInt) do
|
|
begin
|
|
PNativeUInt(@InnerKeyPad[I])^ := NativeUInt(CONST_UINT_OF_0x36);
|
|
PNativeUInt(@OuterKeyPad[I])^ := NativeUInt(CONST_UINT_OF_0x5C);
|
|
Inc(I, SizeOf(NativeUInt));
|
|
end;
|
|
|
|
while I < BlockSize do
|
|
begin
|
|
InnerKeyPad[I] := $36;
|
|
OuterKeyPad[I] := $5C;
|
|
Inc(I);
|
|
end;
|
|
|
|
HashInstance.Init;
|
|
HashInstance.Calc(InnerKeyPad[0], BlockSize);
|
|
if Length(Text) > 0 then
|
|
HashInstance.Calc(Text[0], Length(Text));
|
|
HashInstance.Done;
|
|
Result := HashInstance.DigestAsBytes;
|
|
|
|
HashInstance.Init;
|
|
HashInstance.Calc(OuterKeyPad[0], BlockSize);
|
|
HashInstance.Calc(Result[0], DigestLength);
|
|
HashInstance.Done;
|
|
|
|
Result := HashInstance.DigestAsBytes;
|
|
finally
|
|
HashInstance.Free;
|
|
end;
|
|
end;
|
|
|
|
class function TDECHashAuthentication.PBKDF2(const Password, Salt: TBytes; Iterations: Integer; KeyLength: Integer): TBytes;
|
|
const
|
|
CONST_UINT_OF_0x36 = $3636363636363636;
|
|
CONST_UINT_OF_0x5C = $5C5C5C5C5C5C5C5C;
|
|
var
|
|
Hash: TDECHashAuthentication;
|
|
I, J, C: Integer;
|
|
BlockCount, HashLengthRounded, SaltLength: Integer;
|
|
PassLength, DigestLength, BlockSize: Integer;
|
|
InnerKeyPad, OuterKeyPad: TBytes;
|
|
SaltEx, T, U, TrimmedKey: TBytes;
|
|
begin
|
|
Hash := TDECHashAuthenticationClass(self).Create;
|
|
try
|
|
// Setup needed parameters
|
|
DigestLength := Hash.DigestSize;
|
|
HashLengthRounded := DigestLength - SizeOf(NativeUInt) + 1;
|
|
BlockCount := Trunc((KeyLength + DigestLength - 1) / DigestLength);
|
|
BlockSize := Hash.BlockSize;
|
|
PassLength := Length(Password);
|
|
SaltLength := Length(Salt);
|
|
SaltEx := Salt;
|
|
SetLength(SaltEx, SaltLength + 4); // reserve 4 bytes for INT_32_BE(i)
|
|
SetLength(T, DigestLength);
|
|
|
|
// Prepare Key for HMAC calculation
|
|
// PrepareKeyForHMAC;
|
|
I := 0;
|
|
if PassLength > BlockSize then
|
|
begin
|
|
TrimmedKey := Hash.CalcBytes(Password);
|
|
PassLength := DigestLength;
|
|
end
|
|
else
|
|
TrimmedKey := Password;
|
|
|
|
SetLength(InnerKeyPad, BlockSize);
|
|
SetLength(OuterKeyPad, BlockSize);
|
|
while I < PassLength do
|
|
begin
|
|
InnerKeyPad[I] := TrimmedKey[I] xor $36;
|
|
OuterKeyPad[I] := TrimmedKey[I] xor $5C;
|
|
Inc(I);
|
|
end;
|
|
while I < BlockSize do
|
|
begin
|
|
InnerKeyPad[I] := $36;
|
|
OuterKeyPad[I] := $5C;
|
|
Inc(I);
|
|
end;
|
|
|
|
// Calculate DK
|
|
for I := 1 to BlockCount do
|
|
begin
|
|
SaltEx[SaltLength + 0] := Byte(I shr 24); // INT_32_BE(i)
|
|
SaltEx[SaltLength + 1] := Byte(I shr 16);
|
|
SaltEx[SaltLength + 2] := Byte(I shr 8);
|
|
SaltEx[SaltLength + 3] := Byte(I shr 0);
|
|
FillChar(T[0], DigestLength, 0); // reset Ti / F
|
|
U := SaltEx; // initialize U to U1 = Salt + INT_32_BE(i)
|
|
// Calculate F(Password, Salt, c, i) = U1 ^ U2 ^ ... ^ Uc
|
|
for C := 1 to Iterations do
|
|
begin
|
|
Hash.Init;
|
|
Hash.Calc(InnerKeyPad[0], BlockSize);
|
|
Hash.Calc(U[0], Length(U));
|
|
Hash.Done;
|
|
U := Hash.DigestAsBytes;
|
|
|
|
Hash.Init;
|
|
Hash.Calc(OuterKeyPad[0], BlockSize);
|
|
Hash.Calc(U[0], DigestLength);
|
|
Hash.Done;
|
|
U := Hash.DigestAsBytes; // Ui
|
|
// F = U1 ^ U2 ^ ... ^ Uc
|
|
J := 0;
|
|
while J < HashLengthRounded do
|
|
begin
|
|
PNativeUInt(@T[J])^ := PNativeUInt(@T[J])^ xor PNativeUInt(@U[J])^;
|
|
Inc(J, SizeOf(NativeUInt));
|
|
end;
|
|
while J < DigestLength do
|
|
begin
|
|
T[J] := T[J] xor U[J];
|
|
Inc(J);
|
|
end;
|
|
end;
|
|
|
|
{$IF CompilerVersion >= 28.0}
|
|
Result := Result + T; // DK += F , DK = DK || Ti
|
|
{$ELSE}
|
|
TArrHelper.AppendArrays<Byte>(Result, T);
|
|
{$IFEND}
|
|
end;
|
|
finally
|
|
Hash.Free;
|
|
end;
|
|
|
|
// Trim to the needed key length
|
|
SetLength(Result, KeyLength);
|
|
end;
|
|
|
|
class function TDECHashAuthentication.PBKDF2(const Password, Salt: RawByteString; Iterations: Integer; KeyLength: Integer): TBytes;
|
|
begin
|
|
result := PBKDF2(BytesOf(Password), BytesOf(Salt), Iterations, KeyLength);
|
|
end;
|
|
|
|
{ TArrHelper }
|
|
|
|
{$IF CompilerVersion < 28.0}
|
|
class procedure TArrHelper.AppendArrays<T>(var A: TArray<T>; const B: TArray<T>);
|
|
var
|
|
i, L: Integer;
|
|
begin
|
|
L := Length(A);
|
|
SetLength(A, L + Length(B));
|
|
for i := 0 to High(B) do
|
|
A[L + i] := B[i];
|
|
end;
|
|
{$IFEND}
|
|
|
|
end.
|