Lazarus

Announcements => Third party => Topic started by: Xor-el on July 30, 2016, 04:13:53 pm

Title: HashLib4Pascal
Post by: Xor-el on July 30, 2016, 04:13:53 pm

HashLib4Pascal is a Delphi/FreePascal compatible library that provides an easy to use interface for computing hashes and checksums of strings (with a specified encoding), files, streams, byte arrays and untyped data to mention but a few. It also supports Incremental Hashing.

Supported Algorithms:

non-cryptographic 32-bits hash algorithms: AP, BKDR, Bernstein, Bernstein1, DEK, DJB,
ELF, FNV, FNV1a, JS, Jenkins3, Murmur2, MurmurHash3_x86_32, OneAtTime, PJW, RS,
Rotating, SDBM, ShiftAndXor, SuperFast, XXHash32.

non-cryptographic 64-bits algorithms: FNV, FNV1a, Murmur2_64, SipHash2_4, XXHash64.

non-cryptographic 128-bits algorithms: MurmurHash3_x86_128, MurmurHash3_x64_128.

checksum algorithms: Adler32, All CRC Variants from CRC3 to CRC64.

cryptographic algorithms: GOST, Grindahl, HAS160, Haval, MD2, MD4, MD5, Panama,
RadioGatun, RIPEMD, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA0, SHA1, SHA2-224,
SHA2-256, SHA2-384, SHA2-512, SHA2-512-224, SHA2-512-256, SHA3-224, SHA3-256, SHA3-384,
SHA3-512,Snefru128, Snefru256, Tiger, Tiger2, WhirlPool.

HMAC for any of the above.

PBKDF2_HMAC for any of the above.

Supported Compilers

FreePascal 3.0.0 and Above.

Delphi 2010 and Above.

Installing the Library.

Method One:

Use the Provided Packages in the "Packages" Folder.

Method Two:

Add the Library Path and Sub Path to your Project Search Path.

Usage Examples.

Coming Soon.
But in the mean time, you can poke around the sources and Unit Tests.

Unit Tests.

To Run Unit Tests,

For FPC 3.0.0 and above

Simply compile and run "HashLib.Tests" project in "FreePascal.Tests" Folder.

For Delphi 2010 and above

Method One (Using DUnit Test Runner)

 To Build and Run the Unit Tests For Delphi 10 Seattle (should be similar for
 other versions)

1). Open Project Options of Unit Test (HashLib.Tests) in "Delphi.Tests" Folder.

2). Change Target to All Configurations (Or "Base" In Older Delphi Versions.)

3). In Output directory add ".\$(Platform)\$(Config)" without the quotes.

4). In Search path add "$(BDS)\Source\DUnit\src" without the quotes.

5). In Unit output directory add "." without the quotes.

6). In Unit scope names (If Available), Delete "DUnitX" from the List.

Press Ok and save, then build and run.

Method Two (Using TestInsight) (Preferred).

1). Download and Install TestInsight.

2). Open Project Options of Unit Test (HashLib.Tests.TestInsight) in "Delphi.Tests"
    Folder.

3). Change Target to All Configurations (Or "Base" In Older Delphi Versions.)

4). In Unit scope names (If Available), Delete "DUnitX" from the List.

5). To Use TestInsight, right-click on the project, then select
    "Enable for TestInsight" or "TestInsight Project".
    Save Project then Build and Run Test Project through TestInsight.

License

This "Software" is Licensed Under MIT License (MIT) .


https://github.com/Xor-el/HashLib4Pascal (https://github.com/Xor-el/HashLib4Pascal)
Title: Re: HashLib4Pascal
Post by: Handoko on July 30, 2016, 06:17:26 pm
Thanks for sharing. Although I don't need it now, but I may use it someday.
Title: Re: HashLib4Pascal
Post by: AlexTP on July 30, 2016, 08:28:41 pm
Same wish. Pls make wiki page like this one http://wiki.freepascal.org/JvXPBar
Title: Re: HashLib4Pascal
Post by: kapibara on July 31, 2016, 03:48:25 am
I made a start here: http://wiki.freepascal.org/HashLib4Pascal

Anyone can continue/correct any mistake.
Title: Re: HashLib4Pascal
Post by: totya on October 01, 2016, 01:25:51 pm
Hi!

Thank you for this library! :) The freepascal examples is missing from this library, but after some time, I can create hash for a file, for example:

Code: Pascal  [Select][+][-]
  1. program project1;
  2.  
  3. uses
  4.   SysUtils,
  5.   HlpIHashInfo,
  6.   HlpConverters,
  7.   HlpHashFactory;
  8.  
  9. var
  10.   MyKey, MyResult: String;
  11.   LHMAC: IHMAC;
  12.  
  13. begin
  14.   LHMAC := THashFactory.THMAC.CreateHMAC(THashFactory.TCrypto.CreateSHA2_512);
  15.  
  16.   MyKey:='';
  17.   LHMAC.Key := TConverters.ConvertStringToBytes(MyKey, TEncoding.UTF8);
  18.  
  19.   MyResult:= LHMAC.ComputeFile('testfile.exe').ToString();
  20.  
  21.   WriteLn(MyResult);
  22.  
  23.   ReadLn();
  24.  
  25. end.
  26.  

But I have one problem, the computed SHA512 hash is different from created by the TotalCommander. Why are different these hash?

Thanks!
Title: Re: HashLib4Pascal
Post by: Xor-el on October 01, 2016, 03:05:51 pm
you are using the HMAC version and I assume that is not what you want.
for calculating a normal SHA512 hash, do this

program project1 ;

// posted from my phone so formatting might be bad.
//also attached a little demo

 uses
  SysUtils,
 HlpHashFactory;

 var
 MyResult: String;

 begin

 MyResult := THashFactory.TCrypto.CreateSHA2_512().ComputeFile( 'testfile.exe' ).ToString();
 WriteLn ( MyResult) ;

 ReadLn() ;

 end .
Title: Re: HashLib4Pascal
Post by: totya on October 01, 2016, 03:25:08 pm
Hi!

Thanks for the quick answer!

I see what is the "HAMAC", this is the "Hash-based message authentication code".

Well, my question is, if the key is empty, see my last message:

