forked from synopse/mORMot2
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmormot.crypt.secure.pas
More file actions
11443 lines (10403 loc) · 405 KB
/
Copy pathmormot.crypt.secure.pas
File metadata and controls
11443 lines (10403 loc) · 405 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 Authentication and Security Features
// - 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.secure;
{
*****************************************************************************
Authentication and Security types shared by all framework units
- Password-Safe and TSynConnectionDefinition Classes
- Reusable Authentication Classes
- High-Level TSynSigner/TSynHasher Multi-Algorithm Wrappers
- Client and Server HTTP Access Authentication
- 64-bit TSynUniqueIdentifier and its efficient Generator
- IProtocol Safe Communication with Unilateral or Mutual Authentication
- TBinaryCookieGenerator Simple Cookie Generator
- Rnd/Hash/Sign/Cipher/Asym/Cert/Store High-Level Algorithms Factories
- Minimal PEM/DER Encoding/Decoding
Uses optimized mormot.crypt.core.pas for its actual cryptographic process.
*****************************************************************************
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
sysutils,
classes,
mormot.core.base,
mormot.core.os,
mormot.core.os.security,
mormot.core.rtti,
mormot.core.unicode,
mormot.core.text,
mormot.core.datetime,
mormot.core.buffers,
mormot.core.data,
mormot.core.variants,
mormot.core.json,
mormot.lib.sspi, // for WinCertDecode() - void unit on POSIX
mormot.crypt.core;
{ ***************** Password-Safe and TSynConnectionDefinition Classes }
type
/// abstract class allowing safe storage of a password in a published property
// - the associated Password, e.g. for storage or transmission encryption
// will be persisted encrypted with a private key (which can be customized)
// - if default simple symmetric encryption is not enough, it will also
// read passwords strongly obfuscated for a given user using
// mormot.crypt.core.pas' CryptDataForCurrentUser()
// - a published property should be defined as such in inherited class:
// ! property PasswordPropertyName: RawUtf8 read fPassword write fPassword;
// - use the PassWordPlain property to access to its uncyphered value
TObjectWithPassword = class(TSynPersistent)
protected
fPassWord: SpiUtf8;
fKey: cardinal;
function GetKey: cardinal;
{$ifdef HASINLINE}inline;{$endif}
function GetPassWordPlain: SpiUtf8;
function GetPassWordPlainInternal(AppSecret: RawUtf8): SpiUtf8;
procedure SetPassWordPlain(const Value: SpiUtf8);
public
/// finalize the instance
destructor Destroy; override;
/// this class method could be used to compute the encrypted password,
// ready to be stored as JSON, according to a given private key
class function ComputePassword(const PlainPassword: SpiUtf8;
CustomKey: cardinal = 0): SpiUtf8; overload;
/// this class method could be used to compute the encrypted password from
// a binary digest, ready to be stored as JSON, according to a given private key
// - just a wrapper around ComputePassword(BinToBase64Uri())
class function ComputePassword(PlainPassword: pointer; PlainPasswordLen: integer;
CustomKey: cardinal = 0): SpiUtf8; overload;
/// this class method could be used to decrypt a password, stored as JSON,
// according to a given private key
// - may trigger a ECrypt if the password was stored using hardened
// CryptDataForCurrentUser, and the current user doesn't match the
// expected user stored in the field
class function ComputePlainPassword(const CypheredPassword: SpiUtf8;
CustomKey: cardinal = 0; const AppSecret: RawUtf8 = ''): SpiUtf8;
/// the private key used to cypher the password storage on serialization
// - application can override the default 0 value at runtime
property Key: cardinal
read GetKey write fKey;
/// access to the associated unencrypted Password value
// - may trigger a ECrypt if the password was stored using hardened
// CryptDataForCurrentUser, and the current user doesn't match the
// expected user stored in the field
property PasswordPlain: SpiUtf8
read GetPassWordPlain write SetPassWordPlain;
end;
{$ifndef PUREMORMOT2}
TSynPersistentWithPassword = TObjectWithPassword;
{$endif PUREMORMOT2}
type
/// could be used to store a credential pair, as user name and password
// - password will be stored with TObjectWithPassword encryption
TSynUserPassword = class(TObjectWithPassword)
protected
fUserName: RawUtf8;
published
/// the associated user name
property UserName: RawUtf8
read fUserName write fUserName;
/// the associated encrypted password
// - use the PasswordPlain public property to access to the uncrypted password
property Password: SpiUtf8
read fPassword write fPassword;
end;
/// handle safe storage of any connection properties
// - would be used by mormot.db to serialize TSqlDBConnectionProperties, or
// by mormot.rest.core.pas to serialize TRest instances
// - the password will be stored as Base64, after a simple encryption as
// defined by TObjectWithPassword
// - typical content could be:
// $ {
// $ "Kind": "TSqlDBSQLite3ConnectionProperties",
// $ "ServerName": "server",
// $ "DatabaseName": "",
// $ "User": "",
// $ "Password": "PtvlPA=="
// $ }
// - the "Kind" value will be used to let the corresponding TRest or
// TSqlDBConnectionProperties NewInstance*() class methods create the
// actual instance, from its class name
TSynConnectionDefinition = class(TObjectWithPassword)
protected
fKind: string;
fServerName: RawUtf8;
fDatabaseName: RawUtf8;
fUser: RawUtf8;
public
/// unserialize the database definition from JSON
// - as previously serialized with the SaveToJson method
// - you can specify a custom Key used for password encryption, if the
// default value is not safe enough for you
constructor CreateFromJson(const Json: RawUtf8; Key: cardinal = 0); virtual;
/// serialize the database definition as JSON
function SaveToJson: RawUtf8; virtual;
published
/// the class name implementing the connection or TRest instance
// - will be used to instantiate the expected class type
property Kind: string
read fKind write fKind;
/// the associated server name (or file, for SQLite3) to be connected to
property ServerName: RawUtf8
read fServerName write fServerName;
/// the associated database name (if any), or additional options
property DatabaseName: RawUtf8
read fDatabaseName write fDatabaseName;
/// the associated User Identifier (if any)
property User: RawUtf8
read fUser write fUser;
/// the associated Password, e.g. for storage or transmission encryption
// - will be persisted encrypted with a private key
// - use the PassWordPlain property to access to its uncyphered value
property Password: SpiUtf8
read fPassword write fPassword;
end;
{ ***************** Reusable Authentication Classes }
type
/// class-reference type (metaclass) of an authentication class
TSynAuthenticationClass = class of TSynAuthenticationAbstract;
/// abstract authentication class, implementing safe token/challenge security
// and a list of active sessions
// - do not use this class, but plain TSynAuthentication
TSynAuthenticationAbstract = class
protected
fSafe: TOSLock;
fSessions: TIntegerDynArray;
fSessionsCount: integer;
fSessionGenerator: integer;
fTokenSeed: Int64;
function ComputeCredential(previous: boolean;
const UserName, PassWord: RawUtf8): cardinal; virtual;
function GetPassword(const UserName: RawUtf8;
out Password: RawUtf8): boolean; virtual; abstract;
function GetUsersCount: integer; virtual; abstract;
// check the given Hash challenge, against stored credentials
function CheckCredentials(const UserName: RawUtf8; Hash: cardinal): boolean; virtual;
public
/// initialize the authentication scheme
constructor Create;
/// finalize the authentation
destructor Destroy; override;
/// register one credential for a given user
// - this abstract method will raise an exception: inherited classes should
// implement them as expected
procedure AuthenticateUser(const aName, aPassword: RawUtf8); virtual;
/// unregister one credential for a given user
// - this abstract method will raise an exception: inherited classes should
// implement them as expected
procedure DisauthenticateUser(const aName: RawUtf8); virtual;
/// create a new session
// - should return 0 on authentication error, or an integer session ID
// - this method will check the User name and password, and create a new session
function CreateSession(const User: RawUtf8; Hash: cardinal): integer; virtual;
/// check if the session exists in the internal list
function SessionExists(aID: integer): boolean;
/// delete a session
procedure RemoveSession(aID: integer);
/// returns the current identification token
// - to be sent to the client for its authentication challenge
function CurrentToken: Int64;
/// the number of current opened sessions
property SessionsCount: integer
read fSessionsCount;
/// the number of registered users
property UsersCount: integer
read GetUsersCount;
/// to be used to compute a Hash on the client sude, for a given Token
// - the token should have been retrieved from the server, and the client
// should compute and return this hash value, to perform the authentication
// challenge and create the session
// - internal algorithm is not cryptographic secure, but fast and safe
class function ComputeHash(Token: Int64;
const UserName, PassWord: RawUtf8): cardinal; virtual;
end;
/// simple authentication class, implementing safe token/challenge security
// - maintain a list of user / name credential pairs, and a list of sessions
// - is not meant to handle authorization, just plain user access validation
// - used e.g. by TSqlDBConnection.RemoteProcessMessage (on server side) and
// TSqlDBProxyConnectionPropertiesAbstract (on client side) in mormot.db.proxy
TSynAuthentication = class(TSynAuthenticationAbstract)
protected
fCredentials: TSynNameValue; // store user/password pairs
function GetPassword(const UserName: RawUtf8;
out Password: RawUtf8): boolean; override;
function GetUsersCount: integer; override;
public
/// initialize the authentication scheme
// - you can optionally register one user credential
constructor Create(const aUserName: RawUtf8 = '';
const aPassword: RawUtf8 = ''); reintroduce;
/// register one credential for a given user
procedure AuthenticateUser(const aName, aPassword: RawUtf8); override;
/// unregister one credential for a given user
procedure DisauthenticateUser(const aName: RawUtf8); override;
end;
type
/// optimized thread-safe storage of a list of IP v4 addresses
// - can be used e.g. as white-list or black-list of clients
// - will maintain internally a sorted list of 32-bit integers for fast lookup
// - with optional binary persistence
// - as used by TRestServer.BanIP/JwtForUnauthenticatedRequestWhiteIP
// - see also more efficient and lower level THttpAcceptBan in mormot.net.http
TIPBan = class(TObjectStore)
protected
fIP4: TIntegerDynArray;
fCount: integer;
procedure LoadFromReader; override;
procedure SaveToWriter(aWriter: TBufferWriter); override;
public
/// register one IP to the list
function Add(const aIP: RawUtf8): boolean;
/// unregister one IP to the list
function Delete(const aIP: RawUtf8): boolean;
/// returns true if the IP is in the list
function Exists(const aIP: RawUtf8): boolean;
/// creates a TDynArray wrapper around the stored list of values
// - could be used e.g. for binary persistence
// - warning: caller should make Safe.Unlock(aLock) when finished
function DynArrayLocked(aLock: TRWLockContext = cWrite): TDynArray;
/// low-level access to the internal IPv4 list
// - 32-bit unsigned values are sorted, for fast O(log(n)) binary search
property IP4: TIntegerDynArray
read fIP4;
published
/// how many IPs are currently banned
property Count: integer
read fCount;
end;
{ **************** 64-bit TSynUniqueIdentifier and its Efficient Generator }
type
/// 64-bit integer unique identifier, as computed by TSynUniqueIdentifierGenerator
// - they are increasing over time (so are much easier to store/shard/balance
// than UUID/GUID), and contain generation time and a 16-bit process ID
// - mapped by TSynUniqueIdentifierBits memory structure
// - bits 0..14 map a 15-bit increasing counter (collision-free)
// - bits 15..30 map a 16-bit process identifier
// - bits 31..63 map a 33-bit UTC time, encoded as seconds since Unix epoch
// - may be used on client side for something similar to a MongoDB ObjectID,
// but compatible with TOrm.ID: TID properties
TSynUniqueIdentifier = type TID;
/// 16-bit unique process identifier, used to compute TSynUniqueIdentifier
// - each TSynUniqueIdentifierGenerator instance is expected to have
// its own unique process identifier, stored as a 16-bit integer 0..65535 value
// - when used with TSynUnique53, should be kept in [0..255] range
TSynUniqueIdentifierProcess = type word;
/// 53-bit integer unique identifier, as computed by TSynUniqueIdentifierGenerator
// - could be used as JavaScript-compatible TID value e.g. for TOrm.ID
// - bits 0..14 map a 15-bit increasing counter (collision-free)
// - bits 15..22 map a 8-bit process identifier (0..255)
// - bits 23..53 map a 31-bit UTC time, encoded as seconds since 1/1/2025,
// therefore valid until 2093 (when I hope we will be done with JavaScript)
// or until 2171 if we use the [-2^53+1 .. 0] range of negative numbers -
// but BigInt would be usable at that time for sure for TSynUniqueIdentifier
TSynUnique53 = type Int53;
/// map 64-bit integer unique identifier internal memory structure
// - as stored in TSynUniqueIdentifier = TID = Int64 values, and computed by
// TSynUniqueIdentifierGenerator
// - bits 0..14 map a 15-bit increasing counter (collision-free)
// - bits 15..30 map a 16-bit process identifier
// - bits 31..63 map a 33-bit UTC time, encoded as seconds since Unix epoch
{$ifdef USERECORDWITHMETHODS}
TSynUniqueIdentifierBits = record
{$else}
TSynUniqueIdentifierBits = object
{$endif USERECORDWITHMETHODS}
private
function GetJavaScriptID: TSynUnique53;
procedure SetJavaScriptID(const aJavaScriptID: TSynUnique53);
public
/// the actual 64-bit storage value
// - in practice, only first 63 bits are used
Value: TSynUniqueIdentifier;
/// extract the 15-bit counter (0..32767), starting with a random value
function Counter: word;
{$ifdef HASINLINE}inline;{$endif}
/// extract the 16-bit unique process identifier
// - as specified to TSynUniqueIdentifierGenerator constructor
// - will be in range (0..255) when stored in a TSynUnique53 identifier
function ProcessID: TSynUniqueIdentifierProcess;
{$ifdef HASINLINE}inline;{$endif}
/// extract the UTC generation timestamp as seconds since the Unix epoch
// - time is expressed in Coordinated Universal Time (UTC), not local time
// - it uses in fact an unsigned 33-bit resolution, so is "Year 2038"
// bug-free and would overflow only in year 2242
function CreateTimeUnix: TUnixTime;
{$ifdef HASINLINE}inline;{$endif}
/// extract the UTC generation timestamp as TDateTime
// - time is expressed in Coordinated Universal Time (UTC), not local time
function CreateDateTime: TDateTime;
{$ifdef HASINLINE}inline;{$endif}
/// extract the UTC generation timestamp as our TTimeLog
// - time is expressed in Coordinated Universal Time (UTC), not local time
function CreateTimeLog: TTimeLog;
/// fill this unique identifier structure from its TSynUniqueIdentifier value
// - is just a wrapper around PInt64(@self)^
procedure From(const aID: TSynUniqueIdentifier);
{$ifdef HASINLINE}inline;{$endif}
/// fill this unique identifier back from a 16 chars hexadecimal string
// - returns TRUE if the supplied hexadecimal is on the expected format
// - returns FALSE if the supplied text is invalid
function FromHexa(const hexa: RawUtf8): boolean;
/// fill this unique identifier with a fake value corresponding to a given
// timestamp
// - may be used e.g. to limit database queries on a particular time range
// - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0
procedure FromDateTime(const aDateTime: TDateTime);
/// fill this unique identifier with a fake value corresponding to a given
// timestamp
// - may be used e.g. to limit database queries on a particular time range
// - bits 0..30 would be 0, i.e. would set Counter = 0 and ProcessID = 0
procedure FromUnixTime(const aUnixTime: TUnixTime);
/// compare two Identifiers
function Equal(const Another: TSynUniqueIdentifierBits): boolean;
{$ifdef HASINLINE}inline;{$endif}
/// convert the identifier into a 16 chars hexadecimal string
function ToHexa: RawUtf8;
{$ifdef HASINLINE}inline;{$endif}
/// convert this identifier as an explicit TDocVariant JSON object
// - returns e.g.
// ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1,
// ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"}
function AsVariant: variant;
{$ifdef HASINLINE}inline;{$endif}
/// convert this identifier to an explicit TDocVariant JSON object
// - returns e.g.
// ! {"Created":"2016-04-19T15:27:58","Identifier":1,"Counter":1,
// ! "Value":3137644716930138113,"Hex":"2B8B273F00008001"}
procedure ToVariant(out Result: variant);
/// convert to/from a JavaScript-compatible 53-bit integer value
// - would accept only ProcessID in (0..255) range, or raise ESynCrypto
property JavaScriptID: TSynUnique53
read GetJavaScriptID write SetJavaScriptID;
end;
/// points to a 64-bit integer identifier, as computed by TSynUniqueIdentifierGenerator
// - may be used to access the identifier internals, from its stored
// Int64 or TSynUniqueIdentifier value
PSynUniqueIdentifierBits = ^TSynUniqueIdentifierBits;
/// a 24 chars cyphered hexadecimal string, mapping a TSynUniqueIdentifier
// - has handled by TSynUniqueIdentifierGenerator.ToObfuscated/FromObfuscated
TSynUniqueIdentifierObfuscated = type RawUtf8;
/// thread-safe 64-bit integer unique identifier computation
// - may be used on client side for something similar to a MongoDB ObjectID,
// but compatible with TOrm.ID: TID properties, since it will contain
// a 63-bit unsigned integer, following our ORM expectations
// - each identifier would contain a 16-bit process identifier, which is
// supplied by the application, and should be unique for this process at a
// given time
// - identifiers may be obfuscated as hexadecimal text, using both encryption
// and digital signature
// - all its methods are thread-safe, even during obfuscation processing
TSynUniqueIdentifierGenerator = class(TSynPersistent)
protected
fSafe: TLightLock;
fLastUnixCreateTime: cardinal;
fIdentifier: TSynUniqueIdentifierProcess;
fIdentifierShifted: cardinal;
fLastCounter: cardinal;
fComputedCount: Int64;
fCollisions: cardinal;
fCryptoCRC: cardinal;
fCrypto: TBlock256; // only fCrypto[6..7] are used in practice
fCryptoAesE, fCryptoAesD: TAes; // Initialized if aSharedObfuscationKeyNewKdf
public
/// initialize the generator for the given 16-bit process identifier
// - you can supply an obfuscation key, which should be shared for the
// whole system, so that you may use FromObfuscated/ToObfuscated methods
// - if aSharedObfuscationKeyNewKdf is > 0, indicates the rounds count for
// a safer AES/SHA3 algorithm used for the obfuscation cryptography - keep
// it as default 0 for mORMot 1.18 backward compatibility
constructor Create(aIdentifier: TSynUniqueIdentifierProcess;
const aSharedObfuscationKey: RawUtf8 = '';
aSharedObfuscationKeyNewKdf: integer = 0); reintroduce;
/// finalize the generator structure
destructor Destroy; override;
/// return a new unique ID
// - this method is very optimized, and would use very little CPU
procedure ComputeNew(out result: TSynUniqueIdentifierBits); overload;
/// return a new unique ID, type-casted to an Int64
function ComputeNew: Int64; overload;
{$ifdef HASINLINE}inline;{$endif}
/// return an ID matching this generator pattern, at a given timestamp
// - may be used e.g. to limit database queries on a particular time range
// - the ID is not guaranted to be unique, but match the supplied TDateTime
procedure ComputeFromDateTime(const aDateTime: TDateTime;
out result: TSynUniqueIdentifierBits);
/// return an ID matching this generator pattern, at a given timestamp
// - may be used e.g. to limit database queries on a particular time range
// - the ID is not guaranted to be unique, but match the supplied TUnixTime
procedure ComputeFromUnixTime(const aUnixTime: TUnixTime;
out result: TSynUniqueIdentifierBits);
/// map a TSynUniqueIdentifier as 24/32 chars cyphered hexadecimal text
// - cyphering includes simple key-based encryption and a CRC-32 digital signature
// - returned text size is 24 for the legacy format, and 32 chars if
// aSharedObfuscationKeyNewKdf was set to true
function ToObfuscated(
const aIdentifier: TSynUniqueIdentifier): TSynUniqueIdentifierObfuscated;
/// retrieve a TSynUniqueIdentifier from 24/32 chars cyphered hexadecimal text
// - any file extension (e.g. '.jpeg') would be first deleted from the
// supplied obfuscated text
// - returns true if the supplied obfuscated text has the expected layout
// and a valid digital signature
// - returns false if the supplied obfuscated text is invalid
// - note that this method will work for any TSynUniqueIdentifierProcess
// of the same aSharedObfuscationKey - not only the Identifier of this node
function FromObfuscated(const aObfuscated: TSynUniqueIdentifierObfuscated;
out aIdentifier: TSynUniqueIdentifier): boolean;
/// paranoid loop until LastUnixCreateTime
// - may be called at server shutdown, if you expect a lot of collisions,
// and want to ensure the "fake" timestamp match the time at server restart
procedure WaitForSafeCreateTime(TimeOutSeconds: integer = 30);
/// persist the current state (counter and create time) into a single value
function SaveTo: Int64;
/// read the current state (counter and create time) from a SaveTo value
procedure LoadFrom(aSaved: Int64);
/// some 32-bit value, derivated from aSharedObfuscationKey as supplied
// to the class constructor
// - FromObfuscated and ToObfuscated methods will validate their hexadecimal
// content with this value to secure the associated CRC
// - may be used e.g. as system-depending salt
property CryptoCRC: cardinal
read fCryptoCRC;
/// direct access to the associated mutex
property Safe: TLightLock
read fSafe;
published
/// the process identifier, associated with this generator
property Identifier: TSynUniqueIdentifierProcess
read fIdentifier;
/// how many times ComputeNew method has been called
property ComputedCount: Int64
read fComputedCount;
/// how many times ComputeNew method did have a collision and a fake
// increased timestamp has been involved
property Collisions: cardinal
read fCollisions;
/// low-level access to the last generated timestamp
property LastUnixCreateTime: cardinal
read fLastUnixCreateTime;
/// low-level access to the last generated counter
property LastCounter: cardinal
read fLastCounter;
end;
/// hold a dynamic array of TSynUniqueIdentifierGenerator instances
TSynUniqueIdentifierGenerators = array of TSynUniqueIdentifierGenerator;
{ **************** High-Level TSynSigner/TSynHasher Multi-Algorithm Wrappers }
{ implemented in this unit and not in mormot.crypt.core, since TSynSignerParams
expects JSON support, which requires mormot.core.json }
type
/// hash algorithms available for HashFile/HashFull functions
// and TSynHasher object
THashAlgo = (
hfMD5,
hfSHA1,
hfSHA256,
hfSHA384,
hfSHA512,
hfSHA512_256,
hfSHA3_256,
hfSHA3_512,
hfSHA224,
hfSHA3_224,
hfSHA3_384,
hfShake128,
hfShake256);
/// a pointer to one of our hash algorithms
PHashAlgo = ^THashAlgo;
/// set of algorithms available for HashFile/HashFull functions and TSynHasher object
THashAlgos = set of THashAlgo;
/// store a hash value and its algorithm, e.g. for THttpPeerCacheMessage.Hash
// - we store and compare in our implementation the algorithm in addition to
// the hash, to avoid any potential attack about (unlikely) hash collisions
// between algorithms, and allow any change of algo restrictions in the future
THashDigest = packed record
/// the algorithm used for Hash
Algo: THashAlgo;
/// up to 512-bit of raw binary hash, according to Algo
Bin: THash512Rec;
end;
/// a pointer to one hash value and its algorithm
PHashDigest = ^THashDigest;
/// store a dynamic array of hash value and its algorithm
THashDigests = array of THashDigest;
/// 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: copying the (414 bytes of)
// record content will copy the whole current hashing state
{$ifdef USERECORDWITHMETHODS}
TSynHasher = record
{$else}
TSynHasher = object
{$endif USERECORDWITHMETHODS}
private
ctxt: array[1..SHA3_CONTEXT_SIZE] of byte; // enough space for all algorithms
fAlgo: THashAlgo; // ctxt is better aligned if put first
procedure CopyTo(out aHasher: TSynHasher); {$ifdef FPC} inline; {$endif}
procedure Clear; {$ifdef FPC} inline; {$endif}
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}
/// hash the supplied strings content
procedure Update(const aBuffer: array of RawByteString); overload;
/// hash 32-bit encoded integer as big endian
procedure UpdateBigEndian(aValue: cardinal);
/// returns the resulting hash as lowercase hexadecimal string
procedure Final(var aResult: RawUtf8); overload;
/// set the resulting hash into a binary buffer, and the size as result
function Final(out aDigest: THash512Rec; aNoInit: boolean = false): integer; overload;
/// returns the resulting hash as raw binary string, with optional replication
procedure FinalBin(var aResult: RawByteString; aLen: PtrInt = 0);
/// one-step hash computation of a buffer as lowercase hexadecimal string
function Full(aAlgo: THashAlgo; aBuffer: pointer; aLen: integer): RawUtf8; overload;
/// one-step hash computation of a buffer as lowercase hexadecimal string
function Full(aAlgo: THashAlgo; const aBuffer: RawByteString): RawUtf8; overload;
/// one-step hash computation of several buffers as lowercase hexadecimal string
procedure Full(aAlgo: THashAlgo; const aBuffer: array of RawByteString;
var aResult: RawUtf8); overload;
/// one-step hash computation of a buffer as a binary buffer
// - returns the written aDigest size in bytes
function Full(aAlgo: THashAlgo; aBuffer: pointer; aLen: integer;
out aDigest: THash512Rec): integer; overload;
/// one-step hash computation of several buffers as a binary buffer
// - returns the written aDigest size in bytes
function Full(aAlgo: THashAlgo; const aBuffer: array of RawByteString;
out aDigest: THash512Rec): integer; overload;
/// fill a buffer with the MGF1 seed deriviation, following RFC 2437
// - a Mask Generation Function expands aSeed/aSeedLen into aDestLen buffer
function Mgf1(aAlgo: THashAlgo; aSeed: pointer; aSeedLen, aDestLen: PtrUInt): RawByteString;
/// compute the Unix crypt hash of a given password as '$algo$salt$checksum'
// - currently implements safe SHA-256-CRYPT and SHA-512-CRYPT with hfSHA256
// and hfSHA512, as defined in https://www.akkadia.org/drepper/SHA-crypt.txt
// - deprecated MD5-CRYPT can also be generated (and verified) with hfMD5
// - any other algorithm is unsupported, and will fail and return ''
// - see ModularCryptVerify() for the associated verification function
function UnixCryptHash(aAlgo: THashAlgo; const aPassword: RawUtf8;
aRounds: cardinal = 0; aSaltSize: cardinal = 8;
const aSalt: RawUtf8 = ''; aHashPos: PInteger = nil): RawUtf8;
/// returns the number of bytes of the hash of the current Algo
function HashSize: integer;
/// the hash algorithm used by this instance
property Algo: THashAlgo
read fAlgo;
end;
/// TStreamRedirect with TSynHasher cryptographic hashing
// - do not use this abstract class but inherited types with overriden GetAlgo
TStreamRedirectSynHasher = class(TStreamRedirect)
protected
fHash, fHashAppend: TSynHasher;
procedure DoHash(data: pointer; len: integer); override;
procedure AfterAppend; override;
procedure ResetHash; override;
public
// proper implement TStreamRedirect (abstract) virtual methods
constructor Create(aDestination: TStream; aRead: boolean = false); override;
function GetHash: RawUtf8; override;
class function GetHashFileExt: RawUtf8; override;
/// return the current state of the hash as its raw binary and algorithm
function GetHashDigest(out Digest: THashDigest): boolean; overload;
/// will decode an hexadecimal hash into its raw binary and algorithm
class function GetHashDigest(const HexaHash: RawUtf8;
out Digest: THashDigest): boolean; overload;
/// inherited classes will properly return the hash algorithm
class function GetAlgo: THashAlgo; virtual; abstract;
end;
/// meta-class of TStreamRedirectSynHasher
// - to access e.g. GetAlgo/GetHashFileExt class methods
TStreamRedirectSynHasherClass = class of TStreamRedirectSynHasher;
/// TStreamRedirect with MD5 cryptographic hashing
TStreamRedirectMd5 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-1 cryptographic hashing
TStreamRedirectSha1 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-224 cryptographic hashing
TStreamRedirectSha224 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-256 cryptographic hashing
TStreamRedirectSha256 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-384 cryptographic hashing
TStreamRedirectSha384 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-512 cryptographic hashing
TStreamRedirectSha512 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-512/256 cryptographic hashing
TStreamRedirectSha512_256 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3-224 cryptographic hashing
TStreamRedirectSha3_224 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3-256 cryptographic hashing
TStreamRedirectSha3_256 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3-384 cryptographic hashing
TStreamRedirectSha3_384 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3-512 cryptographic hashing
TStreamRedirectSha3_512 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3 Shake128 cryptographic hashing
TStreamRedirectShake128 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
/// TStreamRedirect with SHA-3 Shake256 cryptographic hashing
TStreamRedirectShake256 = class(TStreamRedirectSynHasher)
public
class function GetAlgo: THashAlgo; override;
end;
const
/// convert a THashAlgo into a TStreamRedirectSynHasher class
HASH_STREAMREDIRECT: array[THashAlgo] of TStreamRedirectClass = (
TStreamRedirectMd5, // hfMD5
TStreamRedirectSha1, // hfSHA1
TStreamRedirectSha256, // hfSHA256
TStreamRedirectSha384, // hfSHA384
TStreamRedirectSha512, // hfSHA512
TStreamRedirectSha512_256, // hfSHA512_256
TStreamRedirectSha3_256, // hfSHA3_256
TStreamRedirectSha3_512, // hfSHA3_512
TStreamRedirectSha224, // hfSHA224
TStreamRedirectSha3_224, // hfSHA3_224
TStreamRedirectSha3_384, // hfSHA3_384
TStreamRedirectShake128, // hfShake128
TStreamRedirectShake256); // hfShake256)
/// the standard text of a THashAlgo (in uppercase characters)
HASH_TXT: array[THashAlgo] of RawUtf8 = (
'MD5', 'SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'SHA-512/256',
'SHA3-256', 'SHA3-512', 'SHA-224', 'SHA3-224', 'SHA3-384',
'SHAKE128', 'SHAKE256');
/// the standard text of a THashAlgo (in lowercase characters)
HASH_TXT_LOWER: array[THashAlgo] of RawUtf8 = (
'md5', 'sha-1', 'sha-256', 'sha-384', 'sha-512', 'sha-512/256',
'sha3-256', 'sha3-512', 'sha-224', 'sha3-224', 'sha3-384',
'shake128', 'shake256');
type
/// the HMAC/SHA-1 HMAC/SHA-2 and SHA-3 algorithms known by TSynSigner
// - HMAC/SHA-1 is considered unsafe, HMAC/SHA-2 are well proven, and
// SHA-3 is newer and strong, including HMAC, so a good candidate for safety
TSignAlgo = (
saSha1,
saSha256,
saSha384,
saSha512,
saSha3224,
saSha3256,
saSha3384,
saSha3512,
saSha3S128,
saSha3S256,
saSha224);
PSignAlgo = ^TSignAlgo;
/// the algorithms known by ModularCryptIdentify/ModularCryptVerify
// - mcfMd5Crypt, mcfSha256Crypt and mcfSha512Crypt are handled by
// TSynHasher.UnixCryptVerify() and match $1$ $5$ $6$ common Unix Hashes
// - mcfPbkdf2Sha1, mcfPbkdf2Sha256 and mcfPbkdf2Sha512 match Python's PassLib
// specific-but-useful '$pbkdf2-{digest}${rounds}${salt}${checksum}' format
// - mcfPbkdf2Sha3 is our own SHA3-512 hash algorithm (with no HMAC) extension
// - mcfBCrypt is the BCrypt hashing algorithm, as developed for BSD systems -
// please include the mormot.crypt.other.pas unit to your project to enable it
// - mcfBCryptSha256 will first hash the password with HMAC-SHA-256 to support
// any password, e.g. > than 72 bytes (following passlib.hash.bcrypt_sha256)
// - mcfSCrypt is the SCrypt memory-intensive hashing algorithm, implemented in
// mormot.crypt.other.pas or in mormot.crypt.openssl.pas via global SCrypt()
// - in practice: use safest mcfBCryptSha256 or mcfSCrypt if possible
TModularCryptFormat = (
mcfInvalid,
mcfUnknown,
mcfMd5Crypt,
mcfSha256Crypt,
mcfSha512Crypt,
mcfPbkdf2Sha1,
mcfPbkdf2Sha256,
mcfPbkdf2Sha512,
mcfPbkdf2Sha3,
mcfBCrypt,
mcfBCryptSha256,
mcfSCrypt);
PModularCryptFormat = ^TModularCryptFormat;
/// allow to specify several ModularCryptIdentify/ModularCryptVerify algorithms
TModularCryptFormats = set of TModularCryptFormat;
const
SIGN_SIZE: array[TSignAlgo] of byte = (
20, 32, 48, 64, 28, 32, 48, 64, 32, 64, 28);
/// the standard text of a TSignAlgo
SIGNER_TXT: array[TSignAlgo] of RawUtf8 = (
'SHA-1', 'SHA-256', 'SHA-384', 'SHA-512', 'SHA3-224', 'SHA3-256',
'SHA3-384', 'SHA3-512', 'SHAKE128', 'SHAKE256', 'SHA-224');
SIGNER_SHA3 = [saSha3224 .. saSha3S256];
SIGNER_DEFAULT_SALT = 'I6sWioAidNnhXO9BK';
SIGNER_DEFAULT_ALGO = saSha3S128;
/// which ModularCryptIdentify/ModularCryptVerify() results are correct
mcfValid = [mcfMd5Crypt .. high(TModularCryptFormat)];
/// the maximum number of PBKDF2 rounds which may trigger a DoS attack
// - 5 millions = 1-5 seconds with SHA-NI is noticeable to be painful
MAX_PBKDF2_ROUNDS = 5000000;
var
/// default number of rounds for PBKDF2 "Modular Crypt" functions
// - numbers adjusted on 2025, and align with OWASP Password Storage Cheat
// Sheet, NIST SP 800-63B and RFC 8018, and are higher than "$pbkdf2" passlib
// - typical values on my Core i5-13500 PC with SHA-NI are Pbkdf2Sha1=68.28ms
// Pbkdf2Sha256=35.89ms Pbkdf2Sha512=110.55ms and Pbkdf2Sha3=112.58ms
// - made as a global variable, since you can adjust/tune those values for your
// own purpose, since they are published with the MCF prefix text itself
MCF_ROUNDS: array[mcfMd5Crypt .. mcfPbkdf2Sha3] of cardinal = (
1000, 535000, 535000, 600000, 310000, 210000, 200000);
type
/// JSON-serializable object as used by TSynSigner.Pbkdf2() overloaded methods
// - default value for unspecified parameters will be SIGNER_DEFAULT_ALGO
// (SHAKE_128) with rounds=1000 and a fixed salt
// - a typical (extended) JSON to supply to TSynSigner.Pbkdf2() may be
// ${algo:"sha-512",secret:"StrongPassword",salt:"FixedSalt",rounds:10000}
TSynSignerParams = packed record
algo: TSignAlgo;
secret, salt: RawUtf8;
rounds: integer;
end;
/// a generic wrapper object to handle digital HMAC of any SHA algorithm
// - used e.g. to implement TJwtSynSignerAbstract
{$ifdef USERECORDWITHMETHODS}
TSynSigner = record
{$else}
TSynSigner = object
{$endif USERECORDWITHMETHODS}
private
fAlgo: TSignAlgo;
fSignatureSize, fBlockMax, fBlockSize: byte;
fHasher: TSynHasher; // raw hash algorithm for the HMAC process
fStep7data: TBlock1024; // pre-computed salt for Final() step
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;
aSecretPbkdf2Round: integer; aPbkdf2Secret: PHash512Rec = nil); overload;
/// process some message content supplied as memory buffer
procedure Update(aBuffer: pointer; aLen: integer); overload;
{$ifdef HASINLINE}inline;{$endif}
/// process some message content supplied as string
procedure Update(const aBuffer: RawByteString); overload;
{$ifdef HASINLINE}inline;{$endif}
/// hash 32-bit encoded integer as big endian
procedure UpdateBigEndian(aValue: cardinal);
{$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
function Final(aSignature: PHash512Rec; aNoInit: boolean = false): integer; 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;
aSecretPbkdf2Round: integer; aBuffer: pointer; aLen: integer): RawUtf8; overload;
/// one-step binary digital signature of a buffer with PBKDF2 derivation
function Full(aAlgo: TSignAlgo; aSecret: pointer; aSecretLen: PtrInt;
const aMessage: RawByteString; aHmac: PHash512Rec): integer; overload;
/// one-step hash computation of a buffer as a binary buffer
// - returns the written aDigest size in bytes
function Hash(aAlgo: TSignAlgo; aBuffer: pointer; aLen: integer;
out aDigest: THash512Rec): integer;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
function Pbkdf2(aAlgo: TSignAlgo; const aSecret, aSalt: RawUtf8;
aSecretPbkdf2Round: integer; aDerivatedKey: PHash512Rec;
aPartNumber: integer = 1): PtrInt; 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 e.g.
// ${algo:"saSha512",secret:"StrongPassword",salt:"FixedSalt",rounds:10000}
procedure Pbkdf2(aParamsJson: PUtf8Char; aParamsJsonLen: integer;
out aDerivatedKey: THash512Rec;
const aDefaultSalt: RawUtf8 = SIGNER_DEFAULT_SALT;
aDefaultAlgo: TSignAlgo = SIGNER_DEFAULT_ALGO); overload;
/// convenient wrapper to perform PBKDF2 safe iterative key derivation
// - accept as input a TSynSignerParams serialized as JSON object e.g.
// ${algo:"saSha512",secret:"StrongPassword",salt:"FixedSalt",rounds:10000}
procedure Pbkdf2(const aParamsJson: RawUtf8;
out aDerivatedKey: THash512Rec;
const aDefaultSalt: RawUtf8 = SIGNER_DEFAULT_SALT;
aDefaultAlgo: TSignAlgo = SIGNER_DEFAULT_ALGO); overload;
/// fill a buffer with the PBKDF2 deriviation, following RFC 2898 5.2
// - in respect to other Pbkdf2() methods, the length of the derived
// key is unbounded and could be bigger than the TSignAlgo digest size
function Pbkdf2(aAlgo: TSignAlgo; const aSecret, aSalt: RawUtf8;
aSecretPbkdf2Round, aDestLen: PtrUInt): RawByteString; overload;
/// compute the Modular Crypt hash of a given password as computed by passlib
// pbkdf2.py - i.e. in '$pbkdf2-{digest}${rounds}${salt}${checksum}' format
// - see ModularCryptVerify() for the associated verification function
// - in addition to official passlib format, will include our '$pbkdf2-sha3$'
function Pbkdf2ModularCrypt(aAlgo: TModularCryptFormat; const aPassword: RawUtf8;
aRounds: cardinal = 0; aSaltSize: cardinal = 16;
const aSalt: RawUtf8 = ''; aHashPos: PInteger = nil): RawUtf8;
/// compute NIST SP800-108 KDF in counter mode (section 5.1)
// - as used e.g. by RFC 8009 for Kerberos AES-CTS HMAC-SHA2 modes
function KdfSP800(aAlgo: TSignAlgo; aDestLen: cardinal;
const aKey, aLabel: RawByteString; const aContext: RawByteString = ''): RawByteString;
/// 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 internal context with zeros, for security
procedure Done;
/// the size, in bytes, of the digital signature of this algorithm
// - potential values are 20 (for SHA-1), 28, 32, 48 and 64 (for SHA-512)
property SignatureSize: byte
read fSignatureSize;
/// the algorithm used for digitial signature
property Algo: TSignAlgo
read fAlgo;
end;
/// reference to a TSynSigner wrapper object
PSynSigner = ^TSynSigner;
const
/// map the size in bytes (16..64) of any THashAlgo digest
// - note that SHA-3 or SHA512-256 share the same size with other algos
HASH_SIZE: array[THashAlgo] of byte = (
SizeOf(TMd5Digest), // 16 bytes for hfMD5
SizeOf(TSHA1Digest), // 20 bytes for hfSHA1
SizeOf(TSHA256Digest), // 32 bytes for hfSHA256
SizeOf(TSHA384Digest), // 48 bytes for hfSHA384
SizeOf(TSHA512Digest), // 64 bytes for hfSHA512
SizeOf(THash256), // 32 bytes for hfSHA512_256
SizeOf(THash256), // 32 bytes for hfSHA3_256
SizeOf(THash512), // 64 bytes for hfSHA3_512
SizeOf(THash224), // 28 bytes for hfSHA224
SizeOf(THash224), // 28 bytes for hfSHA3_224
SizeOf(THash384), // 48 bytes for hfSHA3_384
SizeOf(THash128), // 16 bytes for hfShake128
SizeOf(THash256)); // 32 bytes for hfShake256
/// map the file extension text of any THashAlgo digest
// - TextToHashAlgo() is able to recognize those values
HASH_EXT: array[THashAlgo] of RawUtf8 = (
'.md5', // hfMD5
'.sha1', // hfSHA1
'.sha256', // hfSHA256
'.sha384', // hfSHA384
'.sha512', // hfSHA512
'.sha512-256', // hfSHA512_256
'.sha3-256', // hfSHA3_256
'.sha3-512', // hfSHA3_512
'.sha224', // hfSHA224
'.sha3-224', // hfSHA3_224
'.sha3-384', // hfSHA3_384
'.shake128', // hfShake128
'.shake256'); // hfShake256
/// convert a TSignAlgo / TSynSigner algorithm into a THashAlgo / TSynHasher
SIGN_HASH: array[TSignAlgo] of THashAlgo = (
hfSha1, // saSha1
hfSha256, // saSha256
hfSha384, // saSha384
hfSha512, // saSha512
hfSha3_224, // saSha3224
hfSha3_256, // saSha3256
hfSha3_384, // saSha3384
hfSha3_512, // saSha3512
hfShake128, // saSha3S128
hfShake256, // saSha3S256