-
-
Notifications
You must be signed in to change notification settings - Fork 180
Expand file tree
/
Copy pathmormot.crypt.core.pas
More file actions
10644 lines (9757 loc) · 371 KB
/
Copy pathmormot.crypt.core.pas
File metadata and controls
10644 lines (9757 loc) · 371 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/// Framework Core Cryptographic Process (Hashing and Cypher)
// - this unit is a part of the Open Source Synopse mORMot framework 2,
// licensed under a MPL/GPL/LGPL three license - see LICENSE.md
unit mormot.crypt.core;
{
*****************************************************************************
High-Performance Cryptographic Features shared by all framework units
- Low-Level Memory Buffers Helper Functions
- 256-bit BigInt Low-Level Computation for ECC
- AES Encoding/Decoding with optimized asm and AES-NI/CLMUL support
- AES-256 Cryptographic Pseudorandom Number Generator (CSPRNG)
- SHA-2 SHA-3 Secure Hashing
- HMAC Authentication over SHA-256
- PBKDF2 Key Derivation over SHA-256 and SHA-3
- Digest/Hash to Hexadecimal Text Conversion
- Deprecated MD5 SHA-1 Algorithms
Validated against OpenSSL. Faster than OpenSSL on x86_64 (but AES-GCM).
*****************************************************************************
Original Copyright Notices of some Open Source implementations, included
with (deep) refactoring (other routines are our own coding):
- aes_pascal, keccak_pascal: (c) Wolfgang Ehrhardt under zlib license
- KeccakPermutationKernel MMX/i386: (c) Eric Grange
- Andy Polyakov's keccak1600-avx2.pl from the CRYPTOGAMS project
- MD5_386.asm: (c) Maxim Masiutin - Ritlabs, SRL
- sha512-x86: (c) Project Nayuki under MIT license
- sha512-x64sse4, sha256-sse4, crc32c64: (c) Intel Corporation w/ OS licence
Legal Notice: as stated by our LICENSE.md terms, make sure that you comply
to any restriction about the use of cryptographic software in your country.
}
interface
{$I ..\mormot.defines.inc}
uses
classes,
sysutils,
mormot.core.base,
mormot.core.os,
mormot.core.os.security, // low-level Windows Security API
mormot.core.unicode,
mormot.core.text,
mormot.core.rtti,
mormot.core.buffers;
type
/// class of Exceptions raised by this unit
ESynCrypto = class(ESynException);
{$ifdef ASMX64NOTPIC}
{$ifdef ASMAESNI} // compiler supports asm with aesenc/aesdec opcodes
{$define USEAESNI}
{$define USEAESNI64}
{$define USEAESNICTR} // 8x interleaved aesni
{$ifdef ASMX64AVX0} // Delphi x86_64 SSE asm is buggy before XE7
{$define USECLMUL} // pclmulqdq opcodes
{$define USEGCMAVX} // 8x interleaved aesni + pclmulqdq asm for AES-GCM
{$define USEAESNIHASH} // aesni+sse4.1 32-64-128 aeshash
{$endif ASMX64AVX0}
{$endif ASMAESNI}
{$ifdef OSWINDOWS}
{$define CRC32C_X64} // external crc32_iscsi_01 for win64/lin64
{$define SHA512_X64} // external sha512_sse4 for win64/lin64
{$endif OSWINDOWS}
{$ifdef OSLINUX}
{$define CRC32C_X64} // external crc32_iscsi_01.o for win64/lin64
{$define SHA512_X64} // external sha512_sse4.o for win64/lin64
{$endif OSLINUX}
{$endif ASMX64NOTPIC}
{$ifdef ASMX86NOTPIC} // our x86 asm requires global variables access
{$define USEAESNI}
{$define USEAESNI32}
{$ifdef ASMAESNI} // compiler supports asm with aesenc/aesdec opcodes
{$define USECLMUL} // pclmulqdq opcodes
{$define USEAESNIHASH} // aesni+sse4.1 32-64-128 aeshash
{$define USEAESNICTR} // 4x interleaved aesni
{$endif ASMAESNI}
{$ifdef OSWINDOWS}
{$define SHA512_X86} // external sha512-x86.o for win32/lin32
{$endif OSWINDOWS}
{$ifdef OSLINUX}
{$define SHA512_X86} // external sha512-x86.o for win32/lin32
{$endif OSLINUX}
{$endif ASMX86NOTPIC}
{$ifdef CPUAARCH64}
{$ifdef OSLINUXANDROID}
{$define USEARMCRYPTO}
// AARCH64 armv8.o / sha256armv8.o are only validated on Linux yet
// (it should work on other POSIX ABI, but was reported to fail)
{$endif OSLINUXANDROID}
{$endif CPUAARCH64}
{ ****************** Low-Level Memory Buffers Helper Functions }
/// apply the A = A XOR B operation to the supplied binary buffers of 16 bytes
procedure XorBlock16(A, B: PPtrIntArray);
{$ifdef HASINLINE}inline;{$endif} overload;
/// apply the B = A XOR C operation to the supplied binary buffers of 16 bytes
procedure XorBlock16(A, B, C: PPtrIntArray);
{$ifdef HASINLINE}inline;{$endif} overload;
/// logical XOR memory buffers with a 32-bit mask, as done e.g. during HMAC
// - fill all dst[] cardinals, by 128-bit chunks (e.g. last = 15 or 31):
// ! dst[i] := src[i] xor mask;
procedure Xor32By128(dst, src: PCardinalArray; last: PtrUInt; mask: cardinal);
/// logical "dst := dst XOR src" of 256-bit = 32 bytes
procedure Xor256(dst, src: PPtrIntArray);
{$ifdef CPU64} inline;{$endif}
/// logical "dst := dst XOR src" of 512-bit = 64 bytes - use SSE2 on Intel/AMD
procedure Xor512(dst, src: PPtrIntArray);
{$ifndef ASMINTEL} inline;{$endif}
/// efficient "dst := src" Move of 512-bit = 64 bytes - use SSE2 on Intel/AMD
procedure Move512(dst, src: PPtrIntArray);
{$ifndef ASMINTEL} inline;{$endif}
// little endian fast conversion of 160 bits = 5 integers values
// - use fast bswap asm in x86/x64 mode
procedure bswap160(s, d: PIntegerArray);
// little endian fast conversion of 256-bit = 8 integers = 32 bytes values
// - use fast bswap asm in x86/x64 mode
procedure bswap256(s, d: PIntegerArray);
/// low-level function able to derivate a 0..1 floating-point from 128-bit of data
// - used e.g. by TAesPrng.RandomExt
// - only the lower part of P^ will be used for derivation thanks to AES input
function Hash128ToExt(P: PHash128Rec): TSynExtended;
{$ifdef FPC} inline; {$endif} { Delphi has troubles inlining floats results }
/// low-level function able to derivate a [0..1) 64-bit floating-point from 128-bit of data
// - used e.g. by TAesPrng.RandomDouble
// - only the higher part of P^ will be used for derivation thanks to AES input
function Hash128ToDouble(P: PHash128Rec): double;
{$ifdef FPC} inline; {$endif}
/// low-level function able to derivate a [0..1) 32-bit floating-point from 128-bit of data
// - only the lower part of P^ will be used for derivation thanks to AES input
function Hash128ToSingle(P: PHash128Rec): single;
{$ifdef FPC} inline; {$endif}
/// entry point of the raw MD5 transform function - for low-level use
procedure RawMd5Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-1 transform function - for low-level use
procedure RawSha1Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-256 transform function - for low-level use
procedure RawSha256Compress(var Hash; Data: pointer);
/// entry point of the raw SHA-512 transform function - for low-level use
procedure RawSha512Compress(var Hash; Data: pointer);
type
/// the prototype of our SCrypt() raw function
TSCriptRaw = function(const Password: RawUtf8; const Salt: RawByteString;
N, R, P, DestLen: PtrUInt): RawByteString;
var
/// 32-bit truncation of GoLang runtime aeshash, using aesni opcode
// - just a wrapper around AesNiHash128() with proper 32-bit zeroing
// - Assigned(AesNiHash32) only if AES-NI and SSE 3 are available on this CPU
// - faster than any SSE4.2 crc32c function, with less collision
// - warning: the hashes will be consistent only during a process: at startup,
// AesNiHashAntiFuzzTable is computed to prevent attacks on forged input
// - DefaultHasher() is assigned to this function, when available on the CPU
AesNiHash32: THasher;
/// 64-bit aeshash as implemented in GoLang runtime, using aesni opcode
// - is the fastest and probably one of the safest non-cryptographic hash
// - just a wrapper around AesNiHash128() with proper 64-bit zeroing
// - Assigned(AesNiHash64) only if AES-NI and SSE 3 are available on this CPU
// - warning: the hashes will be consistent only during a process: at startup,
// AesNiHashAntiFuzzTable is computed to prevent attacks on forged input
// - DefaultHasher64() is assigned to this function, when available on the CPU
AesNiHash64: function(seed: QWord; data: pointer; len: PtrUInt): QWord;
/// 128-bit aeshash as implemented in GoLang runtime, using aesni opcode
// - access to the raw function implementing both AesNiHash64 and AesNiHash32
// - Assigned(AesNiHash128) only if AES-NI and SSE 3 are available on this CPU
// - warning: the hashes will be consistent only during a process: at startup,
// AesNiHashAntiFuzzTable is computed to prevent attacks on forged input
// - DefaultHasher128() is assigned to this function, when available on the CPU
AesNiHash128: procedure(hash: PHash128; data: pointer; len: PtrUInt);
/// BCrypt raw implementation function - injected by mormot.crypt.other.pas
// - Cost should be in range 4..31 and Salt '' or exactly 22 characters (128-bit)
// - returns e.g. '$2b$12$GhvMmNVjRW29ulnudl.LbuAnUtN/LRfe1JsBm1Xu6LE3059z5Tr8m'
BCrypt: function(const Password: RawUtf8; const Salt: RawUtf8 = '';
Cost: byte = 12; HashPos: PInteger = nil; PreSha256: boolean = false): RawUtf8;
/// SCrypt raw implementation function
// - injected by mormot.crypt.openssl.pas or by mormot.crypt.other.pas (slower)
// - as defined by http://www.tarsnap.com/scrypt.html and RFC 7914
// - for password storage and interactive login, consider SCryptHash() from
// this unit with default N=65536=2^16, R=8, P=2 (148ms and 64MB of RAM)
// - for local key derivation (e.g. file encryption) consider using this
// function directly with e.g. N=1048576=2^20, R=8, P=1 (1.23s and 1GB) to
// compute the binary encryption key
SCrypt: TSCriptRaw;
{ *************** 256-bit BigInt Low-Level Computation for ECC }
/// optimized 256-bit addition (with Intel/AMD asm) - used by ecc256r1
function _add256(out Output: THash256Rec; const Left, Right: THash256Rec): PtrUInt;
{$ifndef ASMINTEL} inline; {$endif}
/// optimized 256-bit substraction (with Intel/AMD asm) - used by ecc256r1
function _sub256(out Output: THash256Rec; const Left, Right: THash256Rec): PtrUInt;
{$ifndef ASMINTEL} inline; {$endif}
/// optimized 256-bit addition (with Intel/AMD asm) - used by ecc256r1
function _inc256(var Value: THash256Rec; const Added: THash256Rec): PtrUInt;
{$ifndef ASMINTEL} inline; {$endif}
/// optimized 256-bit substraction (with Intel/AMD asm) - used by ecc256r1
function _dec256(var Value: THash256Rec; const Subs: THash256Rec): PtrUInt;
{$ifndef ASMINTEL} inline; {$endif}
/// optimized 128-bit addition (with Intel/AMD asm) - used by ecc256r1
procedure _inc128(var Value: THash256Rec; var Added: THash128Rec);
{$ifndef ASMINTEL} inline; {$endif}
/// optimized 64-bit addition (with Intel/AMD asm) - used by ecc256r1
procedure _inc64(var Value: THash128Rec; var Added: QWord);
{$ifndef ASMINTEL} inline; {$endif}
/// 128-to-256-bit multiplication (with Intel/AMD asm) - used by ecc256r1
procedure _mult128({$ifdef FPC}constref{$else}const{$endif} l, r: THash128Rec;
out product: THash256Rec);
{$ifndef ASMINTEL} inline; {$endif}
/// 256-to-512-bit multiplication (with x86_64 asm) - used by ecc256r1
procedure _mult256(out Output: THash512Rec; const Left, Right: THash256Rec);
/// 256-to-512-bit ^2 computation - used by ecc256r1
procedure _square256(out Output: THash512Rec; const Left: THash256Rec);
{$ifdef ASMX64}inline;{$endif}
/// returns sign of 256-bit Left - Right comparison - used by ecc256r1
function _cmp256(const Left, Right: THash256Rec): integer;
{$ifdef CPU64}inline;{$endif}
/// move and change endianness of a 256-bit value - not as 32-bit bswap256()
// - warning: this code requires dest <> source
procedure _bswap256(dest, source: PQWordArray);
/// right shift of 1 bit of a 256-bit value - used by ecc256r1
procedure _rshift1(var V: THash256Rec);
{$ifdef HASINLINE}{$ifndef ASMX64}inline;{$endif}{$endif}
/// left shift of 1 bit of a 256-bit value - used by ecc256r1
function _lshift1(var V: THash256Rec): PtrUInt;
{$ifdef HASINLINE}inline;{$endif}
// computes Output = Input shl Shift, returning carry, of a 256-bit value
// - can modify in place (if Output == Input). 0 < Shift < 64
function _lshift(var Output: THash256Rec; const Input: THash256Rec; Shift: integer): QWord;
/// compute the highest bit set of a 256-bit value - used by ecc256r1
function _numbits256(const V: THash256Rec): integer;
{$ifdef FPC}inline;{$endif}
{$ifdef ASMINTEL} { x86_64/i386 asm sub-routines for mormot.crypt.rsa }
/// add of two TBigInt 512/1024-bit buffers - as used by mormot.crypt.rsa
function _xasmadd(Value, Adds: pointer; Carry: PtrUInt): PtrUInt;
/// sub of two TBigInt 512/1024-bit buffers - as used by mormot.crypt.rsa
function _xasmsub(Value, Subs: pointer; Carry: PtrUInt): PtrUInt;
/// mul-by-integer of a TBigInt 256/512-bit buffer - as used by mormot.crypt.rsa
function _xasmmul(Src, Dst: pointer; Factor, Carry: PtrUInt): PtrUInt;
/// mul-and-add of a TBigInt 256/512-bit buffer - as used by mormot.crypt.rsa
function _xasmmuladd(Src, Dst: pointer; Factor, Carry: PtrUInt): PtrUInt;
/// div-by-integer of a TBigInt 512/1024-bit buffer - as used by mormot.crypt.rsa
function _xasmdiv(Value: pointer; Factor, Carry: PtrUInt): PtrUInt;
/// mod-by-integer of a TBigInt 512/1024-bit buffer - as used by mormot.crypt.rsa
function _xasmmod(Value: pointer; Factor, Carry: PtrUInt): PtrUInt;
const
_xasmaddn = SizeOf(pointer) * 16; // 512/1024 bits per call
_xasmsubn = SizeOf(pointer) * 16; // 512/1024 bits per call
_xasmmuln = SizeOf(pointer) * 8; // 256/512 bits per call
_xasmmuladdn = SizeOf(pointer) * 8; // 256/512 bits per call
_xasmdivn = SizeOf(pointer) * 16; // 512/1024 bits per call
_xasmmodn = SizeOf(pointer) * 16; // 512/1024 bits per call
{$endif ASMINTEL}
{ *************** AES Encoding/Decoding with optimized asm and AES-NI support }
const
/// hide all AES Context complex code
AES_CONTEXT_SIZE = 276 + SizeOf(pointer)
{$ifdef USEAESNI32} + SizeOf(pointer) {$endif};
/// power of two for a standard AES block size during cypher/uncypher
// - used as "1 shl AesBlockShift" or "1 shr AesBlockShift" for fast */div
AesBlockShift = 4;
/// bit mask for fast modulo of AES block size
AesBlockMod = 15;
/// the AES-GCM GMAC size (in bytes)
GMAC_SIZE = 16;
type
/// 128-bit memory block for AES data cypher/uncypher
TAesBlock = THash128;
PAesBlock = ^TAesBlock;
/// 256-bit memory block for maximum AES key storage
TAesKey = THash256;
/// quickly check if the supplied number of bits is either 128, 192 or 256
function ValidAesKeyBits(bits: cardinal): boolean;
{$ifdef HASINLINE} inline; {$endif}
type
/// internal low-level static engine to handle raw AES cypher/uncypher
// - this is the default Electronic codebook (ECB) mode
// - will use AES-NI hardware instructions, if available
// - we defined a record instead of a class, to allow stack allocation and
// thread-safe reuse of one initialized instance as a static memory copy
// - do not use this raw data structure, but TAesFast[] high-level classes
{$ifdef USERECORDWITHMETHODS}
TAes = record
{$else}
TAes = object
{$endif USERECORDWITHMETHODS}
private
Context: packed array[1 .. AES_CONTEXT_SIZE] of byte; // hidden state
public
/// to be called if this TAes was not filled with zeros, e.g. not used as
// TObject field or global variable, but simply declared on the local stack
procedure InitOnStack;
{$ifdef FPC}inline;{$endif}
/// Initialize AES context for cypher
// - first method to call before using this object for encryption
// - KeySize is in bits, i.e. 128, 192 or 256
function EncryptInit(const Key; KeySize: cardinal): boolean;
/// Initialize AES context for cipher, using CSPRNG as transient key source
// - used e.g. by TAesSignature or Random128() for their initialization
// - Bits=0 will instantiate AES-128 or AES-256 if HasHWAes is available
procedure EncryptInitRandom(Bits: integer = 0);
/// encrypt an AES data block into another data block
// - this method is thread-safe, unless you call EncryptInit/DecryptInit
procedure Encrypt(const BI: TAesBlock; var BO: TAesBlock); overload;
{$ifdef FPC}inline;{$endif}
/// encrypt an AES data block
// - this method is thread-safe, unless you call EncryptInit/DecryptInit
procedure Encrypt(var B: TAesBlock); overload;
{$ifdef FPC}inline;{$endif}
/// Initialize AES context for uncypher
// - first method to call before using this object for decryption
// - KeySize is in bits, i.e. 128, 192 or 256
// - note that any stack-allocated TAes instance requires a InitOnStack call
// before calling this method (nothing is expected if a zeroed TObject field)
function DecryptInit(const Key; KeySize: cardinal): boolean;
/// Initialize AES context for uncypher, from another TAes.EncryptInit
// - note that any stack-allocated TAes instance requires a InitOnStack call
// before calling this method (nothing is expected if a zeroed TObject field)
function DecryptInitFrom(const Encryption: TAes; const Key;
KeySize: cardinal): boolean;
/// decrypt an AES data block
// - this method is thread-safe, unless you call EncryptInit/DecryptInit
procedure Decrypt(var B: TAesBlock); overload;
{$ifdef FPC}inline;{$endif}
/// decrypt an AES data block into another data block
// - this method is thread-safe, unless you call EncryptInit/DecryptInit
procedure Decrypt(const BI: TAesBlock; var BO: TAesBlock); overload;
{$ifdef FPC}inline;{$endif}
/// Finalize AES contexts for both cypher and uncypher
// - would fill the TAes instance with zeros, for (paranoid) safety
procedure Done; {$ifdef FPC}inline;{$endif}
/// 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;
/// performs AES-OFB encryption and decryption on whole blocks
// - may be called instead of TAesOfb when only a raw TAes is available
// - as used e.g. by mormot.db.raw.sqlite3.static.pas for its DB encryption
// - this method is thread-safe, and is optimized for AES-NI on x86_64
procedure DoBlocksOfb(iv: PAesBlock; src, dst: pointer;
blockcount: PtrUInt);
/// performs AES-CTR NIST encryption and decryption on whole blocks
// - may be called instead of TAesCtr when only a raw TAes is available
// - as used e.g. by mormot.db.raw.sqlite3.static.pas for its DB encryption
// - this method is thread-safe, and is optimized for AES-NI on x86_64
procedure DoBlocksCtr(iv: PAesBlock; src, dst: pointer;
blockcount: PtrUInt);
{$ifdef FPC}inline;{$endif}
/// TRUE if the context was initialized via EncryptInit/DecryptInit
function Initialized: boolean;
{$ifdef FPC}inline;{$endif}
/// returns the key size in bits (128/192/256)
function KeyBits: integer;
{$ifdef FPC}inline;{$endif}
end;
/// points to a TAes encryption/decryption instance
PAes = ^TAes;
/// points to a TAesGcmEngine encryption/decryption instance
PAesGcmEngine = ^TAesGcmEngine;
/// internal low-level static engine to handle raw AES-GCM processing
// - implements standard AEAD (authenticated-encryption with associated-data)
// algorithm, as defined by NIST Special Publication 800-38D
// - will use AES-NI and CLMUL Intel/AMD opcodes if available on x86_64/i386
// - do not use this raw data structure, but TAesFast[mGCM] with proper padding,
// unless you work on small messages (a few bytes) and require
{$ifdef USERECORDWITHMETHODS}
TAesGcmEngine = record
{$else}
TAesGcmEngine = object
{$endif USERECORDWITHMETHODS}
private
/// standard AES encryption context
aes: TAes;
/// internal AES-GCM state structure
state: record
/// 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 (flagFinalComputed, flagFlushed, flagCLMUL, flagAVX);
end;
/// 4KB lookup table for fast Galois Finite Field multiplication
// - is defined as last field of the object for better code generation
// - only first 256 bytes are used in flagAVX mode
gf_t4k: array[byte] of THash128Rec;
/// build the gf_t4k[] internal table from current state.ghash_h
procedure Make4K_Table;
/// compute a * ghash_h in Galois Finite Field 2^128 using gf_t4k[]
procedure gf_mul_h_pas(var a: TAesBlock);
/// 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);
{$ifdef USEGCMAVX}
/// redirected from Encrypt() and Decrypt() in flagAVX mode
procedure AvxProcess(BufIn, BufOut: PByte; Count: cardinal; Encrypt: boolean);
{$endif USEGCMAVX}
public
/// initialize the AES-GCM structure for the supplied Key
function Init(const Key; KeyBits: PtrInt; AllowAvx: boolean): 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: PHash128Rec; IV_len: PtrInt): boolean;
/// copy this AES-GCM engine key and state into another instance
procedure Clone(another: PAesGcmEngine);
/// 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
// - mostly used for testing purpose with reference vectors
function FullEncryptAndAuthenticate(const Key; KeyBits: PtrInt;
pIV, pAAD, ptp, ctp: pointer; IV_len, aLen, pLen: PtrInt;
out tag: TAesBlock; allowavx: boolean = true): boolean;
/// single call AES-GCM decryption and verification process
// - mostly used for testing purpose with reference vectors
function FullDecryptAndVerify(const Key; KeyBits: PtrInt;
pIV, pAAD, ctp, ptp, ptag: pointer; IV_len, aLen, pLen, tLen: PtrInt;
allowavx: boolean = true): boolean;
end;
/// transient simple digital signature of a 32-bit number/ID using AES-128
// - typical use is e.g. TRestServerAuthenticationHttpAbstract cookie process
// when TBinaryCookieGenerator from mormot.crypt.secure is overkill since
// TRestServer maintains a list of active sessions with proper expiration
// - uses a 96-bit signature with AES encryption as secure MAC with a random
// nonce (stored in TAesContext.iv), which is similar to CMAC or TLS/AES-GCM
// - modern standards consider this sufficient for authenticity in scenarios
// with limited message volumes (not billions of tokens issued per secret key)
{$ifdef USERECORDWITHMETHODS}
TAesSignature = record
{$else}
TAesSignature = object
{$endif USERECORDWITHMETHODS}
private
fEngine: TAes; // hidden internal AES-128 state (storing mask in iv)
public
/// create the transient random secret key needed for this process
// - the internal secret can't be persisted, and will remain in memory
procedure Init;
/// compute the 128-bit digital signature from a given 32-bit value <> 0
procedure Generate(aValue: cardinal; aSignature: PHash128Rec);
/// compute a 32-chars hexadecimal cookie from a given 32-bit value
function GenerateCookie(aValue: cardinal): RawUtf8;
/// check and extract the 32-bit value from a 128-bit digital signature
// - return 0 if the signature is invalid, or the decoded 32-bit value
function Validate(aSignature: PHash128Rec): cardinal;
/// check and extract the 32-bit value from 32-chars hexadecimal cookie
// - return 0 if the cookie is invalid, or the decoded 32-bit value
function ValidateCookie(aHex: PUtf8Char; aHexLen: PtrInt): cardinal; overload;
/// check and extract the 32-bit value from 32-chars hexadecimal cookie
// - return 0 if the cookie is invalid, or the decoded 32-bit value
function ValidateCookie(const aCookie: RawUtf8): cardinal; overload;
{$ifdef HASSAFEINLINE} inline; {$endif}
/// extract the 32-bit value from a 128-bit digital signature
// - without validating the AES-128 signature itself
// - could be used e.g. when Validate() has already been called once
function Extract(const aSignature: THash128Rec): cardinal; overload;
{$ifdef FPC} inline; {$endif}
/// extract the 32-bit value from a 32-chars hexadecimal bearer
// - without validating the AES-128 signature itself
// - could be used e.g. when Validate() has already been called once
function Extract(aHex: PUtf8Char): cardinal; overload;
end;
PAesSignature = ^TAesSignature;
/// the AES chaining modes implemented by this unit
// - mEcb is unsafe and should not be used as such
// - mC64 is a non standard AES-CTR mode with 64-bit CRC - use mCtr for NIST
// - mCfc, mOfc and mCtc are non standard AEAD modes with 256-bit crc32c
// - matching algo names are e.g. 'aes-128-cfb', 'aes-256-ctc' or 'aes-256-gcm'
TAesMode = (
mEcb,
mCbc,
mCfb,
mOfb,
mC64,
mCtr,
mCfc,
mOfc,
mCtc,
mGcm);
/// class-reference type (metaclass) of an AES cypher/uncypher
TAesAbstractClass = class of TAesAbstract;
TAesAbstractClasses = array[TAesMode] of TAesAbstractClass;
{$M+}
/// 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
protected
fKeySize: cardinal;
fKeySizeBytes: cardinal;
fKey: TAesKey;
fIV: TAesBlock;
fAlgoMode: TAesMode;
fIVUpdated: boolean; // so you can chain Encrypt/Decrypt() calls
procedure AfterCreate; virtual; // circumvent Delphi bug about const aKey
function InternalCopy: TAesAbstract; // copy main properties for Clone()
function DecryptPkcs7Len(var InputLen, ivsize: PtrInt; 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. either 128, 192 or 256
// - warning: aKey is an untyped constant, i.e. expects a raw set of memory
// bytes: do NOT use assign it with a string or a TBytes instance: you would
// use the pointer to the data as key - either digest the string via
// CreateFromPbkdf2 or use Create(TBytes)
constructor Create(const aKey; aKeySizeBits: cardinal;
aIV: PAesBlock = nil); 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 AES-256 cypher
// - first method to call before using this class
// - here, aKey is expected to be a 128-bit, 192-bit or 256-bit TBytes,
// i.e. with 16, 24 or 32 bytes
constructor Create(const aKey: TBytes); 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);
{$ifndef PUREMORMOT2}
/// 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
// - since Sha256Weak() is deprecated, consider using the more secure
// (and more standard and proven) CreateFromPbkdf2() constructor
constructor CreateFromSha256(const aKey: RawUtf8); deprecated;
{$endif PUREMORMOT2}
/// Initialize AES context for cypher, from Pbkdf2HmacSha256 derivation
// - here the Key is supplied as a string, and will be hashed using
// Pbkdf2HmacSha256 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 using the identical key
// - 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
function CloneEncryptDecrypt: TAesAbstract; virtual;
/// release the used instance memory and resources
// - also fill the secret fKey buffer with zeros, for safety
destructor Destroy; override;
/// quick check if this cipher is available on the system
// - this default implementation returns true
// - TAesAbstractOsl.IsAvailable returns false if OpenSSL is not installed
class function IsAvailable: boolean; virtual;
/// perform the AES cypher in the corresponding mode to Count bytes
// - 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 to Count bytes
// - 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 128-bit Initialization Vector will
// be generated by TAesPrng and stored at the beginning of the output buffer
// - if TrailerLen is <> 0, some last bytes will be reserved in output buffer
function EncryptPkcs7(const Input: RawByteString;
IVAtBeginning: boolean = false; TrailerLen: PtrInt = 0): 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 RaiseESynCryptoOnError=false, returns '' on any decryption error
// - if TrailerLen is <> 0, some last bytes from Input will be ignored
function DecryptPkcs7(const Input: RawByteString; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true; TrailerLen: PtrInt = 0): 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
// generated by TAesPrng and stored at the beginning of the output buffer
function EncryptPkcs7(const Input: TBytes;
IVAtBeginning: boolean = false; TrailerLen: PtrInt = 0): 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 RaiseESynCryptoOnError=false, returns [] on any decryption error
function DecryptPkcs7(const Input: TBytes; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true; TrailerLen: PtrInt = 0): 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
// generated by TAesPrng and stored at the beginning of the output buffer
// - 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: PtrUInt;
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
// - if RaiseESynCryptoOnError=false, returns '' on any decryption error
function DecryptPkcs7Buffer(Input: pointer; InputLen: PtrInt;
IVAtBeginning: boolean; RaiseESynCryptoOnError: boolean = true): RawByteString;
{$ifdef HASINLINE} inline; {$endif}
/// decrypt a memory buffer using a PKCS7 padding pattern
// - as called by DecryptPkcs7Buffer()
function DecryptPkcs7Var(Input: pointer; InputLen: PtrInt;
IVAtBeginning: boolean; var Plain: RawByteString): boolean;
/// just fill the IV with zeros
procedure IVFillZero;
/// initialize AEAD (authenticated-encryption with associated-data) nonce
// - i.e. setup 256-bit MAC computation before 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(DoEncrypt: boolean; const RandomNonce: THash256;
const Associated: RawByteString = ''): boolean; virtual;
/// returns AEAD (authenticated-encryption with associated-data) MAC
// - returns a MAC hash (up to 256-bit), computed during the last Encryption
// - may be used e.g. for TAesGcm or our custom TAesOfc/TAesCfbCr modes
// - default implementation, for a non AEAD protocol, returns false
function MacEncryptGetTag(out EncryptMac: THash256): boolean; virtual;
/// validates an AEAD (authenticated-encryption with associated-data) MAC
// - check if the MAC computed during the last Decryption matches DecryptMac
// - default implementation, for a non AEAD protocol, returns false
function MacDecryptCheckTag(const DecryptMac: THash256): boolean; virtual;
/// validate if an encrypted buffer matches the stored AEAD MAC
// - called before the decryption process to ensure the input is not corrupted
// - default implementation, for a non AEAD protocol, returns false
function MacCheckError(Encrypted: pointer; Count: cardinal): boolean; virtual;
/// perform one step PKCS7 encryption/decryption and authentication from
// a given 256-bit key over a small memory block
// - wrapper which creates a TAesAbsract instance and calls MacAndCrypt()
class function MacEncrypt(const Data: RawByteString; const Key: THash256;
Encrypt: boolean; const Associated: RawByteString = '';
IV: PAesBlock = nil): RawByteString; overload;
/// perform one step PKCS7 encryption/decryption and authentication from
// a given 128-bit key over a small memory block
// - wrapper which creates a TAesAbstract instance and calls MacAndCrypt()
class function MacEncrypt(const Data: RawByteString; const Key: THash128;
Encrypt: boolean; const Associated: RawByteString = '';
IV: PAesBlock = nil): RawByteString; overload;
/// perform one step PKCS7 encryption/decryption and authentication with
// the curent AES instance over a small memory block
// - 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, but TAesGcm/TAesCfc/TAesOfc
// - TAesCfc/TAesOfc 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 - for TAesGcm, authentication requires decryption
// - EndingSize can be used if some custom info is stored at the end of Data
function MacAndCrypt(const Data: RawByteString; Encrypt, IVAtBeginning: boolean;
const Associated: RawByteString = ''; EndingSize: cardinal = 0): RawByteString; virtual;
{$ifndef PUREMORMOT2}
/// deprecated wrapper able to cypher/decypher any in-memory content
// - deprecated due to wrong IV process - use AesPkcs7() instead
class function SimpleEncrypt(const Input: RawByteString; const Key;
KeySize: integer; Encrypt: boolean; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true): RawByteString; overload;
/// deprecated wrapper able to cypher/decypher any file content
// - deprecated due to wrong IV process - use AesPkcs7File() instead
class function SimpleEncryptFile(const InputFile, Outputfile: TFileName;
const Key; KeySize: integer; Encrypt: boolean; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true): boolean; overload;
/// deprecated wrapper able to cypher/decypher any in-memory content
// - deprecated due to wrong IV process - use AesPkcs7() instead
// - will use Sha256Weak() and PKCS7 padding with the current class mode,
// so is to be considered as **really** deprecated
class function SimpleEncrypt(const Input, Key: RawByteString;
Encrypt: boolean; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true): RawByteString; overload;
/// deprecated wrapper able to cypher/decypher any file content
// - deprecated due to wrong IV process - use AesPkcs7File() instead
// - will use Sha256Weak() and PKCS7 padding with the current class mode,
// so is to be considered as **really** deprecated
class function SimpleEncryptFile(const InputFile, OutputFile: TFileName;
const Key: RawByteString; Encrypt: boolean; IVAtBeginning: boolean = false;
RaiseESynCryptoOnError: boolean = true): boolean; overload; deprecated;
{$endif PUREMORMOT2}
/// OpenSSL-like Cipher name encoding of this AES engine
// - return e.g. 'aes-128-cfb' or 'aes-256-gcm'
// - our TAesC64, TAesCfc, TAesOfc, TAesCtc custom algorithms
// use non-standard trailing 'c64', 'cfc', 'ofc' and 'ctc' mode names e.g.
// as 'aes-256-cfc'
function AlgoName: TShort15; overload;
{$ifdef HASINLINE} inline; {$endif}
/// OpenSSL-like Cipher name encoding of this AES engine
procedure AlgoName(out Result: TShort15); overload;
/// the chaining mode of this AES engine
property AlgoMode: TAesMode
read fAlgoMode;
/// 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;
/// low-level flag indicating you can call Encrypt/Decrypt several times
// - i.e. the IV and AEAD MAC are updated after each Encrypt/Decrypt call
// - is enabled for our internal classes, and also for OpenSSL, but may be
// disabled for some libraries or APIs (e.g. Windows CryptoApi classes)
// - if you call EncryptPkcs7/DecryptPkcs7 you don't have to care about it
property IVUpdated: boolean
read fIVUpdated;
end;
{$M-}
/// handle AES cypher/uncypher with chaining with our 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;
fAes: TAes;
fAesInit: (initNone, initEncrypt, initDecrypt);
procedure AfterCreate; override;
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 all current static fields to a new instance, by-passing
// the key creation step and reuse the current state of this instance
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
// - this abstract method will set 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 fIn/fOut from BufIn/BufOut
procedure Decrypt(BufIn, BufOut: pointer; Count: cardinal); override;
end;
/// handle AES cypher/uncypher without chaining (ECB)
// - this mode is known to be less secure than the others, and should not be
// needed in practice, but from some legacy / unsafe purposes
// - 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
// - use TAesFast[mEcb] to retrieve the fastest implementation at runtime
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
// - expect IV to be set before process, or IVAtBeginning=true
// - on x86_64, our TAesCbc class is slightly slower than OpenSSL 3.0:
// $ mormot aes-128-cbc in 4.48ms i.e. 544.9K/s or 1.1 GB/s
// $ mormot aes-256-cbc in 5.46ms i.e. 446.9K/s or 0.9 GB/s
// $ openssl aes-128-cbc in 3.23ms i.e. 755.6K/s or 1.6 GB/s
// $ openssl aes-256-cbc in 4.04ms i.e. 602.9K/s or 1.2 GB/s
// - also on i386:
// $ mormot aes-128-cbc in 4.59ms i.e. 530.8K/s or 1.1 GB/s
// $ mormot aes-256-cbc in 5.45ms i.e. 447.8K/s or 0.9 GB/s
// $ openssl aes-128-cbc in 3.50ms i.e. 697.1K/s or 1.4 GB/s
// $ openssl aes-256-cbc in 4.36ms i.e. 558.8K/s or 1.1 GB/s
// - use TAesFast[mCbc] to retrieve the fastest implementation at runtime
TAesCbc = class(TAesAbstractSyn)
protected
procedure AfterCreate; override;
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;
/// Kerberos AES-CBC-CTS cypher in the corresponding mode to Count bytes
// - follow Kerberos CipherText Stealing (CTS) padding from RFC 3962 (not NIST)
// - this method is not re-entrant and should be called once with process tail
// - Count is expected to be >= 16 bytes (i.e. at least one block)
procedure EncryptCts(BufIn, BufOut: pointer; Count: cardinal); overload;
/// Kerberos AES-CBC-CTS un-cypher in the corresponding mode to Count bytes
// - follow Kerberos CipherText Stealing (CTS) padding from RFC 3962 (not NIST)
// - this method is not re-entrant and should be called once with process tail
// - Count is expected to be >= 16 bytes (i.e. at least one block)
procedure DecryptCts(BufIn, BufOut: pointer; Count: cardinal); overload;
/// Kerberos AES-CBC-CTS cypher in the corresponding mode to a buffer
// - follow Kerberos CipherText Stealing (CTS) padding from RFC 3962 (not NIST)
// - if IVAtBeginning is TRUE, a random Initialization Vector will be
// generated by TAesPrng and stored at the beginning of the output buffer
// - length(Input) is expected to be >= 16 bytes (i.e. at least one block)
function EncryptCts(const Input: RawByteString;
IVAtBeginning: boolean = false): RawByteString; overload;
/// Kerberos AES-CBC-CTS un-cypher in the corresponding mode to a buffer
// - follow Kerberos CipherText Stealing (CTS) padding from RFC 3962 (not NIST)
// - if IVAtBeginning is TRUE, the Initialization Vector will be taken
// from the beginning of the input binary buffer
// - length(result) is expected to be >= 16 bytes (i.e. at least one block)
function DecryptCts(const Input: RawByteString;
IVAtBeginning: boolean = false): RawByteString; overload;
end;
/// abstract parent class for chaining modes using only AES encryption
TAesAbstractEncryptOnly = class(TAesAbstractSyn)
protected
procedure AfterCreate; override;
public
/// compute a class instance similar to this one, for performing the
// reverse encryption/decryption process
// - will return self to avoid creating two instances
function CloneEncryptDecrypt: TAesAbstract; override;
end;
/// handle AES cypher/uncypher with Cipher feedback (CFB)
// - this class will use AES-NI hardware instructions, if available
// - expect IV to be set before process, or IVAtBeginning=true
// - on x86_64, our TAesCfb class is really faster than OpenSSL 3.0:
// $ mormot aes-128-cfb in 2.64ms i.e. 0.9M/s or 1.9 GB/s
// $ mormot aes-256-cfb in 3.48ms i.e. 700.7K/s or 1.4 GB/s
// $ openssl aes-128-cfb in 4.95ms i.e. 492.4K/s or 1 GB/s
// $ openssl aes-256-cfb in 5.80ms i.e. 420.6K/s or 0.9 GB/s
// - on i386, our code is almost twice faster:
// $ mormot aes-128-cfb in 2.57ms i.e. 0.9M/s or 2 GB/s
// $ mormot aes-256-cfb in 3.45ms i.e. 706.2K/s or 1.5 GB/s
// $ openssl aes-128-cfb in 5.56ms i.e. 438.5K/s or 0.9 GB/s
// $ openssl aes-256-cfb in 6.41ms i.e. 380.8K/s or 830 MB/s
// - is used e.g. by CryptDataForCurrentUser or WebSockets ProtocolAesClass
// - use TAesFast[mCfb] to retrieve the fastest implementation at runtime
TAesCfb = class(TAesAbstractEncryptOnly)
protected
procedure AfterCreate; override;
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
// - expect IV to be set before process, or IVAtBeginning=true
// - on x86_64, our TAesOfb class is faster than OpenSSL 1.1:
// $ mormot aes-128-ofb in 2.62ms i.e. 0.9M/s or 1.9 GB/s
// $ mormot aes-256-ofb in 3.49ms i.e. 699.3K/s or 1.4 GB/s
// $ openssl aes-128-ofb in 3.49ms i.e. 698.7K/s or 1.4 GB/s
// $ openssl aes-256-ofb in 4.36ms i.e. 558.8K/s or 1.1 GB/s
// - on i386, our code is faster in a similar way:
// $ mormot aes-128-ofb in 2.57ms i.e. 0.9M/s or 2 GB/s
// $ mormot aes-256-ofb in 3.45ms i.e. 707K/s or 1.5 GB/s
// $ openssl aes-128-ofb in 3.97ms i.e. 614.8K/s or 1.3 GB/s
// $ openssl aes-256-ofb in 4.80ms i.e. 508.2K/s or 1 GB/s
// - use TAesFast[mOfb] to retrieve the fastest implementation at runtime
TAesOfb = class(TAesAbstractEncryptOnly)
protected
procedure AfterCreate; override;
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