Code: Pascal  [Select][+][-]
  1. MyKey:='';
  2. LHMAC.Key := TConverters.ConvertStringToBytes(MyKey, TEncoding.UTF8);;

then the result must be equal with the non-HAMC verison, don't you think?

It isn't a uneccessary question, because if the result is good, I can use same as code with two times:
1. if key is empty, then the result same as non-HAMAC version, comparable to other hash, like as TC created hash
2. if key is present, then result is the HAMAC version.
Title: Re: HashLib4Pascal
Post by: Xor-el on October 01, 2016, 03:34:18 pm
HMAC has a totally different implementation so "key or no key", the results will be different because of the okeypad and ikeypad values contencated in the HMAC algorithm.

you can read up more on HMAC here

https://en.m.wikipedia.org/wiki/Hash-based_message_authentication_code
Title: Re: HashLib4Pascal
Post by: totya on October 01, 2016, 03:35:49 pm
HMAC has a totally different implementation so "key or no key"

Okay, thanks for this information, and thanks again for this library! :)
Title: Re: HashLib4Pascal
Post by: Xor-el on October 01, 2016, 03:38:48 pm
you welcome.
Title: Re: HashLib4Pascal
Post by: Petr Nehez on October 04, 2016, 03:01:21 pm
I have started to use this great library and have found 1 issue and made 2 small improvements for inline hints.
Changes are in my fork. Check it out at https://github.com/petr-nehez/HashLib4Pascal/commits/master.
Title: Re: HashLib4Pascal
Post by: Xor-el on October 04, 2016, 05:12:14 pm
Hi, thanks for your improvements.
will surely add it up to the master branch.
one question by the way,
which compiler gave the inline hint?
Title: Re: HashLib4Pascal
Post by: Petr Nehez on October 05, 2016, 04:26:06 am
Hi, thanks for your improvements.
Hi, you're welcome :)

which compiler gave the inline hint?
The thing with inline hints was interesting and it was D2010.
I was wondering why I was getting the hints in our main project and not in testing one.
And finally it turned out that you have to have this directive active:
Code: Pascal  [Select][+][-]
  1. {$RTTI EXPLICIT FIELDS([]) PROPERTIES([]) METHODS([])}
Title: Re: HashLib4Pascal
Post by: Xor-el on October 05, 2016, 08:21:15 am
thanks for replying.
I do not think disabling rtti is the best solution even though it works.
however I feel we could just give Delphi 2010 what it wants like you did in your commit.

according to your commit, I can see that only two units, hlpmurmur2 and hlpsiphash2_4 gives that hint?
please confirm if I am correct.

thanks.
Title: Re: HashLib4Pascal
Post by: Petr Nehez on October 05, 2016, 10:09:19 am
I do not think disabling rtti is the best solution even though it works.
I do agree but we use RTTI in our project only for certain classes and that's why the hints have appeared.

according to your commit, I can see that only two units, hlpmurmur2 and hlpsiphash2_4 gives that hint?
please confirm if I am correct.
Yes, exactly.
Title: Re: HashLib4Pascal
Post by: Thaddy on October 05, 2016, 10:45:59 am
RTTI should never ever be used in security related code.
This library contains multiple "secure" hashes.
Is this really essential for this library?
Then it is unusable. Because that is runtime readable code.
There are many more alternatives that don't make that mistake.

Maybe I misunderstood. In that case please explain.
Title: Re: HashLib4Pascal
Post by: Xor-el on October 05, 2016, 12:10:41 pm
I do not think disabling rtti is the best solution even though it works.
I do agree but we use RTTI in our project only for certain classes and that's why the hints have appeared.

according to your commit, I can see that only two units, hlpmurmur2 and hlpsiphash2_4 gives that hint?
please confirm if I am correct.
Yes, exactly.

thanks for confirming.
my next commit will fix the "inline hints" by adding "sysutils" to the specified units as done in your commit.

thanks.

Title: Re: HashLib4Pascal
Post by: Xor-el on October 05, 2016, 12:15:41 pm
RTTI should never ever be used in security related code.
This library contains multiple "secure" hashes.
Is this really essential for this library?
Then it is unusable. Because that is runtime readable code.
There are many more alternatives that don't make that mistake.

Maybe I misunderstood. In that case please explain.

lol, this library does not use rtti.
at least I don't.
Title: Re: HashLib4Pascal
Post by: Thaddy on October 05, 2016, 12:31:33 pm
Then it's ok.
I will look into its merits.
Title: Re: HashLib4Pascal
Post by: Xor-el on October 06, 2016, 01:12:44 am
Hello all,
I have made a commit that

removed generics usage (thanks Arnaud Bouchez).
some minor optimizations and refactoring.
fixed "inline hints" in Delphi 2010 (thanks Petr. Nehez).
some restructuring.
at least 10% speed improvements (especially on FreePascal).
minor fixes etc.

recommended update  :)
Title: Re: HashLib4Pascal
Post by: Xor-el on October 30, 2016, 08:30:37 pm
Quote
v1.1 Release.

Below is a Summary of (but not limited to) changes in this release.

* optimizations and performance improvements in various hashes
especially those that descends from TBlockHash.
* fix for timing attack when comparing the internal state of hashes.

https://github.com/Xor-el/HashLib4Pascal
Title: Re: HashLib4Pascal
Post by: Xor-el on December 04, 2016, 10:01:58 pm
** Optimizations in Various Hashes like Siphash, MurmurHash etc.

** Cleanup of Some Unused Methods.

** Fixed Some "Potential Bugs".

** Fixed a slowdown (for Delphi in SHA 2 based Hashes) that was introduced in the previous release.

** Various Improvements.

https://github.com/Xor-el/HashLib4Pascal
Title: Re: HashLib4Pascal
Post by: Xor-el on January 02, 2017, 10:59:28 am
Just released v1.4 of this Library.

https://github.com/Xor-el/HashLib4Pascal

see github commits for change log since v1.1 release.  :P
Title: Re: HashLib4Pascal
Post by: Xor-el on May 28, 2018, 10:42:59 am
Just released v2.4 of this Library.

https://github.com/Xor-el/HashLib4Pascal

added Streebog 256 and 512 hash (GOST3411_2012_256, GOST3411_2012_512)

