Browse Source

Merge branch 'master' of https://github.com/PascalCoin/PascalCoin

PascalCoin 6 years ago
parent
commit
fb7797ba52
30 changed files with 4142 additions and 110 deletions
  1. 245 26
      PIP/PIP-0027.md
  2. 1 1
      src/libraries/cryptolib4pascal/ClpBufferedCipherBase.pas
  3. 195 0
      src/libraries/cryptolib4pascal/ClpBufferedStreamCipher.pas
  4. 263 0
      src/libraries/cryptolib4pascal/ClpChaChaEngine.pas
  5. 26 1
      src/libraries/cryptolib4pascal/ClpCipherUtilities.pas
  6. 1 0
      src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas
  7. 3 1
      src/libraries/cryptolib4pascal/ClpGeneratorUtilities.pas
  8. 36 0
      src/libraries/cryptolib4pascal/ClpIBufferedStreamCipher.pas
  9. 36 0
      src/libraries/cryptolib4pascal/ClpIChaChaEngine.pas
  10. 36 0
      src/libraries/cryptolib4pascal/ClpISalsa20Engine.pas
  11. 1 11
      src/libraries/cryptolib4pascal/ClpISignersEncodings.pas
  12. 36 0
      src/libraries/cryptolib4pascal/ClpISpeckEngine.pas
  13. 31 15
      src/libraries/cryptolib4pascal/ClpIStreamCipher.pas
  14. 36 0
      src/libraries/cryptolib4pascal/ClpIXSalsa20Engine.pas
  15. 1 0
      src/libraries/cryptolib4pascal/ClpParameterUtilities.pas
  16. 502 0
      src/libraries/cryptolib4pascal/ClpSalsa20Engine.pas
  17. 1090 0
      src/libraries/cryptolib4pascal/ClpSpeckEngine.pas
  18. 114 0
      src/libraries/cryptolib4pascal/ClpXSalsa20Engine.pas
  19. 15 0
      src/libraries/hashlib4pascal/HlpArgon2TypeAndVersion.pas
  20. 1 0
      src/libraries/hashlib4pascal/HlpCRC32Fast.pas
  21. 76 10
      src/libraries/hashlib4pascal/HlpHashFactory.pas
  22. 60 0
      src/libraries/hashlib4pascal/HlpIHashInfo.pas
  23. 3 3
      src/libraries/hashlib4pascal/HlpPBKDF2_HMACNotBuildInAdapter.pas
  24. 1331 0
      src/libraries/hashlib4pascal/HlpPBKDF_Argon2NotBuildInAdapter.pas
  25. 1 0
      src/libraries/hashlib4pascal/HlpSipHash.pas
  26. 2 0
      src/libraries/hashlib4pascal/README.md
  27. 0 2
      src/libraries/simplebaselib4pascal/SbpBase16.pas
  28. 0 1
      src/libraries/simplebaselib4pascal/SbpBase32.pas
  29. 0 4
      src/libraries/simplebaselib4pascal/SbpBase64.pas
  30. 0 35
      src/libraries/simplebaselib4pascal/SimpleBaseLib.inc

+ 245 - 26
PIP/PIP-0027.md

@@ -11,7 +11,7 @@
 
 ## Summary
 
-This PIP proposes a backwards compatible addressing scheme that enables an infinite address-space within PascalCoin. The usage of these extension addresses can be employed within existing infrastructure such as wallets and exchanges now as well as future Layer-2 dapps.
+This PIP proposes a backwards compatible addressing scheme that enables an infinite address-space within PascalCoin. The usage of these extension addresses can be employed immediately by existing infrastructure such as wallets and exchanges and in future Layer-2 dapps.
 
 ## Motivation
 
@@ -19,7 +19,7 @@ PascalCoin currently allows users to send/receive operations between accounts us
 
 These account numbers are a limited (and commoditized) resource which form a finite address-space (note: this is fundamental to SafeBox design and it's infinite-scaling capability).
 
-PascalCoin will provide an infinite address-space (similar toother other crypto-currencies) via "decentralized custodial accounts" which are Layer-2 dapps governed via a Layer-2 Proof-of-Stake overlay network.
+PascalCoin will provide an infinite address-space (similar to other other crypto-currencies) via "decentralized custodial accounts" which are Layer-2 dapps governed via a Layer-2 Proof-of-Stake overlay network.
 
 Before rolling out this Layer-2 infrastructure, PascalCoin first needs to establish an addressing-scheme for this infinite address-space.
 
@@ -42,12 +42,12 @@ An E-PASA has the following unique characteristics:
 An Extended PASA is defined by the below EBNF grammar:
 
 ```
-    EPASA              = PASA, [ ExtendedAddress ], [ ':', EPASAChecksum ] ;
+    EPASA              = PASA, [ ExtendedAddress ], [ ':', ExtendedChecksum ] ;
     PASA               = ( AccountName | AccountNumber ) ;
     AccountName        = Pascal64String ;
     AccountNumber      = Integer, "-", Checksum ;
     Checksum           = Digit, Digit ;
-    EPASAChecksum      = HexByte, HexByte ;
+    ExtendedChecksum   = HexByte, HexByte ;
     ExtendedAddress    = ( PublicPayload | ReceiverEncPayload | SenderEncPayload | PasswordEncPayload ) ;
     PublicPayload      = "[", [ Payload ], "]" ; 
     ReceiverEncPayload = "(", [ Payload ], ")" ;
@@ -56,24 +56,31 @@ An Extended PASA is defined by the below EBNF grammar:
     Payload            = ( """, SafeAnsiString, """ | "0", "x", HexString | Base58String ) ;
     Password           = SafeAnsiString
     SafeAnsiString     = SafeAnsiChar, { SafeAnsiChar } ;
-    SafeAnsiChar       = (" " | "!" | EscapeChar """ | "#" | "$" | "%" | "&" | "'" | "(" | ")" | "*" | "+" | "," | "-" | "." | "/" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | ":" | ";" | "<" | "=" | ">" | "?" | "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "[" | EscapeChar "\" | "]" | "^" | "_" | "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "{" | "|" | EscapeChar "}" | "~") ;
+    SafeAnsiChar       = (" " | "!" | EscapeChar, """ | "#" | "$" | "%" | "&" | "'" | EscapeChar, "(" | EscapeChar, ")" | "*" | "+" | "," | "-" | "." | "/" | "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | EscapeChar, ":" | ";" | EscapeChar, "<" | "=" | EscapeChar, ">" | "?" | "@" | "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | EscapeChar, "[" | EscapeChar, "\" | EscapeChar, "]" | "^" | "_" | "`" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | EscapeChar, "{" | "|" | EscapeChar, "}" | "~") ;
     Pascal64String     = SafePascal64Char, { Pascal64Char } ;
     Pascal64Char       = (Digit | SafePascal64Char)
-    SafePascal64Char   = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "-", "+", "{", "}", "[", "]", "_", ":", "`", "|", "<", ">", ",", ".", "?", "/", "~", ")" "-", "+", "{", "}", "[", "]", "_", ":", "`", "|", "<", ">", ",", ".", "?", "/", "~" ) ; 
+    SafePascal64Char   = ( "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" | "!" | "@" | "#" | "$" | "%" | "^" | "&" | "*" | EscapeChar, "(" | EscapeChar, ")" | "-" | "+" | EscapeChar, "{" | EscapeChar, "}" | EscapeChar, "[" | EscapeChar, "]" | "_" | EscapeChar, ":" | "`" | "|" | EscapeChar, "<" | EscapeChar, ">" | "," | "." | "?" | "/" | "~" ) ; 
     HexString          = HexByte { HexByte } ;
     HexByte            = HexNibble, HexNibble ;
     HexNibble          = ( Digit | "a" | "b" | "c" | "d" | "e" | "f" ) ;       (* no uppercase hex allowed *)
     Base58String       = Base58Char, { Base58Char } ;
     Base58Char         = ( NaturalDigit | Base58UpperChar | Base58LowerChar ) ; 
