Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions HashLib/src/Base/HlpHashFactory.pas
Original file line number Diff line number Diff line change
Expand Up @@ -611,6 +611,11 @@ TPBKDF_Scrypt = class sealed(TObject)
/// <param name="ABlockSize">the block size, must be >= 1.</param>
/// <param name="AParallelism">Parallelization parameter. Must be a positive integer less than or equal to
/// <code>(System.High(Int32) div (128 * ABlockSize * 8))</code>.</param>
/// <param name="ARelaxCostRestriction">
/// When <c>True</c>, skips the <c>N &lt; 2^(128*r/8)</c> constraint
/// from RFC 7914 that rejects <c>r=1</c> when <c>N &gt;= 65536</c>.
/// See <c>ValidatePBKDF_ScryptInputs</c> for details and references.
/// </param>
/// <returns>
/// The PBKDF_Scrypt KDF Interface Instance <br />
/// </returns>
Expand All @@ -621,8 +626,8 @@ TPBKDF_Scrypt = class sealed(TObject)
/// The cost, blocksize or parallelism is Invalid.
/// </exception>
class function CreatePBKDF_Scrypt(const APasswordBytes,
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32)
: IPBKDF_Scrypt; static;
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32;
ARelaxCostRestriction: Boolean = False): IPBKDF_Scrypt; static;

end;

Expand Down Expand Up @@ -1569,11 +1574,11 @@ class function TKDF.TPBKDF_Argon2.CreatePBKDF_Argon2(const APassword
{ TKDF.TPBKDF_Scrypt }

class function TKDF.TPBKDF_Scrypt.CreatePBKDF_Scrypt(const APasswordBytes,
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32)
: IPBKDF_Scrypt;
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32;
ARelaxCostRestriction: Boolean): IPBKDF_Scrypt;
begin
Result := TPBKDF_ScryptNotBuildInAdapter.Create(APasswordBytes, ASaltBytes,
ACost, ABlockSize, AParallelism);
ACost, ABlockSize, AParallelism, ARelaxCostRestriction);
end;

end.
48 changes: 40 additions & 8 deletions HashLib/src/KDF/HlpPBKDF_ScryptNotBuildInAdapter.pas
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,40 @@ TPBKDF_ScryptNotBuildInAdapter = class sealed(TKDF, IPBKDF_Scrypt,

public

/// <summary>
/// Validates Scrypt input parameters.
/// </summary>
/// <param name="ACost">CPU/Memory cost parameter N.</param>
/// <param name="ABlockSize">Block size parameter r.</param>
/// <param name="AParallelism">Parallelization parameter p.</param>
/// <param name="ARelaxCostRestriction">
/// When <c>True</c>, skips the <c>N &lt; 2^(128*r/8)</c> constraint
/// from RFC 7914 (which rejects <c>r=1</c> when <c>N &gt;= 65536</c>).
/// Colin Percival (Scrypt creator and RFC co-author) confirmed this
/// constraint was an accidental error; the intended bound was
/// <c>N &lt; 2^(128*r*8)</c>, which is trivially satisfied.
/// The Scrypt reference implementation (Tarsnap) does not enforce it,
/// and the Ethereum Web3 Secret Storage standard depends on
/// <c>N=262144, r=1, p=8</c> which violates this erroneous constraint.
/// <list type="bullet">
/// <item>RFC errata: https://www.rfc-editor.org/errata/rfc7914 (5971, 5972, 5973)</item>
/// <item>Author confirmation: https://github.com/golang/go/issues/33703#issuecomment-568198927</item>
/// <item>OpenSSL: https://github.com/openssl/openssl/issues/24650</item>
/// <item>Go: https://github.com/golang/go/issues/33703</item>
/// <item>geth: https://github.com/ethereum/go-ethereum/issues/19977</item>
/// <item>eth-account: https://github.com/ethereum/eth-account/issues/181</item>
/// <item>noble-hashes: https://github.com/paulmillr/noble-hashes/issues/61</item>
/// <item>RustCrypto: https://github.com/RustCrypto/password-hashes/issues/546</item>
/// <item>Rustic: https://github.com/rustic-rs/rustic/issues/1394</item>
/// <item>Node.js: https://github.com/nodejs/node/pull/28799</item>
/// </list>
/// </param>
class procedure ValidatePBKDF_ScryptInputs(ACost, ABlockSize,
AParallelism: Int32); static;
AParallelism: Int32; ARelaxCostRestriction: Boolean = False); static;

constructor Create(const APasswordBytes, ASaltBytes: THashLibByteArray;
ACost, ABlockSize, AParallelism: Int32);
ACost, ABlockSize, AParallelism: Int32;
ARelaxCostRestriction: Boolean = False);

destructor Destroy; override;

Expand Down Expand Up @@ -409,7 +438,7 @@ class function TPBKDF_ScryptNotBuildInAdapter.MFCrypt(const APasswordBytes,
end;

class procedure TPBKDF_ScryptNotBuildInAdapter.ValidatePBKDF_ScryptInputs(ACost,
ABlockSize, AParallelism: Int32);
ABlockSize, AParallelism: Int32; ARelaxCostRestriction: Boolean);
var
LMaxParallel: Int32;
begin
Expand All @@ -419,10 +448,12 @@ class procedure TPBKDF_ScryptNotBuildInAdapter.ValidatePBKDF_ScryptInputs(ACost,
raise EArgumentHashLibException.CreateRes(@SInvalidCost);
end;

// Only value of ABlockSize that cost (as an int) could be exceeded for is 1
if ((ABlockSize = 1) and (ACost >= 65536)) then
if (not ARelaxCostRestriction) then
begin
raise EArgumentHashLibException.CreateRes(@SBlockSizeAndCostIncompatible);
if ((ABlockSize = 1) and (ACost >= 65536)) then
begin
raise EArgumentHashLibException.CreateRes(@SBlockSizeAndCostIncompatible);
end;
end;

if (ABlockSize < 1) then
Expand All @@ -446,10 +477,11 @@ procedure TPBKDF_ScryptNotBuildInAdapter.Clear();
end;

constructor TPBKDF_ScryptNotBuildInAdapter.Create(const APasswordBytes,
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32);
ASaltBytes: THashLibByteArray; ACost, ABlockSize, AParallelism: Int32;
ARelaxCostRestriction: Boolean);
begin
Inherited Create();
ValidatePBKDF_ScryptInputs(ACost, ABlockSize, AParallelism);
ValidatePBKDF_ScryptInputs(ACost, ABlockSize, AParallelism, ARelaxCostRestriction);
FPasswordBytes := System.Copy(APasswordBytes);
FSaltBytes := System.Copy(ASaltBytes);
FCost := ACost;
Expand Down
Loading