see github commits for other change log since v1.4 release.  :P
Title: Re: HashLib4Pascal
Post by: Thaddy on May 28, 2018, 11:07:11 am
- There is still RTTI, which is not a good idea. I wasn't the only one to complain.
- There are some readability issues regarding string types:
You have a lot of {$ifdef FPC} to determine UnicodeString. you need only one per unit:
Code: Pascal  [Select][+][-]
  1. unit xxx
  2. {$ifdef fpc}{$mode delphiunicode}{$H+}{$endif}
  3. ....
  4. //One per unit! instead of one per codeblock
  5.  
That cleans things up a bit.....
Since you are trying to support both Delphi and FreePascal, try to limit the amount of ifdefs. They are likely not necessary when the compiler is in one of the Delphi modes.
I tested this with your repository on a couple of affected units and it looks much better. You can also do the above in your standard include file.
Title: Re: HashLib4Pascal
Post by: Xor-el on May 28, 2018, 11:13:19 am
- There is still RTTI, which is not a good idea. I wasn't the only one to complain.
- There are some readability issues regarding string types:
You have a lot of {$ifdef FPC} to determine UnicodeString. you need only one per unit:
Code: Pascal  [Select][+][-]
  1. unit xxx
  2. {$ifdef fpc}{$mode delphiunicode}{$H+}{$endif}
  3. ....
  4. //One per unit! instead of one per codeblock
  5.  
That cleans things up a bit.....

can you please point out where RTTI was used?
I don't use RTTI except may be an RTL Unit like SysUtils calls it in.

Regarding the UncodeString stuff, will look into it.
Thanks for the tip.
Title: Re: HashLib4Pascal
Post by: Thaddy on May 28, 2018, 11:16:30 am
I added to my comment and our posts crossed when I was still testing. Plz read it again.
As for RTTI scan the code for published.
Title: Re: HashLib4Pascal
Post by: Xor-el on May 28, 2018, 11:21:19 am
I added to my comment and our posts crossed when I was still testing. Plz read it again.
As for RTTI scan the code for published.
thanks for replying.
so I just scanned the codebase for published.
it is only used in the test projects, as we all know, DUnit uses RTTI to find TestFixtures so unfortunately there is nothing I can really do about it.
The true codebase is not affected.
Thanks once again.
Title: Re: HashLib4Pascal
Post by: ASBzone on August 13, 2018, 03:25:19 am
Xor-El,

Is there any way to only include the ciphers that are desired in a particular application?

I have several projects where I need only MD5, SHA1 and SHA2,  as an example, or SHA1, SHA2 and SHA3.

Currently, the only (or easiest) way to use the encyrption is HlpHashFactory, and that brings every hash algorithm into the mix.

Regards,

Title: Re: HashLib4Pascal
Post by: Xor-el on August 13, 2018, 08:40:11 am
Xor-El,

Is there any way to only include the ciphers that are desired in a particular application?

I have several projects where I need only MD5, SHA1 and SHA2,  as an example, or SHA1, SHA2 and SHA3.

Currently, the only (or easiest) way to use the encyrption is HlpHashFactory, and that brings every hash algorithm into the mix.

Regards,

yes it is very possible but why do you want to complicate things for yourself?
if it's because of size, don't worry, smartlinking in FreePascal removes unused details in the final binary.

well to answer your question, you can do this.

Code: Pascal  [Select][+][-]
  1. uses
  2. SysUtils,
  3. HlpIHash,
  4. HlpMD5;
  5.  
  6. var
  7. MD5Hash: IHash;
  8. Value : TBytes;
  9. begin
  10.  
  11. MD5Hash := TMD5.Create();
  12. Value := MD5Hash.ComputeBytes(TBytes.Create($01, $02));
  13.  
  14. end.
  15.  
Title: Re: HashLib4Pascal
Post by: ASBzone on August 13, 2018, 11:53:49 pm
yes it is very possible but why do you want to complicate things for yourself?

I can see why you did it the way you did, but for me, it doesn't seem complicated at all to add just the groupings or families that I need at any given time.


if it's because of size, don't worry, smartlinking in FreePascal removes unused details in the final binary.

I expected Smartlinking to help in that way, but for this particular package, it hasn't played out that way, I'm afraid.


well to answer your question, you can do this.

Code: Pascal  [Select][+][-]
  1. uses
  2. SysUtils,
  3. HlpIHash,
  4. HlpMD5;
  5.  
  6. var
  7. MD5Hash: IHash;
  8. Value : TBytes;
  9. begin
  10.  
  11. MD5Hash := TMD5.Create();
  12. Value := MD5Hash.ComputeBytes(TBytes.Create($01, $02));
  13.  
  14. end.
  15.  

Much appreciated.  I will see how this works for me.
Title: Re: HashLib4Pascal
Post by: ASBzone on August 14, 2018, 12:57:51 am
Code: Pascal  [Select][+][-]
  1. uses
  2. SysUtils,
  3. HlpIHash,
  4. HlpMD5;
  5.  
  6. var
  7. MD5Hash: IHash;
  8. Value : TBytes;
  9. begin
  10.  
  11. MD5Hash := TMD5.Create();
  12. Value := MD5Hash.ComputeBytes(TBytes.Create($01, $02));
  13.  
  14. end.
  15.  


I get the following error for this:

Error: Incompatible types: got "IHashResult" expected "TBytes"

SomeString := MD5Hash.ComputeBytes(TBytes.Create($01,$02)).ToString();    // This doesn't generate any errors

SomeString := MD5Hash.ComputeFile(ThisFile).ToString();    // This results in an access violation.


The following, of course, works:
SomeString := LowerCase(THashFactory.TCrypto.CreateMD5().ComputeFile(ThisFile).ToString());

But I want to be able to target only select hash families, as stated earlier.
Title: Re: HashLib4Pascal
Post by: Xor-el on August 14, 2018, 08:27:57 am
You are right, my bad.
My previous example will not compile.
I wrote the code in my browser.

Regarding
Quote
SomeString := MD5Hash.ComputeFile(ThisFile).ToString();    // This results in an access violation.
Are you sure you created the MD5Hash instance before calling MD5Hash.ComputeFile?

Can you upload your sample code that throws the access violation?
by the way, what version of Lazarus/FPC are you using?
Title: Re: HashLib4Pascal
Post by: ASBzone on August 14, 2018, 10:02:40 pm
You are right, my bad.
My previous example will not compile.

