xtool/contrib/mORMot/SynCrypto.pas

15572 lines
532 KiB
ObjectPascal

/// fast cryptographic routines (hashing and cypher)
// - implements AES,XOR,ADLER32,MD5,RC4,SHA1,SHA256,SHA384,SHA512,SHA3 and JWT
// - optimized for speed (tuned assembler and SSE3/SSE4/AES-NI/PADLOCK support)
// - this unit is a part of the freeware Synopse mORMot framework,
// licensed under a MPL/GPL/LGPL tri-license; version 1.18
unit SynCrypto;
(*
This file is part of Synopse framework.
Synopse framework. Copyright (C) 2022 Arnaud Bouchez
Synopse Informatique - https://synopse.info
*** BEGIN LICENSE BLOCK *****
Version: MPL 1.1/GPL 2.0/LGPL 2.1
The contents of this file are subject to the Mozilla Public License Version
1.1 (the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.mozilla.org/MPL
Software distributed under the License is distributed on an "AS IS" basis,
WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
for the specific language governing rights and limitations under the License.
The Original Code is Synopse mORMot framework.
The Initial Developer of the Original Code is Arnaud Bouchez.
Portions created by the Initial Developer are Copyright (C) 2022
the Initial Developer. All Rights Reserved.
Contributor(s):
- Alfred Glaenzer (alf)
- Eric Grange for SHA-3 MMX asm optimization
- EvaF
- Intel's sha256_sse4.asm under under a three-clause Open Software license
- Johan Bontes
- souchaud
- Project Nayuki (MIT License) for SHA-512 optimized x86 asm
- Wolfgang Ehrhardt under zlib license for SHA-3 and AES "pure pascal" code
- Maxim Masiutin for the MD5 asm
Alternatively, the contents of this file may be used under the terms of
either the GNU General Public License Version 2 or later (the "GPL"), or
the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
in which case the provisions of the GPL or the LGPL are applicable instead
of those above. If you wish to allow use of your version of this file only
under the terms of either the GPL or the LGPL, and not to allow others to
use your version of this file under the terms of the MPL, indicate your
decision by deleting the provisions above and replace them with the notice
and other provisions required by the GPL or the LGPL. If you do not delete
the provisions above, a recipient may use your version of this file under
the terms of any one of the MPL, the GPL or the LGPL.
***** END LICENSE BLOCK *****
Synopse Cryptographic routines
==============================
- fastest ever 100% Delphi (and asm ;) code
- AES Crypto(128,192,256 bits key) with optimized asm version
and multi-threaded code for multi-core CPU for blocks > 512 KB
- XOR Crypto (32 bits key) - very fast with variable or fixed key
- RC4 Crypto - weak, but simple and standard (used e.g. by SynPdf)
- ADLER32 - 32 bits fast Hash with optimized asm version
- MD5 - standard fast 128 bits Hash
- SHA-1 - 160 bits Secure Hash
- SHA-256 - 256 bits Secure Hash with optimized asm version
- SHA-512 - 512 bits Secure Hash with optimized asm version (with SHA-384)
- SHA-3 - 224/256/384/512/Shake algorithms based on Keccak permutation
- hardware AES-NI and SHA-SSE4 support for latest CPU
- VIA PADLOCK optional support - native .o code on linux or .dll (Win32)
(tested on a Dedibox C7 (rev1) linux server - need validation for Win32)
- Microsoft AES Cryptographic Provider optional support via CryptoAPI
*)
interface
{$I Synopse.inc} // define HASINLINE CPU32 CPU64 OWNNORMTOUPPER
{.$define USEPADLOCK}
{.$define AESPASCAL} // for debug
{$ifdef Linux}
{$undef USETHREADSFORBIGAESBLOCKS} // uses low-level WinAPI threading
{$ifdef KYLIX3}
{.$define USEPADLOCK} // dedibox Linux tested only
{$endif}
{$else}
{$ifndef DELPHI5OROLDER}
// on Windows: enable Microsoft AES Cryptographic Provider (XP SP3 and up)
{$define USE_PROV_RSA_AES}
{$endif}
// on Windows: will use Threads for very big blocks (>512KB) if multi-CPU
{$define USETHREADSFORBIGAESBLOCKS}
{$endif}
{$ifdef USEPADLOCK}
{$ifdef MSWINDOWS}
{$define USEPADLOCKDLL} // Win32: we can use LibPadlock.dll
{$else}
{.$define PADLOCKDEBUG} // display message before using padlock
{.$define USEPADLOCKDLL} // Linux: use fast .o linked code
{$endif}
{$endif}
uses
{$ifdef MSWINDOWS}
Windows,
{$else}
{$ifdef KYLIX3}
LibC,
SynKylix,
{$endif}
{$ifdef FPC}
BaseUnix,
SynFPCLinux,
{$endif FPC}
{$endif MSWINDOWS}
SysUtils,
{$ifndef LVCL}
{$ifndef DELPHI5OROLDER}
RTLConsts,
{$endif}
{$endif LVCL}
Classes,
SynLZ, // already included in SynCommons, and used by CompressShaAes()
SynCommons,
SynTable; // for TSynUniqueIdentifierGenerator
{$ifdef ABSOLUTEPASCAL}
{$define AES_PASCAL}
{$define SHA3_PASCAL}
{$else}
{$ifdef DELPHI5OROLDER}
{$define AES_PASCAL} // Delphi 5 internal asm is buggy :(
{$define SHA3_PASCAL}
{$define SHA512_X86} // external sha512-x86.obj
{$else}
{$ifdef CPUINTEL} // AES-NI supported for x86 and x64 under Windows
{$ifdef CPU64}
{$ifdef HASAESNI}
{$define USEAESNI}
{$define USEAESNI64}
{$else}
{$define AES_PASCAL} // Delphi XE2/XE3 do not have the AES-NI opcodes :(
{$endif}
{$define AESPASCAL_OR_CPU64}
{$ifndef BSD}
{$define CRC32C_X64} // external crc32_iscsi_01 for win64/lin64
{$define SHA512_X64} // external sha512_sse4 for win64/lin64
{$endif}
{$else}
{$ifdef MSWINDOWS}
{$define SHA512_X86} // external sha512-x86.obj/.o
{$endif}
{$ifdef ABSOLUTEPASCAL}
{$define AES_PASCAL} // x86 AES asm below is not PIC-safe
{$else}
{$define CPUX86_NOTPIC}
{$endif ABSOLUTEPASCAL}
{$ifdef FPC}
{$ifdef DARWIN}
{$define AES_PASCAL} // as reported by alf
{$endif DARWIN}
{$ifdef LINUX}
{$ifndef AES_PASCAL}
{$define SHA512_X86} // external linux32/sha512-x86.o
{$endif AES_PASCAL}
{$endif}
{$endif FPC}
{$ifndef AES_PASCAL}
{$define USEAESNI} // some functions are not PIC-safe
{$define USEAESNI32}
{$endif AES_PASCAL}
{$endif}
{$else}
{$define AES_PASCAL}
{$define SHA3_PASCAL}
{$endif CPUINTEL}
{$endif}
{$endif}
{$ifdef AES_PASCAL}
{$define AESPASCAL_OR_CPU64}
{$endif}
{.$define AES_ROLLED}
// if defined, use rolled version, which is slightly slower (at least on my CPU)
{$ifndef AESPASCAL_OR_CPU64}
{$define AES_ROLLED} // asm requires rolled decryption keys
{$endif}
{$ifdef CPUX64}
{$define AES_ROLLED} // asm requires rolled decryption keys
{$endif}
{$ifdef USEPADLOCK}
var
/// if dll/so and VIA padlock compatible CPU are present
padlock_available: boolean = false;
{$endif}
const
/// hide all AES Context complex code
AESContextSize = 276+sizeof(pointer){$ifdef USEPADLOCK}*2{$endif}
{$ifdef USEAESNI32}+sizeof(pointer){$endif};
/// hide all SHA-1/SHA-2 complex code by storing the context as buffer
SHAContextSize = 108;
/// hide all SHA-3 complex code by storing the Keccak Sponge as buffer
SHA3ContextSize = 412;
/// power of two for a standard AES block size during cypher/uncypher
// - to be used as 1 shl AESBlockShift or 1 shr AESBlockShift for fast div/mod
AESBlockShift = 4;
/// bit mask for fast modulo of AES block size
AESBlockMod = 15;
/// maximum AES key size (in bytes)
AESKeySize = 256 div 8;
type
/// class of Exceptions raised by this unit
ESynCrypto = class(ESynException);
/// 128 bits memory block for AES data cypher/uncypher
TAESBlock = THash128;
/// points to a 128 bits memory block, as used for AES data cypher/uncypher
PAESBlock = ^TAESBlock;
/// 256 bits memory block for maximum AES key storage
TAESKey = THash256;
/// stores an array of THash128 to check for their unicity
// - used e.g. to implement TAESAbstract.IVHistoryDepth property, but may be
// also used to efficiently store a list of 128-bit IPv6 addresses
{$ifdef USERECORDWITHMETHODS}THash128History = record
{$else}THash128History = object{$endif}
private
Previous: array of THash128Rec;
Index: integer;
public
/// how many THash128 values can be stored
Depth: integer;
/// how many THash128 values are currently stored
Count: integer;
/// initialize the storage for a given history depth
// - if Count reaches Depth, then older items will be removed
procedure Init(size, maxsize: integer);
/// O(n) fast search of a hash value in the stored entries
// - returns true if the hash was found, or false if it did not appear
function Exists(const hash: THash128): boolean;
{$ifdef HASINLINE}inline;{$endif}
/// add a hash value to the stored entries, checking for duplicates
// - returns true if the hash was added, or false if it did already appear
function Add(const hash: THash128): boolean;
end;
PAES = ^TAES;
/// handle AES cypher/uncypher
// - this is the default Electronic codebook (ECB) mode
// - this class will use AES-NI hardware instructions, if available
{$ifdef USEPADLOCK}
// - this class will use VIA PadLock instructions, if available
{$endif}
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance (warning: not for Padlock)
{$ifdef USERECORDWITHMETHODS}TAES = record
{$else}TAES = object{$endif}
private
Context: packed array[1..AESContextSize] of byte;
{$ifdef USEPADLOCK}
function DoPadlockInit(const Key; KeySize: cardinal): boolean;
{$endif}
public
/// Initialize AES contexts for cypher
// - first method to call before using this object for encryption
// - KeySize is in bits, i.e. 128,192,256
function EncryptInit(const Key; KeySize: cardinal): boolean;
/// encrypt an AES data block into another data block
procedure Encrypt(const BI: TAESBlock; var BO: TAESBlock); overload;
/// encrypt an AES data block
procedure Encrypt(var B: TAESBlock); overload;
/// Initialize AES contexts for uncypher
// - first method to call before using this object for decryption
// - KeySize is in bits, i.e. 128,192,256
function DecryptInit(const Key; KeySize: cardinal): boolean;
/// Initialize AES contexts for uncypher, from another TAES.EncryptInit
function DecryptInitFrom(const Encryption{$ifndef DELPHI5OROLDER}: TAES{$endif};
const Key; KeySize: cardinal): boolean;
/// decrypt an AES data block
procedure Decrypt(var B: TAESBlock); overload;
/// decrypt an AES data block into another data block
procedure Decrypt(const BI: TAESBlock; var BO: TAESBlock); overload;
/// Finalize AES contexts for both cypher and uncypher
// - would fill the TAES instance with zeros, for safety
// - is only mandatoy when padlock is used
procedure Done;
/// generic initialization method for AES contexts
// - call either EncryptInit() either DecryptInit() method
function DoInit(const Key; KeySize: cardinal; doEncrypt: boolean): boolean;
/// perform the AES cypher or uncypher to continuous memory blocks
// - call either Encrypt() either Decrypt() method
procedure DoBlocks(pIn, pOut: PAESBlock; out oIn, oOut: PAESBLock; Count: integer; doEncrypt: boolean); overload;
/// perform the AES cypher or uncypher to continuous memory blocks
// - call either Encrypt() either Decrypt() method
procedure DoBlocks(pIn, pOut: PAESBlock; Count: integer; doEncrypt: boolean); overload;
{$ifdef USETHREADSFORBIGAESBLOCKS}
/// perform the AES cypher or uncypher to continuous memory blocks
// - this special method will use Threads for bigs blocks (>512KB) if multi-CPU
// - call either Encrypt() either Decrypt() method
procedure DoBlocksThread(var bIn, bOut: PAESBlock; Count: integer; doEncrypt: boolean);
{$endif}
/// performs AES-OFB encryption and decryption on whole blocks
// - may be called instead of TAESOFB when only a raw TAES is available
// - this method is thread-safe (except if padlock is used)
procedure DoBlocksOFB(const iv: TAESBlock; src, dst: pointer; blockcount: PtrUInt);
/// TRUE if the context was initialized via EncryptInit/DecryptInit
function Initialized: boolean; {$ifdef FPC}inline;{$endif}
/// return TRUE if the AES-NI instruction sets are available on this CPU
function UsesAESNI: boolean; {$ifdef HASINLINE}inline;{$endif}
/// returns the key size in bits (128/192/256)
function KeyBits: integer; {$ifdef FPC}inline;{$endif}
end;
type
/// low-level AES-GCM processing
// - implements standard AEAD (authenticated-encryption with associated-data)
// algorithm, as defined by NIST and
TAESGCMEngine = object
private
/// standard AES encryption context
// - will use AES-NI if available
actx: TAES;
/// ghash value of the Authentication Data
aad_ghv: TAESBlock;
/// ghash value of the Ciphertext
txt_ghv: TAESBlock;
/// ghash H current value
ghash_h: TAESBlock;
/// number of Authentication Data bytes processed
aad_cnt: TQWordRec;
/// number of bytes of the Ciphertext
atx_cnt: TQWordRec;
/// initial 32-bit ctr val - to be reused in Final()
y0_val: integer;
/// current 0..15 position in encryption block
blen: byte;
/// the state of this context
flags: set of (flagInitialized, flagFinalComputed, flagFlushed);
/// lookup table for fast Galois Finite Field multiplication
// - is defined as last field of the object for better code generation
gf_t4k: array[byte] of TAESBlock;
/// build the gf_t4k[] internal table - assuming set to zero by caller
procedure Make4K_Table;
/// compute a * ghash_h in Galois Finite Field 2^128
procedure gf_mul_h(var a: TAESBlock); {$ifdef FPC} inline; {$endif}
/// low-level AES-CTR encryption
procedure internal_crypt(ptp, ctp: PByte; ILen: PtrUInt);
/// low-level GCM authentication
procedure internal_auth(ctp: PByte; ILen: PtrUInt;
var ghv: TAESBlock; var gcnt: TQWordRec);
public
/// initialize the AES-GCM structure for the supplied Key
function Init(const Key; KeyBits: PtrInt): boolean;
/// start AES-GCM encryption with a given Initialization Vector
// - IV_len is in bytes use 12 for exact IV setting, otherwise the
// supplied buffer will be hashed using gf_mul_h()
function Reset(pIV: pointer; IV_len: PtrInt): boolean;
/// encrypt a buffer with AES-GCM, updating the associated authentication data
function Encrypt(ptp, ctp: Pointer; ILen: PtrInt): boolean;
/// decrypt a buffer with AES-GCM, updating the associated authentication data
// - also validate the GMAC with the supplied ptag/tlen if ptag<>nil,
// and skip the AES-CTR phase if the authentication doesn't match
function Decrypt(ctp, ptp: Pointer; ILen: PtrInt;
ptag: pointer=nil; tlen: PtrInt=0): boolean;
/// append some data to be authenticated, but not encrypted
function Add_AAD(pAAD: pointer; aLen: PtrInt): boolean;
/// finalize the AES-GCM encryption, returning the authentication tag
// - will also flush the AES context to avoid forensic issues, unless
// andDone is forced to false
function Final(out tag: TAESBlock; andDone: boolean=true): boolean;
/// flush the AES context to avoid forensic issues
// - do nothing if Final() has been already called
procedure Done;
/// single call AES-GCM encryption and authentication process
function FullEncryptAndAuthenticate(const Key; KeyBits: PtrInt;
pIV: pointer; IV_len: PtrInt; pAAD: pointer; aLen: PtrInt;
ptp, ctp: Pointer; pLen: PtrInt; out tag: TAESBlock): boolean;
/// single call AES-GCM decryption and verification process
function FullDecryptAndVerify(const Key; KeyBits: PtrInt;
pIV: pointer; IV_len: PtrInt; pAAD: pointer; aLen: PtrInt;
ctp, ptp: Pointer; pLen: PtrInt; ptag: pointer; tLen: PtrInt): boolean;
end;
/// class-reference type (metaclass) of an AES cypher/uncypher
TAESAbstractClass = class of TAESAbstract;
/// used internally by TAESAbstract to detect replay attacks
// - when EncryptPKCS7/DecryptPKCS7 are used with IVAtBeginning=true, and
// IVReplayAttackCheck property contains repCheckedIfAvailable or repMandatory
// - EncryptPKCS7 will encrypt this record (using the global shared
// AESIVCTR_KEY over AES-128) to create a random IV, as a secure
// cryptographic pseudorandom number generator (CSPRNG), nonce and ctr
// ensuring 96 bits of entropy
// - DecryptPKCS7 will decode and ensure that the IV has an increasing CTR
// - memory size matches an TAESBlock on purpose, for direct encryption
TAESIVCTR = packed record
/// 8 bytes of random value
nonce: QWord;
/// contains the crc32c hash of the block cipher mode (e.g. 'AESCFB')
// - when magic won't match (i.e. in case of mORMot revision < 3063), the
// check won't be applied in DecryptPKCS7: this security feature is
// backward compatible if IVReplayAttackCheck is repCheckedIfAvailable,
// but will fail for repMandatory
magic: cardinal;
/// an increasing counter, used to detect replay attacks
// - is set to a 32-bit random value at initialization
// - is increased by one for every EncryptPKCS7, so can be checked against
// replay attack in DecryptPKCS7, and implement a safe CSPRNG for stored IV
ctr: cardinal;
end;
/// how TAESAbstract.DecryptPKCS7 should detect replay attack
// - repNoCheck and repCheckedIfAvailable will be compatible with older
// versions of the protocol, but repMandatory will reject any encryption
// without the TAESIVCTR algorithm
TAESIVReplayAttackCheck = (repNoCheck, repCheckedIfAvailable, repMandatory);
/// handle AES cypher/uncypher with chaining
// - use any of the inherited implementation, corresponding to the chaining
// mode required - TAESECB, TAESCBC, TAESCFB, TAESOFB and TAESCTR classes to
// handle in ECB, CBC, CFB, OFB and CTR mode (including PKCS7-like padding)
TAESAbstract = class(TSynPersistent)
protected
fKeySize: cardinal;
fKeySizeBytes: cardinal;
fKey: TAESKey;
fIV: TAESBlock;
fIVCTR: TAESIVCTR;
fIVCTRState: (ctrUnknown, ctrUsed, ctrNotused);
fIVHistoryDec: THash128History;
fIVReplayAttackCheck: TAESIVReplayAttackCheck;
procedure SetIVHistory(aDepth: integer);
procedure SetIVCTR;
function DecryptPKCS7Len(var InputLen,ivsize: integer; Input: pointer;
IVAtBeginning, RaiseESynCryptoOnError: boolean): boolean;
public
/// Initialize AES context for cypher
// - first method to call before using this class
// - KeySize is in bits, i.e. 128,192,256
constructor Create(const aKey; aKeySize: cardinal); reintroduce; overload; virtual;
/// Initialize AES context for AES-128 cypher
// - first method to call before using this class
// - just a wrapper around Create(aKey,128);
constructor Create(const aKey: THash128); reintroduce; overload;
/// Initialize AES context for AES-256 cypher
// - first method to call before using this class
// - just a wrapper around Create(aKey,256);
constructor Create(const aKey: THash256); reintroduce; overload;
/// Initialize AES context for cypher, from some TAESPRNG random bytes
// - may be used to hide some sensitive information from memory, like
// CryptDataForCurrentUser but with a temporary key
constructor CreateTemp(aKeySize: cardinal);
/// Initialize AES context for cypher, from SHA-256 hash
// - here the Key is supplied as a string, and will be hashed using SHA-256
// via the SHA256Weak proprietary algorithm - to be used only for backward
// compatibility of existing code
// - consider using more secure (and more standard) CreateFromPBKDF2 instead
constructor CreateFromSha256(const aKey: RawUTF8);
/// Initialize AES context for cypher, from PBKDF2_HMAC_SHA256 derivation
// - here the Key is supplied as a string, and will be hashed using
// PBKDF2_HMAC_SHA256 with the specified salt and rounds
constructor CreateFromPBKDF2(const aKey: RawUTF8; const aSalt: RawByteString;
aRounds: Integer);
/// compute a class instance similar to this one
// - could be used to have a thread-safe re-use of a given encryption key
function Clone: TAESAbstract; virtual;
/// compute a class instance similar to this one, for performing the
// reverse encryption/decryption process
// - this default implementation calls Clone, but CFB/OFB/CTR chaining modes
// using only AES encryption (i.e. inheriting from TAESAbstractEncryptOnly)
// will return self to avoid creating two instances
// - warning: to be used only with IVAtBeginning=false
function CloneEncryptDecrypt: TAESAbstract; virtual;
/// release the used instance memory and resources
// - also fill the secret fKey buffer with zeros, for safety
destructor Destroy; override;
/// perform the AES cypher in the corresponding mode
// - when used in block chaining mode, you should have set the IV property
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); virtual; abstract;
/// perform the AES un-cypher in the corresponding mode
// - when used in block chaining mode, you should have set the IV property
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); virtual; abstract;
/// encrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will add up to 16 bytes to
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer - this IV may
// contain an internal encrypted CTR, to detect any replay attack attempt,
// if IVReplayAttackCheck is set to repCheckedIfAvailable or repMandatory
function EncryptPKCS7(const Input: RawByteString; IVAtBeginning: boolean=false): RawByteString; overload;
/// decrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will trim up to 16 bytes from
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - if IVAtBeginning is TRUE, the Initialization Vector will be taken
// from the beginning of the input binary buffer - if IVReplayAttackCheck is
// set, this IV will be validated to contain an increasing encrypted CTR,
// and raise an ESynCrypto when a replay attack attempt is detected
// - if RaiseESynCryptoOnError=false, returns '' on any decryption error
function DecryptPKCS7(const Input: RawByteString; IVAtBeginning: boolean=false;
RaiseESynCryptoOnError: boolean=true): RawByteString; overload;
/// encrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will add up to 16 bytes to
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer - this IV may
// contain an internal encrypted CTR, to detect any replay attack attempt,
// if IVReplayAttackCheck is set to repCheckedIfAvailable or repMandatory
function EncryptPKCS7(const Input: TBytes; IVAtBeginning: boolean=false): TBytes; overload;
/// decrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will trim up to 16 bytes from
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - if IVAtBeginning is TRUE, the Initialization Vector will be taken
// from the beginning of the input binary buffer - if IVReplayAttackCheck is
// set, this IV will be validated to contain an increasing encrypted CTR,
// and raise an ESynCrypto when a replay attack attempt is detected
// - if RaiseESynCryptoOnError=false, returns [] on any decryption error
function DecryptPKCS7(const Input: TBytes; IVAtBeginning: boolean=false;
RaiseESynCryptoOnError: boolean=true): TBytes; overload;
/// compute how many bytes would be needed in the output buffer, when
// encrypte using a PKCS7 padding pattern
// - could be used to pre-compute the OutputLength for EncryptPKCS7Buffer()
// - PKCS7 padding is described in RFC 5652 - it will add up to 16 bytes to
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
function EncryptPKCS7Length(InputLen: cardinal; IVAtBeginning: boolean): cardinal;
{$ifdef HASINLINE}inline;{$endif}
/// encrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will add up to 16 bytes to
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - use EncryptPKCS7Length() function to compute the actual needed length
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer - this IV will in
// fact contain an internal encrypted CTR, to detect any replay attack attempt
// - returns TRUE on success, FALSE if OutputLen is not correct - you should
// use EncryptPKCS7Length() to compute the exact needed number of bytes
function EncryptPKCS7Buffer(Input,Output: Pointer; InputLen,OutputLen: cardinal;
IVAtBeginning: boolean): boolean;
/// decrypt a memory buffer using a PKCS7 padding pattern
// - PKCS7 padding is described in RFC 5652 - it will trim up to 16 bytes from
// the input buffer; note this method uses the padding only, not the whole
// PKCS#7 Cryptographic Message Syntax
// - if IVAtBeginning is TRUE, the Initialization Vector will be taken
// from the beginning of the input binary buffer - this IV will in fact
// contain an internal encrypted CTR, to detect any replay attack attempt
// - if RaiseESynCryptoOnError=false, returns '' on any decryption error
function DecryptPKCS7Buffer(Input: Pointer; InputLen: integer;
IVAtBeginning: boolean; RaiseESynCryptoOnError: boolean=true): RawByteString;
/// initialize AEAD (authenticated-encryption with associated-data) nonce
// - i.e. setup 256-bit MAC computation during next Encrypt/Decrypt call
// - may be used e.g. for AES-GCM or our custom AES-CTR modes
// - default implementation, for a non AEAD protocol, returns false
function MACSetNonce(const aKey: THash256; aAssociated: pointer=nil;
aAssociatedLen: integer=0): boolean; virtual;
/// returns AEAD (authenticated-encryption with associated-data) MAC
/// - i.e. optional 256-bit MAC computation during last Encrypt/Decrypt call
// - may be used e.g. for AES-GCM or our custom AES-CTR modes
// - default implementation, for a non AEAD protocol, returns false
function MACGetLast(out aCRC: THash256): boolean; virtual;
/// validate if the computed AEAD MAC matches the expected supplied value
// - is just a wrapper around MACGetLast() and IsEqual() functions
function MACEquals(const aCRC: THash256): boolean; virtual;
/// validate if an encrypted buffer matches the stored AEAD MAC
// - expects the 256-bit MAC, as returned by MACGetLast, to be stored after
// the encrypted data
// - default implementation, for a non AEAD protocol, returns false
function MACCheckError(aEncrypted: pointer; Count: cardinal): boolean; virtual;
/// perform one step PKCS7 encryption/decryption and authentication from
// a given 256-bit key
// - returns '' on any (MAC) issue during decryption (Encrypt=false) or if
// this class does not support AEAD MAC
// - as used e.g. by CryptDataForCurrentUser()
// - do not use this abstract class method, but inherited TAESCFBCRC/TAESOFBCRC
// - will store a header with its own CRC, so detection of most invalid
// formats (e.g. from fuzzing input) will occur before any AES/MAC process
class function MACEncrypt(const Data: RawByteString; const Key: THash256;
Encrypt: boolean): RawByteString; overload;
/// perform one step PKCS7 encryption/decryption and authentication from
// a given 128-bit key
// - returns '' on any (MAC) issue during decryption (Encrypt=false) or if
// this class does not support AEAD MAC
// - do not use this abstract class method, but inherited TAESCFBCRC/TAESOFBCRC
// - will store a header with its own CRC, so detection of most invalid
// formats (e.g. from fuzzing input) will occur before any AES/MAC process
class function MACEncrypt(const Data: RawByteString; const Key: THash128;
Encrypt: boolean): RawByteString; overload;
/// perform one step PKCS7 encryption/decryption and authentication with
// the curent AES instance
// - returns '' on any (MAC) issue during decryption (Encrypt=false) or if
// this class does not support AEAD MAC
// - as used e.g. by CryptDataForCurrentUser()
// - do not use this abstract class method, but inherited TAESCFBCRC/TAESOFBCRC
// - will store a header with its own CRC, so detection of most invalid
// formats (e.g. from fuzzing input) will occur before any AES/MAC process
function MACAndCrypt(const Data: RawByteString; Encrypt: boolean): RawByteString;
/// simple wrapper able to cypher/decypher any in-memory content
// - here data variables could be text or binary
// - use StringToUTF8() to define the Key parameter from a VCL string
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer
// - will use SHA256Weak() and PKCS7 padding with the current class mode
class function SimpleEncrypt(const Input,Key: RawByteString; Encrypt: boolean;
IVAtBeginning: boolean=false; RaiseESynCryptoOnError: boolean=true): RawByteString; overload;
/// simple wrapper able to cypher/decypher any in-memory content
// - here data variables could be text or binary
// - you could use e.g. THMAC_SHA256 to safely compute the Key/KeySize value
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer
// - will use SHA256Weak() and PKCS7 padding with the current class mode
class function SimpleEncrypt(const Input: RawByteString; const Key;
KeySize: integer; Encrypt: boolean; IVAtBeginning: boolean=false;
RaiseESynCryptoOnError: boolean=true): RawByteString; overload;
/// simple wrapper able to cypher/decypher any file content
// - just a wrapper around SimpleEncrypt() and StringFromFile/FileFromString
// - use StringToUTF8() to define the Key parameter from a VCL string
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer
// - will use SHA256Weak() and PKCS7 padding with the current class mode
class function SimpleEncryptFile(const InputFile, OutputFile: TFileName;
const Key: RawByteString; Encrypt: boolean; IVAtBeginning: boolean=false;
RaiseESynCryptoOnError: boolean=true): boolean; overload;
/// simple wrapper able to cypher/decypher any file content
// - just a wrapper around SimpleEncrypt() and StringFromFile/FileFromString
// - you could use e.g. THMAC_SHA256 to safely compute the Key/KeySize value
// - if IVAtBeginning is TRUE, a random Initialization Vector will be computed,
// and stored at the beginning of the output binary buffer
// - will use SHA256Weak() and PKCS7 padding with the current class mode
class function SimpleEncryptFile(const InputFile, Outputfile: TFileName; const Key;
KeySize: integer; Encrypt: boolean; IVAtBeginning: boolean=false;
RaiseESynCryptoOnError: boolean=true): boolean; overload;
//// returns e.g. 'aes128cfb' or '' if nil
function AlgoName: TShort16;
/// associated Key Size, in bits (i.e. 128,192,256)
property KeySize: cardinal read fKeySize;
/// associated Initialization Vector
// - all modes (except ECB) do expect an IV to be supplied for chaining,
// before any encryption or decryption is performed
// - you could also use PKCS7 encoding with IVAtBeginning=true option
property IV: TAESBlock read fIV write fIV;
/// let IV detect replay attack for EncryptPKCS7 and DecryptPKCS7
// - if IVAtBeginning=true and this property is set, EncryptPKCS7 will
// store a random IV from an internal CTR, and DecryptPKCS7 will check this
// incoming IV CTR consistency, and raise an ESynCrypto exception on failure
// - leave it to its default repNoCheck if the very same TAESAbstract
// instance is expected to be used with several sources, by which the IV CTR
// will be unsynchronized
// - security warning: by design, this is NOT cautious with CBC chaining:
// you should use it only with CFB, OFB or CTR mode, since the IV sequence
// will be predictable if you know the fixed AES private key of this unit,
// but the IV sequence features uniqueness as it is generated by a good PRNG -
// see http://crypto.stackexchange.com/q/3515
property IVReplayAttackCheck: TAESIVReplayAttackCheck
read fIVReplayAttackCheck write fIVReplayAttackCheck;
/// maintains an history of previous IV, to avoid re-play attacks
// - only useful when EncryptPKCS7/DecryptPKCS7 are used with
// IVAtBeginning=true, and IVReplayAttackCheck is left to repNoCheck
property IVHistoryDepth: integer read fIVHistoryDec.Depth write SetIVHistory;
end;
/// handle AES cypher/uncypher with chaining with out own optimized code
// - use any of the inherited implementation, corresponding to the chaining
// mode required - TAESECB, TAESCBC, TAESCFB, TAESOFB and TAESCTR classes to
// handle in ECB, CBC, CFB, OFB and CTR mode (including PKCS7-like padding)
// - this class will use AES-NI hardware instructions, if available
// - those classes are re-entrant, i.e. that you can call the Encrypt*
// or Decrypt* methods on the same instance several times
TAESAbstractSyn = class(TAESAbstract)
protected
fIn, fOut: PAESBlock;
fCV: TAESBlock;
AES: TAES;
fAESInit: (initNone, initEncrypt, initDecrypt);
procedure EncryptInit;
procedure DecryptInit;
procedure TrailerBytes(count: cardinal);
public
/// creates a new instance with the very same values
// - by design, our classes will use TAES stateless context, so this method
// will just copy the current fields to a new instance, by-passing
// the key creation step
function Clone: TAESAbstract; override;
/// release the used instance memory and resources
// - also fill the TAES instance with zeros, for safety
destructor Destroy; override;
/// perform the AES cypher in the corresponding mode, over Count bytes
// - this abstract method will set CV from fIV property, and fIn/fOut
// from BufIn/BufOut
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the corresponding mode
// - this abstract method will set CV from fIV property, and fIn/fOut
// from BufIn/BufOut
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// read-only access to the internal CV block, which may be have just been
// used by Encrypt/Decrypt methods
property CV: TAESBlock read fCV;
end;
/// handle AES cypher/uncypher without chaining (ECB)
// - this mode is known to be less secure than the others
// - IV property should be set to a fixed value to encode the trailing bytes
// of the buffer by a simple XOR - but you should better use the PKC7 pattern
// - this class will use AES-NI hardware instructions, if available, e.g.
// ! ECB128: 19.70ms in x86 optimized code, 6.97ms with AES-NI
TAESECB = class(TAESAbstractSyn)
public
/// perform the AES cypher in the ECB mode
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the ECB mode
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES cypher/uncypher with Cipher-block chaining (CBC)
// - this class will use AES-NI hardware instructions, if available, e.g.
// ! CBC192: 24.91ms in x86 optimized code, 9.75ms with AES-NI
// - expect IV to be set before process, or IVAtBeginning=true
TAESCBC = class(TAESAbstractSyn)
public
/// perform the AES cypher in the CBC mode
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the CBC mode
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// abstract parent class for chaining modes using only AES encryption
TAESAbstractEncryptOnly = class(TAESAbstractSyn)
public
/// Initialize AES context for cypher
// - will pre-generate the encryption key (aKeySize in bits, i.e. 128,192,256)
constructor Create(const aKey; aKeySize: cardinal); override;
/// compute a class instance similar to this one, for performing the
// reverse encryption/decryption process
// - will return self to avoid creating two instances
// - warning: to be used only with IVAtBeginning=false
function CloneEncryptDecrypt: TAESAbstract; override;
end;
/// handle AES cypher/uncypher with Cipher feedback (CFB)
// - this class will use AES-NI hardware instructions, if available, e.g.
// ! CFB128: 22.25ms in x86 optimized code, 9.29ms with AES-NI
// - expect IV to be set before process, or IVAtBeginning=true
TAESCFB = class(TAESAbstractEncryptOnly)
public
/// perform the AES cypher in the CFB mode
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the CFB mode
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES cypher/uncypher with Output feedback (OFB)
// - this class will use AES-NI hardware instructions, if available, e.g.
// ! OFB256: 27.69ms in x86 optimized code, 9.94ms with AES-NI
// - expect IV to be set before process, or IVAtBeginning=true
// - TAESOFB 128/256 have an optimized asm version under x86_64 + AES_NI
TAESOFB = class(TAESAbstractEncryptOnly)
public
/// perform the AES cypher in the OFB mode
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the OFB mode
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES cypher/uncypher with 64-bit Counter mode (CTR)
// - the CTR will use a counter in bytes 7..0 by default - which is safe
// but not standard - call ComposeIV() to change e.g. to NIST behavior
// - this class will use AES-NI hardware instructions, e.g.
// ! CTR256: 28.13ms in x86 optimized code, 10.63ms with AES-NI
// - expect IV to be set before process, or IVAtBeginning=true
TAESCTR = class(TAESAbstractEncryptOnly)
protected
fCTROffset, fCTROffsetMin: PtrInt;
public
/// Initialize AES context for cypher
// - will pre-generate the encryption key (aKeySize in bits, i.e. 128,192,256)
constructor Create(const aKey; aKeySize: cardinal); override;
/// defines how the IV is set and updated in CTR mode
// - default (if you don't call this method) uses a Counter in bytes 7..0
// - you can specify startup Nonce and Counter, and the Counter position
// - NonceLen + CounterLen should be 16 - otherwise it fails and returns false
function ComposeIV(Nonce, Counter: PAESBlock; NonceLen, CounterLen: integer;
LSBCounter: boolean): boolean; overload;
/// defines how the IV is set and updated in CTR mode
// - you can specify startup Nonce and Counter, and the Counter position
// - Nonce + Counter lengths should add to 16 - otherwise returns false
function ComposeIV(const Nonce, Counter: TByteDynArray; LSBCounter: boolean): boolean; overload;
/// perform the AES cypher in the CTR mode
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the CTR mode
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// internal 256-bit structure used for TAESAbstractAEAD MAC storage
TAESMAC256 = record
/// the AES-encrypted MAC of the plain content
// - plain text digital signature, to perform message authentication
// and integrity
plain: THash128;
/// the plain MAC of the encrypted content
// - encrypted text digital signature, to check for errors,
// with no compromission of the plain content
encrypted: THash128;
end;
/// AEAD (authenticated-encryption with associated-data) abstract class
// - perform AES encryption and on-the-fly MAC computation, i.e. computes
// a proprietary 256-bit MAC during AES cyphering, as 128-bit CRC of the
// encrypted data and 128-bit CRC of the plain data, seeded from a Key
// - the 128-bit CRC of the plain text is then encrypted using the current AES
// engine, so returned 256-bit MAC value has cryptographic level, and ensure
// data integrity, authenticity, and check against transmission errors
TAESAbstractAEAD = class(TAESAbstractEncryptOnly)
protected
fMAC, fMACKey: TAESMAC256;
public
/// release the used instance memory and resources
// - also fill the internal internal MAC hashes with zeros, for safety
destructor Destroy; override;
/// initialize 256-bit MAC computation for next Encrypt/Decrypt call
// - initialize the internal fMACKey property, and returns true
// - only the plain text crc is seeded from aKey - encrypted message crc
// will use -1 as fixed seed, to avoid aKey compromission
// - should be set with a new MAC key value before each message, to avoid
// replay attacks (as called from TECDHEProtocol.SetKey)
function MACSetNonce(const aKey: THash256; aAssociated: pointer=nil;
aAssociatedLen: integer=0): boolean; override;
/// returns 256-bit MAC computed during last Encrypt/Decrypt call
// - encrypt the internal fMAC property value using the current AES cypher
// on the plain content and returns true; only the plain content CRC-128 is
// AES encrypted, to avoid reverse attacks against the known encrypted data
function MACGetLast(out aCRC: THash256): boolean; override;
/// validate if an encrypted buffer matches the stored MAC
// - expects the 256-bit MAC, as returned by MACGetLast, to be stored after
// the encrypted data
// - returns true if the 128-bit CRC of the encrypted text matches the
// supplied buffer, ignoring the 128-bit CRC of the plain data
// - since it is easy to forge such 128-bit CRC, it will only indicate
// that no transmission error occured, but won't be an integrity or
// authentication proof (which will need full Decrypt + MACGetLast)
// - may use any MACSetNonce() aAssociated value
function MACCheckError(aEncrypted: pointer; Count: cardinal): boolean; override;
end;
/// AEAD combination of AES with Cipher feedback (CFB) and 256-bit MAC
// - this class will use AES-NI and CRC32C hardware instructions, if available
// - expect IV to be set before process, or IVAtBeginning=true
TAESCFBCRC = class(TAESAbstractAEAD)
public
/// perform the AES cypher in the CFB mode, and compute a 256-bit MAC
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the CFB mode, and compute 256-bit MAC
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// AEAD combination of AES with Output feedback (OFB) and 256-bit MAC
// - this class will use AES-NI and CRC32C hardware instructions, if available
// - expect IV to be set before process, or IVAtBeginning=true
TAESOFBCRC = class(TAESAbstractAEAD)
public
/// perform the AES cypher in the OFB mode, and compute a 256-bit MAC
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the OFB mode, and compute a 256-bit MAC
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES-GCM cypher/uncypher with built-in authentication
// - implements AEAD (authenticated-encryption with associated-data) methods
// like MACEncrypt/MACCheckError
// - this class will use AES-NI hardware instructions, if available
TAESGCM = class(TAESAbstract)
protected
fAES: TAESGCMEngine;
fContext: (ctxNone,ctxEncrypt,ctxDecrypt); // used to call AES.Reset()
public
/// Initialize the AES-GCM context for cypher
// - first method to call before using this class
// - KeySize is in bits, i.e. 128,192,256
constructor Create(const aKey; aKeySize: cardinal); override;
/// creates a new instance with the very same values
// - by design, our classes will use TAESGCMEngine stateless context, so
// this method will just copy the current fields to a new instance,
// by-passing the key creation step
function Clone: TAESAbstract; override;
/// release the used instance memory and resources
// - also fill the internal TAES instance with zeros, for safety
destructor Destroy; override;
/// perform the AES-GCM cypher and authentication
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher and authentication
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// prepare the AES-GCM process before Encrypt/Decrypt is called
// - aKey is not used: AES-GCM has its own nonce setting algorithm, and
// the IV will be set from random value by EncryptPKCS7()
// - will just include any supplied associated data to the GMAC tag
function MACSetNonce(const aKey: THash256; aAssociated: pointer=nil;
aAssociatedLen: integer=0): boolean; override;
/// returns AEAD (authenticated-encryption with associated-data) MAC
/// - only the lower 128-bit (THash256.Lo) of aCRC is filled with the GMAC
function MACGetLast(out aCRC: THash256): boolean; override;
/// validate if an encrypted buffer matches the stored AEAD MAC
// - since AES-GCM is a one pass process, always assume the content is fine
// and returns true - we don't know the IV at this time
function MACCheckError(aEncrypted: pointer; Count: cardinal): boolean; override;
end;
{$ifdef USE_PROV_RSA_AES}
type
/// handle AES cypher/uncypher using Windows CryptoAPI and the
// official Microsoft AES Cryptographic Provider (PROV_RSA_AES)
// - see @http://msdn.microsoft.com/en-us/library/windows/desktop/aa386979
// - timing of our optimized asm versions, for small (<=8KB) block processing
// (similar to standard web pages or most typical JSON/XML content),
// benchmarked on a Core i7 notebook and compiled as Win32 platform:
// ! AES128 - ECB:79.33ms CBC:83.37ms CFB:80.75ms OFB:78.98ms CTR:80.45ms
// ! AES192 - ECB:91.16ms CBC:96.06ms CFB:96.45ms OFB:92.12ms CTR:93.38ms
// ! AES256 - ECB:103.22ms CBC:119.14ms CFB:111.59ms OFB:107.00ms CTR:110.13ms
// - timing of the same process, using CryptoAPI official PROV_RSA_AES provider:
// ! AES128 - ECB_API:102.88ms CBC_API:124.91ms
// ! AES192 - ECB_API:115.75ms CBC_API:129.95ms
// ! AES256 - ECB_API:139.50ms CBC_API:154.02ms
// - but the CryptoAPI does not supports AES-NI, whereas our classes handle it,
// with a huge speed benefit
// - under Win64, the official CryptoAPI is faster than our PUREPASCAL version,
// and the Win32 version of CryptoAPI itself, but slower than our AES-NI code
// ! AES128 - ECB:107.95ms CBC:112.65ms CFB:109.62ms OFB:107.23ms CTR:109.42ms
// ! AES192 - ECB:130.30ms CBC:133.04ms CFB:128.78ms OFB:127.25ms CTR:130.22ms
// ! AES256 - ECB:145.33ms CBC:147.01ms CFB:148.36ms OFB:145.96ms CTR:149.67ms
// ! AES128 - ECB_API:89.64ms CBC_API:100.84ms
// ! AES192 - ECB_API:99.05ms CBC_API:105.85ms
// ! AES256 - ECB_API:107.11ms CBC_API:118.04ms
// - in practice, you could forget about using the CryptoAPI, unless you are
// required to do so, for legal/corporate reasons
TAESAbstract_API = class(TAESAbstract)
protected
fKeyHeader: packed record
bType: byte;
bVersion: byte;
reserved: word;
aiKeyAlg: cardinal;
dwKeyLength: cardinal;
end;
fKeyHeaderKey: TAESKey; // should be just after fKeyHeader record
fKeyCryptoAPI: pointer;
fInternalMode: cardinal;
procedure InternalSetMode; virtual; abstract;
procedure EncryptDecrypt(BufIn, BufOut: pointer; Count: cardinal; DoEncrypt: boolean);
public
/// Initialize AES context for cypher
// - first method to call before using this class
// - KeySize is in bits, i.e. 128,192,256
constructor Create(const aKey; aKeySize: cardinal); override;
/// release the AES execution context
destructor Destroy; override;
/// perform the AES cypher in the ECB mode
// - if Count is not a multiple of a 16 bytes block, the IV will be used
// to XOR the trailing bytes - so it won't be compatible with our
// TAESAbstractSyn classes: you should better use PKC7 padding instead
procedure Encrypt(BufIn, BufOut: pointer; Count: cardinal); override;
/// perform the AES un-cypher in the ECB mode
// - if Count is not a multiple of a 16 bytes block, the IV will be used
// to XOR the trailing bytes - so it won't be compatible with our
// TAESAbstractSyn classes: you should better use PKC7 padding instead
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES cypher/uncypher without chaining (ECB) using Windows CryptoAPI
TAESECB_API = class(TAESAbstract_API)
protected
/// will set fInternalMode := CRYPT_MODE_ECB
procedure InternalSetMode; override;
end;
/// handle AES cypher/uncypher Cipher-block chaining (CBC) using Windows CryptoAPI
TAESCBC_API = class(TAESAbstract_API)
protected
/// will set fInternalMode := CRYPT_MODE_CBC
procedure InternalSetMode; override;
end;
/// handle AES cypher/uncypher Cipher feedback (CFB) using Windows CryptoAPI
// - NOT TO BE USED: the current PROV_RSA_AES provider does not return
// expected values for CFB
TAESCFB_API = class(TAESAbstract_API)
protected
/// will set fInternalMode := CRYPT_MODE_CFB
procedure InternalSetMode; override;
end;
/// handle AES cypher/uncypher Output feedback (OFB) using Windows CryptoAPI
// - NOT TO BE USED: the current PROV_RSA_AES provider does not implement
// this mode, and returns a NTE_BAD_ALGID error
TAESOFB_API = class(TAESAbstract_API)
protected
/// will set fInternalMode := CRYPT_MODE_OFB
procedure InternalSetMode; override;
end;
{$endif USE_PROV_RSA_AES}
var
/// 128-bit random AES-128 entropy key for TAESAbstract.IVReplayAttackCheck
// - as used internally by AESIVCtrEncryptDecrypt() function
// - you may customize this secret for your own project, but be aware that
// it will affect all TAESAbstract instances, so should match on all ends
AESIVCTR_KEY: TBlock128 = (
$ce5d5e3e, $26506c65, $568e0092, $12cce480);
/// global shared function which may encrypt or decrypt any 128-bit block
// using AES-128 and the global AESIVCTR_KEY
procedure AESIVCtrEncryptDecrypt(const BI; var BO; DoEncrypt: boolean);
type
/// thread-safe class containing a TAES encryption/decryption engine
TAESLocked = class(TSynPersistentLock)
protected
fAES: TAES;
public
/// finalize all used memory and resources
destructor Destroy; override;
end;
/// cryptographic pseudorandom number generator (CSPRNG) based on AES-256
// - use as a shared instance via TAESPRNG.Fill() overloaded class methods
// - this class is able to generate some random output by encrypting successive
// values of a counter with AES-256 and a secret key
// - this internal secret key is generated from PBKDF2 derivation of OS-supplied
// entropy using HMAC over SHA-512
// - by design, such a PRNG is as good as the cypher used - for reference, see
// https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator
// - it would use fast hardware AES-NI or Padlock opcodes, if available
TAESPRNG = class(TAESLocked)
protected
fCTR: THash128Rec; // we use a litle-endian CTR
fBytesSinceSeed: integer;
fSeedAfterBytes: integer;
fAESKeySize: integer;
fSeedPBKDF2Rounds: cardinal;
fTotalBytes: QWord;
procedure IncrementCTR; {$ifdef HASINLINE}inline;{$endif}
public
/// initialize the internal secret key, using Operating System entropy
// - entropy is gathered from the OS, using GetEntropy() method
// - you can specify how many PBKDF2_HMAC_SHA512 rounds are applied to the
// OS-gathered entropy - the higher, the better, but also the slower
// - internal private key would be re-seeded after ReseedAfterBytes
// bytes (1MB by default) are generated, using GetEntropy()
// - by default, AES-256 will be used, unless AESKeySize is set to 128,
// which may be slightly faster (especially if AES-NI is not available)
constructor Create(PBKDF2Rounds: integer = 16;
ReseedAfterBytes: integer = 1024*1024; AESKeySize: integer = 256); reintroduce; virtual;
/// fill a TAESBlock with some pseudorandom data
// - could be used e.g. to compute an AES Initialization Vector (IV)
// - this method is thread-safe
procedure FillRandom(out Block: TAESBlock); overload; virtual;
/// fill a 256-bit buffer with some pseudorandom data
// - this method is thread-safe
procedure FillRandom(out Buffer: THash256); overload;
/// fill a binary buffer with some pseudorandom data
// - this method is thread-safe
procedure FillRandom(Buffer: pointer; Len: integer); overload; virtual;
/// returns a binary buffer filled with some pseudorandom data
// - this method is thread-safe
function FillRandom(Len: integer): RawByteString; overload;
/// returns a binary buffer filled with some pseudorandom data
// - this method is thread-safe
function FillRandomBytes(Len: integer): TBytes;
/// returns an hexa-encoded binary buffer filled with some pseudorandom data
// - this method is thread-safe
function FillRandomHex(Len: integer): RawUTF8;
/// returns a 32-bit unsigned random number
function Random32: cardinal; overload;
/// returns a 32-bit unsigned random number, with a maximum value
function Random32(max: cardinal): cardinal; overload;
/// returns a 64-bit unsigned random number
function Random64: QWord;
/// returns a floating-point random number in range [0..1]
function RandomExt: TSynExtended;
/// returns a 64-bit floating-point random number in range [0..1]
function RandomDouble: double;
/// computes a random ASCII password
// - will contain uppercase/lower letters, digits and $.:()?%!-+*/@#
// excluding ;,= to allow direct use in CSV content
function RandomPassword(Len: integer): RawUTF8;
/// would force the internal generator to re-seed its private key
// - avoid potential attacks on backward or forward security
// - would be called by FillRandom() methods, according to SeedAfterBytes
// - this method is thread-safe
procedure Seed; virtual;
/// retrieve some entropy bytes from the Operating System
// - entropy comes from CryptGenRandom API on Windows, and /dev/urandom or
// /dev/random on Linux/POSIX
// - this system-supplied entropy is then XORed with the output of a SHA-3
// cryptographic SHAKE-256 generator in XOF mode, of several entropy sources
// (timestamp, thread and system information, SynCommons.Random32 function)
// unless SystemOnly is TRUE
// - depending on the system, entropy may not be true randomness: if you need
// some truly random values, use TAESPRNG.Main.FillRandom() or TAESPRNG.Fill()
// methods, NOT this class function (which will be much slower, BTW)
class function GetEntropy(Len: integer; SystemOnly: boolean=false): RawByteString; virtual;
/// returns a shared instance of a TAESPRNG instance
// - if you need to generate some random content, just call the
// TAESPRNG.Main.FillRandom() overloaded methods, or directly TAESPRNG.Fill()
class function Main: TAESPRNG;
{$ifdef HASINLINE}inline;{$endif}
/// just a wrapper around TAESPRNG.Main.FillRandom() function
// - this method is thread-safe, but you may use your own TAESPRNG instance
// if you need some custom entropy level
class procedure Fill(Buffer: pointer; Len: integer); overload;
{$ifdef HASINLINE}inline;{$endif}
/// just a wrapper around TAESPRNG.Main.FillRandom() function
// - this method is thread-safe, but you may use your own TAESPRNG instance
// if you need some custom entropy level
class procedure Fill(out Block: TAESBlock); overload;
/// just a wrapper around TAESPRNG.Main.FillRandom() function
// - this method is thread-safe, but you may use your own TAESPRNG instance
// if you need some custom entropy level
class procedure Fill(out Block: THash256); overload;
{$ifdef HASINLINE}inline;{$endif}
/// just a wrapper around TAESPRNG.Main.FillRandom() function
// - this method is thread-safe, but you may use your own TAESPRNG instance
// if you need some custom entropy level
class function Fill(Len: integer): RawByteString; overload;
{$ifdef HASINLINE}inline;{$endif}
/// just a wrapper around TAESPRNG.Main.FillRandomBytes() function
// - this method is thread-safe, but you may use your own TAESPRNG instance
// if you need some custom entropy level
class function Bytes(Len: integer): TBytes;
{$ifdef HASINLINE}inline;{$endif}
/// create an anti-forensic representation of a key for safe storage
// - a binary buffer will be split into StripesCount items, ready to be
// saved on disk; returned length is BufferBytes*(StripesCount+1) bytes
// - AFSplit supports secure data destruction crucial for secure on-disk
// key management. The key idea is to bloat information and therefore
// improve the chance of destroying a single bit of it. The information
// is bloated in such a way, that a single missing bit causes the original
// information become unrecoverable.
// - this implementation uses SHA-256 as diffusion element, and the current
// TAESPRNG instance to gather randomness
// - for reference, see TKS1 as used for LUKS and defined in
// @https://gitlab.com/cryptsetup/cryptsetup/wikis/TKS1-draft.pdf
function AFSplit(const Buffer; BufferBytes, StripesCount: integer): RawByteString; overload;
/// create an anti-forensic representation of a key for safe storage
// - a binary buffer will be split into StripesCount items, ready to be
// saved on disk; returned length is BufferBytes*(StripesCount+1) bytes
// - jsut a wrapper around the other overloaded AFSplit() funtion
function AFSplit(const Buffer: RawByteString; StripesCount: integer): RawByteString; overload;
/// retrieve a key from its anti-forensic representation
// - is the reverse function of AFSplit() method
// - returns TRUE if the input buffer matches BufferBytes value
class function AFUnsplit(const Split: RawByteString;
out Buffer; BufferBytes: integer): boolean; overload;
/// retrieve a key from its anti-forensic representation
// - is the reverse function of AFSplit() method
// - returns the un-splitted binary content
// - returns '' if StripesCount is incorrect
class function AFUnsplit(const Split: RawByteString;
StripesCount: integer): RawByteString; overload;
/// after how many generated bytes Seed method would be called
// - default is 1 MB
property SeedAfterBytes: integer read fSeedAfterBytes;
/// how many PBKDF2_HMAC_SHA512 count is applied by Seed to the entropy
// - default is 16 rounds, which is more than enough for entropy gathering,
// since GetEntropy output comes from a SHAKE-256 generator in XOF mode
property SeedPBKDF2Rounds: cardinal read fSeedPBKDF2Rounds;
/// how many bits (128 or 256 - which is the default) are used for the AES
property AESKeySize: integer read fAESKeySize;
/// how many bytes this generator did compute
property TotalBytes: QWord read fTotalBytes;
end;
/// TAESPRNG-compatible class using Operating System pseudorandom source
// - may be used instead of TAESPRNG if a "standard" generator is required -
// you could override MainAESPRNG global variable
// - will call /dev/urandom under POSIX, and CryptGenRandom API on Windows
// - warning: may block on some BSD flavors, depending on /dev/urandom
// - from the cryptographic point of view, our TAESPRNG class doesn't suffer
// from the "black-box" approach of Windows, give consistent randomness
// over all supported cross-platform, and is indubitably faster
TAESPRNGSystem = class(TAESPRNG)
public
/// initialize the Operating System PRNG
constructor Create; reintroduce; virtual;
/// fill a TAESBlock with some pseudorandom data
// - this method is thread-safe
procedure FillRandom(out Block: TAESBlock); override;
/// fill a binary buffer with some pseudorandom data
// - this method is thread-safe
procedure FillRandom(Buffer: pointer; Len: integer); override;
/// called to force the internal generator to re-seed its private key
// - won't do anything for the Operating System pseudorandom source
procedure Seed; override;
end;
var
/// the shared TAESPRNG instance returned by TAESPRNG.Main class function
// - you may override this to a customized instance, e.g. if you expect
// a specific random generator to be used, like TAESPRNGSystem
// - all TAESPRNG.Fill() class functions will use this instance
MainAESPRNG: TAESPRNG;
{$ifdef HASINLINE}
/// defined globally to initialize MainAESPRNG for inlining TAESPRNG.Main
procedure SetMainAESPRNG;
{$endif}
/// low-level function returning some random binary using standard API
// - will call /dev/urandom or /dev/random under POSIX, and CryptGenRandom API
// on Windows, and fallback to SynCommons.FillRandom if the system API failed
// or for padding if more than 32 bytes is retrieved from /dev/urandom
// - you should not have to call this procedure, but faster and safer TAESPRNG
procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: boolean);
/// low-level function able to derivate a 0..1 floating-point from 128-bit of data
// - used e.g. by TAESPRNG.RandomExt
function Hash128ToExt({$ifdef FPC}constref{$else}const{$endif} r: THash128): TSynExtended;
{$ifdef FPC}inline;{$endif}
/// low-level function able to derivate a 0..1 64-bit floating-point from 128-bit of data
// - used e.g. by TAESPRNG.RandomDouble
function Hash128ToDouble({$ifdef FPC}constref{$else}const{$endif} r: THash128): double;
{$ifdef FPC}inline;{$endif}
/// low-level function able to derivate a 0..1 32-bit floating-point from 128-bit of data
function Hash128ToSingle({$ifdef FPC}constref{$else}const{$endif} r: THash128): double;
{$ifdef FPC}inline;{$endif}
type
PSHA1Digest = ^TSHA1Digest;
/// 160 bits memory block for SHA-1 hash digest storage
TSHA1Digest = THash160;
PSHA1 = ^TSHA1;
/// handle SHA-1 hashing
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance, e.g. for THMAC_SHA1
// - see TSynHasher if you expect to support more than one algorithm at runtime
{$ifdef USERECORDWITHMETHODS}TSHA1 = record
{$else}TSHA1 = object{$endif}
private
Context: packed array[1..SHAContextSize] of byte;
public
/// initialize SHA-1 context for hashing
procedure Init;
/// update the SHA-1 context with some data
procedure Update(Buffer: pointer; Len: integer); overload;
/// update the SHA-1 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize and compute the resulting SHA-1 hash Digest of all data
// affected to Update() method
// - will also call Init to reset all internal temporary context, for safety
procedure Final(out Digest: TSHA1Digest; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-1 hash Digest of all data
// affected to Update() method
// - will also call Init to reset all internal temporary context, for safety
function Final(NoInit: boolean=false): TSHA1Digest; overload; {$ifdef HASINLINE}inline;{$endif}
/// one method to rule them all
// - call Init, then Update(), then Final()
// - only Full() is Padlock-implemented - use this rather than Update()
procedure Full(Buffer: pointer; Len: integer; out Digest: TSHA1Digest);
end;
PSHA256Digest = ^TSHA256Digest;
/// 256 bits (32 bytes) memory block for SHA-256 hash digest storage
TSHA256Digest = THash256;
PSHA256 = ^TSHA256;
/// handle SHA-256 hashing
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance, e.g. for THMAC_SHA256
// - see TSynHasher if you expect to support more than one algorithm at runtime
{$ifdef USERECORDWITHMETHODS}TSHA256 = record
{$else}TSHA256 = object{$endif}
private
Context: packed array[1..SHAContextSize] of byte;
public
/// initialize SHA-256 context for hashing
procedure Init;
/// update the SHA-256 context with some data
procedure Update(Buffer: pointer; Len: integer); overload;
/// update the SHA-256 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize and compute the resulting SHA-256 hash Digest of all data
// affected to Update() method
procedure Final(out Digest: TSHA256Digest; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-256 hash Digest of all data
// affected to Update() method
function Final(NoInit: boolean=false): TSHA256Digest; overload; {$ifdef HASINLINE}inline;{$endif}
/// one method to rule them all
// - call Init, then Update(), then Final()
// - only Full() is Padlock-implemented - use this rather than Update()
procedure Full(Buffer: pointer; Len: integer; out Digest: TSHA256Digest);
end;
TSHA512Hash = record a, b, c, d, e, f, g, h: QWord; end;
PSHA384Digest = ^TSHA384Digest;
/// 384 bits (64 bytes) memory block for SHA-384 hash digest storage
TSHA384Digest = THash384;
/// handle SHA-384 hashing
// - it is in fact a TSHA512 truncated hash, with other initial hash values
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance, e.g. for THMAC_SHA384
// - see TSynHasher if you expect to support more than one algorithm at runtime
{$ifdef USERECORDWITHMETHODS}TSHA384 = record
{$else}TSHA384 = object{$endif}
private
Hash: TSHA512Hash;
MLen: QWord;
Data: array[0..127] of byte;
Index: integer;
public
/// initialize SHA-384 context for hashing
procedure Init;
/// update the SHA-384 context with some data
procedure Update(Buffer: pointer; Len: integer); overload;
/// update the SHA-384 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize and compute the resulting SHA-384 hash Digest of all data
// affected to Update() method
// - will also call Init to reset all internal temporary context, for safety
procedure Final(out Digest: TSHA384Digest; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-384 hash Digest of all data
// affected to Update() method
function Final(NoInit: boolean=false): TSHA384Digest; overload; {$ifdef HASINLINE}inline;{$endif}
/// one method to rule them all
// - call Init, then Update(), then Final()
procedure Full(Buffer: pointer; Len: integer; out Digest: TSHA384Digest);
end;
/// points to SHA-384 hashing instance
PSHA384 = ^TSHA384;
PSHA512Digest = ^TSHA512Digest;
/// 512 bits (64 bytes) memory block for SHA-512 hash digest storage
TSHA512Digest = THash512;
/// handle SHA-512 hashing
// - by design, this algorithm is expected to be much faster on 64-bit CPU,
// since all internal process involves QWord - but we included a SSE3 asm
// optimized version on 32-bit CPU under Windows and Linux, which is almost
// as fast as on plain x64, and even faster than SHA-256 and SHA-3
// - under x86/Delphi, plain pascal is 40MB/s, SSE3 asm 180MB/s
// - on x64, pascal Delphi is 150MB/s, and FPC is 190MB/s (thanks to native
// RorQWord intrinsic compiler function) - we also included a SSE4 asm version
// which outperforms other cryptographic hashes to more than 380MB/s
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance, e.g. for THMAC_SHA512
// - see TSynHasher if you expect to support more than one algorithm at runtime
{$ifdef USERECORDWITHMETHODS}TSHA512 = record
{$else}TSHA512 = object{$endif}
private
Hash: TSHA512Hash;
MLen: QWord;
Data: array[0..127] of byte;
Index: integer;
public
/// initialize SHA-512 context for hashing
procedure Init;
/// update the SHA-512 context with some data
procedure Update(Buffer: pointer; Len: integer); overload;
/// update the SHA-512 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize and compute the resulting SHA-512 hash Digest of all data
// affected to Update() method
// - will also call Init to reset all internal temporary context, for safety
procedure Final(out Digest: TSHA512Digest; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-512 hash Digest of all data
// affected to Update() method
function Final(NoInit: boolean=false): TSHA512Digest; overload; {$ifdef HASINLINE}inline;{$endif}
/// one method to rule them all
// - call Init, then Update(), then Final()
procedure Full(Buffer: pointer; Len: integer; out Digest: TSHA512Digest);
end;
/// points to SHA-512 hashing instance
PSHA512 = ^TSHA512;
/// SHA-3 instances, as defined by NIST Standard for Keccak sponge construction
TSHA3Algo = (SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE_128, SHAKE_256);
PSHA3 = ^TSHA3;
/// handle SHA-3 (Keccak) hashing
// - Keccak was the winner of the NIST hashing competition for a new hashing
// algorithm to provide an alternative to SHA-256. It became SHA-3 and was
// named by NIST a FIPS 180-4, then FIPS 202 hashing standard in 2015
// - by design, SHA-3 doesn't need to be encapsulated into a HMAC algorithm,
// since it already includes proper padding, so keys could be concatenated
// - this implementation is based on Wolfgang Ehrhardt's and Eric Grange's,
// with our own manually optimized x64 assembly
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance, e.g. after InitCypher
// - see TSynHasher if you expect to support more than one algorithm at runtime
{$ifdef USERECORDWITHMETHODS}TSHA3 = record
{$else}TSHA3 = object{$endif}
private
Context: packed array[1..SHA3ContextSize] of byte;
public
/// initialize SHA-3 context for hashing
// - in practice, you may use SHA3_256 or SHA3_512 to return THash256
// or THash512 digests
procedure Init(Algo: TSHA3Algo);
/// update the SHA-3 context with some data
procedure Update(Buffer: pointer; Len: integer); overload;
/// update the SHA-3 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize and compute the resulting SHA-3 hash 256-bit Digest
procedure Final(out Digest: THash256; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-3 hash 512-bit Digest
procedure Final(out Digest: THash512; NoInit: boolean=false); overload;
/// finalize and compute the resulting SHA-3 hash 256-bit Digest
function Final256(NoInit: boolean=false): THash256;
/// finalize and compute the resulting SHA-3 hash 512-bit Digest
function Final512(NoInit: boolean=false): THash512;
/// finalize and compute the resulting SHA-3 hash Digest
// - Digest destination buffer must contain enough bytes
// - default DigestBits=0 will write the default number of bits to Digest
// output memory buffer, according to the current TSHA3Algo
// - you can call this method several times, to use this SHA-3 hasher as
// "Extendable-Output Function" (XOF), e.g. for stream encryption (ensure
// NoInit is set to true, to enable recall)
procedure Final(Digest: pointer; DigestBits: integer=0; NoInit: boolean=false); overload;
/// compute a SHA-3 hash 256-bit Digest from a buffer, in one call
// - call Init, then Update(), then Final() using SHA3_256 into a THash256
procedure Full(Buffer: pointer; Len: integer; out Digest: THash256); overload;
/// compute a SHA-3 hash 512-bit Digest from a buffer, in one call
// - call Init, then Update(), then Final() using SHA3_512 into a THash512
procedure Full(Buffer: pointer; Len: integer; out Digest: THash512); overload;
/// compute a SHA-3 hash Digest from a buffer, in one call
// - call Init, then Update(), then Final() using the supplied algorithm
// - default DigestBits=0 will write the default number of bits to Digest
// output memory buffer, according to the specified TSHA3Algo
procedure Full(Algo: TSHA3Algo; Buffer: pointer; Len: integer;
Digest: pointer; DigestBits: integer=0); overload;
/// compute a SHA-3 hash hexadecimal Digest from a buffer, in one call
// - call Init, then Update(), then Final() using the supplied algorithm
// - default DigestBits=0 will write the default number of bits to Digest
// output memory buffer, according to the specified TSHA3Algo
function FullStr(Algo: TSHA3Algo; Buffer: pointer; Len: integer;
DigestBits: integer=0): RawUTF8;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - there is no MAC stored in the resulting binary
// - Source and Dest will have the very same DataLen size in bytes,
// and Dest will be Source XORed with the XOF output, so encryption and
// decryption are just obtained by the same symmetric call
// - in this implementation, Source and Dest should point to two diverse buffers
// - for safety, the Key should be a secret value, pre-pended with a random
// salt/IV or a resource-specific identifier (e.g. a record ID or a S/N),
// to avoid reverse composition of the cypher from known content - note that
// concatenating keys with SHA-3 is as safe as computing a HMAC for SHA-2
procedure Cypher(Key, Source, Dest: pointer; KeyLen, DataLen: integer;
Algo: TSHA3Algo = SHAKE_256); overload;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - this overloaded function works with RawByteString content
// - resulting string will have the very same size than the Source
// - XOF is implemented as a symmetrical algorithm: use this Cypher()
// method for both encryption and decryption of any buffer
function Cypher(const Key, Source: RawByteString; Algo: TSHA3Algo = SHAKE_256): RawByteString; overload;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - prepare the instance to further Cypher() calls
// - you may reuse the very same TSHA3 instance by copying it to a local
// variable before calling this method (this copy is thread-safe)
// - works with RawByteString content
procedure InitCypher(Key: pointer; KeyLen: integer; Algo: TSHA3Algo = SHAKE_256); overload;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - prepare the instance to further Cypher() calls
// - you may reuse the very same TSHA3 instance by copying it to a local
// variable before calling this method (this copy is thread-safe)
// - works with RawByteString content
procedure InitCypher(const Key: RawByteString; Algo: TSHA3Algo = SHAKE_256); overload;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - this overloaded function expects the instance to have been prepared
// by previous InitCypher call
// - resulting Dest buffer will have the very same size than the Source
// - XOF is implemented as a symmetrical algorithm: use this Cypher()
// method for both encryption and decryption of any buffer
// - you can call this method several times, to work with a stream buffer;
// but for safety, you should eventually call Done
procedure Cypher(Source, Dest: pointer; DataLen: integer); overload;
/// uses SHA-3 in "Extendable-Output Function" (XOF) to cypher some content
// - this overloaded function expects the instance to have been prepared
// by previous InitCypher call
// - resulting string will have the very same size than the Source
// - XOF is implemented as a symmetrical algorithm: use this Cypher()
// method for both encryption and decryption of any buffer
// - you can call this method several times, to work with a stream buffer;
// but for safety, you should eventually call Done
function Cypher(const Source: RawByteString): RawByteString; overload;
/// returns the algorithm specified at Init()
function Algorithm: TSHA3Algo;
/// fill all used memory context with zeros, for safety
// - is necessary only when NoInit is set to true (e.g. after InitCypher)
procedure Done;
end;
TMD5In = array[0..15] of cardinal;
PMD5In = ^TMD5In;
/// 128 bits memory block for MD5 hash digest storage
TMD5Digest = THash128;
PMD5Digest = ^TMD5Digest;
PMD5 = ^TMD5;
TMD5Buf = TBlock128;
/// handle MD5 hashing
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance
// - see TSynHasher if you expect to support more than one algorithm at runtime
// - even if MD5 is now seldom used, it is still faster than SHA alternatives,
// when you need a 128-bit cryptographic hash, but can afford some collisions
// - this implementation has optimized x86 and x64 assembly, for processing
// around 500MB/s, and a pure-pascal fallback code on other platforms
{$ifdef USERECORDWITHMETHODS}TMD5 = record
{$else}TMD5 = object{$endif}
private
in_: TMD5In;
bytes: array[0..1] of cardinal;
public
buf: TMD5Buf;
/// initialize MD5 context for hashing
procedure Init;
/// update the MD5 context with some data
procedure Update(const buffer; Len: cardinal); overload;
/// update the MD5 context with some data
procedure Update(const Buffer: RawByteString); overload;
/// finalize the MD5 hash process
// - the resulting hash digest would be stored in buf public variable
procedure Finalize;
/// finalize and compute the resulting MD5 hash Digest of all data
// affected to Update() method
procedure Final(out result: TMD5Digest); overload;
/// finalize and compute the resulting MD5 hash Digest of all data
// affected to Update() method
function Final: TMD5Digest; overload;
/// one method to rule them all
// - call Init, then Update(), then Final()
procedure Full(Buffer: pointer; Len: integer; out Digest: TMD5Digest);
end;
/// handle RC4 encryption/decryption
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance
// - you can also restore and backup any previous state of the RC4 encryption
// by copying the whole TRC4 variable into another (stack-allocated) variable
{$ifdef USERECORDWITHMETHODS}TRC4 = record
{$else}TRC4 = object{$endif}
private
{$ifdef CPUINTEL}
state: array[byte] of PtrInt; // PtrInt=270MB/s byte=240MB/s on x86
{$else}
state: array[byte] of byte; // on ARM, keep the CPU cache usage low
{$endif}
currI, currJ: PtrInt;
public
/// initialize the RC4 encryption/decryption
// - KeyLen is in bytes, and should be within 1..255 range
procedure Init(const aKey; aKeyLen: integer);
/// initialize RC4-drop[3072] encryption/decryption after SHA-3 hashing
// - will use SHAKE-128 generator in XOF mode to generate a 256 bytes key,
// then drop the first 3072 bytes from the RC4 stream
// - this initializer is much safer than plain Init, so should be considered
// for any use on RC4 for new projects - even if AES-NI is 2 times faster,
// and safer SHAKE-128 operates in XOF mode at a similar speed range
procedure InitSHA3(const aKey; aKeyLen: integer);
/// drop the next Count bytes from the RC4 cypher state
// - may be used in Stream mode, or to initialize in RC4-drop[n] mode
procedure Drop(Count: cardinal);
/// perform the RC4 cypher encryption/decryption on a buffer
// - each call to this method shall be preceeded with an Init() call
// - RC4 is a symmetrical algorithm: use this Encrypt() method
// for both encryption and decryption of any buffer
procedure Encrypt(const BufIn; var BufOut; Count: cardinal);
{$ifdef HASINLINE}inline;{$endif}
/// perform the RC4 cypher encryption/decryption on a buffer
// - each call to this method shall be preceeded with an Init() call
// - RC4 is a symmetrical algorithm: use this EncryptBuffer() method
// for both encryption and decryption of any buffer
procedure EncryptBuffer(BufIn, BufOut: PByte; Count: cardinal);
end;
{$A-} { packed memory structure }
/// internal header for storing our AES data with salt and CRC
// - memory size matches an TAESBlock on purpose, for direct encryption
{$ifdef USERECORDWITHMETHODS}TAESFullHeader = record
{$else}TAESFullHeader = object{$endif}
public
/// Len before compression (if any)
OriginalLen,
/// Len before AES encoding
SourceLen,
/// Random Salt for better encryption
SomeSalt,
/// CRC from header
HeaderCheck: cardinal;
/// computes the Key checksum, using Adler32 algorithm
function Calc(const Key; KeySize: cardinal): cardinal;
end;
{$A+}
PAESFull = ^TAESFull;
/// AES and XOR encryption object for easy direct memory or stream access
// - calls internaly TAES objet methods, and handle memory and streams for best speed
// - a TAESFullHeader is encrypted at the begining, allowing fast Key validation,
// but the resulting stream is not compatible with raw TAES object
{$ifdef USERECORDWITHMETHODS}TAESFull = record
{$else}TAESFull = object{$endif}
public
/// header, stored at the beginning of struct -> 16-byte aligned
Head: TAESFullHeader;
/// this memory stream is used in case of EncodeDecode(outStream=bOut=nil)
// method call
outStreamCreated: TMemoryStream;
/// main method of AES or XOR cypher/uncypher
// - return out size, -1 if error on decoding (Key not correct)
// - valid KeySize: 0=nothing, 32=xor, 128,192,256=AES
// - if outStream is TMemoryStream -> auto-reserve space (no Realloc:)
// - for normal usage, you just have to Assign one In and one Out
// - if outStream AND bOut are both nil, an outStream is created via
// THeapMemoryStream.Create
// - if Padlock is used, 16-byte alignment is forced (via tmp buffer if necessary)
// - if Encrypt -> OriginalLen can be used to store unCompressed Len
function EncodeDecode(const Key; KeySize, inLen: cardinal; Encrypt: boolean;
inStream, outStream: TStream; bIn, bOut: pointer; OriginalLen: cardinal=0): integer;
end;
/// AES encryption stream
// - encrypt the Data on the fly, in a compatible way with AES() - last bytes
// are coded with XOR (not compatible with TAESFull format)
// - not optimized for small blocks -> ok if used AFTER TBZCompressor/TZipCompressor
// - warning: Write() will crypt Buffer memory in place -> use AFTER T*Compressor
TAESWriteStream = class(TStream)
public
Adler, // CRC from uncrypted compressed data - for Key check
DestSize: cardinal;
private
Dest: TStream;
Buf: TAESBlock; // very small buffer for remainging 0..15 bytes
BufCount: integer; // number of pending bytes (0..15) in Buf
AES: TAES;
NoCrypt: boolean; // if KeySize=0
public
/// initialize the AES encryption stream for an output stream (e.g.
// a TMemoryStream or a TFileStream)
constructor Create(outStream: TStream; const Key; KeySize: cardinal);
/// finalize the AES encryption stream
// - internaly call the Finish method
destructor Destroy; override;
/// read some data is not allowed -> this method will raise an exception on call
function Read(var Buffer; Count: Longint): Longint; override;
/// append some data to the outStream, after encryption
function Write(const Buffer; Count: Longint): Longint; override;
/// read some data is not allowed -> this method will raise an exception on call
function Seek(Offset: Longint; Origin: Word): Longint; override;
/// write pending data
// - should always be called before closeing the outStream (some data may
// still be in the internal buffers)
procedure Finish;
end;
/// direct MD5 hash calculation of some data
function MD5Buf(const Buffer; Len: Cardinal): TMD5Digest;
/// direct MD5 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
function MD5(const s: RawByteString): RawUTF8;
/// direct SHA-1 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
function SHA1(const s: RawByteString): RawUTF8;
/// direct SHA-384 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
function SHA384(const s: RawByteString): RawUTF8;
/// direct SHA-512 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
function SHA512(const s: RawByteString): RawUTF8;
type
/// compute the HMAC message authentication code using SHA-1 as hash function
// - you may use HMAC_SHA1() overloaded functions for one-step process
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance via Compute(), e.g. for fast PBKDF2
{$ifdef USERECORDWITHMETHODS}THMAC_SHA1 = record
{$else}THMAC_SHA1 = object{$endif}
private
sha: TSHA1;
step7data: THash512Rec;
public
/// prepare the HMAC authentication with the supplied key
// - content of this record is stateless, so you can prepare a HMAC for a
// key using Init, then copy this THMAC_SHA1 instance to a local variable,
// and use this local thread-safe copy for actual HMAC computing
procedure Init(key: pointer; keylen: integer);
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(msg: pointer; msglen: integer);
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: TSHA1Digest; NoInit: boolean=false); overload;
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: RawUTF8; NoInit: boolean=false); overload;
/// computes the HMAC of the supplied message according to the key
// - expects a previous call on Init() to setup the shared key
// - similar to a single Update(msg,msglen) followed by Done, but re-usable
// - this method is thread-safe on any shared THMAC_SHA1 instance
procedure Compute(msg: pointer; msglen: integer; out result: TSHA1Digest);
end;
/// points to a HMAC message authentication context using SHA-1
PHMAC_SHA1 = ^THMAC_SHA1;
/// compute the HMAC message authentication code using SHA-1 as hash function
procedure HMAC_SHA1(const key,msg: RawByteString; out result: TSHA1Digest); overload;
/// compute the HMAC message authentication code using SHA-1 as hash function
procedure HMAC_SHA1(const key: TSHA1Digest; const msg: RawByteString;
out result: TSHA1Digest); overload;
/// compute the HMAC message authentication code using SHA-1 as hash function
procedure HMAC_SHA1(key,msg: pointer; keylen,msglen: integer;
out result: TSHA1Digest); overload;
/// compute the PBKDF2 derivation of a password using HMAC over SHA-1
// - this function expect the resulting key length to match SHA-1 digest size
procedure PBKDF2_HMAC_SHA1(const password,salt: RawByteString; count: Integer;
out result: TSHA1Digest);
type
/// compute the HMAC message authentication code using SHA-384 as hash function
// - you may use HMAC_SHA384() overloaded functions for one-step process
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance via Compute(), e.g. for fast PBKDF2
{$ifdef USERECORDWITHMETHODS}THMAC_SHA384 = record
{$else}THMAC_SHA384 = object{$endif}
private
sha: TSHA384;
step7data: array[0..31] of cardinal;
public
/// prepare the HMAC authentication with the supplied key
// - content of this record is stateless, so you can prepare a HMAC for a
// key using Init, then copy this THMAC_SHA384 instance to a local variable,
// and use this local thread-safe copy for actual HMAC computing
procedure Init(key: pointer; keylen: integer);
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(msg: pointer; msglen: integer);
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: TSHA384Digest; NoInit: boolean=false); overload;
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: RawUTF8; NoInit: boolean=false); overload;
/// computes the HMAC of the supplied message according to the key
// - expects a previous call on Init() to setup the shared key
// - similar to a single Update(msg,msglen) followed by Done, but re-usable
// - this method is thread-safe on any shared THMAC_SHA384 instance
procedure Compute(msg: pointer; msglen: integer; out result: TSHA384Digest);
end;
/// points to a HMAC message authentication context using SHA-384
PHMAC_SHA384 = ^THMAC_SHA384;
/// compute the HMAC message authentication code using SHA-384 as hash function
procedure HMAC_SHA384(const key,msg: RawByteString; out result: TSHA384Digest); overload;
/// compute the HMAC message authentication code using SHA-384 as hash function
procedure HMAC_SHA384(const key: TSHA384Digest; const msg: RawByteString;
out result: TSHA384Digest); overload;
/// compute the HMAC message authentication code using SHA-384 as hash function
procedure HMAC_SHA384(key,msg: pointer; keylen,msglen: integer;
out result: TSHA384Digest); overload;
/// compute the PBKDF2 derivation of a password using HMAC over SHA-384
// - this function expect the resulting key length to match SHA-384 digest size
procedure PBKDF2_HMAC_SHA384(const password,salt: RawByteString; count: Integer;
out result: TSHA384Digest);
type
/// compute the HMAC message authentication code using SHA-512 as hash function
// - you may use HMAC_SHA512() overloaded functions for one-step process
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance via Compute(), e.g. for fast PBKDF2
{$ifdef USERECORDWITHMETHODS}THMAC_SHA512 = record
{$else}THMAC_SHA512 = object{$endif}
private
sha: TSHA512;
step7data: array[0..31] of cardinal;
public
/// prepare the HMAC authentication with the supplied key
// - content of this record is stateless, so you can prepare a HMAC for a
// key using Init, then copy this THMAC_SHA512 instance to a local variable,
// and use this local thread-safe copy for actual HMAC computing
procedure Init(key: pointer; keylen: integer);
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(msg: pointer; msglen: integer);
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: TSHA512Digest; NoInit: boolean=false); overload;
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: RawUTF8; NoInit: boolean=false); overload;
/// computes the HMAC of the supplied message according to the key
// - expects a previous call on Init() to setup the shared key
// - similar to a single Update(msg,msglen) followed by Done, but re-usable
// - this method is thread-safe on any shared THMAC_SHA512 instance
procedure Compute(msg: pointer; msglen: integer; out result: TSHA512Digest);
end;
/// points to a HMAC message authentication context using SHA-512
PHMAC_SHA512 = ^THMAC_SHA512;
/// compute the HMAC message authentication code using SHA-512 as hash function
procedure HMAC_SHA512(const key,msg: RawByteString; out result: TSHA512Digest); overload;
/// compute the HMAC message authentication code using SHA-512 as hash function
procedure HMAC_SHA512(const key: TSHA512Digest; const msg: RawByteString;
out result: TSHA512Digest); overload;
/// compute the HMAC message authentication code using SHA-512 as hash function
procedure HMAC_SHA512(key,msg: pointer; keylen,msglen: integer;
out result: TSHA512Digest); overload;
/// compute the PBKDF2 derivation of a password using HMAC over SHA-512
// - this function expect the resulting key length to match SHA-512 digest size
procedure PBKDF2_HMAC_SHA512(const password,salt: RawByteString; count: Integer;
out result: TSHA512Digest);
/// direct SHA-256 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
function SHA256(const s: RawByteString): RawUTF8; overload;
/// direct SHA-256 hash calculation of some binary data
// - result is returned in hexadecimal format
function SHA256(Data: pointer; Len: integer): RawUTF8; overload;
/// direct SHA-256 hash calculation of some binary data
// - result is returned in TSHA256Digest binary format
// - since the result would be stored temporarly in the stack, it may be
// safer to use an explicit TSHA256Digest variable, which would be filled
// with zeros by a ... finally FillZero(
function SHA256Digest(Data: pointer; Len: integer): TSHA256Digest; overload;
/// direct SHA-256 hash calculation of some binary data
// - result is returned in TSHA256Digest binary format
// - since the result would be stored temporarly in the stack, it may be
// safer to use an explicit TSHA256Digest variable, which would be filled
// with zeros by a ... finally FillZero(
function SHA256Digest(const Data: RawByteString): TSHA256Digest; overload;
/// direct SHA-256 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
// - this procedure has a weak password protection: small incoming data
// is append to some salt, in order to have at least a 256 bytes long hash:
// such a feature improve security for small passwords, e.g.
// - note that this algorithm is proprietary, and less secure (and standard)
// than the PBKDF2 algorithm, so is there only for backward compatibility of
// existing code: use PBKDF2_HMAC_SHA256 or similar functions for password
// derivation
procedure SHA256Weak(const s: RawByteString; out Digest: TSHA256Digest);
type
/// compute the HMAC message authentication code using SHA-256 as hash function
// - you may use HMAC_SHA256() overloaded functions for one-step process
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance via Compute(), e.g. for fast PBKDF2
{$ifdef USERECORDWITHMETHODS}THMAC_SHA256 = record
{$else}THMAC_SHA256 = object{$endif}
private
sha: TSha256;
step7data: THash512Rec;
public
/// prepare the HMAC authentication with the supplied key
// - content of this record is stateless, so you can prepare a HMAC for a
// key using Init, then copy this THMAC_SHA256 instance to a local variable,
// and use this local thread-safe copy for actual HMAC computing
procedure Init(key: pointer; keylen: integer);
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(msg: pointer; msglen: integer); overload;
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(const msg: THash128); overload;
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(const msg: THash256); overload;
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(const msg: RawByteString); overload;
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: TSHA256Digest; NoInit: boolean=false); overload;
/// computes the HMAC of all supplied message according to the key
procedure Done(out result: RawUTF8; NoInit: boolean=false); overload;
/// computes the HMAC of the supplied message according to the key
// - expects a previous call on Init() to setup the shared key
// - similar to a single Update(msg,msglen) followed by Done, but re-usable
// - this method is thread-safe on any shared THMAC_SHA256 instance
procedure Compute(msg: pointer; msglen: integer; out result: TSHA256Digest);
end;
/// points to a HMAC message authentication context using SHA-256
PHMAC_SHA256 = ^THMAC_SHA256;
/// compute the HMAC message authentication code using SHA-256 as hash function
procedure HMAC_SHA256(const key,msg: RawByteString; out result: TSHA256Digest); overload;
/// compute the HMAC message authentication code using SHA-256 as hash function
procedure HMAC_SHA256(const key: TSHA256Digest; const msg: RawByteString;
out result: TSHA256Digest); overload;
/// compute the HMAC message authentication code using SHA-256 as hash function
procedure HMAC_SHA256(key,msg: pointer; keylen,msglen: integer; out result: TSHA256Digest); overload;
/// compute the PBKDF2 derivation of a password using HMAC over SHA-256
// - this function expect the resulting key length to match SHA-256 digest size
procedure PBKDF2_HMAC_SHA256(const password,salt: RawByteString; count: Integer;
out result: TSHA256Digest; const saltdefault: RawByteString=''); overload;
/// compute the PBKDF2 derivation of a password using HMAC over SHA-256, into
// several 256-bit items, so can be used to return any size of output key
// - this function expect the result array to have the expected output length
// - allows resulting key length to be more than one SHA-256 digest size, e.g.
// to be used for both Encryption and MAC
procedure PBKDF2_HMAC_SHA256(const password,salt: RawByteString; count: Integer;
var result: THash256DynArray; const saltdefault: RawByteString=''); overload;
/// low-level anti-forensic diffusion of a memory buffer using SHA-256
// - as used by TAESPRNG.AFSplit and TAESPRNG.AFUnSplit
procedure AFDiffusion(buf,rnd: pointer; size: cardinal);
/// direct SHA-3 hash calculation of some data (string-encoded)
// - result is returned in hexadecimal format
// - default DigestBits=0 will write the default number of bits to Digest
// output memory buffer, according to the specified TSHA3Algo
function SHA3(Algo: TSHA3Algo; const s: RawByteString;
DigestBits: integer=0): RawUTF8; overload;
/// direct SHA-3 hash calculation of some binary buffer
// - result is returned in hexadecimal format
// - default DigestBits=0 will write the default number of bits to Digest
// output memory buffer, according to the specified TSHA3Algo
function SHA3(Algo: TSHA3Algo; Buffer: pointer; Len: integer;
DigestBits: integer=0): RawUTF8; overload;
/// safe key derivation using iterated SHA-3 hashing
// - you can use SHA3_224, SHA3_256, SHA3_384, SHA3_512 algorithm to fill
// the result buffer with the default sized derivated key of 224,256,384 or 512
// bits (leaving resultbytes = 0)
// - or you may select SHAKE_128 or SHAKE_256, and specify any custom key size
// in resultbytes (used e.g. by PBKDF2_SHA3_Crypt)
procedure PBKDF2_SHA3(algo: TSHA3Algo; const password,salt: RawByteString;
count: Integer; result: PByte; resultbytes: integer=0);
/// encryption/decryption of any data using iterated SHA-3 hashing key derivation
// - specified algo is expected to be SHAKE_128 or SHAKE_256
// - expected the supplied data buffer to be small - for bigger content, consider
// using TSHA.Cypher after 256-bit PBKDF2_SHA3 key derivation
procedure PBKDF2_SHA3_Crypt(algo: TSHA3Algo; const password,salt: RawByteString;
count: Integer; var data: RawByteString);
type
/// the HMAC/SHA-3 algorithms known by TSynSigner
TSignAlgo = (
saSha1, saSha256, saSha384, saSha512,
saSha3224, saSha3256, saSha3384, saSha3512, saSha3S128, saSha3S256);
/// JSON-serialization ready object as used by TSynSigner.PBKDF2 overloaded methods
// - default value for unspecified parameters will be SHAKE_128 with
// rounds=1000 and a fixed salt
TSynSignerParams = packed record
algo: TSignAlgo;
secret,salt: RawUTF8;
rounds: integer;
end;
/// a generic wrapper object to handle digital HMAC-SHA-2/SHA-3 signatures
// - used e.g. to implement TJWTSynSignerAbstract
{$ifdef USERECORDWITHMETHODS}TSynSigner = record
{$else}TSynSigner = object{$endif}
private
ctxt: packed array[1..SHA3ContextSize] of byte; // enough space for all
fSignatureSize: integer;
fAlgo: TSignAlgo;
public
/// initialize the digital HMAC/SHA-3 signing context with some secret text
procedure Init(aAlgo: TSignAlgo; const aSecret: RawUTF8); overload;
/// initialize the digital HMAC/SHA-3 signing context with some secret binary
procedure Init(aAlgo: TSignAlgo; aSecret: pointer; aSecretLen: integer); overload;
/// initialize the digital HMAC/SHA-3 signing context with PBKDF2 safe
// iterative key derivation of a secret salted text
procedure Init(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; aPBKDF2Secret: PHash512Rec=nil); overload;
/// process some message content supplied as memory buffer
procedure Update(aBuffer: pointer; aLen: integer); overload;
/// process some message content supplied as string
procedure Update(const aBuffer: RawByteString); overload; {$ifdef HASINLINE}inline;{$endif}
/// returns the computed digital signature as lowercase hexadecimal text
function Final: RawUTF8; overload;
/// returns the raw computed digital signature
// - SignatureSize bytes will be written: use Signature.Lo/h0/b3/b accessors
procedure Final(out aSignature: THash512Rec; aNoInit: boolean=false); overload;
/// one-step digital signature of a buffer as lowercase hexadecimal string
function Full(aAlgo: TSignAlgo; const aSecret: RawUTF8;
aBuffer: Pointer; aLen: integer): RawUTF8; overload;
/// one-step digital signature of a buffer with PBKDF2 derivation
function Full(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; aBuffer: Pointer; aLen: integer): RawUTF8; overload;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
procedure PBKDF2(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; out aDerivatedKey: THash512Rec); overload;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
procedure PBKDF2(const aParams: TSynSignerParams; out aDerivatedKey: THash512Rec); overload;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
// - accept as input a TSynSignerParams serialized as JSON object
procedure PBKDF2(aParamsJSON: PUTF8Char; aParamsJSONLen: integer;
out aDerivatedKey: THash512Rec; const aDefaultSalt: RawUTF8='I6sWioAidNnhXO9BK';
aDefaultAlgo: TSignAlgo=saSha3S128); overload;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
// - accept as input a TSynSignerParams serialized as JSON object
procedure PBKDF2(const aParamsJSON: RawUTF8; out aDerivatedKey: THash512Rec;
const aDefaultSalt: RawUTF8='I6sWioAidNnhXO9BK'; aDefaultAlgo: TSignAlgo=saSha3S128); overload;
/// prepare a TAES object with the key derivated via a PBKDF2() call
// - aDerivatedKey is defined as "var", since it will be zeroed after use
procedure AssignTo(var aDerivatedKey: THash512Rec; out aAES: TAES; aEncrypt: boolean);
/// fill the intenral context with zeros, for security
procedure Done;
/// the algorithm used for digitial signature
property Algo: TSignAlgo read fAlgo;
/// the size, in bytes, of the digital signature of this algorithm
// - potential values are 20, 28, 32, 48 and 64
property SignatureSize: integer read fSignatureSize;
end;
/// reference to TSynSigner wrapper object
PSynSigner = ^TSynSigner;
/// hash algorithms available for HashFile/HashFull functions and TSynHasher object
THashAlgo = (hfMD5, hfSHA1, hfSHA256, hfSHA384, hfSHA512, hfSHA3_256, hfSHA3_512);
/// set of algorithms available for HashFile/HashFull functions and TSynHasher object
THashAlgos = set of THashAlgo;
/// convenient multi-algorithm hashing wrapper
// - as used e.g. by HashFile/HashFull functions
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance
{$ifdef USERECORDWITHMETHODS}TSynHasher = record
{$else}TSynHasher = object{$endif}
private
fAlgo: THashAlgo;
ctxt: array[1..SHA3ContextSize] of byte; // enough space for all algorithms
public
/// initialize the internal hashing structure for a specific algorithm
// - returns false on unknown/unsupported algorithm
function Init(aAlgo: THashAlgo): boolean;
/// hash the supplied memory buffer
procedure Update(aBuffer: Pointer; aLen: integer); overload;
/// hash the supplied string content
procedure Update(const aBuffer: RawByteString); overload; {$ifdef HASINLINE}inline;{$endif}
/// returns the resulting hash as lowercase hexadecimal string
function Final: RawUTF8;
/// one-step hash computation of a buffer as lowercase hexadecimal string
function Full(aAlgo: THashAlgo; aBuffer: Pointer; aLen: integer): RawUTF8;
/// the hash algorithm used by this instance
property Algo: THashAlgo read fAlgo;
end;
/// compute the hexadecimal hash of any (big) file
// - using a temporary buffer of 1MB for the sequential reading
function HashFile(const aFileName: TFileName; aAlgo: THashAlgo): RawUTF8; overload;
/// compute the hexadecimal hashe(s) of one file, as external .md5/.sha256/.. files
// - reading the file once in memory, then apply all algorithms on it and
// generate the text hash files in the very same folder
procedure HashFile(const aFileName: TFileName; aAlgos: THashAlgos); overload;
/// one-step hash computation of a buffer as lowercase hexadecimal string
function HashFull(aAlgo: THashAlgo; aBuffer: Pointer; aLen: integer): RawUTF8;
/// compute the HMAC message authentication code using crc256c as hash function
// - HMAC over a non cryptographic hash function like crc256c is known to be
// safe as MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - performs two crc32c hashes, so SSE 4.2 gives more than 2.2 GB/s on a Core i7
procedure HMAC_CRC256C(key,msg: pointer; keylen,msglen: integer; out result: THash256); overload;
/// compute the HMAC message authentication code using crc256c as hash function
// - HMAC over a non cryptographic hash function like crc256c is known to be
// safe as MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - performs two crc32c hashes, so SSE 4.2 gives more than 2.2 GB/s on a Core i7
procedure HMAC_CRC256C(const key: THash256; const msg: RawByteString; out result: THash256); overload;
/// compute the HMAC message authentication code using crc256c as hash function
// - HMAC over a non cryptographic hash function like crc256c is known to be
// safe as MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - performs two crc32c hashes, so SSE 4.2 gives more than 2.2 GB/s on a Core i7
procedure HMAC_CRC256C(const key,msg: RawByteString; out result: THash256); overload;
type
/// compute the HMAC message authentication code using crc32c as hash function
// - HMAC over a non cryptographic hash function like crc32c is known to be a
// safe enough MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - SSE 4.2 will let MAC be computed at 4 GB/s on a Core i7
// - you may use HMAC_CRC32C() overloaded functions for one-step process
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance via Compute()
{$ifdef USERECORDWITHMETHODS}THMAC_CRC32C = record
{$else}THMAC_CRC32C = object{$endif}
private
seed: cardinal;
step7data: THash512Rec;
public
/// prepare the HMAC authentication with the supplied key
// - consider using Compute to re-use a prepared HMAC instance
procedure Init(key: pointer; keylen: integer); overload;
/// prepare the HMAC authentication with the supplied key
// - consider using Compute to re-use a prepared HMAC instance
procedure Init(const key: RawByteString); overload;
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(msg: pointer; msglen: integer); overload;
{$ifdef HASINLINE}inline;{$endif}
/// call this method for each continuous message block
// - iterate over all message blocks, then call Done to retrieve the HMAC
procedure Update(const msg: RawByteString); overload;
{$ifdef HASINLINE}inline;{$endif}
/// computes the HMAC of all supplied message according to the key
function Done(NoInit: boolean=false): cardinal;
{$ifdef HASINLINE}inline;{$endif}
/// computes the HMAC of the supplied message according to the key
// - expects a previous call on Init() to setup the shared key
// - similar to a single Update(msg,msglen) followed by Done, but re-usable
// - this method is thread-safe
function Compute(msg: pointer; msglen: integer): cardinal;
end;
/// points to HMAC message authentication code using crc32c as hash function
PHMAC_CRC32C= ^THMAC_CRC32C;
/// compute the HMAC message authentication code using crc32c as hash function
// - HMAC over a non cryptographic hash function like crc32c is known to be a
// safe enough MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - SSE 4.2 will let MAC be computed at 4 GB/s on a Core i7
function HMAC_CRC32C(key,msg: pointer; keylen,msglen: integer): cardinal; overload;
/// compute the HMAC message authentication code using crc32c as hash function
// - HMAC over a non cryptographic hash function like crc32c is known to be a
// safe enough MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - SSE 4.2 will let MAC be computed at 4 GB/s on a Core i7
function HMAC_CRC32C(const key: THash256; const msg: RawByteString): cardinal; overload;
/// compute the HMAC message authentication code using crc32c as hash function
// - HMAC over a non cryptographic hash function like crc32c is known to be a
// safe enough MAC, if the supplied key comes e.g. from cryptographic HMAC_SHA256
// - SSE 4.2 will let MAC be computed at 4 GB/s on a Core i7
function HMAC_CRC32C(const key,msg: RawByteString): cardinal; overload;
/// direct Encrypt/Decrypt of data using the TAES class
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
procedure AES(const Key; KeySize: cardinal; buffer: pointer; Len: Integer; Encrypt: boolean); overload;
/// direct Encrypt/Decrypt of data using the TAES class
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
procedure AES(const Key; KeySize: cardinal; bIn, bOut: pointer; Len: Integer; Encrypt: boolean); overload;
/// direct Encrypt/Decrypt of data using the TAES class
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
function AES(const Key; KeySize: cardinal; const s: RawByteString; Encrypt: boolean): RawByteString; overload;
/// direct Encrypt/Decrypt of data using the TAES class
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
function AES(const Key; KeySize: cardinal; buffer: pointer; Len: cardinal; Stream: TStream; Encrypt: boolean): boolean; overload;
/// AES and XOR encryption using the TAESFull format
// - outStream will be larger/smaller than Len (full AES encrypted)
// - returns true if OK
function AESFull(const Key; KeySize: cardinal; bIn: pointer; Len: Integer;
outStream: TStream; Encrypt: boolean; OriginalLen: Cardinal=0): boolean; overload;
/// AES and XOR encryption using the TAESFull format
// - bOut must be at least bIn+32/Encrypt bIn-16/Decrypt
// - returns outLength, -1 if error
function AESFull(const Key; KeySize: cardinal; bIn, bOut: pointer; Len: Integer;
Encrypt: boolean; OriginalLen: Cardinal=0): integer; overload;
/// AES and XOR decryption check using the TAESFull format
// - return true if begining of buff contains true AESFull encrypted data with this Key
// - if not KeySize in [128,192,256] -> use fast and efficient Xor Cypher
function AESFullKeyOK(const Key; KeySize: cardinal; buff: pointer): boolean;
/// AES encryption using the TAES format with a supplied SHA-256 password
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
procedure AESSHA256(Buffer: pointer; Len: integer; const Password: RawByteString; Encrypt: boolean); overload;
/// AES encryption using the TAES format with a supplied SHA-256 password
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
procedure AESSHA256(bIn, bOut: pointer; Len: integer; const Password: RawByteString; Encrypt: boolean); overload;
/// AES encryption using the TAES format with a supplied SHA-256 password
// - last bytes (not part of 16 bytes blocks) are not crypted by AES, but with XOR
function AESSHA256(const s, Password: RawByteString; Encrypt: boolean): RawByteString; overload;
/// AES encryption using the TAESFull format with a supplied SHA-256 password
// - outStream will be larger/smaller than Len: this is a full AES version with
// a triming TAESFullHeader at the beginning
procedure AESSHA256Full(bIn: pointer; Len: Integer; outStream: TStream; const Password: RawByteString; Encrypt: boolean); overload;
var
/// salt for CryptDataForCurrentUser function
// - is filled with some random bytes by default, but you may override
// it for a set of custom processes calling CryptDataForCurrentUser
CryptProtectDataEntropy: THash256 = (
$19,$8E,$BA,$52,$FA,$D6,$56,$99,$7B,$73,$1B,$D0,$8B,$3A,$95,$AB,
$94,$63,$C2,$C0,$78,$05,$9C,$8B,$85,$B7,$A1,$E3,$ED,$93,$27,$18);
{$ifdef MSWINDOWS}
/// protect some data for the current user, using Windows DPAPI
// - the application can specify a secret salt text, which should reflect the
// current execution context, to ensure nobody could decrypt the data without
// knowing this application-specific AppSecret value
// - will use CryptProtectData DPAPI function call under Windows
// - see https://msdn.microsoft.com/en-us/library/ms995355
// - this function is Windows-only, could be slow, and you don't know which
// algorithm is really used on your system, so using CryptDataForCurrentUser()
// may be a better (and cross-platform) alternative
// - also note that DPAPI has been closely reverse engineered - see e.g.
// https://www.passcape.com/index.php?section=docsys&cmd=details&id=28
function CryptDataForCurrentUserDPAPI(const Data,AppSecret: RawByteString; Encrypt: boolean): RawByteString;
{$endif}
/// protect some data via AES-256-CFB and a secret known by the current user only
// - the application can specify a secret salt text, which should reflect the
// current execution context, to ensure nobody could decrypt the data without
// knowing this application-specific AppSecret value
// - here data is cyphered using a random secret key, stored in a file located in
// ! GetSystemPath(spUserData)+sep+PBKDF2_HMAC_SHA256(CryptProtectDataEntropy,User)
// with sep='_' under Windows, and sep='.syn-' under Linux/Posix
// - under Windows, it will encode the secret file via CryptProtectData DPAPI,
// so has the same security level than plain CryptDataForCurrentUserDPAPI()
// - under Linux/POSIX, access to the $HOME user's .xxxxxxxxxxx secret file with
// chmod 400 is considered to be a safe enough approach
// - this function is up to 100 times faster than CryptDataForCurrentUserDPAPI,
// generates smaller results, and is consistent on all Operating Systems
// - you can use this function over a specified variable, to cypher it in place,
// with try ... finally block to protect memory access of the plain data:
// ! constructor TMyClass.Create;
// ! ...
// ! fSecret := CryptDataForCurrentUser('Some Secret Value','appsalt',true);
// ! ...
// ! procedure TMyClass.DoSomething;
// ! var plain: RawByteString;
// ! begin
// ! plain := CryptDataForCurrentUser(fSecret,'appsalt',false);
// ! try
// ! // here plain = 'Some Secret Value'
// ! finally
// ! FillZero(plain); // safely erase uncyphered content from heap
// ! end;
// ! end;
function CryptDataForCurrentUser(const Data,AppSecret: RawByteString; Encrypt: boolean): RawByteString;
const
SHA1DIGESTSTRLEN = sizeof(TSHA1Digest)*2;
SHA256DIGESTSTRLEN = sizeof(TSHA256Digest)*2;
MD5DIGESTSTRLEN = sizeof(TMD5Digest)*2;
type
/// 32-characters ASCII string, e.g. as returned by AESBlockToShortString()
Short32 = string[32];
/// compute the hexadecial representation of an AES 16-byte block
// - returns a stack-allocated short string
function AESBlockToShortString(const block: TAESBlock): short32; overload;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecial representation of an AES 16-byte block
// - fill a stack-allocated short string
procedure AESBlockToShortString(const block: TAESBlock; out result: short32); overload;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecial representation of an AES 16-byte block
function AESBlockToString(const block: TAESBlock): RawUTF8;
/// compute the hexadecimal representation of a SHA-1 digest
function SHA1DigestToString(const D: TSHA1Digest): RawUTF8;
{$ifdef HASINLINE}inline;{$endif}
/// compute the SHA-1 digest from its hexadecimal representation
// - returns true on success (i.e. Source has the expected size and characters)
// - just a wrapper around SynCommons.HexToBin()
function SHA1StringToDigest(const Source: RawUTF8; out Dest: TSHA1Digest): boolean;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecimal representation of a SHA-256 digest
function SHA256DigestToString(const D: TSHA256Digest): RawUTF8;
{$ifdef HASINLINE}inline;{$endif}
/// compute the SHA-256 digest from its hexadecimal representation
// - returns true on success (i.e. Source has the expected size and characters)
// - just a wrapper around SynCommons.HexToBin()
function SHA256StringToDigest(const Source: RawUTF8; out Dest: TSHA256Digest): boolean;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecimal representation of a SHA-384 digest
function SHA384DigestToString(const D: TSHA384Digest): RawUTF8;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecimal representation of a SHA-512 digest
function SHA512DigestToString(const D: TSHA512Digest): RawUTF8;
{$ifdef HASINLINE}inline;{$endif}
/// compute the hexadecimal representation of a MD5 digest
function MD5DigestToString(const D: TMD5Digest): RawUTF8;
{$ifdef HASINLINE}inline;{$endif}
/// compute the MD5 digest from its hexadecimal representation
// - returns true on success (i.e. Source has the expected size and characters)
// - just a wrapper around SynCommons.HexToBin()
function MD5StringToDigest(const Source: RawUTF8; out Dest: TMD5Digest): boolean;
/// apply the XOR operation to the supplied binary buffers of 16 bytes
procedure XorBlock16(A,B: {$ifdef CPU64}PInt64Array{$else}PCardinalArray{$endif});
{$ifdef HASINLINE}inline;{$endif} overload;
/// apply the XOR operation to the supplied binary buffers of 16 bytes
procedure XorBlock16(A,B,C: {$ifdef CPU64}PInt64Array{$else}PCardinalArray{$endif});
{$ifdef HASINLINE}inline;{$endif} overload;
/// compute the HTDigest for a user and a realm, according to a supplied password
// - apache-compatible: 'agent007:download area:8364d0044ef57b3defcfa141e8f77b65'
function htdigest(const user, realm, pass: RawByteString): RawUTF8;
/// self test of Adler32 routines
function Adler32SelfTest: boolean;
/// self test of MD5 routines
function MD5SelfTest: boolean;
/// self test of SHA-1 routines
function SHA1SelfTest: boolean;
/// self test of SHA-256 routines
function SHA256SelfTest: boolean;
/// self test of AES routines
function AESSelfTest(onlytables: Boolean): boolean;
/// self test of RC4 routines
function RC4SelfTest: boolean;
/// entry point of the raw MD5 transform function - may be used for low-level use
procedure RawMd5Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-1 transform function - may be used for low-level use
procedure RawSha1Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-256 transform function - may be used for low-level use
procedure RawSha256Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-512 transform function - may be used for low-level use
procedure RawSha512Compress(var Hash; Data: pointer);
// little endian fast conversion
// - 160 bits = 5 integers
// - use fast bswap asm in x86/x64 mode
procedure bswap160(s,d: PIntegerArray);
// little endian fast conversion
// - 256 bits = 8 integers
// - use fast bswap asm in x86/x64 mode
procedure bswap256(s,d: PIntegerArray);
/// simple Adler32 implementation
// - a bit slower than Adler32Asm() version below, but shorter code size
function Adler32Pas(Adler: cardinal; p: pointer; Count: Integer): cardinal;
/// fast Adler32 implementation
// - 16-bytes-chunck unrolled asm version
function Adler32Asm(Adler: cardinal; p: pointer; Count: Integer): cardinal;
{$ifdef PUREPASCAL}{$ifdef HASINLINE}inline;{$endif}{$endif}
// - very fast XOR according to Cod - not Compression or Stream compatible
// - used in AESFull() for KeySize=32
procedure XorBlock(p: PIntegerArray; Count, Cod: integer);
/// fast and simple XOR Cypher using Index (=Position in Dest Stream)
// - Compression not compatible with this function: should be applied after
// compress (e.g. as outStream for TAESWriteStream)
// - Stream compatible (with updated Index)
// - used in AES() and TAESWriteStream
procedure XorOffset(P: PByteArray; Index,Count: integer);
/// fast XOR Cypher changing by Count value
// - Compression compatible, since the XOR value is always the same, the
// compression rate will not change a lot
procedure XorConst(p: PIntegerArray; Count: integer);
var
/// the encryption key used by CompressShaAes() global function
// - the key is global to the whole process
// - use CompressShaAesSetKey() procedure to set this Key from text
CompressShaAesKey: TSHA256Digest;
/// the AES-256 encoding class used by CompressShaAes() global function
// - use any of the implementation classes, corresponding to the chaining
// mode required - TAESECB, TAESCBC, TAESCFB, TAESOFB and TAESCTR classes to
// handle in ECB, CBC, CFB, OFB and CTR mode (including PKCS7-like padding)
// - set to the secure and efficient CFB mode by default
CompressShaAesClass: TAESAbstractClass = TAESCFB;
/// set an text-based encryption key for CompressShaAes() global function
// - will compute the key via SHA256Weak() and set CompressShaAesKey
// - the key is global to the whole process
procedure CompressShaAesSetKey(const Key: RawByteString; AesClass: TAESAbstractClass=nil);
/// encrypt data content using the AES-256/CFB algorithm, after SynLZ compression
// - as expected by THttpSocket.RegisterCompress()
// - will return 'synshaaes' as ACCEPT-ENCODING: header parameter
// - will use global CompressShaAesKey / CompressShaAesClass variables to be set
// according to the expected algorithm and Key e.g. via a call to CompressShaAesSetKey()
// - if you want to change the chaining mode, you can customize the global
// CompressShaAesClass variable to the expected TAES* class name
// - will store a hash of both cyphered and clear stream: if the
// data is corrupted during transmission, will instantly return ''
function CompressShaAes(var DataRawByteString; Compress: boolean): AnsiString;
type
/// possible return codes by IProtocol classes
TProtocolResult = (sprSuccess,
sprBadRequest, sprUnsupported, sprUnexpectedAlgorithm,
sprInvalidCertificate, sprInvalidSignature,
sprInvalidEphemeralKey, sprInvalidPublicKey, sprInvalidPrivateKey,
sprInvalidMAC);
/// perform safe communication after unilateral or mutual authentication
// - see e.g. TProtocolNone or SynEcc's TECDHEProtocolClient and
// TECDHEProtocolServer implementation classes
IProtocol = interface
['{91E3CA39-3AE2-44F4-9B8C-673AC37C1D1D}']
/// initialize the communication by exchanging some client/server information
// - expects the handshaking messages to be supplied as UTF-8 text, may be as
// base64-encoded binary - see e.g. TWebSocketProtocolBinary.ProcessHandshake
// - should return sprUnsupported if the implemented protocol does not
// expect any handshaking mechanism
// - returns sprSuccess and set something into OutData, depending on the
// current step of the handshake
// - returns an error code otherwise
function ProcessHandshake(const MsgIn: RawUTF8; out MsgOut: RawUTF8): TProtocolResult;
/// encrypt a message on one side, ready to be transmitted to the other side
// - this method should be thread-safe in the implementation class
procedure Encrypt(const aPlain: RawByteString; out aEncrypted: RawByteString);
/// decrypt a message on one side, as transmitted from the other side
// - should return sprSuccess if the
// - should return sprInvalidMAC in case of wrong aEncrypted input (e.g.
// packet corruption, MiM or Replay attacks attempts)
// - this method should be thread-safe in the implementation class
function Decrypt(const aEncrypted: RawByteString; out aPlain: RawByteString): TProtocolResult;
/// will create another instance of this communication protocol
function Clone: IProtocol;
end;
/// stores a list of IProtocol instances
IProtocolDynArray = array of IProtocol;
/// implements a fake no-encryption protocol
// - may be used for debugging purposes, or when encryption is not needed
TProtocolNone = class(TInterfacedObject, IProtocol)
public
/// initialize the communication by exchanging some client/server information
// - this method will return sprUnsupported
function ProcessHandshake(const MsgIn: RawUTF8; out MsgOut: RawUTF8): TProtocolResult;
/// encrypt a message on one side, ready to be transmitted to the other side
// - this method will return the plain text with no actual encryption
procedure Encrypt(const aPlain: RawByteString; out aEncrypted: RawByteString);
/// decrypt a message on one side, as transmitted from the other side
// - this method will return the encrypted text with no actual decryption
function Decrypt(const aEncrypted: RawByteString; out aPlain: RawByteString): TProtocolResult;
/// will create another instance of this communication protocol
function Clone: IProtocol;
end;
/// implements a secure protocol using AES encryption
// - as used e.g. by 'synopsebinary' WebSockets protocol
// - this class will maintain two TAESAbstract instances, one for encryption
// and another one for decryption, with PKCS7 padding and no MAC validation
TProtocolAES = class(TInterfacedObjectLocked, IProtocol)
protected
fAES: array[boolean] of TAESAbstract; // [false]=decrypt [true]=encrypt
public
/// initialize this encryption protocol with the given AES settings
constructor Create(aClass: TAESAbstractClass; const aKey; aKeySize: cardinal;
aIVReplayAttackCheck: TAESIVReplayAttackCheck=repCheckedIfAvailable); reintroduce; virtual;
/// will create another instance of this communication protocol
constructor CreateFrom(aAnother: TProtocolAES); reintroduce; virtual;
/// finalize the encryption
destructor Destroy; override;
/// initialize the communication by exchanging some client/server information
// - this method will return sprUnsupported, since no key negociation is involved
function ProcessHandshake(const MsgIn: RawUTF8; out MsgOut: RawUTF8): TProtocolResult;
/// encrypt a message on one side, ready to be transmitted to the other side
// - this method uses AES encryption and PKCS7 padding
procedure Encrypt(const aPlain: RawByteString; out aEncrypted: RawByteString);
/// decrypt a message on one side, as transmitted from the other side
// - this method uses AES decryption and PKCS7 padding
function Decrypt(const aEncrypted: RawByteString; out aPlain: RawByteString): TProtocolResult;
/// will create another instance of this communication protocol
function Clone: IProtocol;
end;
/// class-reference type (metaclass) of an AES secure protocol
TProtocolAESClass = class of TProtocolAES;
{$ifndef NOVARIANTS}
type
/// JWT Registered Claims, as defined in RFC 7519
// - known registered claims have a specific name and behavior, and will be
// handled automatically by TJWTAbstract
// - corresponding field names are iss,sub,aud,exp,nbf,iat,jti - as defined
// in JWT_CLAIMS_TEXT constant
// - jrcIssuer identifies the server which originated the token, e.g.
// "iss":"https://example.auth0.com/" when the token comes from Auth0 servers
// - jrcSubject is the application-specific extent which is protected by this
// JWT, e.g. an User or Resource ID, e.g. "sub":"auth0|57fe9f1bad961aa242870e"
// - jrcAudience claims that the token is valid only for one or several
// resource servers (may be a JSON string or a JSON array of strings), e.g.
// "aud":["https://myshineyfileserver.sometld"] - TJWTAbstract will check
// that the supplied "aud" field does match an expected list of identifiers
// - jrcExpirationTime contains the Unix timestamp in seconds after which
// the token must not be granted access, e.g. "exp":1477474667
// - jrcNotBefore contains the Unix timestamp in seconds before which the
// token must not be granted access, e.g. "nbf":147745438
// - jrcIssuedAt contains the Unix timestamp in seconds when the token was
// generated, e.g. "iat":1477438667
// - jrcJwtID provides a unique identifier for the JWT, to prevent any replay;
// TJWTAbstract.Compute will set an obfuscated TSynUniqueIdentifierGenerator
// hexadecimal value
TJWTClaim = (
jrcIssuer, jrcSubject, jrcAudience, jrcExpirationTime, jrcNotBefore,
jrcIssuedAt, jrcJwtID);
/// set of JWT Registered Claims, as in TJWTAbstract.Claims
TJWTClaims = set of TJWTClaim;
/// Exception raised when running JSON Web Tokens
EJWTException = class(ESynException);
/// TJWTContent.result codes after TJWTAbstract.Verify method call
TJWTResult = (jwtValid,
jwtNoToken, jwtWrongFormat, jwtInvalidAlgorithm, jwtInvalidPayload,
jwtUnexpectedClaim, jwtMissingClaim, jwtUnknownAudience,
jwtExpired, jwtNotBeforeFailed, jwtInvalidIssuedAt, jwtInvalidID,
jwtInvalidSignature);
//// set of TJWTContent.result codes
TJWTResults = set of TJWTResult;
/// JWT decoded content, as processed by TJWTAbstract
// - optionally cached in memory
TJWTContent = record
/// store latest Verify() result
result: TJWTResult;
/// set of known/registered claims, as stored in the JWT payload
claims: TJWTClaims;
/// match TJWTAbstract.Audience[] indexes for reg[jrcAudience]
audience: set of 0..15;
/// known/registered claims UTF-8 values, as stored in the JWT payload
// - e.g. reg[jrcSubject]='1234567890' and reg[jrcIssuer]='' for
// $ {"sub": "1234567890","name": "John Doe","admin": true}
reg: array[TJWTClaim] of RawUTF8;
/// custom/unregistered claim values, as stored in the JWT payload
// - registered claims will be available from reg[], not in this field
// - e.g. data.U['name']='John Doe' and data.B['admin']=true for
// $ {"sub": "1234567890","name": "John Doe","admin": true}
// but data.U['sub'] if not defined, and reg[jrcSubject]='1234567890'
data: TDocVariantData;
end;
/// pointer to a JWT decoded content, as processed by TJWTAbstract
PJWTContent = ^TJWTContent;
/// used to store a list of JWT decoded content
// - as used e.g. by TJWTAbstract cache
TJWTContentDynArray = array of TJWTContent;
/// available options for TJWTAbstract process
TJWTOption = (joHeaderParse, joAllowUnexpectedClaims, joAllowUnexpectedAudience,
joNoJwtIDGenerate, joNoJwtIDCheck, joDoubleInData);
/// store options for TJWTAbstract process
TJWTOptions = set of TJWTOption;
/// abstract parent class for implementing JSON Web Tokens
// - to represent claims securely between two parties, as defined in industry
// standard @http://tools.ietf.org/html/rfc7519
// - you should never use this abstract class directly, but e.g. TJWTHS256,
// TJWTHS384, TJWTHS512 or TJWTES256 (as defined in SynEcc.pas) inherited classes
// - for security reasons, one inherited class is implementing a single
// algorithm, as is very likely to be the case on production: you pickup one
// "alg", then you stick to it; if your server needs more than one algorithm
// for compatibility reasons, use a separate key and URI - this design will
// reduce attack surface, and fully avoid weaknesses as described in
// @https://auth0.com/blog/critical-vulnerabilities-in-json-web-token-libraries
// and @http://tools.ietf.org/html/rfc7518#section-8.5
TJWTAbstract = class(TSynPersistent)
protected
fAlgorithm: RawUTF8;
fHeader: RawUTF8;
fHeaderB64: RawUTF8;
fClaims: TJWTClaims;
fOptions: TJWTOptions;
fAudience: TRawUTF8DynArray;
fExpirationSeconds: integer;
fIDGen: TSynUniqueIdentifierGenerator;
fCacheTimeoutSeconds: integer;
fCacheResults: TJWTResults;
fCache: TSynDictionary;
procedure SetCacheTimeoutSeconds(value: integer); virtual;
function PayloadToJSON(const DataNameValue: array of const;
const Issuer, Subject, Audience: RawUTF8; NotBefore: TDateTime;
ExpirationMinutes: cardinal): RawUTF8; virtual;
procedure Parse(const Token: RawUTF8; var JWT: TJWTContent;
out headpayload: RawUTF8; out signature: RawByteString; excluded: TJWTClaims); virtual;
function CheckAgainstActualTimestamp(var JWT: TJWTContent): boolean;
// abstract methods which should be overriden by inherited classes
function ComputeSignature(const headpayload: RawUTF8): RawUTF8; virtual; abstract;
procedure CheckSignature(const headpayload: RawUTF8; const signature: RawByteString;
var JWT: TJWTContent); virtual; abstract;
public
/// initialize the JWT processing instance
// - the supplied set of claims are expected to be defined in the JWT payload
// - aAudience are the allowed values for the jrcAudience claim
// - aExpirationMinutes is the deprecation time for the jrcExpirationTime claim
// - aIDIdentifier and aIDObfuscationKey are passed to a
// TSynUniqueIdentifierGenerator instance used for jrcJwtID claim
constructor Create(const aAlgorithm: RawUTF8; aClaims: TJWTClaims;
const aAudience: array of RawUTF8; aExpirationMinutes: integer;
aIDIdentifier: TSynUniqueIdentifierProcess; aIDObfuscationKey: RawUTF8); reintroduce;
/// finalize the instance
destructor Destroy; override;
/// compute a new JWT for a given payload
// - here the data payload is supplied as Name,Value pairs - by convention,
// some registered Names (see TJWTClaim) should not be used here, and private
// claims names are expected to be short (typically 3 chars), or an URI
// - depending on the instance Claims, you should also specify associated
// Issuer, Subject, Audience and NotBefore values; expected 'exp', 'nbf',
// 'iat', 'jti' claims will also be generated and included, if needed
// - you can override the aExpirationMinutes value as defined in Create()
// - Audience is usually a single text, serialized as a JSON string, but
// if the value supplied starts with '[', it is expected to be an array
// of text values, already serialized as a JSON array of strings
// - this method is thread-safe
function Compute(const DataNameValue: array of const; const Issuer: RawUTF8='';
const Subject: RawUTF8=''; const Audience: RawUTF8=''; NotBefore: TDateTime=0;
ExpirationMinutes: integer=0; Signature: PRawUTF8=nil): RawUTF8;
/// compute a HTTP Authorization header containing a JWT for a given payload
// - just a wrapper around Compute(), returned the HTTP header value:
// $ Authorization: <HttpAuthorizationHeader>
// following the expected pattern:
// $ Authorization: Bearer <Token>
// - this method is thread-safe
function ComputeAuthorizationHeader(const DataNameValue: array of const;
const Issuer: RawUTF8=''; const Subject: RawUTF8=''; const Audience: RawUTF8='';
NotBefore: TDateTime=0; ExpirationMinutes: integer=0): RawUTF8;
/// check a JWT value, and its signature
// - will validate all expected Claims (minus ExcludedClaims optional
// parameter), and the associated signature
// - verification state is returned in JWT.result (jwtValid for a valid JWT),
// together with all parsed payload information
// - supplied JWT is transmitted e.g. in HTTP header:
// $ Authorization: Bearer <Token>
// - this method is thread-safe
procedure Verify(const Token: RawUTF8; out JWT: TJWTContent;
ExcludedClaims: TJWTClaims=[]); overload;
/// check a JWT value, and its signature
// - will validate all expected Claims, and the associated signature
// - verification state is returned as function result
// - supplied JWT is transmitted e.g. in HTTP header:
// $ Authorization: Bearer <Token>
// - this method is thread-safe
function Verify(const Token: RawUTF8): TJWTResult; overload;
/// check a HTTP Authorization header value as JWT, and its signature
// - will validate all expected Claims, and the associated signature
// - verification state is returned in JWT.result (jwtValid for a valid JWT),
// together with all parsed payload information
// - expect supplied HttpAuthorizationHeader as transmitted in HTTP header:
// $ Authorization: <HttpAuthorizationHeader>
// - this method is thread-safe
function VerifyAuthorizationHeader(const HttpAuthorizationHeader: RawUTF8;
out JWT: TJWTContent): boolean; overload;
/// in-place decoding and quick check of the JWT paylod
// - it won't check the signature, but the header's algorithm against the
// class name (use TJWTAbstract class to allow any algorithm)
// - it will decode the JWT payload and check for its expiration, and some
// mandatory fied values - you can optionally retrieve the Expiration time,
// the ending Signature, and/or the Payload decoded as TDocVariant
// - NotBeforeDelta allows to define some time frame for the "nbf" field
// - may be used on client side to quickly validate a JWT received from
// server, without knowing the exact algorithm or secret keys
class function VerifyPayload(const Token, ExpectedSubject, ExpectedIssuer,
ExpectedAudience: RawUTF8; Expiration: PUnixTime=nil; Signature: PRawUTF8=nil;
Payload: PVariant=nil; IgnoreTime: boolean=false; NotBeforeDelta: TUnixTime=15): TJWTResult;
published
/// the name of the algorithm used by this instance (e.g. 'HS256')
property Algorithm: RawUTF8 read fAlgorithm;
/// allow to tune the Verify and Compute method process
property Options: TJWTOptions read fOptions write fOptions;
/// the JWT Registered Claims, as implemented by this instance
// - Verify() method will ensure all claims are defined in the payload,
// then fill TJWTContent.reg[] with all corresponding values
property Claims: TJWTClaims read fClaims;
/// the period, in seconds, for the "exp" claim
property ExpirationSeconds: integer read fExpirationSeconds;
/// the audience string values associated with this instance
// - will be checked by Verify() method, and set in TJWTContent.audience
property Audience: TRawUTF8DynArray read fAudience;
/// delay of optional in-memory cache of Verify() TJWTContent
// - equals 0 by default, i.e. cache is disabled
// - may be useful if the signature process is very resource consumming
// (e.g. for TJWTES256 or even HMAC-SHA-256) - see also CacheResults
// - each time this property is assigned, internal cache content is flushed
property CacheTimeoutSeconds: integer read fCacheTimeoutSeconds
write SetCacheTimeoutSeconds;
/// which TJWTContent.result should be stored in in-memory cache
// - default is [jwtValid] but you may also include jwtInvalidSignature
// if signature checking uses a lot of resources
// - only used if CacheTimeoutSeconds>0
property CacheResults: TJWTResults read fCacheResults write fCacheResults;
end;
/// class-reference type (metaclass) of a JWT algorithm process
TJWTAbstractClass = class of TJWTAbstract;
/// implements JSON Web Tokens using 'none' algorithm
// - as defined in @http://tools.ietf.org/html/rfc7518 paragraph 3.6
// - you should never use this weak algorithm in production, unless your
// communication is already secured by other means, and use JWT as cookies
TJWTNone = class(TJWTAbstract)
protected
function ComputeSignature(const headpayload: RawUTF8): RawUTF8; override;
procedure CheckSignature(const headpayload: RawUTF8; const signature: RawByteString;
var JWT: TJWTContent); override;
public
/// initialize the JWT processing using the 'none' algorithm
// - the supplied set of claims are expected to be defined in the JWT payload
// - aAudience are the allowed values for the jrcAudience claim
// - aExpirationMinutes is the deprecation time for the jrcExpirationTime claim
// - aIDIdentifier and aIDObfuscationKey are passed to a
// TSynUniqueIdentifierGenerator instance used for jrcJwtID claim
constructor Create(aClaims: TJWTClaims; const aAudience: array of RawUTF8;
aExpirationMinutes: integer=0; aIDIdentifier: TSynUniqueIdentifierProcess=0;
aIDObfuscationKey: RawUTF8=''); reintroduce;
end;
/// abstract parent of JSON Web Tokens using HMAC-SHA2 or SHA-3 algorithms
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - digital signature will be processed by an internal TSynSigner instance
// - never use this abstract class, but any inherited class, or
// JWT_CLASS[].Create to instantiate a JWT process from a given algorithm
TJWTSynSignerAbstract = class(TJWTAbstract)
protected
fSignPrepared: TSynSigner;
function GetAlgo: TSignAlgo; virtual; abstract;
function ComputeSignature(const headpayload: RawUTF8): RawUTF8; override;
procedure CheckSignature(const headpayload: RawUTF8; const signature: RawByteString;
var JWT: TJWTContent); override;
public
/// initialize the JWT processing using SHA3 algorithm
// - the supplied set of claims are expected to be defined in the JWT payload
// - the supplied secret text will be used to compute the digital signature,
// directly if aSecretPBKDF2Rounds=0, or via PBKDF2 iterative key derivation
// if some number of rounds were specified
// - aAudience are the allowed values for the jrcAudience claim
// - aExpirationMinutes is the deprecation time for the jrcExpirationTime claim
// - aIDIdentifier and aIDObfuscationKey are passed to a
// TSynUniqueIdentifierGenerator instance used for jrcJwtID claim
// - optionally return the PBKDF2 derivated key for aSecretPBKDF2Rounds>0
constructor Create(const aSecret: RawUTF8; aSecretPBKDF2Rounds: integer;
aClaims: TJWTClaims; const aAudience: array of RawUTF8; aExpirationMinutes: integer=0;
aIDIdentifier: TSynUniqueIdentifierProcess=0; aIDObfuscationKey: RawUTF8='';
aPBKDF2Secret: PHash512Rec=nil); reintroduce;
/// finalize the instance
destructor Destroy; override;
/// the digital signature size, in byte
property SignatureSize: integer read fSignPrepared.fSignatureSize;
/// the TSynSigner raw algorithm used for digital signature
property SignatureAlgo: TSignAlgo read fSignPrepared.fAlgo;
/// low-level read access to the internal signature structure
property SignPrepared: TSynSigner read fSignPrepared;
end;
/// meta-class for TJWTSynSignerAbstract creations
TJWTSynSignerAbstractClass = class of TJWTSynSignerAbstract;
/// implements JSON Web Tokens using 'HS256' (HMAC SHA-256) algorithm
// - as defined in @http://tools.ietf.org/html/rfc7518 paragraph 3.2
// - our HMAC SHA-256 implementation used is thread safe, and very fast
// (x86: 3us, x64: 2.5us) so cache is not needed
// - resulting signature size will be of 256 bits
TJWTHS256 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// implements JSON Web Tokens using 'HS384' (HMAC SHA-384) algorithm
// - as defined in @http://tools.ietf.org/html/rfc7518 paragraph 3.2
// - our HMAC SHA-384 implementation used is thread safe, and very fast
// even on x86 (if the CPU supports SSE3 opcodes)
// - resulting signature size will be of 384 bits
TJWTHS384 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// implements JSON Web Tokens using 'HS512' (HMAC SHA-512) algorithm
// - as defined in @http://tools.ietf.org/html/rfc7518 paragraph 3.2
// - our HMAC SHA-512 implementation used is thread safe, and very fast
// even on x86 (if the CPU supports SSE3 opcodes)
// - resulting signature size will be of 512 bits
TJWTHS512 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-224 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 224 bits
TJWTS3224 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-256 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 256 bits
TJWTS3256 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-384 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 384 bits
TJWTS3384 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-512 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 512 bits
TJWTS3512 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-SHAKE128 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 256 bits
TJWTS3S128 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
/// experimental JSON Web Tokens using SHA3-SHAKE256 algorithm
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// but could be used as a safer (and sometimes faster) alternative to HMAC-SHA2
// - resulting signature size will be of 512 bits
TJWTS3S256 = class(TJWTSynSignerAbstract)
protected
function GetAlgo: TSignAlgo; override;
end;
const
/// the text field names of the registerd claims, as defined by RFC 7519
// - see TJWTClaim enumeration and TJWTClaims set
// - RFC standard expects those to be case-sensitive
JWT_CLAIMS_TEXT: array[TJWTClaim] of RawUTF8 = (
'iss','sub','aud','exp','nbf','iat','jti');
/// how TJWTSynSignerAbstract algorithms are identified in the JWT
// - SHA-1 will fallback to HS256 (since there will never be SHA-1 support)
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
JWT_TEXT: array[TSignAlgo] of RawUTF8 = (
'HS256','HS256','HS384','HS512','S3224','S3256','S3384','S3512','S3S128','S3S256');
/// able to instantiate any of the TJWTSynSignerAbstract instance expected
// - SHA-1 will fallback to TJWTHS256 (since SHA-1 will never be supported)
// - SHA-3 is not yet officially defined in @http://tools.ietf.org/html/rfc7518
// - typical use is the following:
// ! result := JWT_CLASS[algo].Create(master, round, claims, [], expirationMinutes);
JWT_CLASS: array[TSignAlgo] of TJWTSynSignerAbstractClass = (
TJWTHS256, TJWTHS256, TJWTHS384, TJWTHS512,
TJWTS3224, TJWTS3256, TJWTS3384, TJWTS3512, TJWTS3S128, TJWTS3S256);
function ToText(res: TJWTResult): PShortString; overload;
function ToCaption(res: TJWTResult): string; overload;
function ToText(claim: TJWTClaim): PShortString; overload;
function ToText(claims: TJWTClaims): ShortString; overload;
{$endif NOVARIANTS}
function ToText(chk: TAESIVReplayAttackCheck): PShortString; overload;
function ToText(res: TProtocolResult): PShortString; overload;
function ToText(algo: TSignAlgo): PShortString; overload;
function ToText(algo: THashAlgo): PShortString; overload;
function ToText(algo: TSHA3Algo): PShortString; overload;
implementation
{$ifndef NOVARIANTS}
uses
Variants;
{$endif}
function ToText(res: TProtocolResult): PShortString;
begin
result := GetEnumName(TypeInfo(TProtocolResult),ord(res));
end;
function ToText(chk: TAESIVReplayAttackCheck): PShortString;
begin
result := GetEnumName(TypeInfo(TAESIVReplayAttackCheck),ord(chk));
end;
function ToText(algo: TSignAlgo): PShortString;
begin
result := GetEnumName(TypeInfo(TSignAlgo),ord(algo));
end;
function ToText(algo: THashAlgo): PShortString;
begin
result := GetEnumName(TypeInfo(THashAlgo),ord(algo));
end;
function ToText(algo: TSHA3Algo): PShortString;
begin
result := GetEnumName(TypeInfo(TSHA3Algo),ord(algo));
end;
{$ifdef CPU64}
procedure XorBlock16(A,B: PInt64Array);
begin
A[0] := A[0] xor B[0];
A[1] := A[1] xor B[1];
end;
procedure XorBlock16(A,B,C: PInt64Array);
begin
B[0] := A[0] xor C[0];
B[1] := A[1] xor C[1];
end;
{$else}
procedure XorBlock16(A,B: PCardinalArray);
begin
A[0] := A[0] xor B[0];
A[1] := A[1] xor B[1];
A[2] := A[2] xor B[2];
A[3] := A[3] xor B[3];
end;
procedure XorBlock16(A,B,C: PCardinalArray);
begin
B[0] := A[0] xor C[0];
B[1] := A[1] xor C[1];
B[2] := A[2] xor C[2];
B[3] := A[3] xor C[3];
end;
{$endif}
procedure AESBlockToShortString(const block: TAESBlock; out result: short32);
begin
result[0] := #32;
SynCommons.BinToHex(@block,@result[1],16);
end;
function AESBlockToShortString(const block: TAESBlock): short32;
begin
AESBlockToShortString(block,result);
end;
function AESBlockToString(const block: TAESBlock): RawUTF8;
begin
FastSetString(result,nil,32);
SynCommons.BinToHex(@block,pointer(result),16);
end;
function MD5DigestToString(const D: TMD5Digest): RawUTF8;
begin
BinToHexLower(@D,sizeof(D),result);
end;
function MD5StringToDigest(const Source: RawUTF8; out Dest: TMD5Digest): boolean;
begin
result := SynCommons.HexToBin(pointer(Source), @Dest, sizeof(Dest));
end;
function SHA1DigestToString(const D: TSHA1Digest): RawUTF8;
begin
BinToHexLower(@D,sizeof(D),result);
end;
function SHA1StringToDigest(const Source: RawUTF8; out Dest: TSHA1Digest): boolean;
begin
result := SynCommons.HexToBin(pointer(Source), @Dest, sizeof(Dest));
end;
function SHA256DigestToString(const D: TSHA256Digest): RawUTF8;
begin
BinToHexLower(@D,sizeof(D),result);
end;
function SHA256StringToDigest(const Source: RawUTF8; out Dest: TSHA256Digest): boolean;
begin
result := SynCommons.HexToBin(pointer(Source), @Dest, sizeof(Dest));
end;
function SHA512DigestToString(const D: TSHA512Digest): RawUTF8;
begin
BinToHexLower(@D, sizeof(D), result);
end;
function SHA384DigestToString(const D: TSHA384Digest): RawUTF8;
begin
BinToHexLower(@D, sizeof(D), result);
end;
{$ifdef USEPADLOCK}
const
AES_SUCCEEDED = 0;
KEY_128BITS = 0;
KEY_192BITS = 1;
KEY_256BITS = 2;
ACE_AES_ECB = 0;
ACE_AES_CBC = 1;
{$ifdef USEPADLOCKDLL}
type
tpadlock_phe_available = function: boolean; cdecl;
tpadlock_phe_sha = function(
buffer: pointer; nbytes: integer; var Digest): integer; cdecl;
tpadlock_ace_available = function: boolean; cdecl;
tpadlock_aes_begin = function: pointer; cdecl;
tpadlock_aes_setkey = function(
ctx: pointer; const key; key_len: integer): integer; cdecl;
tpadlock_aes_setmodeiv = function(
ctx: pointer; mode: integer; var iv): integer; cdecl;
tpadlock_aes_encrypt = function(
ctx, bIn, bOut: pointer; nbytes: integer): integer; cdecl;
tpadlock_aes_decrypt = function(
ctx, bIn, bOut: pointer; nbytes: integer): integer; cdecl;
tpadlock_aes_close = function(
ctx: pointer): integer; cdecl;
var
padlock_phe_available: tpadlock_phe_available = nil;
padlock_phe_sha1: tpadlock_phe_sha = nil;
padlock_phe_sha256: tpadlock_phe_sha = nil;
padlock_ace_available: tpadlock_ace_available = nil;
padlock_aes_begin: tpadlock_aes_begin = nil;
padlock_aes_setkey: tpadlock_aes_setkey = nil;
padlock_aes_setmodeiv: tpadlock_aes_setmodeiv = nil;
padlock_aes_encrypt: tpadlock_aes_encrypt = nil;
padlock_aes_decrypt: tpadlock_aes_decrypt = nil;
padlock_aes_close: tpadlock_aes_close = nil;
{$ifdef MSWINDOWS}
PadLockLibHandle: THandle = 0;
{$else} // Linux:
PadLockLibHandle: HMODULE = 0;
{$endif}
procedure PadlockInit;
begin
{$ifdef MSWINDOWS}
PadLockLibHandle := LoadLibrary('LibPadlock');
{$else} // Linux:
PadLockLibHandle := LoadLibrary('libvia_padlock.so');
if PadLockLibHandle=0 then
PadLockLibHandle := LoadLibrary('libvia_padlock.so.1.0.0');
{$endif}
if PadLockLibHandle=0 then
exit;
padlock_phe_available := GetProcAddress(PadLockLibHandle,'padlock_phe_available');
padlock_phe_sha1 := GetProcAddress(PadLockLibHandle,'padlock_phe_sha1');
padlock_phe_sha256 := GetProcAddress(PadLockLibHandle,'padlock_phe_sha256');
padlock_ace_available := GetProcAddress(PadLockLibHandle,'padlock_ace_available');
padlock_aes_begin := GetProcAddress(PadLockLibHandle,'padlock_aes_begin');
padlock_aes_setkey := GetProcAddress(PadLockLibHandle,'padlock_aes_setkey');
padlock_aes_setmodeiv := GetProcAddress(PadLockLibHandle,'padlock_aes_setmodeiv');
padlock_aes_encrypt := GetProcAddress(PadLockLibHandle,'padlock_aes_encrypt');
padlock_aes_decrypt := GetProcAddress(PadLockLibHandle,'padlock_aes_decrypt');
padlock_aes_close := GetProcAddress(PadLockLibHandle,'padlock_aes_close');
if @padlock_phe_available=nil then exit;
if @padlock_phe_sha1=nil then exit;
if @padlock_phe_sha256=nil then exit;
if @padlock_ace_available=nil then exit;
if @padlock_aes_begin=nil then exit;
if @padlock_aes_setkey=nil then exit;
if @padlock_aes_setmodeiv=nil then exit;
if @padlock_aes_encrypt=nil then exit;
if @padlock_aes_decrypt=nil then exit;
if @padlock_aes_close=nil then exit;
if padlock_phe_available and padlock_ace_available then
padlock_available := true;
end;
{$else} // not USEPADLOCKDLL:
{$ifdef MSWINDOWS}
{$L padlock.obj}
{$L padlock_sha.obj}
{$L padlock_aes.obj}
{$else}
{$L padlock.o}
{$L padlock_sha.o}
{$L padlock_aes.o}
{$endif}
function memcpy(dest, src: Pointer; count: integer): Pointer; cdecl;
begin
MoveFast(src^, dest^, count);
Result := dest;
end;
function memset(dest: Pointer; val: Integer; count: integer): Pointer; cdecl;
begin
FillcharFast(dest^, count, val);
Result := dest;
end;
function malloc(size: integer): Pointer; cdecl;
begin
GetMem(Result, size);
end;
procedure free(pBlock: Pointer); cdecl;
begin
FreeMem(pBlock);
end;
function printf(format:PAnsiChar; args:array of const): PAnsiChar; cdecl;
begin
result := format;
// called on error -> do nothing
end;
{ this .o files have been generated from the sdk sources with
gcc-2.95 -c -O2 padlock*.c -I../include
}
function padlock_phe_available: boolean; cdecl; external;
function padlock_phe_sha1(buf: pointer; nbytes: integer; var Digest): integer; cdecl; external;
function padlock_phe_sha256(buf: pointer; nbytes: integer; var Digest): integer; cdecl; external;
function padlock_ace_available: boolean; cdecl; external;
function padlock_aes_begin: pointer; cdecl; external;
function padlock_aes_setkey(ctx: pointer; const key; key_len: integer): integer; cdecl; external;
function padlock_aes_setmodeiv(ctx: pointer; mode: integer; var iv): integer; cdecl; external;
function padlock_aes_encrypt(ctx, bIn, bOut: pointer; nbytes: integer): integer; cdecl; external;
function padlock_aes_decrypt(ctx, bIn, bOut: pointer; nbytes: integer): integer; cdecl; external;
function padlock_aes_close(ctx: pointer): integer; cdecl; external;
procedure PadlockInit;
begin
if padlock_phe_available and padlock_ace_available then
padlock_available := true;
{$ifdef PADLOCKDEBUG}if padlock_available then writeln('PADLOCK available'); {$endif}
end;
{$endif USEPADLOCKDLL}
{$endif USEPADLOCK}
procedure XorMemoryPtrInt(dest, source: PPtrIntArray; count: integer);
{$ifdef HASINLINE}inline;{$endif}
{$ifdef FPC}
begin
while count>0 do begin
dec(count);
PPtrInt(dest)^ := PPtrInt(dest)^ xor PPtrInt(source)^;
inc(PPtrInt(dest));
inc(PPtrInt(source));
end;
end;
{$else}
var i: integer;
begin
for i := 0 to count-1 do
dest^[i] := dest^[i] xor source^[i];
end;
{$endif}
const
AESMaxRounds = 14;
type
TKeyArray = packed array[0..AESMaxRounds] of TAESBlock;
/// low-level content of TAES.Context (AESContextSize bytes)
// - is defined privately in the implementation section
// - don't change the structure below: it is fixed in the asm code
// -> use PUREPASCAL if you really have to change it
TAESContext = packed record
RK: TKeyArray; // Key (encr. or decr.)
IV: TAESBlock; // IV or CTR
buf: TAESBlock; // Work buffer
{$ifdef USEPADLOCK}
ViaCtx: pointer; // padlock_*() context
{$endif}
DoBlock: procedure(const ctxt, source, dest); // main AES function
{$ifdef USEAESNI32}AesNi32: pointer;{$endif}
Initialized: boolean;
Rounds: byte; // Number of rounds
KeyBits: word; // Number of bits in key (128/192/256)
end;
// helper types for better code generation
type
TWA4 = TBlock128; // AES block as array of cardinal
TAWk = packed array[0..4*(AESMaxRounds+1)-1] of cardinal; // Key as array of cardinal
PWA4 = ^TWA4;
PAWk = ^TAWk;
const
RCon: array[0..9] of cardinal = ($01,$02,$04,$08,$10,$20,$40,$80,$1b,$36);
// AES computed tables - don't change the order below!
var
Td0, Td1, Td2, Td3, Te0, Te1, Te2, Te3: array[byte] of cardinal;
SBox, InvSBox: array[byte] of byte;
Xor32Byte: TByteArray absolute Td0; // 2^13=$2000=8192 bytes of XOR tables ;)
procedure ComputeAesStaticTables;
var i, x,y: byte;
pow,log: array[byte] of byte;
c: cardinal;
begin // 835 bytes of code to compute 4.5 KB of tables
x := 1;
for i := 0 to 255 do begin
pow[i] := x;
log[x] := i;
if x and $80<>0 then
x := x xor (x shl 1) xor $1B else
x := x xor (x shl 1);
end;
SBox[0] := $63;
InvSBox[$63] := 0;
for i := 1 to 255 do begin
x := pow[255-log[i]]; y := (x shl 1)+(x shr 7);
x := x xor y; y := (y shl 1)+(y shr 7);
x := x xor y; y := (y shl 1)+(y shr 7);
x := x xor y; y := (y shl 1)+(y shr 7);
x := x xor y xor $63;
SBox[i] := x;
InvSBox[x] := i;
end;
for i := 0 to 255 do begin
x := SBox[i];
y := x shl 1;
if x and $80<>0 then
y := y xor $1B;
Te0[i] := y+x shl 8+x shl 16+(y xor x)shl 24;
Te1[i] := Te0[i] shl 8+Te0[i] shr 24;
Te2[i] := Te1[i] shl 8+Te1[i] shr 24;
Te3[i] := Te2[i] shl 8+Te2[i] shr 24;
x := InvSBox[i];
if x=0 then
continue;
c := log[x]; // Td0[c] = Si[c].[0e,09,0d,0b] -> e.g. log[$0e]=223 below
Td0[i] := pow[(c+223)mod 255]+pow[(c+199)mod 255]shl 8+
pow[(c+238)mod 255]shl 16+pow[(c+104)mod 255]shl 24;
Td1[i] := Td0[i] shl 8+Td0[i] shr 24;
Td2[i] := Td1[i] shl 8+Td1[i] shr 24;
Td3[i] := Td2[i] shl 8+Td2[i] shr 24;
end;
end;
type
TSHAHash = packed record
A,B,C,D,E,F,G,H: cardinal; // will use A..E with TSHA1, A..H with TSHA256
end;
TSHAContext = packed record
// Working hash (TSHA256.Init expect this field to be the first)
Hash: TSHAHash;
// 64bit msg length
MLen: QWord;
// Block buffer
Buffer: array[0..63] of byte;
// Index in buffer
Index : integer;
end;
{$ifdef CPUINTEL}
{$ifdef CPU32}
procedure bswap256(s,d: PIntegerArray); {$ifdef FPC}nostackframe; assembler;{$endif}
asm
push ebx
mov ecx, eax // ecx=s, edx=d
mov eax, [ecx]
mov ebx, [ecx + 4]
bswap eax
bswap ebx
mov [edx], eax
mov [edx + 4], ebx
mov eax, [ecx + 8]
mov ebx, [ecx + 12]
bswap eax
bswap ebx
mov [edx + 8], eax
mov [edx + 12], ebx
mov eax, [ecx + 16]
mov ebx, [ecx + 20]
bswap eax
bswap ebx
mov [edx + 16], eax
mov [edx + 20], ebx
mov eax, [ecx + 24]
mov ebx, [ecx + 28]
bswap eax
bswap ebx
mov [edx + 24], eax
mov [edx + 28], ebx
pop ebx
end;
procedure bswap160(s,d: PIntegerArray); {$ifdef FPC}nostackframe; assembler;{$endif}
asm
push ebx
mov ecx, eax // ecx=s, edx=d
mov eax, [ecx]
mov ebx, [ecx + 4]
bswap eax
bswap ebx
mov [edx], eax
mov [edx + 4], ebx
mov eax, [ecx + 8]
mov ebx, [ecx + 12]
bswap eax
bswap ebx
mov [edx + 8], eax
mov [edx + 12], ebx
mov eax, [ecx + 16]
bswap eax
mov [edx + 16], eax
pop ebx
end;
function gf2_multiply(x,y,m: PtrUInt): PtrUInt; {$ifdef FPC}nostackframe; assembler;{$endif}
asm // eax=x edx=y ecx=m
push esi
push edi
push ebx
push ebp
mov ebp, 32
mov ebx, eax
and eax, 1
cmovne eax, edx
@s: mov esi, eax
mov edi, ecx
shr esi, 1
xor edi, esi
test al, 1
mov eax, esi
cmovne eax, edi
shr ebx, 1
mov esi, eax
xor esi, edx
test bl, 1
cmovne eax, esi
dec ebp
jne @s
pop ebp
pop ebx
pop edi
pop esi
end;
{$endif CPU32}
{$ifdef CPU64}
procedure bswap256(s,d: PIntegerArray); {$ifdef FPC} nostackframe; assembler;
asm {$else} asm .noframe {$endif}
mov eax, dword ptr[s]
mov r8d, dword ptr[s + 4]
mov r9d, dword ptr[s + 8]
mov r10d, dword ptr[s + 12]
bswap eax
bswap r8d
bswap r9d
bswap r10d
mov dword ptr[d], eax
mov dword ptr[d + 4], r8d
mov dword ptr[d + 8], r9d
mov dword ptr[d + 12], r10d
mov eax, dword ptr[s + 16]
mov r8d, dword ptr[s + 20]
mov r9d, dword ptr[s + 24]
mov r10d, dword ptr[s + 28]
bswap eax
bswap r8d
bswap r9d
bswap r10d
mov dword ptr[d + 16], eax
mov dword ptr[d + 20], r8d
mov dword ptr[d + 24], r9d
mov dword ptr[d + 28], r10d
end;
procedure bswap160(s,d: PIntegerArray); {$ifdef FPC} nostackframe; assembler;
asm {$else} asm .noframe {$endif}
mov eax, dword ptr[s]
mov r8d, dword ptr[s + 4]
mov r9d, dword ptr[s + 8]
mov r10d, dword ptr[s + 12]
bswap eax
bswap r8d
bswap r9d
bswap r10d
mov dword ptr[d], eax
mov dword ptr[d + 4], r8d
mov dword ptr[d + 8], r9d
mov dword ptr[d + 12], r10d
mov eax, dword ptr[s + 16]
bswap eax
mov dword ptr[d + 16], eax
end;
// see http://nicst.de/crc.pdf
function gf2_multiply(x,y,m,bits: PtrUInt): PtrUInt; {$ifdef FPC} nostackframe; assembler;
asm {$else} asm .noframe {$endif}
mov rax, x
and rax, 1
cmovne rax, y
@s: mov r10, rax
mov r11, m
shr r10, 1
xor r11, r10
test al, 1
mov rax, r10
cmovne rax, r11
shr x, 1
mov r10, rax
xor r10, y
{$ifdef win64}
test cl, 1
{$else}
test dil, 1
{$endif}
cmovne rax, r10
dec bits
jne @s
end;
{$endif CPU64}
{$else not CPUINTEL}
procedure bswap256(s,d: PIntegerArray);
begin
{$ifdef FPC} // use fast platform-specific function
d[0] := SwapEndian(s[0]);
d[1] := SwapEndian(s[1]);
d[2] := SwapEndian(s[2]);
d[3] := SwapEndian(s[3]);
d[4] := SwapEndian(s[4]);
d[5] := SwapEndian(s[5]);
d[6] := SwapEndian(s[6]);
d[7] := SwapEndian(s[7]);
{$else}
d[0] := bswap32(s[0]);
d[1] := bswap32(s[1]);
d[2] := bswap32(s[2]);
d[3] := bswap32(s[3]);
d[4] := bswap32(s[4]);
d[5] := bswap32(s[5]);
d[6] := bswap32(s[6]);
d[7] := bswap32(s[7]);
{$endif FPC}
end;
procedure bswap160(s,d: PIntegerArray);
begin
{$ifdef FPC} // use fast platform-specific function
d[0] := SwapEndian(s[0]);
d[1] := SwapEndian(s[1]);
d[2] := SwapEndian(s[2]);
d[3] := SwapEndian(s[3]);
d[4] := SwapEndian(s[4]);
{$else}
d[0] := bswap32(s[0]);
d[1] := bswap32(s[1]);
d[2] := bswap32(s[2]);
d[3] := bswap32(s[3]);
d[4] := bswap32(s[4]);
{$endif FPC}
end;
{$endif CPUINTEL}
function SHA256SelfTest: boolean;
function SingleTest(const s: RawByteString; const TDig: TSHA256Digest): boolean;
var SHA: TSHA256;
Digest: TSHA256Digest;
i: integer;
begin
// 1. Hash complete RawByteString
SHA.Full(pointer(s),length(s),Digest);
result := IsEqual(Digest,TDig);
if not result then exit;
// 2. one update call for all chars
SHA.Init;
for i := 1 to length(s) do
SHA.Update(@s[i],1);
SHA.Final(Digest);
result := IsEqual(Digest,TDig);
// 3. test consistency with Padlock engine down results
{$ifdef USEPADLOCK}
if not result or not padlock_available then exit;
padlock_available := false; // force PadLock engine down
SHA.Full(pointer(s),length(s),Digest);
result := IsEqual(Digest,TDig);
{$ifdef PADLOCKDEBUG} write('=padlock '); {$endif}
padlock_available := true;
{$endif}
end;
var Digest: TSHA256Digest;
const
D1: TSHA256Digest = ($ba,$78,$16,$bf,$8f,$01,$cf,$ea,$41,$41,$40,$de,$5d,$ae,$22,$23,
$b0,$03,$61,$a3,$96,$17,$7a,$9c,$b4,$10,$ff,$61,$f2,$00,$15,$ad);
D2: TSHA256Digest = ($24,$8d,$6a,$61,$d2,$06,$38,$b8,$e5,$c0,$26,$93,$0c,$3e,$60,$39,
$a3,$3c,$e4,$59,$64,$ff,$21,$67,$f6,$ec,$ed,$d4,$19,$db,$06,$c1);
D3: TSHA256Digest =
($94,$E4,$A9,$D9,$05,$31,$23,$1D,$BE,$D8,$7E,$D2,$E4,$F3,$5E,$4A,
$0B,$F4,$B3,$BC,$CE,$EB,$17,$16,$D5,$77,$B1,$E0,$8B,$A9,$BA,$A3);
begin
// result := true; exit;
result := SingleTest('abc', D1) and
SingleTest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', D2);
if not result then exit;
SHA256Weak('lagrangehommage',Digest); // test with len=256>64
result := IsEqual(Digest,D3);
{$ifdef CPU64}
{$ifdef CPUINTEL}
if cfSSE41 in CpuFeatures then begin
Exclude(CpuFeatures,cfSSE41);
result := result and SingleTest('abc', D1) and
SingleTest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq', D2);
Include(CpuFeatures,cfSSE41);
end;
{$endif}
{$endif}
end;
function MD5(const s: RawByteString): RawUTF8;
var MD5: TMD5;
D: TMD5Digest;
begin
MD5.Full(pointer(s),Length(s),D);
result := MD5DigestToString(D);
FillZero(D);
end;
function SHA1(const s: RawByteString): RawUTF8;
var SHA: TSHA1;
Digest: TSHA1Digest;
begin
SHA.Full(pointer(s),length(s),Digest);
result := SHA1DigestToString(Digest);
FillZero(Digest);
end;
function SHA384(const s: RawByteString): RawUTF8;
var SHA: TSHA384;
Digest: TSHA384Digest;
begin
SHA.Full(pointer(s),length(s),Digest);
result := SHA384DigestToString(Digest);
FillZero(Digest);
end;
function SHA512(const s: RawByteString): RawUTF8;
var SHA: TSHA512;
Digest: TSHA512Digest;
begin
SHA.Full(pointer(s),length(s),Digest);
result := SHA512DigestToString(Digest);
FillZero(Digest);
end;
{ THMAC_SHA1 }
procedure THMAC_SHA1.Init(key: pointer; keylen: integer);
var i: integer;
k0,k0xorIpad: THash512Rec;
begin
FillZero(k0.b);
if keylen>sizeof(k0) then
sha.Full(key,keylen,k0.b160) else
MoveFast(key^,k0,keylen);
for i := 0 to 15 do
k0xorIpad.c[i] := k0.c[i] xor $36363636;
for i := 0 to 15 do
step7data.c[i] := k0.c[i] xor $5c5c5c5c;
sha.Init;
sha.Update(@k0xorIpad,sizeof(k0xorIpad));
FillZero(k0.b);
FillZero(k0xorIpad.b);
end;
procedure THMAC_SHA1.Update(msg: pointer; msglen: integer);
begin
sha.Update(msg,msglen);
end;
procedure THMAC_SHA1.Done(out result: TSHA1Digest; NoInit: boolean);
begin
sha.Final(result);
sha.Update(@step7data,sizeof(step7data));
sha.Update(@result,sizeof(result));
sha.Final(result,NoInit);
if not NoInit then
FillZero(step7data.b);
end;
procedure THMAC_SHA1.Done(out result: RawUTF8; NoInit: boolean);
var res: TSHA1Digest;
begin
Done(res,NoInit);
result := SHA1DigestToString(res);
if not NoInit then
FillZero(res);
end;
procedure THMAC_SHA1.Compute(msg: pointer; msglen: integer; out result: TSHA1Digest);
var temp: THMAC_SHA1;
begin
temp := self; // thread-safe copy
temp.Update(msg,msglen);
temp.Done(result);
end;
procedure HMAC_SHA1(key,msg: pointer; keylen,msglen: integer; out result: TSHA1Digest);
var mac: THMAC_SHA1;
begin
mac.Init(key,keylen);
mac.Update(msg,msglen);
mac.Done(result);
end;
procedure HMAC_SHA1(const key,msg: RawByteString; out result: TSHA1Digest);
begin
HMAC_SHA1(pointer(key),pointer(msg),length(key),length(msg),result);
end;
procedure HMAC_SHA1(const key: TSHA1Digest; const msg: RawByteString; out result: TSHA1Digest);
begin
HMAC_SHA1(@key,pointer(msg),sizeof(key),length(msg),result);
end;
procedure PBKDF2_HMAC_SHA1(const password,salt: RawByteString; count: Integer;
out result: TSHA1Digest);
var i: integer;
tmp: TSHA1Digest;
mac: THMAC_SHA1;
first: THMAC_SHA1;
begin
HMAC_SHA1(password,salt+#0#0#0#1,result);
if count<2 then
exit;
tmp := result;
first.Init(pointer(password),length(password));
for i := 2 to count do begin
mac := first; // re-use the very same SHA context for best performance
mac.sha.Update(@tmp,sizeof(tmp));
mac.Done(tmp,true);
XorMemory(@result,@tmp,sizeof(result));
end;
FillcharFast(mac,sizeof(mac),0);
FillcharFast(first,sizeof(first),0);
FillZero(tmp);
end;
{ THMAC_SHA256 }
procedure THMAC_SHA256.Init(key: pointer; keylen: integer);
var i: integer;
k0,k0xorIpad: THash512Rec;
begin
FillZero(k0.b);
if keylen>sizeof(k0) then
sha.Full(key,keylen,k0.Lo) else
MoveFast(key^,k0,keylen);
for i := 0 to 15 do
k0xorIpad.c[i] := k0.c[i] xor $36363636;
for i := 0 to 15 do
step7data.c[i] := k0.c[i] xor $5c5c5c5c;
sha.Init;
sha.Update(@k0xorIpad,sizeof(k0xorIpad));
FillZero(k0.b);
FillZero(k0xorIpad.b);
end;
procedure THMAC_SHA256.Update(msg: pointer; msglen: integer);
begin
sha.Update(msg,msglen);
end;
procedure THMAC_SHA256.Update(const msg: THash128);
begin
sha.Update(@msg,sizeof(msg));
end;
procedure THMAC_SHA256.Update(const msg: THash256);
begin
sha.Update(@msg,sizeof(msg));
end;
procedure THMAC_SHA256.Update(const msg: RawByteString);
begin
sha.Update(pointer(msg),length(msg));
end;
procedure THMAC_SHA256.Done(out result: TSHA256Digest; NoInit: boolean);
begin
sha.Final(result);
sha.Update(@step7data,sizeof(step7data));
sha.Update(@result,sizeof(result));
sha.Final(result,NoInit);
if not NoInit then
FillZero(step7data.b);
end;
procedure THMAC_SHA256.Done(out result: RawUTF8; NoInit: boolean);
var res: THash256;
begin
Done(res,NoInit);
result := SHA256DigestToString(res);
if not NoInit then
FillZero(res);
end;
procedure THMAC_SHA256.Compute(msg: pointer; msglen: integer; out result: TSHA256Digest);
var temp: THMAC_SHA256;
begin
temp := self; // thread-safe copy
temp.Update(msg,msglen);
temp.Done(result);
end;
procedure HMAC_SHA256(key,msg: pointer; keylen,msglen: integer; out result: TSHA256Digest);
var mac: THMAC_SHA256;
begin
mac.Init(key,keylen);
mac.Update(msg,msglen);
mac.Done(result);
end;
procedure HMAC_SHA256(const key,msg: RawByteString; out result: TSHA256Digest);
begin
HMAC_SHA256(pointer(key),pointer(msg),length(key),length(msg),result);
end;
procedure HMAC_SHA256(const key: TSHA256Digest; const msg: RawByteString; out result: TSHA256Digest);
begin
HMAC_SHA256(@key,pointer(msg),sizeof(key),length(msg),result);
end;
procedure PBKDF2_HMAC_SHA256(const password,salt: RawByteString; count: Integer;
out result: TSHA256Digest; const saltdefault: RawByteString);
var i: integer;
tmp: TSHA256Digest;
mac: THMAC_SHA256;
first: THMAC_SHA256;
begin
if salt='' then
HMAC_SHA256(password,saltdefault+#0#0#0#1,result) else
HMAC_SHA256(password,salt+#0#0#0#1,result);
if count<2 then
exit;
tmp := result;
first.Init(pointer(password),length(password));
for i := 2 to count do begin
mac := first; // re-use the very same SHA context for best performance
mac.sha.Update(@tmp,sizeof(tmp));
mac.Done(tmp,true);
XorMemoryPtrInt(@result,@tmp,sizeof(result) shr {$ifdef CPU32}2{$else}3{$endif});
end;
FillcharFast(first,sizeof(first),0);
FillcharFast(mac,sizeof(mac),0);
FillZero(tmp);
end;
procedure PBKDF2_HMAC_SHA256(const password,salt: RawByteString; count: Integer;
var result: THash256DynArray; const saltdefault: RawByteString);
var n,i: integer;
iter: RawByteString;
tmp: TSHA256Digest;
mac: THMAC_SHA256;
first: THMAC_SHA256;
begin
first.Init(pointer(password),length(password));
SetLength(iter,sizeof(integer));
for n := 0 to high(result) do begin
PInteger(iter)^ := bswap32(n+1); // U1 = PRF(Password, Salt || INT_32_BE(i))
if salt='' then
HMAC_SHA256(password,saltdefault+iter,result[n]) else
HMAC_SHA256(password,salt+iter,result[n]);
tmp := result[n];
for i := 2 to count do begin
mac := first; // re-use the very same SHA context for best performance
mac.sha.Update(@tmp,sizeof(tmp));
mac.Done(tmp,true);
XorMemoryPtrInt(@result[n],@tmp,sizeof(result[n]) shr {$ifdef CPU32}2{$else}3{$endif});
end;
end;
FillZero(tmp);
FillcharFast(mac,sizeof(mac),0);
FillcharFast(first,sizeof(first),0);
end;
function SHA256(const s: RawByteString): RawUTF8;
var SHA: TSHA256;
Digest: TSHA256Digest;
begin
SHA.Full(pointer(s),length(s),Digest);
result := SHA256DigestToString(Digest);
FillZero(Digest);
end;
function SHA256(Data: pointer; Len: integer): RawUTF8;
var SHA: TSHA256;
Digest: TSHA256Digest;
begin
SHA.Full(Data,Len,Digest);
result := SHA256DigestToString(Digest);
FillZero(Digest);
end;
function SHA256Digest(Data: pointer; Len: integer): TSHA256Digest;
var SHA: TSHA256;
begin
SHA.Full(Data,Len,result);
end;
function SHA256Digest(const Data: RawByteString): TSHA256Digest;
var SHA: TSHA256;
begin
SHA.Full(pointer(Data),Length(Data),result);
end;
{ THMAC_SHA384 }
procedure THMAC_SHA384.Init(key: pointer; keylen: integer);
var i: integer;
k0,k0xorIpad: array[0..31] of cardinal;
begin
FillCharFast(k0,sizeof(k0),0);
if keylen>sizeof(k0) then
sha.Full(key,keylen,PSHA384Digest(@k0)^) else
MoveFast(key^,k0,keylen);
for i := 0 to 31 do
k0xorIpad[i] := k0[i] xor $36363636;
for i := 0 to 31 do
step7data[i] := k0[i] xor $5c5c5c5c;
sha.Init;
sha.Update(@k0xorIpad,sizeof(k0xorIpad));
FillCharFast(k0,sizeof(k0),0);
FillCharFast(k0xorIpad,sizeof(k0xorIpad),0);
end;
procedure THMAC_SHA384.Update(msg: pointer; msglen: integer);
begin
sha.Update(msg,msglen);
end;
procedure THMAC_SHA384.Done(out result: TSHA384Digest; NoInit: boolean);
begin
sha.Final(result);
sha.Update(@step7data,sizeof(step7data));
sha.Update(@result,sizeof(result));
sha.Final(result,NoInit);
if not NoInit then
FillCharFast(step7data,sizeof(step7data),0);
end;
procedure THMAC_SHA384.Done(out result: RawUTF8; NoInit: boolean);
var res: THash384;
begin
Done(res,NoInit);
result := SHA384DigestToString(res);
if not NoInit then
FillZero(res);
end;
procedure THMAC_SHA384.Compute(msg: pointer; msglen: integer; out result: TSHA384Digest);
var temp: THMAC_SHA384;
begin
temp := self; // thread-safe copy
temp.Update(msg,msglen);
temp.Done(result);
end;
procedure HMAC_SHA384(key,msg: pointer; keylen,msglen: integer; out result: TSHA384Digest);
var mac: THMAC_SHA384;
begin
mac.Init(key,keylen);
mac.Update(msg,msglen);
mac.Done(result);
end;
procedure HMAC_SHA384(const key,msg: RawByteString; out result: TSHA384Digest);
begin
HMAC_SHA384(pointer(key),pointer(msg),length(key),length(msg),result);
end;
procedure HMAC_SHA384(const key: TSHA384Digest; const msg: RawByteString; out result: TSHA384Digest);
begin
HMAC_SHA384(@key,pointer(msg),sizeof(key),length(msg),result);
end;
procedure PBKDF2_HMAC_SHA384(const password,salt: RawByteString; count: Integer;
out result: TSHA384Digest);
var i: integer;
tmp: TSHA384Digest;
mac: THMAC_SHA384;
first: THMAC_SHA384;
begin
HMAC_SHA384(password,salt+#0#0#0#1,result);
if count<2 then
exit;
tmp := result;
first.Init(pointer(password),length(password));
for i := 2 to count do begin
mac := first; // re-use the very same SHA context for best performance
mac.sha.Update(@tmp,sizeof(tmp));
mac.Done(tmp,true);
XorMemoryPtrInt(@result,@tmp,sizeof(result) shr {$ifdef CPU32}2{$else}3{$endif});
end;
FillcharFast(mac,sizeof(mac),0);
FillcharFast(first,sizeof(first),0);
FillZero(tmp);
end;
{ THMAC_SHA512 }
procedure THMAC_SHA512.Init(key: pointer; keylen: integer);
var i: integer;
k0,k0xorIpad: array[0..31] of cardinal;
begin
FillCharFast(k0,sizeof(k0),0);
if keylen>sizeof(k0) then
sha.Full(key,keylen,PSHA512Digest(@k0)^) else
MoveFast(key^,k0,keylen);
for i := 0 to 31 do
k0xorIpad[i] := k0[i] xor $36363636;
for i := 0 to 31 do
step7data[i] := k0[i] xor $5c5c5c5c;
sha.Init;
sha.Update(@k0xorIpad,sizeof(k0xorIpad));
FillCharFast(k0,sizeof(k0),0);
FillCharFast(k0xorIpad,sizeof(k0xorIpad),0);
end;
procedure THMAC_SHA512.Update(msg: pointer; msglen: integer);
begin
sha.Update(msg,msglen);
end;
procedure THMAC_SHA512.Done(out result: TSHA512Digest; NoInit: boolean);
begin
sha.Final(result);
sha.Update(@step7data,sizeof(step7data));
sha.Update(@result,sizeof(result));
sha.Final(result,NoInit);
if not NoInit then
FillCharFast(step7data,sizeof(step7data),0);
end;
procedure THMAC_SHA512.Done(out result: RawUTF8; NoInit: boolean);
var res: THash512;
begin
Done(res,NoInit);
result := SHA512DigestToString(res);
if not NoInit then
FillZero(res);
end;
procedure THMAC_SHA512.Compute(msg: pointer; msglen: integer; out result: TSHA512Digest);
var temp: THMAC_SHA512;
begin
temp := self; // thread-safe copy
temp.Update(msg,msglen);
temp.Done(result);
end;
procedure HMAC_SHA512(key,msg: pointer; keylen,msglen: integer; out result: TSHA512Digest);
var mac: THMAC_SHA512;
begin
mac.Init(key,keylen);
mac.Update(msg,msglen);
mac.Done(result);
end;
procedure HMAC_SHA512(const key,msg: RawByteString; out result: TSHA512Digest);
begin
HMAC_SHA512(pointer(key),pointer(msg),length(key),length(msg),result);
end;
procedure HMAC_SHA512(const key: TSHA512Digest; const msg: RawByteString; out result: TSHA512Digest);
begin
HMAC_SHA512(@key,pointer(msg),sizeof(key),length(msg),result);
end;
procedure PBKDF2_HMAC_SHA512(const password,salt: RawByteString; count: Integer;
out result: TSHA512Digest);
var i: integer;
tmp: TSHA512Digest;
mac: THMAC_SHA512;
first: THMAC_SHA512;
begin
HMAC_SHA512(password,salt+#0#0#0#1,result);
if count<2 then
exit;
tmp := result;
first.Init(pointer(password),length(password));
for i := 2 to count do begin
mac := first; // re-use the very same SHA context for best performance
mac.sha.Update(@tmp,sizeof(tmp));
mac.Done(tmp,true);
XorMemoryPtrInt(@result,@tmp,sizeof(result) shr {$ifdef CPU32}2{$else}3{$endif});
end;
FillcharFast(mac,sizeof(mac),0);
FillcharFast(first,sizeof(first),0);
FillZero(tmp);
end;
{ HMAC_CRC256C }
procedure crc256cmix(h1,h2: cardinal; h: PCardinalArray);
begin // see https://goo.gl/Pls5wi
h^[0] := h1; inc(h1,h2);
h^[1] := h1; inc(h1,h2);
h^[2] := h1; inc(h1,h2);
h^[3] := h1; inc(h1,h2);
h^[4] := h1; inc(h1,h2);
h^[5] := h1; inc(h1,h2);
h^[6] := h1; inc(h1,h2);
h^[7] := h1;
end;
procedure HMAC_CRC256C(key,msg: pointer; keylen,msglen: integer; out result: THash256);
var i: integer;
h1,h2: cardinal;
k0,k0xorIpad,step7data: THash512Rec;
begin
FillCharFast(k0,sizeof(k0),0);
if keylen>sizeof(k0) then
crc256c(key,keylen,k0.Lo) else
MoveFast(key^,k0,keylen);
for i := 0 to 15 do
k0xorIpad.c[i] := k0.c[i] xor $36363636;
for i := 0 to 15 do
step7data.c[i] := k0.c[i] xor $5c5c5c5c;
h1 := crc32c(crc32c(0,@k0xorIpad,sizeof(k0xorIpad)),msg,msglen);
h2 := crc32c(crc32c(h1,@k0xorIpad,sizeof(k0xorIpad)),msg,msglen);
crc256cmix(h1,h2,@result);
h1 := crc32c(crc32c(0,@step7data,sizeof(step7data)),@result,sizeof(result));
h2 := crc32c(crc32c(h1,@step7data,sizeof(step7data)),@result,sizeof(result));
crc256cmix(h1,h2,@result);
FillCharFast(k0,sizeof(k0),0);
FillCharFast(k0xorIpad,sizeof(k0),0);
FillCharFast(step7data,sizeof(k0),0);
end;
procedure HMAC_CRC256C(const key: THash256; const msg: RawByteString; out result: THash256);
begin
HMAC_CRC256C(@key,pointer(msg),SizeOf(key),length(msg),result);
end;
procedure HMAC_CRC256C(const key,msg: RawByteString; out result: THash256);
begin
HMAC_CRC256C(pointer(key),pointer(msg),length(key),length(msg),result);
end;
{ THMAC_CRC32C }
procedure THMAC_CRC32C.Init(const key: RawByteString);
begin
Init(pointer(key),length(key));
end;
procedure THMAC_CRC32C.Init(key: pointer; keylen: integer);
var i: integer;
k0,k0xorIpad: THash512Rec;
begin
FillCharFast(k0,sizeof(k0),0);
if keylen>sizeof(k0) then
crc256c(key,keylen,k0.Lo) else
MoveFast(key^,k0,keylen);
for i := 0 to 15 do
k0xorIpad.c[i] := k0.c[i] xor $36363636;
for i := 0 to 15 do
step7data.c[i] := k0.c[i] xor $5c5c5c5c;
seed := crc32c(0,@k0xorIpad,sizeof(k0xorIpad));
FillCharFast(k0,sizeof(k0),0);
FillCharFast(k0xorIpad,sizeof(k0xorIpad),0);
end;
procedure THMAC_CRC32C.Update(msg: pointer; msglen: integer);
begin
seed := crc32c(seed,msg,msglen);
end;
procedure THMAC_CRC32C.Update(const msg: RawByteString);
begin
seed := crc32c(seed,pointer(msg),length(msg));
end;
function THMAC_CRC32C.Done(NoInit: boolean): cardinal;
begin
result := crc32c(seed,@step7data,sizeof(step7data));
if not NoInit then
FillcharFast(self,sizeof(self),0);
end;
function THMAC_CRC32C.Compute(msg: pointer; msglen: integer): cardinal;
begin
result := crc32c(crc32c(seed,msg,msglen),@step7data,sizeof(step7data));
end;
function HMAC_CRC32C(key,msg: pointer; keylen,msglen: integer): cardinal;
var mac: THMAC_CRC32C;
begin
mac.Init(key,keylen);
mac.Update(msg,msglen);
result := mac.Done;
end;
function HMAC_CRC32C(const key: THash256; const msg: RawByteString): cardinal;
begin
result := HMAC_CRC32C(@key,pointer(msg),SizeOf(key),length(msg));
end;
function HMAC_CRC32C(const key,msg: RawByteString): cardinal;
begin
result := HMAC_CRC32C(pointer(key),pointer(msg),length(key),length(msg));
end;
function SHA1SelfTest: boolean;
function SingleTest(const s: RawByteString; TDig: TSHA1Digest): boolean;
var SHA: TSHA1;
Digest: TSHA1Digest;
i: integer;
begin
// 1. Hash complete RawByteString
SHA.Full(pointer(s),length(s),Digest);
result := IsEqual(Digest,TDig);
if not result then exit;
// 2. one update call for all chars
for i := 1 to length(s) do
SHA.Update(@s[i],1);
SHA.Final(Digest);
result := IsEqual(Digest,TDig);
// 3. test consistency with Padlock engine down results
{$ifdef USEPADLOCK}
if not result or not padlock_available then exit;
padlock_available := false; // force PadLock engine down
SHA.Full(pointer(s),length(s),Digest);
result := IsEqual(Digest,TDig);
{$ifdef PADLOCKDEBUG} write('=padlock '); {$endif}
padlock_available := true;
{$endif}
end;
const
Test1Out: TSHA1Digest=
($A9,$99,$3E,$36,$47,$06,$81,$6A,$BA,$3E,$25,$71,$78,$50,$C2,$6C,$9C,$D0,$D8,$9D);
Test2Out: TSHA1Digest=
($84,$98,$3E,$44,$1C,$3B,$D2,$6E,$BA,$AE,$4A,$A1,$F9,$51,$29,$E5,$E5,$46,$70,$F1);
var
s: RawByteString;
SHA: TSHA1;
Digest: TSHA1Digest;
begin
result := SingleTest('abc',Test1Out) and
SingleTest('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq',Test2Out);
if not result then exit;
s := 'Wikipedia, l''encyclopedie libre et gratuite';
SHA.Full(pointer(s),length(s),Digest);
result := SHA1DigestToString(Digest)='c18cc65028bbdc147288a2d136313287782b9c73';
if not result then exit;
HMAC_SHA1('','',Digest);
result := SHA1DigestToString(Digest)='fbdb1d1b18aa6c08324b7d64b71fb76370690e1d';
if not result then exit;
HMAC_SHA1('key','The quick brown fox jumps over the lazy dog',Digest);
result := SHA1DigestToString(Digest)='de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9';
if not result then exit;
// from https://www.ietf.org/rfc/rfc6070.txt
PBKDF2_HMAC_SHA1('password','salt',1,Digest);
s := SHA1DigestToString(Digest);
result := s='0c60c80f961f0e71f3a9b524af6012062fe037a6';
if not result then exit;
PBKDF2_HMAC_SHA1('password','salt',2,Digest);
s := SHA1DigestToString(Digest);
result := s='ea6c014dc72d6f8ccd1ed92ace1d41f0d8de8957';
if not result then exit;
PBKDF2_HMAC_SHA1('password','salt',4096,Digest);
s := SHA1DigestToString(Digest);
result := s='4b007901b765489abead49d926f721d065a429c1';
end;
{ TAES }
function AESSelfTest(onlytables: Boolean): boolean;
var A: TAES;
st: RawByteString;
Key: TSHA256Digest;
s,b,p: TAESBlock;
i,k,ks: integer;
begin
// ensure that we have $2000 bytes of contiguous XOR tables ;)
result := (PtrUInt(@TD0)+$400=PtrUInt(@TD1))and(PtrUInt(@TD0)+$800=PtrUInt(@TD2))
and(PtrUInt(@TD0)+$C00=PtrUInt(@TD3))and(PtrUInt(@TD0)+$1000=PtrUInt(@TE0))
and(PtrUInt(@TD0)+$1400=PtrUInt(@TE1))and(PtrUInt(@TD0)+$1800=PtrUInt(@TE2))
and(PtrUInt(@TD0)+$1C00=PtrUInt(@TE3))and
(SBox[255]=$16)and(InvSBox[0]=$52)and
(Te0[0]=$a56363c6)and(Te0[255]=$3a16162c)and
(Te1[0]=$6363c6a5)and(Te1[255]=$16162c3a)and
(Te3[0]=$c6a56363)and(Te3[255]=$2c3a1616)and
(Td0[0]=$50a7f451)and(Td0[99]=0)and(Td0[255]=$4257b8d0)and
(Td3[0]=$5150a7f4)and(Td3[255]=$d04257b8);
if onlytables or not result then
exit;
// test
result := false;
Randomize;
st := '1234essai';
PInteger(@st[1])^ := Random(MaxInt);
for k := 0 to 2 do begin
ks := 128+k*64; // test keysize of 128,192 and 256 bits
// write('Test AES ',ks);
for i := 1 to 100 do begin
SHA256Weak(st,Key);
moveFast(Key,s,16);
A.EncryptInit(Key,ks);
A.Encrypt(s,b);
A.Done;
A.DecryptInit(Key,ks);
A.Decrypt(b,p);
A.Done;
if not IsEqual(p,s) then begin
writeln('AESSelfTest compareError with keysize=',ks);
exit;
end;
st := st+AnsiChar(Random(255));
end;
end;
result := true;
end;
procedure TAES.Encrypt(var B: TAESBlock);
begin
TAESContext(Context).DoBlock(Context,B,B);
end;
{$ifdef USEAESNI}
{$ifdef CPU32}
procedure AesNiEncryptXmm7_128; {$ifdef FPC} nostackframe; assembler; {$endif}
asm // input: eax=TAESContext, xmm7=data; output: eax=TAESContext, xmm7=data
movups xmm0, [eax + 16 * 0]
movups xmm1, [eax + 16 * 1]
movups xmm2, [eax + 16 * 2]
movups xmm3, [eax + 16 * 3]
movups xmm4, [eax + 16 * 4]
movups xmm5, [eax + 16 * 5]
movups xmm6, [eax + 16 * 6]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
{$else}
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DC, $FB
db $66, $0F, $38, $DC, $FC
db $66, $0F, $38, $DC, $FD
db $66, $0F, $38, $DC, $FE
{$endif}
movups xmm0, [eax + 16 * 7]
movups xmm1, [eax + 16 * 8]
movups xmm2, [eax + 16 * 9]
movups xmm3, [eax + 16 * 10]
{$ifdef HASAESNI}
aesenc xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenclast xmm7, xmm3
{$else}
db $66, $0F, $38, $DC, $F8
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DD, $FB
{$endif}
end;
procedure aesniencrypt128(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=ctxt edx=source ecx=dest
movups xmm7, [edx]
call AesNiEncryptXmm7_128
movups [ecx], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure AesNiEncryptXmm7_192;
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // input: eax=TAESContext, xmm7=data; output: eax=TAESContext, xmm7=data
movups xmm0, [eax + 16 * 0]
movups xmm1, [eax + 16 * 1]
movups xmm2, [eax + 16 * 2]
movups xmm3, [eax + 16 * 3]
movups xmm4, [eax + 16 * 4]
movups xmm5, [eax + 16 * 5]
movups xmm6, [eax + 16 * 6]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
{$else}
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DC, $FB
db $66, $0F, $38, $DC, $FC
db $66, $0F, $38, $DC, $FD
db $66, $0F, $38, $DC, $FE
{$endif}
movups xmm0, [eax + 16 * 7]
movups xmm1, [eax + 16 * 8]
movups xmm2, [eax + 16 * 9]
movups xmm3, [eax + 16 * 10]
movups xmm4, [eax + 16 * 11]
movups xmm5, [eax + 16 * 12]
{$ifdef HASAESNI}
aesenc xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenclast xmm7, xmm5
{$else}
db $66, $0F, $38, $DC, $F8
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DC, $FB
db $66, $0F, $38, $DC, $FC
db $66, $0F, $38, $DD, $FD
{$endif}
end;
procedure aesniencrypt192(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=ctxt edx=source ecx=dest
movups xmm7, [edx]
call AesNiEncryptXmm7_192
movups [ecx], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure AesNiEncryptXmm7_256;
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // input: eax=TAESContext, xmm7=data; output: eax=TAESContext, xmm7=data
movups xmm0, [eax + 16 * 0]
movups xmm1, [eax + 16 * 1]
movups xmm2, [eax + 16 * 2]
movups xmm3, [eax + 16 * 3]
movups xmm4, [eax + 16 * 4]
movups xmm5, [eax + 16 * 5]
movups xmm6, [eax + 16 * 6]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
{$else}
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DC, $FB
db $66, $0F, $38, $DC, $FC
db $66, $0F, $38, $DC, $FD
db $66, $0F, $38, $DC, $FE
{$endif}
movups xmm0, [eax + 16 * 7]
movups xmm1, [eax + 16 * 8]
movups xmm2, [eax + 16 * 9]
movups xmm3, [eax + 16 * 10]
movups xmm4, [eax + 16 * 11]
movups xmm5, [eax + 16 * 12]
movups xmm6, [eax + 16 * 13]
{$ifdef HASAESNI}
aesenc xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
{$else}
db $66, $0F, $38, $DC, $F8
db $66, $0F, $38, $DC, $F9
db $66, $0F, $38, $DC, $FA
db $66, $0F, $38, $DC, $FB
db $66, $0F, $38, $DC, $FC
db $66, $0F, $38, $DC, $FD
db $66, $0F, $38, $DC, $FE
{$endif}
movups xmm1, [eax + 16 * 14]
{$ifdef HASAESNI}
aesenclast xmm7, xmm1
{$else}
db $66, $0F, $38, $DD, $F9
{$endif}
end;
procedure aesniencrypt256(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=ctxt edx=source ecx=dest
movups xmm7, [edx]
call AesNiEncryptXmm7_256
movups [ecx], xmm7
pxor xmm7, xmm7 // for safety
end;
{$endif CPU32}
{$ifdef CPU64}
procedure aesniencrypt128(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 0]
movups xmm1, dqword ptr[ctxt + 16 * 1]
movups xmm2, dqword ptr[ctxt + 16 * 2]
movups xmm3, dqword ptr[ctxt + 16 * 3]
movups xmm4, dqword ptr[ctxt + 16 * 4]
movups xmm5, dqword ptr[ctxt + 16 * 5]
movups xmm6, dqword ptr[ctxt + 16 * 6]
movups xmm8, dqword ptr[ctxt + 16 * 7]
movups xmm9, dqword ptr[ctxt + 16 * 8]
movups xmm10, dqword ptr[ctxt + 16 * 9]
movups xmm11, dqword ptr[ctxt + 16 * 10]
pxor xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
aesenc xmm7, xmm8
aesenc xmm7, xmm9
aesenc xmm7, xmm10
aesenclast xmm7, xmm11
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure aesniencrypt192(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 0]
movups xmm1, dqword ptr[ctxt + 16 * 1]
movups xmm2, dqword ptr[ctxt + 16 * 2]
movups xmm3, dqword ptr[ctxt + 16 * 3]
movups xmm4, dqword ptr[ctxt + 16 * 4]
movups xmm5, dqword ptr[ctxt + 16 * 5]
movups xmm6, dqword ptr[ctxt + 16 * 6]
movups xmm8, dqword ptr[ctxt + 16 * 7]
movups xmm9, dqword ptr[ctxt + 16 * 8]
movups xmm10, dqword ptr[ctxt + 16 * 9]
movups xmm11, dqword ptr[ctxt + 16 * 10]
movups xmm12, dqword ptr[ctxt + 16 * 11]
movups xmm13, dqword ptr[ctxt + 16 * 12]
pxor xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
aesenc xmm7, xmm8
aesenc xmm7, xmm9
aesenc xmm7, xmm10
aesenc xmm7, xmm11
aesenc xmm7, xmm12
aesenclast xmm7, xmm13
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure aesniencrypt256(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 0]
movups xmm1, dqword ptr[ctxt + 16 * 1]
movups xmm2, dqword ptr[ctxt + 16 * 2]
movups xmm3, dqword ptr[ctxt + 16 * 3]
movups xmm4, dqword ptr[ctxt + 16 * 4]
movups xmm5, dqword ptr[ctxt + 16 * 5]
movups xmm6, dqword ptr[ctxt + 16 * 6]
movups xmm8, dqword ptr[ctxt + 16 * 7]
movups xmm9, dqword ptr[ctxt + 16 * 8]
movups xmm10, dqword ptr[ctxt + 16 * 9]
movups xmm11, dqword ptr[ctxt + 16 * 10]
movups xmm12, dqword ptr[ctxt + 16 * 11]
movups xmm13, dqword ptr[ctxt + 16 * 12]
movups xmm14, dqword ptr[ctxt + 16 * 13]
movups xmm15, dqword ptr[ctxt + 16 * 14]
pxor xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
aesenc xmm7, xmm8
aesenc xmm7, xmm9
aesenc xmm7, xmm10
aesenc xmm7, xmm11
aesenc xmm7, xmm12
aesenc xmm7, xmm13
aesenc xmm7, xmm14
aesenclast xmm7, xmm15
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure aesnidecrypt128(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 10]
movups xmm1, dqword ptr[ctxt + 16 * 9]
movups xmm2, dqword ptr[ctxt + 16 * 8]
movups xmm3, dqword ptr[ctxt + 16 * 7]
movups xmm4, dqword ptr[ctxt + 16 * 6]
movups xmm5, dqword ptr[ctxt + 16 * 5]
movups xmm6, dqword ptr[ctxt + 16 * 4]
movups xmm8, dqword ptr[ctxt + 16 * 3]
movups xmm9, dqword ptr[ctxt + 16 * 2]
movups xmm10, dqword ptr[ctxt + 16 * 1]
movups xmm11, dqword ptr[ctxt + 16 * 0]
pxor xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
aesdec xmm7, xmm8
aesdec xmm7, xmm9
aesdec xmm7, xmm10
aesdeclast xmm7, xmm11
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure aesnidecrypt192(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 12]
movups xmm1, dqword ptr[ctxt + 16 * 11]
movups xmm2, dqword ptr[ctxt + 16 * 10]
movups xmm3, dqword ptr[ctxt + 16 * 9]
movups xmm4, dqword ptr[ctxt + 16 * 8]
movups xmm5, dqword ptr[ctxt + 16 * 7]
movups xmm6, dqword ptr[ctxt + 16 * 6]
movups xmm8, dqword ptr[ctxt + 16 * 5]
movups xmm9, dqword ptr[ctxt + 16 * 4]
movups xmm10, dqword ptr[ctxt + 16 * 3]
movups xmm11, dqword ptr[ctxt + 16 * 2]
movups xmm12, dqword ptr[ctxt + 16 * 1]
movups xmm13, dqword ptr[ctxt + 16 * 0]
pxor xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
aesdec xmm7, xmm8
aesdec xmm7, xmm9
aesdec xmm7, xmm10
aesdec xmm7, xmm11
aesdec xmm7, xmm12
aesdeclast xmm7, xmm13
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
procedure aesnidecrypt256(const ctxt, source, dest); {$ifdef FPC}nostackframe; assembler;
asm {$else} asm .noframe {$endif}
movups xmm7, dqword ptr[source]
movups xmm0, dqword ptr[ctxt + 16 * 14]
movups xmm1, dqword ptr[ctxt + 16 * 13]
movups xmm2, dqword ptr[ctxt + 16 * 12]
movups xmm3, dqword ptr[ctxt + 16 * 11]
movups xmm4, dqword ptr[ctxt + 16 * 10]
movups xmm5, dqword ptr[ctxt + 16 * 9]
movups xmm6, dqword ptr[ctxt + 16 * 8]
movups xmm8, dqword ptr[ctxt + 16 * 7]
movups xmm9, dqword ptr[ctxt + 16 * 6]
movups xmm10, dqword ptr[ctxt + 16 * 5]
movups xmm11, dqword ptr[ctxt + 16 * 4]
movups xmm12, dqword ptr[ctxt + 16 * 3]
movups xmm13, dqword ptr[ctxt + 16 * 2]
movups xmm14, dqword ptr[ctxt + 16 * 1]
movups xmm15, dqword ptr[ctxt + 16 * 0]
pxor xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
aesdec xmm7, xmm8
aesdec xmm7, xmm9
aesdec xmm7, xmm10
aesdec xmm7, xmm11
aesdec xmm7, xmm12
aesdec xmm7, xmm13
aesdec xmm7, xmm14
aesdeclast xmm7, xmm15
movups dqword ptr[dest], xmm7
pxor xmm7, xmm7 // for safety
end;
{$endif CPU64}
{$endif USEAESNI}
procedure aesencryptpas(const ctxt: TAESContext; bi, bo: PWA4);
{ AES_PASCAL version (c) Wolfgang Ehrhardt under zlib license:
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in
a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
-> code has been refactored and tuned especially for FPC x86_64 target }
var
t: PCardinalArray; // faster on a PIC system
{$ifdef AES_ROLLED}
s0,s1,s2,s3: PtrUInt; // TAESBlock s# as separate variables
t0,t1,t2: cardinal; // TAESBlock t# as separate variables
pk: PWA4;
i: integer;
begin
pk := @ctxt.RK;
s0 := bi[0] xor pk[0];
s1 := bi[1] xor pk[1];
s2 := bi[2] xor pk[2];
s3 := bi[3] xor pk[3];
inc(pk);
t := @Te0;
for i := 1 to ctxt.rounds-1 do begin
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24];
s3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[3];;
s0 := t0 xor pk[0];
s1 := t1 xor pk[1];
s2 := t2 xor pk[2];
inc(pk);
end;
bo[0] := ((SBox[s0 and $ff]) xor
(SBox[s1 shr 8 and $ff]) shl 8 xor
(SBox[s2 shr 16 and $ff]) shl 16 xor
(SBox[s3 shr 24]) shl 24 ) xor pk[0];
bo[1] := ((SBox[s1 and $ff]) xor
(SBox[s2 shr 8 and $ff]) shl 8 xor
(SBox[s3 shr 16 and $ff]) shl 16 xor
(SBox[s0 shr 24]) shl 24 ) xor pk[1];
bo[2] := ((SBox[s2 and $ff]) xor
(SBox[s3 shr 8 and $ff]) shl 8 xor
(SBox[s0 shr 16 and $ff]) shl 16 xor
(SBox[s1 shr 24]) shl 24 ) xor pk[2];
bo[3] := ((SBox[s3 and $ff]) xor
(SBox[s0 shr 8 and $ff]) shl 8 xor
(SBox[s1 shr 16 and $ff]) shl 16 xor
(SBox[s2 shr 24]) shl 24 ) xor pk[3];
{$else}
s0,s1,s2,s3,t0,t1,t2,t3: cardinal; // TAESBlock s#/t# as separate variables
pK: PAWk;
begin
pK := @ctxt.RK;
// Initialize with input block
s0 := bi[0] xor pk[0];
s1 := bi[1] xor pk[1];
s2 := bi[2] xor pk[2];
s3 := bi[3] xor pk[3];
t := @Te0;
// Round 1
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[4];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[5];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[6];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[7];
// Round 2
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[8];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[9];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[10];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[11];
// Round 3
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[12];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[13];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[14];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[15];
// Round 4
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[16];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[17];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[18];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[19];
// Round 5
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[20];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[21];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[22];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[23];
// Round 6
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[24];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[25];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[26];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[27];
// Round 7
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[28];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[29];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[30];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[31];
// Round 8
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[32];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[33];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[34];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[35];
// Round 9
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[36];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[37];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[38];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[39];
if ctxt.rounds>10 then begin
// Round 10
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[40];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[41];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[42];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[43];
// Round 11
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[44];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[45];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[46];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[47];
if ctxt.rounds>12 then begin
// Round 12
s0 := t[t0 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[48];
s1 := t[t1 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[49];
s2 := t[t2 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[50];
s3 := t[t3 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[51];
// Round 13
t0 := t[s0 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[52];
t1 := t[s1 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[53];
t2 := t[s2 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[54];
t3 := t[s3 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[55];
end;
end;
inc(PByte(pK), ctxt.rounds shl 4);
bo[0] := ((SBox[t0 and $ff]) xor
(SBox[t1 shr 8 and $ff]) shl 8 xor
(SBox[t2 shr 16 and $ff]) shl 16 xor
(SBox[t3 shr 24]) shl 24 ) xor pk[0];
bo[1] := ((SBox[t1 and $ff]) xor
(SBox[t2 shr 8 and $ff]) shl 8 xor
(SBox[t3 shr 16 and $ff]) shl 16 xor
(SBox[t0 shr 24]) shl 24 ) xor pk[1];
bo[2] := ((SBox[t2 and $ff]) xor
(SBox[t3 shr 8 and $ff]) shl 8 xor
(SBox[t0 shr 16 and $ff]) shl 16 xor
(SBox[t1 shr 24]) shl 24 ) xor pk[2];
bo[3] := ((SBox[t3 and $ff]) xor
(SBox[t0 shr 8 and $ff]) shl 8 xor
(SBox[t1 shr 16 and $ff]) shl 16 xor
(SBox[t2 shr 24]) shl 24 ) xor pk[3];
{$endif}
end;
{$ifdef USEPADLOCK}
procedure aesencryptpadlock(const ctxt: TAESContext; bi, bo: PWA4);
begin
padlock_aes_encrypt(ctxt.ViaCtx,bi,bo,16);
end;
{$endif}
{$ifdef CPUX64}
procedure aesencryptx64(const ctxt: TAESContext; bi, bo: PWA4);
{$ifdef FPC}nostackframe; assembler; asm{$else}
asm // input: rcx/rdi=TAESContext, rdx/rsi=source, r8/rdx=dest
.noframe
{$endif} // rolled optimized encryption asm version by A. Bouchez
push r15
push r14
push r13
push r12
push rbx
push rbp
{$ifdef win64}
push rdi
push rsi
mov r15, r8
mov r12, rcx
{$else}
mov r15, rdx
mov rdx, rsi
mov r12, rdi
{$endif win64}
movzx r13, byte ptr [r12].TAESContext.Rounds
mov eax, dword ptr [rdx]
mov ebx, dword ptr [rdx+4H]
mov ecx, dword ptr [rdx+8H]
mov edx, dword ptr [rdx+0CH]
xor eax, dword ptr [r12]
xor ebx, dword ptr [r12+4H]
xor ecx, dword ptr [r12+8H]
xor edx, dword ptr [r12+0CH]
sub r13, 1
add r12, 16
lea r14, [rip+Te0]
{$ifdef FPC} align 16 {$else} .align 16 {$endif}
@round: mov esi, eax
mov edi, edx
movzx r8d, al
movzx r9d, cl
movzx r10d, bl
mov r8d, dword ptr [r14+r8*4]
mov r9d, dword ptr [r14+r9*4]
mov r10d, dword ptr [r14+r10*4]
shr esi, 16
shr edi, 16
movzx ebp, bh
xor r8d, dword ptr [r14+rbp*4+400H]
movzx ebp, dh
xor r9d, dword ptr [r14+rbp*4+400H]
movzx ebp, ch
xor r10d, dword ptr [r14+rbp*4+400H]
shr ebx, 16
shr ecx, 16
movzx ebp, dl
mov edx, dword ptr [r14+rbp*4]
movzx ebp, cl
xor r8d, dword ptr [r14+rbp*4+800H]
movzx ebp, sil
xor r9d, dword ptr [r14+rbp*4+800H]
movzx r11, dil
movzx eax, ah
shr edi, 8
movzx ebp, bh
shr esi, 8
xor r10d, dword ptr [r14+r11*4+800H]
xor edx, dword ptr [r14+rax*4+400H]
xor r8d, dword ptr [r14+rdi*4+0C00H]
xor r9d, dword ptr [r14+rbp*4+0C00H]
xor r10d, dword ptr [r14+rsi*4+0C00H]
movzx ebp, bl
xor edx, dword ptr [r14+rbp*4+800H]
mov rbx, r10
mov rax, r8
movzx ebp, ch
xor edx, dword ptr [r14+rbp*4+0C00H]
mov rcx, r9
xor eax, dword ptr [r12]
xor ebx, dword ptr [r12+4H]
xor ecx, dword ptr [r12+8H]
xor edx, dword ptr [r12+0CH]
add r12, 16
sub r13, 1
jnz @round
lea r9, [rip+SBox]
movzx r8, al
movzx r14, byte ptr [r9+r8]
movzx edi, bh
movzx r8, byte ptr [r9+rdi]
shl r8d, 8
xor r14d, r8d
mov r11, rcx
shr r11, 16
and r11, 0FFH
movzx r8, byte ptr [r9+r11]
shl r8d, 16
xor r14d, r8d
mov r11, rdx
shr r11, 24
movzx r8, byte ptr [r9+r11]
shl r8d, 24
xor r14d, r8d
xor r14d, dword ptr [r12]
mov dword ptr [r15], r14d
movzx r8, bl
movzx r14, byte ptr [r9+r8]
movzx edi, ch
movzx r8, byte ptr [r9+rdi]
shl r8d, 8
xor r14d, r8d
mov r11, rdx
shr r11, 16
and r11, 0FFH
movzx r8, byte ptr [r9+r11]
shl r8d, 16
xor r14d, r8d
mov r11, rax
shr r11, 24
movzx r8, byte ptr [r9+r11]
shl r8d, 24
xor r14d, r8d
xor r14d, dword ptr [r12+4H]
mov dword ptr [r15+4H], r14d
movzx r8, cl
movzx r14, byte ptr [r9+r8]
movzx edi, dh
movzx r8, byte ptr [r9+rdi]
shl r8d, 8
xor r14d, r8d
mov r11, rax
shr r11, 16
and r11, 0FFH
movzx r8, byte ptr [r9+r11]
shl r8d, 16
xor r14d, r8d
mov r11, rbx
shr r11, 24
movzx r8, byte ptr [r9+r11]
shl r8d, 24
xor r14d, r8d
xor r14d, dword ptr [r12+8H]
mov dword ptr [r15+8H], r14d
and rdx, 0FFH
movzx r14, byte ptr [r9+rdx]
movzx eax, ah
movzx r8, byte ptr [r9+rax]
shl r8d, 8
xor r14d, r8d
shr rbx, 16
and rbx, 0FFH
movzx r8, byte ptr [r9+rbx]
shl r8d, 16
xor r14d, r8d
shr rcx, 24
movzx r8, byte ptr [r9+rcx]
shl r8d, 24
xor r14d, r8d
xor r14d, dword ptr [r12+0CH]
mov dword ptr [r15+0CH], r14d
{$ifdef win64}
pop rsi
pop rdi
{$endif win64}
pop rbp
pop rbx
pop r12
pop r13
pop r14
pop r15
end;
{$endif CPUX64}
{$ifdef CPUX86_NOTPIC}
procedure aesencrypt386(const ctxt: TAESContext; bi, bo: PWA4);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // rolled optimized encryption asm version by A. Bouchez
push ebx
push esi
push edi
push ebp
add esp, - 24
mov [esp + 4], ecx
mov ecx, eax // ecx=pk
movzx eax, byte ptr[eax].taescontext.rounds
dec eax
mov [esp + 20], eax
mov ebx, [edx]
xor ebx, [ecx]
mov esi, [edx + 4]
xor esi, [ecx + 4]
mov eax, [edx + 8]
xor eax, [ecx + 8]
mov edx, [edx + 12]
xor edx, [ecx + 12]
lea ecx, [ecx + 16]
@1: // pk=ecx s0=ebx s1=esi s2=eax s3=edx
movzx edi, bl
mov edi, dword ptr[4 * edi + te0]
movzx ebp, si
shr ebp, $08
xor edi, dword ptr[4 * ebp + te1]
mov ebp, eax
shr ebp, $10
and ebp, $ff
xor edi, dword ptr[4 * ebp + te2]
mov ebp, edx
shr ebp, $18
xor edi, dword ptr[4 * ebp + te3]
mov [esp + 8], edi
mov edi, esi
and edi, 255
mov edi, dword ptr[4 * edi + te0]
movzx ebp, ax
shr ebp, $08
xor edi, dword ptr[4 * ebp + te1]
mov ebp, edx
shr ebp, $10
and ebp, 255
xor edi, dword ptr[4 * ebp + te2]
mov ebp, ebx
shr ebp, $18
xor edi, dword ptr[4 * ebp + te3]
mov [esp + 12], edi
movzx edi, al
mov edi, dword ptr[4 * edi + te0]
movzx ebp, dh
xor edi, dword ptr[4 * ebp + te1]
mov ebp, ebx
shr ebp, $10
and ebp, 255
xor edi, dword ptr[4 * ebp + te2]
mov ebp, esi
shr ebp, $18
xor edi, dword ptr[4 * ebp + te3]
mov [esp + 16], edi
and edx, 255
mov edx, dword ptr[4 * edx + te0]
shr ebx, $08
and ebx, 255
xor edx, dword ptr[4 * ebx + te1]
shr esi, $10
and esi, 255
xor edx, dword ptr[4 * esi + te2]
shr eax, $18
xor edx, dword ptr[4 * eax + te3]
mov ebx, [ecx]
xor ebx, [esp + 8]
mov esi, [ecx + 4]
xor esi, [esp + 12]
mov eax, [ecx + 8]
xor eax, [esp + 16]
xor edx, [ecx + 12]
lea ecx, [ecx + 16]
dec byte ptr[esp + 20]
jne @1
mov ebp, ecx // ebp=pk
movzx ecx, bl
mov edi, esi
movzx ecx, byte ptr[ecx + SBox]
shr edi, $08
and edi, 255
movzx edi, byte ptr[edi + SBox]
shl edi, $08
xor ecx, edi
mov edi, eax
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + SBox]
shl edi, $10
xor ecx, edi
mov edi, edx
shr edi, $18
movzx edi, byte ptr[edi + SBox]
shl edi, $18
xor ecx, edi
xor ecx, [ebp]
mov edi, [esp + 4]
mov [edi], ecx
mov ecx, esi
and ecx, 255
movzx ecx, byte ptr[ecx + SBox]
movzx edi, ah
movzx edi, byte ptr[edi + SBox]
shl edi, $08
xor ecx, edi
mov edi, edx
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + SBox]
shl edi, $10
xor ecx, edi
mov edi, ebx
shr edi, $18
movzx edi, byte ptr[edi + SBox]
shl edi, $18
xor ecx, edi
xor ecx, [ebp + 4]
mov edi, [esp + 4]
mov [edi + 4], ecx
mov ecx, eax
and ecx, 255
movzx ecx, byte ptr[ecx + SBox]
movzx edi, dh
movzx edi, byte ptr[edi + SBox]
shl edi, $08
xor ecx, edi
mov edi, ebx
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + SBox]
shl edi, $10
xor ecx, edi
mov edi, esi
shr edi, $18
movzx edi, byte ptr[edi + SBox]
shl edi, $18
xor ecx, edi
xor ecx, [ebp + 8]
mov edi, [esp + 4]
mov [edi + 8], ecx
and edx, 255
movzx edx, byte ptr[edx + SBox]
shr ebx, $08
and ebx, 255
xor ecx, ecx
mov cl, byte ptr[ebx + SBox]
shl ecx, $08
xor edx, ecx
shr esi, $10
and esi, 255
xor ecx, ecx
mov cl, byte ptr[esi + SBox]
shl ecx, $10
xor edx, ecx
shr eax, $18
movzx eax, byte ptr[eax + SBox]
shl eax, $18
xor edx, eax
xor edx, [ebp + 12]
mov eax, [esp + 4]
mov [eax + 12], edx
add esp, 24
pop ebp
pop edi
pop esi
pop ebx
end;
{$endif CPUX86_NOTPIC}
procedure TAES.Encrypt(const BI: TAESBlock; var BO: TAESBlock);
begin
TAESContext(Context).DoBlock(Context,BI,BO);
end;
{$ifdef USEAESNI} // should be put outside the main method for FPC :(
procedure ShiftAesNi(KeySize: cardinal; pk: pointer);
{$ifdef CPU32} {$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=KeySize edx=pk
movups xmm1, [edx]
movups xmm5, dqword ptr[@mask]
cmp al, 128
je @128
cmp al, 192
je @e // 192 bits is very complicated -> skip by now (use 128+256)
@256: movups xmm3, [edx + 16]
add edx, 32
db $66, $0F, $3A, $DF, $D3, $01 // aeskeygenassist xmm2,xmm3,1
call @exp256
db $66, $0F, $3A, $DF, $D3, $02 // aeskeygenassist xmm2,xmm3,2
call @exp256
db $66, $0F, $3A, $DF, $D3, $04 // aeskeygenassist xmm2,xmm3,4
call @exp256
db $66, $0F, $3A, $DF, $D3, $08 // aeskeygenassist xmm2,xmm3,8
call @exp256
db $66, $0F, $3A, $DF, $D3, $10 // aeskeygenassist xmm2,xmm3,$10
call @exp256
db $66, $0F, $3A, $DF, $D3, $20 // aeskeygenassist xmm2,xmm3,$20
call @exp256
db $66, $0F, $3A, $DF, $D3, $40 // aeskeygenassist xmm2,xmm3,$40
pshufd xmm2, xmm2, $FF
movups xmm4, xmm1
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
pxor xmm1, xmm2
movups [edx], xmm1
jmp @e
@mask: dd $ffffffff
dd $03020100
dd $07060504
dd $0b0a0908
@exp256:pshufd xmm2, xmm2, $ff
movups xmm4, xmm1
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm1, xmm4
pxor xmm1, xmm2
movups [edx], xmm1
add edx, $10
db $66, $0F, $3A, $DF, $E1, $00 // aeskeygenassist xmm4,xmm1,0
pshufd xmm2, xmm4, $AA
movups xmm4, xmm3
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm3, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm3, xmm4
db $66, $0F, $38, $00, $E5 // pshufb xmm4,xmm5
pxor xmm3, xmm4
pxor xmm3, xmm2
movups [edx], xmm3
add edx, $10
ret
@exp128:pshufd xmm2, xmm2, $FF
movups xmm3, xmm1
db $66, $0F, $38, $00, $DD // pshufb xmm3,xmm5
pxor xmm1, xmm3
db $66, $0F, $38, $00, $DD // pshufb xmm3,xmm5
pxor xmm1, xmm3
db $66, $0F, $38, $00, $DD // pshufb xmm3,xmm5
pxor xmm1, xmm3
pxor xmm1, xmm2
movups [edx], xmm1
add edx, $10
ret
@128: add edx, 16
db $66, $0F, $3A, $DF, $D1, $01 // aeskeygenassist xmm2,xmm1,1
call @exp128
db $66, $0F, $3A, $DF, $D1, $02 // aeskeygenassist xmm2,xmm1,2
call @exp128
db $66, $0F, $3A, $DF, $D1, $04 // aeskeygenassist xmm2,xmm1,4
call @exp128
db $66, $0F, $3A, $DF, $D1, $08 // aeskeygenassist xmm2,xmm1,8
call @exp128
db $66, $0F, $3A, $DF, $D1, $10 // aeskeygenassist xmm2,xmm1,$10
call @exp128
db $66, $0F, $3A, $DF, $D1, $20 // aeskeygenassist xmm2,xmm1,$20
call @exp128
db $66, $0F, $3A, $DF, $D1, $40 // aeskeygenassist xmm2,xmm1,$40
call @exp128
db $66, $0F, $3A, $DF, $D1, $80 // aeskeygenassist xmm2,xmm1,$80
call @exp128
db $66, $0F, $3A, $DF, $D1, $1b // aeskeygenassist xmm2,xmm1,$1b
call @exp128
db $66, $0F, $3A, $DF, $D1, $36 // aeskeygenassist xmm2,xmm1,$36
call @exp128
@e: db $f3 // rep ret
end;
{$endif CPU32}
{$ifdef CPU64}
{$ifdef FPC} nostackframe; assembler; asm {$else} asm .noframe {$endif}
mov eax, keysize
movups xmm1, dqword ptr[pk]
movaps xmm5, dqword ptr[rip + @mask]
cmp al, 128
je @128
cmp al, 192
je @e // 192 bits is very complicated -> skip by now (128+256)
@256: movups xmm3, dqword ptr[pk + 16]
add pk, 32
aeskeygenassist xmm2, xmm3, 1
call @exp256
aeskeygenassist xmm2, xmm3, 2
call @exp256
aeskeygenassist xmm2, xmm3, 4
call @exp256
aeskeygenassist xmm2, xmm3, 8
call @exp256
aeskeygenassist xmm2, xmm3, $10
call @exp256
aeskeygenassist xmm2, xmm3, $20
call @exp256
aeskeygenassist xmm2, xmm3, $40
pshufd xmm2, xmm2, $FF
movups xmm4, xmm1
pshufb xmm4, xmm5
pxor xmm1, xmm4
pshufb xmm4, xmm5
pxor xmm1, xmm4
pshufb xmm4, xmm5
pxor xmm1, xmm4
pxor xmm1, xmm2
movups dqword ptr[pk], xmm1
jmp @e
{$ifdef FPC} align 16 {$else} .align 16 {$endif}
@mask: dd $ffffffff
dd $03020100
dd $07060504
dd $0b0a0908
@exp256:pshufd xmm2, xmm2, $ff
movups xmm4, xmm1
pshufb xmm4, xmm5
pxor xmm1, xmm4
pshufb xmm4, xmm5
pxor xmm1, xmm4
pshufb xmm4, xmm5
pxor xmm1, xmm4
pxor xmm1, xmm2
movups dqword ptr[pk], xmm1
add pk, $10
aeskeygenassist xmm4, xmm1, 0
pshufd xmm2, xmm4, $AA
movups xmm4, xmm3
pshufb xmm4, xmm5
pxor xmm3, xmm4
pshufb xmm4, xmm5
pxor xmm3, xmm4
pshufb xmm4, xmm5
pxor xmm3, xmm4
pxor xmm3, xmm2
movups dqword ptr[pk], xmm3
add pk, $10
@e: ret
@exp128:pshufd xmm2, xmm2, $FF
movups xmm3, xmm1
pshufb xmm3, xmm5
pxor xmm1, xmm3
pshufb xmm3, xmm5
pxor xmm1, xmm3
pshufb xmm3, xmm5
pxor xmm1, xmm3
pxor xmm1, xmm2
movups dqword ptr[pk], xmm1
add pk, $10
ret
@128: add pk, 16
aeskeygenassist xmm2, xmm1, 1
call @exp128
aeskeygenassist xmm2, xmm1, 2
call @exp128
aeskeygenassist xmm2, xmm1, 4
call @exp128
aeskeygenassist xmm2, xmm1, 8
call @exp128
aeskeygenassist xmm2, xmm1, $10
call @exp128
aeskeygenassist xmm2, xmm1, $20
call @exp128
aeskeygenassist xmm2, xmm1, $40
call @exp128
aeskeygenassist xmm2, xmm1, $80
call @exp128
aeskeygenassist xmm2, xmm1, $1b
call @exp128
aeskeygenassist xmm2, xmm1, $36
call @exp128
end;
{$endif CPU64}
{$endif USEAESNI}
function TAES.EncryptInit(const Key; KeySize: cardinal): boolean;
procedure Shift(KeySize: cardinal; pk: PAWK);
var i: integer;
temp: cardinal;
begin
// 32 bit use shift and mask
case KeySize of
128:
for i := 0 to 9 do begin
temp := pK^[3];
// SubWord(RotWord(temp)) if "word" count mod 4 = 0
pK^[4] := ((SBox[(temp shr 8) and $ff]) ) xor
((SBox[(temp shr 16) and $ff]) shl 8) xor
((SBox[(temp shr 24) ]) shl 16) xor
((SBox[(temp ) and $ff]) shl 24) xor
pK^[0] xor RCon[i];
pK^[5] := pK^[1] xor pK^[4];
pK^[6] := pK^[2] xor pK^[5];
pK^[7] := pK^[3] xor pK^[6];
inc(PByte(pK),4*4);
end;
192:
for i := 0 to 7 do begin
temp := pK^[5];
// SubWord(RotWord(temp)) if "word" count mod 6 = 0
pK^[ 6] := ((SBox[(temp shr 8) and $ff]) ) xor
((SBox[(temp shr 16) and $ff]) shl 8) xor
((SBox[(temp shr 24) ]) shl 16) xor
((SBox[(temp ) and $ff]) shl 24) xor
pK^[0] xor RCon[i];
pK^[ 7] := pK^[1] xor pK^[6];
pK^[ 8] := pK^[2] xor pK^[7];
pK^[ 9] := pK^[3] xor pK^[8];
if i=7 then exit;
pK^[10] := pK^[4] xor pK^[ 9];
pK^[11] := pK^[5] xor pK^[10];
inc(PByte(pK),6*4);
end;
else // 256:
for i := 0 to 6 do begin
temp := pK^[7];
// SubWord(RotWord(temp)) if "word" count mod 8 = 0
pK^[ 8] := ((SBox[(temp shr 8) and $ff]) ) xor
((SBox[(temp shr 16) and $ff]) shl 8) xor
((SBox[(temp shr 24) ]) shl 16) xor
((SBox[(temp ) and $ff]) shl 24) xor
pK^[0] xor RCon[i];
pK^[ 9] := pK^[1] xor pK^[ 8];
pK^[10] := pK^[2] xor pK^[ 9];
pK^[11] := pK^[3] xor pK^[10];
if i=6 then exit;
temp := pK^[11];
// SubWord(temp) if "word" count mod 8 = 4
pK^[12] := ((SBox[(temp ) and $ff]) ) xor
((SBox[(temp shr 8) and $ff]) shl 8) xor
((SBox[(temp shr 16) and $ff]) shl 16) xor
((SBox[(temp shr 24) ]) shl 24) xor
pK^[4];
pK^[13] := pK^[5] xor pK^[12];
pK^[14] := pK^[6] xor pK^[13];
pK^[15] := pK^[7] xor pK^[14];
inc(PByte(pK),8*4);
end;
end;
end;
var Nk: integer;
ctx: TAESContext absolute Context;
begin
result := true;
ctx.Initialized := true;
{$ifdef USEPADLOCK}
if DoPadlockInit(Key,KeySize) then begin
ctx.DoBlock := @aesencryptpadlock;
exit; // Init OK
end;
{$endif}
if (KeySize<>128) and (KeySize<>192) and (KeySize<>256) then begin
result := false;
ctx.Initialized := false;
exit;
end;
Nk := KeySize div 32;
MoveFast(Key, ctx.RK, 4*Nk);
// aes128ofb: aesencryptpas=140MB/s aesencryptx64=200MB/s aesniencrypt=500MB/s
{$ifdef CPUX64}
ctx.DoBlock := @aesencryptx64;
{$else}
{$ifdef CPUX86_NOTPIC}
ctx.DoBlock := @aesencrypt386;
{$else}
ctx.DoBlock := @aesencryptpas;
{$endif}
{$endif}
{$ifdef CPUINTEL}
{$ifdef USEAESNI}
if cfAESNI in CpuFeatures then begin
case KeySize of
128: ctx.DoBlock := @aesniencrypt128;
192: ctx.DoBlock := @aesniencrypt192;
256: ctx.DoBlock := @aesniencrypt256;
end;
{$ifdef USEAESNI32}
case KeySize of
128: ctx.AesNi32 := @AesNiEncryptXmm7_128;
192: ctx.AesNi32 := @AesNiEncryptXmm7_192;
256: ctx.AesNi32 := @AesNiEncryptXmm7_256;
end;
{$endif}
end;
{$endif}
{$endif}
ctx.Rounds := 6+Nk;
ctx.KeyBits := KeySize;
// Calculate encryption round keys
{$ifdef USEAESNI} // 192 is more complex and seldom used -> skip to pascal
if (KeySize<>192) and (cfAESNI in CpuFeatures) then
ShiftAesNi(KeySize,@ctx.RK) else
{$endif}
Shift(KeySize,pointer(@ctx.RK));
end;
{$ifdef USEAESNI} // should be put outside the main method for FPC :(
{$ifdef CPU32}
procedure MakeDecrKeyAesNi(Rounds: integer; RK: Pointer);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=Rounds edx=RK
sub eax, 9
movups xmm0, [edx + $10]
movups xmm1, [edx + $20]
movups xmm2, [edx + $30]
movups xmm3, [edx + $40]
movups xmm4, [edx + $50]
movups xmm5, [edx + $60]
movups xmm6, [edx + $70]
movups xmm7, [edx + $80]
{$ifdef HASAESNI}
aesimc xmm0, xmm0
aesimc xmm1, xmm1
aesimc xmm2, xmm2
aesimc xmm3, xmm3
aesimc xmm4, xmm4
aesimc xmm5, xmm5
aesimc xmm6, xmm6
aesimc xmm7, xmm7
{$else}
db $66, $0F, $38, $DB, $C0
db $66, $0F, $38, $DB, $C9
db $66, $0F, $38, $DB, $D2
db $66, $0F, $38, $DB, $DB
db $66, $0F, $38, $DB, $E4
db $66, $0F, $38, $DB, $ED
db $66, $0F, $38, $DB, $F6
db $66, $0F, $38, $DB, $FF
{$endif}
movups [edx + $10], xmm0
movups [edx + $20], xmm1
movups [edx + $30], xmm2
movups [edx + $40], xmm3
movups [edx + $50], xmm4
movups [edx + $60], xmm5
movups [edx + $70], xmm6
movups [edx + $80], xmm7
lea edx, [edx + $90]
@loop: movups xmm0, [edx]
db $66, $0F, $38, $DB, $C0 // aesimc xmm0,xmm0
movups [edx], xmm0
dec eax
lea edx, [edx + 16]
jnz @loop
end;
{$endif CPU32}
{$ifdef CPU64}
procedure MakeDecrKeyAesNi(Rounds: integer; RK: Pointer);
{$ifdef FPC} nostackframe; assembler; asm {$else} asm .noframe {$endif}
mov eax, Rounds
sub eax, 9
movups xmm0, dqword ptr[RK + $10]
movups xmm1, dqword ptr[RK + $20]
movups xmm2, dqword ptr[RK + $30]
movups xmm3, dqword ptr[RK + $40]
movups xmm4, dqword ptr[RK + $50]
movups xmm5, dqword ptr[RK + $60]
movups xmm6, dqword ptr[RK + $70]
movups xmm7, dqword ptr[RK + $80]
aesimc xmm0, xmm0
aesimc xmm1, xmm1
aesimc xmm2, xmm2
aesimc xmm3, xmm3
aesimc xmm4, xmm4
aesimc xmm5, xmm5
aesimc xmm6, xmm6
aesimc xmm7, xmm7
movups dqword ptr[RK + $10], xmm0
movups dqword ptr[RK + $20], xmm1
movups dqword ptr[RK + $30], xmm2
movups dqword ptr[RK + $40], xmm3
movups dqword ptr[RK + $50], xmm4
movups dqword ptr[RK + $60], xmm5
movups dqword ptr[RK + $70], xmm6
movups dqword ptr[RK + $80], xmm7
lea RK, [RK + $90]
@loop: movups xmm0, dqword ptr[RK]
aesimc xmm0, xmm0
movups dqword ptr[RK], xmm0
dec eax
lea RK, [RK + 16]
jnz @loop
end;
{$endif CPU64}
{$endif USEAESNI}
{$ifdef USEPADLOCK}
procedure aesdecryptpadlock(const ctxt: TAESContext; bi, bo: PWA4);
begin
padlock_aes_decrypt(ctxt.ViaCtx,bi,bo,16);
end;
{$endif}
procedure aesdecryptpas(const ctxt: TAESContext; bi, bo: PWA4);
var
{$ifdef AES_ROLLED}
s0,s1,s2,s3: PtrUInt; // TAESBlock s# as separate variables
t0,t1,t2: cardinal; // TAESBlock t# as separate variables
i: integer;
pk: PWA4;
{$else}
s0,s1,s2,s3,t0,t1,t2,t3: cardinal; // TAESBlock s#/t# as separate variables
pk: PAWk; // pointer to loop rount key
{$endif}
t: PCardinalArray; // faster on a PIC system
begin
t := @Td0;
{$ifdef AES_ROLLED}
// Wolfgang Ehrhardt rolled version - faster on modern CPU than unrolled one below
// Setup key pointer
pk := PWA4(@ctxt.RK[ctxt.Rounds]);
// Initialize with input block
s0 := bi[0] xor pk[0];
s1 := bi[1] xor pk[1];
s2 := bi[2] xor pk[2];
s3 := bi[3] xor pk[3];
dec(pk);
for I := 1 to ctxt.Rounds-1 do begin
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24];
s3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[3];
s0 := t0 xor pk[0];
s1 := t1 xor pk[1];
s2 := t2 xor pk[2];
dec(pk);
end;
bo[0] := ((InvSBox[s0 and $ff]) xor
(InvSBox[s3 shr 8 and $ff]) shl 8 xor
(InvSBox[s2 shr 16 and $ff]) shl 16 xor
(InvSBox[s1 shr 24]) shl 24 ) xor pk[0];
bo[1] := ((InvSBox[s1 and $ff]) xor
(InvSBox[s0 shr 8 and $ff]) shl 8 xor
(InvSBox[s3 shr 16 and $ff]) shl 16 xor
(InvSBox[s2 shr 24]) shl 24 ) xor pk[1];
bo[2] := ((InvSBox[s2 and $ff]) xor
(InvSBox[s1 shr 8 and $ff]) shl 8 xor
(InvSBox[s0 shr 16 and $ff]) shl 16 xor
(InvSBox[s3 shr 24]) shl 24 ) xor pk[2];
bo[3] := ((InvSBox[s3 and $ff]) xor
(InvSBox[s2 shr 8 and $ff]) shl 8 xor
(InvSBox[s1 shr 16 and $ff]) shl 16 xor
(InvSBox[s0 shr 24]) shl 24 ) xor pk[3];
{$else} // unrolled version (WE6) from Wolfgang Ehrhardt - slower
// Setup key pointer
pk := PAWk(@ctxt.RK);
// Initialize with input block
s0 := bi[0] xor pk[0];
s1 := bi[1] xor pk[1];
s2 := bi[2] xor pk[2];
s3 := bi[3] xor pk[3];
// Round 1
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[4];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[5];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[6];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[7];
// Round 2
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[8];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[9];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[10];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[11];
// Round 3
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[12];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[13];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[14];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[15];
// Round 4
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[16];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[17];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[18];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[19];
// Round 5
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[20];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[21];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[22];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[23];
// Round 6
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[24];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[25];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[26];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[27];
// Round 7
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[28];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[29];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[30];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[31];
// Round 8
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[32];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[33];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[34];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[35];
// Round 9
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[36];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[37];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[38];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[39];
if ctxt.rounds>10 then begin
// Round 10
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[40];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[41];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[42];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[43];
// Round 11
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[44];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[45];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[46];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[47];
if ctxt.rounds>12 then begin
// Round 12
s0 := t[t0 and $ff] xor t[$100+t3 shr 8 and $ff] xor t[$200+t2 shr 16 and $ff] xor t[$300+t1 shr 24] xor pk[48];
s1 := t[t1 and $ff] xor t[$100+t0 shr 8 and $ff] xor t[$200+t3 shr 16 and $ff] xor t[$300+t2 shr 24] xor pk[49];
s2 := t[t2 and $ff] xor t[$100+t1 shr 8 and $ff] xor t[$200+t0 shr 16 and $ff] xor t[$300+t3 shr 24] xor pk[50];
s3 := t[t3 and $ff] xor t[$100+t2 shr 8 and $ff] xor t[$200+t1 shr 16 and $ff] xor t[$300+t0 shr 24] xor pk[51];
// Round 13
t0 := t[s0 and $ff] xor t[$100+s3 shr 8 and $ff] xor t[$200+s2 shr 16 and $ff] xor t[$300+s1 shr 24] xor pk[52];
t1 := t[s1 and $ff] xor t[$100+s0 shr 8 and $ff] xor t[$200+s3 shr 16 and $ff] xor t[$300+s2 shr 24] xor pk[53];
t2 := t[s2 and $ff] xor t[$100+s1 shr 8 and $ff] xor t[$200+s0 shr 16 and $ff] xor t[$300+s3 shr 24] xor pk[54];
t3 := t[s3 and $ff] xor t[$100+s2 shr 8 and $ff] xor t[$200+s1 shr 16 and $ff] xor t[$300+s0 shr 24] xor pk[55];
end;
end;
inc(PByte(pk), (ctxt.rounds shl 4));
// Uses InvSBox and shl, needs type cast cardinal() for
// 16 bit compilers: here InvSBox is byte, Td4 is cardinal
bo[0] := ((InvSBox[t0 and $ff]) xor
(InvSBox[t3 shr 8 and $ff]) shl 8 xor
(InvSBox[t2 shr 16 and $ff]) shl 16 xor
(InvSBox[t1 shr 24]) shl 24) xor pk[0];
bo[1] := ((InvSBox[t1 and $ff]) xor
(InvSBox[t0 shr 8 and $ff]) shl 8 xor
(InvSBox[t3 shr 16 and $ff]) shl 16 xor
(InvSBox[t2 shr 24]) shl 24) xor pk[1];
bo[2] := ((InvSBox[t2 and $ff]) xor
(InvSBox[t1 shr 8 and $ff]) shl 8 xor
(InvSBox[t0 shr 16 and $ff]) shl 16 xor
(InvSBox[t3 shr 24]) shl 24) xor pk[2];
bo[3] := ((InvSBox[t3 and $ff]) xor
(InvSBox[t2 shr 8 and $ff]) shl 8 xor
(InvSBox[t1 shr 16 and $ff]) shl 16 xor
(InvSBox[t0 shr 24]) shl 24) xor pk[3];
{$endif AES_ROLLED}
end;
{$ifdef CPUX86}
{$ifdef USEAESNI}
procedure aesnidecrypt128(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
movups xmm7, [edx]
movups xmm0, [eax + 16 * 10]
movups xmm1, [eax + 16 * 9]
movups xmm2, [eax + 16 * 8]
movups xmm3, [eax + 16 * 7]
movups xmm4, [eax + 16 * 6]
movups xmm5, [eax + 16 * 5]
movups xmm6, [eax + 16 * 4]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
{$else}
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DE, $FB
db $66, $0F, $38, $DE, $FC
{$endif}
movups xmm0, [eax + 16 * 3]
movups xmm1, [eax + 16 * 2]
movups xmm2, [eax + 16 * 1]
movups xmm3, [eax + 16 * 0]
{$ifdef HASAESNI}
aesdec xmm7, xmm5
aesdec xmm7, xmm6
aesdec xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdeclast xmm7, xmm3
{$else}
db $66, $0F, $38, $DE, $FD
db $66, $0F, $38, $DE, $FE
db $66, $0F, $38, $DE, $F8
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DF, $FB
{$endif}
movups [ecx], xmm7
pxor xmm7, xmm7
end;
procedure aesnidecrypt192(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
movups xmm7, [edx]
movups xmm0, [eax + 16 * 12]
movups xmm1, [eax + 16 * 11]
movups xmm2, [eax + 16 * 10]
movups xmm3, [eax + 16 * 9]
movups xmm4, [eax + 16 * 8]
movups xmm5, [eax + 16 * 7]
movups xmm6, [eax + 16 * 6]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
{$else}
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DE, $FB
db $66, $0F, $38, $DE, $FC
db $66, $0F, $38, $DE, $FD
db $66, $0F, $38, $DE, $FE
{$endif}
movups xmm0, [eax + 16 * 5]
movups xmm1, [eax + 16 * 4]
movups xmm2, [eax + 16 * 3]
movups xmm3, [eax + 16 * 2]
movups xmm4, [eax + 16 * 1]
movups xmm5, [eax + 16 * 0]
{$ifdef HASAESNI}
aesdec xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdeclast xmm7, xmm5
{$else}
db $66, $0F, $38, $DE, $F8
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DE, $FB
db $66, $0F, $38, $DE, $FC
db $66, $0F, $38, $DF, $FD
{$endif}
movups [ecx], xmm7
pxor xmm7, xmm7
end;
procedure aesnidecrypt256(const ctxt, source, dest);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
movups xmm7, [edx]
movups xmm0, [eax + 16 * 14]
movups xmm1, [eax + 16 * 13]
movups xmm2, [eax + 16 * 12]
movups xmm3, [eax + 16 * 11]
movups xmm4, [eax + 16 * 10]
movups xmm5, [eax + 16 * 9]
movups xmm6, [eax + 16 * 8]
pxor xmm7, xmm0
{$ifdef HASAESNI}
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
{$else}
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DE, $FB
db $66, $0F, $38, $DE, $FC
db $66, $0F, $38, $DE, $FD
db $66, $0F, $38, $DE, $FE
{$endif}
movups xmm0, [eax + 16 * 7]
movups xmm1, [eax + 16 * 6]
movups xmm2, [eax + 16 * 5]
movups xmm3, [eax + 16 * 4]
movups xmm4, [eax + 16 * 3]
movups xmm5, [eax + 16 * 2]
movups xmm6, [eax + 16 * 1]
{$ifdef HASAESNI}
aesdec xmm7, xmm0
aesdec xmm7, xmm1
aesdec xmm7, xmm2
aesdec xmm7, xmm3
aesdec xmm7, xmm4
aesdec xmm7, xmm5
aesdec xmm7, xmm6
{$else}
db $66, $0F, $38, $DE, $F8
db $66, $0F, $38, $DE, $F9
db $66, $0F, $38, $DE, $FA
db $66, $0F, $38, $DE, $FB
db $66, $0F, $38, $DE, $FC
db $66, $0F, $38, $DE, $FD
db $66, $0F, $38, $DE, $FE
{$endif}
movups xmm0, [eax + 16 * 0]
{$ifdef HASAESNI}
aesdeclast xmm7, xmm0
{$else}
db $66, $0F, $38, $DF, $F8
{$endif}
movups [ecx], xmm7
pxor xmm7, xmm7
end;
{$endif}
{$ifdef CPUX86_NOTPIC}
procedure aesdecrypt386(const ctxt: TAESContext; bi, bo: PWA4);
{$ifdef FPC} nostackframe; assembler; {$endif}
asm
push ebx
push esi
push edi
push ebp
add esp, - 20
mov [esp], ecx
movzx ecx, byte ptr[eax].taescontext.rounds
lea esi, [4 * ecx]
lea ecx, [ecx - 1]
lea eax, [eax + 4 * esi] // eax=@ctx.rk[ctx.rounds]=pk
mov [esp + 16], ecx // [esp+16]=ctx.round
mov ebx, [edx]
xor ebx, [eax]
mov esi, [edx + 4]
xor esi, [eax + 4]
mov ecx, [edx + 8]
xor ecx, [eax + 8]
mov edx, [edx + 12]
xor edx, [eax + 12]
lea eax, [eax - 16]
@1: // pk=eax s0=ebx s1=esi s2=ecx s3=edx
movzx edi, bl
mov edi, dword ptr[4 * edi + td0]
movzx ebp, dh
xor edi, dword ptr[4 * ebp + td1]
mov ebp, ecx
shr ebp, $10
and ebp, 255
xor edi, dword ptr[4 * ebp + td2]
mov ebp, esi
shr ebp, $18
xor edi, dword ptr[4 * ebp + td3]
mov [esp + 4], edi
mov edi, esi
and edi, 255
mov edi, dword ptr[4 * edi + td0]
movzx ebp, bh
xor edi, dword ptr[4 * ebp + td1]
mov ebp, edx
shr ebp, $10
and ebp, 255
xor edi, dword ptr[4 * ebp + td2]
mov ebp, ecx
shr ebp, $18
xor edi, dword ptr[4 * ebp + td3]
mov [esp + 8], edi
movzx edi, cl
mov edi, dword ptr[4 * edi + td0]
movzx ebp, si
shr ebp, $08
xor edi, dword ptr[4 * ebp + td1]
mov ebp, ebx
shr ebp, $10
and ebp, 255
xor edi, dword ptr[4 * ebp + td2]
mov ebp, edx
shr ebp, $18
xor edi, dword ptr[4 * ebp + td3]
mov [esp + 12], edi
and edx, 255
mov edx, dword ptr[4 * edx + td0]
movzx ecx, ch
xor edx, dword ptr[4 * ecx + td1]
shr esi, $10
and esi, 255
xor edx, dword ptr[4 * esi + td2]
shr ebx, $18
xor edx, dword ptr[4 * ebx + td3]
xor edx, [eax + 12]
mov ebx, [eax]
xor ebx, [esp + 4]
mov esi, [eax + 4]
xor esi, [esp + 8]
mov ecx, [eax + 8]
xor ecx, [esp + 12]
lea eax, [eax - 16]
dec byte ptr[esp + 16]
jnz @1
mov ebp, eax
movzx eax, bl
movzx eax, byte ptr[eax + invsbox]
movzx edi, dh
movzx edi, byte ptr[edi + invsbox]
shl edi, $08
xor eax, edi
mov edi, ecx
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + invsbox]
shl edi, $10
xor eax, edi
mov edi, esi
shr edi, $18
movzx edi, byte ptr[edi + invsbox]
shl edi, $18
xor eax, edi
xor eax, [ebp]
mov edi, [esp]
mov [edi], eax
mov eax, esi
and eax, 255
movzx eax, byte ptr[eax + invsbox]
movzx edi, bh
movzx edi, byte ptr[edi + invsbox]
shl edi, $08
xor eax, edi
mov edi, edx
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + invsbox]
shl edi, $10
xor eax, edi
mov edi, ecx
shr edi, $18
movzx edi, byte ptr[edi + invsbox]
shl edi, $18
xor eax, edi
xor eax, [ebp + 4]
mov edi, [esp]
mov [edi + 4], eax
movzx eax, cl
movzx eax, byte ptr[eax + invsbox]
movzx edi, si
shr edi, $08
movzx edi, byte ptr[edi + invsbox]
shl edi, $08
xor eax, edi
mov edi, ebx
shr edi, $10
and edi, 255
movzx edi, byte ptr[edi + invsbox]
shl edi, $10
xor eax, edi
mov edi, edx
shr edi, $18
movzx edi, byte ptr[edi + invsbox]
shl edi, $18
xor eax, edi
xor eax, [ebp + 8]
mov edi, [esp]
mov [edi + 8], eax
and edx, 255
movzx eax, byte ptr[edx + invsbox]
shr ecx, $08
and ecx, 255
movzx edx, byte ptr[ecx + invsbox]
shl edx, $08
xor eax, edx
shr esi, $10
and esi, 255
movzx edx, byte ptr[esi + invsbox]
shl edx, $10
xor eax, edx
shr ebx, $18
movzx edx, byte ptr[ebx + invsbox]
shl edx, $18
xor eax, edx
xor eax, [ebp + 12]
mov [edi + 12], eax
add esp, 20
pop ebp
pop edi
pop esi
pop ebx
end;
{$endif CPUX86_NOTPIC}
{$endif CPUX86}
procedure MakeDecrKey(rounds: integer; k: PAWk);
// Calculate decryption key from encryption key
var x: cardinal;
{$ifndef AES_ROLLED}
i,j: integer;
{$endif}
t: PCardinalArray; // faster on a PIC system
s: PByteArray;
begin
{$ifndef AES_ROLLED} // inversion is needed only for fully unrolled version
i := 0;
j := 4*rounds;
while i<j do begin
x := k[i]; k[i] := k[j]; k[j] := x;
x := k[i+1]; k[i+1] := k[j+1]; k[j+1] := x;
x := k[i+2]; k[i+2] := k[j+2]; k[j+2] := x;
x := k[i+3]; k[i+3] := k[j+3]; k[j+3] := x;
inc(i,4);
dec(j,4);
end;
{$endif}
t := @Td0;
s := @SBox;
repeat
inc(PByte(k),16);
dec(rounds);
x := k[0];
k[0] := t[$300+s[x shr 24]] xor t[$200+s[x shr 16 and $ff]] xor
t[$100+s[x shr 8 and $ff]] xor t[s[x and $ff]];
x := k[1];
k[1] := t[$300+s[x shr 24]] xor t[$200+s[x shr 16 and $ff]] xor
t[$100+s[x shr 8 and $ff]] xor t[s[x and $ff]];
x := k[2];
k[2] := t[$300+s[x shr 24]] xor t[$200+s[x shr 16 and $ff]] xor
t[$100+s[x shr 8 and $ff]] xor t[s[x and $ff]];
x := k[3];
k[3] := t[$300+s[x shr 24]] xor t[$200+s[x shr 16 and $ff]] xor
t[$100+s[x shr 8 and $ff]] xor t[s[x and $ff]];
until rounds=1;
end;
function TAES.DecryptInitFrom(const Encryption{$ifndef DELPHI5OROLDER}: TAES{$endif};
const Key; KeySize: cardinal): boolean;
var ctx: TAESContext absolute Context;
begin
{$ifdef USEPADLOCK}
if DoPadlockInit(Key,KeySize) then begin
result := true;
ctx.Initialized := true;
ctx.DoBlock := @aesdecryptpadlock;
exit; // Init OK
end;
{$endif}
ctx.Initialized := false;
if not {$ifdef DELPHI5OROLDER}TAES{$endif}(Encryption).Initialized then
// e.g. called from DecryptInit()
EncryptInit(Key, KeySize) else // contains Initialized := true
self := {$ifdef DELPHI5OROLDER}TAES{$endif}(Encryption);
result := ctx.Initialized;
if not result then
exit;
{$ifdef CPUX86_NOTPIC}
ctx.DoBlock := @aesdecrypt386;
{$else}
ctx.DoBlock := @aesdecryptpas;
{$endif}
{$ifdef USEAESNI}
if cfAESNI in CpuFeatures then begin
MakeDecrKeyAesNi(ctx.Rounds,@ctx.RK);
case KeySize of
128: ctx.DoBlock := @aesnidecrypt128;
192: ctx.DoBlock := @aesnidecrypt192;
256: ctx.DoBlock := @aesnidecrypt256;
end;
end else
{$endif}
MakeDecrKey(ctx.Rounds,@ctx.RK);
end;
function TAES.DecryptInit(const Key; KeySize: cardinal): boolean;
begin
result := DecryptInitFrom(self, Key, KeySize);
end;
procedure TAES.Decrypt(var B: TAESBlock);
begin
TAESContext(Context).DoBlock(Context,B,B);
end;
procedure TAES.Decrypt(const BI: TAESBlock; var BO: TAESBlock);
begin
TAESContext(Context).DoBlock(Context,BI,BO);
end;
procedure TAES.DoBlocks(pIn, pOut: PAESBlock; out oIn, oOut: PAESBLock;
Count: integer; doEncrypt: boolean);
var i: integer;
ctx: TAESContext absolute Context;
begin
{$ifdef USEPADLOCK}
// assert(PtrUInt(pIn) and $F=0); // must be 16 bytes aligned
if ctx.ViaCtx<>nil then begin
if Count<>0 then begin
Count := Count shl AESBlockShift;
if doEncrypt then
padlock_aes_encrypt(ctx.ViaCtx,pIn,pOut,Count) else
padlock_aes_decrypt(ctx.ViaCtx,pIn,pOut,Count);
end;
oIn := pointer(PtrUInt(pIn)+PtrUInt(Count));
oOut := pointer(PtrUInt(pOut)+PtrUInt(Count));
exit;
end;
{$endif}
for i := 1 to Count do begin
ctx.DoBlock(ctx,pIn^,pOut^);
inc(pIn);
inc(pOut);
end;
oIn := pIn;
oOut := pOut;
end;
function TAES.DoInit(const Key; KeySize: cardinal; doEncrypt: boolean): boolean;
begin
if doEncrypt then
result := EncryptInit(Key, KeySize) else
result := DecryptInit(Key,KeySize);
end;
procedure TAES.DoBlocks(pIn, pOut: PAESBlock; Count: integer; doEncrypt: boolean);
begin
DoBlocks(pIn,pOut,pIn,pOut,Count,doEncrypt);
end;
procedure TAES.DoBlocksOFB(const iv: TAESBlock; src, dst: pointer; blockcount: PtrUInt);
var cv: TAESBlock;
begin
cv := iv;
while blockcount > 0 do begin
dec(blockcount);
TAESContext(Context).DoBlock(Context,cv,cv);
XorBlock16(src,dst,pointer(@cv));
inc(PByte(src),SizeOf(TAESBlock));
inc(PByte(dst),SizeOf(TAESBlock));
end;
end;
function TAES.Initialized: boolean;
begin
result := TAESContext(Context).Initialized;
end;
function TAES.UsesAESNI: boolean;
begin
{$ifdef CPUINTEL}
result := cfAESNI in CpuFeatures;
{$else}
result := false;
{$endif}
end;
function TAES.KeyBits: integer;
begin
result := TAESContext(Context).KeyBits;
end;
procedure TAES.Done;
var ctx: TAESContext absolute Context;
begin
{$ifdef USEPADLOCK}
if Initialized and padlock_available and (ctx.ViaCtx<>nil) then
padlock_aes_close(ctx.ViaCtx);
{$endif USEPADLOCK}
FillcharFast(ctx,sizeof(ctx),0); // always erase key in memory after use
end;
{$ifdef USETHREADSFORBIGAESBLOCKS}
type
TThreadParams = record
bIn, bOut: pAESBlock;
BlockCount,BlockIndex: integer;
Encrypt: boolean;
ID: DWORD;
AES: TAES;
end;
{ we use direct Windows threads, since we don't need any exception handling
nor memory usage inside the Thread handler
-> avoid classes.TThread and system.BeginThread() use
-> application is still "officialy" mono-threaded (i.e. IsMultiThread=false),
for faster System.pas and FastMM4 (no locking)
-> code is even shorter then original one using TThread }
function ThreadWrapper(var P: TThreadParams): Integer; stdcall;
begin
with P do
AES.DoBlocks(bIn,bOut,bIn,bOut,BlockCount,Encrypt);
ExitThread(0);
result := 0; // make the compiler happy, but won't never be called
end;
procedure TAES.DoBlocksThread(var bIn, bOut: PAESBlock; Count: integer; doEncrypt: boolean);
var Thread: array[0..3] of TThreadParams; // faster than dynamic array
Handle: array[0..3] of THandle; // high(Thread) is not compiled by XE2
nThread, i, nOne: integer;
pIn, pOut: PAESBlock;
begin
if Count=0 then exit;
if {$ifdef USEPADLOCK} padlock_available or {$endif}
{$ifdef USEAESNI} (cfAESNI in CpuFeatures) or {$endif}
(SystemInfo.dwNumberOfProcessors<=1) or // (DebugHook<>0) or
(Count<((512*1024) shr AESBlockShift)) then begin // not needed below 512 KB
DoBlocks(bIn,bOut,bIn,bOut,Count,doEncrypt);
exit;
end;
nThread := SystemInfo.dwNumberOfProcessors;
if nThread>length(Thread) then // a quad-core is enough ;)
nThread := length(Thread);
nOne := Count div nThread;
pIn := bIn;
pOut := bOut;
for i := 0 to nThread-1 do
with Thread[i] do begin // create threads parameters
bIn := pIn;
bOut := pOut;
BlockCount := nOne;
BlockIndex := i+1;
Encrypt := doEncrypt;
AES := self; // local copy of the AES context for every thread
Handle[i] := CreateThread(nil,0,@ThreadWrapper,@Thread[i],0,ID);
inc(pIn,nOne);
inc(pOut,nOne);
dec(Count,nOne);
end;
if Count>0 then
DoBlocks(pIn,pOut,pIn,pOut,Count,doEncrypt); // remaining blocks
{$ifopt C+}
inc(Count,nOne*nThread);
assert(PtrUInt(pIn)-PtrUInt(bIn)=cardinal(Count)shl AESBlockShift);
assert(PtrUInt(pOut)-PtrUInt(bOut)=cardinal(Count)shl AESBlockShift);
{$endif}
bIn := pIn;
bOut := pOut;
WaitForMultipleObjects(nThread,@Handle[0],True,INFINITE);
for i := 0 to nThread-1 do
CloseHandle(Handle[i]);
end;
{$endif USETHREADSFORBIGAESBLOCKS}
{ AES-GCM Support }
const
// lookup table as used by mul_x/gf_mul/gf_mul_h
gft_le: array[byte] of word = (
$0000, $c201, $8403, $4602, $0807, $ca06, $8c04, $4e05,
$100e, $d20f, $940d, $560c, $1809, $da08, $9c0a, $5e0b,
$201c, $e21d, $a41f, $661e, $281b, $ea1a, $ac18, $6e19,
$3012, $f213, $b411, $7610, $3815, $fa14, $bc16, $7e17,
$4038, $8239, $c43b, $063a, $483f, $8a3e, $cc3c, $0e3d,
$5036, $9237, $d435, $1634, $5831, $9a30, $dc32, $1e33,
$6024, $a225, $e427, $2626, $6823, $aa22, $ec20, $2e21,
$702a, $b22b, $f429, $3628, $782d, $ba2c, $fc2e, $3e2f,
$8070, $4271, $0473, $c672, $8877, $4a76, $0c74, $ce75,
$907e, $527f, $147d, $d67c, $9879, $5a78, $1c7a, $de7b,
$a06c, $626d, $246f, $e66e, $a86b, $6a6a, $2c68, $ee69,
$b062, $7263, $3461, $f660, $b865, $7a64, $3c66, $fe67,
$c048, $0249, $444b, $864a, $c84f, $0a4e, $4c4c, $8e4d,
$d046, $1247, $5445, $9644, $d841, $1a40, $5c42, $9e43,
$e054, $2255, $6457, $a656, $e853, $2a52, $6c50, $ae51,
$f05a, $325b, $7459, $b658, $f85d, $3a5c, $7c5e, $be5f,
$00e1, $c2e0, $84e2, $46e3, $08e6, $cae7, $8ce5, $4ee4,
$10ef, $d2ee, $94ec, $56ed, $18e8, $dae9, $9ceb, $5eea,
$20fd, $e2fc, $a4fe, $66ff, $28fa, $eafb, $acf9, $6ef8,
$30f3, $f2f2, $b4f0, $76f1, $38f4, $faf5, $bcf7, $7ef6,
$40d9, $82d8, $c4da, $06db, $48de, $8adf, $ccdd, $0edc,
$50d7, $92d6, $d4d4, $16d5, $58d0, $9ad1, $dcd3, $1ed2,
$60c5, $a2c4, $e4c6, $26c7, $68c2, $aac3, $ecc1, $2ec0,
$70cb, $b2ca, $f4c8, $36c9, $78cc, $bacd, $fccf, $3ece,
$8091, $4290, $0492, $c693, $8896, $4a97, $0c95, $ce94,
$909f, $529e, $149c, $d69d, $9898, $5a99, $1c9b, $de9a,
$a08d, $628c, $248e, $e68f, $a88a, $6a8b, $2c89, $ee88,
$b083, $7282, $3480, $f681, $b884, $7a85, $3c87, $fe86,
$c0a9, $02a8, $44aa, $86ab, $c8ae, $0aaf, $4cad, $8eac,
$d0a7, $12a6, $54a4, $96a5, $d8a0, $1aa1, $5ca3, $9ea2,
$e0b5, $22b4, $64b6, $a6b7, $e8b2, $2ab3, $6cb1, $aeb0,
$f0bb, $32ba, $74b8, $b6b9, $f8bc, $3abd, $7cbf, $bebe);
procedure mul_x(var a: TAESBlock; const b: TAESBlock);
// {$ifdef HASINLINE}inline;{$endif} // inlining has no benefit here
var t: cardinal;
y: TWA4 absolute b;
const
MASK_80 = cardinal($80808080);
MASK_7F = cardinal($7f7f7f7f);
begin
t := gft_le[(y[3] shr 17) and MASK_80];
TWA4(a)[3] := ((y[3] shr 1) and MASK_7F) or (((y[3] shl 15) or (y[2] shr 17)) and MASK_80);
TWA4(a)[2] := ((y[2] shr 1) and MASK_7F) or (((y[2] shl 15) or (y[1] shr 17)) and MASK_80);
TWA4(a)[1] := ((y[1] shr 1) and MASK_7F) or (((y[1] shl 15) or (y[0] shr 17)) and MASK_80);
TWA4(a)[0] := (((y[0] shr 1) and MASK_7F) or ( (y[0] shl 15) and MASK_80)) xor t;
end;
procedure gf_mul(var a: TAESBlock; const b: TAESBlock);
var p: array[0..7] of TAESBlock;
x: TWA4;
t: cardinal;
i: PtrInt;
j: integer;
c: byte;
begin
p[0] := b;
for i := 1 to 7 do
mul_x(p[i], p[i-1]);
FillZero(TAESBlock(x));
for i:=0 to 15 do begin
c := a[15-i];
if i>0 then begin
// inlined mul_x8()
t := gft_le[x[3] shr 24];
x[3] := ((x[3] shl 8) or (x[2] shr 24));
x[2] := ((x[2] shl 8) or (x[1] shr 24));
x[1] := ((x[1] shl 8) or (x[0] shr 24));
x[0] := ((x[0] shl 8) xor t);
end;
for j:=0 to 7 do begin
if c and ($80 shr j) <> 0 then begin
x[3] := x[3] xor TWA4(p[j])[3];
x[2] := x[2] xor TWA4(p[j])[2];
x[1] := x[1] xor TWA4(p[j])[1];
x[0] := x[0] xor TWA4(p[j])[0];
end;
end;
end;
a := TAESBlock(x);
end;
{ TAESGCMEngine }
procedure TAESGCMEngine.Make4K_Table;
var j, k: PtrInt;
begin
gf_t4k[128] := ghash_h;
j := 64;
while j>0 do begin
mul_x(gf_t4k[j],gf_t4k[j+j]);
j := j shr 1;
end;
j := 2;
while j<256 do begin
for k := 1 to j-1 do
XorBlock16(@gf_t4k[k],@gf_t4k[j+k],@gf_t4k[j]);
inc(j,j);
end;
end;
procedure TAESGCMEngine.gf_mul_h(var a: TAESBlock);
var
x: TWA4;
i: PtrUInt;
t: cardinal;
p: PWA4;
{$ifdef CPUX86NOTPIC}
tab: TWordArray absolute gft_le;
{$else}
tab: PWordArray;
{$endif CPUX86NOTPIC}
begin
{$ifndef CPUX86NOTPIC}
tab := @gft_le;
{$endif CPUX86NOTPIC}
x := TWA4(gf_t4k[a[15]]);
for i := 14 downto 0 do begin
p := @gf_t4k[a[i]];
t := tab[x[3] shr 24];
// efficient mul_x8 and xor using pre-computed table entries
x[3] := ((x[3] shl 8) or (x[2] shr 24)) xor p^[3];
x[2] := ((x[2] shl 8) or (x[1] shr 24)) xor p^[2];
x[1] := ((x[1] shl 8) or (x[0] shr 24)) xor p^[1];
x[0] := ((x[0] shl 8) xor t) xor p^[0];
end;
a := TAESBlock(x);
end;
procedure GCM_IncCtr(var x: TAESBlock); {$ifdef HASINLINE} inline; {$endif}
begin
// in AES-GCM, CTR covers only 32 LSB Big-Endian bits, i.e. x[15]..x[12]
inc(x[15]);
if x[15]<>0 then
exit;
inc(x[14]);
if x[14]<>0 then
exit;
inc(x[13]);
if x[13]=0 then
inc(x[12]);
end;
procedure TAESGCMEngine.internal_crypt(ptp, ctp: PByte; ILen: PtrUInt);
var b_pos: PtrUInt;
begin
b_pos := blen;
inc(blen,ILen);
blen := blen and AESBlockMod;
if b_pos=0 then
b_pos := SizeOf(TAESBlock) else
while (ILen>0) and (b_pos<SizeOf(TAESBlock)) do begin
ctp^ := ptp^ xor TAESContext(actx).buf[b_pos];
inc(b_pos);
inc(ptp);
inc(ctp);
dec(ILen);
end;
while ILen>=SizeOf(TAESBlock) do begin
GCM_IncCtr(TAESContext(actx).IV);
actx.Encrypt(TAESContext(actx).IV,TAESContext(actx).buf); // maybe AES-NI
XorBlock16(pointer(ptp),pointer(ctp),@TAESContext(actx).buf);
inc(PAESBlock(ptp));
inc(PAESBlock(ctp));
dec(ILen,SizeOf(TAESBlock));
end;
while ILen>0 do begin
if b_pos=SizeOf(TAESBlock) then begin
GCM_IncCtr(TAESContext(actx).IV);
actx.Encrypt(TAESContext(actx).IV,TAESContext(actx).buf);
b_pos := 0;
end;
ctp^ := TAESContext(actx).buf[b_pos] xor ptp^;
inc(b_pos);
inc(ptp);
inc(ctp);
dec(ILen);
end;
end;
procedure TAESGCMEngine.internal_auth(ctp: PByte; ILen: PtrUInt;
var ghv: TAESBlock; var gcnt: TQWordRec);
var b_pos: PtrUInt;
begin
b_pos := gcnt.L and AESBlockMod;
inc(gcnt.V,ILen);
if (b_pos=0) and (gcnt.V<>0) then
gf_mul_h(ghv);
while (ILen>0) and (b_pos<SizeOf(TAESBlock)) do begin
ghv[b_pos] := ghv[b_pos] xor ctp^;
inc(b_pos);
inc(ctp);
dec(ILen);
end;
while ILen>=SizeOf(TAESBlock) do begin
gf_mul_h(ghv);
XorBlock16(@ghv,pointer(ctp));
inc(PAESBlock(ctp));
dec(ILen,SizeOf(TAESBlock));
end;
while ILen>0 do begin
if b_pos=SizeOf(TAESBlock) then begin
gf_mul_h(ghv);
b_pos := 0;
end;
ghv[b_pos] := ghv[b_pos] xor ctp^;
inc(b_pos);
inc(ctp);
dec(ILen);
end;
end;
function TAESGCMEngine.Init(const Key; KeyBits: PtrInt): boolean;
begin
FillcharFast(self,SizeOf(self),0);
result := actx.EncryptInit(Key,KeyBits);
if not result then
exit;
actx.Encrypt(ghash_h, ghash_h);
Make4K_Table;
end;
const
CTR_POS = 12;
function TAESGCMEngine.Reset(pIV: pointer; IV_len: PtrInt): boolean;
var i, n_pos: PtrInt;
begin
if (pIV=nil) or (IV_len=0) then begin
result := false;
exit;
end;
if IV_len=CTR_POS then begin
// Initialization Vector size matches perfect size of 12 bytes
MoveFast(pIV^,TAESContext(actx).IV,CTR_POS);
TWA4(TAESContext(actx).IV)[3] := $01000000;
end else begin
// Initialization Vector is otherwise computed from GHASH(IV,H)
n_pos := IV_len;
FillZero(TAESContext(actx).IV);
while n_pos>=SizeOf(TAESBlock) do begin
XorBlock16(@TAESContext(actx).IV,pIV);
inc(PAesBlock(pIV));
dec(n_pos,SizeOf(TAESBlock));
gf_mul_h(TAESContext(actx).IV);
end;
if n_pos>0 then begin
for i := 0 to n_pos-1 do
TAESContext(actx).IV[i] := TAESContext(actx).IV[i] xor PAESBlock(pIV)^[i];
gf_mul_h(TAESContext(actx).IV);
end;
n_pos := IV_len shl 3;
i := 15;
while n_pos>0 do begin
TAESContext(actx).IV[i] := TAESContext(actx).IV[i] xor byte(n_pos);
n_pos := n_pos shr 8;
dec(i);
end;
gf_mul_h(TAESContext(actx).IV);
end;
// reset internal state and counters
y0_val := TWA4(TAESContext(actx).IV)[3];
FillZero(aad_ghv);
FillZero(txt_ghv);
aad_cnt.V := 0;
atx_cnt.V := 0;
flags := [];
result := true;
end;
function TAESGCMEngine.Encrypt(ptp, ctp: Pointer; ILen: PtrInt): boolean;
begin
if ILen>0 then begin
if (ptp=nil) or (ctp=nil) or (flagFinalComputed in flags) then begin
result := false;
exit;
end;
if (ILen and AESBlockMod=0) and (blen=0) then begin
inc(atx_cnt.V,ILen);
ILen := ILen shr AESBlockShift;
repeat // loop optimized e.g. for PKCS7 padding
GCM_IncCtr(TAESContext(actx).IV);
actx.Encrypt(TAESContext(actx).IV,TAESContext(actx).buf); // maybe AES-NI
XorBlock16(ptp,ctp,@TAESContext(actx).buf);
gf_mul_h(txt_ghv);
XorBlock16(@txt_ghv,ctp);
inc(PAESBlock(ptp));
inc(PAESBlock(ctp));
dec(ILen);
until ILen=0;
end else begin // generic process in dual steps
internal_crypt(ptp,ctp,iLen);
internal_auth(ctp,ILen,txt_ghv,atx_cnt);
end;
end;
result := true;
end;
function TAESGCMEngine.Decrypt(ctp, ptp: Pointer; ILen: PtrInt;
ptag: pointer; tlen: PtrInt): boolean;
var tag: TAESBlock;
begin
result := false;
if ILen>0 then begin
if (ptp=nil) or (ctp=nil) or (flagFinalComputed in flags) then
exit;
if (ILen and AESBlockMod=0) and (blen=0) then begin
inc(atx_cnt.V,ILen);
ILen := ILen shr AESBlockShift;
repeat // loop optimized e.g. for PKCS7 padding
gf_mul_h(txt_ghv);
XorBlock16(@txt_ghv,ctp);
GCM_IncCtr(TAESContext(actx).IV);
actx.Encrypt(TAESContext(actx).IV,TAESContext(actx).buf); // maybe AES-NI
XorBlock16(ctp,ptp,@TAESContext(actx).buf);
inc(PAESBlock(ptp));
inc(PAESBlock(ctp));
dec(ILen);
until ILen=0;
if (ptag<>nil) and (tlen>0) then begin
Final(tag,{anddone=}false);
if not IsEqual(tag,ptag^,tlen) then
exit; // check authentication after single pass encryption + auth
end;
end else begin // generic process in dual steps
internal_auth(ctp,ILen,txt_ghv,atx_cnt);
if (ptag<>nil) and (tlen>0) then begin
Final(tag,{anddone=}false);
if not IsEqual(tag,ptag^,tlen) then
exit; // check authentication before encryption
end;
internal_crypt(ctp,ptp,iLen);
end;
end;
result := true;
end;
function TAESGCMEngine.Add_AAD(pAAD: pointer; aLen: PtrInt): boolean;
begin
if aLen>0 then begin
if (pAAD=nil) or (flagFinalComputed in flags) then begin
result := false;
exit;
end;
internal_auth(pAAD,aLen,aad_ghv,aad_cnt);
end;
result := true;
end;
function TAESGCMEngine.Final(out tag: TAESBlock; andDone: boolean): boolean;
var
tbuf: TAESBlock;
ln: cardinal;
begin
if not (flagFinalComputed in flags) then begin
include(flags,flagFinalComputed);
// compute GHASH(H, AAD, ctp)
gf_mul_h(aad_ghv);
gf_mul_h(txt_ghv);
// compute len(AAD) || len(ctp) with each len as 64-bit big-endian
ln := (atx_cnt.V+AESBlockMod) shr AESBlockShift;
if (aad_cnt.V>0) and (ln<>0) then begin
tbuf := ghash_h;
while ln<>0 do begin
if odd(ln) then
gf_mul(aad_ghv,tbuf);
ln := ln shr 1;
if ln<>0 then
gf_mul(tbuf,tbuf);
end;
end;
TWA4(tbuf)[0] := bswap32((aad_cnt.L shr 29) or (aad_cnt.H shl 3));
TWA4(tbuf)[1] := bswap32((aad_cnt.L shl 3));
TWA4(tbuf)[2] := bswap32((atx_cnt.L shr 29) or (atx_cnt.H shl 3));
TWA4(tbuf)[3] := bswap32((atx_cnt.L shl 3));
XorBlock16(@tbuf,@txt_ghv);
XorBlock16(@aad_ghv,@tbuf);
gf_mul_h(aad_ghv);
// compute E(K,Y0)
tbuf := TAESContext(actx).IV;
TWA4(tbuf)[3] := y0_val;
actx.Encrypt(tbuf);
// GMAC = GHASH(H, AAD, ctp) xor E(K,Y0)
XorBlock16(@aad_ghv,@tag,@tbuf);
if andDone then
Done;
result := true;
end else begin
Done;
result := false;
end;
end;
procedure TAESGCMEngine.Done;
begin
if flagFlushed in flags then
exit;
actx.Done;
include(flags,flagFlushed);
end;
function TAESGCMEngine.FullEncryptAndAuthenticate(const Key; KeyBits: PtrInt;
pIV: pointer; IV_len: PtrInt; pAAD: pointer; aLen: PtrInt; ptp, ctp: Pointer;
pLen: PtrInt; out tag: TAESBlock): boolean;
begin
result := Init(Key,KeyBits) and Reset(pIV,IV_len) and Add_AAD(pAAD,aLen) and
Encrypt(ptp,ctp,pLen) and Final(tag);
Done;
end;
function TAESGCMEngine.FullDecryptAndVerify(const Key; KeyBits: PtrInt;
pIV: pointer; IV_len: PtrInt; pAAD: pointer; aLen: PtrInt; ctp, ptp: Pointer;
pLen: PtrInt; ptag: pointer; tLen: PtrInt): boolean;
begin
result := Init(Key,KeyBits) and Reset(pIV,IV_len) and Add_AAD(pAAD,aLen) and
Decrypt(ctp,ptp,pLen,ptag,tlen);
Done;
end;
{ TSHA256 }
// under Win32, with a Core i7 CPU: pure pascal: 152ms - x86: 112ms
// under Win64, with a Core i7 CPU: pure pascal: 202ms - SSE4: 78ms
procedure Sha256ExpandMessageBlocks(W, Buf: PIntegerArray);
// Calculate "expanded message blocks"
{$ifdef AES_PASCAL}
var i: integer;
begin
// bswap256() instead of "for i := 0 to 15 do W[i]:= bswap32(Buf[i]);"
bswap256(@Buf[0],@W[0]);
bswap256(@Buf[8],@W[8]);
for i := 16 to 63 do
{$ifdef FPC} // uses faster built-in right rotate intrinsic
W[i] := (RorDWord(W[i-2],17)xor RorDWord(W[i-2],19)xor(W[i-2]shr 10))+W[i-7]+
(RorDWord(W[i-15],7)xor RorDWord(W[i-15],18)xor(W[i-15]shr 3))+W[i-16];
{$else}
W[i] := (((W[i-2]shr 17)or(W[i-2]shl 15))xor((W[i-2]shr 19)or(W[i-2]shl 13))
xor (W[i-2]shr 10))+W[i-7]+(((W[i-15]shr 7)or(W[i-15]shl 25))
xor ((W[i-15]shr 18)or(W[i-15]shl 14))xor(W[i-15]shr 3))+W[i-16];
{$endif}
end;
{$else}
{$ifdef CPUX86} {$ifdef FPC} nostackframe; assembler; {$endif}
asm // W=eax Buf=edx
push esi
push edi
push ebx
mov esi, eax
// part 1: W[i]:= RB(TW32Buf(Buf)[i])
mov eax, [edx]
mov ebx, [edx + 4]
bswap eax
bswap ebx
mov [esi], eax
mov [esi + 4], ebx
mov eax, [edx + 8]
mov ebx, [edx + 12]
bswap eax
bswap ebx
mov [esi + 8], eax
mov [esi + 12], ebx
mov eax, [edx + 16]
mov ebx, [edx + 20]
bswap eax
bswap ebx
mov [esi + 16], eax
mov [esi + 20], ebx
mov eax, [edx + 24]
mov ebx, [edx + 28]
bswap eax
bswap ebx
mov [esi + 24], eax
mov [esi + 28], ebx
mov eax, [edx + 32]
mov ebx, [edx + 36]
bswap eax
bswap ebx
mov [esi + 32], eax
mov [esi + 36], ebx
mov eax, [edx + 40]
mov ebx, [edx + 44]
bswap eax
bswap ebx
mov [esi + 40], eax
mov [esi + 44], ebx
mov eax, [edx + 48]
mov ebx, [edx + 52]
bswap eax
bswap ebx
mov [esi + 48], eax
mov [esi + 52], ebx
mov eax, [edx + 56]
mov ebx, [edx + 60]
bswap eax
bswap ebx
mov [esi + 56], eax
mov [esi + 60], ebx
lea esi, [esi + 64]
// part2: w[i]:= lrot_1(w[i-3] xor w[i-8] xor w[i-14] xor w[i-16])
mov ecx, 48
@@2: mov eax, [esi - 2 * 4] // w[i-2]
mov edi, [esi - 7 * 4] // w[i-7]
mov edx, eax
mov ebx, eax // sig1: rr17 xor rr19 xor srx,10
ror eax, 17
ror edx, 19
shr ebx, 10
xor eax, edx
xor eax, ebx
add edi, eax
mov eax, [esi - 15 * 4] // w[i-15]
mov ebx, eax // sig0: rr7 xor rr18 xor sr3
mov edx, eax
ror eax, 7
ror edx, 18
shr ebx, 3
xor eax, edx
xor eax, ebx
add eax, edi
add eax, [esi - 16 * 4] // w[i-16]
mov [esi], eax
add esi, 4
dec ecx
jnz @@2
pop ebx
pop edi
pop esi
end;
{$endif CPUX86}
{$ifdef CPUX64}
{$ifdef FPC}nostackframe; assembler; asm{$else}
asm // W=rcx Buf=rdx
.noframe
{$endif}
{$ifndef win64}
mov rdx, rsi
mov rcx, rdi
{$endif win64}
mov rax, rcx
push rsi
push rdi
push rbx
mov rsi, rax
// part 1: W[i]:= RB(TW32Buf(Buf)[i])
mov eax, [rdx]
mov ebx, [rdx + 4]
bswap eax
bswap ebx
mov [rsi], eax
mov [rsi + 4], ebx
mov eax, [rdx + 8]
mov ebx, [rdx + 12]
bswap eax
bswap ebx
mov [rsi + 8], eax
mov [rsi + 12], ebx
mov eax, [rdx + 16]
mov ebx, [rdx + 20]
bswap eax
bswap ebx
mov [rsi + 16], eax
mov [rsi + 20], ebx
mov eax, [rdx + 24]
mov ebx, [rdx + 28]
bswap eax
bswap ebx
mov [rsi + 24], eax
mov [rsi + 28], ebx
mov eax, [rdx + 32]
mov ebx, [rdx + 36]
bswap eax
bswap ebx
mov [rsi + 32], eax
mov [rsi + 36], ebx
mov eax, [rdx + 40]
mov ebx, [rdx + 44]
bswap eax
bswap ebx
mov [rsi + 40], eax
mov [rsi + 44], ebx
mov eax, [rdx + 48]
mov ebx, [rdx + 52]
bswap eax
bswap ebx
mov [rsi + 48], eax
mov [rsi + 52], ebx
mov eax, [rdx + 56]
mov ebx, [rdx + 60]
bswap eax
bswap ebx
mov [rsi + 56], eax
mov [rsi + 60], ebx
lea rsi, [rsi + 64]
// part2: W[i]:= LRot_1(W[i-3] xor W[i-8] xor W[i-14] xor W[i-16])
mov ecx, 48
@@2: mov eax, [rsi - 2 * 4] // W[i-2]
mov edi, [rsi - 7 * 4] // W[i-7]
mov edx, eax
mov ebx, eax // Sig1: RR17 xor RR19 xor SRx,10
ror eax, 17
ror edx, 19
shr ebx, 10
xor eax, edx
xor eax, ebx
add edi, eax
mov eax, [rsi - 15 * 4] // W[i-15]
mov ebx, eax // Sig0: RR7 xor RR18 xor SR3
mov edx, eax
ror eax, 7
ror edx, 18
shr ebx, 3
xor eax, edx
xor eax, ebx
add eax, edi
add eax, [rsi - 16 * 4] // W[i-16]
mov [rsi], eax
add rsi, 4
dec ecx
jnz @@2
pop rbx
pop rdi
pop rsi
end;
{$endif CPUX64}
{$endif AES_PASCAL}
const
K256: array[0..63] of cardinal = (
$428a2f98, $71374491, $b5c0fbcf, $e9b5dba5, $3956c25b, $59f111f1,
$923f82a4, $ab1c5ed5, $d807aa98, $12835b01, $243185be, $550c7dc3,
$72be5d74, $80deb1fe, $9bdc06a7, $c19bf174, $e49b69c1, $efbe4786,
$0fc19dc6, $240ca1cc, $2de92c6f, $4a7484aa, $5cb0a9dc, $76f988da,
$983e5152, $a831c66d, $b00327c8, $bf597fc7, $c6e00bf3, $d5a79147,
$06ca6351, $14292967, $27b70a85, $2e1b2138, $4d2c6dfc, $53380d13,
$650a7354, $766a0abb, $81c2c92e, $92722c85, $a2bfe8a1, $a81a664b,
$c24b8b70, $c76c51a3, $d192e819, $d6990624, $f40e3585, $106aa070,
$19a4c116, $1e376c08, $2748774c, $34b0bcb5, $391c0cb3, $4ed8aa4a,
$5b9cca4f, $682e6ff3, $748f82ee, $78a5636f, $84c87814, $8cc70208,
$90befffa, $a4506ceb, $bef9a3f7, $c67178f2);
{$ifdef CPUX64}
// optimized unrolled version from Intel's sha256_sse4.asm
// Original code is released as Copyright (c) 2012, Intel Corporation
var
K256AlignedStore: RawByteString;
K256Aligned: pointer; // movaps + paddd do expect 16 bytes alignment
const
STACK_SIZE = 32{$ifndef LINUX}+7*16{$endif};
procedure sha256_sse4(var input_data; var digest; num_blks: PtrUInt);
{$ifdef FPC}nostackframe; assembler; asm{$else}
asm // rcx=input_data rdx=digest r8=num_blks (Linux: rdi,rsi,rdx)
.noframe
{$endif FPC}
push rbx
{$ifdef LINUX}
mov r8, rdx
mov rcx, rdi
mov rdx, rsi
{$else}
push rsi // Win64 expects those registers to be preserved
push rdi
{$endif}
push rbp
push r13
push r14
push r15
sub rsp, STACK_SIZE
{$ifndef LINUX}
movaps [rsp + 20H], xmm6 // manual .PUSHNV for FPC compatibility
movaps [rsp + 30H], xmm7
movaps [rsp + 40H], xmm8
movaps [rsp + 50H], xmm9
movaps [rsp + 60H], xmm10
movaps [rsp + 70H], xmm11
movaps [rsp + 80H], xmm12
{$endif}
shl r8, 6
je @done
add r8, rcx
mov [rsp], r8
mov eax, [rdx]
mov ebx, [rdx + 4H]
mov edi, [rdx + 8H]
mov esi, [rdx + 0CH]
mov r8d, [rdx + 10H]
mov r9d, [rdx + 14H]
mov r10d, [rdx + 18H]
mov r11d, [rdx + 1CH]
movaps xmm12, [rip + @flip]
movaps xmm10, [rip + @00BA]
movaps xmm11, [rip + @DC00]
@loop0: mov rbp, [rip + K256Aligned]
movups xmm4, [rcx]
pshufb xmm4, xmm12
movups xmm5, [rcx + 10h]
pshufb xmm5, xmm12
movups xmm6, [rcx + 20h]
pshufb xmm6, xmm12
movups xmm7, [rcx + 30h]
pshufb xmm7, xmm12
mov [rsp + 8h], rcx
mov rcx, 3
@loop1: movaps xmm9, [rbp]
paddd xmm9, xmm4
movaps [rsp + 10h], xmm9
movaps xmm0, xmm7
mov r13d, r8d
ror r13d, 14
mov r14d, eax
palignr xmm0, xmm6, 04h
ror r14d, 9
xor r13d, r8d
mov r15d, r9d
ror r13d, 5
movaps xmm1, xmm5
xor r14d, eax
xor r15d, r10d
paddd xmm0, xmm4
xor r13d, r8d
and r15d, r8d
ror r14d, 11
palignr xmm1, xmm4, 04h
xor r14d, eax
ror r13d, 6
xor r15d, r10d
movaps xmm2, xmm1
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 10h]
movaps xmm3, xmm1
mov r13d, eax
add r11d, r15d
mov r15d, eax
pslld xmm1, 25
or r13d, edi
add esi, r11d
and r15d, edi
psrld xmm2, 7
and r13d, ebx
add r11d, r14d
por xmm1, xmm2
or r13d, r15d
add r11d, r13d
movaps xmm2, xmm3
mov r13d, esi
mov r14d, r11d
movaps xmm8, xmm3
ror r13d, 14
xor r13d, esi
mov r15d, r8d
ror r14d, 9
pslld xmm3, 14
xor r14d, r11d
ror r13d, 5
xor r15d, r9d
psrld xmm2, 18
ror r14d, 11
xor r13d, esi
and r15d, esi
ror r13d, 6
pxor xmm1, xmm3
xor r14d, r11d
xor r15d, r9d
psrld xmm8, 3
add r15d, r13d
add r15d, [rsp + 14h]
ror r14d, 2
pxor xmm1, xmm2
mov r13d, r11d
add r10d, r15d
mov r15d, r11d
pxor xmm1, xmm8
or r13d, ebx
add edi, r10d
and r15d, ebx
pshufd xmm2, xmm7, 0fah
and r13d, eax
add r10d, r14d
paddd xmm0, xmm1
or r13d, r15d
add r10d, r13d
movaps xmm3, xmm2
mov r13d, edi
mov r14d, r10d
ror r13d, 14
movaps xmm8, xmm2
xor r13d, edi
ror r14d, 9
mov r15d, esi
xor r14d, r10d
ror r13d, 5
psrlq xmm2, 17
xor r15d, r8d
psrlq xmm3, 19
xor r13d, edi
and r15d, edi
psrld xmm8, 10
ror r14d, 11
xor r14d, r10d
xor r15d, r8d
ror r13d, 6
pxor xmm2, xmm3
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
pxor xmm8, xmm2
mov r13d, r10d
add r9d, r15d
mov r15d, r10d
pshufb xmm8, xmm10
or r13d, eax
add ebx, r9d
and r15d, eax
paddd xmm0, xmm8
and r13d, r11d
add r9d, r14d
pshufd xmm2, xmm0, 50h
or r13d, r15d
add r9d, r13d
movaps xmm3, xmm2
mov r13d, ebx
ror r13d, 14
mov r14d, r9d
movaps xmm4, xmm2
ror r14d, 9
xor r13d, ebx
mov r15d, edi
ror r13d, 5
psrlq xmm2, 17
xor r14d, r9d
xor r15d, esi
psrlq xmm3, 19
xor r13d, ebx
and r15d, ebx
ror r14d, 11
psrld xmm4, 10
xor r14d, r9d
ror r13d, 6
xor r15d, esi
pxor xmm2, xmm3
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 1ch]
pxor xmm4, xmm2
mov r13d, r9d
add r8d, r15d
mov r15d, r9d
pshufb xmm4, xmm11
or r13d, r11d
add eax, r8d
and r15d, r11d
paddd xmm4, xmm0
and r13d, r10d
add r8d, r14d
or r13d, r15d
add r8d, r13d
movaps xmm9, [rbp + 10h]
paddd xmm9, xmm5
movaps [rsp + 10h], xmm9
movaps xmm0, xmm4
mov r13d, eax
ror r13d, 14
mov r14d, r8d
palignr xmm0, xmm7, 04h
ror r14d, 9
xor r13d, eax
mov r15d, ebx
ror r13d, 5
movaps xmm1, xmm6
xor r14d, r8d
xor r15d, edi
paddd xmm0, xmm5
xor r13d, eax
and r15d, eax
ror r14d, 11
palignr xmm1, xmm5, 04h
xor r14d, r8d
ror r13d, 6
xor r15d, edi
movaps xmm2, xmm1
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 10h]
movaps xmm3, xmm1
mov r13d, r8d
add esi, r15d
mov r15d, r8d
pslld xmm1, 25
or r13d, r10d
add r11d, esi
and r15d, r10d
psrld xmm2, 7
and r13d, r9d
add esi, r14d
por xmm1, xmm2
or r13d, r15d
add esi, r13d
movaps xmm2, xmm3
mov r13d, r11d
mov r14d, esi
movaps xmm8, xmm3
ror r13d, 14
xor r13d, r11d
mov r15d, eax
ror r14d, 9
pslld xmm3, 14
xor r14d, esi
ror r13d, 5
xor r15d, ebx
psrld xmm2, 18
ror r14d, 11
xor r13d, r11d
and r15d, r11d
ror r13d, 6
pxor xmm1, xmm3
xor r14d, esi
xor r15d, ebx
psrld xmm8, 3
add r15d, r13d
add r15d, [rsp + 14h]
ror r14d, 2
pxor xmm1, xmm2
mov r13d, esi
add edi, r15d
mov r15d, esi
pxor xmm1, xmm8
or r13d, r9d
add r10d, edi
and r15d, r9d
pshufd xmm2, xmm4, 0fah
and r13d, r8d
add edi, r14d
paddd xmm0, xmm1
or r13d, r15d
add edi, r13d
movaps xmm3, xmm2
mov r13d, r10d
mov r14d, edi
ror r13d, 14
movaps xmm8, xmm2
xor r13d, r10d
ror r14d, 9
mov r15d, r11d
xor r14d, edi
ror r13d, 5
psrlq xmm2, 17
xor r15d, eax
psrlq xmm3, 19
xor r13d, r10d
and r15d, r10d
psrld xmm8, 10
ror r14d, 11
xor r14d, edi
xor r15d, eax
ror r13d, 6
pxor xmm2, xmm3
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
pxor xmm8, xmm2
mov r13d, edi
add ebx, r15d
mov r15d, edi
pshufb xmm8, xmm10
or r13d, r8d
add r9d, ebx
and r15d, r8d
paddd xmm0, xmm8
and r13d, esi
add ebx, r14d
pshufd xmm2, xmm0, 50h
or r13d, r15d
add ebx, r13d
movaps xmm3, xmm2
mov r13d, r9d
ror r13d, 14
mov r14d, ebx
movaps xmm5, xmm2
ror r14d, 9
xor r13d, r9d
mov r15d, r10d
ror r13d, 5
psrlq xmm2, 17
xor r14d, ebx
xor r15d, r11d
psrlq xmm3, 19
xor r13d, r9d
and r15d, r9d
ror r14d, 11
psrld xmm5, 10
xor r14d, ebx
ror r13d, 6
xor r15d, r11d
pxor xmm2, xmm3
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 1ch]
pxor xmm5, xmm2
mov r13d, ebx
add eax, r15d
mov r15d, ebx
pshufb xmm5, xmm11
or r13d, esi
add r8d, eax
and r15d, esi
paddd xmm5, xmm0
and r13d, edi
add eax, r14d
or r13d, r15d
add eax, r13d
movaps xmm9, [rbp + 20h]
paddd xmm9, xmm6
movaps [rsp + 10h], xmm9
movaps xmm0, xmm5
mov r13d, r8d
ror r13d, 14
mov r14d, eax
palignr xmm0, xmm4, 04h
ror r14d, 9
xor r13d, r8d
mov r15d, r9d
ror r13d, 5
movaps xmm1, xmm7
xor r14d, eax
xor r15d, r10d
paddd xmm0, xmm6
xor r13d, r8d
and r15d, r8d
ror r14d, 11
palignr xmm1, xmm6, 04h
xor r14d, eax
ror r13d, 6
xor r15d, r10d
movaps xmm2, xmm1
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 10h]
movaps xmm3, xmm1
mov r13d, eax
add r11d, r15d
mov r15d, eax
pslld xmm1, 25
or r13d, edi
add esi, r11d
and r15d, edi
psrld xmm2, 7
and r13d, ebx
add r11d, r14d
por xmm1, xmm2
or r13d, r15d
add r11d, r13d
movaps xmm2, xmm3
mov r13d, esi
mov r14d, r11d
movaps xmm8, xmm3
ror r13d, 14
xor r13d, esi
mov r15d, r8d
ror r14d, 9
pslld xmm3, 14
xor r14d, r11d
ror r13d, 5
xor r15d, r9d
psrld xmm2, 18
ror r14d, 11
xor r13d, esi
and r15d, esi
ror r13d, 6
pxor xmm1, xmm3
xor r14d, r11d
xor r15d, r9d
psrld xmm8, 3
add r15d, r13d
add r15d, [rsp + 14h]
ror r14d, 2
pxor xmm1, xmm2
mov r13d, r11d
add r10d, r15d
mov r15d, r11d
pxor xmm1, xmm8
or r13d, ebx
add edi, r10d
and r15d, ebx
pshufd xmm2, xmm5, 0fah
and r13d, eax
add r10d, r14d
paddd xmm0, xmm1
or r13d, r15d
add r10d, r13d
movaps xmm3, xmm2
mov r13d, edi
mov r14d, r10d
ror r13d, 14
movaps xmm8, xmm2
xor r13d, edi
ror r14d, 9
mov r15d, esi
xor r14d, r10d
ror r13d, 5
psrlq xmm2, 17
xor r15d, r8d
psrlq xmm3, 19
xor r13d, edi
and r15d, edi
psrld xmm8, 10
ror r14d, 11
xor r14d, r10d
xor r15d, r8d
ror r13d, 6
pxor xmm2, xmm3
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
pxor xmm8, xmm2
mov r13d, r10d
add r9d, r15d
mov r15d, r10d
pshufb xmm8, xmm10
or r13d, eax
add ebx, r9d
and r15d, eax
paddd xmm0, xmm8
and r13d, r11d
add r9d, r14d
pshufd xmm2, xmm0, 50h
or r13d, r15d
add r9d, r13d
movaps xmm3, xmm2
mov r13d, ebx
ror r13d, 14
mov r14d, r9d
movaps xmm6, xmm2
ror r14d, 9
xor r13d, ebx
mov r15d, edi
ror r13d, 5
psrlq xmm2, 17
xor r14d, r9d
xor r15d, esi
psrlq xmm3, 19
xor r13d, ebx
and r15d, ebx
ror r14d, 11
psrld xmm6, 10
xor r14d, r9d
ror r13d, 6
xor r15d, esi
pxor xmm2, xmm3
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 1ch]
pxor xmm6, xmm2
mov r13d, r9d
add r8d, r15d
mov r15d, r9d
pshufb xmm6, xmm11
or r13d, r11d
add eax, r8d
and r15d, r11d
paddd xmm6, xmm0
and r13d, r10d
add r8d, r14d
or r13d, r15d
add r8d, r13d
movaps xmm9, [rbp + 30h]
paddd xmm9, xmm7
movaps [rsp + 10h], xmm9
add rbp, 64
movaps xmm0, xmm6
mov r13d, eax
ror r13d, 14
mov r14d, r8d
palignr xmm0, xmm5, 04h
ror r14d, 9
xor r13d, eax
mov r15d, ebx
ror r13d, 5
movaps xmm1, xmm4
xor r14d, r8d
xor r15d, edi
paddd xmm0, xmm7
xor r13d, eax
and r15d, eax
ror r14d, 11
palignr xmm1, xmm7, 04h
xor r14d, r8d
ror r13d, 6
xor r15d, edi
movaps xmm2, xmm1
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 10h]
movaps xmm3, xmm1
mov r13d, r8d
add esi, r15d
mov r15d, r8d
pslld xmm1, 25
or r13d, r10d
add r11d, esi
and r15d, r10d
psrld xmm2, 7
and r13d, r9d
add esi, r14d
por xmm1, xmm2
or r13d, r15d
add esi, r13d
movaps xmm2, xmm3
mov r13d, r11d
mov r14d, esi
movaps xmm8, xmm3
ror r13d, 14
xor r13d, r11d
mov r15d, eax
ror r14d, 9
pslld xmm3, 14
xor r14d, esi
ror r13d, 5
xor r15d, ebx
psrld xmm2, 18
ror r14d, 11
xor r13d, r11d
and r15d, r11d
ror r13d, 6
pxor xmm1, xmm3
xor r14d, esi
xor r15d, ebx
psrld xmm8, 3
add r15d, r13d
add r15d, [rsp + 14h]
ror r14d, 2
pxor xmm1, xmm2
mov r13d, esi
add edi, r15d
mov r15d, esi
pxor xmm1, xmm8
or r13d, r9d
add r10d, edi
and r15d, r9d
pshufd xmm2, xmm6, 0fah
and r13d, r8d
add edi, r14d
paddd xmm0, xmm1
or r13d, r15d
add edi, r13d
movaps xmm3, xmm2
mov r13d, r10d
mov r14d, edi
ror r13d, 14
movaps xmm8, xmm2
xor r13d, r10d
ror r14d, 9
mov r15d, r11d
xor r14d, edi
ror r13d, 5
psrlq xmm2, 17
xor r15d, eax
psrlq xmm3, 19
xor r13d, r10d
and r15d, r10d
psrld xmm8, 10
ror r14d, 11
xor r14d, edi
xor r15d, eax
ror r13d, 6
pxor xmm2, xmm3
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
pxor xmm8, xmm2
mov r13d, edi
add ebx, r15d
mov r15d, edi
pshufb xmm8, xmm10
or r13d, r8d
add r9d, ebx
and r15d, r8d
paddd xmm0, xmm8
and r13d, esi
add ebx, r14d
pshufd xmm2, xmm0, 50h
or r13d, r15d
add ebx, r13d
movaps xmm3, xmm2
mov r13d, r9d
ror r13d, 14
mov r14d, ebx
movaps xmm7, xmm2
ror r14d, 9
xor r13d, r9d
mov r15d, r10d
ror r13d, 5
psrlq xmm2, 17
xor r14d, ebx
xor r15d, r11d
psrlq xmm3, 19
xor r13d, r9d
and r15d, r9d
ror r14d, 11
psrld xmm7, 10
xor r14d, ebx
ror r13d, 6
xor r15d, r11d
pxor xmm2, xmm3
ror r14d, 2
add r15d, r13d
add r15d, [rsp + 1ch]
pxor xmm7, xmm2
mov r13d, ebx
add eax, r15d
mov r15d, ebx
pshufb xmm7, xmm11
or r13d, esi
add r8d, eax
and r15d, esi
paddd xmm7, xmm0
and r13d, edi
add eax, r14d
or r13d, r15d
add eax, r13d
sub rcx, 1
jne @loop1
mov rcx, 2
@loop2: paddd xmm4, [rbp]
movaps [rsp + 10h], xmm4
mov r13d, r8d
ror r13d, 14
mov r14d, eax
xor r13d, r8d
ror r14d, 9
mov r15d, r9d
xor r14d, eax
ror r13d, 5
xor r15d, r10d
xor r13d, r8d
ror r14d, 11
and r15d, r8d
xor r14d, eax
ror r13d, 6
xor r15d, r10d
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 10h]
mov r13d, eax
add r11d, r15d
mov r15d, eax
or r13d, edi
add esi, r11d
and r15d, edi
and r13d, ebx
add r11d, r14d
or r13d, r15d
add r11d, r13d
mov r13d, esi
ror r13d, 14
mov r14d, r11d
xor r13d, esi
ror r14d, 9
mov r15d, r8d
xor r14d, r11d
ror r13d, 5
xor r15d, r9d
xor r13d, esi
ror r14d, 11
and r15d, esi
xor r14d, r11d
ror r13d, 6
xor r15d, r9d
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 14h]
mov r13d, r11d
add r10d, r15d
mov r15d, r11d
or r13d, ebx
add edi, r10d
and r15d, ebx
and r13d, eax
add r10d, r14d
or r13d, r15d
add r10d, r13d
mov r13d, edi
ror r13d, 14
mov r14d, r10d
xor r13d, edi
ror r14d, 9
mov r15d, esi
xor r14d, r10d
ror r13d, 5
xor r15d, r8d
xor r13d, edi
ror r14d, 11
and r15d, edi
xor r14d, r10d
ror r13d, 6
xor r15d, r8d
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
mov r13d, r10d
add r9d, r15d
mov r15d, r10d
or r13d, eax
add ebx, r9d
and r15d, eax
and r13d, r11d
add r9d, r14d
or r13d, r15d
add r9d, r13d
mov r13d, ebx
ror r13d, 14
mov r14d, r9d
xor r13d, ebx
ror r14d, 9
mov r15d, edi
xor r14d, r9d
ror r13d, 5
xor r15d, esi
xor r13d, ebx
ror r14d, 11
and r15d, ebx
xor r14d, r9d
ror r13d, 6
xor r15d, esi
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 1ch]
mov r13d, r9d
add r8d, r15d
mov r15d, r9d
or r13d, r11d
add eax, r8d
and r15d, r11d
and r13d, r10d
add r8d, r14d
or r13d, r15d
add r8d, r13d
paddd xmm5, [rbp + 10h]
movaps [rsp + 10h], xmm5
add rbp, 32
mov r13d, eax
ror r13d, 14
mov r14d, r8d
xor r13d, eax
ror r14d, 9
mov r15d, ebx
xor r14d, r8d
ror r13d, 5
xor r15d, edi
xor r13d, eax
ror r14d, 11
and r15d, eax
xor r14d, r8d
ror r13d, 6
xor r15d, edi
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 10h]
mov r13d, r8d
add esi, r15d
mov r15d, r8d
or r13d, r10d
add r11d, esi
and r15d, r10d
and r13d, r9d
add esi, r14d
or r13d, r15d
add esi, r13d
mov r13d, r11d
ror r13d, 14
mov r14d, esi
xor r13d, r11d
ror r14d, 9
mov r15d, eax
xor r14d, esi
ror r13d, 5
xor r15d, ebx
xor r13d, r11d
ror r14d, 11
and r15d, r11d
xor r14d, esi
ror r13d, 6
xor r15d, ebx
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 14h]
mov r13d, esi
add edi, r15d
mov r15d, esi
or r13d, r9d
add r10d, edi
and r15d, r9d
and r13d, r8d
add edi, r14d
or r13d, r15d
add edi, r13d
mov r13d, r10d
ror r13d, 14
mov r14d, edi
xor r13d, r10d
ror r14d, 9
mov r15d, r11d
xor r14d, edi
ror r13d, 5
xor r15d, eax
xor r13d, r10d
ror r14d, 11
and r15d, r10d
xor r14d, edi
ror r13d, 6
xor r15d, eax
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 18h]
mov r13d, edi
add ebx, r15d
mov r15d, edi
or r13d, r8d
add r9d, ebx
and r15d, r8d
and r13d, esi
add ebx, r14d
or r13d, r15d
add ebx, r13d
mov r13d, r9d
ror r13d, 14
mov r14d, ebx
xor r13d, r9d
ror r14d, 9
mov r15d, r10d
xor r14d, ebx
ror r13d, 5
xor r15d, r11d
xor r13d, r9d
ror r14d, 11
and r15d, r9d
xor r14d, ebx
ror r13d, 6
xor r15d, r11d
add r15d, r13d
ror r14d, 2
add r15d, [rsp + 1ch]
mov r13d, ebx
add eax, r15d
mov r15d, ebx
or r13d, esi
add r8d, eax
and r15d, esi
and r13d, edi
add eax, r14d
or r13d, r15d
add eax, r13d
movaps xmm4, xmm6
movaps xmm5, xmm7
dec rcx
jne @loop2
add eax, [rdx]
mov [rdx], eax
add ebx, [rdx + 4H]
add edi, [rdx + 8H]
add esi, [rdx + 0CH]
add r8d, [rdx + 10H]
add r9d, [rdx + 14H]
add r10d, [rdx + 18H]
add r11d, [rdx + 1CH]
mov [rdx + 4H], ebx
mov [rdx + 8H], edi
mov [rdx + 0CH], esi
mov [rdx + 10H], r8d
mov [rdx + 14H], r9d
mov [rdx + 18H], r10d
mov [rdx + 1CH], r11d
mov rcx, [rsp + 8H]
add rcx, 64
cmp rcx, [rsp]
jne @loop0
@done: {$ifndef LINUX}
movaps xmm6, [rsp + 20H]
movaps xmm7, [rsp + 30H]
movaps xmm8, [rsp + 40H]
movaps xmm9, [rsp + 50H]
movaps xmm10, [rsp + 60H]
movaps xmm11, [rsp + 70H]
movaps xmm12, [rsp + 80H]
{$endif}
add rsp, STACK_SIZE
pop r15
pop r14
pop r13
pop rbp
{$ifndef LINUX}
pop rdi
pop rsi
{$endif}
pop rbx
ret
{$ifdef FPC} align 16 {$else} .align 16 {$endif}
@flip: dq $0405060700010203
dq $0C0D0E0F08090A0B
@00BA: dq $0B0A090803020100
dq $FFFFFFFFFFFFFFFF
@DC00: dq $FFFFFFFFFFFFFFFF
dq $0B0A090803020100
end;
{$endif CPUX64}
procedure Sha256CompressPas(var Hash: TSHAHash; Data: pointer);
// Actual hashing function
var H: TSHAHash;
W: array[0..63] of cardinal;
{$ifdef PUREPASCAL}
i: integer;
t1, t2: cardinal;
{$endif}
begin
// calculate "expanded message blocks"
Sha256ExpandMessageBlocks(@W,Data);
// assign old working hash to local variables A..H
H.A := Hash.A;
H.B := Hash.B;
H.C := Hash.C;
H.D := Hash.D;
H.E := Hash.E;
H.F := Hash.F;
H.G := Hash.G;
H.H := Hash.H;
{$ifdef PUREPASCAL}
// SHA-256 compression function
for i := 0 to high(W) do begin
{$ifdef FPC} // uses built-in right rotate intrinsic
t1 := H.H+(RorDWord(H.E,6) xor RorDWord(H.E,11) xor RorDWord(H.E,25))+
((H.E and H.F)xor(not H.E and H.G))+K256[i]+W[i];
t2 := (RorDWord(H.A,2) xor RorDWord(H.A,13) xor RorDWord(H.A,22))+
((H.A and H.B)xor(H.A and H.C)xor(H.B and H.C));
{$else}
t1 := H.H+(((H.E shr 6)or(H.E shl 26))xor((H.E shr 11)or(H.E shl 21))xor
((H.E shr 25)or(H.E shl 7)))+((H.E and H.F)xor(not H.E and H.G))+K256[i]+W[i];
t2 := (((H.A shr 2)or(H.A shl 30))xor((H.A shr 13)or(H.A shl 19))xor
((H.A shr 22)xor(H.A shl 10)))+((H.A and H.B)xor(H.A and H.C)xor(H.B and H.C));
{$endif}
H.H := H.G; H.G := H.F; H.F := H.E; H.E := H.D+t1;
H.D := H.C; H.C := H.B; H.B := H.A; H.A := t1+t2;
end;
{$else}
// SHA-256 compression function - optimized by A.B. for pipelined CPU
asm
push ebx
push esi
push edi
xor edi,edi // edi=i
// rolled version faster than the unrolled one (good pipelining work :)
@s: mov eax,[H].TSHAHash.E
mov ecx,eax
mov edx,eax
mov ebx,eax // ebx=E
ror eax,6
ror edx,11
ror ecx,25
xor eax,edx
mov edx,[H].TSHAHash.G
xor eax,ecx
mov ecx,[H].TSHAHash.H
add ecx,eax // T1=ecx
mov eax,[H].TSHAHash.F
mov [H].TSHAHash.H,edx
mov [H].TSHAHash.G,eax
xor eax,edx
mov [H].TSHAHash.F,ebx
and eax,ebx
xor eax,edx
add eax,dword ptr [K256+edi*4]
add eax,ecx
mov ecx,[H].TSHAHash.D
add eax,dword ptr [W+edi*4]
mov ebx,[H].TSHAHash.A
// eax= T1 := H + Sum1(E) +(((F xor G) and E) xor G)+K256[i]+W[i];
add ecx,eax
mov esi,eax // esi = T1
mov [H].TSHAHash.E,ecx // E := D + T1;
mov eax,ebx // Sum0(A)
mov edx,ebx
ror eax,2
mov ecx,ebx
ror edx,13
ror ecx,22
xor eax,edx
xor eax,ecx // eax = Sum0(A)
mov ecx,[H].TSHAHash.B
add esi,eax
mov eax,ebx // ebx=A
mov edx,ebx // eax=edx=A
or eax,ecx
and eax,[H].TSHAHash.C // eax = (A or B)and C
and edx,ecx
or eax,edx // eax = ((A or B)and C) or (A and B)
inc edi
add esi,eax // esi= T1+T2
mov [H].TSHAHash.A,esi // all these instructions are pipelined -> roll OK
mov eax,[H].TSHAHash.C // eax=C ecx=B ebx=A
mov [H].TSHAHash.B,ebx
mov [H].TSHAHash.C,ecx
mov [H].TSHAHash.D,eax
cmp edi,64
jnz @s
pop edi
pop esi
pop ebx
end;
{$endif PUREPASCAL}
// calculate new working hash
inc(Hash.A,H.A);
inc(Hash.B,H.B);
inc(Hash.C,H.C);
inc(Hash.D,H.D);
inc(Hash.E,H.E);
inc(Hash.F,H.F);
inc(Hash.G,H.G);
inc(Hash.H,H.H);
end;
procedure RawSha256Compress(var Hash; Data: pointer);
begin
{$ifdef CPUX64}
if K256AlignedStore<>'' then // use optimized Intel's sha256_sse4.asm
sha256_sse4(Data^,Hash,1) else
{$endif CPUX64}
Sha256CompressPas(TSHAHash(Hash),Data);
end;
procedure TSHA256.Final(out Digest: TSHA256Digest; NoInit: boolean);
// finalize SHA-256 calculation, clear context
var Data: TSHAContext absolute Context;
begin
// append bit '1' after Buffer
Data.Buffer[Data.Index] := $80;
FillcharFast(Data.Buffer[Data.Index+1],63-Data.Index,0);
// compress if more than 448 bits (no space for 64 bit length storage)
if Data.Index>=56 then begin
RawSha256Compress(Data.Hash,@Data.Buffer);
FillcharFast(Data.Buffer,56,0);
end;
// write 64 bit Buffer length into the last bits of the last block
// (in big endian format) and do a final compress
PInteger(@Data.Buffer[56])^ := bswap32(TQWordRec(Data.MLen).H);
PInteger(@Data.Buffer[60])^ := bswap32(TQWordRec(Data.MLen).L);
RawSha256Compress(Data.Hash,@Data.Buffer);
// Hash -> Digest to little endian format
bswap256(@Data.Hash,@Digest);
// clear Data and internally stored Digest
if not NoInit then
Init;
end;
function TSHA256.Final(NoInit: boolean): TSHA256Digest;
begin
Final(result,NoInit);
end;
procedure TSHA256.Full(Buffer: pointer; Len: integer; out Digest: TSHA256Digest);
begin
{$ifdef USEPADLOCK}
// Padlock need all data once -> Full() is OK, not successive Update()
if padlock_available then begin
Init; // for later Update use
{$ifdef PADLOCKDEBUG}write('padlock_phe_sha256 ');{$endif}
if padlock_phe_sha256(buffer,Len,Digest)=0 then
exit else
{$ifdef PADLOCKDEBUG}write(':ERROR ');{$endif}
end;
{$endif}
Init;
Update(Buffer,Len);
Final(Digest);
end;
procedure TSHA256.Init;
var Data: TSHAContext absolute Context;
begin
Data.Hash.A := $6a09e667;
Data.Hash.B := $bb67ae85;
Data.Hash.C := $3c6ef372;
Data.Hash.D := $a54ff53a;
Data.Hash.E := $510e527f;
Data.Hash.F := $9b05688c;
Data.Hash.G := $1f83d9ab;
Data.Hash.H := $5be0cd19;
FillcharFast(Data.MLen,sizeof(Data)-sizeof(Data.Hash),0);
end;
procedure TSHA256.Update(Buffer: pointer; Len: integer);
var Data: TSHAContext absolute Context;
aLen: integer;
begin
if Buffer=nil then exit; // avoid GPF
inc(Data.MLen,QWord(cardinal(Len)) shl 3);
{$ifdef CPUX64}
if (K256AlignedStore<>'') and (Data.Index=0) and (Len>=64) then begin
// use optimized Intel's sha256_sse4.asm for whole blocks
sha256_sse4(Buffer^,Data.Hash,Len shr 6);
inc(PByte(Buffer),Len);
Len := Len and 63;
dec(PByte(Buffer),Len);
end;
{$endif CPUX64}
while Len>0 do begin
aLen := 64-Data.Index;
if aLen<=Len then begin
if Data.Index<>0 then begin
MoveFast(Buffer^,Data.Buffer[Data.Index],aLen);
RawSha256Compress(Data.Hash,@Data.Buffer);
Data.Index := 0;
end else
RawSha256Compress(Data.Hash,Buffer); // avoid temporary copy
dec(Len,aLen);
inc(PByte(Buffer),aLen);
end else begin
MoveFast(Buffer^,Data.Buffer[Data.Index],Len);
inc(Data.Index,Len);
break;
end;
end;
end;
procedure TSHA256.Update(const Buffer: RawByteString);
begin
Update(pointer(Buffer),length(Buffer));
end;
procedure SHA256Weak(const s: RawByteString; out Digest: TSHA256Digest);
var L: integer;
SHA: TSHA256;
p: PAnsiChar;
tmp: array[0..255] of byte;
begin
L := length(s);
p := pointer(s);
if L<sizeof(tmp) then begin
FillcharFast(tmp,sizeof(tmp),L); // add some salt to unweak password
if L>0 then
MoveFast(p^,tmp,L);
SHA.Full(@tmp,sizeof(tmp),Digest);
end else
SHA.Full(p,L,Digest);
end;
{ common SHA384/SHA512 hashing kernel }
const
SHA512K: array[0..79] of QWord = (
QWord($428a2f98d728ae22),QWord($7137449123ef65cd),QWord($b5c0fbcfec4d3b2f),QWord($e9b5dba58189dbbc),
QWord($3956c25bf348b538),QWord($59f111f1b605d019),QWord($923f82a4af194f9b),QWord($ab1c5ed5da6d8118),
QWord($d807aa98a3030242),QWord($12835b0145706fbe),QWord($243185be4ee4b28c),QWord($550c7dc3d5ffb4e2),
QWord($72be5d74f27b896f),QWord($80deb1fe3b1696b1),QWord($9bdc06a725c71235),QWord($c19bf174cf692694),
QWord($e49b69c19ef14ad2),QWord($efbe4786384f25e3),QWord($0fc19dc68b8cd5b5),QWord($240ca1cc77ac9c65),
QWord($2de92c6f592b0275),QWord($4a7484aa6ea6e483),QWord($5cb0a9dcbd41fbd4),QWord($76f988da831153b5),
QWord($983e5152ee66dfab),QWord($a831c66d2db43210),QWord($b00327c898fb213f),QWord($bf597fc7beef0ee4),
QWord($c6e00bf33da88fc2),QWord($d5a79147930aa725),QWord($06ca6351e003826f),QWord($142929670a0e6e70),
QWord($27b70a8546d22ffc),QWord($2e1b21385c26c926),QWord($4d2c6dfc5ac42aed),QWord($53380d139d95b3df),
QWord($650a73548baf63de),QWord($766a0abb3c77b2a8),QWord($81c2c92e47edaee6),QWord($92722c851482353b),
QWord($a2bfe8a14cf10364),QWord($a81a664bbc423001),QWord($c24b8b70d0f89791),QWord($c76c51a30654be30),
QWord($d192e819d6ef5218),QWord($d69906245565a910),QWord($f40e35855771202a),QWord($106aa07032bbd1b8),
QWord($19a4c116b8d2d0c8),QWord($1e376c085141ab53),QWord($2748774cdf8eeb99),QWord($34b0bcb5e19b48a8),
QWord($391c0cb3c5c95a63),QWord($4ed8aa4ae3418acb),QWord($5b9cca4f7763e373),QWord($682e6ff3d6b2b8a3),
QWord($748f82ee5defb2fc),QWord($78a5636f43172f60),QWord($84c87814a1f0ab72),QWord($8cc702081a6439ec),
QWord($90befffa23631e28),QWord($a4506cebde82bde9),QWord($bef9a3f7b2c67915),QWord($c67178f2e372532b),
QWord($ca273eceea26619c),QWord($d186b8c721c0c207),QWord($eada7dd6cde0eb1e),QWord($f57d4f7fee6ed178),
QWord($06f067aa72176fba),QWord($0a637dc5a2c898a6),QWord($113f9804bef90dae),QWord($1b710b35131c471b),
QWord($28db77f523047d84),QWord($32caab7b40c72493),QWord($3c9ebe0a15c9bebc),QWord($431d67c49c100d4c),
QWord($4cc5d4becb3e42b6),QWord($597f299cfc657e2a),QWord($5fcb6fab3ad6faec),QWord($6c44198c4a475817));
procedure sha512_compresspas(var Hash: TSHA512Hash; Data: PQWordArray);
var a,b,c,d,e,f,g,h, temp1,temp2: QWord; // to use registers on CPU64
w: array[0..79] of QWord;
i: integer;
begin
bswap64array(Data,@w,16);
for i := 16 to 79 do
{$ifdef FPC} // uses faster built-in right rotate intrinsic
w[i] := (RorQWord(w[i-2],19) xor RorQWord(w[i-2],61) xor (w[i-2] shr 6)) +
w[i-7] + (RorQWord(w[i-15],1) xor RorQWord(w[i-15],8) xor (w[i-15] shr 7)) + w[i-16];
{$else}
w[i] := (((w[i-2] shr 19) or (w[i-2] shl 45)) xor ((w[i-2] shr 61) or (w[i-2] shl 3)) xor
(w[i-2] shr 6)) + w[i-7] + (((w[i-15] shr 1) or (w[i-15] shl 63)) xor
((w[i-15] shr 8) or (w[i-15] shl 56)) xor (w[i-15] shr 7)) + w[i-16];
{$endif}
a := Hash.a;
b := Hash.b;
c := Hash.c;
d := Hash.d;
e := Hash.e;
f := Hash.f;
g := Hash.g;
h := Hash.h;
for i := 0 to 79 do begin
{$ifdef FPC}
temp1 := h + (RorQWord(e,14) xor RorQWord(e,18) xor RorQWord(e,41)) +
((e and f) xor (not e and g)) + SHA512K[i] + w[i];
temp2 := (RorQWord(a,28) xor RorQWord(a,34) xor RorQWord(a,39)) +
((a and b) xor (a and c) xor (b and c));
{$else}
temp1 := h + (((e shr 14) or (e shl 50)) xor ((e shr 18) or (e shl 46)) xor
((e shr 41) or (e shl 23))) + ((e and f) xor (not e and g)) + SHA512K[i] + w[i];
temp2 := (((a shr 28) or (a shl 36)) xor ((a shr 34) or (a shl 30)) xor
((a shr 39) or (a shl 25))) + ((a and b) xor (a and c) xor (b and c));
{$endif}
h := g;
g := f;
f := e;
e := d + temp1;
d := c;
c := b;
b := a;
a := temp1 + temp2;
end;
inc(Hash.a,a);
inc(Hash.b,b);
inc(Hash.c,c);
inc(Hash.d,d);
inc(Hash.e,e);
inc(Hash.f,f);
inc(Hash.g,g);
inc(Hash.h,h);
end;
{$ifdef SHA512_X86} // optimized asm using SSE3 instructions for x86 32-bit
{$ifdef FPC}
{$ifdef MSWINDOWS}
{$L static\i386-win32\sha512-x86.o}
{$else}
{$L static/i386-linux/sha512-x86.o}
{$endif}
{$else}
{$L sha512-x86.obj}
{$endif}
{
SHA-512 hash in x86 assembly
Copyright (c) 2014 Project Nayuki. (MIT License)
https://www.nayuki.io/page/fast-sha2-hashes-in-x86-assembly
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
- The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
- The Software is provided "as is", without warranty of any kind, express or
implied, including but not limited to the warranties of merchantability,
fitness for a particular purpose and noninfringement. In no event shall the
authors or copyright holders be liable for any claim, damages or other liability,
whether in an action of contract, tort or otherwise, arising from, out of or
in connection with the Software or the use or other dealings in the Software.
}
procedure sha512_compress(state: PQWord; block: PByteArray); cdecl; external;
{$endif SHA512_X86}
{$ifdef SHA512_X64} // optimized asm using SSE4 instructions for x64 64-bit
{$ifdef FPC}
{$ifdef MSWINDOWS}
{$L sha512-x64sse4.obj}
{$else}
{$L static/x86_64-linux/sha512-x64sse4.o}
{$endif}
{$else}
{$L sha512-x64sse4.obj}
{$endif}
procedure sha512_sse4(data, hash: pointer; blocks: Int64); {$ifdef FPC}cdecl;{$endif} external;
{$endif SHA512_X64}
procedure RawSha512Compress(var Hash; Data: pointer);
begin
{$ifdef SHA512_X86}
if cfSSSE3 in CpuFeatures then
sha512_compress(@Hash,Data) else
{$endif}
{$ifdef SHA512_X64}
if cfSSE41 in CpuFeatures then
sha512_sse4(Data,@Hash,1) else
{$endif}
sha512_compresspas(TSHA512Hash(Hash), Data);
end;
{ TSHA384 }
procedure TSHA384.Final(out Digest: TSHA384Digest; NoInit: boolean);
begin
Data[Index] := $80;
FillcharFast(Data[Index+1],127-Index,0);
if Index>=112 then begin
RawSha512Compress(Hash,@Data);
FillcharFast(Data,112,0);
end;
PQWord(@Data[112])^ := bswap64(MLen shr 61);
PQWord(@Data[120])^ := bswap64(MLen shl 3);
RawSha512Compress(Hash,@Data);
bswap64array(@Hash,@Digest,6);
if not NoInit then
Init;
end;
function TSHA384.Final(NoInit: boolean): TSHA384Digest;
begin
Final(result,NoInit);
end;
procedure TSHA384.Full(Buffer: pointer; Len: integer; out Digest: TSHA384Digest);
begin
Init;
Update(Buffer,Len); // final bytes
Final(Digest);
end;
procedure TSHA384.Init;
begin
Hash.a := QWord($cbbb9d5dc1059ed8);
Hash.b := QWord($629a292a367cd507);
Hash.c := QWord($9159015a3070dd17);
Hash.d := QWord($152fecd8f70e5939);
Hash.e := QWord($67332667ffc00b31);
Hash.f := QWord($8eb44a8768581511);
Hash.g := QWord($db0c2e0d64f98fa7);
Hash.h := QWord($47b5481dbefa4fa4);
MLen := 0;
Index := 0;
FillcharFast(Data,sizeof(Data),0);
end;
procedure TSHA384.Update(Buffer: pointer; Len: integer);
var aLen: integer;
begin
if (Buffer=nil) or (Len<=0) then exit; // avoid GPF
inc(MLen,Len);
repeat
aLen := sizeof(Data)-Index;
if aLen<=Len then begin
if Index<>0 then begin
MoveFast(Buffer^,Data[Index],aLen);
RawSha512Compress(Hash,@Data);
Index := 0;
end else // avoid temporary copy
RawSha512Compress(Hash,Buffer);
dec(Len,aLen);
inc(PByte(Buffer),aLen);
end else begin
MoveFast(Buffer^,Data[Index],Len);
inc(Index,Len);
break;
end;
until Len<=0;
end;
procedure TSHA384.Update(const Buffer: RawByteString);
begin
Update(pointer(Buffer),length(Buffer));
end;
{ TSHA512 }
procedure TSHA512.Final(out Digest: TSHA512Digest; NoInit: boolean);
begin
Data[Index] := $80;
FillcharFast(Data[Index+1],127-Index,0);
if Index>=112 then begin
RawSha512Compress(Hash,@Data);
FillcharFast(Data,112,0);
end;
PQWord(@Data[112])^ := bswap64(MLen shr 61);
PQWord(@Data[120])^ := bswap64(MLen shl 3);
RawSha512Compress(Hash,@Data);
bswap64array(@Hash,@Digest,8);
if not NoInit then
Init;
end;
function TSHA512.Final(NoInit: boolean): TSHA512Digest;
begin
Final(result,NoInit);
end;
procedure TSHA512.Full(Buffer: pointer; Len: integer; out Digest: TSHA512Digest);
begin
Init;
Update(Buffer,Len); // final bytes
Final(Digest);
end;
procedure TSHA512.Init;
begin
Hash.a := $6a09e667f3bcc908;
Hash.b := QWord($bb67ae8584caa73b);
Hash.c := $3c6ef372fe94f82b;
Hash.d := QWord($a54ff53a5f1d36f1);
Hash.e := $510e527fade682d1;
Hash.f := QWord($9b05688c2b3e6c1f);
Hash.g := $1f83d9abfb41bd6b;
Hash.h := $5be0cd19137e2179;
MLen := 0;
Index := 0;
FillcharFast(Data,sizeof(Data),0);
end;
procedure TSHA512.Update(Buffer: pointer; Len: integer);
var aLen: integer;
begin
if (Buffer=nil) or (Len<=0) then exit; // avoid GPF
inc(MLen,Len);
repeat
aLen := sizeof(Data)-Index;
if aLen<=Len then begin
if Index<>0 then begin
MoveFast(Buffer^,Data[Index],aLen);
RawSha512Compress(Hash,@Data);
Index := 0;
end else // avoid temporary copy
RawSha512Compress(Hash,Buffer);
dec(Len,aLen);
inc(PByte(Buffer),aLen);
end else begin
MoveFast(Buffer^,Data[Index],Len);
inc(Index,Len);
break;
end;
until Len<=0;
end;
procedure TSHA512.Update(const Buffer: RawByteString);
begin
Update(pointer(Buffer),length(Buffer));
end;
{ TSHA3 }
{ SHA-3 / Keccak original code (c) Wolfgang Ehrhardt under zlib license:
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in
a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution. }
const
cKeccakPermutationSize = 1600;
cKeccakMaximumRate = 1536;
cKeccakPermutationSizeInBytes = cKeccakPermutationSize div 8;
cKeccakMaximumRateInBytes = cKeccakMaximumRate div 8;
cKeccakNumberOfRounds = 24;
cRoundConstants: array[0..cKeccakNumberOfRounds-1] of QWord = (
QWord($0000000000000001), QWord($0000000000008082), QWord($800000000000808A),
QWord($8000000080008000), QWord($000000000000808B), QWord($0000000080000001),
QWord($8000000080008081), QWord($8000000000008009), QWord($000000000000008A),
QWord($0000000000000088), QWord($0000000080008009), QWord($000000008000000A),
QWord($000000008000808B), QWord($800000000000008B), QWord($8000000000008089),
QWord($8000000000008003), QWord($8000000000008002), QWord($8000000000000080),
QWord($000000000000800A), QWord($800000008000000A), QWord($8000000080008081),
QWord($8000000000008080), QWord($0000000080000001), QWord($8000000080008008));
type
{$ifdef USERECORDWITHMETHODS}TSHA3Context = record
{$else}TSHA3Context = object{$endif}
public
State: packed array[0..cKeccakPermutationSizeInBytes-1] of byte;
DataQueue: packed array[0..cKeccakMaximumRateInBytes-1] of byte;
Algo: TSHA3Algo;
Squeezing: boolean;
Rate: integer;
Capacity: integer;
BitsInQueue: integer;
BitsAvailableForSqueezing: integer;
procedure Init(aAlgo: TSHA3Algo);
procedure InitSponge(aRate, aCapacity: integer);
procedure AbsorbQueue;
procedure Absorb(data: PByteArray; databitlen: integer);
procedure AbsorbFinal(data: PByteArray; databitlen: integer);
procedure PadAndSwitchToSqueezingPhase;
procedure Squeeze(output: PByteArray; outputLength: integer);
procedure FinalBit_LSB(bits: byte; bitlen: integer;
hashval: Pointer; numbits: integer);
end;
PSHA3Context = ^TSHA3Context;
{$ifdef SHA3_PASCAL}
{$ifdef FPC} // RotL/RolQword is an intrinsic function under FPC :)
function RotL(const x: QWord; c: integer): QWord; inline;
begin
result := RolQword(x, c);
end;
function RotL1(const x: QWord): QWord; inline;
begin
result := RolQword(x);
end;
{$else}
function RotL(const x: QWord; c: integer): QWord; {$ifdef HASINLINE}inline;{$endif}
begin
result := (x shl c) or (x shr (64 - c));
end;
function RotL1(var x: QWord): QWord; {$ifdef HASINLINE}inline;{$endif}
begin
result := (x shl 1) or (x shr (64 - 1));
end;
{$endif FPC}
procedure KeccakPermutation(A: PQWordArray);
var
B: array[0..24] of QWord;
C0, C1, C2, C3, C4, D0, D1, D2, D3, D4: QWord;
i: integer;
begin
for i := 0 to 23 do begin
C0 := A[00] xor A[05] xor A[10] xor A[15] xor A[20];
C1 := A[01] xor A[06] xor A[11] xor A[16] xor A[21];
C2 := A[02] xor A[07] xor A[12] xor A[17] xor A[22];
C3 := A[03] xor A[08] xor A[13] xor A[18] xor A[23];
C4 := A[04] xor A[09] xor A[14] xor A[19] xor A[24];
D0 := RotL1(C0) xor C3;
D1 := RotL1(C1) xor C4;
D2 := RotL1(C2) xor C0;
D3 := RotL1(C3) xor C1;
D4 := RotL1(C4) xor C2;
B[00] := A[00] xor D1;
B[01] := RotL(A[06] xor D2, 44);
B[02] := RotL(A[12] xor D3, 43);
B[03] := RotL(A[18] xor D4, 21);
B[04] := RotL(A[24] xor D0, 14);
B[05] := RotL(A[03] xor D4, 28);
B[06] := RotL(A[09] xor D0, 20);
B[07] := RotL(A[10] xor D1, 3);
B[08] := RotL(A[16] xor D2, 45);
B[09] := RotL(A[22] xor D3, 61);
B[10] := RotL(A[01] xor D2, 1);
B[11] := RotL(A[07] xor D3, 6);
B[12] := RotL(A[13] xor D4, 25);
B[13] := RotL(A[19] xor D0, 8);
B[14] := RotL(A[20] xor D1, 18);
B[15] := RotL(A[04] xor D0, 27);
B[16] := RotL(A[05] xor D1, 36);
B[17] := RotL(A[11] xor D2, 10);
B[18] := RotL(A[17] xor D3, 15);
B[19] := RotL(A[23] xor D4, 56);
B[20] := RotL(A[02] xor D3, 62);
B[21] := RotL(A[08] xor D4, 55);
B[22] := RotL(A[14] xor D0, 39);
B[23] := RotL(A[15] xor D1, 41);
B[24] := RotL(A[21] xor D2, 2);
A[00] := B[00] xor ((not B[01]) and B[02]);
A[01] := B[01] xor ((not B[02]) and B[03]);
A[02] := B[02] xor ((not B[03]) and B[04]);
A[03] := B[03] xor ((not B[04]) and B[00]);
A[04] := B[04] xor ((not B[00]) and B[01]);
A[05] := B[05] xor ((not B[06]) and B[07]);
A[06] := B[06] xor ((not B[07]) and B[08]);
A[07] := B[07] xor ((not B[08]) and B[09]);
A[08] := B[08] xor ((not B[09]) and B[05]);
A[09] := B[09] xor ((not B[05]) and B[06]);
A[10] := B[10] xor ((not B[11]) and B[12]);
A[11] := B[11] xor ((not B[12]) and B[13]);
A[12] := B[12] xor ((not B[13]) and B[14]);
A[13] := B[13] xor ((not B[14]) and B[10]);
A[14] := B[14] xor ((not B[10]) and B[11]);
A[15] := B[15] xor ((not B[16]) and B[17]);
A[16] := B[16] xor ((not B[17]) and B[18]);
A[17] := B[17] xor ((not B[18]) and B[19]);
A[18] := B[18] xor ((not B[19]) and B[15]);
A[19] := B[19] xor ((not B[15]) and B[16]);
A[20] := B[20] xor ((not B[21]) and B[22]);
A[21] := B[21] xor ((not B[22]) and B[23]);
A[22] := B[22] xor ((not B[23]) and B[24]);
A[23] := B[23] xor ((not B[24]) and B[20]);
A[24] := B[24] xor ((not B[20]) and B[21]);
A[00] := A[00] xor cRoundConstants[i];
end;
end;
{$else SHA3_PASCAL}
{ - MMX 32-bit assembler version based on optimized SHA-3 kernel by Eric Grange
https://www.delphitools.info/2016/04/19/new-sha-3-permutation-kernel
- new x64 assembler version by Synopse }
procedure KeccakPermutationKernel(B, A, C: Pointer);
{$ifdef CPU32} // Eric Grange's MMX version (PIC-safe)
{$ifdef FPC}nostackframe; assembler;{$endif}
asm
add edx, 128
add eax, 128
movq mm1, [edx - 120]
movq mm4, [edx - 96]
movq mm3, [edx - 104]
pxor mm1, [edx - 80]
movq mm5, [edx + 16]
pxor mm1, [edx]
movq mm2, [edx - 112]
pxor mm1, [edx + 40]
pxor mm1, [edx - 40]
movq mm0, [edx - 128]
movq mm6, mm1
pxor mm4, [edx - 56]
movq [ecx + 8], mm1
psrlq mm6, 63
pxor mm4, [edx + 24]
pxor mm4, [edx + 64]
pxor mm4, [edx - 16]
psllq mm1, 1
pxor mm2, [edx + 48]
por mm1, mm6
movq mm6, [edx - 88]
pxor mm1, mm4
pxor mm2, [edx - 32]
pxor mm2, [edx - 72]
pxor mm6, mm1
movq mm7, mm6
psrlq mm7, 28
psllq mm6, 36
por mm6, mm7
pxor mm2, [edx + 8]
movq [eax], mm6
movq mm6, [edx + 32]
movq mm7, mm4
psrlq mm7, 63
psllq mm4, 1
pxor mm0, mm6
por mm4, mm7
pxor mm4, mm2
pxor mm5, mm4
movq mm7, mm5
pxor mm0, [edx - 8]
psllq mm5, 21
psrlq mm7, 43
pxor mm6, mm1
por mm5, mm7
movq [eax - 104], mm5
movq mm5, [edx - 48]
pxor mm0, mm5
movq mm7, mm6
psrlq mm7, 46
psllq mm6, 18
por mm6, mm7
movq [eax - 16], mm6
movq mm6, [edx + 56]
pxor mm5, mm1
movq mm7, mm5
pxor mm3, mm6
psllq mm5, 3
psrlq mm7, 61
pxor mm3, [edx + 16]
pxor mm3, [edx - 24]
por mm5, mm7
pxor mm6, mm4
pxor mm0, [edx - 88]
movq mm7, mm6
psrlq mm7, 8
movq [eax - 72], mm5
movq mm5, mm2
psllq mm2, 1
psllq mm6, 56
psrlq mm5, 63
por mm6, mm7
por mm2, mm5
pxor mm2, mm0
movq [eax + 24], mm6
movq mm5, [edx - 120]
movq mm6, mm0
psllq mm0, 1
pxor mm5, mm2
pxor mm3, [edx - 64]
psrlq mm6, 63
por mm0, mm6
movq mm6, [edx - 64]
movq mm7, mm5
psllq mm5, 1
psrlq mm7, 63
pxor mm6, mm4
por mm5, mm7
pxor mm0, mm3
movq mm7, mm6
movq [eax - 48], mm5
movq mm5, [edx]
psllq mm6, 55
psrlq mm7, 9
por mm6, mm7
movq [eax + 40], mm6
movq mm6, [edx - 40]
pxor mm5, mm2
movq mm7, mm5
psllq mm5, 45
psrlq mm7, 19
pxor mm6, mm2
por mm5, mm7
movq [eax - 64], mm5
movq mm5, [edx + 40]
movq mm7, mm6
pxor mm5, mm2
psllq mm6, 10
psrlq mm7, 54
por mm6, mm7
movq [eax + 8], mm6
movq mm6, [edx - 96]
movq mm7, mm3
psrlq mm7, 63
psllq mm3, 1
por mm3, mm7
movq mm7, mm5
psllq mm5, 2
psrlq mm7, 62
por mm5, mm7
movq [eax + 64], mm5
movq mm5, [edx + 24]
pxor mm6, mm0
movq mm7, mm6
psrlq mm7, 37
psllq mm6, 27
por mm6, mm7
movq [eax - 8], mm6
pxor mm5, mm0
movq mm6, [edx - 16]
movq mm7, mm5
psllq mm5, 8
pxor mm3, [ecx + 8]
psrlq mm7, 56
pxor mm6, mm0
por mm5, mm7
movq [eax - 24], mm5
movq mm7, mm6
psllq mm6, 39
movq mm5, [edx - 112]
psrlq mm7, 25
por mm6, mm7
movq [eax + 48], mm6
movq mm6, [edx - 24]
pxor mm5, mm3
movq mm7, mm5
psrlq mm7, 2
psllq mm5, 62
por mm5, mm7
movq [eax + 32], mm5
movq mm5, [edx - 104]
pxor mm6, mm4
movq mm7, mm6
psrlq mm7, 39
psllq mm6, 25
por mm6, mm7
pxor mm5, mm4
movq [eax - 32], mm6
movq mm6, [edx - 128]
pxor mm6, mm1
movq mm4, mm6
movq [eax - 128], mm6
movq mm4, mm6
movq mm6, [edx - 8]
movq mm7, mm5
psrlq mm7, 36
psllq mm5, 28
pxor mm6, mm1
por mm5, mm7
movq mm7, mm6
psrlq mm7, 23
movq mm1, mm5
movq [eax - 88], mm5
movq mm5, [edx - 56]
pxor mm5, mm0
psllq mm6, 41
por mm6, mm7
movq [eax + 56], mm6
movq mm6, [edx + 48]
pxor mm6, mm3
movq mm7, mm5
psrlq mm7, 44
psllq mm5, 20
por mm5, mm7
movq [eax - 80], mm5
pandn mm1, mm5
movq mm5, [edx - 32]
movq mm7, mm6
psrlq mm7, 3
psllq mm6, 61
por mm6, mm7
pxor mm1, mm6
movq [eax - 56], mm6
movq mm6, [edx + 8]
movq [edx - 56], mm1
movq mm1, [eax - 112]
pxor mm5, mm3
movq mm7, mm5
psllq mm5, 43
psrlq mm7, 21
pxor mm6, mm3
por mm5, mm7
movq mm1, mm5
movq mm5, [edx - 80]
pxor mm5, mm2
movq mm2, [eax - 104]
movq mm7, mm6
psrlq mm7, 49
psllq mm6, 15
por mm6, mm7
movq [eax + 16], mm6
movq mm6, [edx + 64]
movq [eax - 96], mm6
movq mm7, mm5
psrlq mm7, 20
psllq mm5, 44
pxor mm6, mm0
por mm5, mm7
movq mm7, mm6
psrlq mm7, 50
psllq mm6, 14
por mm6, mm7
pandn mm2, mm6
movq mm0, mm5
pandn mm0, mm1
pxor mm2, mm1
pandn mm1, [eax - 104]
movq [edx - 112], mm2
pandn mm4, mm5
pxor mm1, mm5
movq [eax - 120], mm5
movq mm2, [eax - 40]
movq [edx - 120], mm1
movq mm5, [edx - 72]
movq mm1, [eax - 64]
pxor mm4, mm6
movq [edx - 96], mm4
pxor mm5, mm3
movq mm4, [eax - 88]
movq mm7, mm5
movq mm3, mm6
pxor mm0, [eax - 128]
movq [edx - 128], mm0
movq mm6, [eax - 72]
psllq mm5, 6
psrlq mm7, 58
movq mm0, [eax - 56]
por mm5, mm7
movq mm2, mm5
movq mm5, [eax - 80]
movq mm7, mm1
pandn mm7, mm0
pxor mm7, mm6
movq [edx - 72], mm7
movq mm7, [eax - 72]
pandn mm6, mm1
pxor mm6, mm5
pandn mm0, mm4
pandn mm5, mm7
movq mm7, [eax]
pxor mm5, mm4
movq mm4, [eax - 24]
movq [edx - 80], mm6
movq mm6, [eax - 48]
movq [edx - 88], mm5
movq mm5, mm1
movq mm1, [eax - 16]
pxor mm0, mm5
movq mm5, mm1
pandn mm3, [eax - 128]
pxor mm3, [eax - 104]
movq [edx - 64], mm0
movq mm0, [eax + 8]
movq [edx - 104], mm3
movq mm3, [eax - 32]
pandn mm6, mm2
pxor mm6, mm5
movq [edx - 16], mm6
movq mm6, [eax + 56]
pandn mm3, mm4
pxor mm3, mm2
movq [edx - 40], mm3
movq mm3, [eax - 32]
pandn mm5, [eax - 48]
pxor mm5, mm4
movq [edx - 24], mm5
pandn mm7, mm0
movq mm5, [eax + 16]
pandn mm4, mm1
pxor mm4, mm3
movq [edx - 32], mm4
movq mm4, [eax + 40]
movq mm1, mm5
movq mm5, [eax + 48]
pandn mm5, mm6
pxor mm5, mm4
pandn mm2, mm3
movq mm3, [eax - 8]
movq [edx + 40], mm5
movq mm5, [eax + 24]
pxor mm7, mm3
movq [edx - 8], mm7
movq mm7, [eax + 64]
pxor mm2, [eax - 48]
movq [edx - 48], mm2
movq mm2, mm5
pandn mm2, mm3
pxor mm2, mm1
movq [edx + 16], mm2
pandn mm3, [eax]
movq mm2, mm5
movq mm5, [eax + 48]
pandn mm6, mm7
pxor mm6, mm5
movq [edx + 48], mm6
pandn mm1, mm2
movq mm6, [eax + 32]
pxor mm1, mm0
pxor mm3, mm2
movq [edx + 24], mm3
pandn mm0, [eax + 16]
pxor mm0, [eax]
movq mm3, mm4
movq [edx + 8], mm1
movq [edx], mm0
movq mm0, mm6
movq mm1, [eax + 56]
pandn mm4, mm5
pxor mm4, mm0
pandn mm0, mm3
pxor mm0, mm7
movq [edx + 32], mm4
pandn mm7, mm6
pxor mm7, mm1
movq [edx + 56], mm7
movq [edx + 64], mm0
{$else}
{$ifdef FPC}nostackframe; assembler; asm{$else}
// Synopse's x64 asm, optimized for both in+out-order pipelined CPUs
asm // input: rcx=B, rdx=A, r8=C (Linux: rdi,rsi,rdx)
.noframe
{$endif}{$ifndef win64}
mov r8, rdx
mov rdx, rsi
mov rcx, rdi
{$endif win64}
push rbx
push r12
push r13
push r14
add rdx, 128
add rcx, 128
// theta
mov r10, [rdx - 128]
mov r11, [rdx - 120]
mov r12, [rdx - 112]
mov r13, [rdx - 104]
mov r14, [rdx - 96]
xor r10, [rdx - 88]
xor r11, [rdx - 80]
xor r12, [rdx - 72]
xor r13, [rdx - 64]
xor r14, [rdx - 56]
xor r10, [rdx - 48]
xor r11, [rdx - 40]
xor r12, [rdx - 32]
xor r13, [rdx - 24]
xor r14, [rdx - 16]
xor r10, [rdx - 8]
xor r11, [rdx]
xor r12, [rdx + 8]
xor r13, [rdx + 16]
xor r14, [rdx + 24]
xor r10, [rdx + 32]
xor r11, [rdx + 40]
xor r12, [rdx + 48]
xor r13, [rdx + 56]
xor r14, [rdx + 64]
mov [r8], r10
mov [r8 + 8], r11
mov [r8 + 16], r12
mov [r8 + 24], r13
mov [r8 + 32], r14
rol r10, 1
rol r11, 1
rol r12, 1
rol r13, 1
rol r14, 1
xor r10, [r8 + 24]
xor r11, [r8 + 32]
xor r12, [r8]
xor r13, [r8 + 8]
xor r14, [r8 + 16]
// rho pi
mov rax, [rdx - 128]
mov r8, [rdx - 80]
mov r9, [rdx - 32]
mov rbx, [rdx + 16]
xor rax, r11
xor r8, r12
xor r9, r13
xor rbx, r14
rol r8, 44
rol r9, 43
rol rbx, 21
mov [rcx - 128], rax
mov [rcx - 120], r8
mov [rcx - 112], r9
mov [rcx - 104], rbx
mov rax, [rdx + 64]
mov r8, [rdx - 104]
mov r9, [rdx - 56]
mov rbx, [rdx - 48]
xor rax, r10
xor r8, r14
xor r9, r10
xor rbx, r11
rol rax, 14
rol r8, 28
rol r9, 20
rol rbx, 3
mov [rcx - 96], rax
mov [rcx - 88], r8
mov [rcx - 80], r9
mov [rcx - 72], rbx
mov rax, [rdx]
mov r8, [rdx + 48]
mov r9, [rdx - 120]
mov rbx, [rdx - 72]
xor rax, r12
xor r8, r13
xor r9, r12
xor rbx, r13
rol rax, 45
rol r8, 61
rol r9, 1
rol rbx, 6
mov [rcx - 64], rax
mov [rcx - 56], r8
mov [rcx - 48], r9
mov [rcx - 40], rbx
mov rax, [rdx - 24]
mov r8, [rdx + 24]
mov r9, [rdx + 32]
mov rbx, [rdx - 96]
xor rax, r14
xor r8, r10
xor r9, r11
xor rbx, r10
rol rax, 25
rol r8, 8
rol r9, 18
rol rbx, 27
mov [rcx - 32], rax
mov [rcx - 24], r8
mov [rcx - 16], r9
mov [rcx - 8], rbx
mov rax, [rdx - 88]
mov r8, [rdx - 40]
mov r9, [rdx + 8]
mov rbx, [rdx + 56]
xor rax, r11
xor r8, r12
xor r9, r13
xor rbx, r14
rol rax, 36
rol r8, 10
rol r9, 15
rol rbx, 56
mov [rcx], rax
mov [rcx + 8], r8
mov [rcx + 16], r9
mov [rcx + 24], rbx
mov rax, [rdx - 112]
mov r8, [rdx - 64]
mov r9, [rdx - 16]
mov rbx, [rdx - 8]
xor rax, r13
xor r8, r14
xor r9, r10
mov r10, [rdx + 40]
xor rbx, r11
rol rax, 62
rol r8, 55
xor r10, r12
rol r9, 39
rol rbx, 41
mov [rcx + 32], rax
mov [rcx + 40], r8
rol r10, 2
mov [rcx + 48], r9
mov [rcx + 56], rbx
mov [rcx + 64], r10
// chi
mov rax, [rcx - 120]
mov r8, [rcx - 112]
mov r9, [rcx - 104]
mov r10, [rcx - 96]
mov r11, [rcx - 128]
mov r12, [rcx - 80]
mov r13, [rcx - 72]
mov r14, [rcx - 64]
mov rbx, [rcx - 56]
not rax
not r8
not r9
not r10
not r11
not r12
not r13
not r14
not rbx
and rax, [rcx - 112]
and r8, [rcx - 104]
and r9, [rcx - 96]
and r10, [rcx - 128]
and r11, [rcx - 120]
and r12, [rcx - 72]
and r13, [rcx - 64]
and r14, [rcx - 56]
and rbx, [rcx - 88]
xor rax, [rcx - 128]
xor r8, [rcx - 120]
xor r9, [rcx - 112]
xor r10, [rcx - 104]
xor r11, [rcx - 96]
xor r12, [rcx - 88]
xor r13, [rcx - 80]
xor r14, [rcx - 72]
xor rbx, [rcx - 64]
mov [rdx - 128], rax
mov [rdx - 120], r8
mov [rdx - 112], r9
mov [rdx - 104], r10
mov [rdx - 96], r11
mov [rdx - 88], r12
mov [rdx - 80], r13
mov [rdx - 72], r14
mov [rdx - 64], rbx
mov rax, [rcx - 88]
mov rbx, [rcx - 40]
mov r8, [rcx - 32]
mov r9, [rcx - 24]
mov r10, [rcx - 16]
mov r11, [rcx - 48]
mov r12, [rcx]
mov r13, [rcx + 8]
mov r14, [rcx + 16]
not rax
not rbx
not r8
not r9
not r10
not r11
not r12
not r13
not r14
and rax, [rcx - 80]
and rbx, [rcx - 32]
and r8, [rcx - 24]
and r9, [rcx - 16]
and r10, [rcx - 48]
and r11, [rcx - 40]
and r12, [rcx + 8]
and r13, [rcx + 16]
and r14, [rcx + 24]
xor rax, [rcx - 56]
xor rbx, [rcx - 48]
xor r8, [rcx - 40]
xor r9, [rcx - 32]
xor r10, [rcx - 24]
xor r11, [rcx - 16]
xor r12, [rcx - 8]
xor r13, [rcx]
xor r14, [rcx + 8]
mov [rdx - 56], rax
mov [rdx - 48], rbx
mov [rdx - 40], r8
mov [rdx - 32], r9
mov [rdx - 24], r10
mov [rdx - 16], r11
mov [rdx - 8], r12
mov [rdx], r13
mov [rdx + 8], r14
mov rax, [rcx + 24]
mov rbx, [rcx - 8]
mov r8, [rcx + 40]
mov r9, [rcx + 48]
mov r10, [rcx + 56]
mov r11, [rcx + 64]
mov r12, [rcx + 32]
not rax
not rbx
not r8
not r9
not r10
not r11
not r12
and rax, [rcx - 8]
and rbx, [rcx]
and r8, [rcx + 48]
and r9, [rcx + 56]
and r10, [rcx + 64]
and r11, [rcx + 32]
and r12, [rcx + 40]
xor rax, [rcx + 16]
xor rbx, [rcx + 24]
xor r8, [rcx + 32]
xor r9, [rcx + 40]
xor r10, [rcx + 48]
xor r11, [rcx + 56]
xor r12, [rcx + 64]
mov [rdx + 16], rax
mov [rdx + 24], rbx
mov [rdx + 32], r8
mov [rdx + 40], r9
mov [rdx + 48], r10
mov [rdx + 56], r11
mov [rdx + 64], r12
pop r14
pop r13
pop r12
pop rbx
{$endif}
end;
procedure KeccakPermutation(A: PQWordArray);
var
B: array[0..24] of QWord;
C: array[0..4] of QWord;
i: integer;
begin
for i := 0 to 23 do begin
KeccakPermutationKernel(@B, A, @C);
A[00] := A[00] xor cRoundConstants[i];
end;
{$ifdef CPU32}
asm
emms // reset MMX after use
end;
{$endif}
end;
{$endif SHA3_PASCAL}
procedure TSHA3Context.Init(aAlgo: TSHA3Algo);
begin
case aAlgo of
SHA3_224:
InitSponge(1152, 448);
SHA3_256:
InitSponge(1088, 512);
SHA3_384:
InitSponge(832, 768);
SHA3_512:
InitSponge(576, 1024);
SHAKE_128:
InitSponge(1344, 256);
SHAKE_256:
InitSponge(1088, 512);
else
raise ESynCrypto.CreateUTF8('Unexpected TSHA3Context.Init(%)', [ord(aAlgo)]);
end;
Algo := aAlgo;
end;
procedure TSHA3Context.InitSponge(aRate, aCapacity: integer);
begin
if (aRate + aCapacity <> 1600) or (aRate <= 0) or (aRate >= 1600) or
((aRate and 63) <> 0) then
raise ESynCrypto.CreateUTF8('Unexpected TSHA3Context.Init(%,%)', [aRate, aCapacity]);
FillCharFast(self, sizeof(self), 0);
Rate := aRate;
Capacity := aCapacity;
end;
procedure TSHA3Context.AbsorbQueue;
begin
XorMemoryPtrInt(@State, @DataQueue, Rate shr {$ifdef CPU32}5{$else}6{$endif});
KeccakPermutation(@State);
end;
procedure TSHA3Context.Absorb(data: PByteArray; databitlen: integer);
var
i, j, wholeBlocks, partialBlock: integer;
partialByte: integer;
curData: pointer;
begin
if BitsInQueue and 7 <> 0 then
raise ESynCrypto.Create('TSHA3Context.Absorb: only last may contain partial');
if Squeezing then
raise ESynCrypto.Create('TSHA3Context.Absorb: too late for additional input');
i := 0;
while i < databitlen do begin
if (BitsInQueue = 0) and (databitlen >= Rate) and (i <= (databitlen - Rate)) then begin
wholeBlocks := (databitlen - i) div Rate;
curData := @data^[i shr 3];
for j := 1 to wholeBlocks do begin
XorMemoryPtrInt(@State, curData, Rate shr {$ifdef CPU32}5{$else}6{$endif});
KeccakPermutation(@State);
inc(PByte(curData), Rate shr 3);
end;
inc(i, wholeBlocks * Rate);
end
else begin
partialBlock := databitlen - i;
if partialBlock + BitsInQueue > Rate then
partialBlock := Rate - BitsInQueue;
partialByte := partialBlock and 7;
dec(partialBlock, partialByte);
MoveFast(data^[i shr 3], DataQueue[BitsInQueue shr 3], partialBlock shr 3);
inc(BitsInQueue, partialBlock);
inc(i, partialBlock);
if BitsInQueue = Rate then begin
AbsorbQueue;
BitsInQueue := 0;
end;
if partialByte > 0 then begin
DataQueue[BitsInQueue shr 3] := data^[i shr 3] and ((1 shl partialByte) - 1);
inc(BitsInQueue, partialByte);
inc(i, partialByte);
end;
end;
end;
end;
procedure TSHA3Context.AbsorbFinal(data: PByteArray; databitlen: integer);
var
lastByte: byte;
begin
if databitlen and 7 = 0 then
Absorb(data, databitlen)
else begin
Absorb(data, databitlen - (databitlen and 7));
// Align the last partial byte to the least significant bits
lastByte := data^[databitlen shr 3] shr (8 - (databitlen and 7));
Absorb(@lastByte, databitlen and 7);
end;
end;
procedure TSHA3Context.PadAndSwitchToSqueezingPhase;
var
i: integer;
begin // note: the bits are numbered from 0=LSB to 7=MSB
if BitsInQueue + 1 = Rate then begin
i := BitsInQueue shr 3;
DataQueue[i] := DataQueue[i] or (1 shl (BitsInQueue and 7));
AbsorbQueue;
FillCharFast(DataQueue, Rate shr 3, 0);
end
else begin
i := BitsInQueue shr 3;
FillCharFast(DataQueue[(BitsInQueue + 7) shr 3], Rate shr 3 - (BitsInQueue + 7) shr 3, 0);
DataQueue[i] := DataQueue[i] or (1 shl (BitsInQueue and 7));
end;
i := (Rate - 1) shr 3;
DataQueue[i] := DataQueue[i] or (1 shl ((Rate - 1) and 7));
AbsorbQueue;
MoveFast(State, DataQueue, Rate shr 3);
BitsAvailableForSqueezing := Rate;
Squeezing := true;
end;
procedure TSHA3Context.Squeeze(output: PByteArray; outputLength: integer);
var
i: integer;
partialBlock: integer;
begin
if not Squeezing then
PadAndSwitchToSqueezingPhase;
if outputLength and 7 <> 0 then
raise ESynCrypto.CreateUTF8('TSHA3Context.Squeeze(%?)', [outputLength]);
i := 0;
while i < outputLength do begin
if BitsAvailableForSqueezing = 0 then begin
KeccakPermutation(@State);
MoveFast(State, DataQueue, Rate shr 3);
BitsAvailableForSqueezing := Rate;
end;
partialBlock := BitsAvailableForSqueezing;
if partialBlock > outputLength - i then
partialBlock := outputLength - i;
MoveFast(DataQueue[(Rate - BitsAvailableForSqueezing) shr 3],
output^[i shr 3], partialBlock shr 3);
dec(BitsAvailableForSqueezing, partialBlock);
inc(i, partialBlock);
end;
end;
procedure TSHA3Context.FinalBit_LSB(bits: byte; bitlen: integer;
hashval: Pointer; numbits: integer);
var
ll: integer;
lw: word;
begin
bitlen := bitlen and 7;
if bitlen = 0 then
lw := 0
else
lw := bits and Pred(word(1) shl bitlen);
// 'append' (in LSB language) the domain separation bits
if Algo >= SHAKE_128 then begin
// SHAKE: append four bits 1111
lw := lw or (word($F) shl bitlen);
ll := bitlen + 4;
end
else begin
// SHA-3: append two bits 01
lw := lw or (word($2) shl bitlen);
ll := bitlen + 2;
end;
// update state with final bits
if ll < 9 then begin // 0..8 bits, one call to update
lw := lw shl (8 - ll);
AbsorbFinal(@lw, ll);
// squeeze the digits from the sponge
Squeeze(hashval, numbits);
end
else begin
// more than 8 bits, first a regular update with low byte
AbsorbFinal(@lw, 8);
// finally update remaining last bits
dec(ll, 8);
lw := lw shr ll;
AbsorbFinal(@lw, ll);
Squeeze(hashval, numbits);
end;
end;
procedure TSHA3.Init(Algo: TSHA3Algo);
begin
PSHA3Context(@Context)^.Init(Algo);
end;
function TSHA3.Algorithm: TSHA3Algo;
begin
result := PSHA3Context(@Context)^.Algo;
end;
procedure TSHA3.Update(const Buffer: RawByteString);
begin
if Buffer <> '' then
PSHA3Context(@Context)^.Absorb(pointer(Buffer), Length(Buffer) shl 3);
end;
procedure TSHA3.Update(Buffer: pointer; Len: integer);
begin
if Len > 0 then
PSHA3Context(@Context)^.Absorb(Buffer, Len shl 3);
end;
procedure TSHA3.Final(out Digest: THash256; NoInit: boolean);
begin
Final(@Digest, 256, NoInit);
end;
procedure TSHA3.Final(out Digest: THash512; NoInit: boolean);
begin
Final(@Digest, 512, NoInit);
end;
const
SHA3_DEF_LEN: array[TSHA3Algo] of integer = (224, 256, 384, 512, 256, 512);
procedure TSHA3.Final(Digest: pointer; DigestBits: integer; NoInit: boolean);
begin
if DigestBits = 0 then
DigestBits := SHA3_DEF_LEN[TSHA3Context(Context).Algo];
if TSHA3Context(Context).Squeezing then // used as Extendable-Output Function
PSHA3Context(@Context)^.Squeeze(Digest, DigestBits)
else
PSHA3Context(@Context)^.FinalBit_LSB(0, 0, Digest, DigestBits);
if not NoInit then
FillCharFast(Context, sizeof(Context), 0);
end;
function TSHA3.Final256(NoInit: boolean): THash256;
begin
Final(result,NoInit);
end;
function TSHA3.Final512(NoInit: boolean): THash512;
begin
Final(result,NoInit);
end;
procedure TSHA3.Full(Buffer: pointer; Len: integer; out Digest: THash256);
begin
Full(SHA3_256, Buffer, Len, @Digest, 256);
end;
procedure TSHA3.Full(Buffer: pointer; Len: integer; out Digest: THash512);
begin
Full(SHA3_512, Buffer, Len, @Digest, 512);
end;
procedure TSHA3.Full(Algo: TSHA3Algo; Buffer: pointer; Len: integer;
Digest: pointer; DigestBits: integer);
begin
Init(Algo);
Update(Buffer, Len);
Final(Digest, DigestBits);
end;
function TSHA3.FullStr(Algo: TSHA3Algo; Buffer: pointer; Len: integer;
DigestBits: integer): RawUTF8;
var
tmp: RawByteString;
begin
if DigestBits = 0 then
DigestBits := SHA3_DEF_LEN[Algo];
SetLength(tmp, DigestBits shr 3);
Full(Algo, Buffer, Len, pointer(tmp), DigestBits);
result := SynCommons.BinToHex(tmp);
FillZero(tmp);
end;
procedure TSHA3.Cypher(Key, Source, Dest: pointer; KeyLen, DataLen: integer;
Algo: TSHA3Algo);
begin
if DataLen <= 0 then
exit;
if Source = Dest then
raise ESynCrypto.Create('Unexpected TSHA3.Cypher(Source=Dest)');
Full(Algo, Key, KeyLen, Dest, DataLen shl 3);
XorMemory(Dest, Source, DataLen); // just as simple as that!
end;
function TSHA3.Cypher(const Key, Source: RawByteString; Algo: TSHA3Algo): RawByteString;
var
len: integer;
begin
len := length(Source);
SetString(result, nil, len);
Cypher(pointer(Key), pointer(Source), pointer(result), length(Key), len);
end;
procedure TSHA3.InitCypher(Key: pointer; KeyLen: integer; Algo: TSHA3Algo);
begin
Init(Algo);
Update(Key, KeyLen);
PSHA3Context(@Context)^.FinalBit_LSB(0, 0, nil, 0);
end;
procedure TSHA3.InitCypher(const Key: RawByteString; Algo: TSHA3Algo);
begin
InitCypher(pointer(Key), length(Key), Algo);
end;
procedure TSHA3.Cypher(Source, Dest: pointer; DataLen: integer);
begin
Final(Dest, DataLen shl 3, true); // in XOF mode
XorMemory(Dest, Source, DataLen);
end;
function TSHA3.Cypher(const Source: RawByteString): RawByteString;
var
len: integer;
begin
len := length(Source);
SetString(result, nil, len);
Cypher(pointer(Source), pointer(result), len);
end;
procedure TSHA3.Done;
begin
FillCharFast(self, sizeof(self), 0);
end;
function SHA3(Algo: TSHA3Algo; const s: RawByteString;
DigestBits: integer): RawUTF8;
begin
result := SHA3(algo, pointer(s), length(s), DigestBits);
end;
function SHA3(Algo: TSHA3Algo; Buffer: pointer; Len, DigestBits: integer): RawUTF8;
var
instance: TSHA3;
begin
result := instance.FullStr(algo, Buffer, Len, DigestBits);
end;
procedure PBKDF2_SHA3(algo: TSHA3Algo; const password,salt: RawByteString;
count: Integer; result: PByte; resultbytes: Integer);
var i: integer;
tmp: RawByteString;
mac: TSHA3;
first: TSHA3;
begin
if resultbytes<=0 then
resultbytes := SHA3_DEF_LEN[algo] shr 3;
SetLength(tmp,resultbytes);
first.Init(algo);
first.Update(password);
mac := first;
mac.Update(salt);
mac.Final(pointer(tmp),resultbytes shl 3,true);
MoveFast(pointer(tmp)^,result^,resultbytes);
for i := 2 to count do begin
mac := first;
mac.Update(pointer(tmp),resultbytes);
mac.Final(pointer(tmp),resultbytes shl 3,true);
XorMemory(pointer(result),pointer(tmp),resultbytes);
end;
FillcharFast(mac,sizeof(mac),0);
FillcharFast(first,sizeof(first),0);
FillZero(tmp);
end;
procedure PBKDF2_SHA3_Crypt(algo: TSHA3Algo; const password,salt: RawByteString;
count: Integer; var data: RawByteString);
var key: RawByteString;
len: integer;
begin
len := length(data);
SetLength(key,len);
PBKDF2_SHA3(algo,password,salt,count,pointer(key),len);
XorMemory(pointer(data),pointer(key),len);
FillZero(key);
end;
{ TSynHasher }
function TSynHasher.Init(aAlgo: THashAlgo): boolean;
begin
fAlgo := aAlgo;
result := true;
case aAlgo of
hfMD5: PMD5(@ctxt)^.Init;
hfSHA1: PSHA1(@ctxt)^.Init;
hfSHA256: PSHA256(@ctxt)^.Init;
hfSHA384: PSHA384(@ctxt)^.Init;
hfSHA512: PSHA512(@ctxt)^.Init;
hfSHA3_256: PSHA3(@ctxt)^.Init(SHA3_256);
hfSHA3_512: PSHA3(@ctxt)^.Init(SHA3_512);
else result := false;
end;
end;
procedure TSynHasher.Update(aBuffer: Pointer; aLen: integer);
begin
case fAlgo of
hfMD5: PMD5(@ctxt)^.Update(aBuffer^,aLen);
hfSHA1: PSHA1(@ctxt)^.Update(aBuffer,aLen);
hfSHA256: PSHA256(@ctxt)^.Update(aBuffer,aLen);
hfSHA384: PSHA384(@ctxt)^.Update(aBuffer,aLen);
hfSHA512: PSHA512(@ctxt)^.Update(aBuffer,aLen);
hfSHA3_256: PSHA3(@ctxt)^.Update(aBuffer,aLen);
hfSHA3_512: PSHA3(@ctxt)^.Update(aBuffer,aLen);
end;
end;
procedure TSynHasher.Update(const aBuffer: RawByteString);
begin
Update(pointer(aBuffer),length(aBuffer));
end;
function TSynHasher.Final: RawUTF8;
begin
case fAlgo of
hfMD5: result := MD5DigestToString(PMD5(@ctxt)^.Final);
hfSHA1: result := SHA1DigestToString(PSHA1(@ctxt)^.Final);
hfSHA256: result := SHA256DigestToString(PSHA256(@ctxt)^.Final);
hfSHA384: result := SHA384DigestToString(PSHA384(@ctxt)^.Final);
hfSHA512: result := SHA512DigestToString(PSHA512(@ctxt)^.Final);
hfSHA3_256: result := SHA256DigestToString(PSHA3(@ctxt)^.Final256);
hfSHA3_512: result := SHA512DigestToString(PSHA3(@ctxt)^.Final512);
end;
end;
function TSynHasher.Full(aAlgo: THashAlgo; aBuffer: Pointer; aLen: integer): RawUTF8;
begin
Init(aAlgo);
Update(aBuffer,aLen);
result := Final;
end;
function HashFull(aAlgo: THashAlgo; aBuffer: Pointer; aLen: integer): RawUTF8;
var hasher: TSynHasher;
begin
result := hasher.Full(aAlgo,aBuffer,aLen);
end;
function HashFile(const aFileName: TFileName; aAlgo: THashAlgo): RawUTF8;
var
hasher: TSynHasher;
temp: RawByteString;
F: THandle;
size: TQWordRec;
read: cardinal;
begin
result := '';
if (aFileName='') or not hasher.Init(aAlgo) then
exit;
F := FileOpenSequentialRead(aFileName);
if PtrInt(F)>=0 then
try
size.L := GetFileSize(F,@size.H);
SetLength(temp,1 shl 20);
while size.V>0 do begin
read := FileRead(F,pointer(temp)^,1 shl 20);
if read<=0 then
exit;
hasher.Update(pointer(temp),read);
dec(size.V,read);
end;
result := hasher.Final;
finally
FileClose(F);
end;
end;
procedure HashFile(const aFileName: TFileName; aAlgos: THashAlgos);
var data, hash: RawUTF8;
efn, fn: string;
a: THashAlgo;
begin
if aAlgos=[] then
exit;
efn := ExtractFileName(aFileName);
data := StringFromFile(aFileName);
if data<>'' then
for a := low(a) to high(a) do
if a in aAlgos then begin
FormatUTF8('% *%',[HashFull(a,pointer(data),length(data)),efn],hash);
FormatString('%.%',[efn,LowerCase(TrimLeftLowerCaseShort(ToText(a)))],fn);
FileFromString(hash,fn);
end;
end;
{ TSynSigner }
procedure TSynSigner.Init(aAlgo: TSignAlgo; aSecret: pointer; aSecretLen: integer);
const
SIGN_SIZE: array[TSignAlgo] of byte = (
20, 32, 48, 64, 28, 32, 48, 64, 32, 64);
SHA3_ALGO: array[saSha3224..saSha3S256] of TSHA3Algo = (
SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE_128, SHAKE_256);
begin
fAlgo := aAlgo;
fSignatureSize := SIGN_SIZE[fAlgo];
case fAlgo of
saSha1: PHMAC_SHA1(@ctxt)^.Init(aSecret,aSecretLen);
saSha256: PHMAC_SHA256(@ctxt)^.Init(aSecret,aSecretLen);
saSha384: PHMAC_SHA384(@ctxt)^.Init(aSecret,aSecretLen);
saSha512: PHMAC_SHA512(@ctxt)^.Init(aSecret,aSecretLen);
saSha3224..saSha3S256: begin
PSHA3(@ctxt)^.Init(SHA3_ALGO[fAlgo]);
PSHA3(@ctxt)^.Update(aSecret,aSecretLen); // HMAC pattern included in SHA-3
end;
end;
end;
procedure TSynSigner.Init(aAlgo: TSignAlgo; const aSecret: RawUTF8);
begin
Init(aAlgo,pointer(aSecret),length(aSecret));
end;
procedure TSynSigner.Init(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; aPBKDF2Secret: PHash512Rec);
var temp: THash512Rec;
begin
if aSecretPBKDF2Rounds>1 then begin
PBKDF2(aAlgo,aSecret,aSalt,aSecretPBKDF2Rounds,temp);
Init(aAlgo,@temp,fSignatureSize);
if aPBKDF2Secret<>nil then
aPBKDF2Secret^ := temp;
FillZero(temp.b);
end else
Init(aAlgo,aSecret);
end;
procedure TSynSigner.Update(const aBuffer: RawByteString);
begin
Update(pointer(aBuffer),length(aBuffer));
end;
procedure TSynSigner.Update(aBuffer: pointer; aLen: integer);
begin
case fAlgo of
saSha1: PHMAC_SHA1(@ctxt)^.Update(aBuffer,aLen);
saSha256: PHMAC_SHA256(@ctxt)^.Update(aBuffer,aLen);
saSha384: PHMAC_SHA384(@ctxt)^.Update(aBuffer,aLen);
saSha512: PHMAC_SHA512(@ctxt)^.Update(aBuffer,aLen);
saSha3224..saSha3S256: PSHA3(@ctxt)^.Update(aBuffer,aLen);
end;
end;
procedure TSynSigner.Final(out aSignature: THash512Rec; aNoInit: boolean);
begin
case fAlgo of
saSha1: PHMAC_SHA1(@ctxt)^.Done(aSignature.b160,aNoInit);
saSha256: PHMAC_SHA256(@ctxt)^.Done(aSignature.Lo,aNoInit);
saSha384: PHMAC_SHA384(@ctxt)^.Done(aSignature.b384,aNoInit);
saSha512: PHMAC_SHA512(@ctxt)^.Done(aSignature.b,aNoInit);
saSha3224..saSha3S256: PSHA3(@ctxt)^.Final(@aSignature,fSignatureSize shl 3,aNoInit);
end;
end;
function TSynSigner.Final: RawUTF8;
var sig: THash512Rec;
begin
Final(sig);
result := BinToHexLower(@sig,fSignatureSize);
end;
function TSynSigner.Full(aAlgo: TSignAlgo; const aSecret: RawUTF8;
aBuffer: Pointer; aLen: integer): RawUTF8;
begin
Init(aAlgo,aSecret);
Update(aBuffer,aLen);
result := Final;
end;
function TSynSigner.Full(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; aBuffer: Pointer; aLen: integer): RawUTF8;
begin
Init(aAlgo,aSecret,aSalt,aSecretPBKDF2Rounds);
Update(aBuffer,aLen);
result := Final;
end;
procedure TSynSigner.PBKDF2(aAlgo: TSignAlgo; const aSecret, aSalt: RawUTF8;
aSecretPBKDF2Rounds: integer; out aDerivatedKey: THash512Rec);
var iter: TSynSigner;
temp: THash512Rec;
i: integer;
begin
Init(aAlgo,aSecret);
iter := self;
iter.Update(aSalt);
if fAlgo<saSha3224 then
iter.Update(#0#0#0#1); // padding and XoF mode already part of SHA-3 process
iter.Final(aDerivatedKey,true);
if aSecretPBKDF2Rounds<2 then
exit;
temp := aDerivatedKey;
for i := 2 to aSecretPBKDF2Rounds do begin
iter := self;
iter.Update(@temp,fSignatureSize);
iter.Final(temp,true);
XorMemory(@aDerivatedKey,@temp,fSignatureSize);
end;
FillZero(temp.b);
FillCharFast(iter.ctxt,SizeOf(iter.ctxt),0);
FillCharFast(ctxt,SizeOf(ctxt),0);
end;
procedure TSynSigner.PBKDF2(const aParams: TSynSignerParams;
out aDerivatedKey: THash512Rec);
begin
PBKDF2(aParams.algo,aParams.secret,aParams.salt,aParams.rounds,aDerivatedKey);
end;
procedure TSynSigner.PBKDF2(aParamsJSON: PUTF8Char; aParamsJSONLen: integer;
out aDerivatedKey: THash512Rec; const aDefaultSalt: RawUTF8; aDefaultAlgo: TSignAlgo);
var tmp: TSynTempBuffer;
k: TSynSignerParams;
procedure SetDefault;
begin
k.algo := aDefaultAlgo;
k.secret := '';
k.salt := aDefaultSalt;
k.rounds := 1000;
end;
begin
SetDefault;
if (aParamsJSON=nil) or (aParamsJSONLen<=0) then
k.secret := aDefaultSalt else
if aParamsJSON[1]<>'{' then
FastSetString(k.secret,aParamsJSON,aParamsJSONLen) else begin
tmp.Init(aParamsJSON,aParamsJSONLen);
try
if (RecordLoadJSON(k,tmp.buf,TypeInfo(TSynSignerParams))=nil) or
(k.secret='') or (k.salt='') then begin
SetDefault;
FastSetString(k.secret,aParamsJSON,aParamsJSONLen);
end;
finally
FillCharFast(tmp.buf^,tmp.len,0);
tmp.Done;
end;
end;
PBKDF2(k.algo,k.secret,k.salt,k.rounds,aDerivatedKey);
FillZero(k.secret);
end;
procedure TSynSigner.PBKDF2(const aParamsJSON: RawUTF8; out aDerivatedKey: THash512Rec;
const aDefaultSalt: RawUTF8; aDefaultAlgo: TSignAlgo);
begin
PBKDF2(pointer(aParamsJSON),length(aParamsJSON),aDerivatedKey,aDefaultSalt,aDefaultAlgo);
end;
procedure TSynSigner.AssignTo(var aDerivatedKey: THash512Rec; out aAES: TAES; aEncrypt: boolean);
var ks: integer;
begin
case Algo of
saSha3S128: ks := 128; // truncate to Keccak sponge precision
saSha3S256: ks := 256;
else
case SignatureSize of
20: begin
ks := 128;
aDerivatedKey.i0 := aDerivatedKey.i0 xor aDerivatedKey.i4;
end;
28: ks := 192;
32: ks := 256;
48: begin
ks := 256;
aDerivatedKey.d0 := aDerivatedKey.d0 xor aDerivatedKey.d4;
aDerivatedKey.d1 := aDerivatedKey.d1 xor aDerivatedKey.d5;
end;
64: begin
ks := 256;
aDerivatedKey.d0 := aDerivatedKey.d0 xor aDerivatedKey.d4;
aDerivatedKey.d1 := aDerivatedKey.d1 xor aDerivatedKey.d5;
aDerivatedKey.d2 := aDerivatedKey.d0 xor aDerivatedKey.d6;
aDerivatedKey.d3 := aDerivatedKey.d1 xor aDerivatedKey.d7;
end;
else exit;
end;
end;
aAES.DoInit(aDerivatedKey,ks,aEncrypt);
FillZero(aDerivatedKey.b);
end;
procedure TSynSigner.Done;
begin
FillCharFast(self, SizeOf(self), 0);
end;
procedure AES(const Key; KeySize: cardinal; buffer: pointer; Len: Integer; Encrypt: boolean);
begin
AES(Key,KeySize,buffer,buffer,Len,Encrypt);
end;
procedure AES(const Key; KeySize: cardinal; bIn, bOut: pointer; Len: Integer; Encrypt: boolean);
var n: integer;
pIn, pOut: PAESBlock;
Crypt: TAES;
begin
if (bIn=nil) or (bOut=nil) then exit;
// 1. Init
n := Len shr AESBlockShift;
if n<0 then exit else
if n>0 then
if (KeySize>4) and not Crypt.DoInit(Key,KeySize,Encrypt) then
KeySize := 4; // if error in KeySize, use default fast XorOffset()
if KeySize=0 then begin // KeySize=0 -> no encryption -> direct copy
MoveFast(bIn^, bOut^, Len);
exit;
end;
if n<1 then begin // too small for AES -> XorOffset() remaining 0..15 bytes
MoveFast(bIn^, bOut^, Len);
XorOffset(bOut,0,Len);
exit;
end;
// 2. All full blocks, with AES
{$ifdef USETHREADSFORBIGAESBLOCKS}
pIn := bIn;
pOut := bOut;
Crypt.DoBlocksThread(pIn,pOut,n,Encrypt);
{$else}
Crypt.DoBlocks(bIn,bOut,pIn,pOut,n,Encrypt);
{$endif}
// 3. Last block, just XORed from Key
// assert(KeySize div 8>=AESBlockSize);
n := cardinal(Len) and AESBlockMod;
MoveFast(pIn^,pOut^,n); // pIn=pOut is tested in MoveFast()
XorOffset(pointer(pOut),Len-n,n);
Crypt.Done;
end;
const TmpSize = 65536;
// Tmp buffer for AESFull -> Xor Crypt is TmpSize-dependent / use XorBlock()
TmpSizeBlock = TmpSize shr AESBlockShift;
type
TTmp = array[0..TmpSizeBlock-1] of TAESBlock;
function AES(const Key; KeySize: cardinal; const s: RawByteString; Encrypt: boolean): RawByteString;
begin
SetString(result,nil,length(s));
if s<>'' then
AES(Key,KeySize,pointer(s),pointer(result),length(s),Encrypt);
end;
function AES(const Key; KeySize: cardinal; buffer: pointer; Len: cardinal; Stream: TStream; Encrypt: boolean): boolean; overload;
var buf: pointer;
last, b, n, i: cardinal;
Crypt: TAES;
begin
result := false;
if buffer=nil then exit;
if (KeySize>4) and not Crypt.DoInit(Key,KeySize,Encrypt) then
KeySize := 4; // if error in KeySize, use default fast XorOffset()
if KeySize=0 then begin // no Crypt -> direct write to dest Stream
Stream.WriteBuffer(buffer^,Len);
result := true;
exit;
end;
getmem(buf,TmpSize);
try
Last := Len and AESBlockMod;
n := Len-Last;
i := 0;
while n>0 do begin // crypt/uncrypt all AESBlocks
if n>TmpSize then
b := TmpSize else
b := n;
assert(b and AESBlockMod=0);
if KeySize=4 then begin
MoveFast(buffer^,buf^,b);
XorOffset(pointer(buf),i,b);
inc(i,b);
end else
Crypt.DoBlocks(buffer,buf,b shr AESBlockShift,Encrypt);
Stream.WriteBuffer(buf^,b);
inc(PByte(buffer),b);
dec(n,b);
end;
assert((KeySize>4)or(i=Len-Last));
if last>0 then begin // crypt/uncrypt (Xor) last 0..15 bytes
MoveFast(buffer^,buf^,Last);
XorOffset(pointer(buf),Len-Last,Last);
Stream.WriteBuffer(buf^,Last);
end;
result := true;
finally
freemem(buf);
end;
end;
function KeyFrom(const Key; KeySize: cardinal): cardinal;
begin
case KeySize div 8 of
0: result := 0;
1: result := PByte(@Key)^;
2,3: result := PWord(@Key)^;
else result := PInteger(@Key)^;
end;
end;
function TAESFullHeader.Calc(const Key; KeySize: cardinal): cardinal;
begin
result := Adler32Asm(KeySize,@Key,KeySize shr 3) xor Te0[OriginalLen and $FF]
xor Te1[SourceLen and $FF] xor Td0[SomeSalt and $7FF];
end;
function TAESFull.EncodeDecode(const Key; KeySize, inLen: cardinal; Encrypt: boolean;
inStream, outStream: TStream; bIn, bOut: pointer; OriginalLen: Cardinal=0): integer;
var Tmp: ^TTmp;
pIn, pOut: PAESBlock;
Crypt: TAES;
nBlock,
XorCod: cardinal;
procedure Read(Tmp: pointer; ByteCount: cardinal);
begin
if pIn=nil then
InStream.Read(Tmp^,ByteCount) else begin
MoveFast(pIn^,Tmp^,ByteCount);
inc(PByte(pIn),ByteCount);
end;
end;
procedure Write(Tmp: pointer; ByteCount: cardinal);
begin
if pOut=nil then
OutStream.WriteBuffer(Tmp^,ByteCount) else begin
MoveFast(Tmp^,pOut^,ByteCount);
inc(PByte(pOut),ByteCount);
end;
end;
procedure SetOutLen(Len: cardinal);
var P: cardinal;
begin
result := Len; // global EncodeDecode() result
if OutStream<>nil then begin
if OutStream.InheritsFrom(TMemoryStream) then
with TMemoryStream(OutStream) do begin
P := Seek(0,soCurrent);
Size := P+Len; // auto-reserve space (no Realloc:)
Seek(P+Len,soBeginning);
bOut := PAnsiChar(Memory)+P;
pOut := bOut;
OutStream := nil; // OutStream is slower and use no thread
end;
end else
if bOut=nil then begin
outStreamCreated := THeapMemoryStream.Create; // faster than TMemoryStream
outStreamCreated.Size := Len; // auto-reserve space (no Realloc:)
bOut := outStreamCreated.Memory;
pOut := bOut; // OutStream is slower and use no thread
end;
if KeySize=0 then exit; // no Tmp to be allocated on direct copy
{$ifdef USEPADLOCK} // PADLOCK prefers 16-bytes alignment
if (KeySize=32) or (InStream<>nil) or (OutStream<>nil) or
(PtrUInt(bIn) and $f<>0) or (PtrUInt(bOut) and $f<>0) then begin
New(Tmp);
// assert(PtrUInt(Tmp) and $F=0);
end;
{$else}
if (KeySize=32) or (InStream<>nil) or (OutStream<>nil) then
New(Tmp);
{$endif}
end;
procedure DoBlock(BlockCount: integer);
begin
if BlockCount=0 then
exit;
Read(Tmp,BlockCount shl AESBlockShift);
Crypt.DoBlocks(PAESBLock(Tmp),PAESBLock(Tmp),BlockCount,Encrypt);
Write(Tmp,BlockCount shl AESBlockShift);
end;
var n, LastLen: cardinal;
i: integer;
Last: TAESBlock;
begin
result := 0; // makes FixInsight happy
Tmp := nil;
outStreamCreated := nil;
Head.SourceLen := InLen;
nBlock := Head.SourceLen shr AESBlockShift;
if Encrypt and (OriginalLen<>0) then
Head.OriginalLen := OriginalLen else
Head.OriginalLen := InLen;
KeySize := KeySize div 8;
if not (KeySize in [0,4,16,24,32]) then
KeySize := 0 else // valid KeySize: 0=nothing, 32=xor, 128,192,256=AES
KeySize := KeySize*8;
XorCod := inLen;
if (inStream<>nil) and inStream.InheritsFrom(TMemoryStream) then begin
bIn := TMemoryStream(inStream).Memory;
inStream := nil;
end;
pIn := bIn;
pOut := bOut;
if (KeySize>=128) and not Crypt.DoInit(Key,KeySize,Encrypt) then
KeySize := 32;
if KeySize=32 then
XorCod := KeyFrom(Key,KeySize) xor XorCod else
if (KeySize=0) and (InStream=nil) then begin
SetOutLen(inLen);
Write(bIn,inLen); // no encryption -> direct write
exit;
end;
try
// 0. KeySize = 0:direct copy 32:XorBlock
if KeySize<128 then begin
SetOutLen(inLen);
assert(Tmp<>nil);
LastLen := inLen;
while LastLen<>0 do begin
if LastLen>TmpSize then
n := TmpSize else
n := LastLen;
Read(Tmp,n);
if KeySize>0 then
XorBlock(pointer(Tmp),n,XorCod);
Write(Tmp,n);
dec(LastLen,n);
end;
end else begin // now we do AES encryption:
// 1. Header process
if Encrypt then begin
// encrypt data
if (pIn=pOut) and (pIn<>nil) then begin
assert(false); // Head in pOut^ will overflow data in pIn^
result := 0;
exit;
end;
LastLen := inLen and AESBlockMod;
if LastLen=0 then
SetOutLen(inLen+sizeof(TAESBlock)) else
SetOutLen((nBlock+2)shl AESBlockShift);
Head.SomeSalt := random(MaxInt);
Head.HeaderCheck := Head.Calc(Key,KeySize);
Crypt.Encrypt(TAESBlock(Head));
Write(@Head,sizeof(Head));
end else begin
// uncrypt data
dec(nBlock); // Header is already done
Read(@Head,sizeof(Head));
Crypt.Decrypt(TAESBlock(Head));
with Head do begin
if HeaderCheck<>Head.Calc(Key,KeySize) then begin
result := -1;
exit; // wrong key
end;
SetOutLen(SourceLen);
LastLen := SourceLen and AESBlockMod;
end;
if LastLen<>0 then
dec(nBlock); // the very last block is for the very last bytes
end;
// 2. All full blocks, with AES
if Tmp=nil then begin
{$ifdef USETHREADSFORBIGAESBLOCKS} // Tmp is 64KB -> helpless Threads
Crypt.DoBlocksThread(pIn,pOut,nBlock,Encrypt);
{$else}
Crypt.DoBlocks(pIn,pOut,pIn,pOut,nBlock,Encrypt);
{$endif}
end else begin
for i := 1 to nBlock div TmpSizeBlock do
DoBlock(TmpSizeBlock);
DoBlock(nBlock mod TmpSizeBlock);
end;
// 3. Last block
if LastLen<>0 then
if Encrypt then begin
FillcharFast(Last,sizeof(TAESBlock),0);
Read(@Last,LastLen);
Crypt.Encrypt(Last);
Write(@Last,sizeof(TAESBlock));
end else begin
Read(@Last,sizeof(TAESBlock));
Crypt.Decrypt(Last);
Write(@Last,LastLen);
end;
Crypt.Done;
end;
finally
if Tmp<>nil then
Freemem(Tmp);
end;
end;
function AESFullKeyOK(const Key; KeySize: cardinal; buff: pointer): boolean;
// true if begining of buff contains true AESFull encrypted data with this Key
var Crypt: TAES;
Head: TAESFullHeader;
begin
if KeySize<128 then
result := true else
if not Crypt.DecryptInit(Key,KeySize) then
result := false else begin
Crypt.Decrypt(PAESBlock(buff)^,TAESBlock(Head));
result := Head.Calc(Key,KeySize)=Head.HeaderCheck;
Crypt.Done;
end;
end;
function AESFull(const Key; KeySize: cardinal; bIn, bOut: pointer; Len: integer;
Encrypt: boolean; OriginalLen: Cardinal=0): integer; overload;
// bOut must be at least bIn+32/Encrypt bIn-16/Decrypt -> returns outLength, <0 if error
var A: TAESFull;
begin
result := A.EncodeDecode(Key,KeySize,Len,Encrypt,nil,nil,bIn,bOut,OriginalLen);
end;
function AESFull(const Key; KeySize: cardinal; bIn: pointer; Len: Integer;
outStream: TStream; Encrypt: boolean; OriginalLen: Cardinal=0): boolean; // true is Key OK
// outStream will be larger/smaller than Len: this is a full AES version
// if not KeySize in [128,192,256] -> use very fast and Simple Xor Cypher
var A: TAESFull;
begin
result := A.EncodeDecode(Key,KeySize,
Len,Encrypt,nil,outStream,bIn,nil,OriginalLen)>=0;
end;
procedure AESSHA256(bIn, bOut: pointer; Len: integer; const Password: RawByteString; Encrypt: boolean);
var Digest: TSHA256Digest;
begin
SHA256Weak(Password,Digest);
AES(Digest,sizeof(Digest)*8,bIn,bOut,Len,Encrypt);
FillZero(Digest);
end;
function AESSHA256(const s, Password: RawByteString; Encrypt: boolean): RawByteString;
begin
SetString(result,nil,length(s));
AESSHA256(pointer(s),pointer(result),length(s),Password,Encrypt);
end;
procedure AESSHA256(Buffer: pointer; Len: integer; const Password: RawByteString; Encrypt: boolean);
// Encrypt/Decrypt Buffer with AES and SHA-256 password
begin
AESSHA256(Buffer,Buffer,Len,Password,Encrypt);
end;
procedure AESSHA256Full(bIn: pointer; Len: Integer; outStream: TStream; const Password: RawByteString; Encrypt: boolean);
// outStream will be larger/smaller than Len: this is a full AES version
var Digest: TSHA256Digest;
begin
SHA256Weak(Password,Digest);
AESFull(Digest,sizeof(Digest)*8,bIn,Len,outStream,Encrypt);
end;
function Adler32Pas(Adler: cardinal; p: pointer; Count: Integer): cardinal;
// simple Adler32 implementation (twice slower than Asm, but shorter code size)
var s1, s2: cardinal;
i, n: integer;
begin
s1 := LongRec(Adler).Lo;
s2 := LongRec(Adler).Hi;
while Count>0 do begin
if Count<5552 then
n := Count else
n := 5552;
for i := 1 to n do begin
inc(s1,PByte(p)^);
inc(PByte(p));
inc(s2,s1);
end;
s1 := s1 mod 65521;
s2 := s2 mod 65521;
dec(Count,n);
end;
result := (s1 and $ffff)+(s2 and $ffff) shl 16;
end;
function Adler32Asm(Adler: cardinal; p: pointer; Count: Integer): cardinal;
{$ifdef PUREPASCAL}
begin
result := Adler32Pas(Adler,p,Count);
end;
{$else} {$ifdef FPC} nostackframe; assembler; {$endif}
asm
push ebx
push esi
push edi
mov edi, eax
shr edi, 16
movzx ebx, ax
push ebp
mov esi, edx
test esi, esi
mov ebp, ecx
jne @31
mov eax, 1
jmp @32
@31: test ebp, ebp
jbe @34
@33: cmp ebp, 5552
jae @35
mov eax, ebp
jmp @36
@35: mov eax, 5552
@36: sub ebp, eax
cmp eax, 16
jl @38
xor edx, edx
xor ecx, ecx
@39: sub eax, 16
mov dl, [esi]
mov cl, [esi + 1]
add ebx, edx
add edi, ebx
add ebx, ecx
mov dl, [esi + 2]
add edi, ebx
add ebx, edx
mov cl, [esi + 3]
add edi, ebx
add ebx, ecx
mov dl, [esi + 4]
add edi, ebx
add ebx, edx
mov cl, [esi + 5]
add edi, ebx
add ebx, ecx
mov dl, [esi + 6]
add edi, ebx
add ebx, edx
mov cl, [esi + 7]
add edi, ebx
add ebx, ecx
mov dl, [esi + 8]
add edi, ebx
add ebx, edx
mov cl, [esi + 9]
add edi, ebx
add ebx, ecx
mov dl, [esi + 10]
add edi, ebx
add ebx, edx
mov cl, [esi + 11]
add edi, ebx
add ebx, ecx
mov dl, [esi + 12]
add edi, ebx
add ebx, edx
mov cl, [esi + 13]
add edi, ebx
add ebx, ecx
mov dl, [esi + 14]
add edi, ebx
add ebx, edx
mov cl, [esi + 15]
add edi, ebx
add ebx, ecx
add esi, 16
lea edi, [edi + ebx]
cmp eax, 16
jge @39
@38: test eax, eax
je @42
@43: movzx edx, byte ptr[esi]
add ebx, edx
dec eax
lea esi, [esi + 1]
lea edi, [edi + ebx]
jg @43
@42: mov ecx, 65521
mov eax, ebx
xor edx, edx
div ecx
mov ebx, edx
mov ecx, 65521
mov eax, edi
xor edx, edx
div ecx
test ebp, ebp
mov edi, edx
ja @33
@34: mov eax, edi
shl eax, 16
or eax, ebx
@32: pop ebp
pop edi
pop esi
pop ebx
end;
{$endif}
function Adler32SelfTest: boolean;
begin
result :=
{$ifndef PUREPASCAL}
(Adler32Asm(1,@Te0,sizeof(Te0))=$BCBEFE10) and
(Adler32Asm(7,@Te1,sizeof(Te1)-3)=$DA91FDBE) and
{$endif}
(Adler32Pas(1,@Te0,sizeof(Te0))=$BCBEFE10) and
(Adler32Pas(7,@Te1,sizeof(Te1)-3)=$DA91FDBE);
end;
{ TAESWriteStream }
constructor TAESWriteStream.Create(outStream: TStream; const Key; KeySize: cardinal);
begin
inherited Create;
if KeySize=0 then
NoCrypt := true else
AES.EncryptInit(Key,KeySize);
Dest := outStream;
end;
destructor TAESWriteStream.Destroy;
begin
Finish;
AES.Done;
inherited;
end;
procedure TAESWriteStream.Finish;
begin
if BufCount=0 then exit;
assert((BufCount<sizeof(TAESBlock)) and AES.Initialized and not NoCrypt);
XorOffset(@Buf,DestSize,BufCount);
Dest.WriteBuffer(Buf,BufCount);
BufCount := 0;
end;
function TAESWriteStream.Read(var Buffer; Count: Integer): Longint;
begin
raise ESynCrypto.CreateUTF8('Unexpected %.Read',[self]);
end;
function TAESWriteStream.Seek(Offset: Integer; Origin: Word): Longint;
begin
raise ESynCrypto.CreateUTF8('Unexpected %.Seek',[self]);
end;
function TAESWriteStream.Write(const Buffer; Count: Integer): Longint;
// most of the time, a 64KB-buffered compressor have BufCount=0
// will crypt 'const Buffer' memory in place -> use AFTER T*Compressor
var B: TByteArray absolute Buffer;
Len: integer;
begin
result := Count;
Adler := Adler32Asm(Adler,@Buffer,Count);
if not NoCrypt then // KeySize=0 -> save as-is
if not AES.Initialized then // if error in KeySize -> default fast XorOffset()
XorOffset(@B,DestSize,Count) else begin
if BufCount>0 then begin
Len := sizeof(TAESBlock)-BufCount;
if Len>Count then
Len := Count;
MoveFast(Buffer,Buf[BufCount],Len);
inc(BufCount,Len);
if BufCount<sizeof(TAESBlock) then
exit;
AES.Encrypt(Buf);
Dest.WriteBuffer(Buf,sizeof(TAESBlock));
inc(DestSize,sizeof(TAESBlock));
Dec(Count,Len);
AES.DoBlocks(@B[Len],@B[Len],cardinal(Count) shr AESBlockShift,true);
end else
AES.DoBlocks(@B,@B,cardinal(Count) shr AESBlockShift,true);
BufCount := cardinal(Count) and AESBlockMod;
if BufCount<>0 then begin
dec(Count,BufCount);
MoveFast(B[Count],Buf[0],BufCount);
end;
end;
Dest.WriteBuffer(Buffer,Count);
inc(DestSize,Count);
end;
procedure XorBlock(p: PIntegerArray; Count, Cod: integer);
// very fast Xor() according to Cod - not Compression or Stream compatible
var i: integer;
begin
for i := 1 to Count shr 4 do begin // proceed through 16 bytes blocs
Cod := (Cod shl 11) xor integer(Td0[cod shr 21]); // shr 21 -> 8*[byte] of cardinal
p^[0] := p^[0] xor Cod;
p^[1] := p^[1] xor Cod;
p^[2] := p^[2] xor Cod;
p^[3] := p^[3] xor Cod;
inc(PByte(p),16);
end;
Cod := (Cod shl 11) xor integer(Td0[cod shr 21]);
for i := 1 to (Count and AESBlockMod)shr 2 do begin // last 4 bytes blocs
p^[0] := p^[0] xor Cod;
inc(PByte(p),4);
end;
for i := 1 to Count and 3 do begin
PByte(p)^ := PByte(p)^ xor byte(Cod);
inc(PByte(p));
end;
end;
procedure XorOffset(P: PByteArray; Index,Count: integer);
// XorOffset: fast and simple Cypher using Index (=Position in Dest Stream):
// Compression not OK -> apply after compress (e.g. TBZCompressor.withXor=true)
var Len: integer;
begin
if Count>0 then
repeat
Index := Index and $1FFF;
Len := $2000-Index;
if Len>Count then
Len := Count;
XorMemory(P,@Xor32Byte[Index],Len);
inc(P,Len);
inc(Index,Len);
Dec(Count,Len);
until Count=0;
end;
procedure XorConst(P: PIntegerArray; Count: integer);
// XorConst: fast Cypher changing by Count value
// (compression OK):
var i: integer;
Code: integer;
begin // 1 to 3 bytes may stay unencrypted: not relevant
Code := integer(Td0[Count and $3FF]);
for i := 1 to (Count shr 4) do begin
P^[0] := P^[0] xor Code;
P^[1] := P^[1] xor Code;
P^[2] := P^[2] xor Code;
P^[3] := P^[3] xor Code;
inc(PByte(P),16);
end;
for i := 0 to ((Count and AESBlockMod)shr 2)-1 do // last 4 bytes blocs
P^[i] := P^[i] xor Code;
end;
{ TMD5 }
procedure MD5Transform(var buf: TMD5Buf; const in_: TMD5In);
// see https://synopse.info/forum/viewtopic.php?id=4369 for asm numbers
{$ifdef CPUX64}
{
MD5_Transform-x64
MD5 transform routine optimized for x64 processors
Copyright 2018 Ritlabs, SRL
The 64-bit version is written by Maxim Masiutin <max@ritlabs.com>
The main advantage of this 64-bit version is that it loads 64 bytes of hashed
message into 8 64-bit registers (RBP, R8, R9, R10, R11, R12, R13, R14) at the
beginning, to avoid excessive memory load operations througout the routine.
MD5_Transform-x64 is released under a dual license, and you may choose to use
it under either the Mozilla Public License 2.0 (MPL 2.1, available from
https://www.mozilla.org/en-US/MPL/2.0/) or the GNU Lesser General Public
License Version 3, dated 29 June 2007 (LGPL 3, available from
https://www.gnu.org/licenses/lgpl.html).
MD5_Transform-x64 is based on Peter Sawatzki's code.
Taken from https://github.com/maximmasiutin/MD5_Transform-x64
}
{$ifdef FPC}nostackframe; assembler; asm{$else}
asm // W=rcx Buf=rdx
.noframe
{$endif}
{$ifndef win64}
mov rdx, rsi
mov rcx, rdi
{$endif win64}
push rbx
push rsi
push rdi
push rbp
push r12
push r13
push r14
mov r14, rdx
mov rsi, rcx
push rsi
mov eax, dword ptr [rsi]
mov ebx, dword ptr [rsi+4H]
mov ecx, dword ptr [rsi+8H]
mov edx, dword ptr [rsi+0CH]
mov rbp, qword ptr [r14]
add eax, -680876936
add eax, ebp
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
ror rbp, 32
add edx, -389564586
add edx, ebp
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
mov r8, qword ptr [r14+8H]
add ecx, 606105819
add ecx, r8d
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
ror r8, 32
add ebx, -1044525330
add ebx, r8d
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
mov r9, qword ptr [r14+10H]
add eax, -176418897
add eax, r9d
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
ror r9, 32
add edx, 1200080426
add edx, r9d
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
mov r10, qword ptr [r14+18H]
add ecx, -1473231341
add ecx, r10d
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
ror r10, 32
add ebx, -45705983
add ebx, r10d
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
mov r11, qword ptr [r14+20H]
add eax, 1770035416
add eax, r11d
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
ror r11, 32
add edx, -1958414417
add edx, r11d
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
mov r12, qword ptr [r14+28H]
add ecx, -42063
add ecx, r12d
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
ror r12, 32
add ebx, -1990404162
add ebx, r12d
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
mov r13, qword ptr [r14+30H]
add eax, 1804603682
add eax, r13d
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
ror r13, 32
add edx, -40341101
add edx, r13d
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
mov r14, qword ptr [r14+38H]
add ecx, -1502002290
add ecx, r14d
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
ror r14, 32
add ebx, 1236535329
add ebx, r14d
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
add eax, -165796510
add eax, ebp
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
ror r10, 32
add edx, -1069501632
add edx, r10d
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, 643717713
add ecx, r12d
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
ror rbp, 32
add ebx, -373897302
add ebx, ebp
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, -701558691
add eax, r9d
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
ror r12, 32
add edx, 38016083
add edx, r12d
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, -660478335
add ecx, r14d
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
ror r9, 32
add ebx, -405537848
add ebx, r9d
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, 568446438
add eax, r11d
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
ror r14, 32
add edx, -1019803690
add edx, r14d
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, -187363961
add ecx, r8d
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
ror r11, 32
add ebx, 1163531501
add ebx, r11d
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, -1444681467
add eax, r13d
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
ror r8, 32
add edx, -51403784
add edx, r8d
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
ror r10, 32
add ecx, 1735328473
add ecx, r10d
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
ror r13, 32
add ebx, -1926607734
add ebx, r13d
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
ror r9, 32
add eax, -378558
add eax, r9d
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
add edx, -2022574463
add edx, r11d
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
ror r12, 32
add ecx, 1839030562
add ecx, r12d
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
add ebx, -35309556
add ebx, r14d
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
ror rbp, 32
add eax, -1530992060
add eax, ebp
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
ror r9, 32
add edx, 1272893353
add edx, r9d
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
add ecx, -155497632
add ecx, r10d
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
ror r12, 32
add ebx, -1094730640
add ebx, r12d
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
ror r13, 32
add eax, 681279174
add eax, r13d
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
ror rbp, 32
add edx, -358537222
add edx, ebp
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
ror r8, 32
add ecx, -722521979
add ecx, r8d
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
ror r10, 32
add ebx, 76029189
add ebx, r10d
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
ror r11, 32
add eax, -640364487
add eax, r11d
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
ror r13, 32
add edx, -421815835
add edx, r13d
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
ror r14, 32
add ecx, 530742520
add ecx, r14d
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
ror r8, 32
add ebx, -995338651
add ebx, r8d
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
add eax, -198630844
add eax, ebp
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
ror r10, 32
add edx, 1126891415
add edx, r10d
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
ror r14, 32
add ecx, -1416354905
add ecx, r14d
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
ror r9, 32
add ebx, -57434055
add ebx, r9d
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
add eax, 1700485571
add eax, r13d
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
ror r8, 32
add edx, -1894986606
add edx, r8d
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
add ecx, -1051523
add ecx, r12d
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
ror rbp, 32
add ebx, -2054922799
add ebx, ebp
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
ror r11, 32
add eax, 1873313359
add eax, r11d
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
ror r14, 32
add edx, -30611744
add edx, r14d
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
ror r10, 32
add ecx, -1560198380
add ecx, r10d
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
ror r13, 32
add ebx, 1309151649
add ebx, r13d
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
ror r9, 32
add eax, -145523070
add eax, r9d
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
ror r12, 32
add edx, -1120210379
add edx, r12d
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
ror r8, 32
add ecx, 718787259
add ecx, r8d
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
ror r11, 32
add ebx, -343485551
add ebx, r11d
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
pop rsi
add dword ptr [rsi], eax
add dword ptr [rsi+4H], ebx
add dword ptr [rsi+8H], ecx
add dword ptr [rsi+0CH], edx
pop r14
pop r13
pop r12
pop rbp
pop rdi
pop rsi
pop rbx
end;
{$else}
{$ifdef PUREPASCAL}
var a,b,c,d: cardinal; // unrolled -> compiler will only use cpu registers :)
// the code below is very fast, and can be compared proudly against C or ASM
begin
a := buf[0];
b := buf[1];
c := buf[2];
d := buf[3];
{$ifdef FPC} // uses faster built-in right rotate intrinsic
inc(a,in_[0]+$d76aa478+(d xor(b and(c xor d)))); a := RolDWord(a,7)+b;
inc(d,in_[1]+$e8c7b756+(c xor(a and(b xor c)))); d := RolDWord(d,12)+a;
inc(c,in_[2]+$242070db+(b xor(d and(a xor b)))); c := RolDWord(c,17)+d;
inc(b,in_[3]+$c1bdceee+(a xor(c and(d xor a)))); b := RolDWord(b,22)+c;
inc(a,in_[4]+$f57c0faf+(d xor(b and(c xor d)))); a := RolDWord(a,7)+b;
inc(d,in_[5]+$4787c62a+(c xor(a and(b xor c)))); d := RolDWord(d,12)+a;
inc(c,in_[6]+$a8304613+(b xor(d and(a xor b)))); c := RolDWord(c,17)+d;
inc(b,in_[7]+$fd469501+(a xor(c and(d xor a)))); b := RolDWord(b,22)+c;
inc(a,in_[8]+$698098d8+(d xor(b and(c xor d)))); a := RolDWord(a,7)+b;
inc(d,in_[9]+$8b44f7af+(c xor(a and(b xor c)))); d := RolDWord(d,12)+a;
inc(c,in_[10]+$ffff5bb1+(b xor(d and(a xor b)))); c := RolDWord(c,17)+d;
inc(b,in_[11]+$895cd7be+(a xor(c and(d xor a)))); b := RolDWord(b,22)+c;
inc(a,in_[12]+$6b901122+(d xor(b and(c xor d)))); a := RolDWord(a,7)+b;
inc(d,in_[13]+$fd987193+(c xor(a and(b xor c)))); d := RolDWord(d,12)+a;
inc(c,in_[14]+$a679438e+(b xor(d and(a xor b)))); c := RolDWord(c,17)+d;
inc(b,in_[15]+$49b40821+(a xor(c and(d xor a)))); b := RolDWord(b,22)+c;
inc(a,in_[1]+$f61e2562+(c xor(d and(b xor c)))); a := RolDWord(a,5)+b;
inc(d,in_[6]+$c040b340+(b xor(c and(a xor b)))); d := RolDWord(d,9)+a;
inc(c,in_[11]+$265e5a51+(a xor(b and(d xor a)))); c := RolDWord(c,14)+d;
inc(b,in_[0]+$e9b6c7aa+(d xor(a and(c xor d)))); b := RolDWord(b,20)+c;
inc(a,in_[5]+$d62f105d+(c xor(d and(b xor c)))); a := RolDWord(a,5)+b;
inc(d,in_[10]+$02441453+(b xor(c and(a xor b)))); d := RolDWord(d,9)+a;
inc(c,in_[15]+$d8a1e681+(a xor(b and(d xor a)))); c := RolDWord(c,14)+d;
inc(b,in_[4]+$e7d3fbc8+(d xor(a and(c xor d)))); b := RolDWord(b,20)+c;
inc(a,in_[9]+$21e1cde6+(c xor(d and(b xor c)))); a := RolDWord(a,5)+b;
inc(d,in_[14]+$c33707d6+(b xor(c and(a xor b)))); d := RolDWord(d,9)+a;
inc(c,in_[3]+$f4d50d87+(a xor(b and(d xor a)))); c := RolDWord(c,14)+d;
inc(b,in_[8]+$455a14ed+(d xor(a and(c xor d)))); b := RolDWord(b,20)+c;
inc(a,in_[13]+$a9e3e905+(c xor(d and(b xor c)))); a := RolDWord(a,5)+b;
inc(d,in_[2]+$fcefa3f8+(b xor(c and(a xor b)))); d := RolDWord(d,9)+a;
inc(c,in_[7]+$676f02d9+(a xor(b and(d xor a)))); c := RolDWord(c,14)+d;
inc(b,in_[12]+$8d2a4c8a+(d xor(a and(c xor d)))); b := RolDWord(b,20)+c;
inc(a,in_[5]+$fffa3942+(b xor c xor d)); a := RolDWord(a,4)+b;
inc(d,in_[8]+$8771f681+(a xor b xor c)); d := RolDWord(d,11)+a;
inc(c,in_[11]+$6d9d6122+(d xor a xor b)); c := RolDWord(c,16)+d;
inc(b,in_[14]+$fde5380c+(c xor d xor a)); b := RolDWord(b,23)+c;
inc(a,in_[1]+$a4beea44+(b xor c xor d)); a := RolDWord(a,4)+b;
inc(d,in_[4]+$4bdecfa9+(a xor b xor c)); d := RolDWord(d,11)+a;
inc(c,in_[7]+$f6bb4b60+(d xor a xor b)); c := RolDWord(c,16)+d;
inc(b,in_[10]+$bebfbc70+(c xor d xor a)); b := RolDWord(b,23)+c;
inc(a,in_[13]+$289b7ec6+(b xor c xor d)); a := RolDWord(a,4)+b;
inc(d,in_[0]+$eaa127fa+(a xor b xor c)); d := RolDWord(d,11)+a;
inc(c,in_[3]+$d4ef3085+(d xor a xor b)); c := RolDWord(c,16)+d;
inc(b,in_[6]+$04881d05+(c xor d xor a)); b := RolDWord(b,23)+c;
inc(a,in_[9]+$d9d4d039+(b xor c xor d)); a := RolDWord(a,4)+b;
inc(d,in_[12]+$e6db99e5+(a xor b xor c)); d := RolDWord(d,11)+a;
inc(c,in_[15]+$1fa27cf8+(d xor a xor b)); c := RolDWord(c,16)+d;
inc(b,in_[2]+$c4ac5665+(c xor d xor a)); b := RolDWord(b,23)+c;
inc(a,in_[0]+$f4292244+(c xor(b or(not d)))); a := RolDWord(a,6)+b;
inc(d,in_[7]+$432aff97+(b xor(a or(not c)))); d := RolDWord(d,10)+a;
inc(c,in_[14]+$ab9423a7+(a xor(d or(not b)))); c := RolDWord(c,15)+d;
inc(b,in_[5]+$fc93a039+(d xor(c or(not a)))); b := RolDWord(b,21)+c;
inc(a,in_[12]+$655b59c3+(c xor(b or(not d)))); a := RolDWord(a,6)+b;
inc(d,in_[3]+$8f0ccc92+(b xor(a or(not c)))); d := RolDWord(d,10)+a;
inc(c,in_[10]+$ffeff47d+(a xor(d or(not b)))); c := RolDWord(c,15)+d;
inc(b,in_[1]+$85845dd1+(d xor(c or(not a)))); b := RolDWord(b,21)+c;
inc(a,in_[8]+$6fa87e4f+(c xor(b or(not d)))); a := RolDWord(a,6)+b;
inc(d,in_[15]+$fe2ce6e0+(b xor(a or(not c)))); d := RolDWord(d,10)+a;
inc(c,in_[6]+$a3014314+(a xor(d or(not b)))); c := RolDWord(c,15)+d;
inc(b,in_[13]+$4e0811a1+(d xor(c or(not a)))); b := RolDWord(b,21)+c;
inc(a,in_[4]+$f7537e82+(c xor(b or(not d)))); a := RolDWord(a,6)+b;
inc(d,in_[11]+$bd3af235+(b xor(a or(not c)))); d := RolDWord(d,10)+a;
inc(c,in_[2]+$2ad7d2bb+(a xor(d or(not b)))); c := RolDWord(c,15)+d;
inc(b,in_[9]+$eb86d391+(d xor(c or(not a)))); b := RolDWord(b,21)+c;
{$else}
inc(a,in_[0]+$d76aa478+(d xor(b and(c xor d)))); a := ((a shl 7)or(a shr(32-7)))+b;
inc(d,in_[1]+$e8c7b756+(c xor(a and(b xor c)))); d := ((d shl 12)or(d shr(32-12)))+a;
inc(c,in_[2]+$242070db+(b xor(d and(a xor b)))); c := ((c shl 17)or(c shr(32-17)))+d;
inc(b,in_[3]+$c1bdceee+(a xor(c and(d xor a)))); b := ((b shl 22)or(b shr(32-22)))+c;
inc(a,in_[4]+$f57c0faf+(d xor(b and(c xor d)))); a := ((a shl 7)or(a shr(32-7)))+b;
inc(d,in_[5]+$4787c62a+(c xor(a and(b xor c)))); d := ((d shl 12)or(d shr(32-12)))+a;
inc(c,in_[6]+$a8304613+(b xor(d and(a xor b)))); c := ((c shl 17)or(c shr(32-17)))+d;
inc(b,in_[7]+$fd469501+(a xor(c and(d xor a)))); b := ((b shl 22)or(b shr(32-22)))+c;
inc(a,in_[8]+$698098d8+(d xor(b and(c xor d)))); a := ((a shl 7)or(a shr(32-7)))+b;
inc(d,in_[9]+$8b44f7af+(c xor(a and(b xor c)))); d := ((d shl 12)or(d shr(32-12)))+a;
inc(c,in_[10]+$ffff5bb1+(b xor(d and(a xor b)))); c := ((c shl 17)or(c shr(32-17)))+d;
inc(b,in_[11]+$895cd7be+(a xor(c and(d xor a)))); b := ((b shl 22)or(b shr(32-22)))+c;
inc(a,in_[12]+$6b901122+(d xor(b and(c xor d)))); a := ((a shl 7)or(a shr(32-7)))+b;
inc(d,in_[13]+$fd987193+(c xor(a and(b xor c)))); d := ((d shl 12)or(d shr(32-12)))+a;
inc(c,in_[14]+$a679438e+(b xor(d and(a xor b)))); c := ((c shl 17)or(c shr(32-17)))+d;
inc(b,in_[15]+$49b40821+(a xor(c and(d xor a)))); b := ((b shl 22)or(b shr(32-22)))+c;
inc(a,in_[1]+$f61e2562+(c xor(d and(b xor c)))); a := ((a shl 5)or(a shr(32-5)))+b;
inc(d,in_[6]+$c040b340+(b xor(c and(a xor b)))); d := ((d shl 9)or(d shr(32-9)))+a;
inc(c,in_[11]+$265e5a51+(a xor(b and(d xor a)))); c := ((c shl 14)or(c shr(32-14)))+d;
inc(b,in_[0]+$e9b6c7aa+(d xor(a and(c xor d)))); b := ((b shl 20)or(b shr(32-20)))+c;
inc(a,in_[5]+$d62f105d+(c xor(d and(b xor c)))); a := ((a shl 5)or(a shr(32-5)))+b;
inc(d,in_[10]+$02441453+(b xor(c and(a xor b)))); d := ((d shl 9)or(d shr(32-9)))+a;
inc(c,in_[15]+$d8a1e681+(a xor(b and(d xor a)))); c := ((c shl 14)or(c shr(32-14)))+d;
inc(b,in_[4]+$e7d3fbc8+(d xor(a and(c xor d)))); b := ((b shl 20)or(b shr(32-20)))+c;
inc(a,in_[9]+$21e1cde6+(c xor(d and(b xor c)))); a := ((a shl 5)or(a shr(32-5)))+b;
inc(d,in_[14]+$c33707d6+(b xor(c and(a xor b)))); d := ((d shl 9)or(d shr(32-9)))+a;
inc(c,in_[3]+$f4d50d87+(a xor(b and(d xor a)))); c := ((c shl 14)or(c shr(32-14)))+d;
inc(b,in_[8]+$455a14ed+(d xor(a and(c xor d)))); b := ((b shl 20)or(b shr(32-20)))+c;
inc(a,in_[13]+$a9e3e905+(c xor(d and(b xor c)))); a := ((a shl 5)or(a shr(32-5)))+b;
inc(d,in_[2]+$fcefa3f8+(b xor(c and(a xor b)))); d := ((d shl 9)or(d shr(32-9)))+a;
inc(c,in_[7]+$676f02d9+(a xor(b and(d xor a)))); c := ((c shl 14)or(c shr(32-14)))+d;
inc(b,in_[12]+$8d2a4c8a+(d xor(a and(c xor d)))); b := ((b shl 20)or(b shr(32-20)))+c;
inc(a,in_[5]+$fffa3942+(b xor c xor d)); a := ((a shl 4)or(a shr(32-4)))+b;
inc(d,in_[8]+$8771f681+(a xor b xor c)); d := ((d shl 11)or(d shr(32-11)))+a;
inc(c,in_[11]+$6d9d6122+(d xor a xor b)); c := ((c shl 16)or(c shr(32-16)))+d;
inc(b,in_[14]+$fde5380c+(c xor d xor a)); b := ((b shl 23)or(b shr(32-23)))+c;
inc(a,in_[1]+$a4beea44+(b xor c xor d)); a := ((a shl 4)or(a shr(32-4)))+b;
inc(d,in_[4]+$4bdecfa9+(a xor b xor c)); d := ((d shl 11)or(d shr(32-11)))+a;
inc(c,in_[7]+$f6bb4b60+(d xor a xor b)); c := ((c shl 16)or(c shr(32-16)))+d;
inc(b,in_[10]+$bebfbc70+(c xor d xor a)); b := ((b shl 23)or(b shr(32-23)))+c;
inc(a,in_[13]+$289b7ec6+(b xor c xor d)); a := ((a shl 4)or(a shr(32-4)))+b;
inc(d,in_[0]+$eaa127fa+(a xor b xor c)); d := ((d shl 11)or(d shr(32-11)))+a;
inc(c,in_[3]+$d4ef3085+(d xor a xor b)); c := ((c shl 16)or(c shr(32-16)))+d;
inc(b,in_[6]+$04881d05+(c xor d xor a)); b := ((b shl 23)or(b shr(32-23)))+c;
inc(a,in_[9]+$d9d4d039+(b xor c xor d)); a := ((a shl 4)or(a shr(32-4)))+b;
inc(d,in_[12]+$e6db99e5+(a xor b xor c)); d := ((d shl 11)or(d shr(32-11)))+a;
inc(c,in_[15]+$1fa27cf8+(d xor a xor b)); c := ((c shl 16)or(c shr(32-16)))+d;
inc(b,in_[2]+$c4ac5665+(c xor d xor a)); b := ((b shl 23)or(b shr(32-23)))+c;
inc(a,in_[0]+$f4292244+(c xor(b or(not d)))); a := ((a shl 6)or(a shr(32-6)))+b;
inc(d,in_[7]+$432aff97+(b xor(a or(not c)))); d := ((d shl 10)or(d shr(32-10)))+a;
inc(c,in_[14]+$ab9423a7+(a xor(d or(not b)))); c := ((c shl 15)or(c shr(32-15)))+d;
inc(b,in_[5]+$fc93a039+(d xor(c or(not a)))); b := ((b shl 21)or(b shr(32-21)))+c;
inc(a,in_[12]+$655b59c3+(c xor(b or(not d)))); a := ((a shl 6)or(a shr(32-6)))+b;
inc(d,in_[3]+$8f0ccc92+(b xor(a or(not c)))); d := ((d shl 10)or(d shr(32-10)))+a;
inc(c,in_[10]+$ffeff47d+(a xor(d or(not b)))); c := ((c shl 15)or(c shr(32-15)))+d;
inc(b,in_[1]+$85845dd1+(d xor(c or(not a)))); b := ((b shl 21)or(b shr(32-21)))+c;
inc(a,in_[8]+$6fa87e4f+(c xor(b or(not d)))); a := ((a shl 6)or(a shr(32-6)))+b;
inc(d,in_[15]+$fe2ce6e0+(b xor(a or(not c)))); d := ((d shl 10)or(d shr(32-10)))+a;
inc(c,in_[6]+$a3014314+(a xor(d or(not b)))); c := ((c shl 15)or(c shr(32-15)))+d;
inc(b,in_[13]+$4e0811a1+(d xor(c or(not a)))); b := ((b shl 21)or(b shr(32-21)))+c;
inc(a,in_[4]+$f7537e82+(c xor(b or(not d)))); a := ((a shl 6)or(a shr(32-6)))+b;
inc(d,in_[11]+$bd3af235+(b xor(a or(not c)))); d := ((d shl 10)or(d shr(32-10)))+a;
inc(c,in_[2]+$2ad7d2bb+(a xor(d or(not b)))); c := ((c shl 15)or(c shr(32-15)))+d;
inc(b,in_[9]+$eb86d391+(d xor(c or(not a)))); b := ((b shl 21)or(b shr(32-21)))+c;
{$endif}
inc(buf[0],a);
inc(buf[1],b);
inc(buf[2],c);
inc(buf[3],d);
end;
{$else PUREPASCAL}
{
MD5_386.Asm - 386 optimized helper routine for calculating
MD Message-Digest values
written 2/2/94 by Peter Sawatzki
Buchenhof 3, D58091 Hagen, Germany Fed Rep
Peter@Sawatzki.de http://www.sawatzki.de
original C Source was found in Dr. Dobbs Journal Sep 91
MD5 algorithm from RSA Data Security, Inc.
Taken from https://github.com/maximmasiutin/MD5_Transform-x64
} {$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=buf:TMD5Buf edx=in_:TMD5In
push ebx
push esi
push edi
push ebp
mov ebp, edx
push eax
mov edx, dword ptr [eax+0CH]
mov ecx, dword ptr [eax+8H]
mov ebx, dword ptr [eax+4H]
mov eax, dword ptr [eax]
add eax, dword ptr [ebp]
add eax, -680876936
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
add edx, dword ptr [ebp+4H]
add edx, -389564586
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
add ecx, dword ptr [ebp+8H]
add ecx, 606105819
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
add ebx, dword ptr [ebp+0CH]
add ebx, -1044525330
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
add eax, dword ptr [ebp+10H]
add eax, -176418897
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
add edx, dword ptr [ebp+14H]
add edx, 1200080426
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
add ecx, dword ptr [ebp+18H]
add ecx, -1473231341
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
add ebx, dword ptr [ebp+1CH]
add ebx, -45705983
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
add eax, dword ptr [ebp+20H]
add eax, 1770035416
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
add edx, dword ptr [ebp+24H]
add edx, -1958414417
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
add ecx, dword ptr [ebp+28H]
add ecx, -42063
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
add ebx, dword ptr [ebp+2CH]
add ebx, -1990404162
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
add eax, dword ptr [ebp+30H]
add eax, 1804603682
mov esi, ebx
not esi
and esi, edx
mov edi, ecx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 7
add eax, ebx
add edx, dword ptr [ebp+34H]
add edx, -40341101
mov esi, eax
not esi
and esi, ecx
mov edi, ebx
and edi, eax
or esi, edi
add edx, esi
rol edx, 12
add edx, eax
add ecx, dword ptr [ebp+38H]
add ecx, -1502002290
mov esi, edx
not esi
and esi, ebx
mov edi, eax
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 17
add ecx, edx
add ebx, dword ptr [ebp+3CH]
add ebx, 1236535329
mov esi, ecx
not esi
and esi, eax
mov edi, edx
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 22
add ebx, ecx
add eax, dword ptr [ebp+4H]
add eax, -165796510
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
add edx, dword ptr [ebp+18H]
add edx, -1069501632
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, dword ptr [ebp+2CH]
add ecx, 643717713
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
add ebx, dword ptr [ebp]
add ebx, -373897302
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, dword ptr [ebp+14H]
add eax, -701558691
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
add edx, dword ptr [ebp+28H]
add edx, 38016083
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, dword ptr [ebp+3CH]
add ecx, -660478335
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
add ebx, dword ptr [ebp+10H]
add ebx, -405537848
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, dword ptr [ebp+24H]
add eax, 568446438
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
add edx, dword ptr [ebp+38H]
add edx, -1019803690
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, dword ptr [ebp+0CH]
add ecx, -187363961
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
add ebx, dword ptr [ebp+20H]
add ebx, 1163531501
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, dword ptr [ebp+34H]
add eax, -1444681467
mov esi, edx
not esi
and esi, ecx
mov edi, edx
and edi, ebx
or esi, edi
add eax, esi
rol eax, 5
add eax, ebx
add edx, dword ptr [ebp+8H]
add edx, -51403784
mov esi, ecx
not esi
and esi, ebx
mov edi, ecx
and edi, eax
or esi, edi
add edx, esi
rol edx, 9
add edx, eax
add ecx, dword ptr [ebp+1CH]
add ecx, 1735328473
mov esi, ebx
not esi
and esi, eax
mov edi, ebx
and edi, edx
or esi, edi
add ecx, esi
rol ecx, 14
add ecx, edx
add ebx, dword ptr [ebp+30H]
add ebx, -1926607734
mov esi, eax
not esi
and esi, edx
mov edi, eax
and edi, ecx
or esi, edi
add ebx, esi
rol ebx, 20
add ebx, ecx
add eax, dword ptr [ebp+14H]
add eax, -378558
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
add edx, dword ptr [ebp+20H]
add edx, -2022574463
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
add ecx, dword ptr [ebp+2CH]
add ecx, 1839030562
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
add ebx, dword ptr [ebp+38H]
add ebx, -35309556
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
add eax, dword ptr [ebp+4H]
add eax, -1530992060
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
add edx, dword ptr [ebp+10H]
add edx, 1272893353
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
add ecx, dword ptr [ebp+1CH]
add ecx, -155497632
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
add ebx, dword ptr [ebp+28H]
add ebx, -1094730640
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
add eax, dword ptr [ebp+34H]
add eax, 681279174
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
add edx, dword ptr [ebp]
add edx, -358537222
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
add ecx, dword ptr [ebp+0CH]
add ecx, -722521979
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
add ebx, dword ptr [ebp+18H]
add ebx, 76029189
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
add eax, dword ptr [ebp+24H]
add eax, -640364487
mov esi, edx
xor esi, ecx
xor esi, ebx
add eax, esi
rol eax, 4
add eax, ebx
add edx, dword ptr [ebp+30H]
add edx, -421815835
mov esi, ecx
xor esi, ebx
xor esi, eax
add edx, esi
rol edx, 11
add edx, eax
add ecx, dword ptr [ebp+3CH]
add ecx, 530742520
mov esi, ebx
xor esi, eax
xor esi, edx
add ecx, esi
rol ecx, 16
add ecx, edx
add ebx, dword ptr [ebp+8H]
add ebx, -995338651
mov esi, eax
xor esi, edx
xor esi, ecx
add ebx, esi
rol ebx, 23
add ebx, ecx
add eax, dword ptr [ebp]
add eax, -198630844
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
add edx, dword ptr [ebp+1CH]
add edx, 1126891415
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
add ecx, dword ptr [ebp+38H]
add ecx, -1416354905
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
add ebx, dword ptr [ebp+14H]
add ebx, -57434055
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
add eax, dword ptr [ebp+30H]
add eax, 1700485571
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
add edx, dword ptr [ebp+0CH]
add edx, -1894986606
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
add ecx, dword ptr [ebp+28H]
add ecx, -1051523
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
add ebx, dword ptr [ebp+4H]
add ebx, -2054922799
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
add eax, dword ptr [ebp+20H]
add eax, 1873313359
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
add edx, dword ptr [ebp+3CH]
add edx, -30611744
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
add ecx, dword ptr [ebp+18H]
add ecx, -1560198380
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
add ebx, dword ptr [ebp+34H]
add ebx, 1309151649
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
add eax, dword ptr [ebp+10H]
add eax, -145523070
mov esi, edx
not esi
or esi, ebx
xor esi, ecx
add eax, esi
rol eax, 6
add eax, ebx
add edx, dword ptr [ebp+2CH]
add edx, -1120210379
mov esi, ecx
not esi
or esi, eax
xor esi, ebx
add edx, esi
rol edx, 10
add edx, eax
add ecx, dword ptr [ebp+8H]
add ecx, 718787259
mov esi, ebx
not esi
or esi, edx
xor esi, eax
add ecx, esi
rol ecx, 15
add ecx, edx
add ebx, dword ptr [ebp+24H]
add ebx, -343485551
mov esi, eax
not esi
or esi, ecx
xor esi, edx
add ebx, esi
rol ebx, 21
add ebx, ecx
pop esi
add dword ptr [esi], eax
add dword ptr [esi+4H], ebx
add dword ptr [esi+8H], ecx
add dword ptr [esi+0CH], edx
pop ebp
pop edi
pop esi
pop ebx
end;
{$endif PUREPASCAL}
{$endif CPUX64}
procedure RawMd5Compress(var Hash; Data: pointer);
begin
MD5Transform(TMD5Buf(Hash), PMD5In(Data)^);
end;
function TMD5.Final: TMD5Digest;
begin
Finalize;
result := TMD5Digest(buf);
end;
procedure TMD5.Final(out result: TMD5Digest);
begin
Finalize;
result := TMD5Digest(buf);
end;
procedure TMD5.Finalize;
var count: Integer;
p: ^Byte;
begin
count := bytes[0] and $3f; // number of pending bytes in
p := @in_;
Inc(p,count);
// Set the first char of padding to 0x80. There is always room
p^ := $80;
Inc(p);
// Bytes of padding needed to make 56 bytes (-8..55)
count := 55-count;
if count<0 then begin // Padding forces an extra block
FillcharFast(p^,count+8,0);
MD5Transform(buf,in_);
p := @in_;
count := 56;
end;
FillcharFast(p^,count,0);
// Append length in bits and transform
in_[14] := bytes[0] shl 3;
in_[15] := (bytes[1] shl 3) or (bytes[0] shr 29);
MD5Transform(buf,in_);
end;
procedure TMD5.Full(Buffer: pointer; Len: integer; out Digest: TMD5Digest);
begin
buf[0] := $67452301;
buf[1] := $efcdab89;
buf[2] := $98badcfe;
buf[3] := $10325476;
bytes[0] := Len;
while Len>=SizeOf(TMD5In) do begin
MD5Transform(buf,PMD5In(Buffer)^);
inc(PMD5In(Buffer));
dec(Len,SizeOf(TMD5In));
end;
MoveFast(Buffer^,in_,Len);
Buffer := PAnsiChar(@in_)+Len;
PByte(Buffer)^ := $80;
inc(PByte(Buffer));
Len := 55-Len;
if Len>=0 then
FillcharFast(Buffer^,Len,0) else begin
FillcharFast(Buffer^,Len+8,0);
MD5Transform(buf,in_);
FillcharFast(in_,56,0);
end;
Len := bytes[0];
in_[14] := Len shl 3;
in_[15] := Len shr 29;
MD5Transform(buf,in_);
Digest := TMD5Digest(buf);
end;
procedure TMD5.Init;
begin
buf[0] := $67452301;
buf[1] := $efcdab89;
buf[2] := $98badcfe;
buf[3] := $10325476;
bytes[0] := 0;
bytes[1] := 0;
end;
procedure TMD5.Update(const buffer; len: Cardinal);
var p: ^TMD5In;
t: cardinal;
i: integer;
begin
if len=0 then
exit;
p := @buffer;
// Update byte count
t := bytes[0];
Inc(bytes[0],len);
if bytes[0]<t then
Inc(bytes[1]); // 64 bit carry from low to high
t := 64-(t and 63); // space available in in_ (at least 1)
if t>len then begin
MoveFast(p^,Pointer(PtrUInt(@in_)+64-t)^,len);
exit;
end;
// First chunk is an odd size
MoveFast(p^,Pointer(PtrUInt(@in_)+64-t)^,t);
MD5Transform(buf,in_);
inc(PByte(p),t);
dec(len,t);
// Process data in 64-byte chunks
for i := 1 to len shr 6 do begin
MD5Transform(buf,p^);
inc(p);
end;
// Handle any remaining bytes of data.
MoveFast(p^,in_,len and 63);
end;
procedure TMD5.Update(const Buffer: RawByteString);
begin
Update(pointer(Buffer)^,length(Buffer));
end;
function MD5Buf(const Buffer; Len: Cardinal): TMD5Digest;
var MD5: TMD5;
begin
MD5.Full(@Buffer,Len,result);
end;
function htdigest(const user, realm, pass: RawByteString): RawUTF8;
// apache-compatible: agent007:download area:8364d0044ef57b3defcfa141e8f77b65
// hash=`echo -n "$user:$realm:$pass" | md5sum | cut -b -32`
// echo "$user:$realm:$hash"
var tmp: RawByteString;
begin
tmp := user+':'+realm+':';
result := tmp+MD5(tmp+pass);
end;
function MD5SelfTest: boolean;
begin
result := (htdigest('agent007','download area','secret')=
'agent007:download area:8364d0044ef57b3defcfa141e8f77b65') and
(MD5('')='d41d8cd98f00b204e9800998ecf8427e') and
(MD5('a')='0cc175b9c0f1b6a831c399e269772661') and
(MD5('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789')=
'd174ab98d277d9f5a5611c2c9f419d9f');
end;
{ TSHA1 }
// TSHAContext = Hash,MLen,Buffer,Index
procedure sha1Compress(var Hash: TSHAHash; Data: PByteArray);
var A, B, C, D, E, X: cardinal;
W: array[0..79] of cardinal;
i: integer;
begin
// init W[] + A..E
bswap256(@Data[0],@W[0]);
bswap256(@Data[32],@W[8]);
for i := 16 to 79 do begin
X := W[i-3] xor W[i-8] xor W[i-14] xor W[i-16];
W[i] := (X shl 1) or (X shr 31);
end;
A := Hash.A;
B := Hash.B;
C := Hash.C;
D := Hash.D;
E := Hash.E;
// unrolled loop -> all is computed in cpu registers
// note: FPC detects "(A shl 5) or (A shr 27)" pattern into "RolDWord(A,5)" :)
Inc(E,((A shl 5) or (A shr 27)) + (D xor (B and (C xor D))) + $5A827999 + W[ 0]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (C xor (A and (B xor C))) + $5A827999 + W[ 1]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (B xor (E and (A xor B))) + $5A827999 + W[ 2]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (A xor (D and (E xor A))) + $5A827999 + W[ 3]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (E xor (C and (D xor E))) + $5A827999 + W[ 4]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (D xor (B and (C xor D))) + $5A827999 + W[ 5]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (C xor (A and (B xor C))) + $5A827999 + W[ 6]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (B xor (E and (A xor B))) + $5A827999 + W[ 7]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (A xor (D and (E xor A))) + $5A827999 + W[ 8]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (E xor (C and (D xor E))) + $5A827999 + W[ 9]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (D xor (B and (C xor D))) + $5A827999 + W[10]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (C xor (A and (B xor C))) + $5A827999 + W[11]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (B xor (E and (A xor B))) + $5A827999 + W[12]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (A xor (D and (E xor A))) + $5A827999 + W[13]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (E xor (C and (D xor E))) + $5A827999 + W[14]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (D xor (B and (C xor D))) + $5A827999 + W[15]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (C xor (A and (B xor C))) + $5A827999 + W[16]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (B xor (E and (A xor B))) + $5A827999 + W[17]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (A xor (D and (E xor A))) + $5A827999 + W[18]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (E xor (C and (D xor E))) + $5A827999 + W[19]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $6ED9EBA1 + W[20]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $6ED9EBA1 + W[21]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $6ED9EBA1 + W[22]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $6ED9EBA1 + W[23]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $6ED9EBA1 + W[24]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $6ED9EBA1 + W[25]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $6ED9EBA1 + W[26]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $6ED9EBA1 + W[27]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $6ED9EBA1 + W[28]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $6ED9EBA1 + W[29]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $6ED9EBA1 + W[30]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $6ED9EBA1 + W[31]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $6ED9EBA1 + W[32]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $6ED9EBA1 + W[33]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $6ED9EBA1 + W[34]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $6ED9EBA1 + W[35]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $6ED9EBA1 + W[36]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $6ED9EBA1 + W[37]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $6ED9EBA1 + W[38]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $6ED9EBA1 + W[39]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + ((B and C) or (D and (B or C))) + $8F1BBCDC + W[40]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + ((A and B) or (C and (A or B))) + $8F1BBCDC + W[41]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + ((E and A) or (B and (E or A))) + $8F1BBCDC + W[42]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + ((D and E) or (A and (D or E))) + $8F1BBCDC + W[43]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + ((C and D) or (E and (C or D))) + $8F1BBCDC + W[44]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + ((B and C) or (D and (B or C))) + $8F1BBCDC + W[45]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + ((A and B) or (C and (A or B))) + $8F1BBCDC + W[46]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + ((E and A) or (B and (E or A))) + $8F1BBCDC + W[47]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + ((D and E) or (A and (D or E))) + $8F1BBCDC + W[48]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + ((C and D) or (E and (C or D))) + $8F1BBCDC + W[49]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + ((B and C) or (D and (B or C))) + $8F1BBCDC + W[50]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + ((A and B) or (C and (A or B))) + $8F1BBCDC + W[51]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + ((E and A) or (B and (E or A))) + $8F1BBCDC + W[52]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + ((D and E) or (A and (D or E))) + $8F1BBCDC + W[53]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + ((C and D) or (E and (C or D))) + $8F1BBCDC + W[54]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + ((B and C) or (D and (B or C))) + $8F1BBCDC + W[55]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + ((A and B) or (C and (A or B))) + $8F1BBCDC + W[56]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + ((E and A) or (B and (E or A))) + $8F1BBCDC + W[57]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + ((D and E) or (A and (D or E))) + $8F1BBCDC + W[58]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + ((C and D) or (E and (C or D))) + $8F1BBCDC + W[59]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $CA62C1D6 + W[60]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $CA62C1D6 + W[61]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $CA62C1D6 + W[62]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $CA62C1D6 + W[63]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $CA62C1D6 + W[64]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $CA62C1D6 + W[65]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $CA62C1D6 + W[66]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $CA62C1D6 + W[67]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $CA62C1D6 + W[68]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $CA62C1D6 + W[69]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $CA62C1D6 + W[70]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $CA62C1D6 + W[71]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $CA62C1D6 + W[72]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $CA62C1D6 + W[73]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $CA62C1D6 + W[74]); C:= (C shl 30) or (C shr 2);
Inc(E,((A shl 5) or (A shr 27)) + (B xor C xor D) + $CA62C1D6 + W[75]); B:= (B shl 30) or (B shr 2);
Inc(D,((E shl 5) or (E shr 27)) + (A xor B xor C) + $CA62C1D6 + W[76]); A:= (A shl 30) or (A shr 2);
Inc(C,((D shl 5) or (D shr 27)) + (E xor A xor B) + $CA62C1D6 + W[77]); E:= (E shl 30) or (E shr 2);
Inc(B,((C shl 5) or (C shr 27)) + (D xor E xor A) + $CA62C1D6 + W[78]); D:= (D shl 30) or (D shr 2);
Inc(A,((B shl 5) or (B shr 27)) + (C xor D xor E) + $CA62C1D6 + W[79]); C:= (C shl 30) or (C shr 2);
// Calculate new working hash
inc(Hash.A,A);
inc(Hash.B,B);
inc(Hash.C,C);
inc(Hash.D,D);
inc(Hash.E,E);
end;
procedure RawSha1Compress(var Hash; Data: pointer);
begin
sha1Compress(TSHAHash(Hash), Data);
end;
procedure TSHA1.Final(out Digest: TSHA1Digest; NoInit: boolean);
var Data: TSHAContext absolute Context;
begin
// 1. append bit '1' after Buffer
Data.Buffer[Data.Index] := $80;
FillcharFast(Data.Buffer[Data.Index+1],63-Data.Index,0);
// 2. Compress if more than 448 bits, (no room for 64 bit length
if Data.Index>=56 then begin
sha1Compress(Data.Hash,@Data.Buffer);
FillcharFast(Data.Buffer,56,0);
end;
// Write 64 bit Buffer length into the last bits of the last block
// (in big endian format) and do a final compress
PCardinal(@Data.Buffer[56])^ := bswap32(TQWordRec(Data.MLen).H);
PCardinal(@Data.Buffer[60])^ := bswap32(TQWordRec(Data.MLen).L);
sha1Compress(Data.Hash,@Data.Buffer);
// Hash -> Digest to little endian format
bswap160(@Data.Hash,@Digest);
// Clear Data
if not NoInit then
Init;
end;
function TSHA1.Final(NoInit: boolean): TSHA1Digest;
begin
Final(result,NoInit);
end;
procedure TSHA1.Full(Buffer: pointer; Len: integer; out Digest: TSHA1Digest);
begin
{$ifdef USEPADLOCK}
// Padlock need all data once -> Full() is OK, not successive Update()
if padlock_available then begin
Init; // for later Update use
{$ifdef PADLOCKDEBUG}write('padlock_phe_sha1 ');{$endif}
if padlock_phe_sha1(buffer,Len,Digest)=0 then
exit else
{$ifdef PADLOCKDEBUG}write(':ERROR ');{$endif}
end;
{$endif}
Init;
Update(Buffer,Len);
Final(Digest);
end;
procedure TSHA1.Init;
var Data: TSHAContext absolute Context;
begin
Data.Hash.A := $67452301;
Data.Hash.B := $EFCDAB89;
Data.Hash.C := $98BADCFE;
Data.Hash.D := $10325476;
Data.Hash.E := $C3D2E1F0;
FillcharFast(Data.MLen,sizeof(Data)-sizeof(Data.Hash),0);
end;
procedure TSHA1.Update(Buffer: pointer; Len: integer);
var Data: TSHAContext absolute Context;
aLen: integer;
begin
if Buffer=nil then exit; // avoid GPF
inc(Data.MLen,QWord(Cardinal(Len)) shl 3);
while Len>0 do begin
aLen := sizeof(Data.Buffer)-Data.Index;
if aLen<=Len then begin
if Data.Index<>0 then begin
MoveFast(buffer^,Data.Buffer[Data.Index],aLen);
sha1Compress(Data.Hash,@Data.Buffer);
Data.Index := 0;
end else
sha1Compress(Data.Hash,Buffer); // avoid temporary copy
dec(Len,aLen);
inc(PByte(buffer),aLen);
end else begin
MoveFast(buffer^,Data.Buffer[Data.Index],Len);
inc(Data.Index,Len);
break;
end;
end;
end;
procedure TSHA1.Update(const Buffer: RawByteString);
begin
Update(pointer(Buffer),length(Buffer));
end;
{ TAESAbstract }
var
aesivctr: array[boolean] of TAESLocked;
procedure AESIVCtrEncryptDecrypt(const BI; var BO; DoEncrypt: boolean);
begin
if aesivctr[DoEncrypt]=nil then begin
GarbageCollectorFreeAndNil(aesivctr[DoEncrypt],TAESLocked.Create);
with aesivctr[DoEncrypt].fAES do
if DoEncrypt then
EncryptInit(AESIVCTR_KEY,128) else
DecryptInit(AESIVCTR_KEY,128);
end;
with aesivctr[DoEncrypt] do begin
fSafe^.Lock;
TAESContext(fAES.Context).DoBlock(fAES.Context,BI,BO);
fSafe^.UnLock;
end;
end;
constructor TAESAbstract.Create(const aKey; aKeySize: cardinal);
begin
if (aKeySize<>128) and (aKeySize<>192) and (aKeySize<>256) then
raise ESynCrypto.CreateUTF8('%.Create(aKeySize=%): 128/192/256 required',[self,aKeySize]);
fKeySize := aKeySize;
fKeySizeBytes := fKeySize shr 3;
MoveFast(aKey,fKey,fKeySizeBytes);
end;
procedure TAESAbstract.SetIVCTR;
var tmp: PShortString; // temp variable to circumvent FPC bug
begin
repeat
TAESPRNG.Main.FillRandom(TAESBLock(fIVCTR)); // set nonce + ctr
until fIVCTR.nonce<>0;
tmp := ClassNameShort(self);
fIVCtr.magic := crc32c($aba5aba5,@tmp^[2],6); // TAESECB_API -> 'AESECB'
end;
constructor TAESAbstract.Create(const aKey: THash128);
begin
Create(aKey,128);
end;
constructor TAESAbstract.Create(const aKey: THash256);
begin
Create(aKey,256);
end;
constructor TAESAbstract.CreateTemp(aKeySize: cardinal);
var tmp: THash256;
begin
TAESPRNG.Main.FillRandom(tmp);
Create(tmp,aKeySize);
FillZero(tmp);
end;
constructor TAESAbstract.CreateFromSha256(const aKey: RawUTF8);
var Digest: TSHA256Digest;
begin
SHA256Weak(aKey,Digest);
Create(Digest,256);
FillZero(Digest);
end;
constructor TAESAbstract.CreateFromPBKDF2(const aKey: RawUTF8; const aSalt: RawByteString;
aRounds: Integer);
var Digest: TSHA256Digest;
begin
PBKDF2_HMAC_SHA256(aKey,aSalt,aRounds,Digest,ToText(ClassType));
Create(Digest,256);
FillZero(Digest);
end;
destructor TAESAbstract.Destroy;
begin
inherited Destroy;
FillZero(fKey);
end;
function TAESAbstract.AlgoName: TShort16;
const TXT: array[2..4] of array[0..7] of AnsiChar = (#9'aes128',#9'aes192',#9'aes256');
var s: PShortString;
begin
if (self=nil) or (KeySize=0) then
result[0] := #0 else begin
PInt64(@result)^ := PInt64(@TXT[KeySize shr 6])^;
s := ClassNameShort(self);
if s^[0]<#7 then
result[0] := #6 else begin
result[7] := NormToLower[s^[5]]; // TAESCBC -> 'aes128cbc'
result[8] := NormToLower[s^[6]];
result[9] := NormToLower[s^[7]];
end;
end;
end;
procedure TAESAbstract.SetIVHistory(aDepth: integer);
begin
fIVHistoryDec.Init(aDepth,aDepth);
end;
function TAESAbstract.EncryptPKCS7(const Input: RawByteString;
IVAtBeginning: boolean): RawByteString;
begin
SetString(result,nil,EncryptPKCS7Length(length(Input),IVAtBeginning));
EncryptPKCS7Buffer(Pointer(Input),pointer(result),
length(Input),length(result),IVAtBeginning);
end;
function TAESAbstract.EncryptPKCS7(const Input: TBytes;
IVAtBeginning: boolean): TBytes;
begin
result := nil;
SetLength(result,EncryptPKCS7Length(length(Input),IVAtBeginning));
EncryptPKCS7Buffer(Pointer(Input),pointer(result),
length(Input),length(result),IVAtBeginning);
end;
function TAESAbstract.EncryptPKCS7Length(InputLen: cardinal;
IVAtBeginning: boolean): cardinal;
begin
result := InputLen+sizeof(TAESBlock)-(InputLen and AESBlockMod);
if IVAtBeginning then
inc(Result,sizeof(TAESBlock));
end;
function TAESAbstract.EncryptPKCS7Buffer(Input,Output: Pointer; InputLen,OutputLen: cardinal;
IVAtBeginning: boolean): boolean;
var padding, ivsize: cardinal;
begin
padding := sizeof(TAESBlock)-(InputLen and AESBlockMod);
if IVAtBeginning then
ivsize := sizeof(TAESBlock) else
ivsize := 0;
if OutputLen<>ivsize+InputLen+padding then begin
result := false;
exit;
end;
if IVAtBeginning then begin
if fIVReplayAttackCheck<>repNoCheck then begin
if fIVCTR.nonce=0 then
SetIVCTR;
AESIVCtrEncryptDecrypt(fIVCTR,fIV,true); // PRNG from fixed secret
inc(fIVCTR.ctr); // replay attack protection
end else
TAESPRNG.Main.FillRandom(fIV); // PRNG from real entropy
PAESBlock(Output)^ := fIV;
end;
MoveFast(Input^,PByteArray(Output)^[ivsize],InputLen);
FillcharFast(PByteArray(Output)^[ivsize+InputLen],padding,padding);
Inc(PByte(Output),ivsize);
Encrypt(Output,Output,InputLen+padding);
result := true;
end;
function TAESAbstract.DecryptPKCS7Len(var InputLen,ivsize: Integer;
Input: pointer; IVAtBeginning, RaiseESynCryptoOnError: boolean): boolean;
var ctr: TAESIVCTR;
begin
result := true;
if (InputLen<sizeof(TAESBlock)) or (InputLen and AESBlockMod<>0) then
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: Invalid InputLen=%',[self,InputLen]) else
result := false;
if result and IVAtBeginning then begin
if (fIVReplayAttackCheck<>repNoCheck) and (fIVCTRState<>ctrNotUsed) then begin
if fIVCTR.nonce=0 then
SetIVCTR;
AESIVCtrEncryptDecrypt(Input^,ctr,false);
if fIVCTRState=ctrUnknown then
if ctr.magic=fIVCTR.magic then begin
fIVCTR := ctr;
fIVCTRState := ctrUsed;
inc(fIVCTR.ctr);
end else
if fIVReplayAttackCheck=repMandatory then
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: IVCTR is not handled '+
'on encryption',[self]) else
result := false else begin
fIVCTRState := ctrNotused;
if fIVHistoryDec.Depth=0 then
SetIVHistory(64); // naive but efficient fallback
end else
if IsEqual(TAESBlock(ctr),TAESBlock(fIVCTR)) then
inc(fIVCTR.ctr) else
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: wrong IVCTR %/% %/% -> '+
'potential replay attack',[self,ctr.magic,fIVCTR.magic,ctr.ctr,fIVCTR.ctr]) else
result := false;
end;
fIV := PAESBlock(Input)^;
if result and (fIVHistoryDec.Depth>0) and not fIVHistoryDec.Add(fIV) then
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: duplicated IV=% -> '+
'potential replay attack',[self,AESBlockToShortString(fIV)]) else
result := false;
dec(InputLen,sizeof(TAESBlock));
ivsize := sizeof(TAESBlock);
end else
ivsize := 0;
end;
function TAESAbstract.DecryptPKCS7Buffer(Input: Pointer; InputLen: integer;
IVAtBeginning, RaiseESynCryptoOnError: boolean): RawByteString;
var ivsize,padding: integer;
tmp: array[0..1023] of AnsiChar;
P: PAnsiChar;
begin
result := '';
if not DecryptPKCS7Len(InputLen,ivsize,Input,IVAtBeginning,RaiseESynCryptoOnError) then
exit;
if InputLen<sizeof(tmp) then
P := @tmp else begin
SetString(result,nil,InputLen);
P := pointer(result);
end;
Decrypt(@PByteArray(Input)^[ivsize],P,InputLen);
padding := ord(P[InputLen-1]); // result[1..len]
if padding>sizeof(TAESBlock) then
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: Invalid Input',[self]) else
result := '' else
if P=@tmp then
SetString(result,P,InputLen-padding) else
SetLength(result,InputLen-padding); // fast in-place resize
end;
function TAESAbstract.DecryptPKCS7(const Input: RawByteString;
IVAtBeginning, RaiseESynCryptoOnError: boolean): RawByteString;
begin
result := DecryptPKCS7Buffer(pointer(Input),length(Input),IVAtBeginning,RaiseESynCryptoOnError);
end;
function TAESAbstract.DecryptPKCS7(const Input: TBytes;
IVAtBeginning, RaiseESynCryptoOnError: boolean): TBytes;
var len,ivsize,padding: integer;
begin
result := nil;
len := length(Input);
if not DecryptPKCS7Len(len,ivsize,pointer(Input),IVAtBeginning,RaiseESynCryptoOnError) then
exit;
SetLength(result,len);
Decrypt(@PByteArray(Input)^[ivsize],pointer(result),len);
padding := result[len-1]; // result[0..len-1]
if padding>sizeof(TAESBlock) then
if RaiseESynCryptoOnError then
raise ESynCrypto.CreateUTF8('%.DecryptPKCS7: Invalid Input',[self]) else
result := nil else
SetLength(result,len-padding); // fast in-place resize
end;
function TAESAbstract.MACSetNonce(const aKey: THash256; aAssociated: pointer;
aAssociatedLen: integer): boolean;
begin
result := false;
end;
function TAESAbstract.MACGetLast(out aCRC: THash256): boolean;
begin
result := false;
end;
function TAESAbstract.MACEquals(const aCRC: THash256): boolean;
var mac: THash256;
begin
result := MACGetLast(mac) and IsEqual(mac,aCRC);
end;
function TAESAbstract.MACCheckError(aEncrypted: pointer; Count: cardinal): boolean;
begin
result := false;
end;
function TAESAbstract.MACAndCrypt(const Data: RawByteString; Encrypt: boolean): RawByteString;
type
TCryptData = packed record
nonce,mac: THash256;
crc: cardinal; // crc32c(nonce+mac) to avoid naive fuzzing
data: RawByteString;
end;
PCryptData = ^TCryptData;
const
VERSION = 1;
CRCSIZ = sizeof(THash256)*2;
SIZ = CRCSIZ+sizeof(cardinal);
var rec: TCryptData;
len: integer;
pcd: PCryptData absolute Data;
P: PAnsiChar;
begin
result := ''; // e.g. MACSetNonce not supported
try
if Encrypt then begin
TAESPRNG.Main.FillRandom(rec.nonce);
if not MACSetNonce(rec.nonce) then
exit;
rec.Data := EncryptPKCS7(Data,{IVAtBeginning=}true);
if not MACGetLast(rec.mac) then
exit;
rec.crc := crc32c(VERSION,@rec.nonce,CRCSIZ);
result := RecordSave(rec,TypeInfo(TCryptData));
end else begin
if (length(Data)<=SIZ) or (pcd^.crc<>crc32c(VERSION,pointer(pcd),CRCSIZ)) then
exit;
P := @pcd^.data; // inlined RecordLoad() for safety
len := FromVarUInt32(PByte(P));
if length(Data)-len<>P-pointer(Data) then
exit; // avoid buffer overflow
if MACSetNonce(pcd^.nonce) then
result := DecryptPKCS7Buffer(P,len,true,false);
if result<>'' then
if not MACEquals(pcd^.mac) then begin
FillZero(result);
result := '';
end;
end;
finally
FillZero(rec.data);
end;
end;
class function TAESAbstract.MACEncrypt(const Data: RawByteString; const Key: THash256;
Encrypt: boolean): RawByteString;
var aes: TAESAbstract;
begin
aes := Create(Key);
try
result := aes.MACAndCrypt(Data,Encrypt);
finally
aes.Free;
end;
end;
class function TAESAbstract.MACEncrypt(const Data: RawByteString; const Key: THash128;
Encrypt: boolean): RawByteString;
var aes: TAESAbstract;
begin
aes := Create(Key);
try
result := aes.MACAndCrypt(Data,Encrypt);
finally
aes.Free;
end;
end;
class function TAESAbstract.SimpleEncrypt(const Input,Key: RawByteString;
Encrypt, IVAtBeginning, RaiseESynCryptoOnError: boolean): RawByteString;
var instance: TAESAbstract;
begin
instance := CreateFromSha256(Key);
try
if Encrypt then
result := instance.EncryptPKCS7(Input,IVAtBeginning) else
result := instance.DecryptPKCS7(Input,IVAtBeginning,RaiseESynCryptoOnError);
finally
instance.Free;
end;
end;
class function TAESAbstract.SimpleEncrypt(const Input: RawByteString; const Key;
KeySize: integer; Encrypt, IVAtBeginning, RaiseESynCryptoOnError: boolean): RawByteString;
var instance: TAESAbstract;
begin
instance := Create(Key,KeySize);
try
if Encrypt then
result := instance.EncryptPKCS7(Input,IVAtBeginning) else
result := instance.DecryptPKCS7(Input,IVAtBeginning,RaiseESynCryptoOnError);
finally
instance.Free;
end;
end;
class function TAESAbstract.SimpleEncryptFile(const InputFile, OutputFile: TFileName;
const Key: RawByteString; Encrypt, IVAtBeginning,RaiseESynCryptoOnError: boolean): boolean;
var src,dst: RawByteString;
begin
result := false;
src := StringFromFile(InputFile);
if src<>'' then begin
dst := SimpleEncrypt(src,Key,Encrypt,IVAtBeginning,RaiseESynCryptoOnError);
if dst<>'' then
result := FileFromString(dst,OutputFile);
end;
end;
class function TAESAbstract.SimpleEncryptFile(const InputFile, Outputfile: TFileName;
const Key; KeySize: integer; Encrypt, IVAtBeginning, RaiseESynCryptoOnError: boolean): boolean;
var src,dst: RawByteString;
begin
result := false;
src := StringFromFile(InputFile);
if src<>'' then begin
dst := SimpleEncrypt(src,Key,KeySize,Encrypt,IVAtBeginning,RaiseESynCryptoOnError);
if dst<>'' then
result := FileFromString(dst,OutputFile);
end;
end;
function TAESAbstract.Clone: TAESAbstract;
begin
result := TAESAbstractClass(ClassType).Create(fKey,fKeySize);
result.IVHistoryDepth := IVHistoryDepth;
result.IVReplayAttackCheck := IVReplayAttackCheck;
end;
function TAESAbstract.CloneEncryptDecrypt: TAESAbstract;
begin
result := Clone;
end;
{ TAESAbstractSyn }
destructor TAESAbstractSyn.Destroy;
begin
inherited Destroy;
AES.Done; // mandatory for Padlock - also fill buffer with 0 for safety
FillZero(fCV); // may contain sensitive data on some modes
FillZero(fIV);
end;
function TAESAbstractSyn.Clone: TAESAbstract;
begin
if (fIVHistoryDec.Count<>0) {$ifdef USEPADLOCK} or
TAESContext(AES).initialized and (TAESContext(AES).ViaCtx<>nil){$endif} then
result := inherited Clone else begin
result := NewInstance as TAESAbstractSyn;
MoveFast(pointer(self)^,pointer(result)^,InstanceSize);
end;
end;
procedure TAESAbstractSyn.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
fIn := BufIn;
fOut := BufOut;
fCV := fIV;
end;
procedure TAESAbstractSyn.DecryptInit;
begin
if AES.DecryptInit(fKey,fKeySize) then
fAESInit := initDecrypt else
raise ESynCrypto.CreateUTF8('%.DecryptInit',[self]);
end;
procedure TAESAbstractSyn.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
fIn := BufIn;
fOut := BufOut;
fCV := fIV;
end;
procedure TAESAbstractSyn.EncryptInit;
begin
if AES.EncryptInit(fKey,fKeySize) then
fAESInit := initEncrypt else
raise ESynCrypto.CreateUTF8('%.EncryptInit',[self]);
end;
procedure TAESAbstractSyn.TrailerBytes(count: cardinal);
begin
if fAESInit<>initEncrypt then
EncryptInit;
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
XorMemory(pointer(fOut),pointer(fIn),@fCV,count);
end;
{ TAESECB }
procedure TAESECB.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
inherited; // CV := IV + set fIn,fOut
if fAESInit<>initDecrypt then
DecryptInit;
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fIn^,fOut^);
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
procedure TAESECB.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
inherited; // CV := IV + set fIn,fOut
if fAESInit<>initEncrypt then
EncryptInit;
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fIn^,fOut^);
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
{ TAESCBC }
procedure TAESCBC.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
tmp: TAESBlock;
begin
inherited; // CV := IV + set fIn,fOut
if Count>=sizeof(TAESBlock) then begin
if fAESInit<>initDecrypt then
DecryptInit;
for i := 1 to Count shr 4 do begin
tmp := fIn^;
TAESContext(AES.Context).DoBlock(AES.Context,fIn^,fOut^);
XorBlock16(pointer(fOut),pointer(@fCV));
fCV := tmp;
inc(fIn);
inc(fOut);
end;
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
procedure TAESCBC.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
inherited; // CV := IV + set fIn,fOut
if fAESInit<>initEncrypt then
EncryptInit;
for i := 1 to Count shr 4 do begin
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
TAESContext(AES.Context).DoBlock(AES.Context,fOut^,fOut^);
fCV := fOut^;
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
{ TAESAbstractEncryptOnly }
constructor TAESAbstractEncryptOnly.Create(const aKey; aKeySize: cardinal);
begin
inherited Create(aKey,aKeySize);
EncryptInit; // as expected by overriden Encrypt/Decrypt methods below
end;
function TAESAbstractEncryptOnly.CloneEncryptDecrypt: TAESAbstract;
begin
result := self;
end;
{ TAESCFB }
{$ifdef USEAESNI32}
procedure AesNiTrailer; // = TAESAbstractSyn.EncryptTrailer from AES-NI asm
{$ifdef FPC} nostackframe; assembler; {$endif}
asm // eax=TAESContext ecx=len xmm7=CV esi=BufIn edi=BufOut
call dword ptr [eax].TAESContext.AesNi32 // = AES.Encrypt(fCV,fCV)
lea edx, [eax].TAESContext.buf // used as temporary buffer
movups [edx], xmm7
cld
@s: lodsb
xor al, [edx] // = XorMemory(pointer(fOut),pointer(fIn),@fCV,len);
inc edx
stosb
dec ecx
jnz @s
end;
{$endif}
procedure TAESCFB.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
tmp: TAESBlock;
begin
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) then
asm
push esi
push edi
mov eax, self
mov ecx, count
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[eax].TAESCFB.fIV
lea eax, [eax].TAESCFB.AES
push ecx
shr ecx, 4
jz @z
@s: call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
movaps xmm1, xmm0
pxor xmm0, xmm7
movaps xmm7, xmm1 // fCV := fIn
movups dqword ptr[edi], xmm0 // fOut := fIn xor fCV
dec ecx
lea esi, [esi + 16]
lea edi, [edi + 16]
jnz @s
@z: pop ecx
and ecx, 15
jz @0
call AesNiTrailer
@0: pop edi
pop esi
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
tmp := fIn^;
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
fCV := tmp;
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
end;
procedure TAESCFB.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) then
asm
push esi
push edi
mov eax, self
mov ecx, count
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[eax].TAESCFB.fIV
lea eax, [eax].TAESCFB.AES
push ecx
shr ecx, 4
jz @z
@s: call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
pxor xmm7, xmm0
movups dqword ptr[edi], xmm7 // fOut := fIn xor fCV
dec ecx
lea esi, [esi + 16]
lea edi, [edi + 16]
jnz @s
@z: pop ecx
and ecx, 15
jz @0
call AesNiTrailer
@0: pop edi
pop esi
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
fCV := fOut^;
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
end;
{ TAESAbstractAEAD }
destructor TAESAbstractAEAD.Destroy;
begin
inherited Destroy;
FillCharFast(fMacKey,sizeof(fMacKey),0);
FillCharFast(fMac,sizeof(fMac),0);
end;
function TAESAbstractAEAD.MACSetNonce(const aKey: THash256; aAssociated: pointer;
aAssociatedLen: integer): boolean;
var rec: THash256Rec absolute aKey;
begin
// safe seed for plain text crc, before AES encryption
// from TECDHEProtocol.SetKey, aKey is a public nonce to avoid replay attacks
fMACKey.plain := rec.Lo;
XorBlock16(@fMACKey.plain,@rec.Hi);
// neutral seed for encrypted crc, to check for errors, with no compromission
if (aAssociated<>nil) and (aAssociatedLen>0) then
crc128c(aAssociated,aAssociatedLen,fMACKey.encrypted) else
FillcharFast(fMACKey.encrypted,sizeof(THash128),255);
result := true;
end;
function TAESAbstractAEAD.MACGetLast(out aCRC: THash256): boolean;
var rec: THash256Rec absolute aCRC;
begin
// encrypt the plain text crc, to perform message authentication and integrity
AES.Encrypt(fMAC.plain,rec.Lo);
// store the encrypted text crc, to check for errors, with no compromission
rec.Hi := fMAC.encrypted;
result := true;
end;
function TAESAbstractAEAD.MACCheckError(aEncrypted: pointer; Count: cardinal): boolean;
var crc: THash128;
begin
result := false;
if (Count<32) or (Count and AESBlockMod<>0) then
exit;
crc := fMACKey.encrypted;
crcblocks(@crc,aEncrypted,Count shr 4-2);
result := IsEqual(crc,PHash128(@PByteArray(aEncrypted)[Count-sizeof(crc)])^);
end;
{ TAESCFBCRC }
procedure TAESCFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
tmp: TAESBlock;
begin
if Count=0 then
exit;
fMAC := fMACKey; // reuse the same key until next MACSetNonce()
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) and (Count and AESBlockMod=0) then
asm
push ebx
push esi
push edi
mov ebx, self
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[ebx].TAESCFBCRC.fIV
@s: lea eax, [ebx].TAESCFBCRC.fMAC.encrypted
mov edx, esi
call crcblock // using SSE4.2 or fast tables
lea eax, [ebx].TAESCFBCRC.AES
call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
movaps xmm1, xmm0
pxor xmm0, xmm7
movaps xmm7, xmm1 // fCV := fIn
movups dqword ptr[edi], xmm0 // fOut := fIn xor fCV
lea eax, [ebx].TAESCFBCRC.fMAC.plain
mov edx, edi
call crcblock
sub dword ptr[count], 16
lea esi, [esi + 16]
lea edi, [edi + 16]
ja @s
@z: pop edi
pop esi
pop ebx
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
tmp := fIn^;
crcblock(@fMAC.encrypted,pointer(fIn)); // fIn may be = fOut
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
fCV := tmp;
crcblock(@fMAC.plain,pointer(fOut));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then begin
TrailerBytes(Count);
with fMAC do // includes trailing bytes to the plain crc
PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),Count);
end;
end;
end;
procedure TAESCFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
if Count=0 then
exit;
fMAC := fMACKey; // reuse the same key until next MACSetNonce()
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) and (Count and AESBlockMod=0) then
asm
push ebx
push esi
push edi
mov ebx, self
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[ebx].TAESCFBCRC.fIV
@s: lea eax, [ebx].TAESCFBCRC.fMAC.plain
mov edx, esi
call crcblock
lea eax, [ebx].TAESCFBCRC.AES
call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
pxor xmm7, xmm0
movups dqword ptr[edi], xmm7 // fOut := fIn xor fCV + fCV := fOut^
lea eax, [ebx].TAESCFBCRC.fMAC.encrypted
mov edx, edi
call crcblock
sub dword ptr[count], 16
lea esi, [esi + 16]
lea edi, [edi + 16]
ja @s
pop edi
pop esi
pop ebx
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
crcblock(@fMAC.plain,pointer(fIn)); // fOut may be = fIn
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
fCV := fOut^;
crcblock(@fMAC.encrypted,pointer(fOut));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then begin
with fMAC do // includes trailing bytes to the plain crc
PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),Count);
TrailerBytes(Count);
end;
end;
end;
{ TAESOFBCRC }
procedure TAESOFBCRC.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
if Count=0 then
exit;
fMAC := fMACKey; // reuse the same key until next MACSetNonce()
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) and (Count and AESBlockMod=0) then
asm
push ebx
push esi
push edi
mov ebx, self
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[ebx].TAESOFBCRC.fIV
@s: lea eax, [ebx].TAESOFBCRC.fMAC.encrypted
mov edx, esi
call crcblock
lea eax, [ebx].TAESOFBCRC.AES
call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
pxor xmm0, xmm7
movups dqword ptr[edi], xmm0 // fOut := fIn xor fCV
lea eax, [ebx].TAESOFBCRC.fMAC.plain
mov edx, edi
call crcblock
sub dword ptr[count], 16
lea esi, [esi + 16]
lea edi, [edi + 16]
ja @s
pop edi
pop esi
pop ebx
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
crcblock(@fMAC.encrypted,pointer(fIn)); // fOut may be = fIn
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
crcblock(@fMAC.plain,pointer(fOut));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then begin
TrailerBytes(Count);
with fMAC do // includes trailing bytes to the plain crc
PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fOut),Count);
end;
end;
end;
procedure TAESOFBCRC.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
if Count=0 then
exit;
fMAC := fMACKey; // reuse the same key until next MACSetNonce()
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) and (Count and AESBlockMod=0) then
asm
push ebx
push esi
push edi
mov ebx, self
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[ebx].TAESOFBCRC.fIV
@s: lea eax, [ebx].TAESOFBCRC.fMAC.plain
mov edx, esi
call crcblock
lea eax, [ebx].TAESOFBCRC.AES
call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
pxor xmm0, xmm7
movups dqword ptr[edi], xmm0 // fOut := fIn xor fCV
lea eax, [ebx].TAESOFBCRC.fMAC.encrypted
mov edx, edi
call crcblock
sub dword ptr[count], 16
lea esi, [esi + 16]
lea edi, [edi + 16]
ja @s
pop edi
pop esi
pop ebx
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited Encrypt(BufIn,BufOut,Count); // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
crcblock(@fMAC.plain,pointer(fIn)); // fOut may be = fIn
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
crcblock(@fMAC.encrypted,pointer(fOut));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then begin
with fMAC do // includes trailing bytes to the plain crc
PCardinal(@plain)^ := crc32c(PCardinal(@plain)^,pointer(fIn),Count);
TrailerBytes(Count);
end;
end;
end;
{ TAESOFB }
{$ifdef USEAESNI64}
procedure AesNiEncryptOFB_128(self: TAESOFB; source, dest: pointer; blockcount: PtrUInt);
{$ifdef FPC} nostackframe; assembler; asm {$else} asm .noframe {$endif}
test blockcount, blockcount
jz @z
movups xmm7, dqword ptr[self].TAESOFB.fIV // xmm7 = fCV
lea self, [self].TAESOFB.AES
movups xmm0, dqword ptr[self + 16 * 0]
movups xmm1, dqword ptr[self + 16 * 1]
movups xmm2, dqword ptr[self + 16 * 2]
movups xmm3, dqword ptr[self + 16 * 3]
movups xmm4, dqword ptr[self + 16 * 4]
movups xmm5, dqword ptr[self + 16 * 5]
movups xmm6, dqword ptr[self + 16 * 6]
movups xmm8, dqword ptr[self + 16 * 7]
movups xmm9, dqword ptr[self + 16 * 8]
movups xmm10, dqword ptr[self + 16 * 9]
movups xmm11, dqword ptr[self + 16 * 10]
{$ifdef FPC} align 16 {$else} .align 16 {$endif}
@s: movups xmm15, dqword ptr[source]
pxor xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
aesenc xmm7, xmm8
aesenc xmm7, xmm9
aesenc xmm7, xmm10
aesenclast xmm7, xmm11
pxor xmm15, xmm7
movups dqword ptr[dest], xmm15 // fOut := fIn xor fCV
add source, 16
add dest, 16
dec blockcount
jnz @s
@z:
end;
procedure AesNiEncryptOFB_256(self: TAESOFB; source, dest: pointer; blockcount: PtrUInt);
{$ifdef FPC} nostackframe; assembler; asm {$else} asm .noframe {$endif}
test blockcount, blockcount
jz @z
movups xmm7, dqword ptr[self].TAESOFB.fIV // xmm7 = fCV
lea self, [self].TAESOFB.AES
movups xmm0, dqword ptr[self + 16 * 0]
movups xmm1, dqword ptr[self + 16 * 1]
movups xmm2, dqword ptr[self + 16 * 2]
movups xmm3, dqword ptr[self + 16 * 3]
movups xmm4, dqword ptr[self + 16 * 4]
movups xmm5, dqword ptr[self + 16 * 5]
movups xmm6, dqword ptr[self + 16 * 6]
movups xmm8, dqword ptr[self + 16 * 7]
movups xmm9, dqword ptr[self + 16 * 8]
movups xmm10, dqword ptr[self + 16 * 9]
movups xmm11, dqword ptr[self + 16 * 10]
movups xmm12, dqword ptr[self + 16 * 11]
movups xmm13, dqword ptr[self + 16 * 12]
movups xmm14, dqword ptr[self + 16 * 13]
add self, 16 * 14
{$ifdef FPC} align 16 {$else} .align 16 {$endif}
@s: movups xmm15, dqword ptr[self]
pxor xmm7, xmm0
aesenc xmm7, xmm1
aesenc xmm7, xmm2
aesenc xmm7, xmm3
aesenc xmm7, xmm4
aesenc xmm7, xmm5
aesenc xmm7, xmm6
aesenc xmm7, xmm8
aesenc xmm7, xmm9
aesenc xmm7, xmm10
aesenc xmm7, xmm11
aesenc xmm7, xmm12
aesenc xmm7, xmm13
aesenc xmm7, xmm14
aesenclast xmm7, xmm15
movups xmm15, dqword ptr[source]
pxor xmm15, xmm7
movups dqword ptr[dest], xmm15 // fOut := fIn xor fCV
add source, 16
add dest, 16
dec blockcount
jnz @s
@z:
end;
{$endif USEAESNI64}
procedure TAESOFB.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
Encrypt(BufIn, BufOut, Count); // by definition
end;
procedure TAESOFB.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
begin
{$ifdef USEAESNI64}
if (Count and AESBlockMod=0) and (cfAESNI in CpuFeatures) then
with TAESContext(AES.Context) do
case KeyBits of
128: begin
AesNiEncryptOFB_128(self,BufIn,BufOut,Count shr 4);
exit;
end;
256: begin
AesNiEncryptOFB_256(self,BufIn,BufOut,Count shr 4);
exit;
end;
end;
{$endif USEAESNI64}
{$ifdef USEAESNI32}
if Assigned(TAESContext(AES.Context).AesNi32) then
asm
push esi
push edi
mov eax, self
mov ecx, count
mov esi, BufIn
mov edi, BufOut
movups xmm7, dqword ptr[eax].TAESOFB.fIV // xmm7 = fCV
lea eax, [eax].TAESOFB.AES
push ecx
shr ecx, 4
jz @z
@s: call dword ptr[eax].TAESContext.AesNi32 // AES.Encrypt(fCV,fCV)
movups xmm0, dqword ptr[esi]
pxor xmm0, xmm7
movups dqword ptr[edi], xmm0 // fOut := fIn xor fCV
dec ecx
lea esi, [esi + 16]
lea edi, [edi + 16]
jnz @s
@z: pop ecx
and ecx, 15
jz @0
call AesNiTrailer
@0: pop edi
pop esi
pxor xmm7, xmm7 // for safety
end else
{$endif} begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,fCV);
XorBlock16(pointer(fIn),pointer(fOut),pointer(@fCV));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then
TrailerBytes(Count);
end;
end;
{ TAESCTR }
constructor TAESCTR.Create(const aKey; aKeySize: cardinal);
begin
inherited Create(aKey, aKeySize);
fCTROffset := 7; // counter is in the lower 64 bits, nonce in the upper 64 bits
end;
function TAESCTR.ComposeIV(Nonce, Counter: PAESBlock; NonceLen, CounterLen: integer;
LSBCounter: boolean): boolean;
begin
result := (NonceLen + CounterLen = 16) and (CounterLen > 0);
if result then
if LSBCounter then begin
MoveFast(Nonce[0], fIV[0], NonceLen);
MoveFast(Counter[0], fIV[NonceLen], CounterLen);
fCTROffset := 15;
fCTROffsetMin := 16-CounterLen;
end else begin
MoveFast(Counter[0], fIV[0], CounterLen);
MoveFast(Nonce[0], fIV[CounterLen], NonceLen);
fCTROffset := CounterLen-1;
fCTROffsetMin := 0;
end;
end;
function TAESCTR.ComposeIV(const Nonce, Counter: TByteDynArray;
LSBCounter: boolean): boolean;
begin
result := ComposeIV(pointer(Nonce), pointer(Counter),
length(Nonce), length(Counter), LSBCounter);
end;
procedure TAESCTR.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
var i: integer;
offs: PtrInt;
tmp: TAESBlock;
begin
inherited; // CV := IV + set fIn,fOut
for i := 1 to Count shr 4 do begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,tmp);
offs := fCTROffset;
inc(fCV[offs]);
if fCV[offs]=0 then // manual big-endian increment
repeat
dec(offs);
inc(fCV[offs]);
if (fCV[offs]<>0) or (offs=fCTROffsetMin) then
break;
until false;
XorBlock16(pointer(fIn),pointer(fOut),pointer(@tmp));
inc(fIn);
inc(fOut);
end;
Count := Count and AESBlockMod;
if Count<>0 then begin
TAESContext(AES.Context).DoBlock(AES.Context,fCV,tmp);
XorMemory(pointer(fOut),pointer(fIn),@tmp,Count);
end;
end;
procedure TAESCTR.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
Encrypt(BufIn, BufOut, Count); // by definition
end;
{ TAESGCM }
constructor TAESGCM.Create(const aKey; aKeySize: cardinal);
begin
inherited Create(aKey,aKeySize); // set fKey/fKeySize
if not fAES.Init(aKey,aKeySize) then
raise ESynCrypto.CreateUTF8('%.Create(keysize=%) failed',[self,aKeySize]);
end;
function TAESGCM.Clone: TAESAbstract;
begin
result := NewInstance as TAESGCM;
result.fKey := fKey;
result.fKeySize := fKeySize;
result.fKeySizeBytes := fKeySizeBytes;
TAESGCM(result).fAES := fAES; // reuse the very same TAESGCMEngine memory
end;
destructor TAESGCM.Destroy;
begin
inherited Destroy;
fAES.Done;
FillZero(fIV);
end;
procedure TAESGCM.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
if fContext<>ctxEncrypt then
if fContext=ctxNone then begin
fAES.Reset(@fIV,CTR_POS); // caller should have set the IV
fContext := ctxEncrypt;
end else
raise ESynCrypto.CreateUTF8('%.Encrypt after Decrypt',[self]);
if not fAES.Encrypt(BufIn,BufOut,Count) then
raise ESynCrypto.CreateUTF8('%.Encrypt called after GCM final state',[self]);
end;
procedure TAESGCM.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
if fContext<>ctxDecrypt then
if fContext=ctxNone then begin
fAES.Reset(@fIV,CTR_POS);
fContext := ctxDecrypt;
end else
raise ESynCrypto.CreateUTF8('%.Decrypt after Encrypt',[self]);
if not fAES.Decrypt(BufIn,BufOut,Count) then
raise ESynCrypto.CreateUTF8('%.Decrypt called after GCM final state',[self]);
end;
function TAESGCM.MACSetNonce(const aKey: THash256; aAssociated: pointer;
aAssociatedLen: integer): boolean;
begin
if fContext<>ctxNone then begin
result := false; // should be called before Encrypt/Decrypt
exit;
end;
// aKey is ignored since not used during GMAC computation
if (aAssociated<>nil) and (aAssociatedLen>0) then
fAES.Add_AAD(aAssociated,aAssociatedLen);
result := true;
end;
function TAESGCM.MACGetLast(out aCRC: THash256): boolean;
begin
if fContext=ctxNone then begin
result := false; // should be called after Encrypt/Decrypt
exit;
end;
fAES.Final(THash256Rec(aCRC).Lo,{forreuse:anddone=}false);
FillZero(THash256Rec(aCRC).Hi); // upper 128-bit are not used
fContext := ctxNone; // allow reuse of this fAES instance
result := true;
end;
function TAESGCM.MACCheckError(aEncrypted: pointer; Count: cardinal): boolean;
begin
result := true; // AES-GCM requires the IV to be set -> will be checked later
end;
{$ifdef MSWINDOWS}
type
HCRYPTPROV = pointer;
HCRYPTKEY = pointer;
HCRYPTHASH = pointer;
{$ifdef USERECORDWITHMETHODS}TCryptLibrary = record
{$else}TCryptLibrary = object{$endif}
public
AcquireContextA: function(var phProv: HCRYPTPROV; pszContainer: PAnsiChar;
pszProvider: PAnsiChar; dwProvType: DWORD; dwFlags: DWORD): BOOL; stdcall;
ReleaseContext: function(hProv: HCRYPTPROV; dwFlags: PtrUInt): BOOL; stdcall;
ImportKey: function(hProv: HCRYPTPROV; pbData: pointer; dwDataLen: DWORD;
hPubKey: HCRYPTKEY; dwFlags: DWORD; var phKey: HCRYPTKEY): BOOL; stdcall;
SetKeyParam: function(hKey: HCRYPTKEY; dwParam: DWORD; pbData: pointer;
dwFlags: DWORD): BOOL; stdcall;
DestroyKey: function(hKey: HCRYPTKEY): BOOL; stdcall;
Encrypt: function(hKey: HCRYPTKEY; hHash: HCRYPTHASH; Final: BOOL;
dwFlags: DWORD; pbData: pointer; var pdwDataLen: DWORD; dwBufLen: DWORD): BOOL; stdcall;
Decrypt: function(hKey: HCRYPTKEY; hHash: HCRYPTHASH; Final: BOOL;
dwFlags: DWORD; pbData: pointer; var pdwDataLen: DWORD): BOOL; stdcall;
GenRandom: function(hProv: HCRYPTPROV; dwLen: DWORD; pbBuffer: Pointer): BOOL; stdcall;
Tested: boolean;
Handle: THandle;
function Available: boolean;
end;
const
HCRYPTPROV_NOTTESTED = HCRYPTPROV(-1);
PROV_RSA_FULL = 1;
CRYPT_VERIFYCONTEXT = DWORD($F0000000);
var
CryptoAPI: TCryptLibrary;
function TCryptLibrary.Available: boolean;
procedure Acquire;
const NAMES: array[0..7] of PChar = (
'CryptAcquireContextA','CryptReleaseContext',
'CryptImportKey','CryptSetKeyParam','CryptDestroyKey',
'CryptEncrypt','CryptDecrypt','CryptGenRandom');
var P: PPointer;
i: integer;
begin
Tested := true;
Handle := GetModuleHandle('advapi32.dll');
if Handle<>0 then begin
P := @@AcquireContextA;
for i := 0 to high(NAMES) do begin
P^ := GetProcAddress(Handle,NAMES[i]);
if P^=nil then begin
PPointer(@@AcquireContextA)^ := nil;
break;
end;
inc(P);
end;
end;
end;
begin
if not Tested then
Acquire;
result := Assigned(AcquireContextA);
end;
{$ifdef USE_PROV_RSA_AES}
var
CryptoAPIAESProvider: HCRYPTPROV = HCRYPTPROV_NOTTESTED;
const
PROV_RSA_AES = 24;
CRYPT_NEWKEYSET = 8;
PLAINTEXTKEYBLOB = 8;
CUR_BLOB_VERSION = 2;
KP_IV = 1;
KP_MODE = 4;
CALG_AES_128 = $660E;
CALG_AES_192 = $660F;
CALG_AES_256 = $6610;
CRYPT_MODE_CBC = 1;
CRYPT_MODE_ECB = 2;
CRYPT_MODE_OFB = 3;
CRYPT_MODE_CFB = 4;
CRYPT_MODE_CTS = 5;
procedure EnsureCryptoAPIAESProviderAvailable;
begin
if CryptoAPIAESProvider=nil then
raise ESynCrypto.Create('PROV_RSA_AES provider not installed') else
if CryptoAPIAESProvider=HCRYPTPROV_NOTTESTED then begin
CryptoAPIAESProvider := nil;
if CryptoAPI.Available then begin
if not CryptoAPI.AcquireContextA(CryptoAPIAESProvider,nil,nil,PROV_RSA_AES,0) then
if (HRESULT(GetLastError)<>NTE_BAD_KEYSET) or not CryptoAPI.AcquireContextA(
CryptoAPIAESProvider,nil,nil,PROV_RSA_AES,CRYPT_NEWKEYSET) then
raise ESynCrypto.CreateLastOSError('in AcquireContext',[]);
end;
end;
end;
{ TAESAbstract_API }
constructor TAESAbstract_API.Create(const aKey; aKeySize: cardinal);
begin
EnsureCryptoAPIAESProviderAvailable;
inherited Create(aKey,aKeySize); // check and set fKeySize[Bytes]
InternalSetMode;
fKeyHeader.bType := PLAINTEXTKEYBLOB;
fKeyHeader.bVersion := CUR_BLOB_VERSION;
case fKeySize of
128: fKeyHeader.aiKeyAlg := CALG_AES_128;
192: fKeyHeader.aiKeyAlg := CALG_AES_192;
256: fKeyHeader.aiKeyAlg := CALG_AES_256;
end;
fKeyHeader.dwKeyLength := fKeySizeBytes;
fKeyHeaderKey := fKey;
end;
destructor TAESAbstract_API.Destroy;
begin
if fKeyCryptoAPI<>nil then
CryptoAPI.DestroyKey(fKeyCryptoAPI);
FillCharFast(fKeyHeaderKey,sizeof(fKeyHeaderKey),0);
inherited;
end;
procedure TAESAbstract_API.EncryptDecrypt(BufIn, BufOut: pointer; Count: cardinal;
DoEncrypt: boolean);
var n: Cardinal;
begin
if Count=0 then
exit; // nothing to do
if fKeyCryptoAPI<>nil then begin
CryptoAPI.DestroyKey(fKeyCryptoAPI);
fKeyCryptoAPI := nil;
end;
if not CryptoAPI.ImportKey(CryptoAPIAESProvider,
@fKeyHeader,sizeof(fKeyHeader)+fKeySizeBytes,nil,0,fKeyCryptoAPI) then
raise ESynCrypto.CreateLastOSError('in CryptImportKey for %',[self]);
if not CryptoAPI.SetKeyParam(fKeyCryptoAPI,KP_IV,@fIV,0) then
raise ESynCrypto.CreateLastOSError('in CryptSetKeyParam(KP_IV) for %',[self]);
if not CryptoAPI.SetKeyParam(fKeyCryptoAPI,KP_MODE,@fInternalMode,0) then
raise ESynCrypto.CreateLastOSError('in CryptSetKeyParam(KP_MODE,%) for %',[fInternalMode,self]);
if BufOut<>BufIn then
MoveFast(BufIn^,BufOut^,Count);
n := Count and not AESBlockMod;
if DoEncrypt then begin
if not CryptoAPI.Encrypt(fKeyCryptoAPI,nil,false,0,BufOut,n,Count) then
raise ESynCrypto.CreateLastOSError('in Encrypt() for %',[self]);
end else
if not CryptoAPI.Decrypt(fKeyCryptoAPI,nil,false,0,BufOut,n) then
raise ESynCrypto.CreateLastOSError('in Decrypt() for %',[self]);
dec(Count,n);
if Count>0 then // remaining bytes will be XORed with the supplied IV
XorMemory(@PByteArray(BufOut)[n],@PByteArray(BufIn)[n],@fIV,Count);
end;
procedure TAESAbstract_API.Encrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
EncryptDecrypt(BufIn,BufOut,Count,true);
end;
procedure TAESAbstract_API.Decrypt(BufIn, BufOut: pointer; Count: cardinal);
begin
EncryptDecrypt(BufIn,BufOut,Count,false);
end;
{ TAESECB_API }
procedure TAESECB_API.InternalSetMode;
begin
fInternalMode := CRYPT_MODE_ECB;
end;
{ TAESCBC_API }
procedure TAESCBC_API.InternalSetMode;
begin
fInternalMode := CRYPT_MODE_CBC;
end;
{ TAESCFB_API }
procedure TAESCFB_API.InternalSetMode;
begin
raise ESynCrypto.CreateUTF8('%: CRYPT_MODE_CFB does not work',[self]);
fInternalMode := CRYPT_MODE_CFB;
end;
{ TAESOFB_API }
procedure TAESOFB_API.InternalSetMode;
begin
raise ESynCrypto.CreateUTF8('%: CRYPT_MODE_OFB not implemented by PROV_RSA_AES',[self]);
fInternalMode := CRYPT_MODE_OFB;
end;
{$endif USE_PROV_RSA_AES}
{$endif MSWINDOWS}
{ TAESLocked }
destructor TAESLocked.Destroy;
begin
inherited Destroy;
fAES.Done; // mandatory for Padlock - also fill AES buffer with 0 for safety
end;
{ TAESPRNG }
constructor TAESPRNG.Create(PBKDF2Rounds, ReseedAfterBytes, AESKeySize: integer);
begin
inherited Create;
if PBKDF2Rounds<2 then
PBKDF2Rounds := 2;
fSeedPBKDF2Rounds := PBKDF2Rounds;
fSeedAfterBytes := ReseedAfterBytes;
fAESKeySize := AESKeySize;
Seed;
end;
procedure FillSystemRandom(Buffer: PByteArray; Len: integer; AllowBlocking: boolean);
var fromos: boolean;
i: integer;
{$ifdef LINUX}
dev: integer;
{$endif}
{$ifdef MSWINDOWS}
prov: HCRYPTPROV;
{$endif}
tmp: array[byte] of byte;
begin
fromos := false;
{$ifdef LINUX}
dev := FileOpen('/dev/urandom',fmOpenRead);
if (dev<=0) and AllowBlocking then
dev := FileOpen('/dev/random',fmOpenRead);
if dev>0 then
try
i := Len;
if i>32 then
i := 32; // up to 256 bits - see "man urandom" Usage paragraph
fromos := (FileRead(dev,Buffer[0],i)=i) and (Len<=32); // will XOR up to Len
finally
FileClose(dev);
end;
{$endif LINUX}
{$ifdef MSWINDOWS}
if CryptoAPI.Available then
if CryptoAPI.AcquireContextA(prov,nil,nil,PROV_RSA_FULL,CRYPT_VERIFYCONTEXT) then begin
fromos := CryptoAPI.GenRandom(prov,len,Buffer);
CryptoAPI.ReleaseContext(prov,0);
end;
{$endif MSWINDOWS}
if fromos then
exit;
i := Len;
repeat // call Random32() (=RdRand32 or Lecuyer) as fallback/padding
SynCommons.FillRandom(@tmp,SizeOf(tmp) shr 2);
if i<=SizeOf(tmp) then begin
XorMemory(@Buffer^[Len-i],@tmp,i);
break;
end;
XorMemoryPtrInt(@Buffer^[Len-i],@tmp,SizeOf(tmp) shr {$ifdef CPU32}2{$else}3{$endif});
dec(i,SizeOf(tmp));
until false;
end;
{$ifdef DELPHI5OROLDER} // not defined in SysUtils.pas
function CreateGuid(out guid: TGUID): HResult; stdcall;
external 'ole32.dll' name 'CoCreateGuid';
{$endif}
class function TAESPRNG.GetEntropy(Len: integer; SystemOnly: boolean): RawByteString;
var ext: TSynExtended;
data: THash512Rec;
fromos, version: RawByteString;
sha3: TSHA3;
procedure sha3update;
var g: TGUID;
begin
SynCommons.FillRandom(@data.Hi,8); // QueryPerformanceCounter+8*Random32
sha3.Update(@data,sizeof(data));
CreateGUID(g); // not random, but genuine (at least on Windows)
sha3.Update(@g,sizeof(g));
end;
begin
QueryPerformanceCounter(data.d0); // data.d1 = some bytes on stack
try
// retrieve some initial entropy from OS
SetLength(fromos,Len);
FillSystemRandom(pointer(fromos),len,{allowblocking=}SystemOnly);
if SystemOnly then begin
result := fromos;
fromos := '';
exit;
end;
// xor some explicit entropy - it won't hurt
sha3.Init(SHAKE_256); // used in XOF mode for variable-length output
sha3update;
data.h1 := ExeVersion.Hash.b;
version := RecordSave(ExeVersion,TypeInfo(TExeVersion));
sha3.Update(version); // exe and host/user info
ext := NowUTC;
sha3.Update(@ext,sizeof(ext));
sha3update;
ext := Random; // why not?
sha3.Update(@ext,sizeof(ext));
data.i0 := integer(HInstance); // override data.d0d1/h0
data.i1 := integer(GetCurrentThreadId);
data.i2 := integer(MainThreadID);
data.i3 := integer(UnixMSTimeUTCFast);
SleepHiRes(0); // force non deterministic time shift
sha3update;
sha3.Update(OSVersionText);
sha3.Update(@SystemInfo,sizeof(SystemInfo));
result := sha3.Cypher(fromos); // = XOR entropy using SHA-3 in XOF mode
finally
sha3.Done;
FillZero(fromos);
end;
end;
procedure TAESPRNG.Seed;
var key: THash512Rec;
entropy: RawByteString;
begin
try
entropy := GetEntropy(128); // 128 bytes is the HMAC_SHA512 key block size
PBKDF2_HMAC_SHA512(entropy,ExeVersion.User,fSeedPBKDF2Rounds,key.b);
fSafe^.Lock;
try
fAES.EncryptInit(key.Lo,fAESKeySize);
crcblocks(@fCTR,@key.Hi,2);
fBytesSinceSeed := 0;
finally
fSafe^.UnLock;
end;
finally
FillZero(key.b); // avoid the key appear in clear on stack
FillZero(entropy);
end;
end;
procedure TAESPRNG.IncrementCTR;
begin
{$ifdef CPU64}
inc(fCTR.Lo);
if fCTR.Lo=0 then
inc(fCTR.Hi);
{$else}
inc(fCTR.i0);
if fCTR.i0=0 then begin
inc(fCTR.i1);
if fCTR.i1=0 then begin
inc(fCTR.i2);
if fCTR.i2=0 then
inc(fCTR.i3);
end;
end;
{$endif}
end;
procedure TAESPRNG.FillRandom(out Block: TAESBlock);
begin
if fBytesSinceSeed>fSeedAfterBytes then
Seed;
fSafe^.Lock;
TAESContext(fAES.Context).DoBlock(fAES.Context,fCTR.b,Block);
IncrementCTR;
inc(fBytesSinceSeed,SizeOf(Block));
inc(fTotalBytes,SizeOf(Block));
fSafe^.UnLock;
end;
procedure TAESPRNG.FillRandom(out Buffer: THash256);
begin
FillRandom(@Buffer,sizeof(Buffer));
end;
procedure TAESPRNG.FillRandom(Buffer: pointer; Len: integer);
var buf: ^TAESBlock absolute Buffer;
rnd: TAESBLock;
i: integer;
begin
if Len<=0 then
exit;
if fBytesSinceSeed>fSeedAfterBytes then
Seed;
fSafe^.Lock;
for i := 1 to Len shr 4 do begin
TAESContext(fAES.Context).DoBlock(fAES.Context,fCTR.b,buf^);
IncrementCTR;
inc(buf);
end;
inc(fBytesSinceSeed,Len);
inc(fTotalBytes,Len);
Len := Len and AESBlockMod;
if Len>0 then begin
TAESContext(fAES.Context).DoBlock(fAES.Context,fCTR.b,rnd);
IncrementCTR;
MoveFast(rnd,buf^,Len);
end;
fSafe^.UnLock;
end;
function TAESPRNG.FillRandom(Len: integer): RawByteString;
begin
SetString(result,nil,Len);
FillRandom(pointer(result),Len);
end;
function TAESPRNG.FillRandomBytes(Len: integer): TBytes;
begin
if Len<>length(result) then
result := nil;
SetLength(result,Len);
FillRandom(pointer(result),Len);
end;
function TAESPRNG.FillRandomHex(Len: integer): RawUTF8;
var bin: pointer;
begin
FastSetString(result,nil,Len*2);
if Len=0 then
exit;
bin := @PByteArray(result)[Len]; // temporary store random bytes at the end
FillRandom(bin,Len);
SynCommons.BinToHex(bin,pointer(result),Len);
end;
function TAESPRNG.Random32: cardinal;
var block: THash128Rec;
begin
FillRandom(block.b);
result := block.c0; // no need to XOR with c1, c2, c3 with a permutation algo
end;
function TAESPRNG.Random32(max: cardinal): cardinal;
var block: THash128Rec;
begin
FillRandom(block.b);
result := (Qword(block.c0)*max) shr 32; // no need to XOR with c1, c2, c3
end;
function TAESPRNG.Random64: QWord;
var block: THash128Rec;
begin
FillRandom(block.b);
result := block.L; // no need to XOR with H
end;
function Hash128ToExt({$ifdef FPC}constref{$else}const{$endif} r: THash128): TSynExtended;
const
COEFF64: TSynExtended = (1.0/$80000000)/$100000000; // 2^-63
begin
result := (THash128Rec(r).Lo and $7fffffffffffffff)*COEFF64;
end;
function Hash128ToDouble({$ifdef FPC}constref{$else}const{$endif} r: THash128): double;
const
COEFF64: double = (1.0/$80000000)/$100000000; // 2^-63
begin
result := (THash128Rec(r).Lo and $7fffffffffffffff)*COEFF64;
end;
function Hash128ToSingle({$ifdef FPC}constref{$else}const{$endif} r: THash128): double;
const
COEFF64: single = (1.0/$80000000)/$100000000; // 2^-63
begin
result := (THash128Rec(r).Lo and $7fffffffffffffff)*COEFF64;
end;
function TAESPRNG.RandomExt: TSynExtended;
var block: THash128;
begin
FillRandom(block);
result := Hash128ToExt(block);
end;
function TAESPRNG.RandomDouble: double;
var block: THash128;
begin
FillRandom(block);
result := Hash128ToDouble(block);
end;
function TAESPRNG.RandomPassword(Len: integer): RawUTF8;
const CHARS: array[0..127] of AnsiChar =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'+
':bcd.fgh(jklmn)pqrst?vwxyz+BCD%FGH!JKLMN/PQRST@VWX#Z$.:()?%!-+*/@#';
var i: integer;
haspunct: boolean;
P: PAnsiChar;
begin
repeat
result := FillRandom(Len);
haspunct := false;
P := pointer(result);
for i := 1 to Len do begin
P^ := CHARS[ord(P^) mod sizeof(CHARS)];
if not haspunct and
not (ord(P^) in [ord('A')..ord('Z'),ord('a')..ord('z'),ord('0')..ord('9')]) then
haspunct := true;
inc(P);
end;
until (Len<=4) or (haspunct and (LowerCase(result)<>result));
end;
procedure SetMainAESPRNG;
begin
GlobalLock;
if MainAESPRNG=nil then
GarbageCollectorFreeAndNil(MainAESPRNG, TAESPRNG.Create);
GlobalUnLock;
end;
class function TAESPRNG.Main: TAESPRNG;
begin
if MainAESPRNG=nil then
SetMainAESPRNG;
result := MainAESPRNG;
end;
procedure AFDiffusion(buf,rnd: pointer; size: cardinal);
var sha: TSHA256;
dig: TSHA256Digest;
last, iv: cardinal;
i: integer;
begin
XorMemory(buf,rnd,size);
sha.Init;
last := size div SizeOf(dig);
for i := 0 to last-1 do begin
iv := bswap32(i); // host byte order independent hash IV (as in TKS1/LUKS)
sha.Update(@iv,SizeOf(iv));
sha.Update(buf,SizeOf(dig));
sha.Final(PSHA256Digest(buf)^);
inc(PByte(buf),SizeOf(dig));
end;
dec(size,last*SizeOf(dig));
if size=0 then
exit;
iv := bswap32(last);
sha.Update(@iv,SizeOf(iv));
sha.Update(buf,size);
sha.Final(dig);
MoveSmall(@dig,buf,size);
end;
function TAESPRNG.AFSplit(const Buffer; BufferBytes, StripesCount: integer): RawByteString;
var dst: pointer;
tmp: TByteDynArray;
i: integer;
begin
result := '';
if self<>nil then
SetLength(result,BufferBytes*(StripesCount+1));
if result='' then
exit;
dst := pointer(result);
SetLength(tmp,BufferBytes);
for i := 1 to StripesCount do begin
FillRandom(dst,BufferBytes);
AFDiffusion(pointer(tmp),dst,BufferBytes);
inc(PByte(dst),BufferBytes);
end;
XorMemory(dst,@Buffer,pointer(tmp),BufferBytes);
end;
function TAESPRNG.AFSplit(const Buffer: RawByteString; StripesCount: integer): RawByteString;
begin
result := AFSplit(pointer(Buffer)^,length(Buffer),StripesCount);
end;
class function TAESPRNG.AFUnsplit(const Split: RawByteString;
out Buffer; BufferBytes: integer): boolean;
var len: cardinal;
i: integer;
src: pointer;
tmp: TByteDynArray;
begin
len := length(Split);
result := (len<>0) and (len mod cardinal(BufferBytes)=0);
if not result then
exit;
src := pointer(Split);
SetLength(tmp,BufferBytes);
for i := 2 to len div cardinal(BufferBytes) do begin
AFDiffusion(pointer(tmp),src,BufferBytes);
inc(PByte(src),BufferBytes);
end;
XorMemory(@Buffer,src,pointer(tmp),BufferBytes);
end;
class function TAESPRNG.AFUnsplit(const Split: RawByteString;
StripesCount: integer): RawByteString;
var len: cardinal;
begin
result := '';
len := length(Split);
if (len=0) or (len mod cardinal(StripesCount+1)<>0) then
exit;
len := len div cardinal(StripesCount+1);
SetLength(result,len);
if not AFUnsplit(Split,pointer(result)^,len) then
result := '';
end;
class procedure TAESPRNG.Fill(Buffer: pointer; Len: integer);
begin
Main.FillRandom(Buffer,Len);
end;
class procedure TAESPRNG.Fill(out Block: TAESBlock);
begin
Main.FillRandom(Block);
end;
class procedure TAESPRNG.Fill(out Block: THash256);
begin
Main.FillRandom(Block);
end;
class function TAESPRNG.Fill(Len: integer): RawByteString;
begin
result := Main.FillRandom(Len);
end;
class function TAESPRNG.Bytes(Len: integer): TBytes;
begin
result := Main.FillRandomBytes(Len);
end;
{ TAESPRNGSystem }
constructor TAESPRNGSystem.Create;
begin
inherited Create(0,0);
end;
procedure TAESPRNGSystem.FillRandom(out Block: TAESBlock);
begin
FillRandom(@Block,sizeof(Block));
end;
procedure TAESPRNGSystem.FillRandom(Buffer: pointer; Len: integer);
begin
FillSystemRandom(Buffer,Len,false);
end;
procedure TAESPRNGSystem.Seed;
begin // do nothing
end;
{ TRC4 }
procedure TRC4.Init(const aKey; aKeyLen: integer);
var i,k: integer;
j,tmp: PtrInt;
begin
if aKeyLen<=0 then
raise ESynCrypto.CreateUTF8('TRC4.Init(invalid aKeyLen=%)',[aKeyLen]);
dec(aKeyLen);
for i := 0 to high(state) do
state[i] := i;
j := 0;
k := 0;
for i := 0 to high(state) do begin
j := (j+state[i]+TByteArray(aKey)[k]) and $ff;
tmp := state[i];
state[i] := state[j];
state[j] := tmp;
if k>=aKeyLen then // avoid slow mod operation within loop
k := 0 else
inc(k);
end;
currI := 0;
currJ := 0;
end;
procedure TRC4.InitSHA3(const aKey; aKeyLen: integer);
var sha: TSHA3;
dig: array[byte] of byte; // max RC4 state size is 256 bytes
begin
sha.Full(SHAKE_128,@aKey,aKeyLen,@dig,SizeOf(dig)shl 3); // XOF mode
Init(dig,SizeOf(dig));
FillCharFast(dig,SizeOf(dig),0);
Drop(3072);
end;
procedure TRC4.EncryptBuffer(BufIn, BufOut: PByte; Count: cardinal);
var i,j,ki,kj: PtrInt;
by4: array[0..3] of byte;
begin
i := currI;
j := currJ;
while Count>3 do begin
dec(Count,4);
i := (i+1) and $ff;
ki := State[i];
j := (j+ki) and $ff;
kj := (ki+State[j]) and $ff;
State[i] := State[j];
i := (i+1) and $ff;
State[j] := ki;
ki := State[i];
by4[0] := State[kj];
j := (j+ki) and $ff;
kj := (ki+State[j]) and $ff;
State[i] := State[j];
i := (i+1) and $ff;
State[j] := ki;
by4[1] := State[kj];
ki := State[i];
j := (j+ki) and $ff;
kj := (ki+State[j]) and $ff;
State[i] := State[j];
i := (i+1) and $ff;
State[j] := ki;
by4[2] := State[kj];
ki := State[i];
j := (j+ki) and $ff;
kj := (ki+State[j]) and $ff;
State[i] := State[j];
State[j] := ki;
by4[3] := State[kj];
PCardinal(BufOut)^ := PCardinal(BufIn)^ xor cardinal(by4);
inc(BufIn,4);
inc(BufOut,4);
end;
while Count>0 do begin
dec(Count);
i := (i+1) and $ff;
ki := State[i];
j := (j+ki) and $ff;
kj := (ki+State[j]) and $ff;
State[i] := State[j];
State[j] := ki;
BufOut^ := BufIn^ xor State[kj];
inc(BufIn);
inc(BufOut);
end;
currI := i;
currJ := j;
end;
procedure TRC4.Encrypt(const BufIn; var BufOut; Count: cardinal);
begin
EncryptBuffer(@BufIn,@BufOut,Count);
end;
procedure TRC4.Drop(Count: cardinal);
var i,j,ki: PtrInt;
begin
i := currI;
j := currJ;
while Count>0 do begin
dec(Count);
i := (i+1) and $ff;
ki := state[i];
j := (j+ki) and $ff;
state[i] := state[j];
state[j] := ki;
end;
currI := i;
currJ := j;
end;
function RC4SelfTest: boolean;
const
Key: array[0..4] of byte = ($61,$8A,$63,$D2,$FB);
InDat: array[0..4] of byte = ($DC,$EE,$4C,$F9,$2C);
OutDat: array[0..4] of byte = ($F1,$38,$29,$C9,$DE);
Test1: array[0..7] of byte = ($01,$23,$45,$67,$89,$ab,$cd,$ef);
Res1: array[0..7] of byte = ($75,$b7,$87,$80,$99,$e0,$c5,$96);
Key2: array[0..3] of byte = ($ef,$01,$23,$45);
Test2: array[0..9] of byte = (0,0,0,0,0,0,0,0,0,0);
Res2: array[0..9] of byte = ($d6,$a1,$41,$a7,$ec,$3c,$38,$df,$bd,$61);
var RC4: TRC4;
Dat: array[0..9] of byte;
Backup: TRC4;
begin
RC4.Init(Test1,8);
RC4.Encrypt(Test1,Dat,8);
result := CompareMem(@Dat,@Res1,sizeof(Res1));
RC4.Init(Key2,4);
RC4.Encrypt(Test2,Dat,10);
result := result and CompareMem(@Dat,@Res2,sizeof(Res2));
RC4.Init(Key,sizeof(Key));
RC4.Encrypt(InDat,Dat,sizeof(InDat));
result := result and CompareMem(@Dat,@OutDat,sizeof(OutDat));
RC4.Init(Key,sizeof(Key));
Backup := RC4;
RC4.Encrypt(InDat,Dat,sizeof(InDat));
result := result and CompareMem(@Dat,@OutDat,sizeof(OutDat));
RC4 := Backup;
RC4.Encrypt(InDat,Dat,sizeof(InDat));
result := result and CompareMem(@Dat,@OutDat,sizeof(OutDat));
RC4 := Backup;
RC4.Encrypt(OutDat,Dat,sizeof(InDat));
result := result and CompareMem(@Dat,@InDat,sizeof(OutDat));
end;
procedure CompressShaAesSetKey(const Key: RawByteString; AesClass: TAESAbstractClass);
begin
if Key='' then
FillZero(CompressShaAesKey) else
SHA256Weak(Key,CompressShaAesKey);
end;
function CompressShaAes(var DataRawByteString; Compress: boolean): AnsiString;
var Data: RawByteString absolute DataRawByteString;
begin
if (Data<>'') and (CompressShaAesClass<>nil) then
try
with CompressShaAesClass.Create(CompressShaAesKey,256) do
try
if Compress then begin
CompressSynLZ(Data,true);
Data := EncryptPKCS7(Data,{IVAtBeginning=}true);
end else begin
Data := DecryptPKCS7(Data,{IVAtBeginning=}true);
if CompressSynLZ(Data,false)='' then begin
result := '';
exit; // invalid content
end;
end;
finally
Free;
end;
except
on Exception do begin // e.g. ESynCrypto in DecryptPKCS7(Data)
result := '';
exit; // invalid content
end;
end;
result := 'synshaaes'; // mark success
end;
{ THash128History }
procedure THash128History.Init(size, maxsize: integer);
begin
Depth := maxsize;
SetLength(Previous,size);
Count := 0;
Index := 0;
end;
function THash128History.Exists(const hash: THash128): boolean;
begin
if Count = 0 then
result := false else
result := Hash128Index(pointer(Previous),Count,@hash)>=0;
end;
function THash128History.Add(const hash: THash128): boolean;
var n: integer;
begin
result := Hash128Index(pointer(Previous),Count,@hash)<0;
if not result then
exit;
Previous[Index].b := hash;
inc(Index);
if Index>=length(Previous) then
if Index=Depth then
Index := 0 else begin
n := NextGrow(Index);
if n>=Depth then
n := Depth;
SetLength(Previous,n);
end;
if Count<Depth then
inc(Count);
end;
{$ifdef MSWINDOWS}
type
{$ifdef FPC}
{$PACKRECORDS C} // mandatory under Win64
{$endif}
DATA_BLOB = record
cbData: DWORD;
pbData: PAnsiChar;
end;
PDATA_BLOB = ^DATA_BLOB;
{$ifdef FPC}
{$PACKRECORDS DEFAULT}
{$endif}
const
CRYPTPROTECT_UI_FORBIDDEN = $1;
CRYPTDLL = 'Crypt32.dll';
function CryptProtectData(const DataIn: DATA_BLOB; szDataDescr: PWideChar;
OptionalEntropy: PDATA_BLOB; Reserved, PromptStruct: Pointer; dwFlags: DWORD;
var DataOut: DATA_BLOB): BOOL; stdcall; external CRYPTDLL name 'CryptProtectData';
function CryptUnprotectData(const DataIn: DATA_BLOB; szDataDescr: PWideChar;
OptionalEntropy: PDATA_BLOB; Reserved, PromptStruct: Pointer; dwFlags: DWORD;
var DataOut: DATA_BLOB): Bool; stdcall; external CRYPTDLL name 'CryptUnprotectData';
function CryptDataForCurrentUserDPAPI(const Data,AppSecret: RawByteString; Encrypt: boolean): RawByteString;
var src,dst,ent: DATA_BLOB;
e: PDATA_BLOB;
ok: boolean;
begin
src.pbData := pointer(Data);
src.cbData := length(Data);
if AppSecret<>'' then begin
ent.pbData := pointer(AppSecret);
ent.cbData := length(AppSecret);
e := @ent;
end else
e := nil;
if Encrypt then
ok := CryptProtectData(src,nil,e,nil,nil,CRYPTPROTECT_UI_FORBIDDEN,dst) else
ok := CryptUnprotectData(src,nil,e,nil,nil,CRYPTPROTECT_UI_FORBIDDEN,dst);
if ok then begin
SetString(result,dst.pbData,dst.cbData);
LocalFree(HLOCAL(dst.pbData));
end else
result := '';
end;
{$endif MSWINDOWS}
var
__h: THash256;
__hmac: THMAC_SHA256; // initialized from CryptProtectDataEntropy salt
procedure read__h__hmac;
var keyfile: TFileName;
instance: THash256;
key,key2,appsec: RawByteString;
begin
__hmac.Init(@CryptProtectDataEntropy,32);
SetString(appsec,PAnsiChar(@CryptProtectDataEntropy),32);
PBKDF2_HMAC_SHA256(appsec,ExeVersion.User,100,instance);
FillZero(appsec);
appsec := BinToBase64URI(@instance,15); // local file has 21 chars length
FormatString({$ifdef MSWINDOWS}'%_%'{$else}'%.syn-%'{$endif},
[GetSystemPath(spUserData),appsec], string(keyfile)); // .* files are hidden under Linux
SetString(appsec,PAnsiChar(@instance[15]),17); // use remaining bytes as key
try
key := StringFromFile(keyfile);
if key<>'' then begin
try
key2 := TAESCFB.SimpleEncrypt(key,appsec,false,true);
except
key2 := ''; // handle decryption error
end;
FillZero(key);
{$ifdef MSWINDOWS}
key := CryptDataForCurrentUserDPAPI(key2,appsec,false);
{$else}
key := key2;
{$endif}
if TAESPRNG.AFUnsplit(key,__h,sizeof(__h)) then
exit; // successfully extracted secret key in __h
end;
if FileExists(keyfile) then // allow rewrite of invalid local file
{$ifdef MSWINDOWS}
SetFileAttributes(pointer(keyfile),FILE_ATTRIBUTE_NORMAL);
{$else}
{$ifdef FPC}fpchmod{$else}chmod{$endif}(pointer(keyfile),S_IRUSR or S_IWUSR);
{$endif}
TAESPRNG.Main.FillRandom(__h);
key := TAESPRNG.Main.AFSplit(__h,sizeof(__h),126);
{$ifdef MSWINDOWS} // 4KB local file, DPAPI-cyphered but with no DPAPI BLOB layout
key2 := CryptDataForCurrentUserDPAPI(key,appsec,true);
FillZero(key);
{$else} // 4KB local chmod 400 hidden file in $HOME folder under Linux/POSIX
key2 := key;
{$endif}
key := TAESCFB.SimpleEncrypt(key2,appsec,true,true);
if not FileFromString(key,keyfile) then
ESynCrypto.CreateUTF8('Unable to write %',[keyfile]);
{$ifdef MSWINDOWS}
SetFileAttributes(pointer(keyfile),FILE_ATTRIBUTE_HIDDEN or FILE_ATTRIBUTE_READONLY);
{$else}
{$ifdef FPC}fpchmod{$else}chmod{$endif}(pointer(keyfile),S_IRUSR);
{$endif}
finally
FillZero(key);
FillZero(key2);
FillZero(appsec);
FillZero(instance);
end;
end;
function CryptDataForCurrentUser(const Data,AppSecret: RawByteString; Encrypt: boolean): RawByteString;
var hmac: THMAC_SHA256;
secret: THash256;
begin
result := '';
if Data='' then
exit;
if IsZero(__h) then
read__h__hmac;
try
hmac := __hmac; // thread-safe reuse of CryptProtectDataEntropy salt
hmac.Update(AppSecret);
hmac.Update(__h);
hmac.Done(secret);
result := TAESCFBCRC.MACEncrypt(Data,secret,Encrypt);
finally
FillZero(secret);
end;
end;
{ TProtocolNone }
function TProtocolNone.ProcessHandshake(
const MsgIn: RawUTF8; out MsgOut: RawUTF8): TProtocolResult;
begin
result := sprUnsupported;
end;
function TProtocolNone.Decrypt(const aEncrypted: RawByteString;
out aPlain: RawByteString): TProtocolResult;
begin
aPlain := aEncrypted;
result := sprSuccess;
end;
procedure TProtocolNone.Encrypt(const aPlain: RawByteString;
out aEncrypted: RawByteString);
begin
aEncrypted := aPlain;
end;
function TProtocolNone.Clone: IProtocol;
begin
result := TProtocolNone.Create;
end;
{ TProtocolAES }
constructor TProtocolAES.Create(aClass: TAESAbstractClass; const aKey;
aKeySize: cardinal; aIVReplayAttackCheck: TAESIVReplayAttackCheck);
begin
inherited Create;
fAES[false] := aClass.Create(aKey,aKeySize);
fAES[false].IVReplayAttackCheck := aIVReplayAttackCheck;
fAES[true] := fAES[false].Clone;
end;
constructor TProtocolAES.CreateFrom(aAnother: TProtocolAES);
begin
inherited Create;
fAES[false] := aAnother.fAES[false].Clone;
fAES[true] := fAES[false].Clone;
end;
destructor TProtocolAES.Destroy;
begin
fAES[false].Free;
fAES[true].Free;
inherited Destroy;
end;
function TProtocolAES.ProcessHandshake(
const MsgIn: RawUTF8; out MsgOut: RawUTF8): TProtocolResult;
begin
result := sprUnsupported;
end;
function TProtocolAES.Decrypt(const aEncrypted: RawByteString;
out aPlain: RawByteString): TProtocolResult;
begin
fSafe.Lock;
try
try
aPlain := fAES[false].DecryptPKCS7(aEncrypted,{iv=}true,{raise=}false);
if aPlain='' then
result := sprBadRequest else
result := sprSuccess;
except
result := sprInvalidMAC;
end;
finally
fSafe.UnLock;
end;
end;
procedure TProtocolAES.Encrypt(const aPlain: RawByteString;
out aEncrypted: RawByteString);
begin
fSafe.Lock;
try
aEncrypted := fAES[true].EncryptPKCS7(aPlain,{IVAtBeginning=}true);
finally
fSafe.UnLock;
end;
end;
function TProtocolAES.Clone: IProtocol;
begin
result := TProtocolAESClass(ClassType).CreateFrom(self);
end;
{$ifndef NOVARIANTS}
{ TJWTAbstract }
constructor TJWTAbstract.Create(const aAlgorithm: RawUTF8;
aClaims: TJWTClaims; const aAudience: array of RawUTF8; aExpirationMinutes: integer;
aIDIdentifier: TSynUniqueIdentifierProcess; aIDObfuscationKey: RawUTF8);
begin
if aAlgorithm='' then
raise EJWTException.CreateUTF8('%.Create(algo?)',[self]);
inherited Create;
if high(aAudience)>=0 then begin
fAudience := TRawUTF8DynArrayFrom(aAudience);
include(aClaims,jrcAudience);
end;
if aExpirationMinutes>0 then begin
include(aClaims,jrcExpirationTime);
fExpirationSeconds := aExpirationMinutes*60;
end else
exclude(aClaims,jrcExpirationTime);
fAlgorithm := aAlgorithm;
fClaims := aClaims;
if jrcJwtID in aClaims then
fIDGen := TSynUniqueIdentifierGenerator.Create(aIDIdentifier,aIDObfuscationKey);
if fHeader='' then
FormatUTF8('{"alg":"%","typ":"JWT"}',[aAlgorithm],fHeader);
fHeaderB64 := BinToBase64URI(fHeader)+'.';
fCacheResults := [jwtValid];
end;
destructor TJWTAbstract.Destroy;
begin
fIDGen.Free;
fCache.Free;
inherited;
end;
const
JWT_MAXSIZE = 4096; // coherent with HTTP headers limitations
function TJWTAbstract.Compute(const DataNameValue: array of const;
const Issuer, Subject, Audience: RawUTF8; NotBefore: TDateTime;
ExpirationMinutes: integer; Signature: PRawUTF8): RawUTF8;
var payload, headpayload, signat: RawUTF8;
begin
result := '';
if self=nil then
exit;
payload := PayloadToJSON(DataNameValue,Issuer,Subject,Audience,NotBefore,ExpirationMinutes);
headpayload := fHeaderB64+BinToBase64URI(payload);
signat := ComputeSignature(headpayload);
result := headpayload+'.'+signat;
if length(result)>JWT_MAXSIZE then
raise EJWTException.CreateUTF8('%.Compute oversize: len=%',[self,length(result)]);
if Signature<>nil then
Signature^ := signat;
end;
function TJWTAbstract.ComputeAuthorizationHeader(const DataNameValue: array of const;
const Issuer, Subject, Audience: RawUTF8; NotBefore: TDateTime;
ExpirationMinutes: integer): RawUTF8;
begin
if self=nil then
result := '' else
result := 'Bearer '+
Compute(DataNameValue,Issuer,Subject,Audience,NotBefore,ExpirationMinutes);
end;
function TJWTAbstract.PayloadToJSON(const DataNameValue: array of const;
const Issuer, Subject, Audience: RawUTF8; NotBefore: TDateTime;
ExpirationMinutes: cardinal): RawUTF8;
procedure RaiseMissing(c: TJWTClaim);
begin
raise EJWTException.CreateUTF8('%.PayloadJSON: missing %', [self, ToText(c)^]);
end;
var payload: TDocVariantData;
begin
result := '';
payload.InitObject(DataNameValue,JSON_OPTIONS_FAST);
if jrcIssuer in fClaims then
if Issuer='' then
RaiseMissing(jrcIssuer) else
payload.AddValueFromText(JWT_CLAIMS_TEXT[jrcIssuer],Issuer,true);
if jrcSubject in fClaims then
if Subject='' then
RaiseMissing(jrcSubject) else
payload.AddValueFromText(JWT_CLAIMS_TEXT[jrcSubject],Subject,true);
if jrcAudience in fClaims then
if Audience='' then
RaiseMissing(jrcAudience) else
if Audience[1]='[' then
payload.AddOrUpdateValue(JWT_CLAIMS_TEXT[jrcAudience],_JsonFast(Audience)) else
payload.AddValueFromText(JWT_CLAIMS_TEXT[jrcAudience],Audience,true);
if jrcNotBefore in fClaims then
if NotBefore<=0 then
payload.AddOrUpdateValue(JWT_CLAIMS_TEXT[jrcNotBefore],UnixTimeUTC) else
payload.AddOrUpdateValue(JWT_CLAIMS_TEXT[jrcNotBefore],DateTimeToUnixTime(NotBefore));
if jrcIssuedAt in fClaims then
payload.AddOrUpdateValue(JWT_CLAIMS_TEXT[jrcIssuedAt],UnixTimeUTC);
if jrcExpirationTime in fClaims then begin
if ExpirationMinutes=0 then
ExpirationMinutes := fExpirationSeconds else
ExpirationMinutes := ExpirationMinutes*60;
payload.AddOrUpdateValue(JWT_CLAIMS_TEXT[jrcExpirationTime],UnixTimeUTC+ExpirationMinutes);
end;
if jrcJwtID in fClaims then
if joNoJwtIDGenerate in fOptions then begin
if payload.GetValueIndex(JWT_CLAIMS_TEXT[jrcJwtID])<0 then
exit; // not generated, but should be supplied
end else
payload.AddValueFromText(JWT_CLAIMS_TEXT[jrcJwtID],fIDGen.ToObfuscated(fIDGen.ComputeNew));
result := payload.ToJSON;
end;
procedure TJWTAbstract.SetCacheTimeoutSeconds(value: integer);
begin
fCacheTimeoutSeconds := value;
FreeAndNil(fCache);
if (value>0) and (fCacheResults<>[]) then
fCache := TSynDictionary.Create(TypeInfo(TRawUTF8DynArray),
TypeInfo(TJWTContentDynArray),false,value);
end;
procedure TJWTAbstract.Verify(const Token: RawUTF8; out JWT: TJWTContent;
ExcludedClaims: TJWTClaims);
var headpayload: RawUTF8;
signature: RawByteString;
fromcache: boolean;
begin
JWT.result := jwtNoToken;
if (self=nil) or (fCache=nil) then
fromcache := false else begin
fromcache := fCache.FindAndCopy(Token,JWT);
fCache.DeleteDeprecated;
end;
if not fromcache then
Parse(Token,JWT,headpayload,signature,ExcludedClaims);
if JWT.result in [jwtValid,jwtNotBeforeFailed] then
if CheckAgainstActualTimestamp(JWT) and not fromcache then
CheckSignature(headpayload,signature,JWT); // depending on the algorithm used
if not fromcache and (self<>nil) and (fCache<>nil) and (JWT.result in fCacheResults) then
fCache.Add(Token,JWT);
end;
function TJWTAbstract.Verify(const Token: RawUTF8): TJWTResult;
var jwt: TJWTContent;
begin
Verify(Token,jwt);
result := jwt.result;
end;
function TJWTAbstract.CheckAgainstActualTimestamp(var JWT: TJWTContent): boolean;
var nowunix, unix: cardinal;
begin
if [jrcExpirationTime,jrcNotBefore,jrcIssuedAt]*JWT.claims<>[] then begin
result := false;
nowunix := UnixTimeUTC; // validate against actual timestamp
if jrcExpirationTime in JWT.claims then
if not ToCardinal(JWT.reg[jrcExpirationTime],unix) or (nowunix>unix) then begin
JWT.result := jwtExpired;
exit;
end;
if jrcNotBefore in JWT.claims then
if not ToCardinal(JWT.reg[jrcNotBefore],unix) or (nowunix<unix) then begin
JWT.result := jwtNotBeforeFailed;
exit;
end;
if jrcIssuedAt in JWT.claims then // allow 1 minute time lap between nodes
if not ToCardinal(JWT.reg[jrcIssuedAt],unix) or (unix>nowunix+60) then begin
JWT.result := jwtInvalidIssuedAt;
exit;
end;
end;
result := true;
JWT.result := jwtValid;
end;
procedure TJWTAbstract.Parse(const Token: RawUTF8; var JWT: TJWTContent;
out headpayload: RawUTF8; out signature: RawByteString; excluded: TJWTClaims);
var payloadend,j,toklen,c,cap,headerlen,len,a: integer;
P: PUTF8Char;
N,V: PUTF8Char;
wasString: boolean;
EndOfObject: AnsiChar;
claim: TJWTClaim;
requiredclaims: TJWTClaims;
id: TSynUniqueIdentifierBits;
value: variant;
payload: RawUTF8;
head: array[0..1] of TValuePUTF8Char;
aud: TDocVariantData;
tok: PAnsiChar absolute Token;
begin
// 0. initialize parsing
Finalize(JWT.reg);
JWT.data.InitFast(0,dvObject); // custom claims
byte(JWT.claims) := 0;
word(JWT.audience) := 0;
toklen := length(Token);
if (toklen=0) or (self=nil) then begin
JWT.result := jwtNoToken;
exit;
end;
// 1. validate the header (including algorithm "alg" verification)
JWT.result := jwtInvalidAlgorithm;
if joHeaderParse in fOptions then begin // slower parsing
headerlen := PosExChar('.',Token);
if (headerlen=0) or (headerlen>512) then
exit;
Base64URIToBin(tok,headerlen-1,signature);
JSONDecode(pointer(signature),['alg','typ'],@head);
if not head[0].Idem(fAlgorithm) or
((head[1].Value<>nil) and not head[1].Idem('JWT')) then
exit;
end else begin // fast direct compare of fHeaderB64 (including "alg")
headerlen := length(fHeaderB64);
if (toklen<=headerlen) or not CompareMem(pointer(fHeaderB64),tok,headerlen) then
exit;
end;
// 2. extract the payload
JWT.result := jwtWrongFormat;
if toklen>JWT_MAXSIZE Then
exit;
payloadend := PosEx('.',Token,headerlen+1);
if (payloadend=0) or (payloadend-headerlen>2700) then
exit;
Base64URIToBin(tok+payloadend,toklen-payloadend,signature);
if (signature='') and (payloadend<>toklen) then
exit;
JWT.result := jwtInvalidPayload;
Base64URIToBin(tok+headerlen,payloadend-headerlen-1,RawByteString(payload));
if payload='' then
exit;
// 3. decode the payload into JWT.reg[]/JWT.claims (known) and JWT.data (custom)
P := GotoNextNotSpace(pointer(payload));
if P^<>'{' then
exit;
P := GotoNextNotSpace(P+1);
cap := JSONObjectPropCount(P);
if cap<0 then
exit;
requiredclaims := fClaims - excluded;
if cap>0 then
repeat
N := GetJSONPropName(P);
if N=nil then
exit;
V := GetJSONFieldOrObjectOrArray(P,@wasstring,@EndOfObject,true);
len := StrLen(N);
if (len=3) and (V<>nil) then begin
c := PInteger(N)^;
for claim := low(claim) to high(claim) do
if PInteger(JWT_CLAIMS_TEXT[claim])^=c then begin
if V^=#0 then
exit;
include(JWT.claims,claim);
if not(claim in fClaims) and not(joAllowUnexpectedClaims in fOptions) then begin
JWT.result := jwtUnexpectedClaim;
exit;
end;
FastSetString(JWT.reg[claim],V,StrLen(V));
if claim in requiredclaims then
case claim of
jrcJwtID:
if not(joNoJwtIDCheck in fOptions) then
if not fIDGen.FromObfuscated(JWT.reg[jrcJwtID],id.Value) or
(id.CreateTimeUnix<UNIXTIME_MINIMAL) then begin
JWT.result := jwtInvalidID;
exit;
end;
jrcAudience:
if JWT.reg[jrcAudience][1]='[' then begin
aud.InitJSON(JWT.reg[jrcAudience],JSON_OPTIONS_FAST);
if aud.Count=0 then
exit;
for j := 0 to aud.Count-1 do begin
a := FindRawUTF8(fAudience,VariantToUTF8(aud.Values[j]));
if a<0 then begin
JWT.result := jwtUnknownAudience;
if not (joAllowUnexpectedAudience in fOptions) then
exit;
end else
include(JWT.audience,a);
end;
aud.Clear;
end else begin
a := FindRawUTF8(fAudience,JWT.reg[jrcAudience]);
if a<0 then begin
JWT.result := jwtUnknownAudience;
if not (joAllowUnexpectedAudience in fOptions) then
exit;
end else
include(JWT.audience,a);
end;
end;
len := 0; // don't add to JWT.data
dec(cap);
break;
end;
if len=0 then
continue;
end;
GetVariantFromJSON(V,wasString,value,@JSON_OPTIONS[true],joDoubleInData in fOptions);
if JWT.data.Count=0 then
JWT.data.Capacity := cap;
JWT.data.AddValue(N,len,value)
until (EndOfObject='}') or (P=nil);
if JWT.data.Count>0 then
JWT.data.Capacity := JWT.data.Count;
if requiredclaims-JWT.claims<>[] then
JWT.result := jwtMissingClaim else begin
FastSetString(headpayload,tok,payloadend-1);
JWT.result := jwtValid;
end;
end;
function TJWTAbstract.VerifyAuthorizationHeader(const HttpAuthorizationHeader: RawUTF8;
out JWT: TJWTContent): boolean;
begin
if (cardinal(length(HttpAuthorizationHeader)-10)>4096) or
not IdemPChar(pointer(HttpAuthorizationHeader), 'BEARER ') then
JWT.result := jwtWrongFormat else
Verify(copy(HttpAuthorizationHeader,8,maxInt),JWT);
result := JWT.result=jwtValid;
end;
class function TJWTAbstract.VerifyPayload(const Token, ExpectedSubject, ExpectedIssuer,
ExpectedAudience: RawUTF8; Expiration: PUnixTime; Signature: PRawUTF8; Payload: PVariant;
IgnoreTime: boolean; NotBeforeDelta: TUnixTime): TJWTResult;
var P,B: PUTF8Char;
V: array[0..4] of TValuePUTF8Char;
now, time: PtrUInt;
text: RawUTF8;
begin
result := jwtInvalidAlgorithm;
B := pointer(Token);
P := PosChar(B,'.');
if P=nil then
exit;
if self<>TJWTAbstract then begin
text := Base64URIToBin(PAnsiChar(B),P-B);
if not IdemPropNameU(copy(ToText(self),5,10),JSONDecode(text,'alg')) then
exit;
end;
B := P+1;
P := PosChar(B,'.');
result := jwtInvalidSignature;
if P=nil then
exit;
result := jwtInvalidPayload;
text := Base64URIToBin(PAnsiChar(B),P-B);
if text='' then
exit;
if Payload<>nil then
_Json(text,Payload^,JSON_OPTIONS_FAST);
JSONDecode(pointer(text),['iss','aud','exp','nbf','sub'],@V,true);
result := jwtUnexpectedClaim;
if ((ExpectedSubject<>'') and not V[4].Idem(ExpectedSubject)) or
((ExpectedIssuer<>'') and not V[0].Idem(ExpectedIssuer)) then
exit;
result := jwtUnknownAudience;
if (ExpectedAudience<>'') and not V[1].Idem(ExpectedAudience) then
exit;
if Expiration<>nil then
Expiration^ := 0;
if (V[2].Value<>nil) or (V[3].Value<>nil) then begin
now := UnixTimeUTC;
if V[2].Value<>nil then begin
time := V[2].ToCardinal;
result := jwtExpired;
if not IgnoreTime and (now>time) then
exit;
if Expiration<>nil then
Expiration^ := time;
end;
if not IgnoreTime and (V[3].Value<>nil) then begin
time := V[3].ToCardinal;
result := jwtNotBeforeFailed;
if (time=0) or (now+PtrUInt(NotBeforeDelta)<time) then
exit;
end;
end;
inc(P);
if Signature<>nil then
FastSetString(Signature^,P,StrLen(P));
result := jwtValid;
end;
{ TJWTNone }
constructor TJWTNone.Create(aClaims: TJWTClaims;
const aAudience: array of RawUTF8; aExpirationMinutes: integer;
aIDIdentifier: TSynUniqueIdentifierProcess; aIDObfuscationKey: RawUTF8);
begin
fHeader := '{"alg":"none"}'; // "typ":"JWT" is optional, so we save a few bytes
inherited Create('none',aClaims,aAudience,aExpirationMinutes,
aIDIdentifier,aIDObfuscationKey);
end;
procedure TJWTNone.CheckSignature(const headpayload: RawUTF8; const signature: RawByteString;
var JWT: TJWTContent);
begin
if signature='' then // JWA defined empty string for "none" JWS
JWT.result := jwtValid else
JWT.result := jwtInvalidSignature;
end;
function TJWTNone.ComputeSignature(const headpayload: RawUTF8): RawUTF8;
begin
result := '';
end;
{ TJWTSynSignerAbstract }
constructor TJWTSynSignerAbstract.Create(const aSecret: RawUTF8;
aSecretPBKDF2Rounds: integer; aClaims: TJWTClaims; const aAudience: array of RawUTF8;
aExpirationMinutes: integer; aIDIdentifier: TSynUniqueIdentifierProcess;
aIDObfuscationKey: RawUTF8; aPBKDF2Secret: PHash512Rec);
var algo: TSignAlgo;
begin
algo := GetAlgo;
inherited Create(JWT_TEXT[algo],aClaims,aAudience,
aExpirationMinutes,aIDIdentifier,aIDObfuscationKey);
if (aSecret<>'') and (aSecretPBKDF2Rounds>0) then
fSignPrepared.Init(algo,aSecret,fHeaderB64,aSecretPBKDF2Rounds,aPBKDF2Secret) else
fSignPrepared.Init(algo,aSecret);
end;
procedure TJWTSynSignerAbstract.CheckSignature(const headpayload: RawUTF8;
const signature: RawByteString; var JWT: TJWTContent);
var signer: TSynSigner;
temp: THash512Rec;
begin
JWT.result := jwtInvalidSignature;
if length(signature)<>SignatureSize then
exit;
signer := fSignPrepared; // thread-safe re-use of prepared TSynSigner
signer.Update(pointer(headpayload),length(headpayload));
signer.Final(temp);
{ writeln('payload=',headpayload);
writeln('sign=',bintohex(@temp,SignatureSize));
writeln('expected=',bintohex(pointer(signature),SignatureSize)); }
if CompareMem(@temp,pointer(signature),SignatureSize) then
JWT.result := jwtValid;
end;
function TJWTSynSignerAbstract.ComputeSignature(const headpayload: RawUTF8): RawUTF8;
var signer: TSynSigner;
temp: THash512Rec;
begin
signer := fSignPrepared;
signer.Update(pointer(headpayload),length(headpayload));
signer.Final(temp);
result := BinToBase64URI(@temp,SignatureSize);
end;
destructor TJWTSynSignerAbstract.Destroy;
begin
FillCharFast(fSignPrepared,SizeOf(fSignPrepared),0);
inherited Destroy;
end;
{ TJWTHS256 }
function TJWTHS256.GetAlgo: TSignAlgo;
begin
result := saSha256;
end;
{ TJWTHS384 }
function TJWTHS384.GetAlgo: TSignAlgo;
begin
result := saSha384;
end;
{ TJWTHS512 }
function TJWTHS512.GetAlgo: TSignAlgo;
begin
result := saSha512;
end;
{ TJWTS3224 }
function TJWTS3224.GetAlgo: TSignAlgo;
begin
result := saSha3224;
end;
{ TJWTS3256 }
function TJWTS3256.GetAlgo: TSignAlgo;
begin
result := saSha3256;
end;
{ TJWTS3384 }
function TJWTS3384.GetAlgo: TSignAlgo;
begin
result := saSha3384;
end;
{ TJWTS3512 }
function TJWTS3512.GetAlgo: TSignAlgo;
begin
result := saSha3512;
end;
{ TJWTS3S128 }
function TJWTS3S128.GetAlgo: TSignAlgo;
begin
result := saSha3S128;
end;
{ TJWTS3S256 }
function TJWTS3S256.GetAlgo: TSignAlgo;
begin
result := saSha3S256;
end;
var
_TJWTResult: array[TJWTResult] of PShortString;
_TJWTClaim: array[TJWTClaim] of PShortString;
function ToText(res: TJWTResult): PShortString;
begin
result := _TJWTResult[res];
end;
function ToCaption(res: TJWTResult): string;
begin
GetCaptionFromTrimmed(_TJWTResult[res],result);
end;
function ToText(claim: TJWTClaim): PShortString;
begin
result := _TJWTClaim[claim];
end;
function ToText(claims: TJWTClaims): ShortString;
begin
GetSetNameShort(TypeInfo(TJWTClaims),claims,result);
end;
{$endif NOVARIANTS}
{$ifdef CRC32C_X64}
{ ISCSI CRC 32 Implementation with crc32 and pclmulqdq Instruction
Copyright(c) 2011-2015 Intel Corporation All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICESLOSS OF USE,
DATA, OR PROFITSOR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. }
{$ifdef FPC}
{$ifdef MSWINDOWS}
{$L crc32c64.obj}
{$else}
{$L static/x86_64-linux/crc32c64.o}
{$endif}
{$else}
{$L crc32c64.obj}
{$endif}
// defined in SynCrypto.pas, not in SynCommons.pas, to avoid .o/.obj dependencies
function crc32_iscsi_01(buf: PAnsiChar; len: PtrUInt; crc: cardinal): cardinal; {$ifdef FPC}cdecl;{$endif} external;
function crc32c_sse42_aesni(crc: PtrUInt; buf: PAnsiChar; len: PtrUInt): cardinal;
{$ifdef FPC}nostackframe; assembler; asm{$else}asm .noframe {$endif}
mov rax, crc
mov rcx, len
not eax
test buf, buf
jz @z
cmp len, 64
jb @sml
// our call: rcx/rdi=crc rdx/rsi=buf r8/rdx=len
// iscsi_01: rcx/rdi=buf rdx/rsi=len r8/rdx=crc
mov crc, buf
mov buf, len
mov len, rax
call crc32_iscsi_01
@z: not eax
ret
@sml: shr len, 3
jz @2
{$ifdef FPC} align 16
@s: crc32 rax, qword [buf] // hash 8 bytes per loop
{$else} @s: db $F2,$48,$0F,$38,$F1,$02 // circumvent Delphi inline asm compiler bug
{$endif}add buf, 8
dec len
jnz @s
@2: test cl, 4
jz @3
crc32 eax, dword ptr[buf]
add buf, 4
@3: test cl, 2
jz @1
crc32 eax, word ptr[buf]
add buf, 2
@1: test cl, 1
jz @0
crc32 eax, byte ptr[buf]
@0: not eax
end;
{$endif CRC32C_X64}
initialization
ComputeAesStaticTables;
{$ifdef USEPADLOCK}
PadlockInit;
{$endif USEPADLOCK}
{$ifdef CPUX64}
{$ifdef CRC32C_X64} // use SSE4.2+pclmulqdq instructions
if (cfSSE42 in CpuFeatures) and (cfAesNi in CpuFeatures) then
crc32c := @crc32c_sse42_aesni;
{$endif CRC32C_X64}
if cfSSE41 in CpuFeatures then begin // optimized Intel's sha256_sse4.asm
if K256AlignedStore='' then
GetMemAligned(K256AlignedStore,@K256,SizeOf(K256),K256Aligned);
if PtrUInt(K256Aligned) and 15<>0 then
K256AlignedStore := ''; // if not properly aligned -> fallback to pascal
end;
{$endif CPUX64}
TTextWriter.RegisterCustomJSONSerializerFromTextSimpleType(TypeInfo(TSignAlgo));
TTextWriter.RegisterCustomJSONSerializerFromText(TypeInfo(TSynSignerParams),
'algo:TSignAlgo secret,salt:RawUTF8 rounds:integer');
{$ifndef NOVARIANTS}
GetEnumNames(TypeInfo(TJWTResult),@_TJWTResult);
GetEnumNames(TypeInfo(TJWTClaim),@_TJWTClaim);
{$endif NOVARIANTS}
assert(sizeof(TMD5Buf)=sizeof(TMD5Digest));
assert(sizeof(TAESContext)=AESContextSize);
assert(AESContextSize<=300); // see synsqlite3.c KEYLENGTH
assert(sizeof(TSHAContext)=SHAContextSize);
assert(sizeof(TSHA3Context)=SHA3ContextSize);
assert(1 shl AESBlockShift=sizeof(TAESBlock));
assert(sizeof(TAESFullHeader)=sizeof(TAESBlock));
assert(sizeof(TAESIVCTR)=sizeof(TAESBlock));
assert(sizeof(TSHA256)=sizeof(TSHA1));
assert(sizeof(TSHA512)>sizeof(TSHA256));
assert(sizeof(TSHA3)>sizeof(TSHA512));
assert(sizeof(TSHA3)>sizeof(THMAC_SHA512));
finalization
{$ifdef USEPADLOCKDLL}
if PadLockLibHandle<>0 then
FreeLibrary(PadLockLibHandle); // same on Win+Linux, thanks to SysUtils
{$endif USEPADLOCKDLL}
FillZero(__h);
{$ifdef MSWINDOWS}
if CryptoAPI.Handle<>0 then begin
{$ifdef USE_PROV_RSA_AES}
if (CryptoAPIAESProvider<>nil) and (CryptoAPIAESProvider<>HCRYPTPROV_NOTTESTED) then
CryptoAPI.ReleaseContext(CryptoAPIAESProvider,0);
{$endif USE_PROV_RSA_AES}
FreeLibrary(CryptoAPI.Handle);
end;
{$endif MSWINDOWS}
end.