-    Base58UpperChar    = ( "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" ) ;     (* missing I, O *)
-    Base58LowerChar    = ( "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" ) ; (* missing l *)
+    Base58UpperChar    = ( "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "J" | "K" | "L" | "M" | "N" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" ) ;  (* missing I, O *)
+    Base58LowerChar    = ( "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" ) ; (* missing l *)
     Integer            = NaturalDigit, { Digit } ;
     Digit              = ( "0" | NaturalDigit ) ;
     NaturalDigit       = ( "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ) ;
     EscapeChar         = "\" ;
-```
 
-| Rule               | Explanation                                                                                                   |
+**NOTES**: 
+ * Text payload and passwords are restricted to ANSI charset subset range 32..126
+ * The following characters are escaped in Pascal64 and SafeAnsi strings): **:**, **\\**, **"**, **[**, **]**, **(**, **), **<**, **>**,**{**, **}**
+ * Escape character is always: **\\**
+
+The above rules can be interpreted as follows:
+
+```
+| Rule               | Interpretation                                                                                                   |
 | -----------------: | :------------------------------------------------------------------------------------------------------------ |
 | EPASA              | This is a layer-2 address, fully backwards compatible as Layer-1 address                                      |
 | PASA               | This is the standard layer-1 address of the receiver account (account number or account name)                 |
@@ -84,7 +91,7 @@ An Extended PASA is defined by the below EBNF grammar:
 | SenderEncPayload   | A payload which is ECIES encrypted using the senders public key (only sender can decrypt EPASA)               |
 | PasswordEncPayload | A payload which is AES256 encrypted using the specified password                                              |
 | Payload            | The actual payload data, specified it an well-defined encoding                                                |
-| PayloadChecksum    | An UINT16 specified by two hexbytes (4 hexnibbles) that denotes a checksum of payload, used to ensure payload consistency (prevents typo/copy-paste errors)  |
+| ExtendedChecksum   | A checksum of all the preceding text in the E-PASA (necessary to prevent typo-errors)                         |
 | Password           | The password used in PasswordEndPayload. Must be specified as a SafeAnsiString (chars 32..126)                |
 | Pascal64String     | An ANSI string involving a limited subset used for account names (cannot start with a digit)                  |
 | SafeAnsiString     | An ANSI string involvolving subset characters 32..126                                                         |
@@ -92,11 +99,6 @@ An Extended PASA is defined by the below EBNF grammar:
 | HexString          | A hexadecimal-encoded string prefixed with a 0x. Every byte specified by two hexdigits, lower-case            |
 
 
-**NOTES**: 
- * Text payload and passwords are restricted to ANSI charset subset range 32..126
- * The following characters **\\**, **"**, and **}** must be escaped in ASCII payloads/AES passwords via preceding **\\**
- * Payload content is 
-
 #### Validation Rules
 
 #### AccountNumber Checksum
@@ -117,9 +119,9 @@ These strings are used to denote an account names and conform to the following r
 - By definition, they must **not** start with a digit. 
 - String length must between 3..64 inclusive.
 
-#### E-PASA Checksum
+#### Extended Checksum
 
-In order to avoid data entry errors, the EPASA is checksummed. In short, the E-PASA checksum is simply the 16-bit MurMur3 hash of all preceding text. 
+In order to avoid data entry errors, the EPASA is checksummed via an Extended Checksum. In short, the Extended Checksum is simply the 16-bit MurMur3 hash of all preceding text. 
 
 Formally, the EPASA checksum is defined as follows:
 
@@ -133,7 +135,7 @@ Formally, the EPASA checksum is defined as follows:
        ToHexStringLE   = converts the 16bit unsigned integer argument into 4 hexadecimal characters in little-endian
 ```
 
-**IMPORTANT**: Whilst the E-PASA grammar allows optional E-PASA checksums, this is purely for convenience. Implementations are expected to automatically fill-in the checksum if not specified in the input.
+**IMPORTANT**: Whilst the E-PASA grammar allows optional Extended Checksums, this is purely for convenience. Implementations are expected to automatically fill-in the checksum if not specified in the input.
 
 #### Payload Lengths
 
@@ -155,16 +157,69 @@ The following validation rules must be applied to Payload lengths
 
 #### Payload Type
 
-In order for an operation receiver to **automatically decode** an E-PASA from the raw network payload in the **exact same form** that it was entered by the sender, an additional *PayloadType* field is required for all operations that carry a Payload.
-
-The PayloadType indicates how the raw network payload was encrypted and has general-purposes usage beyond this PIP.
+In order for a recipient of an operation to **automatically** and **deterministically** decode an E-PASA from a raw network payload, the PascalCoin protocol needs additional data to describe the Payload.
 
-For E-PASA, the PayloadType allows the receiver to deterministically and unambiguously derive the E-PASA used by the sender, including the encryption type and payload type (i.e. ascii/hex/base58) as well as if an account name was used instead of an account number.
+This PIP proposes pre-fixing all Payloads in operations with a single-byte value called the **PayloadType**. The PayloadType describes the encryption and encoding of the Payload.
 
-This capability is fundamental for using E-PASA as an *address-space* in Layer-2 applications. Without this, a Layer-2 application could decode a single raw network payload in a multitude of E-PASA forms breaking the 1-1 relationship necessary to establish uniqueness.
+PayloadType will allow E-PASA's to be deterministically decoded by the recipient and prevent ambiguous decodings.  This capability is fundamental for using E-PASA as an *address-space* since uniqueness requires a 1-1 mapping. With this feature, Layer-2 apps can safely use E-PASA as Layer-2 addresses.
 
 #### Payload Type Specification:
 
+```csharp
+	[Flags]
+	public enum PayloadType {
+
+		/// <summary>
+		/// Payload encryption and encoding method not specified.
+		/// </summary>
+		NonDeterministic = 0x00000000,
+
+		/// <summary>
+		/// Unencrypted, public payload.
+		/// </summary>
+		Public = 0x00000001,
+
+		/// <summary>
+		/// ECIES encrypted using recipient accounts public key.
+		/// </summary>		
+		RecipientKeyEncrypted = 0x00000010,
+
+		/// <summary>
+		/// ECIES encrypted using sender accounts public key.
+		/// </summary>
+		SenderKeyEncrypted = 0x00000100,
+
+		/// <summary>
+		/// AES encrypted using pwd param
+		/// </summary>
+		PasswordEncrypted = 0x00001000,
+
+		/// <summary>
+		/// Payload data encoded in ASCII
+		/// </summary>
+		AsciiFormatted = 0x00010000,
+
+		/// <summary>
+		/// Payload data encoded in HEX
+		/// </summary>
+		HexFormatted = 0x00100000,
+
+		/// <summary>
+		/// Payload data encoded in Base58
+		/// </summary>
+		Base58Formatted = 0x01000000,
+
+		/// <summary>
+		/// E-PASA addressed by account name (not number).
+		/// </summary>
+		AddressedByName = 0x10000000,
+
+	}
+```
+
+The values are interpreted as follows:
+
+
 | Value    | Interpretation                                                                  |
 | -------: | :------------------------------------------------------------------------------ |
 | 00000000 | Non-deterministic (requires manual decoding by receiver and **not** an E-PASA)  |
@@ -315,8 +370,172 @@ For Layer-2 applications the ability for a receiver to auto-decode the E-PASA vi
  
 ## Reference Implementation
 
-WIP
+The following regex parses an e-pasa:
+```
+((?<AccountNumber>[1-9]\d+)(?:(?<ChecksumDelim>-)(?<Checksum>\d{2}))?|(?<AccountName>(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|`|\||\\<|\\>|,|\.|\?|/|~)(a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|0|1|2|3|4|5|6|7|8|9|!|@|#|\$|%|\^|&|\*|\\\(|\\\)|-|\+|\\\{|\\\}|\\\[|\\]|_|\\:|`|\||\\<|\\>|,|\.|\?|/|~){2,63}))(?:(?<PayloadStartChar>[\[\(<\{])(?<PayloadContent>"( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+"|0x(?:[0-9a-f]{2})+|[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+)?(?:(?<PayloadPasswordDelim>:){1}(?<PayloadPassword>( |!|\\"|#|\$|%|&|'|\\\(|\\\)|\*|\+|,|-|\.|/|0|1|2|3|4|5|6|7|8|9|\\:|;|\\<|=|\\>|\?|@|A|B|C|D|E|F|G|H|I|J|K|L|M|N|O|P|Q|R|S|T|U|V|W|X|Y|Z|\\\[|\\\\|\\]|\^|_|`|a|b|c|d|e|f|g|h|i|j|k|l|m|n|o|p|q|r|s|t|u|v|w|x|y|z|\\\{|\||\\\}|~)+)?)?(?<PayloadEndChar>[]\)>\}]))?(?:(?<ExtendedChecksumDelim>:)(?<ExtendedChecksum>[0-9a-f]{2}[0-9a-f]{2}))?
+```
+
+After matching with the above regex, the named groups need to be extracted and validated as the below snippet shows
+
+```csharp
+
+	public bool TryParse(string epasaText, out EPasa epasa, out EPasaErrorCode errorCode) {
+		errorCode = EPasaErrorCode.Success;
+		epasa = new EPasa();
+
+		if (string.IsNullOrEmpty(epasaText)) {
+			errorCode = EPasaErrorCode.BadFormat;
+			return false;
+		}
+
+		var match = _epasaRegex.Match(epasaText);
+		var checksumDelim = match.Groups["ChecksumDelim"].Success ? match.Groups["ChecksumDelim"].Value : null;
+		var accountNumber = match.Groups["AccountNumber"].Success ? match.Groups["AccountNumber"].Value : null;
+		var accountChecksum = match.Groups["AccountChecksum"].Success ? match.Groups["AccountChecksum"].Value : null;
+		var accountName = match.Groups["AccountName"].Success ? match.Groups["AccountName"].Value : null;
+		var payloadStartChar = match.Groups["PayloadStartChar"].Success ? match.Groups["PayloadStartChar"].Value : null;
+		var payloadEndChar = match.Groups["PayloadEndChar"].Success ? match.Groups["PayloadEndChar"].Value : null;
+		var payloadContent = match.Groups["PayloadContent"].Success ? match.Groups["PayloadContent"].Value : null;
+		var payloadPasswordDelim = match.Groups["PayloadPasswordDelim"].Success ? match.Groups["PayloadPasswordDelim"].Value : null;
+		var payloadPassword = match.Groups["PayloadPassword"].Success ? match.Groups["PayloadPassword"].Value : null;
+		var extendedChecksumDelim = match.Groups["ExtendedChecksumDelim"].Success ? match.Groups["ExtendedChecksumDelim"].Value : null;
+		var extendedChecksum = match.Groups["ExtendedChecksum"].Success ? match.Groups["ExtendedChecksum"].Value : null;
+
+		// Check parsed completely
+		if (epasaText != match.Value) {
+			errorCode = EPasaErrorCode.BadFormat;
+			return false;
+		}
+
+		if (accountName != null) {
+			// Account Name
+			if (string.IsNullOrEmpty(accountName)) {
+				errorCode = EPasaErrorCode.BadFormat;
+				return false;
+			}
+			epasa.PayloadType = epasa.PayloadType | PayloadType.AddressedByName;
+			epasa.AccountName = accountName;
+		} else {
+			// Account Number
+			if (!uint.TryParse(accountNumber, out var accNo)) {
+				errorCode = EPasaErrorCode.AccountNumberTooLong;
+				return false;
+			}
+			epasa.PayloadType = epasa.PayloadType ^ PayloadType.AddressedByName;
+			epasa.Account = accNo;
+
+			if (checksumDelim != null) {
+				if (!uint.TryParse(accountChecksum, out var accChecksum)) {
+					errorCode = EPasaErrorCode.AccountChecksumInvalid;
+					return false;
+				}
+				if (!AccountHelper.IsValidAccountChecksum(epasa.Account.Value, accChecksum)) {
+					errorCode = EPasaErrorCode.BadChecksum;
+					return false;
+				}
+				epasa.AccountChecksum = accChecksum;
+			}
+		}
+
+		// Encryption type			
+		switch (payloadStartChar) {
+			case null:
+				break;
+			case "[":
+				if (payloadEndChar != "]") {
+					errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
+					return false;
+				}
+				epasa.PayloadType = epasa.PayloadType | PayloadType.Public;
+				break;
+			case "(":
+				if (payloadEndChar != ")") {
+					errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
+					return false;
+				}
+				epasa.PayloadType = epasa.PayloadType | PayloadType.RecipientKeyEncrypted;
+				break;
+			case "<":
+				if (payloadEndChar != ">") {
+					errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
+					return false;
+				}
+				epasa.PayloadType = epasa.PayloadType | PayloadType.SenderKeyEncrypted;
+				break;
+			case "{":
+				if (payloadEndChar != "}") {
+					errorCode = EPasaErrorCode.MismatchedPayloadEncoding;
+					return false;
+				}
+				epasa.PayloadType = epasa.PayloadType | PayloadType.PasswordEncrypted;
+				break;
+			default:
+				throw new NotSupportedException($"Unrecognized start character '{payloadStartChar}'");
+
+		}
+ 
+		// Password
+		if (epasa.PayloadType.HasFlag(PayloadType.PasswordEncrypted)) {
+			if (payloadPasswordDelim == null) {
+				errorCode = EPasaErrorCode.MissingPassword;
+				return false;
+			}
+			epasa.Password = payloadPassword ?? "";
+		} else if (payloadPasswordDelim != null) {
+			errorCode = EPasaErrorCode.UnusedPassword;
+			return false;
+		}
+
+		// Payload 
+		if (payloadStartChar != null) {
+			if (payloadContent == null) {
+				epasa.Payload = string.Empty;
+			} else if (payloadContent.StartsWith("\"")) {
+				epasa.PayloadType = epasa.PayloadType | PayloadType.AsciiFormatted;
+				epasa.Payload = payloadContent.Trim('"');					
+			} else if (payloadContent.StartsWith("0x")) {
+				epasa.PayloadType = epasa.PayloadType | PayloadType.HexFormatted;
+				epasa.Payload = payloadContent.Substring(2);
+			} else  {
+				epasa.PayloadType = epasa.PayloadType | PayloadType.Base58Formatted;
+				epasa.Payload = payloadContent;
+			} 
+			epasa.Payload = payloadContent;
+		}
+
+		// Payload Lengths
+		if (!EPasaHelper.IsValidPayloadLength(epasa.PayloadType, epasa.Payload)) {
+			errorCode = EPasaErrorCode.PayloadTooLarge;
+			return false;
+		}
+
+		// Extended Checksum
+		if (extendedChecksumDelim != null) {
+			if (checksumDelim == null) {
+				errorCode = EPasaErrorCode.MissingAccountChecksum;
+				return false;
+			}
+			if (!EPasaHelper.IsValidExtendedChecksum(epasa.ToString(true), epasa.ExtendedChecksum)) {
+				errorCode = EPasaErrorCode.BadExtendedChecksum;
+				return false;
+			}
+			epasa.ExtendedChecksum = extendedChecksum;
+		}
+		return true;
+	}
+
+```
+
+Full source-code for the above is available [here][1]. 
+
+A recursive-descent implementation can be found [here][2].
+
 
 ## Links
 
-None
+1. [C# Regex Parser][1]
+2. [C# Recursive-Descent Parser][2]
+
+[1]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Parsing/RegexEPasaParser.cs
+[2]: https://github.com/Sphere10/NPascalCoin/blob/master/src/NPascalCoin/Common/Parsing/RecursiveDescentEPasaParser.cs
+ 

+ 1 - 1
src/libraries/cryptolib4pascal/ClpBufferedCipherBase.pas

@@ -56,6 +56,7 @@ type
 
   strict protected
 
+    function GetAlgorithmName: String; virtual; abstract;
     function GetBufferSize: Int32; inline;
     procedure SetBufferSize(value: Int32); inline;
     function GetOnProgress: TBufferedCipherProgressEvent; inline;
@@ -122,7 +123,6 @@ type
 
     procedure Reset(); virtual; abstract;
 
-    function GetAlgorithmName: String; virtual; abstract;
     property AlgorithmName: String read GetAlgorithmName;
 
     /// <summary>

+ 195 - 0
src/libraries/cryptolib4pascal/ClpBufferedStreamCipher.pas

@@ -0,0 +1,195 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpBufferedStreamCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIStreamCipher,
+  ClpICipherParameters,
+  ClpIParametersWithRandom,
+  ClpIBufferedStreamCipher,
+  ClpBufferedCipherBase,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SCipherNil = 'Cipher Instance Cannot be Nil';
+
+type
+  TBufferedStreamCipher = class(TBufferedCipherBase, IBufferedStreamCipher)
+
+  strict private
+  var
+    FCipher: IStreamCipher;
+
+  strict protected
+    function GetAlgorithmName: String; override;
+
+  public
+    constructor Create(const cipher: IStreamCipher);
+
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); override;
+
+    function GetBlockSize(): Int32; override;
+
+    function GetOutputSize(inputLen: Int32): Int32; override;
+
+    function GetUpdateOutputSize(inputLen: Int32): Int32; override;
+
+    function ProcessByte(input: Byte): TCryptoLibByteArray; overload; override;
+    function ProcessByte(input: Byte; const output: TCryptoLibByteArray;
+      outOff: Int32): Int32; overload; override;
+
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, Length: Int32): TCryptoLibByteArray; overload; override;
+    function ProcessBytes(const input: TCryptoLibByteArray;
+      inOff, Length: Int32; const output: TCryptoLibByteArray; outOff: Int32)
+      : Int32; overload; override;
+
+    function DoFinal(): TCryptoLibByteArray; overload; override;
+    function DoFinal(const input: TCryptoLibByteArray; inOff, Length: Int32)
+      : TCryptoLibByteArray; overload; override;
+
+    procedure Reset(); override;
+
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ TBufferedStreamCipher }
+
+constructor TBufferedStreamCipher.Create(const cipher: IStreamCipher);
+begin
+  Inherited Create();
+  if (cipher = Nil) then
+  begin
+    raise EArgumentNilCryptoLibException.CreateRes(@SCipherNil);
+  end;
+
+  FCipher := cipher;
+end;
+
+function TBufferedStreamCipher.GetAlgorithmName: String;
+begin
+  result := FCipher.AlgorithmName;
+end;
+
+function TBufferedStreamCipher.GetBlockSize: Int32;
+begin
+  result := 0;
+end;
+
+function TBufferedStreamCipher.GetOutputSize(inputLen: Int32): Int32;
+begin
+  result := inputLen;
+end;
+
+function TBufferedStreamCipher.GetUpdateOutputSize(inputLen: Int32): Int32;
+begin
+  result := inputLen;
+end;
+
+procedure TBufferedStreamCipher.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  LParameters: ICipherParameters;
+begin
+  LParameters := parameters;
+  if Supports(LParameters, IParametersWithRandom) then
+  begin
+    LParameters := (LParameters as IParametersWithRandom).parameters;
+  end;
+  FCipher.Init(forEncryption, LParameters);
+end;
+
+function TBufferedStreamCipher.ProcessByte(input: Byte;
+  const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if (outOff >= System.Length(output)) then
+  begin
+    raise EDataLengthCryptoLibException.CreateRes(@SOutputBufferTooSmall);
+  end;
+  output[outOff] := FCipher.ReturnByte(input);
+  result := 1;
+end;
+
+function TBufferedStreamCipher.ProcessByte(input: Byte): TCryptoLibByteArray;
+begin
+  result := TCryptoLibByteArray.Create(FCipher.ReturnByte(input));
+end;
+
+function TBufferedStreamCipher.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, Length: Int32; const output: TCryptoLibByteArray;
+  outOff: Int32): Int32;
+begin
+  if (Length < 1) then
+  begin
+    result := 0;
+    Exit;
+  end;
+
+  if (Length > 0) then
+  begin
+    FCipher.ProcessBytes(input, inOff, Length, output, outOff);
+  end;
+
+  result := Length;
+end;
+
+function TBufferedStreamCipher.ProcessBytes(const input: TCryptoLibByteArray;
+  inOff, Length: Int32): TCryptoLibByteArray;
+begin
+  if (Length < 1) then
+  begin
+    result := Nil;
+    Exit;
+  end;
+  System.SetLength(result, Length);
+  FCipher.ProcessBytes(input, inOff, Length, result, 0);
+end;
+
+function TBufferedStreamCipher.DoFinal: TCryptoLibByteArray;
+begin
+  Reset();
+  result := EmptyBuffer;
+end;
+
+function TBufferedStreamCipher.DoFinal(const input: TCryptoLibByteArray;
+  inOff, Length: Int32): TCryptoLibByteArray;
+begin
+  if (Length < 1) then
+  begin
+    result := EmptyBuffer;
+    Exit;
+  end;
+  result := ProcessBytes(input, inOff, Length);
+  Reset();
+end;
+
+procedure TBufferedStreamCipher.Reset;
+begin
+  FCipher.Reset();
+end;
+
+end.

+ 263 - 0
src/libraries/cryptolib4pascal/ClpChaChaEngine.pas

@@ -0,0 +1,263 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpChaChaEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpIChaChaEngine,
+  ClpSalsa20Engine,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+type
+
+  /// <summary>
+  /// Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+  /// </summary>
+  TChaChaEngine = class sealed(TSalsa20Engine, IChaChaEngine)
+
+  strict private
+    /// <summary>
+    /// ChaCha function.
+    /// </summary>
+    /// <param name="rounds">The number of ChaCha rounds to execute</param>
+    /// <param name="input">The input words.</param>
+    /// <param name="x">The ChaCha state to modify.</param>
+    class procedure ChaChaCore(rounds: Int32;
+      const input, x: TCryptoLibUInt32Array); static;
+
+  strict protected
+    function GetAlgorithmName: String; override;
+
+    procedure AdvanceCounter(); override;
+    procedure ResetCounter(); override;
+    procedure SetKey(const keyBytes, ivBytes: TCryptoLibByteArray); override;
+    procedure GenerateKeyStream(const output: TCryptoLibByteArray); override;
+
+  public
+    /// <summary>
+    /// Creates a 20 rounds ChaCha engine.
+    /// </summary>
+    constructor Create(); overload;
+    /// <summary>
+    /// Creates a ChaCha engine with a specific number of rounds.
+    /// </summary>
+    /// <param name="rounds">the number of rounds (must be an even number).</param>
+    constructor Create(rounds: Int32); overload;
+
+  end;
+
+implementation
+
+{ TChaChaEngine }
+
+procedure TChaChaEngine.AdvanceCounter;
+begin
+  System.Inc(FEngineState[12]);
+  if (FEngineState[12] = 0) then
+  begin
+    System.Inc(FEngineState[13]);
+  end;
+end;
+
+class procedure TChaChaEngine.ChaChaCore(rounds: Int32;
+  const input, x: TCryptoLibUInt32Array);
+var
+  x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14,
+    x15: UInt32;
+  Idx: Int32;
+begin
+  if (System.Length(input) <> 16) then
+  begin
+    raise EArgumentCryptoLibException.Create('');
+  end;
+  if (System.Length(x) <> 16) then
+  begin
+    raise EArgumentCryptoLibException.Create('');
+  end;
+  if ((rounds mod 2) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SRoundsMustbeEven);
+  end;
+
+  x00 := input[0];
+  x01 := input[1];
+  x02 := input[2];
+  x03 := input[3];
+  x04 := input[4];
+  x05 := input[5];
+  x06 := input[6];
+  x07 := input[7];
+  x08 := input[8];
+  x09 := input[9];
+  x10 := input[10];
+  x11 := input[11];
+  x12 := input[12];
+  x13 := input[13];
+  x14 := input[14];
+  x15 := input[15];
+
+  Idx := rounds;
+  while Idx > 0 do
+  begin
+
+    x00 := x00 + x04;
+    x12 := R(x12 xor x00, 16);
+    x08 := x08 + x12;
+    x04 := R(x04 xor x08, 12);
+    x00 := x00 + x04;
+    x12 := R(x12 xor x00, 8);
+    x08 := x08 + x12;
+    x04 := R(x04 xor x08, 7);
+    x01 := x01 + x05;
+    x13 := R(x13 xor x01, 16);
+    x09 := x09 + x13;
+    x05 := R(x05 xor x09, 12);
+    x01 := x01 + x05;
+    x13 := R(x13 xor x01, 8);
+    x09 := x09 + x13;
+    x05 := R(x05 xor x09, 7);
+    x02 := x02 + x06;
+    x14 := R(x14 xor x02, 16);
+    x10 := x10 + x14;
+    x06 := R(x06 xor x10, 12);
+    x02 := x02 + x06;
+    x14 := R(x14 xor x02, 8);
+    x10 := x10 + x14;
+    x06 := R(x06 xor x10, 7);
+    x03 := x03 + x07;
+    x15 := R(x15 xor x03, 16);
+    x11 := x11 + x15;
+    x07 := R(x07 xor x11, 12);
+    x03 := x03 + x07;
+    x15 := R(x15 xor x03, 8);
+    x11 := x11 + x15;
+    x07 := R(x07 xor x11, 7);
+    x00 := x00 + x05;
+    x15 := R(x15 xor x00, 16);
+    x10 := x10 + x15;
+    x05 := R(x05 xor x10, 12);
+    x00 := x00 + x05;
+    x15 := R(x15 xor x00, 8);
+    x10 := x10 + x15;
+    x05 := R(x05 xor x10, 7);
+    x01 := x01 + x06;
+    x12 := R(x12 xor x01, 16);
+    x11 := x11 + x12;
+    x06 := R(x06 xor x11, 12);
+    x01 := x01 + x06;
+    x12 := R(x12 xor x01, 8);
+    x11 := x11 + x12;
+    x06 := R(x06 xor x11, 7);
+    x02 := x02 + x07;
+    x13 := R(x13 xor x02, 16);
+    x08 := x08 + x13;
+    x07 := R(x07 xor x08, 12);
+    x02 := x02 + x07;
+    x13 := R(x13 xor x02, 8);
+    x08 := x08 + x13;
+    x07 := R(x07 xor x08, 7);
+    x03 := x03 + x04;
+    x14 := R(x14 xor x03, 16);
+    x09 := x09 + x14;
+    x04 := R(x04 xor x09, 12);
+    x03 := x03 + x04;
+    x14 := R(x14 xor x03, 8);
+    x09 := x09 + x14;
+    x04 := R(x04 xor x09, 7);
+
+    System.Dec(Idx, 2);
+  end;
+
+  x[0] := x00 + input[0];
+  x[1] := x01 + input[1];
+  x[2] := x02 + input[2];
+  x[3] := x03 + input[3];
+  x[4] := x04 + input[4];
+  x[5] := x05 + input[5];
+  x[6] := x06 + input[6];
+  x[7] := x07 + input[7];
+  x[8] := x08 + input[8];
+  x[9] := x09 + input[9];
+  x[10] := x10 + input[10];
+  x[11] := x11 + input[11];
+  x[12] := x12 + input[12];
+  x[13] := x13 + input[13];
+  x[14] := x14 + input[14];
+  x[15] := x15 + input[15];
+
+end;
+
+constructor TChaChaEngine.Create;
+begin
+  Inherited Create();
+end;
+
+constructor TChaChaEngine.Create(rounds: Int32);
+begin
+  Inherited Create(rounds);
+end;
+
+procedure TChaChaEngine.GenerateKeyStream(const output: TCryptoLibByteArray);
+begin
+  ChaChaCore(FRounds, FEngineState, Fx);
+  TConverters.le32_copy(PCardinal(Fx), 0, PByte(output), 0,
+    System.Length(Fx) * System.SizeOf(UInt32));
+end;
+
+function TChaChaEngine.GetAlgorithmName: String;
+begin
+  result := Format('ChaCha%d', [FRounds]);
+end;
+
+procedure TChaChaEngine.ResetCounter;
+begin
+  FEngineState[12] := 0;
+  FEngineState[13] := 0;
+end;
+
+procedure TChaChaEngine.SetKey(const keyBytes, ivBytes: TCryptoLibByteArray);
+begin
+  if (keyBytes <> Nil) then
+  begin
+    if not(Byte(System.Length(keyBytes)) in [16, 32]) then
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidKeySize,
+        [AlgorithmName]);
+    end;
+
+    PackTauOrSigma(System.Length(keyBytes), FEngineState, 0);
+
+    // Key
+    TConverters.le32_copy(PByte(keyBytes), 0, PCardinal(FEngineState),
+      4 * System.SizeOf(UInt32), 4 * System.SizeOf(UInt32));
+    TConverters.le32_copy(PByte(keyBytes), (System.Length(keyBytes) - 16) *
+      System.SizeOf(Byte), PCardinal(FEngineState), 8 * System.SizeOf(UInt32),
+      4 * System.SizeOf(UInt32));
+  end;
+
+  // IV
+  TConverters.le32_copy(PByte(ivBytes), 0, PCardinal(FEngineState),
+    14 * System.SizeOf(UInt32), 2 * System.SizeOf(UInt32));
+end;
+
+end.

+ 26 - 1
src/libraries/cryptolib4pascal/ClpCipherUtilities.pas

@@ -33,16 +33,21 @@ uses
   ClpIBlockCipherModes,
   ClpBufferedBlockCipher,
   ClpIBufferedBlockCipher,
+  ClpBufferedStreamCipher,
+  ClpIBufferedStreamCipher,
   ClpPaddedBufferedBlockCipher,
   ClpIPaddedBufferedBlockCipher,
   ClpNistObjectIdentifiers,
   ClpIAsn1Objects,
   ClpIBufferedCipher,
   ClpIBlockCipher,
+  ClpIStreamCipher,
   ClpAesEngine,
   ClpIAesEngine,
   ClpBlowfishEngine,
   ClpIBlowfishEngine,
+  ClpSalsa20Engine,
+  ClpISalsa20Engine,
   ClpIBlockCipherPadding;
 
 resourcestring
@@ -51,6 +56,8 @@ resourcestring
   SUnRecognizedCipher = '"Cipher " %s Not Recognised.';
   SSICModeWarning =
     'Warning: SIC-Mode Can Become a TwoTime-Pad if the Blocksize of the Cipher is Too Small. Use a Cipher With a Block Size of at Least 128 bits (e.g. AES)';
+  SModeAndPaddingNotNeededStreamCipher =
+    'Modes and Paddings Not Used for Stream Ciphers';
 
 type
 
@@ -63,7 +70,7 @@ type
 
   type
 {$SCOPEDENUMS ON}
-    TCipherAlgorithm = (AES, BLOWFISH);
+    TCipherAlgorithm = (AES, BLOWFISH, SALSA20);
     TCipherMode = (NONE, CBC, CFB, CTR, ECB, OFB, SIC);
     TCipherPadding = (NOPADDING, ISO10126PADDING, ISO10126D2PADDING,
       ISO10126_2PADDING, ISO7816_4PADDING, ISO9797_1PADDING, PKCS5,
@@ -191,6 +198,7 @@ var
   cipherPadding: TCipherPadding;
   cipherMode: TCipherMode;
   blockCipher: IBlockCipher;
+  streamCipher: IStreamCipher;
   padding: IBlockCipherPadding;
 begin
   if (algorithm = '') then
@@ -230,6 +238,10 @@ begin
     TCipherAlgorithm.BLOWFISH:
       begin
         blockCipher := TBlowfishEngine.Create() as IBlowfishEngine;
+      end;
+    TCipherAlgorithm.SALSA20:
+      begin
+        streamCipher := TSalsa20Engine.Create() as ISalsa20Engine;
       end
   else
     begin
@@ -238,6 +250,19 @@ begin
     end;
   end;
 
+  if (streamCipher <> Nil) then
+  begin
+    if (System.Length(parts) > 1) then
+    begin
+      raise EArgumentCryptoLibException.CreateRes
+        (@SModeAndPaddingNotNeededStreamCipher);
+    end;
+
+    Result := TBufferedStreamCipher.Create(streamCipher)
+      as IBufferedStreamCipher;
+    Exit;
+  end;
+
   padded := true;
   padding := Nil;
 

+ 1 - 0
src/libraries/cryptolib4pascal/ClpCryptoLibTypes.pas

@@ -55,6 +55,7 @@ type
   ESecurityUtilityCryptoLibException = class(ECryptoLibException);
   EAccessCryptoLibException = class(ECryptoLibException);
   EDataLengthCryptoLibException = class(ECryptoLibException);
+  EMaxBytesExceededCryptoLibException = class(ECryptoLibException);
   EOutputLengthCryptoLibException = class(ECryptoLibException);
   EBadBlockCryptoLibException = class(ECryptoLibException);
 

+ 3 - 1
src/libraries/cryptolib4pascal/ClpGeneratorUtilities.pas

@@ -196,6 +196,8 @@ begin
 
   AddKgAlgorithm('BLOWFISH', ['1.3.6.1.4.1.3029.1.2']);
 
+  AddKgAlgorithm('SALSA20', []);
+
   //
   // HMac key generators
   //
@@ -244,7 +246,7 @@ begin
   AddKpgAlgorithm('ECDSA', []);
 
   AddDefaultKeySizeEntries(128, ['AES128', 'BLOWFISH', 'HMACMD2', 'HMACMD4',
-    'HMACMD5', 'HMACRIPEMD128']);
+    'HMACMD5', 'HMACRIPEMD128', 'SALSA20']);
   AddDefaultKeySizeEntries(160, ['HMACRIPEMD160', 'HMACSHA1']);
   AddDefaultKeySizeEntries(192, ['AES', 'AES192', 'HMACTIGER']);
   AddDefaultKeySizeEntries(224, ['HMACSHA3-224', 'HMACSHA224',

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIBufferedStreamCipher.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIBufferedStreamCipher;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBufferedCipherBase;
+
+type
+  IBufferedStreamCipher = interface(IBufferedCipherBase)
+
+    ['{F20A1BF7-4AA6-445C-8218-9EA0DF7FC123}']
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIChaChaEngine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIChaChaEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISalsa20Engine;
+
+type
+
+  IChaChaEngine = interface(ISalsa20Engine)
+    ['{8CD31B82-B157-4A56-B529-E2F1079BADD8}']
+
+  end;
+
+implementation
+
+end.

+ 36 - 0
src/libraries/cryptolib4pascal/ClpISalsa20Engine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpISalsa20Engine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIStreamCipher;
+
+type
+
+  ISalsa20Engine = interface(IStreamCipher)
+    ['{F60193BE-E5E6-4C7A-9596-5AAF6B8B8E9C}']
+
+  end;
+
+implementation
+
+end.

+ 1 - 11
src/libraries/cryptolib4pascal/ClpISignersEncodings.pas

@@ -17,7 +17,7 @@
 
 unit ClpISignersEncodings;
 
-{$I ..\Include\CryptoLib.inc}
+{$I CryptoLib.inc}
 
 interface
 
@@ -79,11 +79,6 @@ type
       pos: Int32): TBigInteger;
     function EncodeValue(const n, x: TBigInteger): IDerInteger;
 
-    function Decode(const n: TBigInteger; const encoding: TCryptoLibByteArray)
-      : TCryptoLibGenericArray<TBigInteger>;
-
-    function Encode(const n, r, s: TBigInteger): TCryptoLibByteArray;
-
   end;
 
 type
@@ -96,11 +91,6 @@ type
     procedure EncodeValue(const n, x: TBigInteger;
       const buf: TCryptoLibByteArray; off, len: Int32);
 
-    function Decode(const n: TBigInteger; const encoding: TCryptoLibByteArray)
-      : TCryptoLibGenericArray<TBigInteger>;
-
-    function Encode(const n, r, s: TBigInteger): TCryptoLibByteArray;
-
   end;
 
 type

+ 36 - 0
src/libraries/cryptolib4pascal/ClpISpeckEngine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpISpeckEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpIBlockCipher;
+
+type
+
+  ISpeckEngine = interface(IBlockCipher)
+    ['{60AEDFFF-5FAB-4018-90E7-ED9BD1B3D406}']
+
+  end;
+
+implementation
+
+end.

+ 31 - 15
src/libraries/cryptolib4pascal/ClpIStreamCipher.pas

@@ -17,7 +17,7 @@
 
 unit ClpIStreamCipher;
 
-{$I ..\Include\CryptoLib.inc}
+{$I CryptoLib.inc}
 
 interface
 
@@ -28,7 +28,7 @@ uses
 type
   /// <remarks>the interface stream ciphers conform to.</remarks>
   IStreamCipher = interface(IInterface)
-   ['{A4366B7A-2BC4-4D92-AEF2-512B621CA746}']
+    ['{A4366B7A-2BC4-4D92-AEF2-512B621CA746}']
 
     /// <summary>The name of the algorithm this cipher implements.</summary>
     function GetAlgorithmName: String;
@@ -39,19 +39,35 @@ type
     /// <param name="parameters">The key or other data required by the cipher.</param>
     procedure Init(forEncryption: Boolean; const parameters: ICipherParameters);
 
-    /// <summary>Indicates whether this cipher can handle partial blocks.</summary>
-    function GetIsPartialBlockOkay: Boolean;
-    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
-
-    /// <summary>Process a block.</summary>
-    /// <param name="inBuf">The input buffer.</param>
-    /// <param name="inOff">The offset into <paramref>inBuf</paramref> that the input block begins.</param>
-    /// <param name="outBuf">The output buffer.</param>
-    /// <param name="outOff">The offset into <paramref>outBuf</paramref> to write the output block.</param>
-    /// <exception cref="DataLengthException">If input block is wrong size, or outBuf too small.</exception>
-    /// <returns>The number of bytes processed and produced.</returns>
-    function ProcessBlock(inBuf: TCryptoLibByteArray; inOff: Int32;
-      outBuf: TCryptoLibByteArray; outOff: Int32): Int32;
+    /// <summary>encrypt/decrypt a single byte returning the result.</summary>
+    /// <param name="input">the byte to be processed.</param>
+    /// <returns>the result of processing the input byte.</returns>
+    function ReturnByte(input: Byte): Byte;
+
+    /// <summary>
+    /// Process a block of bytes from <c>input</c> putting the result into <c>
+    /// output</c>.
+    /// </summary>
+    /// <param name="inBytes">
+    /// The input byte array.
+    /// </param>
+    /// <param name="inOff">
+    /// The offset into <c>input</c> where the data to be processed starts.
+    /// </param>
+    /// <param name="len">
+    /// The number of bytes to be processed.
+    /// </param>
+    /// <param name="outBytes">
+    /// The output buffer the processed bytes go into.
+    /// </param>
+    /// <param name="outOff">
+    /// The offset into <c>output</c> the processed data starts at.
+    /// </param>
+    /// <exception cref="EDataLengthCryptoLibException">
+    /// If the output buffer is too small.
+    /// </exception>
+    procedure ProcessBytes(const inBytes: TCryptoLibByteArray;
+      inOff, len: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32);
 
     /// <summary>
     /// Reset the cipher to the same state as it was after the last init (if there was one).

+ 36 - 0
src/libraries/cryptolib4pascal/ClpIXSalsa20Engine.pas

@@ -0,0 +1,36 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpIXSalsa20Engine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpISalsa20Engine;
+
+type
+
+  IXSalsa20Engine = interface(ISalsa20Engine)
+    ['{D95969E8-EAB0-4D2D-8542-40B55925F68D}']
+
+  end;
+
+implementation
+
+end.

+ 1 - 0
src/libraries/cryptolib4pascal/ClpParameterUtilities.pas

@@ -131,6 +131,7 @@ begin
     TNistObjectIdentifiers.IdAes256Cfb.ID,
     TNistObjectIdentifiers.IdAes256Ecb.ID,
     TNistObjectIdentifiers.IdAes256Ofb.ID]);
+  AddAlgorithm('SALSA20', []);
 
   AddBasicIVSizeEntries(16, ['AES', 'AES128', 'AES192', 'AES256']);
 

+ 502 - 0
src/libraries/cryptolib4pascal/ClpSalsa20Engine.pas

@@ -0,0 +1,502 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpSalsa20Engine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpBits,
+  ClpCheck,
+  ClpISalsa20Engine,
+  ClpIKeyParameter,
+  ClpICipherParameters,
+  ClpParametersWithIV,
+  ClpIParametersWithIV,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SInvalidRound = '"rounds" Must be a Positive, Even Number';
+  SInvalidKeySize = '%s Requires 128 bit or 256 bit key';
+  SMaxByteExceeded = '2^70 Byte Limit per IV; Change IV';
+  SMaxByteExceededTwo = '2^70 byte limit per IV would be exceeded; Change IV';
+  SEngineNotInitialized = '%s not Initialized';
+  SInputBuffertooShort = 'Input Buffer too Short';
+  SOutputBuffertooShort = 'Output Buffer too Short';
+  SRoundsMustbeEven = 'Number of Rounds Must be Even';
+{$IFNDEF _FIXINSIGHT_}
+  SIVRequired = '%s Init Requires an IV, "parameters"';
+  SInvalidIV = '%s Requires exactly %d bytes of IV';
+  SInitError =
+    '%s Init Parameters must Contain a KeyParameter (or null for Re-Init)';
+  SKeyParameterNullForFirstInit =
+    'KeyParameter can not be null for First Initialisation';
+{$ENDIF}
+
+type
+
+  /// <summary>
+  /// Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
+  /// </summary>
+  TSalsa20Engine = class(TInterfacedObject, ISalsa20Engine)
+
+  strict private
+  const
+    DEFAULT_ROUNDS = Int32(20);
+    STATE_SIZE = Int32(16); // 16, 32 bit ints = 64 bytes
+    // representation of 'expand 16-byte k' + 'expand 32-byte k' as an array of UInt32
+    TAU_SIGMA: array [0 .. 7] of UInt32 = (1634760805, 824206446, 2036477238,
+      1797285236, 1634760805, 857760878, 2036477234, 1797285236);
+
+  var
+    FIndex: Int32;
+    // internal counter
+    FCW0, FCW1, FCW2: UInt32;
+    FKeyStream: TCryptoLibByteArray;
+    FInitialised: Boolean;
+
+    procedure ResetLimitCounter(); inline;
+    function LimitExceeded(): Boolean; overload; inline;
+    // this relies on the fact len will always be positive.
+    function LimitExceeded(len: UInt32): Boolean; overload; inline;
+
+  strict protected
+  var
+    FRounds: Int32;
+    FEngineState, Fx: TCryptoLibUInt32Array;
+
+    function GetAlgorithmName: String; virtual;
+    function GetNonceSize: Int32; virtual;
+    property NonceSize: Int32 read GetNonceSize;
+
+    procedure AdvanceCounter(); virtual;
+    procedure ResetCounter(); virtual;
+    procedure SetKey(const keyBytes, ivBytes: TCryptoLibByteArray); virtual;
+    procedure GenerateKeyStream(const output: TCryptoLibByteArray); virtual;
+
+    /// <summary>
+    /// Rotate left
+    /// </summary>
+    /// <param name="x">
+    /// value to rotate
+    /// </param>
+    /// <param name="y">
+    /// amount to rotate x
+    /// </param>
+    /// <returns>
+    /// rotated x
+    /// </returns>
+    class function R(x: UInt32; y: Int32): UInt32; static; inline;
+    class procedure PackTauOrSigma(keyLength: Int32;
+      const state: TCryptoLibUInt32Array; stateOffset: Int32); static;
+    class procedure SalsaCore(rounds: Int32;
+      const input, x: TCryptoLibUInt32Array); static;
+
+  public
+    /// <summary>
+    /// Creates a 20 round Salsa20 engine.
+    /// </summary>
+    constructor Create(); overload;
+    /// <summary>
+    /// Creates a Salsa20 engine with a specific number of rounds.
+    /// </summary>
+    /// <param name="rounds">the number of rounds (must be an even number).</param>
+    constructor Create(rounds: Int32); overload;
+
+{$IFNDEF _FIXINSIGHT_}
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); virtual;
+{$ENDIF}
+    function ReturnByte(input: Byte): Byte; virtual;
+
+    procedure ProcessBytes(const inBytes: TCryptoLibByteArray;
+      inOff, len: Int32; const outBytes: TCryptoLibByteArray;
+      outOff: Int32); virtual;
+
+    procedure Reset(); virtual;
+
+    property AlgorithmName: String read GetAlgorithmName;
+
+  end;
+
+implementation
+
+{ TSalsa20Engine }
+
+constructor TSalsa20Engine.Create;
+begin
+  Create(DEFAULT_ROUNDS);
+end;
+
+procedure TSalsa20Engine.AdvanceCounter;
+begin
+  System.Inc(FEngineState[8]);
+  if (FEngineState[8] = 0) then
+  begin
+    System.Inc(FEngineState[9]);
+  end;
+end;
+
+constructor TSalsa20Engine.Create(rounds: Int32);
+begin
+  Inherited Create();
+  if ((rounds <= 0) or ((rounds and 1) <> 0)) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SInvalidRound);
+  end;
+  FRounds := rounds;
+  FIndex := 0;
+  FInitialised := false;
+  System.SetLength(FEngineState, STATE_SIZE); // state
+  System.SetLength(Fx, STATE_SIZE); // internal buffer
+  System.SetLength(FKeyStream, STATE_SIZE * 4); // expanded state, 64 bytes
+end;
+
+procedure TSalsa20Engine.GenerateKeyStream(const output: TCryptoLibByteArray);
+begin
+  SalsaCore(FRounds, FEngineState, Fx);
+  TConverters.le32_copy(PCardinal(Fx), 0, PByte(output), 0,
+    System.Length(Fx) * System.SizeOf(UInt32));
+end;
+
+function TSalsa20Engine.GetAlgorithmName: String;
+begin
+  result := 'Salsa20';
+  if (FRounds <> DEFAULT_ROUNDS) then
+  begin
+    result := Format('%s/%d', [result, FRounds]);
+  end;
+end;
+
+function TSalsa20Engine.GetNonceSize: Int32;
+begin
+  result := 8;
+end;
+
+{$IFNDEF _FIXINSIGHT_}
+
+procedure TSalsa20Engine.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  ivParams: IParametersWithIV;
+  iv: TCryptoLibByteArray;
+  keyParam: ICipherParameters;
+begin
+  (*
+    * Salsa20 encryption and decryption is completely
+    * symmetrical, so the 'forEncryption' is
+    * irrelevant. (Like 90% of stream ciphers)
+  *)
+
+  if not Supports(parameters, IParametersWithIV, ivParams) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SIVRequired,
+      [AlgorithmName]);
+  end;
+
+  iv := ivParams.GetIV();
+  if ((iv = Nil) or (System.Length(iv) <> NonceSize)) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidIV,
+      [AlgorithmName, NonceSize]);
+  end;
+
+  keyParam := ivParams.parameters;
+  if (keyParam = Nil) then
+  begin
+    if (not FInitialised) then
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt
+        (@SKeyParameterNullForFirstInit, [AlgorithmName]);
+    end;
+
+    SetKey(Nil, iv);
+  end
+  else if Supports(keyParam, IKeyParameter) then
+  begin
+    SetKey((keyParam as IKeyParameter).GetKey(), iv);
+  end
+  else
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInitError,
+      [AlgorithmName]);
+  end;
+
+  Reset();
+  FInitialised := true;
+end;
+{$ENDIF}
+
+function TSalsa20Engine.LimitExceeded: Boolean;
+begin
+  System.Inc(FCW0);
+  if (FCW0 = 0) then
+  begin
+    System.Inc(FCW1);
+    if (FCW1 = 0) then
+    begin
+      System.Inc(FCW2);
+      result := (FCW2 and $20) <> 0; // 2^(32 + 32 + 6)
+      Exit;
+    end;
+  end;
+
+  result := false;
+end;
+
+function TSalsa20Engine.LimitExceeded(len: UInt32): Boolean;
+var
+  Old: UInt32;
+begin
+  Old := FCW0;
+  System.Inc(FCW0, len);
+  if (FCW0 < Old) then
+  begin
+    System.Inc(FCW1);
+    if (FCW1 = 0) then
+    begin
+      System.Inc(FCW2);
+      result := (FCW2 and $20) <> 0; // 2^(32 + 32 + 6)
+      Exit;
+    end;
+  end;
+
+  result := false;
+end;
+
+class procedure TSalsa20Engine.PackTauOrSigma(keyLength: Int32;
+  const state: TCryptoLibUInt32Array; stateOffset: Int32);
+var
+  tsOff: Int32;
+begin
+  tsOff := (keyLength - 16) div 4;
+  state[stateOffset] := TAU_SIGMA[tsOff];
+  state[stateOffset + 1] := TAU_SIGMA[tsOff + 1];
+  state[stateOffset + 2] := TAU_SIGMA[tsOff + 2];
+  state[stateOffset + 3] := TAU_SIGMA[tsOff + 3];
+end;
+
+procedure TSalsa20Engine.ProcessBytes(const inBytes: TCryptoLibByteArray;
+  inOff, len: Int32; const outBytes: TCryptoLibByteArray; outOff: Int32);
+var
+  Idx: Int32;
+begin
+  if (not FInitialised) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateResFmt
+      (@SEngineNotInitialized, [AlgorithmName]);
+  end;
+
+  TCheck.DataLength(inBytes, inOff, len, SInputBuffertooShort);
+  TCheck.OutputLength(outBytes, outOff, len, SOutputBuffertooShort);
+
+  if (LimitExceeded(UInt32(len))) then
+  begin
+    raise EMaxBytesExceededCryptoLibException.CreateRes(@SMaxByteExceededTwo);
+  end;
+
+  for Idx := 0 to System.Pred(len) do
+  begin
+    if (FIndex = 0) then
+    begin
+      GenerateKeyStream(FKeyStream);
+      AdvanceCounter();
+    end;
+    outBytes[Idx + outOff] := Byte(FKeyStream[FIndex] xor inBytes[Idx + inOff]);
+    FIndex := (FIndex + 1) and 63;
+  end;
+end;
+
+class function TSalsa20Engine.R(x: UInt32; y: Int32): UInt32;
+begin
+  result := TBits.RotateLeft32(x, y);
+end;
+
+procedure TSalsa20Engine.ResetCounter;
+begin
+  FEngineState[8] := 0;
+  FEngineState[9] := 0;
+end;
+
+procedure TSalsa20Engine.ResetLimitCounter;
+begin
+  FCW0 := 0;
+  FCW1 := 0;
+  FCW2 := 0;
+end;
+
+procedure TSalsa20Engine.Reset;
+begin
+  FIndex := 0;
+  ResetLimitCounter();
+  ResetCounter();
+end;
+
+function TSalsa20Engine.ReturnByte(input: Byte): Byte;
+var
+  output: Byte;
+begin
+  if (LimitExceeded()) then
+  begin
+    raise EMaxBytesExceededCryptoLibException.CreateRes(@SMaxByteExceeded);
+  end;
+
+  if (FIndex = 0) then
+  begin
+    GenerateKeyStream(FKeyStream);
+    AdvanceCounter();
+  end;
+
+  output := Byte(FKeyStream[FIndex] xor input);
+  FIndex := (FIndex + 1) and 63;
+
+  result := output;
+end;
+
+class procedure TSalsa20Engine.SalsaCore(rounds: Int32;
+  const input, x: TCryptoLibUInt32Array);
+var
+  x00, x01, x02, x03, x04, x05, x06, x07, x08, x09, x10, x11, x12, x13, x14,
+    x15: UInt32;
+  Idx: Int32;
+begin
+  if (System.Length(input) <> 16) then
+  begin
+    raise EArgumentCryptoLibException.Create('');
+  end;
+  if (System.Length(x) <> 16) then
+  begin
+    raise EArgumentCryptoLibException.Create('');
+  end;
+  if ((rounds mod 2) <> 0) then
+  begin
+    raise EArgumentCryptoLibException.CreateRes(@SRoundsMustbeEven);
+  end;
+
+  x00 := input[0];
+  x01 := input[1];
+  x02 := input[2];
+  x03 := input[3];
+  x04 := input[4];
+  x05 := input[5];
+  x06 := input[6];
+  x07 := input[7];
+  x08 := input[8];
+  x09 := input[9];
+  x10 := input[10];
+  x11 := input[11];
+  x12 := input[12];
+  x13 := input[13];
+  x14 := input[14];
+  x15 := input[15];
+
+  Idx := rounds;
+  while Idx > 0 do
+  begin
+
+    x04 := x04 xor (R((x00 + x12), 7));
+    x08 := x08 xor (R((x04 + x00), 9));
+    x12 := x12 xor (R((x08 + x04), 13));
+    x00 := x00 xor (R((x12 + x08), 18));
+    x09 := x09 xor (R((x05 + x01), 7));
+    x13 := x13 xor (R((x09 + x05), 9));
+    x01 := x01 xor (R((x13 + x09), 13));
+    x05 := x05 xor (R((x01 + x13), 18));
+    x14 := x14 xor (R((x10 + x06), 7));
+    x02 := x02 xor (R((x14 + x10), 9));
+    x06 := x06 xor (R((x02 + x14), 13));
+    x10 := x10 xor (R((x06 + x02), 18));
+    x03 := x03 xor (R((x15 + x11), 7));
+    x07 := x07 xor (R((x03 + x15), 9));
+    x11 := x11 xor (R((x07 + x03), 13));
+    x15 := x15 xor (R((x11 + x07), 18));
+
+    x01 := x01 xor (R((x00 + x03), 7));
+    x02 := x02 xor (R((x01 + x00), 9));
+    x03 := x03 xor (R((x02 + x01), 13));
+    x00 := x00 xor (R((x03 + x02), 18));
+    x06 := x06 xor (R((x05 + x04), 7));
+    x07 := x07 xor (R((x06 + x05), 9));
+    x04 := x04 xor (R((x07 + x06), 13));
+    x05 := x05 xor (R((x04 + x07), 18));
+    x11 := x11 xor (R((x10 + x09), 7));
+    x08 := x08 xor (R((x11 + x10), 9));
+    x09 := x09 xor (R((x08 + x11), 13));
+    x10 := x10 xor (R((x09 + x08), 18));
+    x12 := x12 xor (R((x15 + x14), 7));
+    x13 := x13 xor (R((x12 + x15), 9));
+    x14 := x14 xor (R((x13 + x12), 13));
+    x15 := x15 xor (R((x14 + x13), 18));
+
+    System.Dec(Idx, 2);
+  end;
+
+  x[0] := x00 + input[0];
+  x[1] := x01 + input[1];
+  x[2] := x02 + input[2];
+  x[3] := x03 + input[3];
+  x[4] := x04 + input[4];
+  x[5] := x05 + input[5];
+  x[6] := x06 + input[6];
+  x[7] := x07 + input[7];
+  x[8] := x08 + input[8];
+  x[9] := x09 + input[9];
+  x[10] := x10 + input[10];
+  x[11] := x11 + input[11];
+  x[12] := x12 + input[12];
+  x[13] := x13 + input[13];
+  x[14] := x14 + input[14];
+  x[15] := x15 + input[15];
+
+end;
+
+procedure TSalsa20Engine.SetKey(const keyBytes, ivBytes: TCryptoLibByteArray);
+var
+  tsOff: Int32;
+begin
+  if (keyBytes <> Nil) then
+  begin
+    if not(Byte(System.Length(keyBytes)) in [16, 32]) then
+    begin
+      raise EArgumentCryptoLibException.CreateResFmt(@SInvalidKeySize,
+        [AlgorithmName]);
+    end;
+
+    tsOff := (System.Length(keyBytes) - 16) div 4;
+    FEngineState[0] := TAU_SIGMA[tsOff];
+    FEngineState[5] := TAU_SIGMA[tsOff + 1];
+    FEngineState[10] := TAU_SIGMA[tsOff + 2];
+    FEngineState[15] := TAU_SIGMA[tsOff + 3];
+
+    // Key
+    TConverters.le32_copy(PByte(keyBytes), 0, PCardinal(FEngineState),
+      1 * System.SizeOf(UInt32), 4 * System.SizeOf(UInt32));
+    TConverters.le32_copy(PByte(keyBytes), (System.Length(keyBytes) - 16) *
+      System.SizeOf(Byte), PCardinal(FEngineState), 11 * System.SizeOf(UInt32),
+      4 * System.SizeOf(UInt32));
+  end;
+
+  // IV
+  TConverters.le32_copy(PByte(ivBytes), 0, PCardinal(FEngineState),
+    6 * System.SizeOf(UInt32), 2 * System.SizeOf(UInt32));
+end;
+
+end.

+ 1090 - 0
src/libraries/cryptolib4pascal/ClpSpeckEngine.pas

@@ -0,0 +1,1090 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpSpeckEngine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  SysUtils,
+  ClpCheck,
+  ClpISpeckEngine,
+  ClpIBlockCipher,
+  ClpICipherParameters,
+  ClpIKeyParameter,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SSpeckEngineNotInitialised = '%s Engine not Initialised';
+  SInputBuffertooShort = 'Input Buffer too Short';
+  SOutputBuffertooShort = 'Output Buffer too Short';
+  SInvalidArgumentEncountered = 'Invalid Argument Encountered.';
+  SInvalidParameterSpeckInit = 'Invalid Parameter Passed to Speck Init - "%s"';
+  SSpeck32InvalidKeySize =
+    'Speck32 requires a key of 64 bits but input was "%d" bits.';
+  SSpeck48InvalidKeySize =
+    'Speck48 requires a key of 72 or 96 bits but input was "%d" bits.';
+  SSpeck64InvalidKeySize =
+    'Speck64 requires a key of 96 or 128 bits but input was "%d" bits.';
+  SSpeck96InvalidKeySize =
+    'Speck96 requires a key of 96 or 144 bits but input was "%d" bits.';
+  SSpeck128InvalidKeySize =
+    'Speck128 requires a key of 128, 192 or 256 bits but input was "%d" bits.';
+
+type
+
+  /// <summary>
+  /// <para>
+  /// The Speck family of block ciphers, described in <i>The Simon and
+  /// Speck Families of Lightweight Block Ciphers</i> by <i>Ray Beaulieu,
+  /// Douglas Shors, Jason Smith, Stefan Treatman-Clark, Bryan Weeks,
+  /// Louis Wingers</i> All block size and key size variants are
+  /// supported, with the key size determined from the key during <see cref="ClpSpeckEngine|TSpeckEngine.Init(Boolean,ICipherParameters)" />
+  /// .
+  /// </para>
+  /// </summary>
+  TSpeckEngine = class abstract(TInterfacedObject, ISpeckEngine, IBlockCipher)
+
+  strict private
+  var
+
+    Finitialised, FforEncryption: Boolean;
+
+    function GetIsPartialBlockOkay: Boolean; virtual;
+
+    /// <summary>
+    /// Internal method to Initialise this cipher instance.
+    /// <code>true</code> for encryption, <code>false</code> for decryption.
+    /// the bytes of the key to use.
+    /// </summary>
+    procedure EngineInit(forEncryption: Boolean;
+      const keyBytes: TCryptoLibByteArray); virtual;
+
+  strict protected
+
+  var
+    FblockSize, FwordSize, FwordSizeBits, Falpha, Fbeta, FbaseRounds,
+      Frounds: Int32;
+
+    /// <summary>
+    /// Gets the algorithm name of this Speck engine.
+    /// </summary>
+    /// <value>
+    /// the name of the Speck variant, specified to the level of the block size (e.g.
+    /// <em>Speck96</em>).
+    /// </value>
+    function GetAlgorithmName: String; virtual;
+    function GetBlockSize(): Int32; virtual;
+
+    /// <summary>
+    /// Checks whether a key size provided to the <see cref="ClpSpeckEngine|TSpeckEngine.EngineInit(Boolean,TCryptoLibByteArray)" />
+    /// method is valid.
+    /// </summary>
+    procedure CheckKeySize(keySizeBytes: Int32); virtual; abstract;
+
+    /// <summary>
+    /// Sets a key for this cipher instance, calculating the key schedule.
+    /// </summary>
+    procedure SetKey(const keyBytes: TCryptoLibByteArray); virtual; abstract;
+
+    /// <summary>
+    /// Unpack a block of data into working state prior to an
+    /// encrypt/decrypt operation.
+    /// </summary>
+    /// <param name="bytes">
+    /// the input data.
+    /// </param>
+    /// <param name="off">
+    /// the offset to begin reading the input data at.
+    /// </param>
+    procedure UnPackBlock(const bytes: TCryptoLibByteArray; off: Int32);
+      virtual; abstract;
+
+    /// <summary>
+    /// Packs the 2 word working state following an encrypt/decrypt into a
+    /// byte sequence.
+    /// </summary>
+    /// <param name="bytes">
+    /// the output buffer.
+    /// </param>
+    /// <param name="off">
+    /// the offset to begin writing the output data at.
+    /// </param>
+    procedure PackBlock(const bytes: TCryptoLibByteArray; off: Int32);
+      virtual; abstract;
+
+    /// <summary>
+    /// Encrypts the plaintext words loaded with a previous call to <see cref="ClpSpeckEngine|TSpeckEngine.UnPackBlock(TCryptoLibByteArray,Int32)" />
+    /// leaving the resulting ciphertext words in the working state.
+    /// </summary>
+    procedure EncryptBlock(); virtual; abstract;
+
+    /// <summary>
+    /// Decrypts the ciphertext words loaded with a previous call to <see cref="ClpSpeckEngine|TSpeckEngine.UnPackBlock(TCryptoLibByteArray,Int32)" />
+    /// leaving the resulting ciphertext words in the working state.
+    /// </summary>
+
+    procedure DecryptBlock(); virtual; abstract;
+
+    /// <summary>
+    /// Constructs a Speck engine.
+    /// </summary>
+    /// <param name="wordSize">
+    /// the size of the word to use, in bytes.
+    /// </param>
+    /// <param name="baseRounds">
+    /// the base number of rounds (for a 2 word key variant) for the
+    /// specified word/block size.
+    /// </param>
+    /// <param name="alpha">
+    /// the alpha rotation constant to use.
+    /// </param>
+    /// <param name="beta">
+    /// the beta rotation constant to use.
+    /// </param>
+    constructor Create(wordSize, baseRounds, alpha, beta: Int32);
+
+    /// <summary>
+    /// initialise a Speck cipher.
+    /// </summary>
+    /// <param name="forEncryption">
+    /// whether or not we are for encryption.
+    /// </param>
+    /// <param name="parameters">
+    /// the parameters required to set up the cipher.
+    /// </param>
+    /// <exception cref="EArgumentCryptoLibException">
+    /// if the parameters argument is inappropriate.
+    /// </exception>
+    procedure Init(forEncryption: Boolean;
+      const parameters: ICipherParameters); virtual;
+
+    function ProcessBlock(const input: TCryptoLibByteArray; inOff: Int32;
+      const output: TCryptoLibByteArray; outOff: Int32): Int32; virtual;
+
+    procedure Reset(); virtual;
+
+    property AlgorithmName: String read GetAlgorithmName;
+    property IsPartialBlockOkay: Boolean read GetIsPartialBlockOkay;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Base class of Speck variants that fit in 32 bit Pascal Integers:
+  /// Speck32, Speck48, Speck64.
+  /// Speck32 and Speck48 (16 and 24 bit word sizes) are implemented using masking.
+  /// </summary>
+  TSpeckUInt32Engine = class abstract(TSpeckEngine)
+
+  strict private
+  var
+
+    /// <summary>
+    /// The expanded key schedule for all <see cref="ClpSpeckEngine|TSpeckEngine.Frounds" />
+    /// </summary>
+    Fk: TCryptoLibUInt32Array;
+
+    /// <summary>
+    /// The 2 words of the working state;
+    /// </summary>
+    Fx, Fy: UInt32;
+
+    /// <summary>
+    /// Rotates a word left by the specified distance. <br />The rotation is
+    /// on the word size of the cipher instance, not on the full 32 bits of
+    /// the UInt32.
+    /// </summary>
+    /// <param name="i">
+    /// the word to rotate.
+    /// </param>
+    /// <param name="distance">
+    /// the distance in bits to rotate.
+    /// </param>
+    /// <returns>
+    /// the rotated word, which may have unmasked high (&gt; word size) bits.
+    /// </returns>
+    function Rotl(i: UInt32; distance: Int32): UInt32; inline;
+
+    /// <summary>
+    /// Rotates a word right by the specified distance. <br />The rotation is
+    /// on the word size of the cipher instance, not on the full 32 bits of
+    /// the UInt32.
+    /// </summary>
+    /// <param name="i">
+    /// the word to rotate.
+    /// </param>
+    /// <param name="distance">
+    /// the distance in bits to rotate.
+    /// </param>
+    /// <returns>
+    /// the rotated word, which may have unmasked high (&gt; word size) bits.
+    /// </returns>
+    function Rotr(i: UInt32; distance: Int32): UInt32; inline;
+
+    /// <summary>
+    /// Read <see cref="ClpSpeckEngine|TSpeckEngine.FwordSize" /> bytes from
+    /// the input data in <b>little-endian</b> order.
+    /// </summary>
+    /// <param name="bytes">
+    /// the data to read a word from.
+    /// </param>
+    /// <param name="off">
+    /// the offset to read the word from.
+    /// </param>
+    /// <returns>
+    /// the read word, with zeroes in any bits higher than the word size.
+    /// </returns>
+    function BytesToWord(const bytes: TCryptoLibByteArray; off: Int32)
+      : UInt32; inline;
+
+    /// <summary>
+    /// Writes <see cref="ClpSpeckEngine|TSpeckEngine.FwordSize" /> bytes
+    /// into a buffer in <b>little-endian</b> order.
+    /// </summary>
+    /// <param name="word">
+    /// the word to write.
+    /// </param>
+    /// <param name="bytes">
+    /// the buffer to write the word bytes to.
+    /// </param>
+    /// <param name="off">
+    /// the offset to write the data at.
+    /// </param>
+    procedure WordToBytes(word: UInt32; const bytes: TCryptoLibByteArray;
+      off: Int32); inline;
+
+  strict protected
+
+    /// <summary>
+    /// Masks all bits higher than the word size of this cipher in the
+    /// supplied value.
+    /// </summary>
+    /// <param name="val">
+    /// the value to mask.
+    /// </param>
+    /// <returns>
+    /// the masked value.
+    /// </returns>
+    function Mask(val: UInt32): UInt32; virtual; abstract;
+
+    procedure SetKey(const keyBytes: TCryptoLibByteArray); override;
+
+    procedure UnPackBlock(const bytes: TCryptoLibByteArray;
+      off: Int32); override;
+
+    procedure PackBlock(const bytes: TCryptoLibByteArray; off: Int32); override;
+
+    procedure EncryptBlock(); override;
+
+    procedure DecryptBlock(); override;
+
+    /// <summary>
+    /// Constructs a Speck cipher with &lt;= 32 bit word size, using the
+    /// standard 8,3 rotation constants.
+    /// </summary>
+    /// <param name="wordSize">
+    /// the word size in bytes.
+    /// </param>
+    /// <param name="baseRounds">
+    /// the base (for 2 word key) round count.
+    /// </param>
+    constructor Create(wordSize, baseRounds: Int32); overload;
+
+    /// <summary>
+    /// Constructs a Speck cipher with &lt;= 32 bit word size, using custom
+    /// rotation constants.
+    /// </summary>
+    /// <param name="wordSize">
+    /// the word size in bytes.
+    /// </param>
+    /// <param name="baseRounds">
+    /// the base (for 2 word key) round count.
+    /// </param>
+    /// <param name="alpha">
+    /// the <em>alpha</em> rotation constant.
+    /// </param>
+    /// <param name="beta">
+    /// the <em>beta</em> rotation constant.
+    /// </param>
+    constructor Create(wordSize, baseRounds, alpha, beta: Int32); overload;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Base class of Speck variants that fit in 64 bit Pascal Integers:
+  /// Speck96, Speck128.
+  /// Speck96 (48 bit word size) is implemented using masking.
+  /// </summary>
+  TSpeckUInt64Engine = class abstract(TSpeckEngine)
+
+  strict private
+  var
+
+    /// <summary>
+    /// The expanded key schedule for all <see cref="ClpSpeckEngine|TSpeckEngine.Frounds" />
+    /// </summary>
+    Fk: TCryptoLibUInt64Array;
+
+    /// <summary>
+    /// The 2 words of the working state;
+    /// </summary>
+    Fx, Fy: UInt64;
+
+    /// <summary>
+    /// Rotates a word left by the specified distance. <br />The rotation is
+    /// on the word size of the cipher instance, not on the full 64 bits of
+    /// the UInt64.
+    /// </summary>
+    /// <param name="i">
+    /// the word to rotate.
+    /// </param>
+    /// <param name="distance">
+    /// the distance in bits to rotate.
+    /// </param>
+    /// <returns>
+    /// the rotated word, which may have unmasked high (&gt; word size) bits.
+    /// </returns>
+    function Rotl(i: UInt64; distance: Int32): UInt64; inline;
+
+    /// <summary>
+    /// Rotates a word right by the specified distance. <br />The rotation is
+    /// on the word size of the cipher instance, not on the full 64 bits of
+    /// the UInt64.
+    /// </summary>
+    /// <param name="i">
+    /// the word to rotate.
+    /// </param>
+    /// <param name="distance">
+    /// the distance in bits to rotate.
+    /// </param>
+    /// <returns>
+    /// the rotated word, which may have unmasked high (&gt; word size) bits.
+    /// </returns>
+    function Rotr(i: UInt64; distance: Int32): UInt64; inline;
+
+    /// <summary>
+    /// Read <see cref="ClpSpeckEngine|TSpeckEngine.FwordSize" /> bytes from
+    /// the input data in little-endian order.
+    /// </summary>
+    /// <param name="bytes">
+    /// the data to read a word from.
+    /// </param>
+    /// <param name="off">
+    /// the offset to read the word from.
+    /// </param>
+    /// <returns>
+    /// the read word, with zeroes in any bits higher than the word size.
+    /// </returns>
+    function BytesToWord(const bytes: TCryptoLibByteArray; off: Int32)
+      : UInt64; inline;
+
+    /// <summary>
+    /// Writes <see cref="ClpSpeckEngine|TSpeckEngine.FwordSize" /> bytes
+    /// into a buffer in little-endian order.
+    /// </summary>
+    /// <param name="word">
+    /// the word to write.
+    /// </param>
+    /// <param name="bytes">
+    /// the buffer to write the word bytes to.
+    /// </param>
+    /// <param name="off">
+    /// the offset to write the data at.
+    /// </param>
+    procedure WordToBytes(word: UInt64; const bytes: TCryptoLibByteArray;
+      off: Int32); inline;
+
+  strict protected
+
+    /// <summary>
+    /// Masks all bits higher than the word size of this cipher in the
+    /// supplied value.
+    /// </summary>
+    /// <param name="val">
+    /// the value to mask.
+    /// </param>
+    /// <returns>
+    /// the masked value.
+    /// </returns>
+    function Mask(val: UInt64): UInt64; virtual; abstract;
+
+    procedure SetKey(const keyBytes: TCryptoLibByteArray); override;
+
+    procedure UnPackBlock(const bytes: TCryptoLibByteArray;
+      off: Int32); override;
+
+    procedure PackBlock(const bytes: TCryptoLibByteArray; off: Int32); override;
+
+    procedure EncryptBlock(); override;
+
+    procedure DecryptBlock(); override;
+
+    /// <summary>
+    /// Constructs a Speck cipher with &lt;= 64 bit word size, using the
+    /// standard 8,3 rotation constants.
+    /// </summary>
+    /// <param name="wordSize">
+    /// the word size in bytes.
+    /// </param>
+    /// <param name="baseRounds">
+    /// the base (for 2 word key) round count.
+    /// </param>
+    constructor Create(wordSize, baseRounds: Int32); overload;
+
+    /// <summary>
+    /// Constructs a Speck cipher with &lt;= 64 bit word size, using custom
+    /// rotation constants.
+    /// </summary>
+    /// <param name="wordSize">
+    /// the word size in bytes.
+    /// </param>
+    /// <param name="baseRounds">
+    /// the base (for 2 word key) round count.
+    /// </param>
+    /// <param name="alpha">
+    /// the <em>alpha</em> rotation constant.
+    /// </param>
+    /// <param name="beta">
+    /// the <em>beta</em> rotation constant.
+    /// </param>
+    constructor Create(wordSize, baseRounds, alpha, beta: Int32); overload;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Speck32: 2 byte words, 7/2 rotation constants.
+  /// <p>
+  /// 20 base rounds (hypothetical)
+  /// </p>
+  /// 64 bit key/22 rounds.
+  /// </summary>
+  TSpeck32Engine = class sealed(TSpeckUInt32Engine)
+
+  strict protected
+    function Mask(val: UInt32): UInt32; override;
+    procedure CheckKeySize(keySizeBytes: Int32); override;
+
+  public
+    constructor Create();
+
+  end;
+
+type
+
+  /// <summary>
+  /// Speck48: 3 byte words, 8/3 rotation constants.
+  /// <p>
+  /// 21 base rounds (hypothetical)
+  /// </p>
+  /// 72 bit key/22 rounds.
+  /// 96 bit key/23 rounds.
+  /// </summary>
+  TSpeck48Engine = class sealed(TSpeckUInt32Engine)
+
+  strict protected
+    function Mask(val: UInt32): UInt32; override;
+    procedure CheckKeySize(keySizeBytes: Int32); override;
+
+  public
+    constructor Create();
+
+  end;
+
+type
+
+  /// <summary>
+  /// Speck64: 4 byte words, 8/3 rotation constants.
+  /// <p>
+  /// 25 base rounds (hypothetical)
+  /// </p>
+  /// 96 bit key/26 rounds.
+  /// 128 bit key/27 rounds.
+  /// </summary>
+  TSpeck64Engine = class sealed(TSpeckUInt32Engine)
+
+  strict protected
+    function Mask(val: UInt32): UInt32; override;
+    procedure CheckKeySize(keySizeBytes: Int32); override;
+
+  public
+    constructor Create();
+
+  end;
+
+type
+
+  /// <summary>
+  /// Speck96: 6 byte words, 8/3 rotation constants.
+  /// <p>
+  /// 28 base rounds
+  /// </p>
+  /// 96 bit key/28 rounds.
+  /// 144 bit key/29 rounds.
+  /// </summary>
+  TSpeck96Engine = class sealed(TSpeckUInt64Engine)
+
+  strict protected
+    function Mask(val: UInt64): UInt64; override;
+    procedure CheckKeySize(keySizeBytes: Int32); override;
+
+  public
+    constructor Create();
+
+  end;
+
+type
+
+  /// <summary>
+  /// Speck128: 8 byte words, 8/3 rotation constants.
+  /// <p>
+  /// 32 base rounds
+  /// </p>
+  /// 128 bit key/32 rounds.
+  /// 192 bit key/33 rounds.
+  /// 256 bit key/34 rounds.
+  /// </summary>
+  TSpeck128Engine = class sealed(TSpeckUInt64Engine)
+
+  strict protected
+    function Mask(val: UInt64): UInt64; override;
+    procedure CheckKeySize(keySizeBytes: Int32); override;
+
+  public
+    constructor Create();
+
+  end;
+
+implementation
+
+{ TSpeckEngine }
+
+constructor TSpeckEngine.Create(wordSize, baseRounds, alpha, beta: Int32);
+begin
+  Inherited Create();
+  FwordSize := wordSize;
+  FbaseRounds := baseRounds;
+  Frounds := baseRounds;
+  FblockSize := wordSize * 2;
+  FwordSizeBits := wordSize * 8;
+  Falpha := alpha;
+  Fbeta := beta;
+end;
+
+function TSpeckEngine.GetBlockSize: Int32;
+begin
+  result := FblockSize;
+end;
+
+procedure TSpeckEngine.EngineInit(forEncryption: Boolean;
+  const keyBytes: TCryptoLibByteArray);
+begin
+  FforEncryption := forEncryption;
+  CheckKeySize(System.Length(keyBytes));
+  SetKey(keyBytes);
+  Finitialised := true;
+end;
+
+function TSpeckEngine.GetAlgorithmName: String;
+begin
+  result := Format('Speck%d', [FblockSize * 8]);
+end;
+
+function TSpeckEngine.GetIsPartialBlockOkay: Boolean;
+begin
+  result := false;
+end;
+
+procedure TSpeckEngine.Init(forEncryption: Boolean;
+  const parameters: ICipherParameters);
+var
+  keyParameter: IKeyParameter;
+  keyBytes: TCryptoLibByteArray;
+begin
+
+  if not Supports(parameters, IKeyParameter, keyParameter) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidParameterSpeckInit,
+      [(parameters as TObject).ToString]);
+  end;
+  keyBytes := keyParameter.GetKey;
+  EngineInit(forEncryption, keyBytes);
+end;
+
+function TSpeckEngine.ProcessBlock(const input: TCryptoLibByteArray;
+  inOff: Int32; const output: TCryptoLibByteArray; outOff: Int32): Int32;
+begin
+  if (not Finitialised) then
+  begin
+    raise EInvalidOperationCryptoLibException.CreateResFmt
+      (@SSpeckEngineNotInitialised, [AlgorithmName]);
+  end;
+
+  TCheck.DataLength((inOff + FblockSize) > System.Length(input),
+    SInputBuffertooShort);
+  TCheck.DataLength((outOff + FblockSize) > System.Length(output),
+    SOutputBuffertooShort);
+
+  UnPackBlock(input, inOff);
+  if (FforEncryption) then
+  begin
+    EncryptBlock();
+  end
+  else
+  begin
+    DecryptBlock();
+  end;
+  PackBlock(output, outOff);
+
+  result := FblockSize;
+end;
+
+procedure TSpeckEngine.Reset;
+begin
+  // nothing to do.
+end;
+
+{ TSpeckUInt32Engine }
+
+function TSpeckUInt32Engine.Rotl(i: UInt32; distance: Int32): UInt32;
+begin
+  result := ((i shl distance) or (i shr (FwordSizeBits - distance)));
+end;
+
+function TSpeckUInt32Engine.Rotr(i: UInt32; distance: Int32): UInt32;
+begin
+  result := ((i shr distance) or (i shl (FwordSizeBits - distance)));
+end;
+
+function TSpeckUInt32Engine.BytesToWord(const bytes: TCryptoLibByteArray;
+  off: Int32): UInt32;
+var
+  index: Int32;
+begin
+  TCheck.DataLength((off + FwordSize) > System.Length(bytes),
+    SInvalidArgumentEncountered);
+
+  index := off + FwordSize - 1;
+  result := (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  if (FwordSize > 2) then
+  begin
+    result := (result shl 8) or (bytes[index]);
+    System.Dec(index);
+    if (FwordSize > 3) then
+    begin
+      result := (result shl 8) or (bytes[index]);
+    end;
+  end;
+
+end;
+
+procedure TSpeckUInt32Engine.WordToBytes(word: UInt32;
+  const bytes: TCryptoLibByteArray; off: Int32);
+begin
+  TCheck.DataLength((off + FwordSize) > System.Length(bytes),
+    SInvalidArgumentEncountered);
+
+  bytes[off] := Byte(word);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 8);
+  System.Inc(off);
+  if (FwordSize > 2) then
+  begin
+    bytes[off] := Byte(word shr 16);
+    System.Inc(off);
+    if (FwordSize > 3) then
+    begin
+      bytes[off] := Byte(word shr 24);
+    end;
+  end;
+
+end;
+
+constructor TSpeckUInt32Engine.Create(wordSize, baseRounds: Int32);
+begin
+  Create(wordSize, baseRounds, 8, 3);
+end;
+
+constructor TSpeckUInt32Engine.Create(wordSize, baseRounds, alpha, beta: Int32);
+begin
+  Inherited Create(wordSize, baseRounds, alpha, beta);
+end;
+
+procedure TSpeckUInt32Engine.EncryptBlock;
+var
+  x, y: UInt32;
+  r: Int32;
+begin
+  x := Fx;
+  y := Fy;
+
+  for r := 0 to System.Pred(Frounds) do
+  begin
+    x := Mask((Rotr(x, Falpha) + y) xor Fk[r]);
+    y := Mask(Rotl(y, Fbeta) xor x);
+  end;
+
+  Fx := x;
+  Fy := y;
+end;
+
+procedure TSpeckUInt32Engine.DecryptBlock;
+var
+  x, y: UInt32;
+  r: Int32;
+begin
+  x := Fx;
+  y := Fy;
+
+  for r := System.Pred(Frounds) downto 0 do
+  begin
+    y := Mask(Rotr(x xor y, Fbeta));
+    x := Mask(Rotl(Mask((x xor Fk[r]) - y), Falpha));
+  end;
+
+  Fx := x;
+  Fy := y;
+
+end;
+
+procedure TSpeckUInt32Engine.PackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  WordToBytes(Fx, bytes, off + FwordSize);
+  WordToBytes(Fy, bytes, off);
+end;
+
+procedure TSpeckUInt32Engine.UnPackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  Fx := BytesToWord(bytes, off + FwordSize);
+  Fy := BytesToWord(bytes, off);
+end;
+
+procedure TSpeckUInt32Engine.SetKey(const keyBytes: TCryptoLibByteArray);
+var
+  keyWords, i, lw: Int32;
+  L: TCryptoLibUInt32Array;
+begin
+  // Determine number of key words m
+  keyWords := System.Length(keyBytes) div FwordSize;
+
+  // Number of rounds is increased by 1 for each key word > 2
+  Frounds := FbaseRounds + (keyWords - 2);
+  System.SetLength(Fk, Frounds);
+
+  // Load k[0]
+  Fk[0] := BytesToWord(keyBytes, 0);
+
+  // Load l[m-2]...l[0], leave space for l[m-1] in key expansion
+  System.SetLength(L, keyWords);
+
+  for i := 0 to System.Pred(keyWords - 1) do
+  begin
+    L[(keyWords - 2) - i] := BytesToWord(keyBytes, ((keyWords - 1) - i) *
+      FwordSize);
+  end;
+  // Key expansion using round function over l[m-2]...l[0],k[0] with round number as key
+  for i := 0 to System.Pred(Frounds - 1) do
+  begin
+    lw := (i + keyWords - 1) mod keyWords;
+    L[lw] := Mask((Rotr(L[i mod keyWords], Falpha) + Fk[i]) xor UInt32(i));
+    Fk[i + 1] := Mask(Rotl(Fk[i], Fbeta) xor L[lw]);
+
+  end;
+
+end;
+
+{ TSpeckUInt64Engine }
+
+function TSpeckUInt64Engine.Rotl(i: UInt64; distance: Int32): UInt64;
+begin
+  result := ((i shl distance) or (i shr (FwordSizeBits - distance)));
+end;
+
+function TSpeckUInt64Engine.Rotr(i: UInt64; distance: Int32): UInt64;
+begin
+  result := ((i shr distance) or (i shl (FwordSizeBits - distance)));
+end;
+
+function TSpeckUInt64Engine.BytesToWord(const bytes: TCryptoLibByteArray;
+  off: Int32): UInt64;
+var
+  index: Int32;
+begin
+  TCheck.DataLength((off + FwordSize) > System.Length(bytes),
+    SInvalidArgumentEncountered);
+
+  index := off + FwordSize - 1;
+  result := (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  result := (result shl 8) or (bytes[index]);
+  System.Dec(index);
+  if (FwordSize = 8) then
+  begin
+    result := (result shl 8) or (bytes[index]);
+    System.Dec(index);
+    result := (result shl 8) or (bytes[index]);
+  end;
+end;
+
+procedure TSpeckUInt64Engine.WordToBytes(word: UInt64;
+  const bytes: TCryptoLibByteArray; off: Int32);
+begin
+  TCheck.DataLength((off + FwordSize) > System.Length(bytes),
+    SInvalidArgumentEncountered);
+
+  bytes[off] := Byte(word);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 8);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 16);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 24);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 32);
+  System.Inc(off);
+  bytes[off] := Byte(word shr 40);
+  System.Inc(off);
+  if (FwordSize = 8) then
+  begin
+    bytes[off] := Byte(word shr 48);
+    System.Inc(off);
+    bytes[off] := Byte(word shr 56);
+  end;
+
+end;
+
+constructor TSpeckUInt64Engine.Create(wordSize, baseRounds: Int32);
+begin
+  Create(wordSize, baseRounds, 8, 3);
+end;
+
+constructor TSpeckUInt64Engine.Create(wordSize, baseRounds, alpha, beta: Int32);
+begin
+  Inherited Create(wordSize, baseRounds, alpha, beta);
+end;
+
+procedure TSpeckUInt64Engine.EncryptBlock;
+var
+  x, y: UInt64;
+  r: Int32;
+begin
+  x := Fx;
+  y := Fy;
+
+  for r := 0 to System.Pred(Frounds) do
+  begin
+    x := Mask((Rotr(x, Falpha) + y) xor Fk[r]);
+    y := Mask(Rotl(y, Fbeta) xor x);
+  end;
+
+  Fx := x;
+  Fy := y;
+end;
+
+procedure TSpeckUInt64Engine.DecryptBlock;
+var
+  x, y: UInt64;
+  r: Int32;
+begin
+  x := Fx;
+  y := Fy;
+
+  for r := System.Pred(Frounds) downto 0 do
+  begin
+    y := Mask(Rotr(x xor y, Fbeta));
+    x := Mask(Rotl(Mask((x xor Fk[r]) - y), Falpha));
+  end;
+
+  Fx := x;
+  Fy := y;
+
+end;
+
+procedure TSpeckUInt64Engine.PackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  WordToBytes(Fx, bytes, off + FwordSize);
+  WordToBytes(Fy, bytes, off);
+end;
+
+procedure TSpeckUInt64Engine.UnPackBlock(const bytes: TCryptoLibByteArray;
+  off: Int32);
+begin
+  Fx := BytesToWord(bytes, off + FwordSize);
+  Fy := BytesToWord(bytes, off);
+end;
+
+procedure TSpeckUInt64Engine.SetKey(const keyBytes: TCryptoLibByteArray);
+var
+  keyWords, i, lw: Int32;
+  L: TCryptoLibUInt64Array;
+begin
+  // Determine number of key words m
+  keyWords := System.Length(keyBytes) div FwordSize;
+
+  // Number of rounds is increased by 1 for each key word > 2
+  Frounds := FbaseRounds + (keyWords - 2);
+  System.SetLength(Fk, Frounds);
+
+  // Load k[0]
+  Fk[0] := BytesToWord(keyBytes, 0);
+
+  // Load l[m-2]...l[0], leave space for l[m-1] in key expansion
+  System.SetLength(L, keyWords);
+
+  for i := 0 to System.Pred(keyWords - 1) do
+  begin
+    L[(keyWords - 2) - i] := BytesToWord(keyBytes, ((keyWords - 1) - i) *
+      FwordSize);
+  end;
+  // Key expansion using round function over l[m-2]...l[0],k[0] with round number as key
+  for i := 0 to System.Pred(Frounds - 1) do
+  begin
+    lw := (i + keyWords - 1) mod keyWords;
+    L[lw] := Mask((Rotr(L[i mod keyWords], Falpha) + Fk[i]) xor UInt64(i));
+    Fk[i + 1] := Mask(Rotl(Fk[i], Fbeta) xor L[lw]);
+
+  end;
+
+end;
+
+{ TSpeck32Engine }
+
+constructor TSpeck32Engine.Create;
+begin
+  Inherited Create(2, 20, 7, 2);
+end;
+
+function TSpeck32Engine.Mask(val: UInt32): UInt32;
+begin
+  result := (val and $FFFF);
+end;
+
+procedure TSpeck32Engine.CheckKeySize(keySizeBytes: Int32);
+begin
+  if (keySizeBytes <> 8) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SSpeck32InvalidKeySize,
+      [keySizeBytes * 8]);
+  end;
+end;
+
+{ TSpeck48Engine }
+
+constructor TSpeck48Engine.Create;
+begin
+  Inherited Create(3, 21);
+end;
+
+function TSpeck48Engine.Mask(val: UInt32): UInt32;
+begin
+  result := (val and $FFFFFF);
+end;
+
+procedure TSpeck48Engine.CheckKeySize(keySizeBytes: Int32);
+begin
+  if not(keySizeBytes in [9, 12]) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SSpeck48InvalidKeySize,
+      [keySizeBytes * 8]);
+  end;
+end;
+
+{ TSpeck64Engine }
+
+constructor TSpeck64Engine.Create;
+begin
+  Inherited Create(4, 25);
+end;
+
+function TSpeck64Engine.Mask(val: UInt32): UInt32;
+begin
+  result := val;
+end;
+
+procedure TSpeck64Engine.CheckKeySize(keySizeBytes: Int32);
+begin
+  if not(keySizeBytes in [12, 16]) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SSpeck64InvalidKeySize,
+      [keySizeBytes * 8]);
+  end;
+end;
+
+{ TSpeck96Engine }
+
+constructor TSpeck96Engine.Create;
+begin
+  Inherited Create(6, 28);
+end;
+
+function TSpeck96Engine.Mask(val: UInt64): UInt64;
+begin
+  result := (val and $0000FFFFFFFFFFFF);
+end;
+
+procedure TSpeck96Engine.CheckKeySize(keySizeBytes: Int32);
+begin
+  if not(keySizeBytes in [12, 18]) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SSpeck96InvalidKeySize,
+      [keySizeBytes * 8]);
+  end;
+end;
+
+{ TSpeck128Engine }
+
+constructor TSpeck128Engine.Create;
+begin
+  Inherited Create(8, 32);
+end;
+
+function TSpeck128Engine.Mask(val: UInt64): UInt64;
+begin
+  result := val;
+end;
+
+procedure TSpeck128Engine.CheckKeySize(keySizeBytes: Int32);
+begin
+  if not(keySizeBytes in [16, 24, 32]) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SSpeck128InvalidKeySize,
+      [keySizeBytes * 8]);
+  end;
+end;
+
+end.

+ 114 - 0
src/libraries/cryptolib4pascal/ClpXSalsa20Engine.pas

@@ -0,0 +1,114 @@
+{ *********************************************************************************** }
+{ *                              CryptoLib Library                                  * }
+{ *                Copyright (c) 2018 - 20XX Ugochukwu Mmaduekwe                    * }
+{ *                 Github Repository <https://github.com/Xor-el>                   * }
+
+{ *  Distributed under the MIT software license, see the accompanying file LICENSE  * }
+{ *          or visit http://www.opensource.org/licenses/mit-license.php.           * }
+
+{ *                              Acknowledgements:                                  * }
+{ *                                                                                 * }
+{ *      Thanks to Sphere 10 Software (http://www.sphere10.com/) for sponsoring     * }
+{ *                           development of this library                           * }
+
+{ * ******************************************************************************* * }
+
+(* &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& *)
+
+unit ClpXSalsa20Engine;
+
+{$I CryptoLib.inc}
+
+interface
+
+uses
+  ClpSalsa20Engine,
+  ClpIXSalsa20Engine,
+  ClpConverters,
+  ClpCryptoLibTypes;
+
+resourcestring
+  SNullKeyReInit = '%s Doesn''t Support Re-Init with Null Key';
+  SInvalidKeySize = '%s Requires a 256 bit Key';
+
+type
+  /// <summary>
+  /// Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+  /// </summary>
+  /// <remarks>
+  /// XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+  /// </remarks>
+  TXSalsa20Engine = class sealed(TSalsa20Engine, IXSalsa20Engine)
+
+  strict protected
+    function GetAlgorithmName: String; override;
+    function GetNonceSize: Int32; override;
+    /// <summary>
+    /// XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce
+    /// using a core Salsa20 function without input addition to produce 256 bit working key
+    /// and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state.
+    /// </summary>
+    procedure SetKey(const keyBytes, ivBytes: TCryptoLibByteArray); override;
+
+  end;
+
+implementation
+
+{ TXSalsa20Engine }
+
+function TXSalsa20Engine.GetAlgorithmName: String;
+begin
+  result := 'XSalsa20';
+end;
+
+function TXSalsa20Engine.GetNonceSize: Int32;
+begin
+  result := 24;
+end;
+
+procedure TXSalsa20Engine.SetKey(const keyBytes, ivBytes: TCryptoLibByteArray);
+var
+  hsalsa20Out: TCryptoLibUInt32Array;
+begin
+  if (keyBytes = Nil) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SNullKeyReInit,
+      [AlgorithmName]);
+  end;
+
+  if (System.Length(keyBytes) <> 32) then
+  begin
+    raise EArgumentCryptoLibException.CreateResFmt(@SInvalidKeySize,
+      [AlgorithmName]);
+  end;
+
+  // Set key for HSalsa20
+  Inherited SetKey(keyBytes, ivBytes);
+
+  // Pack next 64 bits of IV into engine state instead of counter
+  TConverters.le32_copy(PByte(ivBytes), 8 * System.SizeOf(Byte),
+    PCardinal(FEngineState), 8 * System.SizeOf(UInt32),
+    2 * System.SizeOf(UInt32));
+
+  // Process engine state to generate Salsa20 key
+  System.SetLength(hsalsa20Out, System.Length(FEngineState));
+  SalsaCore(20, FEngineState, hsalsa20Out);
+
+  // Set new key, removing addition in last round of salsaCore
+  FEngineState[1] := hsalsa20Out[0] - FEngineState[0];
+  FEngineState[2] := hsalsa20Out[5] - FEngineState[5];
+  FEngineState[3] := hsalsa20Out[10] - FEngineState[10];
+  FEngineState[4] := hsalsa20Out[15] - FEngineState[15];
+
+  FEngineState[11] := hsalsa20Out[6] - FEngineState[6];
+  FEngineState[12] := hsalsa20Out[7] - FEngineState[7];
+  FEngineState[13] := hsalsa20Out[8] - FEngineState[8];
+  FEngineState[14] := hsalsa20Out[9] - FEngineState[9];
+
+  // Last 64 bits of input IV
+  TConverters.le32_copy(PByte(ivBytes), 16 * System.SizeOf(Byte),
+    PCardinal(FEngineState), 6 * System.SizeOf(UInt32),
+    2 * System.SizeOf(UInt32));
+end;
+
+end.

+ 15 - 0
src/libraries/hashlib4pascal/HlpArgon2TypeAndVersion.pas

@@ -0,0 +1,15 @@
+unit HlpArgon2TypeAndVersion;
+
+{$I HashLib.inc}
+
+interface
+
+type
+{$SCOPEDENUMS ON}
+  TArgon2Type = (a2tARGON2_d = $00, a2tARGON2_i = $01, a2tARGON2_id = $02);
+  TArgon2Version = (a2vARGON2_VERSION_10 = $10, a2vARGON2_VERSION_13 = $13);
+{$SCOPEDENUMS OFF}
+
+implementation
+
+end.

+ 1 - 0
src/libraries/hashlib4pascal/HlpCRC32Fast.pas

@@ -240,3 +240,4 @@ begin
 end;
 
 end.
+

+ 76 - 10
src/libraries/hashlib4pascal/HlpHashFactory.pas

@@ -90,7 +90,9 @@ uses
   // HMAC Unit
   HlpHMACNotBuildInAdapter,
   // PBKDF2_HMAC Unit
-  HlpPBKDF2_HMACNotBuildInAdapter;
+  HlpPBKDF2_HMACNotBuildInAdapter,
+  // PBKDF_Argon2 Unit
+  HlpPBKDF_Argon2NotBuildInAdapter;
 
 type
   THashFactory = class sealed(TObject)
@@ -417,21 +419,69 @@ type
     public
 
       /// <summary>
-      /// Initializes a new interface instance of the TPBKDF2_HMAC class using a password, a salt, a number of iterations and an Instance of an "IHash" to be used as an "IHMAC" hashing implementation to derive the key.
+      /// Initializes a new interface instance of the TPBKDF2_HMAC class
+      /// using a password, a salt, a number of iterations and an Instance of
+      /// an "IHash" to be used as an "IHMAC" hashing implementation to
+      /// derive the key.
       /// </summary>
-      /// <param name="a_hash">The name of the "IHash" implementation to be transformed to an "IHMAC" Instance so it can be used to derive the key.</param>
-      /// <param name="password">The password to derive the key for.</param>
-      /// <param name="salt">The salt to use to derive the key.</param>
-      /// <param name="iterations">The number of iterations to use to derive the key.</param>
-      /// <exception cref="EArgumentNilHashLibException">The password, salt or algorithm is Nil.</exception>
-      /// <exception cref="EArgumentHashLibException">The iteration is less than 1.</exception>
-
+      /// <param name="a_hash">
+      /// The name of the "IHash" implementation to be transformed to an
+      /// "IHMAC" Instance so it can be used to derive the key.
+      /// </param>
+      /// <param name="a_password">
+      /// The password to derive the key for.
+      /// </param>
+      /// <param name="a_salt">
+      /// The salt to use to derive the key.
+      /// </param>
+      /// <param name="a_iterations">
+      /// The number of iterations to use to derive the key.
+      /// </param>
+      /// <returns>
+      /// The PKDF2_HMAC KDF Interface Instance <br />
+      /// </returns>
+      /// <exception cref="EArgumentNilHashLibException">
+      /// The password, salt or algorithm is Nil.
+      /// </exception>
+      /// <exception cref="EArgumentHashLibException">
+      /// The iteration is less than 1.
+      /// </exception>
       class function CreatePBKDF2_HMAC(const a_hash: IHash;
         const a_password, a_salt: THashLibByteArray; a_iterations: UInt32)
         : IPBKDF2_HMAC; static;
 
     end;
 
+
+    // ====================== TPBKDF_Argon2 ====================== //
+
+  type
+    TPBKDF_Argon2 = class sealed(TObject)
+
+    public
+
+      /// <summary>
+      /// Initializes a new interface instance of the TPBKDF_Argon2 class
+      /// using a password and an Argon2 parameter object to derive
+      /// the key.
+      /// </summary>
+      /// <param name="APassword">
+      /// The password to derive the key for. <br />
+      /// </param>
+      /// <param name="AArgon2Parameters">
+      /// The object to use for the Argon2 KDF
+      /// </param>
+      /// <returns>
+      /// The Argon2 KDF Interface Instance
+      /// </returns>
+      /// /// <exception cref="EArgumentNilHashLibException">
+      /// The password or builder instance is Nil.
+      /// </exception>
+      class function CreatePBKDF_Argon2(const APassword: THashLibByteArray;
+        const AArgon2Parameters: IArgon2Parameters): IPBKDF_Argon2; static;
+
+    end;
+
   end;
 
 implementation
@@ -1195,7 +1245,7 @@ class function TKDF.TPBKDF2_HMAC.CreatePBKDF2_HMAC(const a_hash: IHash;
 begin
 
   if not(System.Assigned(a_hash)) then
-    raise EArgumentNilHashLibException.CreateRes(@SUninitializedInstance);
+    raise EArgumentNilHashLibException.CreateRes(@SNotInitializedIHashInstance);
 
   if (a_password = Nil) then
     raise EArgumentNilHashLibException.CreateRes(@SEmptyPassword);
@@ -1210,4 +1260,20 @@ begin
     a_iterations);
 end;
 
+{ TKDF.TPBKDF_Argon2 }
+
+class function TKDF.TPBKDF_Argon2.CreatePBKDF_Argon2(const APassword
+  : THashLibByteArray; const AArgon2Parameters: IArgon2Parameters)
+  : IPBKDF_Argon2;
+begin
+  if not(System.Assigned(AArgon2Parameters)) then
+    raise EArgumentNilHashLibException.CreateRes
+      (@SArgon2ParameterBuilderNotInitialized);
+
+  if (APassword = Nil) then
+    raise EArgumentNilHashLibException.CreateRes(@SEmptyPassword);
+
+  Result := TPBKDF_Argon2NotBuildInAdapter.Create(APassword, AArgon2Parameters)
+end;
+
 end.

+ 60 - 0
src/libraries/hashlib4pascal/HlpIHashInfo.pas

@@ -8,6 +8,7 @@ uses
   HlpHashLibTypes,
   HlpIKDF,
   HlpIHash,
+  HlpArgon2TypeAndVersion,
   HlpNullable;
 
 type
@@ -55,6 +56,14 @@ type
     ['{D7E23DFB-036D-44AD-AA0C-FB83C9970565}']
   end;
 
+  IPBKDF_Argon2 = Interface(IKDF)
+    ['{A2BF19D2-8CEE-45B7-93A1-110A63A0A5A7}']
+  end;
+
+  IPBKDF_Argon2NotBuildIn = Interface(IPBKDF_Argon2)
+    ['{666D652C-E4E5-4C72-B09F-145495D1A95D}']
+  end;
+
   IHMAC = Interface(IWithKey)
     ['{A6D4DCC6-F6C3-4110-8CA2-FBE85227676E}']
   end;
@@ -91,6 +100,57 @@ type
     function SetXOFOutputSize(a_xof_size_in_bits: Int32): IXOF;
   end;
 
+type
+  IArgon2Parameters = interface(IInterface)
+    ['{566D3381-57F1-4EE0-81EC-3DB21FF49FBC}']
+    procedure Clear();
+
+    function GetSalt(): THashLibByteArray;
+    property Salt: THashLibByteArray read GetSalt;
+    function GetSecret(): THashLibByteArray;
+    property Secret: THashLibByteArray read GetSecret;
+    function GetAdditional(): THashLibByteArray;
+    property Additional: THashLibByteArray read GetAdditional;
+    function GetIterations(): Int32;
+    property Iterations: Int32 read GetIterations;
+    function GetMemory(): Int32;
+    property Memory: Int32 read GetMemory;
+    function GetLanes(): Int32;
+    property Lanes: Int32 read GetLanes;
+    function GetType(): TArgon2Type;
+    property &Type: TArgon2Type read GetType;
+    function GetVersion(): TArgon2Version;
+    property Version: TArgon2Version read GetVersion;
+  end;
+
+type
+  IArgon2ParametersBuilder = interface(IInterface)
+    ['{DD0EF0C0-BAB8-4587-95FD-B9A266E67BC1}']
+
+    function WithParallelism(AParallelism: Int32): IArgon2ParametersBuilder;
+
+    function WithSalt(const ASalt: THashLibByteArray): IArgon2ParametersBuilder;
+
+    function WithSecret(const ASecret: THashLibByteArray)
+      : IArgon2ParametersBuilder;
+
+    function WithAdditional(const AAdditional: THashLibByteArray)
+      : IArgon2ParametersBuilder;
+
+    function WithIterations(AIterations: Int32): IArgon2ParametersBuilder;
+
+    function WithMemoryAsKB(AMemory: Int32): IArgon2ParametersBuilder;
+
+    function WithMemoryPowOfTwo(AMemory: Int32): IArgon2ParametersBuilder;
+
+    function WithVersion(AVersion: TArgon2Version): IArgon2ParametersBuilder;
+
+    procedure Clear();
+
+    function Build(): IArgon2Parameters;
+
+  end;
+
 implementation
 
 end.

+ 3 - 3
src/libraries/hashlib4pascal/HlpPBKDF2_HMACNotBuildInAdapter.pas

@@ -13,10 +13,10 @@ uses
   HlpHashLibTypes;
 
 resourcestring
-  SInvalidArgument =
+  SInvalidByteCount =
     '"bc (ByteCount)" Argument must be a value greater than zero.';
   SInvalidIndex = 'Invalid start or end index in the internal buffer';
-  SUninitializedInstance = '"IHash" instance is uninitialized';
+  SNotInitializedIHashInstance = '"IHash" instance is uninitialized';
   SEmptyPassword = 'Password can''t be empty';
   SEmptySalt = 'Salt can''t be empty';
   SIterationtooSmall = 'Iteration must be greater than zero.';
@@ -131,7 +131,7 @@ var
 begin
 
   if (bc <= 0) then
-    raise EArgumentOutOfRangeHashLibException.CreateRes(@SInvalidArgument);
+    raise EArgumentOutOfRangeHashLibException.CreateRes(@SInvalidByteCount);
 
   System.SetLength(LKey, bc);
 

+ 1331 - 0
src/libraries/hashlib4pascal/HlpPBKDF_Argon2NotBuildInAdapter.pas

@@ -0,0 +1,1331 @@
+unit HlpPBKDF_Argon2NotBuildInAdapter;
+
+{$I HashLib.inc}
+
+interface
+
+uses
+{$IFDEF DELPHIXE7_UP}
+  System.Classes,
+  System.SysUtils,
+  System.Threading,
+{$ENDIF DELPHIXE7_UP}
+  HlpKDF,
+  HlpBits,
+  HlpIHash,
+  HlpIHashInfo,
+  HlpBlake2B,
+  HlpBlake2BConfig,
+  HlpIBlake2BConfig,
+  HlpConverters,
+  HlpArgon2TypeAndVersion,
+  HlpHashLibTypes;
+
+resourcestring
+  SInvalidOutputByteCount = '"bc (ByteCount)" Argument Less Than "%d".';
+  SBlockInstanceNotInitialized = 'Block Instance not Initialized';
+  SInputLengthInvalid = 'Input Length "%d" is not Equal to BlockSize "%d"';
+  SLanesTooSmall = 'Lanes Must be Greater Than "%d"';
+  SLanesTooBig = 'Lanes Must be Less Than "%d"';
+  SMemoryTooSmall = 'Memory is Less Than: "%d", Expected "%d"';
+  SIterationsTooSmall = 'Iterations is Less Than: "%d"';
+  SArgon2ParameterBuilderNotInitialized =
+    'Argon2 Parameter Builder Not Initialized';
+
+type
+  TArgon2ParametersBuilder = class abstract(TInterfacedObject,
+    IArgon2ParametersBuilder)
+
+  strict private
+
+  const
+    DEFAULT_ITERATIONS = Int32(3);
+    DEFAULT_MEMORY_COST = Int32(12);
+    DEFAULT_LANES = Int32(1);
+    DEFAULT_TYPE: TArgon2Type = TArgon2Type.a2tARGON2_i;
+    DEFAULT_VERSION: TArgon2Version = TArgon2Version.a2vARGON2_VERSION_13;
+
+  var
+    FSalt, FSecret, FAdditional: THashLibByteArray;
+    FIterations, FMemory, FLanes: Int32;
+    FType: TArgon2Type;
+    FVersion: TArgon2Version;
+
+  type
+    TArgon2Parameters = class sealed(TInterfacedObject, IArgon2Parameters)
+
+    strict private
+      FSalt, FSecret, FAdditional: THashLibByteArray;
+      FIterations, FMemory, FLanes: Int32;
+      FType: TArgon2Type;
+      FVersion: TArgon2Version;
+
+      function GetSalt(): THashLibByteArray; inline;
+      function GetSecret(): THashLibByteArray; inline;
+      function GetAdditional(): THashLibByteArray; inline;
+      function GetIterations(): Int32; inline;
+      function GetMemory(): Int32; inline;
+      function GetLanes(): Int32; inline;
+      function GetType(): TArgon2Type; inline;
+      function GetVersion(): TArgon2Version; inline;
+
+    public
+      constructor Create(AType: TArgon2Type;
+        const ASalt, ASecret, AAdditional: THashLibByteArray;
+        AIterations, AMemory, ALanes: Int32; AVersion: TArgon2Version);
+
+      procedure Clear(); inline;
+
+      property Salt: THashLibByteArray read GetSalt;
+      property Secret: THashLibByteArray read GetSecret;
+      property Additional: THashLibByteArray read GetAdditional;
+      property Iterations: Int32 read GetIterations;
+      property Memory: Int32 read GetMemory;
+      property Lanes: Int32 read GetLanes;
+      property &Type: TArgon2Type read GetType;
+      property Version: TArgon2Version read GetVersion;
+    end;
+
+  strict protected
+
+    constructor Create(AType: TArgon2Type);
+
+  public
+
+    function WithParallelism(AParallelism: Int32)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithSalt(const ASalt: THashLibByteArray)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithSecret(const ASecret: THashLibByteArray)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithAdditional(const AAdditional: THashLibByteArray)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithIterations(AIterations: Int32)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithMemoryAsKB(AMemory: Int32): IArgon2ParametersBuilder; virtual;
+
+    function WithMemoryPowOfTwo(AMemory: Int32)
+      : IArgon2ParametersBuilder; virtual;
+
+    function WithVersion(AVersion: TArgon2Version)
+      : IArgon2ParametersBuilder; virtual;
+
+    procedure Clear(); virtual;
+
+    function Build(): IArgon2Parameters; virtual;
+  end;
+
+type
+  TArgon2iParametersBuilder = class sealed(TArgon2ParametersBuilder)
+
+  strict private
+    constructor Create();
+
+  public
+    class function Builder(): IArgon2ParametersBuilder; static; inline;
+
+  end;
+
+type
+  TArgon2dParametersBuilder = class sealed(TArgon2ParametersBuilder)
+
+  strict private
+    constructor Create();
+
+  public
+    class function Builder(): IArgon2ParametersBuilder; static; inline;
+
+  end;
+
+type
+  TArgon2idParametersBuilder = class sealed(TArgon2ParametersBuilder)
+
+  strict private
+    constructor Create();
+
+  public
+    class function Builder(): IArgon2ParametersBuilder; static; inline;
+
+  end;
+
+type
+
+  /// <summary>
+  /// Argon2 PBKDF - Based on the results of https://password-hashing.net/
+  /// and https://www.ietf.org/archive/id/draft-irtf-cfrg-argon2-03.txt
+  /// </summary>
+  TPBKDF_Argon2NotBuildInAdapter = class sealed(TKDF, IPBKDF_Argon2,
+    IPBKDF_Argon2NotBuildIn)
+
+  strict private
+  type
+    TBlock = record
+
+    private
+    var
+      // 128 * 8 Byte QWords
+      Fv: THashLibUInt64Array;
+      FInitialized: Boolean;
+
+      procedure CheckAreBlocksInitialized(const ABlocks
+        : THashLibGenericArray<TBlock>);
+      procedure CopyBlock(const AOther: TBlock); inline;
+      procedure &Xor(const AB1, AB2: TBlock); overload;
+      procedure XorWith(const AOther: TBlock);
+
+    public
+      class function CreateBlock(): TBlock; static;
+
+      procedure Clear(); inline;
+      procedure &Xor(const AB1, AB2, AB3: TBlock); overload;
+      procedure FromBytes(const AInput: THashLibByteArray);
+
+      function ToBytes(): THashLibByteArray;
+      function ToString(): String;
+
+    end;
+
+  type
+    TPosition = record
+
+    private
+    var
+      FPass, FLane, FSlice, FIndex: Int32;
+
+    public
+      class function CreatePosition(APass, ALane, ASlice, AIndex: Int32)
+        : TPosition; static;
+
+    end;
+
+  const
+
+    ARGON2_BLOCK_SIZE = Int32(1024);
+    ARGON2_QWORDS_IN_BLOCK = Int32(ARGON2_BLOCK_SIZE div 8);
+
+    ARGON2_ADDRESSES_IN_BLOCK = Int32(128);
+
+    ARGON2_PREHASH_DIGEST_LENGTH = Int32(64);
+    ARGON2_PREHASH_SEED_LENGTH = Int32(72);
+
+    ARGON2_SYNC_POINTS = Int32(4);
+
+    // Minimum and maximum number of lanes (degree of parallelism)
+    MIN_PARALLELISM = Int32(1);
+    MAX_PARALLELISM = Int32(16777216);
+
+    // Minimum and maximum digest size in bytes
+    MIN_OUTLEN = Int32(4);
+
+    // Minimum and maximum number of passes
+    MIN_ITERATIONS = Int32(1);
+
+  var
+
+    FMemory: THashLibGenericArray<TBlock>;
+    FSegmentLength, FLaneLength: Int32;
+    FParameters: IArgon2Parameters;
+    FPassword, FResult: THashLibByteArray;
+
+    class procedure AddIntToLittleEndian(const AHash: IHash; An: Int32);
+      static; inline;
+
+    class procedure AddByteString(const AHash: IHash;
+      const AOctets: THashLibByteArray); static; inline;
+
+    class function MakeBlake2BInstanceAndInitialize(AHashSize: Int32): IHash;
+      static; inline;
+
+    class function GetStartingIndex(const APosition: TPosition): Int32; inline;
+
+    procedure InitializeMemory(AMemoryBlocks: Int32);
+    procedure DoInit(const AParameters: IArgon2Parameters);
+    // Clear memory.
+    procedure Reset();
+    {
+      *designed by the Lyra PHC team */
+      /* a <- a + b + 2*aL*bL
+      * + == addition modulo 2^64
+      * aL = least 32 bit
+      * }
+    procedure fBlaMka(var ABlock: TBlock; Ax, Ay: Int32); inline;
+    procedure Rotr64(var ABlock: TBlock; Av, Aw, Ac: Int32); inline;
+    procedure F(var ABlock: TBlock; Aa, Ab, Ac, Ad: Int32); inline;
+    procedure RoundFunction(var ABlock: TBlock; Av0, Av1, Av2, Av3, Av4, Av5,
+      Av6, Av7, Av8, Av9, Av10, Av11, Av12, Av13, Av14, Av15: Int32); inline;
+    procedure FillBlock(var Ax, Ay, ACurrentBlock: TBlock; AWithXor: Boolean);
+
+    function IsDataIndependentAddressing(const APosition: TPosition)
+      : Boolean; inline;
+    procedure NextAddresses(var AZeroBlock, AInputBlock, AAddressBlock
+      : TBlock); inline;
+    function IntToUInt64(Ax: Int32): UInt64; inline;
+    procedure InitAddressBlocks(const APosition: TPosition;
+      var AZeroBlock, AInputBlock, AAddressBlock: TBlock);
+    (* 1.2 Computing the index of the reference block
+      1.2.1 Taking pseudo-random value from the previous block *)
+    function GetPseudoRandom(const APosition: TPosition;
+      var AAddressBlock, AInputBlock, AZeroBlock: TBlock; APrevOffset: Int32;
+      ADataIndependentAddressing: Boolean): UInt64;
+    function GetRefLane(const APosition: TPosition; APseudoRandom: UInt64)
+      : Int32; inline;
+    function GetRefColumn(const APosition: TPosition; APseudoRandom: UInt64;
+      ASameLane: Boolean): Int32;
+    function IsWithXor(const APosition: TPosition): Boolean; inline;
+    function GetPrevOffset(ACurrentOffset: Int32): Int32; inline;
+    function RotatePrevOffset(ACurrentOffset, APrevOffset: Int32)
+      : Int32; inline;
+    procedure FillSegment(var APosition: TPosition);
+    procedure FillMemoryBlocks();
+
+    (* *
+
+      * H0 = H64(p, τ, m, t, v, y, |P|, P, |S|, S, |L|, K, |X|, X)
+      * -> 64 byte (ARGON2_PREHASH_DIGEST_LENGTH)
+    *)
+    function InitialHash(const AParameters: IArgon2Parameters;
+      AOutputLength: Int32; const APassword: THashLibByteArray)
+      : THashLibByteArray; inline;
+
+    function GetInitialHashLong(const AInitialHash,
+      AAppendix: THashLibByteArray): THashLibByteArray; inline;
+
+    // H' - hash - variable length hash function
+    function Hash(const AInput: THashLibByteArray; AOutputLength: Int32)
+      : THashLibByteArray;
+    procedure Digest(AOutputLength: Int32);
+    (* *
+      * (H0 || 0 || i) 72 byte -> 1024 byte
+      * (H0 || 1 || i) 72 byte -> 1024 byte
+    *)
+    procedure FillFirstBlocks(const AInitialHash: THashLibByteArray);
+    procedure Initialize(const APassword: THashLibByteArray;
+      AOutputLength: Int32); inline;
+
+  public
+
+    /// <summary>
+    /// Initialise the <see cref="HlpPBKDF_Argon2NotBuildInAdapter|TPBKDF_Argon2NotBuildInAdapter" />
+    /// from the password and parameters.
+    /// </summary>
+    /// <param name="APassword">
+    /// the password to use.
+    /// </param>
+    /// <param name="AParameters">
+    /// Argon2 configuration.
+    /// </param>
+    constructor Create(const APassword: THashLibByteArray;
+      const AParameters: IArgon2Parameters);
+
+    /// <summary>
+    /// Returns the pseudo-random bytes for this object.
+    /// </summary>
+    /// <param name="bc">The number of pseudo-random key bytes to generate.</param>
+    /// <returns>A byte array filled with pseudo-random key bytes.</returns>
+    /// /// <exception cref="EArgumentOutOfRangeHashLibException">bc must be greater than zero.</exception>
+    function GetBytes(bc: Int32): THashLibByteArray; override;
+
+  end;
+
+implementation
+
+{ TArgon2ParametersBuilder.TArgon2Parameters }
+
+constructor TArgon2ParametersBuilder.TArgon2Parameters.Create
+  (AType: TArgon2Type; const ASalt, ASecret, AAdditional: THashLibByteArray;
+  AIterations, AMemory, ALanes: Int32; AVersion: TArgon2Version);
+begin
+  Inherited Create();
+  FSalt := System.Copy(ASalt);
+  FSecret := System.Copy(ASecret);
+  FAdditional := System.Copy(AAdditional);
+  FIterations := AIterations;
+  FMemory := AMemory;
+  FLanes := ALanes;
+  FType := AType;
+  FVersion := AVersion;
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetSalt: THashLibByteArray;
+begin
+  result := System.Copy(FSalt);
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetSecret
+  : THashLibByteArray;
+begin
+  result := System.Copy(FSecret);
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetAdditional
+  : THashLibByteArray;
+begin
+  result := System.Copy(FAdditional);
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetIterations: Int32;
+begin
+  result := FIterations;
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetMemory: Int32;
+begin
+  result := FMemory;
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetLanes: Int32;
+begin
+  result := FLanes;
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetVersion: TArgon2Version;
+begin
+  result := FVersion;
+end;
+
+function TArgon2ParametersBuilder.TArgon2Parameters.GetType: TArgon2Type;
+begin
+  result := FType;
+end;
+
+procedure TArgon2ParametersBuilder.TArgon2Parameters.Clear();
+begin
+  System.FillChar(FSalt[0], System.Length(FSalt) * System.SizeOf(Byte),
+    Byte(0));
+  System.FillChar(FSecret[0], System.Length(FSecret) *
+    System.SizeOf(Byte), Byte(0));
+  System.FillChar(FAdditional[0], System.Length(FAdditional) *
+    System.SizeOf(Byte), Byte(0));
+end;
+
+{ TArgon2ParametersBuilder }
+
+constructor TArgon2ParametersBuilder.Create(AType: TArgon2Type);
+begin
+  Inherited Create();
+  FLanes := DEFAULT_LANES;
+  FMemory := 1 shl DEFAULT_MEMORY_COST;
+  FIterations := DEFAULT_ITERATIONS;
+  FType := AType;
+  FVersion := DEFAULT_VERSION;
+end;
+
+function TArgon2ParametersBuilder.WithAdditional(const AAdditional
+  : THashLibByteArray): IArgon2ParametersBuilder;
+begin
+  FAdditional := System.Copy(AAdditional);
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithIterations(AIterations: Int32)
+  : IArgon2ParametersBuilder;
+begin
+  FIterations := AIterations;
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithMemoryAsKB(AMemory: Int32)
+  : IArgon2ParametersBuilder;
+begin
+  FMemory := AMemory;
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithMemoryPowOfTwo(AMemory: Int32)
+  : IArgon2ParametersBuilder;
+begin
+  FMemory := 1 shl AMemory;
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithParallelism(AParallelism: Int32)
+  : IArgon2ParametersBuilder;
+begin
+  FLanes := AParallelism;
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithSalt(const ASalt: THashLibByteArray)
+  : IArgon2ParametersBuilder;
+begin
+  FSalt := System.Copy(ASalt);
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithSecret(const ASecret: THashLibByteArray)
+  : IArgon2ParametersBuilder;
+begin
+  FSecret := System.Copy(ASecret);
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.WithVersion(AVersion: TArgon2Version)
+  : IArgon2ParametersBuilder;
+begin
+  FVersion := AVersion;
+  result := Self as IArgon2ParametersBuilder;
+end;
+
+function TArgon2ParametersBuilder.Build(): IArgon2Parameters;
+begin
+  result := TArgon2Parameters.Create(FType, FSalt, FSecret, FAdditional,
+    FIterations, FMemory, FLanes, FVersion);
+end;
+
+procedure TArgon2ParametersBuilder.Clear();
+begin
+  System.FillChar(FSalt[0], System.Length(FSalt) * System.SizeOf(Byte),
+    Byte(0));
+  System.FillChar(FSecret[0], System.Length(FSecret) *
+    System.SizeOf(Byte), Byte(0));
+  System.FillChar(FAdditional[0], System.Length(FAdditional) *
+    System.SizeOf(Byte), Byte(0));
+end;
+
+{ TArgon2iParametersBuilder }
+
+constructor TArgon2iParametersBuilder.Create;
+begin
+  Inherited Create(TArgon2Type.a2tARGON2_i);
+end;
+
+class function TArgon2iParametersBuilder.Builder: IArgon2ParametersBuilder;
+begin
+  result := TArgon2iParametersBuilder.Create() as IArgon2ParametersBuilder;
+end;
+
+{ TArgon2dParametersBuilder }
+
+constructor TArgon2dParametersBuilder.Create;
+begin
+  Inherited Create(TArgon2Type.a2tARGON2_d);
+end;
+
+class function TArgon2dParametersBuilder.Builder: IArgon2ParametersBuilder;
+begin
+  result := TArgon2dParametersBuilder.Create() as IArgon2ParametersBuilder;
+end;
+
+{ TArgon2idParametersBuilder }
+
+constructor TArgon2idParametersBuilder.Create;
+begin
+  Inherited Create(TArgon2Type.a2tARGON2_id);
+end;
+
+class function TArgon2idParametersBuilder.Builder: IArgon2ParametersBuilder;
+begin
+  result := TArgon2idParametersBuilder.Create() as IArgon2ParametersBuilder;
+end;
+
+{ TPBKDF_Argon2NotBuildInAdapter }
+
+class procedure TPBKDF_Argon2NotBuildInAdapter.AddIntToLittleEndian
+  (const AHash: IHash; An: Int32);
+begin
+  AHash.TransformBytes(TConverters.ReadUInt32AsBytesLE(UInt32(An)));
+end;
+
+class procedure TPBKDF_Argon2NotBuildInAdapter.AddByteString(const AHash: IHash;
+  const AOctets: THashLibByteArray);
+begin
+  if (AOctets <> Nil) then
+  begin
+    AddIntToLittleEndian(AHash, System.Length(AOctets));
+    AHash.TransformBytes(AOctets, 0, System.Length(AOctets));
+  end
+  else
+  begin
+    AddIntToLittleEndian(AHash, 0);
+  end;
+end;
+
+class function TPBKDF_Argon2NotBuildInAdapter.MakeBlake2BInstanceAndInitialize
+  (AHashSize: Int32): IHash;
+begin
+  result := TBlake2B.Create(TBlake2BConfig.Create(AHashSize) as IBlake2BConfig);
+  result.Initialize;
+end;
+
+class function TPBKDF_Argon2NotBuildInAdapter.GetStartingIndex(const APosition
+  : TPosition): Int32;
+begin
+  if ((APosition.FPass = 0) and (APosition.FSlice = 0)) then
+  begin
+    // we have already generated the first two blocks
+    result := 2;
+    Exit;
+  end
+  else
+  begin
+    result := 0;
+    Exit;
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.InitializeMemory(AMemoryBlocks: Int32);
+var
+  LIdx: Int32;
+begin
+  System.SetLength(FMemory, AMemoryBlocks);
+  for LIdx := 0 to System.Pred(System.Length(FMemory)) do
+  begin
+    FMemory[LIdx] := TBlock.CreateBlock();
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.DoInit(const AParameters
+  : IArgon2Parameters);
+var
+  LMemoryBlocks: Int32;
+begin
+  // 2. Align memory size
+  // Minimum memoryBlocks = 8L blocks, where L is the number of lanes */
+  LMemoryBlocks := AParameters.Memory;
+
+  if (LMemoryBlocks < (2 * ARGON2_SYNC_POINTS * AParameters.Lanes)) then
+  begin
+    LMemoryBlocks := 2 * ARGON2_SYNC_POINTS * AParameters.Lanes;
+  end;
+
+  FSegmentLength := LMemoryBlocks div (FParameters.Lanes * ARGON2_SYNC_POINTS);
+  FLaneLength := FSegmentLength * ARGON2_SYNC_POINTS;
+
+  // Ensure that all segments have equal length
+  LMemoryBlocks := FSegmentLength * (AParameters.Lanes * ARGON2_SYNC_POINTS);
+
+  InitializeMemory(LMemoryBlocks);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.Reset;
+var
+  LIdx: Int32;
+begin
+  // Reset memory.
+  for LIdx := 0 to System.Pred(System.Length(FMemory)) do
+  begin
+    FMemory[LIdx].Clear;
+    FMemory[LIdx] := Default(TBlock);
+  end;
+  FMemory := Nil;
+  System.FillChar(FResult[0], System.Length(FResult) *
+    System.SizeOf(Byte), Byte(0));
+  DoInit(FParameters);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.fBlaMka(var ABlock: TBlock;
+  Ax, Ay: Int32);
+var
+  Lm: UInt32;
+  Lxy: UInt64;
+begin
+  Lm := $FFFFFFFF;
+  Lxy := (ABlock.Fv[Ax] and Lm) * (ABlock.Fv[Ay] and Lm);
+
+  ABlock.Fv[Ax] := ABlock.Fv[Ax] + ABlock.Fv[Ay] + (2 * Lxy);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.Rotr64(var ABlock: TBlock;
+  Av, Aw, Ac: Int32);
+var
+  Ltemp: UInt64;
+begin
+  Ltemp := ABlock.Fv[Av] xor ABlock.Fv[Aw];
+  ABlock.Fv[Av] := TBits.RotateRight64(Ltemp, Ac);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.F(var ABlock: TBlock;
+  Aa, Ab, Ac, Ad: Int32);
+begin
+  fBlaMka(ABlock, Aa, Ab);
+  Rotr64(ABlock, Ad, Aa, 32);
+
+  fBlaMka(ABlock, Ac, Ad);
+  Rotr64(ABlock, Ab, Ac, 24);
+
+  fBlaMka(ABlock, Aa, Ab);
+  Rotr64(ABlock, Ad, Aa, 16);
+
+  fBlaMka(ABlock, Ac, Ad);
+  Rotr64(ABlock, Ab, Ac, 63);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.RoundFunction(var ABlock: TBlock;
+  Av0, Av1, Av2, Av3, Av4, Av5, Av6, Av7, Av8, Av9, Av10, Av11, Av12, Av13,
+  Av14, Av15: Int32);
+begin
+  F(ABlock, Av0, Av4, Av8, Av12);
+  F(ABlock, Av1, Av5, Av9, Av13);
+  F(ABlock, Av2, Av6, Av10, Av14);
+  F(ABlock, Av3, Av7, Av11, Av15);
+
+  F(ABlock, Av0, Av5, Av10, Av15);
+  F(ABlock, Av1, Av6, Av11, Av12);
+  F(ABlock, Av2, Av7, Av8, Av13);
+  F(ABlock, Av3, Av4, Av9, Av14);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.FillBlock(var Ax, Ay,
+  ACurrentBlock: TBlock; AWithXor: Boolean);
+var
+  R, Z: TBlock;
+  i: Int32;
+begin
+  R := TBlock.CreateBlock();
+  Z := TBlock.CreateBlock();
+
+  R.&Xor(Ax, Ay);
+  Z.CopyBlock(R);
+
+  (* Apply Blake2 on columns of 64-bit words: (0,1,...,15) , then
+    (16,17,..31)... finally (112,113,...127) *)
+
+  for i := 0 to System.Pred(8) do
+  begin
+
+    RoundFunction(Z, 16 * i, 16 * i + 1, 16 * i + 2, 16 * i + 3, 16 * i + 4,
+      16 * i + 5, 16 * i + 6, 16 * i + 7, 16 * i + 8, 16 * i + 9, 16 * i + 10,
+      16 * i + 11, 16 * i + 12, 16 * i + 13, 16 * i + 14, 16 * i + 15);
+  end;
+
+  (* Apply Blake2 on rows of 64-bit words: (0,1,16,17,...112,113), then
+    (2,3,18,19,...,114,115).. finally (14,15,30,31,...,126,127) *)
+
+  for i := 0 to System.Pred(8) do
+  begin
+
+    RoundFunction(Z, 2 * i, 2 * i + 1, 2 * i + 16, 2 * i + 17, 2 * i + 32,
+      2 * i + 33, 2 * i + 48, 2 * i + 49, 2 * i + 64, 2 * i + 65, 2 * i + 80,
+      2 * i + 81, 2 * i + 96, 2 * i + 97, 2 * i + 112, 2 * i + 113);
+
+  end;
+
+  if (AWithXor) then
+  begin
+    ACurrentBlock.&Xor(R, Z, ACurrentBlock);
+  end
+  else
+  begin
+    ACurrentBlock.&Xor(R, Z);
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.InitialHash(const AParameters
+  : IArgon2Parameters; AOutputLength: Int32; const APassword: THashLibByteArray)
+  : THashLibByteArray;
+var
+  LBlake2B: IHash;
+begin
+  LBlake2B := MakeBlake2BInstanceAndInitialize(ARGON2_PREHASH_DIGEST_LENGTH);
+
+  AddIntToLittleEndian(LBlake2B, AParameters.Lanes);
+  AddIntToLittleEndian(LBlake2B, AOutputLength);
+  AddIntToLittleEndian(LBlake2B, AParameters.Memory);
+  AddIntToLittleEndian(LBlake2B, AParameters.Iterations);
+  AddIntToLittleEndian(LBlake2B, Int32(AParameters.Version));
+  AddIntToLittleEndian(LBlake2B, Int32(AParameters.&Type));
+
+  AddByteString(LBlake2B, APassword);
+  AddByteString(LBlake2B, AParameters.Salt);
+  AddByteString(LBlake2B, AParameters.Secret);
+  AddByteString(LBlake2B, AParameters.Additional);
+
+  result := LBlake2B.TransformFinal.GetBytes();
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetInitialHashLong(const AInitialHash,
+  AAppendix: THashLibByteArray): THashLibByteArray;
+begin
+  System.SetLength(result, ARGON2_PREHASH_SEED_LENGTH);
+  System.Move(AInitialHash[0], result[0], ARGON2_PREHASH_DIGEST_LENGTH *
+    System.SizeOf(Byte));
+  System.Move(AAppendix[0], result[ARGON2_PREHASH_DIGEST_LENGTH],
+    4 * System.SizeOf(Byte));
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.Hash(const AInput: THashLibByteArray;
+  AOutputLength: Int32): THashLibByteArray;
+var
+  LOutlenBytes, LOutBuffer: THashLibByteArray;
+  LBlake2BLength, Lr, LPosition, LIdx, LLastLength: Int32;
+  LBlake2B: IHash;
+begin
+  System.SetLength(result, AOutputLength);
+  LOutlenBytes := TConverters.ReadUInt32AsBytesLE(UInt32(AOutputLength));
+
+  LBlake2BLength := 64;
+
+  if (AOutputLength <= LBlake2BLength) then
+  begin
+
+    LBlake2B := MakeBlake2BInstanceAndInitialize(AOutputLength);
+
+    LBlake2B.TransformBytes(LOutlenBytes, 0, System.Length(LOutlenBytes));
+    LBlake2B.TransformBytes(AInput, 0, System.Length(AInput));
+    result := LBlake2B.TransformFinal.GetBytes();
+  end
+  else
+  begin
+
+    LBlake2B := MakeBlake2BInstanceAndInitialize(LBlake2BLength);
+
+    System.SetLength(LOutBuffer, LBlake2BLength);
+
+    // V1
+    LBlake2B.TransformBytes(LOutlenBytes, 0, System.Length(LOutlenBytes));
+    LBlake2B.TransformBytes(AInput, 0, System.Length(AInput));
+    LOutBuffer := LBlake2B.TransformFinal.GetBytes();
+
+    System.Move(LOutBuffer[0], result[0], (LBlake2BLength div 2) *
+      System.SizeOf(Byte));
+
+    Lr := ((AOutputLength + 31) div 32) - 2;
+
+    LPosition := LBlake2BLength div 2;
+
+    LIdx := 2;
+
+    while LIdx <= Lr do
+    begin
+      // V2 to Vr
+      LBlake2B.TransformBytes(LOutBuffer, 0, System.Length(LOutBuffer));
+      LOutBuffer := LBlake2B.TransformFinal.GetBytes();
+
+      System.Move(LOutBuffer[0], result[LPosition], (LBlake2BLength div 2) *
+        System.SizeOf(Byte));
+
+      System.Inc(LIdx);
+      LPosition := LPosition + (LBlake2BLength div 2);
+    end;
+
+    LLastLength := AOutputLength - (32 * Lr);
+
+    // Vr+1
+
+    LBlake2B := MakeBlake2BInstanceAndInitialize(LLastLength);
+
+    LBlake2B.TransformBytes(LOutBuffer, 0, System.Length(LOutBuffer));
+    LOutBuffer := LBlake2B.TransformFinal.GetBytes();
+    System.Move(LOutBuffer[0], result[LPosition],
+      LLastLength * System.SizeOf(Byte));
+  end;
+{$IFDEF DEBUG}
+  System.Assert(System.Length(result) = AOutputLength);
+{$ENDIF DEBUG}
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.Digest(AOutputLength: Int32);
+var
+  LIdx, LLastBlockInLane: Int32;
+  FFinalBlockBytes: THashLibByteArray;
+  FFinalBlock: TBlock;
+begin
+  FFinalBlock := FMemory[FLaneLength - 1];
+
+  // XOR the last blocks
+  for LIdx := 1 to System.Pred(FParameters.Lanes) do
+  begin
+    LLastBlockInLane := (LIdx * FLaneLength) + (FLaneLength - 1);
+    FFinalBlock.XorWith(FMemory[LLastBlockInLane]);
+  end;
+
+  FFinalBlockBytes := FFinalBlock.ToBytes();
+
+  FResult := Hash(FFinalBlockBytes, AOutputLength);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.FillFirstBlocks(const AInitialHash
+  : THashLibByteArray);
+var
+  LZeroBytes, LOneBytes, LInitialHashWithZeros, LInitialHashWithOnes,
+    LBlockHashBytes: THashLibByteArray;
+  LIdx: Int32;
+begin
+
+  LZeroBytes := THashLibByteArray.Create(0, 0, 0, 0);
+  LOneBytes := THashLibByteArray.Create(1, 0, 0, 0);
+
+  LInitialHashWithZeros := GetInitialHashLong(AInitialHash, LZeroBytes);
+  LInitialHashWithOnes := GetInitialHashLong(AInitialHash, LOneBytes);
+
+  for LIdx := 0 to System.Pred(FParameters.Lanes) do
+  begin
+    TConverters.ReadUInt32AsBytesLE(UInt32(LIdx), LInitialHashWithZeros,
+      ARGON2_PREHASH_DIGEST_LENGTH + 4);
+    TConverters.ReadUInt32AsBytesLE(UInt32(LIdx), LInitialHashWithOnes,
+      ARGON2_PREHASH_DIGEST_LENGTH + 4);
+
+    LBlockHashBytes := Hash(LInitialHashWithZeros, ARGON2_BLOCK_SIZE);
+    FMemory[LIdx * FLaneLength].FromBytes(LBlockHashBytes);
+
+    LBlockHashBytes := Hash(LInitialHashWithOnes, ARGON2_BLOCK_SIZE);
+    FMemory[(LIdx * FLaneLength) + 1].FromBytes(LBlockHashBytes);
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.IsDataIndependentAddressing
+  (const APosition: TPosition): Boolean;
+begin
+  result := (FParameters.&Type = TArgon2Type.a2tARGON2_i) or
+    ((FParameters.&Type = TArgon2Type.a2tARGON2_id) and (APosition.FPass = 0)
+    and (APosition.FSlice < (ARGON2_SYNC_POINTS div 2)));
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.NextAddresses(var AZeroBlock,
+  AInputBlock, AAddressBlock: TBlock);
+begin
+  System.Inc(AInputBlock.Fv[6]);
+  FillBlock(AZeroBlock, AInputBlock, AAddressBlock, False);
+  FillBlock(AZeroBlock, AAddressBlock, AAddressBlock, False);
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.IntToUInt64(Ax: Int32): UInt64;
+begin
+  result := UInt64((Ax and UInt32($FFFFFFFF)))
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.InitAddressBlocks(const APosition
+  : TPosition; var AZeroBlock, AInputBlock, AAddressBlock: TBlock);
+begin
+  AInputBlock.Fv[0] := IntToUInt64(APosition.FPass);
+  AInputBlock.Fv[1] := IntToUInt64(APosition.FLane);
+  AInputBlock.Fv[2] := IntToUInt64(APosition.FSlice);
+  AInputBlock.Fv[3] := IntToUInt64(System.Length(FMemory));
+  AInputBlock.Fv[4] := IntToUInt64(FParameters.Iterations);
+  AInputBlock.Fv[5] := IntToUInt64(Int32(FParameters.&Type));
+
+  if ((APosition.FPass = 0) and (APosition.FSlice = 0)) then
+  begin
+    // Don't forget to generate the first block of addresses: */
+    NextAddresses(AZeroBlock, AInputBlock, AAddressBlock);
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetPseudoRandom(const APosition
+  : TPosition; var AAddressBlock, AInputBlock, AZeroBlock: TBlock;
+  APrevOffset: Int32; ADataIndependentAddressing: Boolean): UInt64;
+begin
+  if (ADataIndependentAddressing) then
+  begin
+    if (APosition.FIndex mod ARGON2_ADDRESSES_IN_BLOCK = 0) then
+    begin
+      NextAddresses(AZeroBlock, AInputBlock, AAddressBlock);
+    end;
+    result := AAddressBlock.Fv[APosition.FIndex mod ARGON2_ADDRESSES_IN_BLOCK];
+    Exit;
+  end
+  else
+  begin
+    result := FMemory[APrevOffset].Fv[0];
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetRefLane(const APosition: TPosition;
+  APseudoRandom: UInt64): Int32;
+var
+  LRefLane: Int32;
+begin
+  LRefLane := Int32((APseudoRandom shr 32) mod UInt64(FParameters.Lanes));
+
+  if ((APosition.FPass = 0) and (APosition.FSlice = 0)) then
+  begin
+    // Can not reference other lanes yet
+    LRefLane := APosition.FLane;
+  end;
+  result := LRefLane;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetRefColumn(const APosition: TPosition;
+  APseudoRandom: UInt64; ASameLane: Boolean): Int32;
+var
+  LReferenceAreaSize, LStartPosition, Ltemp: Int32;
+  LRelativePosition: UInt64;
+begin
+
+  if (APosition.FPass = 0) then
+  begin
+    LStartPosition := 0;
+
+    if (ASameLane) then
+    begin
+      // The same lane => add current segment
+      LReferenceAreaSize := ((APosition.FSlice) * FSegmentLength) +
+        APosition.FIndex - 1;
+    end
+    else
+    begin
+      if (APosition.FIndex = 0) then
+      begin
+        Ltemp := -1;
+      end
+      else
+      begin
+        Ltemp := 0;
+      end;
+      LReferenceAreaSize := (APosition.FSlice * FSegmentLength) + Ltemp;
+    end
+
+  end
+  else
+  begin
+    LStartPosition := ((APosition.FSlice + 1) * FSegmentLength) mod FLaneLength;
+
+    if (ASameLane) then
+    begin
+      LReferenceAreaSize := FLaneLength - FSegmentLength + APosition.FIndex - 1;
+    end
+    else
+    begin
+      if (APosition.FIndex = 0) then
+      begin
+        Ltemp := -1;
+      end
+      else
+      begin
+        Ltemp := 0;
+      end;
+      LReferenceAreaSize := FLaneLength - FSegmentLength + Ltemp;
+    end;
+  end;
+
+  LRelativePosition := APseudoRandom and UInt32($FFFFFFFF);
+  LRelativePosition := (LRelativePosition * LRelativePosition) shr 32;
+  LRelativePosition := UInt64(LReferenceAreaSize) - 1 -
+    UInt64((UInt64(LReferenceAreaSize) * LRelativePosition) shr 32);
+
+  result := Int32(UInt64(LStartPosition) + LRelativePosition) mod FLaneLength;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.IsWithXor(const APosition
+  : TPosition): Boolean;
+begin
+  result := not((APosition.FPass = 0) or
+    (FParameters.Version = TArgon2Version.a2vARGON2_VERSION_10));
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetPrevOffset(ACurrentOffset
+  : Int32): Int32;
+begin
+  if (ACurrentOffset mod FLaneLength = 0) then
+  begin
+    // Last block in this lane
+    result := ACurrentOffset + FLaneLength - 1;
+    Exit;
+  end
+  else
+  begin
+    // Previous block
+    result := ACurrentOffset - 1;
+    Exit;
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.RotatePrevOffset(ACurrentOffset,
+  APrevOffset: Int32): Int32;
+begin
+  if (ACurrentOffset mod FLaneLength = 1) then
+  begin
+    APrevOffset := ACurrentOffset - 1;
+  end;
+  result := APrevOffset;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.FillSegment(var APosition: TPosition);
+var
+  LAddressBlock, LInputBlock, LZeroBlock, LPrevBlock, LRefBlock,
+    LCurrentBlock: TBlock;
+  LDataIndependentAddressing, LWithXor: Boolean;
+  LStartingIndex, LCurrentOffset, LPrevOffset, LRefLane, LRefColumn: Int32;
+  LPseudoRandom: UInt64;
+begin
+
+  LDataIndependentAddressing := IsDataIndependentAddressing(APosition);
+  LStartingIndex := GetStartingIndex(APosition);
+  LCurrentOffset := (APosition.FLane * FLaneLength) +
+    (APosition.FSlice * FSegmentLength) + LStartingIndex;
+  LPrevOffset := GetPrevOffset(LCurrentOffset);
+
+  if (LDataIndependentAddressing) then
+  begin
+    LAddressBlock := TBlock.CreateBlock();
+    LZeroBlock := TBlock.CreateBlock();
+    LInputBlock := TBlock.CreateBlock();
+
+    InitAddressBlocks(APosition, LZeroBlock, LInputBlock, LAddressBlock);
+  end;
+
+  APosition.FIndex := LStartingIndex;
+
+  while APosition.FIndex < FSegmentLength do
+  begin
+    LPrevOffset := RotatePrevOffset(LCurrentOffset, LPrevOffset);
+
+    LPseudoRandom := GetPseudoRandom(APosition, LAddressBlock, LInputBlock,
+      LZeroBlock, LPrevOffset, LDataIndependentAddressing);
+    LRefLane := GetRefLane(APosition, LPseudoRandom);
+    LRefColumn := GetRefColumn(APosition, LPseudoRandom,
+      LRefLane = APosition.FLane);
+
+    // 2 Creating a new block
+    LPrevBlock := FMemory[LPrevOffset];
+    LRefBlock := FMemory[(((FLaneLength) * LRefLane) + LRefColumn)];
+    LCurrentBlock := FMemory[LCurrentOffset];
+
+    LWithXor := IsWithXor(APosition);
+    FillBlock(LPrevBlock, LRefBlock, LCurrentBlock, LWithXor);
+
+    System.Inc(APosition.FIndex);
+    System.Inc(LCurrentOffset);
+    System.Inc(LPrevOffset);
+  end;
+end;
+
+{$IFDEF DELPHIXE7_UP}
+
+procedure TPBKDF_Argon2NotBuildInAdapter.FillMemoryBlocks;
+
+  function CreateTask(APosition: TPosition): ITask;
+  begin
+    result := TTask.Create(
+      procedure()
+      begin
+        FillSegment(APosition);
+      end);
+  end;
+
+var
+  LIdx, LJdx, LKdx, LTaskIdx: Int32;
+  LPosition: TPosition;
+  LArrayTasks: array of ITask;
+begin
+  System.SetLength(LArrayTasks, FParameters.Lanes);
+  for LIdx := 0 to System.Pred(FParameters.Iterations) do
+  begin
+    for LJdx := 0 to System.Pred(ARGON2_SYNC_POINTS) do
+    begin
+      for LKdx := 0 to System.Pred(FParameters.Lanes) do
+      begin
+        LPosition := TPosition.CreatePosition(LIdx, LKdx, LJdx, 0);
+        LArrayTasks[LKdx] := CreateTask(LPosition);
+      end;
+      for LTaskIdx := System.Low(LArrayTasks) to System.High(LArrayTasks) do
+      begin
+        LArrayTasks[LTaskIdx].Start;
+      end;
+      TTask.WaitForAll(LArrayTasks);
+    end;
+  end;
+
+end;
+
+{$ELSE}
+
+procedure TPBKDF_Argon2NotBuildInAdapter.FillMemoryBlocks;
+var
+  LIdx, LJdx, LKdx: Int32;
+  LPosition: TPosition;
+begin
+  for LIdx := 0 to System.Pred(FParameters.Iterations) do
+  begin
+    for LJdx := 0 to System.Pred(ARGON2_SYNC_POINTS) do
+    begin
+      for LKdx := 0 to System.Pred(FParameters.Lanes) do
+      begin
+        LPosition := TPosition.CreatePosition(LIdx, LKdx, LJdx, 0);
+        FillSegment(LPosition);
+      end;
+    end;
+  end;
+end;
+
+{$ENDIF DELPHIXE7_UP}
+
+procedure TPBKDF_Argon2NotBuildInAdapter.Initialize(const APassword
+  : THashLibByteArray; AOutputLength: Int32);
+var
+  LInitialHash: THashLibByteArray;
+begin
+  LInitialHash := InitialHash(FParameters, AOutputLength, APassword);
+  FillFirstBlocks(LInitialHash);
+end;
+
+constructor TPBKDF_Argon2NotBuildInAdapter.Create(const APassword
+  : THashLibByteArray; const AParameters: IArgon2Parameters);
+begin
+  Inherited Create();
+  FPassword := System.Copy(APassword);
+  FParameters := AParameters;
+
+  if (FParameters.Lanes < MIN_PARALLELISM) then
+  begin
+    raise EArgumentInvalidHashLibException.CreateResFmt(@SLanesTooSmall,
+      [MIN_PARALLELISM]);
+  end
+  else if (FParameters.Lanes > MAX_PARALLELISM) then
+  begin
+    raise EArgumentInvalidHashLibException.CreateResFmt(@SLanesTooBig,
+      [MAX_PARALLELISM]);
+  end
+  else if (FParameters.Memory < (2 * FParameters.Lanes)) then
+  begin
+    raise EArgumentInvalidHashLibException.CreateResFmt(@SMemoryTooSmall,
+      [(2 * FParameters.Lanes), (2 * FParameters.Lanes)]);
+  end
+  else if (FParameters.Iterations < MIN_ITERATIONS) then
+  begin
+    raise EArgumentInvalidHashLibException.CreateResFmt(@SIterationsTooSmall,
+      [MIN_ITERATIONS]);
+  end;
+
+  DoInit(AParameters);
+
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.GetBytes(bc: Int32): THashLibByteArray;
+begin
+  if (bc <= MIN_OUTLEN) then
+    raise EArgumentOutOfRangeHashLibException.CreateResFmt
+      (@SInvalidOutputByteCount, [MIN_OUTLEN]);
+
+  Initialize(FPassword, bc);
+  FillMemoryBlocks();
+  Digest(bc);
+  System.SetLength(result, bc);
+  System.Move(FResult[0], result[0], bc * System.SizeOf(Byte));
+
+  Reset();
+
+end;
+
+{ TPBKDF_Argon2NotBuildInAdapter.TBlock }
+
+class function TPBKDF_Argon2NotBuildInAdapter.TBlock.CreateBlock: TBlock;
+begin
+  result := Default(TBlock);
+  System.SetLength(result.Fv, ARGON2_QWORDS_IN_BLOCK);
+  result.FInitialized := True;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.CheckAreBlocksInitialized
+  (const ABlocks: THashLibGenericArray<TBlock>);
+var
+  LBlock: TBlock;
+begin
+  for LBlock in ABlocks do
+  begin
+    if not(LBlock.FInitialized) then
+    begin
+      raise EArgumentNilHashLibException.Create(SBlockInstanceNotInitialized);
+    end;
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.CopyBlock(const AOther: TBlock);
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self, AOther));
+  Fv := System.Copy(AOther.Fv);
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.&Xor(const AB1, AB2: TBlock);
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self,
+    AB1, AB2));
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    Fv[LIdx] := AB1.Fv[LIdx] xor AB2.Fv[LIdx];
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.XorWith(const AOther: TBlock);
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self, AOther));
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    Fv[LIdx] := Fv[LIdx] xor AOther.Fv[LIdx];
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.Clear;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self));
+  System.FillChar(Fv[0], System.Length(Fv) * System.SizeOf(Byte), Byte(0));
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.&Xor(const AB1, AB2,
+  AB3: TBlock);
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self, AB1,
+    AB2, AB3));
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    Fv[LIdx] := AB1.Fv[LIdx] xor AB2.Fv[LIdx] xor AB3.Fv[LIdx];
+  end;
+end;
+
+procedure TPBKDF_Argon2NotBuildInAdapter.TBlock.FromBytes
+  (const AInput: THashLibByteArray);
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self));
+  if (System.Length(AInput) <> ARGON2_BLOCK_SIZE) then
+  begin
+    raise EArgumentHashLibException.CreateResFmt(@SInputLengthInvalid,
+      [System.Length(AInput), ARGON2_BLOCK_SIZE]);
+  end;
+
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    Fv[LIdx] := TConverters.ReadBytesAsUInt64LE(PByte(AInput), LIdx * 8);
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.TBlock.ToBytes: THashLibByteArray;
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self));
+  System.SetLength(result, ARGON2_BLOCK_SIZE);
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    TConverters.ReadUInt64AsBytesLE(Fv[LIdx], result, LIdx * 8);
+  end;
+end;
+
+function TPBKDF_Argon2NotBuildInAdapter.TBlock.ToString: String;
+var
+  LIdx: Int32;
+begin
+  CheckAreBlocksInitialized(THashLibGenericArray<TBlock>.Create(Self));
+  result := '';
+  for LIdx := 0 to System.Pred(System.Length(Fv)) do
+  begin
+    result := result + TConverters.ConvertBytesToHexString
+      (TConverters.ReadUInt64AsBytesLE(Fv[LIdx]), False);
+  end;
+end;
+
+{ TPBKDF_Argon2NotBuildInAdapter.TPosition }
+
+class function TPBKDF_Argon2NotBuildInAdapter.TPosition.CreatePosition(APass,
+  ALane, ASlice, AIndex: Int32): TPosition;
+begin
+  result := Default(TPosition);
+  result.FPass := APass;
+  result.FLane := ALane;
+  result.FSlice := ASlice;
+  result.FIndex := AIndex;
+end;
+
+end.

+ 1 - 0
src/libraries/hashlib4pascal/HlpSipHash.pas

@@ -379,3 +379,4 @@ begin
 end;
 
 end.
+

+ 2 - 0
src/libraries/hashlib4pascal/README.md

@@ -26,6 +26,8 @@ It also supports Incremental Hashing.
     HMAC for any of the above.
     
     PBKDF2_HMAC for any of the above.
+    
+    PBKDF_Argon2 (2i, 2d and 2id variants).
 
 **Supported Compilers**
  

+ 0 - 2
src/libraries/simplebaselib4pascal/SbpBase16.pas

@@ -60,8 +60,6 @@ class function TBase16.Decode(const text: TSimpleBaseLibCharArray)
 
   function GetHexByte(c: Int32): Int32; inline;
   begin
-    Result := -1;
-
     case c of
       Ord('0') .. Ord('9'):
         Result := c - Ord('0');

+ 0 - 1
src/libraries/simplebaselib4pascal/SbpBase32.pas

@@ -208,7 +208,6 @@ begin
 
   bitsLeft := bitsPerByte;
   currentByte := Int32(Byte(pInput^));
-  outputPad := 0;
 
   while (pInput <> pEnd) do
   begin

+ 0 - 4
src/libraries/simplebaselib4pascal/SbpBase64.pas

@@ -221,7 +221,6 @@ begin
   if (padding = 0) then
   begin
     resultPtr^ := Byte(((temp1 and $03) shl 6) or temp2);
-    System.Inc(resultPtr);
   end;
 
 end;
@@ -316,7 +315,6 @@ begin
   else
   begin
     b3 := InputPtr^;
-    System.Inc(InputPtr);
   end;
 
   OutputPtr^ := table[(TBits.Asr32((b1 and $FC), 2)) + 1];
@@ -344,8 +342,6 @@ begin
     OutputPtr^ := table[(b3 and $3F) + 1]
   end;
 
-  System.Inc(OutputPtr);
-
   if (not Falphabet.PaddingEnabled) then
   begin
     if (pad2) then

+ 0 - 35
src/libraries/simplebaselib4pascal/SimpleBaseLib.inc

@@ -58,9 +58,6 @@
 // It does not affect the binary size or generated code
 {$DEFINITIONINFO ON}
 
-// Disable Hints.
-{$HINTS OFF}
-
 // Disable Overflow and RangeChecks.
 {$OVERFLOWCHECKS OFF}
 {$RANGECHECKS OFF}
@@ -79,11 +76,6 @@
 {$DEFINE DELPHIXE3_UP}
 {$LEGACYIFEND ON}
 {$ZEROBASEDSTRINGS OFF}
-{$IFEND}
-
- // 2010 only
-{$IF CompilerVersion = 21.0}
-{$DEFINE DELPHI2010}
 {$IFEND}
 
   // 2010 and Above
@@ -94,27 +86,6 @@
   // XE and Above
 {$IF CompilerVersion >= 22.0}
 {$DEFINE DELPHIXE_UP}
-{$IFEND}
-
-  // XE2 and Above
-{$IF CompilerVersion >= 23.0}
-{$DEFINE DELPHIXE2_UP}
-{$DEFINE HAS_UNITSCOPE}
-{$IFEND}
-
-// XE3 and Below
-{$IF CompilerVersion <= 24.0}
-{$DEFINE DELPHIXE3_DOWN}
-{$IFEND}
-
-  // XE7 and Above
-{$IF CompilerVersion >= 28.0}
-{$DEFINE DELPHIXE7_UP}
-{$IFEND}
-
-  // 10.2 Tokyo and Above
-{$IF CompilerVersion >= 32.0}
-{$DEFINE DELPHI10.2_TOKYO_UP}
 {$IFEND}
 
   // 2010 and Above
@@ -122,12 +93,6 @@
 {$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
 {$ENDIF}
 
-  // 10.2 Tokyo and Above
-{$IFDEF DELPHI10.2_TOKYO_UP}
-{$WARN COMBINING_SIGNED_UNSIGNED OFF}
-{$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
-{$ENDIF}
-
 
 {$ENDIF DELPHI}