No worries.  I was able to get around it.


Are you sure you created the MD5Hash instance before calling MD5Hash.ComputeFile?

Yes, I did.  It turns out that I was using the wrong file variable.  In the process of making a smaller sample to upload, I realized my error.   The sample, of course, works.

Admittedly, had I done the smaller sample from the beginning, this would have been fine.


Can you upload your sample code that throws the access violation?
by the way, what version of Lazarus/FPC are you using?

I'm using 32-bit Lazarus 1.8.4 and FPC 3.0.4 on Windows 10 x64

And here's the now fully working code.  :)

Code: Pascal  [Select][+][-]
  1.  
  2. {$MODE objfpc}{$LONGSTRINGS ON}
  3. {$APPTYPE CONSOLE}
  4. {$R *.res}
  5.  
  6. Program GenerateHashes;
  7.  
  8. Uses
  9.         SysUtils, StrUtils, HlpIHash, HlpMD5, HlpSHA1, HlpSHA2_256, HlpSHA3;
  10.  
  11.  
  12. Const
  13.         TestText = 'This is some test text';
  14.    MD5TextResult = 'c93037c9c2d7be6d6ad51a0e694fad5f';
  15.    SHA1TextResult = '510ec59b0eac4a84dab06d910d7f69445a2d54c6';
  16.    SHA2TextResult = 'f4544fb4c5af57bf6a823fbb623de44363cb3384928ca95360baede7b2e578c5';
  17.    SHA3TextResult = 'd3baea49d0668c3c0d20c9ff64cc97a5947db1333d9e8c53e57c8b283557c15b458f5923fe6ef9b1704c889f0edf0ba9';
  18.  
  19.         TestFile = 'C:\Temp\TestFile.TXT';
  20.    MD5FileResult = '5fa5f3ef591f54557564da635b169ad7';
  21.    SHA1FileResult = 'f2f739fb7c1a755563520d818abf6c892295e292';
  22.    SHA2FileResult = 'ef76f11b7e364532fac399bcf97c63ecff4b3697cf693b0ee2022011e6ba79cb';
  23.    SHA3FileResult = '2514447bfca1950b8718f3d55dba830fee11da94d9af315725b06763c27c099dea4ddee417c2ddcbeaf00e72d14b82a0';
  24.  
  25.  
  26. Var
  27.    MD5Hash, SHA1Hash, SHA2Hash, SHA3Hash : IHash;
  28.    ThisHash : String;
  29.  
  30.  
  31. Begin
  32.    WriteLn('Calculating hash for following file: ', TestFile);
  33.    MD5Hash  := TMD5.Create();
  34.    ThisHash := MD5Hash.ComputeFile(TestFile).ToString();
  35.    WriteLn('MD5 Hash (Test) ........... ', ThisHash);
  36.    WriteLn('MD5 Hash (Real) ........... ', MD5FileResult);
  37.    WriteLn;
  38.  
  39.    SHA1Hash := TSHA1.Create();
  40.    ThisHash := SHA1Hash.ComputeFile(TestFile).ToString();
  41.    WriteLn('SHA1 Hash (Test) .......... ', ThisHash);
  42.    WriteLn('SHA1 Hash (Real) .......... ', SHA1FileResult);
  43.    WriteLn;
  44.  
  45.    SHA2Hash := TSHA2_256.Create();
  46.    ThisHash := SHA2Hash.ComputeFile(TestFile).ToString();
  47.    WriteLn('SHA2(256) Hash (Test) ..... ', ThisHash);
  48.    WriteLn('SHA2(256) Hash (Real) ..... ', SHA2FileResult);
  49.    WriteLn;
  50.  
  51.    SHA3Hash := TSHA3_384.Create();
  52.    ThisHash := SHA3Hash.ComputeFile(TestFile).ToString();
  53.    WriteLn('SHA3(384) Hash (Test) ..... ', ThisHash);
  54.    WriteLn('SHA3(384) Hash (Real) ..... ', SHA3FileResult);
  55.    WriteLn;
  56.  
  57.    WriteLn;
  58.    WriteLn('Calculating hash for following text: ', TestText);
  59.    MD5Hash  := TMD5.Create();
  60.    ThisHash := MD5Hash.ComputeString(TestText, TEncoding.UTF8).ToString();
  61.    WriteLn('MD5 Hash (Test) ........... ', ThisHash);
  62.    WriteLn('MD5 Hash (Real) ........... ', MD5TextResult);
  63.    WriteLn;
  64.  
  65.    SHA1Hash := TSHA1.Create();
  66.    ThisHash := SHA1Hash.ComputeString(TestText, TEncoding.UTF8).ToString();
  67.    WriteLn('SHA1 Hash (Test) .......... ', ThisHash);
  68.    WriteLn('SHA1 Hash (Real) .......... ', SHA1TextResult);
  69.    WriteLn;
  70.  
  71.    SHA2Hash := TSHA2_256.Create();
  72.    ThisHash := SHA2Hash.ComputeString(TestText, TEncoding.UTF8).ToString();
  73.    WriteLn('SHA2(256) Hash (Test) ..... ', ThisHash);
  74.    WriteLn('SHA2(256) Hash (Real) ..... ', SHA2TextResult);
  75.    WriteLn;
  76.  
  77.    SHA3Hash := TSHA3_384.Create();
  78.    ThisHash := SHA3Hash.ComputeString(TestText, TEncoding.UTF8).ToString();
  79.    WriteLn('SHA3(384) Hash (Test) ..... ', ThisHash);
  80.    WriteLn('SHA3(384) Hash (Real) ..... ', SHA3TextResult);
  81.    WriteLn;
  82. End.
  83.  
  84.  

Thanks again for your assistance.   Very much appreciated.
Title: Re: HashLib4Pascal
Post by: Xor-el on August 14, 2018, 10:46:25 pm
you are welcome.  ;)
Title: Re: HashLib4Pascal
Post by: Xor-el on September 08, 2018, 10:15:38 am
Just released v2.8 of this Library.

https://github.com/Xor-el/HashLib4Pascal (https://github.com/Xor-el/HashLib4Pascal)

New Important Feature

