15572 lines
532 KiB
Plaintext
15572 lines
532 KiB
Plaintext
/// 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.
|