Added the ability to persist the internal state of a hash object via the .Clone() method.
this is useful when computing hashes of large data sets which share the same initial subdata.
similar to the python hashlib.copy method
https://docs.python.org/3/library/hashlib.html#hashlib.hash.copy (https://docs.python.org/3/library/hashlib.html#hashlib.hash.copy)
Return a copy (“clone”) of the hash object. This can be used to efficiently compute the digests of data that share a common initial subdata.

Regards.
Title: Re: HashLib4Pascal
Post by: Xor-el on December 16, 2018, 10:19:15 pm
Just released v3.0 of this Library.

https://github.com/Xor-el/HashLib4Pascal

Added support for Shake128 and Shake256 XOF.

Regards.
Title: Re: HashLib4Pascal
Post by: gorkamorka on December 19, 2018, 11:39:22 pm
Thank You  :)
Title: Re: HashLib4Pascal
Post by: ASBzone on December 20, 2018, 01:02:28 am
Just released v3.0 of this Library.

https://github.com/Xor-el/HashLib4Pascal

Added support for Shake128 and Shake256 XOF.

Regards.

Thanks for the update, @Xor-el
Title: Re: HashLib4Pascal
Post by: Xor-el on March 15, 2019, 07:47:24 am
Just added support for Argon2 KDF (2i, 2d and 2id) in version 3.4

Commit link

https://github.com/Xor-el/HashLib4Pascal/commit/fabd0a57bf6ef3665a4e0f753fe724d393dc17a4

** Now updated in OPM
Title: Re: HashLib4Pascal
Post by: totya on May 26, 2019, 11:47:53 pm
Hi!

Thanks for this great library!

My question is, what is the default blocksize for TBlake2B?

And... I want 32 byte blocksize for TBlake2B (64bit) like as TBlake2S, so:

Code: Pascal  [Select][+][-]
  1.     {$IFDEF WIN32}
  2.     Hash := TBlake2S.Create();
  3.     {$ENDIF}
  4.     {$IFDEF WIN64}
  5.     Hash := TBlake2B.Create(32, 65535);
  6.     {$ENDIF}
  7.  

I got error after run: "config."
Title: Re: HashLib4Pascal
Post by: Xor-el on May 27, 2019, 09:14:39 am
Hi!

Thanks for this great library!

My question is, what is the default blocksize for TBlake2B?

And... I want 32 byte blocksize for TBlake2B (64bit) like as TBlake2S, so:

Code: Pascal  [Select][+][-]
  1.     {$IFDEF WIN32}
  2.     Hash := TBlake2S.Create();
  3.     {$ENDIF}
  4.     {$IFDEF WIN64}
  5.     Hash := TBlake2B.Create(32, 65535);
  6.     {$ENDIF}
  7.  

I got error after run: "config."

1. I guess what you mean is HashSize (which is probably what you mean). Default HashSize for Blake2B is 512bit (64 bytes)

2. With respect to HashSize, this is what you need

https://github.com/Xor-el/HashLib4Pascal/blob/master/HashLib/src/Base/HlpHashFactory.pas#L386
Title: Re: HashLib4Pascal
Post by: totya on May 27, 2019, 08:00:27 pm
1. I guess what you mean is HashSize (which is probably what you mean). Default HashSize for Blake2B is 512bit (64 bytes)

2. With respect to HashSize, this is what you need

https://github.com/Xor-el/HashLib4Pascal/blob/master/HashLib/src/Base/HlpHashFactory.pas#L386

Hi!

Thank you for the answer!

You are guess right, I'm sorry. But the problem is stay, because the last line hint for Hash := TBlake2B.Create() this:
Quote
Create(a_hash_size, a_block_size:int32)
So, if I set a_hash_size and  a_block_size why the result is: "config. exception"? Something wrong with it.

Second, thanks for this tip:
Code: Pascal  [Select][+][-]
  1. class function CreateBlake2B_256(): IHash; static;

Seems to me I can use it in this way:
Code: Pascal  [Select][+][-]
  1. {$IFDEF WIN64}
  2.   Hash := THashFactory.TCrypto.CreateBlake2B_256();
  3. {$ENDIF}
  4.  

Third, I read the svn changelog, tree hashing for blake2 is supported. I think this it is about speed up on multi-cores systems. How can I use it/force it, or works automatically?

Thank you, and sorry for my bad english.
Title: Re: HashLib4Pascal
Post by: Xor-el on May 27, 2019, 08:15:30 pm
What are you trying to achieve in question 1?

Tree Hashing in Blake has nothing to speed up.
The speed up I think you are referring to is Blake Parallel Hashing found in Blake2bp and Blake2sp which HashLib4Pascal doesn't implement at the moment.
Title: Re: HashLib4Pascal
Post by: totya on May 27, 2019, 08:29:23 pm
What are you trying to achieve in question 1?

Trying? I'm done... with this code
Code: Pascal  [Select][+][-]
  1. THashFactory.TCrypto.CreateBlake2B_256();

It's simple, my log memo show many hash value, and with default size of log memo, the 64 byte hash result is very long, and wrapped in the window, it's ugly, thats all. ;)

I choose between of two methods, because 2b is faster on 64bit, 2s is faster under 64 bit (source: docs of Blake2). I don't test these speeds with your implementation, but I guess it's true here too.
Title: Re: HashLib4Pascal
Post by: Xor-el on May 27, 2019, 08:40:25 pm
Yes it's true, blake2b is faster on 64bit and blake2s is faster on 32bit.
Title: Re: HashLib4Pascal
Post by: Xor-el on May 30, 2019, 09:26:40 am
updated to version 3.6

- changelog

* fixed bug in Shake128 and Shake256 for large output sizes
* add Keccak288 support
* add full support for tree hashing mode in Blake2b and Blake2s as per python's HashLib specification.

** now updated in OPM
Title: Re: HashLib4Pascal
Post by: Thaddy on May 30, 2019, 09:32:50 am
keccak = sha3?
Title: Re: HashLib4Pascal
Post by: Xor-el on May 30, 2019, 10:43:16 am
keccak = sha3?

nope, Keccak is the original implementation before NIST made a modification to the finalization padding bit from 0x1 to 0x6 which resulted in SHA3. implementation wise, they are very similar but output is totally different.
Title: Re: HashLib4Pascal
Post by: totya on June 07, 2019, 10:21:09 pm
Hi!

I'd like selectable hash algorithm, so I use common variable for it:

Code: Pascal  [Select][+][-]
  1. var Hash: IHash;

and for example:
Code: Pascal  [Select][+][-]
  1. Hash := THashFactory.TCrypto.CreateBlake2S_256();

So, can I use this next line with IHash variable?
Code: Pascal  [Select][+][-]
  1. Hash := THashFactory.THash32.CreateXXHash32;

Because as I see the result is IHashWithKey
Code: Pascal  [Select][+][-]
  1. class function THashFactory.THash32.CreateXXHash32: IHashWithKey;

Thanks!
Title: Re: HashLib4Pascal
Post by: Xor-el on June 07, 2019, 11:54:12 pm
Hi!

I'd like selectable hash algorithm, so I use common variable for it:

Code: Pascal  [Select][+][-]
  1. var Hash: IHash;

and for example:
Code: Pascal  [Select][+][-]
  1. Hash := THashFactory.TCrypto.CreateBlake2S_256();

So, can I use this next line with IHash variable?
Code: Pascal  [Select][+][-]
  1. Hash := THashFactory.THash32.CreateXXHash32;

Because as I see the result is IHashWithKey
Code: Pascal  [Select][+][-]
  1. class function THashFactory.THash32.CreateXXHash32: IHashWithKey;

Thanks!

Code: Pascal  [Select][+][-]
  1. IHashWithKey
inherits from
Code: Pascal  [Select][+][-]
  1. IWithKey
which inherits from
Code: Pascal  [Select][+][-]
  1. IHash
so yes
Code: Pascal  [Select][+][-]
  1. Hash := THashFactory.THash32.CreateXXHash32;
is assignment compatible to
Code: Pascal  [Select][+][-]
  1. IHash
and can be used.
You only need to care about
Code: Pascal  [Select][+][-]
  1. IHashWithKey
if you want to use a different Key from the default that is made available internally.
Title: Re: HashLib4Pascal
Post by: totya on June 08, 2019, 11:04:43 am
Thank you for the answer!  :)
Title: Re: HashLib4Pascal
Post by: The ZipGenius Team on September 09, 2019, 05:37:24 pm
Hello.
I'm trying to use HashLib4Pascal to hash a string using Shake-256. I wrote this sample test code but it produces nothing (an empty string). Am I missing anything?

Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  3.   HlpIHash, HlpIHashResult, HlpSHA3;
  4.  
  5. ...
  6.  
  7. procedure TForm1.Button1Click(Sender: TObject);
  8. var
  9.  cHash: IHash;
  10.  Value : string;
  11. begin
  12.   cHash := TShake_256.Create();
  13.   Value := cHash.ComputeString(Edit1.Text, TEncoding.UTF8).ToString();
  14.   Memo1.lines.Clear;
  15.   Memo1.lines.Text:=Value;
  16. end;
  17.  

Thanks!
Title: Re: HashLib4Pascal
Post by: Thaddy on September 09, 2019, 07:42:47 pm
Hello.
I'm trying to use HashLib4Pascal to hash a string using Shake-256. I wrote this sample test code but it produces nothing (an empty string). Am I missing anything?
Code: Pascal  [Select][+][-]
  1.    cHash := THashFactory.TShake_256.Create();
Title: Re: HashLib4Pascal
Post by: Xor-el on September 09, 2019, 08:06:29 pm
Hello.
I'm trying to use HashLib4Pascal to hash a string using Shake-256. I wrote this sample test code but it produces nothing (an empty string). Am I missing anything?

Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  3.   HlpIHash, HlpIHashResult, HlpSHA3;
  4.  
  5. ...
  6.  
  7. procedure TForm1.Button1Click(Sender: TObject);
  8. var
  9.  cHash: IHash;
  10.  Value : string;
  11. begin
  12.   cHash := TShake_256.Create();
  13.   Value := cHash.ComputeString(Edit1.Text, TEncoding.UTF8).ToString();
  14.   Memo1.lines.Clear;
  15.   Memo1.lines.Text:=Value;
  16. end;
  17.  

Thanks!

Hi,
You do have to understand that shake256 is a XOF https://en.m.wikipedia.org/wiki/Category:Extendable-output_functions (https://en.m.wikipedia.org/wiki/Category:Extendable-output_functions) so it can output variable length output based on your input.
Unfortunately, you didn't specify what output size you wanted that's why your output is empty.
In this example below, I will use output XOF size as 256 bits (32 bytes)

Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  3.   HlpIHash, HlpHashFactory;
  4.  
  5. ...
  6.  
  7. procedure TForm1.Button1Click(Sender: TObject);
  8. var
  9.  cHash: IHash;
  10.  Value : string;
  11. begin
  12.   cHash := THashFactory.TXOF.CreateShake_256(256);
  13.   Value := cHash.ComputeString(Edit1.Text, TEncoding.UTF8).ToString();
  14.   Memo1.lines.Clear;
  15.   Memo1.lines.Text:=Value;
  16. end;
  17.  
Title: Re: HashLib4Pascal
Post by: The ZipGenius Team on September 10, 2019, 11:36:05 am
Hi,
You do have to understand that shake256 is a XOF https://en.m.wikipedia.org/wiki/Category:Extendable-output_functions (https://en.m.wikipedia.org/wiki/Category:Extendable-output_functions) so it can output variable length output based on your input.
Unfortunately, you didn't specify what output size you wanted that's why your output is empty.
In this example below, I will use output XOF size as 256 bits (32 bytes)

Code: Pascal  [Select][+][-]
  1. uses
  2.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls,
  3.   HlpIHash, HlpHashFactory;
  4.  
  5. ...
  6.  
  7. procedure TForm1.Button1Click(Sender: TObject);
  8. var
  9.  cHash: IHash;
  10.  Value : string;
  11. begin
  12.   cHash := THashFactory.TXOF.CreateShake_256(256);
  13.   Value := cHash.ComputeString(Edit1.Text, TEncoding.UTF8).ToString();
  14.   Memo1.lines.Clear;
  15.   Memo1.lines.Text:=Value;
  16. end;
  17.  

Thank you very much! That was what I was looking for. In fact, that's what I did some time ago when I had to develop in C# using BouncyCastle. Unfortunately, I didn't find any example regarding Shake.
Thanks again :)
Title: Re: HashLib4Pascal
Post by: Xor-el on December 02, 2019, 11:14:57 pm
updated to version 4.0

- changelog from 3.6 to 4.0
* add Scrypt support
* add CShake128 and CShake256 support
* add Blake2XS and Blake2XB XOF support
* add KMAC128 and KMAC256 (XOF) support
* add Blake2BMAC and Blake2SMAC support
* add Blake2BP and Blake2SP support
* performance enhancements in various hashes and checksums including but not limited to XXHash32, XXHash64, Siphash, MurmurHash, Adler32, CRC32 etc

** Now updated in OPM
Title: Re: HashLib4Pascal
Post by: trevor on January 20, 2020, 07:09:30 am
Is it possible to make a password for Apache (like in htpasswd (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html)) with HashLib4Pascal?  :-\
Title: Re: HashLib4Pascal
Post by: trevor on January 20, 2020, 07:22:44 am
Is it possible to make a password for Apache (like in htpasswd (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html)) with HashLib4Pascal?  :-\

BTW I need a
Quote
"{SHA}" + Base64-encoded SHA-1 digest of the password
only (-nbs).

Thank you :)
Title: Re: HashLib4Pascal
Post by: Xor-el on January 20, 2020, 08:20:32 am
Is it possible to make a password for Apache (like in htpasswd (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html)) with HashLib4Pascal?  :-\
Hi trevor, what exact algorithms does Apache Password use and in what order is it used?
Title: Re: HashLib4Pascal
Post by: Xor-el on January 20, 2020, 08:23:55 am
BTW I need a
Quote
"{SHA}" + Base64-encoded SHA-1 digest of the password
only (-nbs).
HashLib4Pascal has support for SHA-1 including a host of other algorithms.
for Base64, you can use https://github.com/Xor-el/SimpleBaseLib4Pascal (https://github.com/Xor-el/SimpleBaseLib4Pascal)
Title: Re: HashLib4Pascal
Post by: trevor on January 20, 2020, 10:07:32 am
BTW I need a
Quote
"{SHA}" + Base64-encoded SHA-1 digest of the password
only (-nbs).
HashLib4Pascal has support for SHA-1 including a host of other algorithms.
for Base64, you can use https://github.com/Xor-el/SimpleBaseLib4Pascal (https://github.com/Xor-el/SimpleBaseLib4Pascal)

Thanks! I'm trying to figure out but so far unsuccessfully:

Code: Pascal  [Select][+][-]
  1. var
  2.   shah: IHash;
  3. begin
  4.   shah := TSHA1.Create();
  5.   Memo1.Text := shah.ComputeString(Edit1.Text, TEncoding.UTF8);
  6. end;

Now I need to wrap 'shah' into base64 right? Something like 'TBase64(shah.ComputeString(Edit1.Text, TEncoding.UTF8))'?

Code: Pascal  [Select][+][-]
  1.     class property Default: IBase64 read GetDefault;
  2.     class property DefaultNoPadding: IBase64 read GetDefaultNoPadding;
  3.     class property UrlEncoding: IBase64 read GetUrlEncoding;
  4.     class property XmlEncoding: IBase64 read GetXmlEncoding;
  5.     class property RegExEncoding: IBase64 read GetRegExEncoding;
  6.     class property FileEncoding: IBase64 read GetFileEncoding;

So what should I use?
Title: Re: HashLib4Pascal
Post by: Xor-el on January 20, 2020, 10:28:38 am
@trevor, below is a Demo that computes the output using the SHA-1 algorithm and compares the result to the one found at https://httpd.apache.org/docs/2.4/misc/password_encryptions.html (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html).
You will need the HashLib4Pascal and SimpleBaseLib4Pascal packages as they are dependencies.

Code: Pascal  [Select][+][-]
  1. program PassDemo;
  2.  
  3. uses
  4.   SysUtils,
  5.   SbpBase64,
  6.   HlpHashFactory;
  7.  
  8. var
  9.   Password, Result: string;
  10. begin
  11.   Password := 'myPassword';
  12.   Result := '{SHA}' + TBase64.Default.Encode(
  13.     THashFactory.TCrypto.CreateSHA1().ComputeString(
  14.     Password, TEncoding.UTF8).GetBytes());
  15.   WriteLn(Result);
  16.   Assert(Result = '{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=');
  17.   ReadLn;
  18. end.
  19.  
Title: Re: HashLib4Pascal
Post by: trevor on January 20, 2020, 10:42:52 am
@trevor, below is a Demo that computes the output using the SHA-1 algorithm and compares the result to the one found at https://httpd.apache.org/docs/2.4/misc/password_encryptions.html (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html).
You will need the HashLib4Pascal and SimpleBaseLib4Pascal packages as they are dependencies.

Code: Pascal  [Select][+][-]
  1. program PassDemo;
  2.  
  3. uses
  4.   SysUtils,
  5.   SbpBase64,
  6.   HlpHashFactory;
  7.  
  8. var
  9.   Password, Result: string;
  10. begin
  11.   Password := 'myPassword';
  12.   Result := '{SHA}' + TBase64.Default.Encode(
  13.     THashFactory.TCrypto.CreateSHA1().ComputeString(
  14.     Password, TEncoding.UTF8).GetBytes());
  15.   WriteLn(Result);
  16.   Assert(Result = '{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=');
  17.   ReadLn;
  18. end.
  19.  

Thanks a lot (https://i.gifer.com/origin/8f/8fec972f7dccfc995ee799f8044a0341_w200.gif)! You're the best! Exactly what I need!  ;D
Title: Re: HashLib4Pascal
Post by: Xor-el on February 18, 2020, 09:11:58 am
updated to version 4.1

- changelog from 4.0 to 4.1

* add Blake3 and Blake3XOF support
* performance improvements especially XOF Related.

** Now updated in OPM
Title: Re: HashLib4Pascal
Post by: lvincent7 on April 27, 2020, 05:21:15 am
@trevor, below is a Demo that computes the output using the SHA-1 algorithm and compares the result to the one found at https://httpd.apache.org/docs/2.4/misc/password_encryptions.html (https://httpd.apache.org/docs/2.4/misc/password_encryptions.html).
You will need the HashLib4Pascal and SimpleBaseLib4Pascal packages as they are dependencies.

Code: Pascal  [Select][+][-]
  1. program PassDemo;
  2.  
  3. uses
  4.   SysUtils,
  5.   SbpBase64,
  6.   HlpHashFactory;
  7.  
  8. var
  9.   Password, Result: string;
  10. begin
  11.   Password := 'myPassword';
  12.   Result := '{SHA}' + TBase64.Default.Encode(
  13.     THashFactory.TCrypto.CreateSHA1().ComputeString(
  14.     Password, TEncoding.UTF8).GetBytes());
  15.   WriteLn(Result);
  16.   Assert(Result = '{SHA}VBPuJHI7uixaa6LQGWx4s+5GKNE=');
  17.   ReadLn;
  18. end.
  19.  

Thanks a lot (https://i.gifer.com/origin/8f/8fec972f7dccfc995ee799f8044a0341_w200.gif)! You're the best! Exactly what I need!  ;D

Hi Xor-el,

I really appreciate this Hash library. Thank you. Although I'm still new with hashing and cryptos, I would like to ask if the example above can be fitted similar to the freeformatter website?: https://www.freeformatter.com/hmac-generator.html#ad-output (https://www.freeformatter.com/hmac-generator.html#ad-output)

I'm a little bit confused what corresponds to the string and which correspond to the secret key in the example.

Thanks again!

Title: Re: HashLib4Pascal
Post by: ASBzone on April 27, 2020, 03:03:15 pm
updated to version 4.1

- changelog from 4.0 to 4.1

* add Blake3 and Blake3XOF support
* performance improvements especially XOF Related.

** Now updated in OPM




Thanks for your work on this encryption library, Xor-el.

Question for you:  Based on the following URL, I had expected Blake3 to perform faster than it does.

https://www.infoq.com/news/2020/01/blake3-fast-crypto-hash/ (https://www.infoq.com/news/2020/01/blake3-fast-crypto-hash/)

Here's the result I receive from compiling the performance benchmark project:


Adler32 Throughput: 1008.67 MB/s with Blocks of 64 KB
CRC-32_PKZIP_Generic Throughput: 272.60 MB/s with Blocks of 64 KB
CRC32_PKZIP_Fast Throughput: 1362.69 MB/s with Blocks of 64 KB
MurmurHash3_x86_32 Throughput: 1059.08 MB/s with Blocks of 64 KB
XXHash32 Throughput: 2872.15 MB/s with Blocks of 64 KB
SipHash2_4 Throughput: 490.02 MB/s with Blocks of 64 KB
XXHash64 Throughput: 5555.96 MB/s with Blocks of 64 KB
MurmurHash3_x86_128 Throughput: 1161.00 MB/s with Blocks of 64 KB
MurmurHash3_x64_128 Throughput: 2166.02 MB/s with Blocks of 64 KB
MD5 Throughput: 406.56 MB/s with Blocks of 64 KB
SHA1 Throughput: 203.96 MB/s with Blocks of 64 KB
SHA2_256 Throughput: 105.44 MB/s with Blocks of 64 KB
SHA2_512 Throughput: 166.15 MB/s with Blocks of 64 KB
SHA3_256 Throughput: 114.94 MB/s with Blocks of 64 KB
SHA3_512 Throughput: 62.42 MB/s with Blocks of 64 KB
Blake2B_256 Throughput: 253.35 MB/s with Blocks of 64 KB
Blake2B_512 Throughput: 250.90 MB/s with Blocks of 64 KB
Blake2S_128 Throughput: 135.23 MB/s with Blocks of 64 KB
Blake2S_256 Throughput: 134.46 MB/s with Blocks of 64 KB
Blake2BP_512 Throughput: 229.02 MB/s with Blocks of 64 KB
Blake2SP_256 Throughput: 122.54 MB/s with Blocks of 64 KB
Blake3_256 Throughput: 171.33 MB/s with Blocks of 64 KB

The results are for the 64-bit version running on an older i5 system.

On my Ryzen 2700X, I get the following (higher scores, but relatively similar ratios):


Adler32 Throughput: 1640.79 MB/s with Blocks of 64 KB
CRC-32_PKZIP_Generic Throughput: 473.77 MB/s with Blocks of 64 KB
CRC32_PKZIP_Fast Throughput: 2661.13 MB/s with Blocks of 64 KB
MurmurHash3_x86_32 Throughput: 2870.29 MB/s with Blocks of 64 KB
XXHash32 Throughput: 6589.35 MB/s with Blocks of 64 KB
SipHash2_4 Throughput: 875.60 MB/s with Blocks of 64 KB
XXHash64 Throughput: 11974.94 MB/s with Blocks of 64 KB
MurmurHash3_x86_128 Throughput: 3170.98 MB/s with Blocks of 64 KB
MurmurHash3_x64_128 Throughput: 5952.44 MB/s with Blocks of 64 KB
MD5 Throughput: 645.21 MB/s with Blocks of 64 KB
SHA1 Throughput: 362.31 MB/s with Blocks of 64 KB
SHA2_256 Throughput: 206.44 MB/s with Blocks of 64 KB
SHA2_512 Throughput: 331.27 MB/s with Blocks of 64 KB
SHA3_256 Throughput: 216.15 MB/s with Blocks of 64 KB
SHA3_512 Throughput: 117.08 MB/s with Blocks of 64 KB
Blake2B_256 Throughput: 622.10 MB/s with Blocks of 64 KB
Blake2B_512 Throughput: 609.48 MB/s with Blocks of 64 KB
Blake2S_128 Throughput: 262.54 MB/s with Blocks of 64 KB
Blake2S_256 Throughput: 264.10 MB/s with Blocks of 64 KB
Blake2BP_512 Throughput: 542.54 MB/s with Blocks of 64 KB
Blake2SP_256 Throughput: 237.92 MB/s with Blocks of 64 KB
Blake3_256 Throughput: 347.90 MB/s with Blocks of 64 KB

I skipped implementing Blake2 altogether, since it seems to be a little bit more involved to use than the other hash functions, and I expected Blake3 to be faster anyway.   (It would have helped if I had run the benchmark first.)

Any thoughts on the above?   Is there something that I am overlooking?

I compiled the benchmark as is.

Regards,
TinyPortal © 2005-2018