forked from JackTrapper/bcrypt-for-delphi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBcrypt.pas
2854 lines (2329 loc) · 106 KB
/
Bcrypt.pas
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
unit Bcrypt;
(*
Sample Usage
============
//Hash a password using default cost (e.g. 11 ==> 2^11 ==> 2,048 rounds)
hash := TBCrypt.HashPassword('correct horse battery staple');
//Hash a password using custom cost factor
hash := TBCrypt.HashPassword('correct horse battery staple', 14); //14 ==> 2^14 ==> 16,384 rounds
//Check a password
var
passwordRehashNeeded: Boolean;
TBCrypt.CheckPassword(szPassword, existingHash, {out}passwordRehashNeeded);
Enhanced version
----------------
Enhanced mode pre-hashes the password with SHA2-256. This gives two benfits:
- overcomes the 72-byte limit on passwords; allowing them to be arbitrarily long
- avoids potential denial of service with really long passwords
It is essentially: HashPassword(base64(sha256(password)))
// Hash password with SHA256 prehash:
hash := TBCrytpt.EnhancedHashPassword('correct horse battery staple'); // $bcrypt-sha256$
// Check enhanced password the same way as regular hashes
isPasswordValid := TBCrypt.CheckPassword(szPassword, existingHash, {out}passwordRehashNeeded);
Remarks
=======
Bcrypt is an algorithm designed for hashing passwords, and only passwords.
i.e. It's not a generic, high-speed, generic hashing algorithm.
It's not a password-based key derivation function
It's computationally and memory expensive
It's limited to passwords of 71 bytes.
http://static.usenix.org/events/usenix99/provos/provos.pdf
It uses the Blowfish encryption algorithm, but with an "expensive key setup" modification,
contained in the function EksBlowfishSetup.
Version History
===============
Version 1.17 20201125
- Simplified the performance timestamp to just two functions, and removed the use of GetPerformanceFrequency.
Version 1.16 20201123
- Changed minimum key length from 1 to 0. The known test vectors allow an empty password.
And if a person wants to have an empty password: that's their right.
Version 1.15 20201120
- Idera redefined the "fundamental" LongWord type to no longer be 32-bits. It is now 32-bits on 16-bit platforms, 32-bits on 32-bit platforms, and 64-bits on 64-bit platforms.
And Cardinal, the "generic" type, as been changed to be 16-bits on 16-bit platforms, 32-bits on 32-bit platforms, and 32-bits on 64-bit platforms.
This means they violated the contract of correctly written code - retroactively breaking all existing code.
- Refactored a lot of the OS-specific code into separate functions.
This way you can create your own IFDEFs to plug in your own OS-specific equivalents:
- GetPerformanceTimestamp ==> QueryPerformanceCounter (Windows)
- PerformanceTimestampToMs ==> Timestamp / QueryPerformanceFrequency (Windows)
- GenRandomBytes ==> Crypto API (Windows)
- HashBytes ==> Crypto API (Windows)
Version 1.14 20190823
- Added function to manually prehash a password using SHA-2_256
- Added EnchancedHashPassword to prehash the password and output $bcrypt-sha256$ identifier
- Improvement: unrolled main loop for performance improvement
Version 1.13 20180729
- UIntPtr isn't declared in Delphi 2010 (21.0). Maybe it first appeared in Delphi XE (22.0)?
Version 1.12 20180419
- Made compatible with Delphi 5
- Published all self tests, but put slow ones behind the -SlowUnitTests command line parameter
Version 1.11 20180120
- Bugfix: The raw version of CheckPassword forgot to time the hash operation, and set PasswordRehashNeeded out parameter approriately
Version 1.10 20161212
- Bugfix: Don't zero out password byte array when it is empty - it's a range check error and unneeded
Version 1.09 20161122
- Added: In accordance with the recommendations of NIST SP 800-63B, we now apply KC normalization to the password.
Choice was between NFKC and NFKD. SASLprep (rfc4013), like StringPrep (rfc3454) both specified NFKC.
Version 1.08 20161029
- We now burn the intermediate UTF8 form of the password.
- BCrypt key has a maximum size of 72 bytes. We burn off any excess bytes after converting the string to utf8
- Changed the handling of passwords longer than the 72 characters to match other implemntations.
No longer do we forcibly add a terminating null, but instead simply chop the utf8 array at 72 bytes.
Version 1.07 20160423
- Changed: Fixed up compiler defines so that we work on Delphi 5, Delphi 7, and Delphi XE5 (the only versions of Delphi i use)
- Added the fast SelfTests into their own DUnit test; the ones that are too slow are still kept in the public section
- Added granular abilty to disable BCrypt unit tests
Version 1.06 20151026
- Added: CheckPassword functions now take "PasswordRehashNeeded" out parameter.
The value will contain True if the password needs to be rehashed.
For for example: if the BCrypt cost needs to be increased as the hash was calculated too quickly,
or if the BCrypt standard has been updated (as OpenBSD updated their canonical output to 2b)
- Updated: We now use OS CryptGenRandom for salt generation.
If it fails, we fall back to a type 4 ("random") UUID.
Salt doesn't have to be cryptographically strong, just different.
- Fix: We now recognize version "2" and well as anything version "2"+[letter].
OpenBSD canonical version now outputs "2c".
We will continue to output "2b" for a while, at least hopefully until everyone's updated.
Previous version would fail on anything besides "2a".
In the case of a federated login, we can't have older software that only knows how to handle "2a" suddenly
start failing when it encounters "2b".
- Updated: Removed dependancy on Blowfish.pas. Copied relavent ECB function, state variable, and digits of PI
Version 1.05 20150321
- Performance improvement: Was so worried about using faster verison of Move in BlowfishEncryptECB and BlowfishDecryptECB,
that i didn't stop to notice that i shouldn't even be using Move; but instead a 32-bit assignment
- Fix: Fixed bug in EksBlowfishSetup. If you used a cost factor 31 (2,147,483,648), then the Integer loop
control variable would overflow and the expensive key setup wouldn't run at all (zero iterations)
- The original whitepaper mentions the maximum key length of 56 bytes.
This was a misunderstanding based on the Blowfish maximum recommended key size of 448 bits.
The algorithm can, and does, support up to 72 bytes (e.g. 71 ASCII characters + null terminator).
Note: Variant 2b of bcrypt also *caps* the password length at 72 (to avoid integer wraparound on passwords longer than 2 billion characters O.o)
Version 1.04 20150312
- Performance improvement: ExpandKey: Hoisted loop variable, use xor to calculate SaltHalfIndex to avoid speculative execution jump, unrolled loop to two 32-bit XORs (16% faster)
- Performance improvement (D5,D7): Now use pure pascal version of FastCode Move() (50% faster)
Version 1.03 20150319
- Fix: Defined away Modernizer (so people who are not me can use it)
- Added: If no cost factor is specified when hashing a password,
the cost factor is now a sliding factor, based on Moore's Law and when BCrypt was designed
Version 1.02 20141215
- Added support for XE2 string/UnicodeString/AnsiString
- Update: Updated code to work in 64-bit environment
Version 1.01 20130612
- New: Added HashPassword overload that lets you specify your desired cost
Version 1.0 20120504
- Initial release by Ian Boyd, Public Domain
Background
==========
bcrypt was designed for OpenBSD, where hashes in the password file have a certain format.
The convention used in BSD when generating password hash strings is to format it as:
$version$salt$hash
MD5 hash uses version "1":
"$"+"1"+"$"+salt+hash
bcrypt uses version "2a", but also encodes the cost
"$"+"2a"+"$"+rounds+"$"+salt+hash
e.g.
$2a$10$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm
$==$==$======================-------------------------------
The benfit of this scheme is:
- the number of rounds
- the salt used
This means that stored hashes are backwards and forwards compatible with changing the number of rounds
BCrypt variants
===============
$2$
The original specification used the prefix $2$.
This was in contrast to the other algorithm prefixes:
$1$ - MD5
$5$ - SHA-256
$6$ - SHA-512
$2a$
The original specification did not define how to handle non-ASCII character, or how to handle a null terminator.
The specification was revised to specify that when hashing strings:
- the string must be UTF-8 encoded
- the null terminator must be included
$2x$, $2y$ June 2011
A bug was discovered in crypt_blowfish, a PHP implementation of BCrypt.
It was mis-handling characters with the 8th bit set.
They suggested that system administrators update their existing password database, replacing $2a$ with $2x$,
to indicate that those hashes are bad (and need to use the old broken algorithm).
They also suggested the idea of having crypt_blowfish emit $2y$ for hashes generated by the fixed algorithm.
Nobody else, including canonical OpenBSD, adopted the idea of 2x/2y. This version marker was was limited to crypt_blowfish.
http://seclists.org/oss-sec/2011/q2/632
$2b$ February 2014
A bug was discovered in the OpenBSD implemenation of bcrypt.
They were storing the length of their strings in an unsigned char.
If a password was longer than 255 characters, it would overflow and wrap at 255.
BCrypt was created for OpenBSD. When they have a bug in *their* library, they decided its ok to bump the version.
This means that everyone else needs to follow suit if you want to remain current to "their" specification.
http://undeadly.org/cgi?action=article&sid=20140224132743
http://marc.info/?l=openbsd-misc&m=139320023202696
BCrypt strength
===============
scrypt is weaker than bcrypt for memory requirements less than 4 MB. Stick with at least 16 MB.
https://blog.ircmaxell.com/2014/03/why-i-dont-recommend-scrypt.html#Putting-It-In-Perspective
To put it in perspective, scrypt requires approximately 1000 times the memory
of bcrypt to achieve a comparable level of defense against GPU based attacks
(again, for password storage). On one hand, that's still fine, as bcrypt
uses 4KB, which means the equivalent effective scrypt protection occurs at
4MB. And considering the recommended settings are in the 16MB range, that
should be clear that scrypt is definitively stronger than bcrypt.
This proves that scrypt is demonstrably weaker than bcrypt for password
storage when using memory settings under 4mb. This is why the recommendations
are 16mb or higher. If you're using 16+mb of memory in scrypt (p=1, r=8 and
N=2^14, or p=1, r=1 and N=17), you are fine.
Argon2 is weaker than bcrypt for run times less than 1 second. (i.e. for authentication)
https://twitter.com/jmgosney/status/1111865772656246786
Actually, bcrypt is stronger than Argon2 for authentication (target runtime < 500ms.)
Argon2 does not match or surpass bcrypt's strength until >= ~1000ms runtimes (KDF.)
So "Use Argon2" is not a good one-size-fits-all answer.
*)
interface
uses
SysUtils, Math, SynCrypto, System.Hash, System.Diagnostics, System.TimeSpan, windows,
Types;
type
TBlowfishData= record
InitBlock: array[0..7] of Byte; { initial IV }
LastBlock: array[0..7] of Byte; { current IV }
SBox: array[0..3, 0..255] of UInt32; //4 SBoxes
PBox: array[0..17] of UInt32; //18 subkeys
end;
TBCrypt = class(TObject)
private
class function TryParseHashString(const hashString: string;
out version: string; out Cost: Integer; out Salt: TBytes; out IsEnhanced: Boolean): Boolean;
protected
class function EksBlowfishSetup(const Cost: Integer; const Salt, Key: array of Byte): TBlowfishData;
class procedure ExpandKey(var state: TBlowfishData; const salt, key: array of Byte);
class function CryptCore(const Cost: Integer; Key: array of Byte; salt: array of Byte): TBytes;
class function FormatPasswordHashForBsd(const Version: string; const cost: Integer; const salt: array of Byte; const hash: array of Byte): string;
class function FormatEnhancedPasswordHash(const Version: string; const Cost: Integer; const Salt: array of Byte; const Hash: array of Byte): string;
class function BsdBase64Encode(const data: array of Byte; BytesToEncode: Integer): string;
class function BsdBase64Decode(const s: string): TBytes;
class function PasswordStringPrep(const Source: String): TBytes;
class function SelfTestA: Boolean; //known test vectors
class function SelfTestB: Boolean; //BSD's base64 encoder/decoder
class function SelfTestC: Boolean; //unicode strings in UTF8
class function SelfTestD: Boolean; //different length passwords
class function SelfTestE: Boolean; //salt rng
class function SelfTestF: Boolean; //correctbatteryhorsestapler
class function SelfTestG: Boolean; //check that we support up to 72 characters
class function SelfTestH: Boolean; //check that we don't limit our passwords to 256 characters (as OpenBSD did)
class function SelfTestI: Boolean; //check that we use unicode compatible composition (NFKC) on passwords
class function SelfTestJ: Boolean; //check that composed and decomposed strings both validate to the same
class function SelfTestK: Boolean; //SASLprep rules for passwords
class function SelfTestL: Boolean; //Test prehashing a password (sha256 -> base64)
class function GenRandomBytes(len: Integer; const data: Pointer): HRESULT; //Ask the operating system for len random bytes
class function GetModernCost(SampleCost: Integer; SampleHashDurationMS: Real): Integer;
class function GetModernCost_Benchmark: Integer;
class function TimingSafeSameString(const Safe, User: string): Boolean;
class function PasswordRehashNeededCore(const Version: string; const Cost: Integer; SampleCost: Integer; SampleHashDurationMS: Real): Boolean;
class function HashBytes256(Data: TBytes): string;
class function Base64Encode(const data: array of Byte): string;
public
// Hashes a password into the OpenBSD password-file format (non-standard base-64 encoding). Also validate that BSD style string
class function HashPassword(const Password: String): string; overload;
class function HashPassword(const Password: String; Cost: Integer): string; overload;
class function CheckPassword(const Password: String; const ExpectedHashString: string; out PasswordRehashNeeded: Boolean): Boolean; overload;
// Applies sha-256 preshashing to a password. The returned string is suitable to pass to HashPassword.
class function Prehash256(const password: String): string;
// Performs sha-256 pre-hashing on the password (to allow unlimited password lengths, and avoid DoS attacks). Emits '$bcrypt-sha256$' format.
class function EnhancedHashPassword(const password: String): string; overload;
class function EnhancedHashPassword(const password: String; Cost: Integer): string; overload;
// If you want to handle the cost, salt, and encoding yourself, you can do that.
class function HashPassword(const password: String; const salt: array of Byte; const cost: Integer): TBytes; overload;
class function CheckPassword(const password: String; const salt, hash: array of Byte; const Cost: Integer; out PasswordRehashNeeded: Boolean): Boolean; overload;
class function GenerateSalt: TBytes;
// Evaluate the cost (or version) of a hash string, and figure out if it needs to be rehashed
class function PasswordRehashNeeded(const HashString: string): Boolean;
class function SelfTest: Boolean;
end;
EBCryptException = class(Exception);
implementation
{$IFDEF UnitTests}
{$DEFINE BCryptUnitTests}
{$ENDIF}
{$IFDEF NoBCryptUnitTests}
{$UNDEF BCryptUnitTests}
{$ENDIF}
{$IFDEF BCryptUnitTests}
uses
TestFramework
;
{$ENDIF}
const
BCRYPT_COST = 11; //cost determintes the number of rounds. 11 = 2^11 rounds (2,048)
{
| Cost | Iterations | E6300 | E5-2620 | i5-2500 | i7-2700K |
| | 2006-Q3 | 2012-Q1 | 2011-Q1 | 2011-Q4 |
| | 1.86 GHz | 2 GHz | 3.3 GHz | 3.5 GHz |
|------|-------------------|--------------|-------------|------------|-------------|
| 8 | 256 iterations | 61.65 ms | 48.8 ms | 21.7 ms | 20.8 ms | <-- minimum allowed by BCrypt
| 9 | 512 iterations | 126.09 ms | 77.7 ms | 43.3 ms | 41.5 ms |
| 10 | 1,024 iterations | 249.10 ms | 128.8 ms | 85.5 ms | 83.2 ms |
| 11 | 2,048 iterations | 449.23 ms | 250.1 ms | 173.3 ms | 166.8 ms | <-- current default (BCRYPT_COST=11)
| 12 | 4,096 iterations | 1,007.05 ms | 498.8 ms | 345.6 ms | 333.4 ms |
| 13 | 8,192 iterations | 1,995.48 ms | 999.1 ms | 694.3 ms | 667.9 ms |
| 14 | 16,384 iterations | 4,006.78 ms | 1,997.6 ms | 1,390.5 ms | 1,336.5 ms |
| 15 | 32,768 iterations | 8,027.05 ms | 3,999.9 ms | 2,781.4 ms | 2,670.5 ms |
| 16 | 65,536 iterations | 15,982.14 ms | 8,008.2 ms | 5,564.9 ms | 5,342.8 ms |
| Cost | Iterations | i7-10750H |
| | 2022-Q4 |
| | 2.60 GHz |
|------|-------------------|--------------|
| 8 | 256 iterations | 17.10 ms | <-- minimum allowed by BCrypt
| 9 | 512 iterations | 33.41 ms |
| 10 | 1,024 iterations | 66.00 ms |
| 11 | 2,048 iterations | 130.86 ms | <-- current default (BCRYPT_COST=11)
| 12 | 4,096 iterations | 274.76 ms |
| 13 | 8,192 iterations | 510.37 ms |
| 14 | 16,384 iterations | 1,116.58 ms |
| 15 | 32,768 iterations | 1,879.10 ms |
| 16 | 65,536 iterations | 3,652.44 ms |
At the time of deployment in 1976, crypt could hash fewer than 4 passwords per second. (250 ms per password)
In 1977, on a VAX-11/780, crypt (MD5) could be evaluated about 3.6 times per second. (277 ms per password)
If 277 ms per hash was our target, it would mean a range of 180 ms..360 ms.
At the time of publication of BCrypt (1999) the default costs were:
- Normal User: 6
- the Superuser: 8
"Of course, whatever cost people choose should be reevaluated from time to time."
We want to target between 250-500ms per hash.
}
BCRYPT_SALT_LEN = 16; //bcrypt uses 128-bit (16-byte) salt (This isn't an adjustable parameter, just a name for a constant)
BCRYPT_MaxKeyLen = 72; //72 bytes ==> 71 ansi charcters + null terminator
BsdBase64EncodeTable: array[0..63] of Char =
{ 0:} './'+
{ 2:} 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'+
{28:} 'abcdefghijklmnopqrstuvwxyz'+
{54:} '0123456789';
//the traditional base64 encode table:
//'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
//'abcdefghijklmnopqrstuvwxyz' +
//'0123456789+/';
BsdBase64DecodeTable: array[#0..#127] of Integer = (
{ 0:} -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ________________
{ 16:} -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ________________
{ 32:} -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, // ______________./
{ 48:} 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1, // 0123456789______
{ 64:} -1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, // _ABCDEFGHIJKLMNO
{ 80:} 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, // PQRSTUVWXYZ_____
{ 96:} -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, // _abcdefghijklmno
{113:} 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1); // pqrstuvwxyz_____
TestVectors: array[1..20, 1..3] of string = (
('', '$2a$06$DCq7YPn5Rq63x1Lad4cll.', '$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.'),
('', '$2a$08$HqWuK6/Ng6sg9gQzbLrgb.', '$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye'),
('', '$2a$10$k1wbIrmNyFAPwPVPSVa/ze', '$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW'),
('', '$2a$12$k42ZFHFWqBp3vWli.nIn8u', '$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO'),
('a', '$2a$06$m0CrhHm10qJ3lXRY.5zDGO', '$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe'),
('a', '$2a$08$cfcvVd2aQ8CMvoMpP2EBfe', '$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V.'),
('a', '$2a$10$k87L/MF28Q673VKh8/cPi.', '$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u'),
('a', '$2a$12$8NJH3LsPrANStV6XtBakCe', '$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS'),
('abc', '$2a$06$If6bvum7DFjUnE9p2uDeDu', '$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'),
('abc', '$2a$08$Ro0CUfOqk6cXEKf3dyaM7O', '$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm'),
('abc', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.', '$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi'),
('abc', '$2a$12$EXRkfkdmXn2gzds2SSitu.', '$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q'),
('abcdefghijklmnopqrstuvwxyz', '$2a$06$.rCVZVOThsIa97pEDOxvGu', '$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC'),
('abcdefghijklmnopqrstuvwxyz', '$2a$08$aTsUwsyowQuzRrDqFflhge', '$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz.'),
('abcdefghijklmnopqrstuvwxyz', '$2a$10$fVH8e28OQRj9tqiDXs1e1u', '$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq'),
('abcdefghijklmnopqrstuvwxyz', '$2a$12$D4G5f18o7aMMfwasBL7Gpu', '$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$06$fPIsBO8qRqkjj273rfaOI.', '$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$08$Eq2r4G/76Wv39MzSX262hu', '$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$10$LgfYWkbzEvQ4JakH7rOvHe', '$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS'),
('~!@#$%^&*() ~!@#$%^&*()PNBFRD', '$2a$12$WApznUOJfkEGSmYRfnkrPO', '$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC')
);
SInvalidHashString = 'Invalid base64 hash string';
SBcryptCostRangeError = 'BCrypt cost factor must be between 4..31 (%d)';
SKeyRangeError = 'Key must be between 0 and 72 bytes long (%d)';
SSaltLengthError = 'Salt must be 16 bytes';
SInvalidLength = 'Invalid length';
{
TODO: bcrypt with SHA256 pre-hashing
passlib.hash.bcrypt_sha256 - BCrypt+SHA256¶
https://passlib.readthedocs.io/en/stable/lib/passlib.hash.bcrypt_sha256.html
BCrypt was developed to replace md5_crypt for BSD systems. It uses a modified version of the Blowfish stream cipher.
It does, however, truncate passwords to 72 bytes, and some other minor quirks (see BCrypt Password Truncation for details).
This class works around that issue by first running the password through SHA2-256.
See also: https://blogs.dropbox.com/tech/2016/09/how-dropbox-securely-stores-your-passwords/
who use sha-512 and truncate, rather than sha256.
Format
--------
Bcrypt-SHA256 is compatible with the Modular Crypt Format, and uses $bcrypt-sha256$ as the identifying prefix for all
its strings. An example hash (of password) is:
$bcrypt-sha256$2a,12$LrmaIX5x4TRtAwEfwJZa1.$2ehnw6LvuIUTM0iz4iz9hTxv21B6KFO
Bcrypt-SHA256 hashes have the format
$bcrypt-sha256$[variant],[rounds]$[salt]$[checksum]
where:
- **variant**: is the BCrypt variant in use (usually, as in this case, `2a`).
- **rounds**: is a cost parameter, encoded as decimal integer, which determines the number of iterations used
via `iterations=2**rounds` (rounds is 12 in the example).
- **salt** is a 22 character salt string, using the characters in the regexp range [./A-Za-z0-9]
(LrmaIX5x4TRtAwEfwJZa1. in the example).
- **checksum** is a 31 character checksum, using the same characters as the salt (2ehnw6LvuIUTM0iz4iz9hTxv21B6KFO in the example).
Algorithm
---------
The algorithm this hash uses is as follows:
- first the password is encoded to UTF-8 if not already encoded.
- then it's run through SHA2-256 to generate a 32 byte digest.
- this is encoded using base64, resulting in a 44-byte result (including the trailing padding =).
For the example "password", the output from this stage would be "XohImNooBHFR0OVvjcYpJ3NgPQ1qq73WKhHvch0VQtg=".
- this base64 string is then passed on to the underlying bcrypt algorithm as the new password to be hashed.
}
procedure DebugOutput(const AText: string);
begin
{
This function is the generic "cross platform" version of OutputDebugString.
It is the same name as the Indy function in IdGlobal that exists for the same reason.
In reality their version detects Kylix and DotNet, and does whatever those things have to have.
That's too much work for me.
I understand your pain, GeoffSmith82. But i need it to remain compatible with Delphi 5.
Yes, you heard me. Delphi 5.
}
{$IFDEF MSWINDOWS}
OutputDebugString(PChar(AText));
{$ENDIF}
end;
{$IFDEF BCryptUnitTests}
type
TBCryptTests = class(TTestCase)
public
procedure SpeedTests;
function GetCompilerOptions: string;
public
//These are just too darn slow (as they should be) for continuous testing
procedure SelfTest;
published
procedure SelfTestA_KnownTestVectors; //known test vectors
procedure SelfTestB_Base64EncoderDecoder; //BSD's base64 encoder/decoder
procedure SelfTestC_UnicodeStrings; //unicode strings in UTF8
procedure SelfTestD_VariableLengthPasswords; //different length passwords
procedure SelfTestE_SaltRNG; //salt rng
procedure SelfTestF_CorrectBattery; //correctbatteryhorsestapler
procedure SelfTestG_PasswordLength; //check that we support up to 72 characters
procedure SelfTestH_OpenBSDLengthBug; //check that we don't limit our passwords to 256 characters (as OpenBSD did)
procedure SelfTestI_UnicodeCompatibleComposition; //check that we apply KC normalization (NIST SP 800-63B)
procedure SelfTestJ_NormalizedPasswordsMatch; //
procedure SelfTestK_SASLprep; //
procedure SelfTestL_Prehash;
procedure Test_ParseHashString; //How well we handle past, present, and future versioning strings
procedure TestEnhancedHash;
procedure TestParseEnhancedHash;
procedure Benchmark;
procedure Test_ManualSystem;
end;
{$ENDIF}
procedure BurnString(var s: UnicodeString); overload;
begin
if Length(s) > 0 then
begin
FillChar(s[1], Length(s), 0);
s := '';
end;
end;
{ TBCrypt }
class function TBCrypt.HashPassword(const Password: String): string;
var
cost: Integer;
begin
{
Generate a hash for the specified password using the default cost.
Sample Usage:
hash := TBCrypt.HashPassword('correct horse battery stample');
Rather than using a fixed default cost, use a self-adjusting cost.
We give ourselves two methods:
- Moore's Law sliding constant
- Benchmark
The problem with using Moore's Law is that it's falling behind for single-core performance.
Since 2004, single-core performance is only going up 21% per year, rather than the 26% of Moore's Law.
26%/year ==> doubles every 18 months
21%/year ==> doubles every 44 months
So i could use a more practical variation of Moore's Law. Knowing that it is now doubling every 44 months,
and that i want the target speed to be between 500-750ms, i could use the new value.
The alternative is to run a quick benchmark. It only takes 1.8ms to do a cost=4 hash. Use it benchmark the computer.
The 3rd alternative would be to run the hash as normal, and time it. If it takes less than 500ms to calculate, then
do it again with a cost of BCRYPT_COST+1.
}
cost := TBCrypt.GetModernCost_Benchmark;
if cost < BCRYPT_COST then
cost := BCRYPT_COST;
Result := TBCrypt.HashPassword(password, cost);
end;
class function TBCrypt.HashPassword(const password: String; Cost: Integer): string;
var
salt: TBytes;
hash: TBytes;
begin
{
Generate a hash for the supplied password using the specified cost.
Sample usage:
hash := TBCrypt.HashPassword('Correct battery Horse staple', 13); //Cost factor 13
}
salt := GenerateSalt();
hash := TBCrypt.HashPassword(password, salt, cost);
//20151010 I don't want to emit 2b just yet. The previous bcrypt would fail on anything besides 2a.
//This version handles any single letter suffix. But if we have cross system authentication, and an older system
//tries to validate a 2b password it will fail.
//Wait a year or so until everyone has the new bcrypt
Result := FormatPasswordHashForBsd('2a', cost, salt, hash);
end;
class function TBCrypt.GenerateSalt: TBytes;
var
type4Uuid: TGUID;
salt: TBytes;
begin
//Salt is a 128-bit (16 byte) random value
SetLength(salt, BCRYPT_SALT_LEN);
//20150309 Use real random data. Fallback to random guid if it fails
if Failed(Self.GenRandomBytes(BCRYPT_SALT_LEN, {out}@salt[0])) then
begin
//Type 4 UUID (RFC 4122) is a handy source of (almost) 128-bits of random data (actually 120 bits)
//But the security doesn't come from the salt being secret, it comes from the salt being different each time
CreateGUID(type4Uuid);
Move(type4Uuid.D1, salt[0], BCRYPT_SALT_LEN); //16 bytes
end;
Result := salt;
end;
class function TBCrypt.HashBytes256(Data: TBytes): string;
var
hashSha256 : THashSHA2;
begin
SetLength(Result, 0);
hashSha256 := THashSHA2.Create(THashSHA2.TSHA2Version.SHA256);
hashSha256.Update(Data);
Result := Base64Encode(hashSha256.HashAsBytes);
end;
class function TBCrypt.HashPassword(const password: UnicodeString; const salt: array of Byte; const cost: Integer): TBytes;
var
key: TBytes;
begin
{
The canonical BSD algorithm expects a null-terminated UTF8 key.
If the key is longer than 72 bytes, they truncate the array of bytes to 72.
Yes, this does mean that can can lose the null terminator, and we can chop a multi-byte utf8 code point into an invalid character.
}
//Pseudo-standard dictates that unicode strings are converted to UTF8 (rather than UTF16, UTF32, UTF16LE, ISO-8859-1, Windows-1252, etc)
key := TBCrypt.PasswordStringPrep(password);
try
//Truncate if its longer than 72 bytes (BCRYPT_MaxKeyLen), and burn the excess
if Length(key) > BCRYPT_MaxKeyLen then
begin
FillChar(key[BCRYPT_MaxKeyLen], Length(key)-BCRYPT_MaxKeyLen, 0);
SetLength(key,BCRYPT_MaxKeyLen);
end;
Result := TBCrypt.CryptCore(cost, key, salt);
finally
if Length(key) > 0 then
begin
FillChar(key[0], Length(key), 0);
SetLength(key, 0);
end;
end;
end;
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
type
TSBox = array[0..255] of UInt32;
PSBox = ^TSBox;
{
Encrypt a single 64-bit block encoded as two 32-bit halves.
InData: Pointer to two
}
procedure BlowfishEncryptECB(const Data: TBlowfishData; InData: PUInt32; OutData: PUInt32);
var
xL, xR: UInt32;
S0, S1, S2, S3: PSBox;
begin
xL := PUInt32(InData)^;
xR := PUInt32(UInt32(InData)+4)^;
xL := (xL shr 24) or ((xL shr 8) and $FF00) or ((xL shl 8) and $FF0000) or (xL shl 24);
xR := (xR shr 24) or ((xR shr 8) and $FF00) or ((xR shl 8) and $FF0000) or (xR shl 24);
xL := xL xor Data.PBox[0];
S0 := @Data.SBox[0];
S1 := @Data.SBox[1];
S2 := @Data.SBox[2];
S3 := @Data.SBox[3];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[ 1];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[ 2];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[ 3];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[ 4];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[ 5];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[ 6];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[ 7];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[ 8];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[ 9];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[10];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[11];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[12];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[13];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[14];
xR := xR xor (((S0[(xL shr 24)] + S1[Byte(xL shr 16)]) xor S2[Byte(xL shr 8)]) + S3[Byte(xL)]) xor Data.PBox[15];
xL := xL xor (((S0[(xR shr 24)] + S1[Byte(xR shr 16)]) xor S2[Byte(xR shr 8)]) + S3[Byte(xR)]) xor Data.PBox[16];
xR := xR xor Data.PBox[17];
//Copy xL,xR to output buffer
xL := (xL shr 24) or ((xL shr 8) and $FF00) or ((xL shl 8) and $FF0000) or (xL shl 24);
xR := (xR shr 24) or ((xR shr 8) and $FF00) or ((xR shl 8) and $FF0000) or (xR shl 24);
//Got rid of the moves
PLongWord(OutData)^ := xR;
PLongWord(UIntPtr(OutData)+4)^ := xL;
end;
{$RANGECHECKS ON}
{$OVERFLOWCHECKS ON}
class function TBCrypt.CryptCore(const Cost: Integer; key, salt: array of Byte): TBytes;
var
state: TBlowfishData;
i: Integer;
plainText: array[0..23] of Byte;
cipherText: array[0..23] of Byte;
const
magicText: AnsiString = 'OrpheanBeholderScryDoubt'; //the 24-byte data we will be encrypting 64 times
begin
{
The output of bcrypt is 24-bytes, the result of encrypting "OrpheanBeholderScryDoubt" (24-characters) 64 times.
}
try
state := TBCrypt.EksBlowfishSetup(cost, salt, key);
//Conceptually we are encrypting "OrpheanBeholderScryDoubt" 64 times
Move(magicText[1], plainText[0], 24);
for i := 1 to 64 do
begin
//The painful thing is that the plaintext is 24 bytes long; this is three 8-byte blocks.
//Which means we have to do the EBC encryption on 3 separate sections.
BlowfishEncryptECB(state, Pointer(@plainText[ 0]), Pointer(@cipherText[ 0]));
BlowfishEncryptECB(state, Pointer(@plainText[ 8]), Pointer(@cipherText[ 8]));
BlowfishEncryptECB(state, Pointer(@plainText[16]), Pointer(@cipherText[16]));
Move(cipherText[0], plainText[0], 24);
end;
//Copy final cipherText to Result
SetLength(Result, 24);
Move(cipherText[0], Result[0], 24);
finally
//Burn cipher state
FillChar(state, SizeOf(state), 0);
FillChar(plainText[0], SizeOf(plainText), 0);
FillChar(cipherText[0], 24, 0);
end;
end;
class function TBCrypt.EksBlowfishSetup(const Cost: Integer; const Salt, Key: array of Byte): TBlowfishData;
var
rounds: Cardinal; //rounds = 2^cost
i: Cardinal;
len: Integer;
const
zero: array[0..15] of Byte = (0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0);
{
P subkeys and Substitution Boxes (SBoxes) are the hex (base-16) fractional digits of PI:
3.243f6a88 85a308d3 13198a2e 03707344 a4093822 299f31d0 082efa98 ec4e6c89 452821e6 38d01377 be5466cf 34e90c6c c0ac29b7 c97c50dd 3f84d5b5 b5470917 9216d5d9 8979fb1b
d1310ba6 98dfb5ac 2ffd72db d01adfb7 b8e1afed 6a267e96 ba7c9045 f12c7f99 24a19947 b3916cf7 0801f2e2 858efc16 636920d8 71574e69 a458fea3 f4933d7e 0d95748f 728eb658
718bcd58 82154aee 7b54a41d c25a59b5 9c30d539 2af26013 c5d1b023 286085f0 ca417918 b8db38ef 8e79dcb0 603a180e 6c9e0e8b b01e8a3e d71577c1 bd314b27 78af2fda 55605c60
e65525f3 aa55ab94 57489862 63e81440 55ca396a 2aab10b6 b4cc5c34 1141e8ce a15486af 7c72e993 b3ee1411 636fbc2a 2ba9c55d 741831f6 ce5c3e16 9b87931e afd6ba33 6c24cf5c
7a325381 28958677 3b8f4898 6b4bb9af c4bfe81b 66282193 61d809cc fb21a991 487cac60 5dec8032 ef845d5d e98575b1 dc262302 eb651b88 23893e81 d396acc5 0f6d6ff3 83f44239
2e0b4482 a4842004 69c8f04a 9e1f9b5e 21c66842 f6e96c9a 670c9c61 abd388f0 6a51a0d2 d8542f68 960fa728 ab5133a3 6eef0b6c 137a3be4 ba3bf050 7efb2a98 a1f1651d 39af0176
66ca593e 82430e88 8cee8619 456f9fb4 7d84a5c3 3b8b5ebe e06f75d8 85c12073 401a449f 56c16aa6 4ed3aa62 363f7706 1bfedf72 429b023d 37d0d724 d00a1248 db0fead3 p0......
The amount of hex digits can be increased if you want to experiment with more rounds and longer key lengths.
}
PBox: array[0..17] of UInt32 = (
$243f6a88, $85a308d3, $13198a2e, $03707344, $a4093822, $299f31d0,
$082efa98, $ec4e6c89, $452821e6, $38d01377, $be5466cf, $34e90c6c,
$c0ac29b7, $c97c50dd, $3f84d5b5, $b5470917, $9216d5d9, $8979fb1b);
SBox0: array[0..255] of UInt32 = (
$d1310ba6, $98dfb5ac, $2ffd72db, $d01adfb7, $b8e1afed, $6a267e96, $ba7c9045, $f12c7f99,
$24a19947, $b3916cf7, $0801f2e2, $858efc16, $636920d8, $71574e69, $a458fea3, $f4933d7e,
$0d95748f, $728eb658, $718bcd58, $82154aee, $7b54a41d, $c25a59b5, $9c30d539, $2af26013,
$c5d1b023, $286085f0, $ca417918, $b8db38ef, $8e79dcb0, $603a180e, $6c9e0e8b, $b01e8a3e,
$d71577c1, $bd314b27, $78af2fda, $55605c60, $e65525f3, $aa55ab94, $57489862, $63e81440,
$55ca396a, $2aab10b6, $b4cc5c34, $1141e8ce, $a15486af, $7c72e993, $b3ee1411, $636fbc2a,
$2ba9c55d, $741831f6, $ce5c3e16, $9b87931e, $afd6ba33, $6c24cf5c, $7a325381, $28958677,
$3b8f4898, $6b4bb9af, $c4bfe81b, $66282193, $61d809cc, $fb21a991, $487cac60, $5dec8032,
$ef845d5d, $e98575b1, $dc262302, $eb651b88, $23893e81, $d396acc5, $0f6d6ff3, $83f44239,
$2e0b4482, $a4842004, $69c8f04a, $9e1f9b5e, $21c66842, $f6e96c9a, $670c9c61, $abd388f0,
$6a51a0d2, $d8542f68, $960fa728, $ab5133a3, $6eef0b6c, $137a3be4, $ba3bf050, $7efb2a98,
$a1f1651d, $39af0176, $66ca593e, $82430e88, $8cee8619, $456f9fb4, $7d84a5c3, $3b8b5ebe,
$e06f75d8, $85c12073, $401a449f, $56c16aa6, $4ed3aa62, $363f7706, $1bfedf72, $429b023d,
$37d0d724, $d00a1248, $db0fead3, $49f1c09b, $075372c9, $80991b7b, $25d479d8, $f6e8def7,
$e3fe501a, $b6794c3b, $976ce0bd, $04c006ba, $c1a94fb6, $409f60c4, $5e5c9ec2, $196a2463,
$68fb6faf, $3e6c53b5, $1339b2eb, $3b52ec6f, $6dfc511f, $9b30952c, $cc814544, $af5ebd09,
$bee3d004, $de334afd, $660f2807, $192e4bb3, $c0cba857, $45c8740f, $d20b5f39, $b9d3fbdb,
$5579c0bd, $1a60320a, $d6a100c6, $402c7279, $679f25fe, $fb1fa3cc, $8ea5e9f8, $db3222f8,
$3c7516df, $fd616b15, $2f501ec8, $ad0552ab, $323db5fa, $fd238760, $53317b48, $3e00df82,
$9e5c57bb, $ca6f8ca0, $1a87562e, $df1769db, $d542a8f6, $287effc3, $ac6732c6, $8c4f5573,
$695b27b0, $bbca58c8, $e1ffa35d, $b8f011a0, $10fa3d98, $fd2183b8, $4afcb56c, $2dd1d35b,
$9a53e479, $b6f84565, $d28e49bc, $4bfb9790, $e1ddf2da, $a4cb7e33, $62fb1341, $cee4c6e8,
$ef20cada, $36774c01, $d07e9efe, $2bf11fb4, $95dbda4d, $ae909198, $eaad8e71, $6b93d5a0,
$d08ed1d0, $afc725e0, $8e3c5b2f, $8e7594b7, $8ff6e2fb, $f2122b64, $8888b812, $900df01c,
$4fad5ea0, $688fc31c, $d1cff191, $b3a8c1ad, $2f2f2218, $be0e1777, $ea752dfe, $8b021fa1,
$e5a0cc0f, $b56f74e8, $18acf3d6, $ce89e299, $b4a84fe0, $fd13e0b7, $7cc43b81, $d2ada8d9,
$165fa266, $80957705, $93cc7314, $211a1477, $e6ad2065, $77b5fa86, $c75442f5, $fb9d35cf,
$ebcdaf0c, $7b3e89a0, $d6411bd3, $ae1e7e49, $00250e2d, $2071b35e, $226800bb, $57b8e0af,
$2464369b, $f009b91e, $5563911d, $59dfa6aa, $78c14389, $d95a537f, $207d5ba2, $02e5b9c5,
$83260376, $6295cfa9, $11c81968, $4e734a41, $b3472dca, $7b14a94a, $1b510052, $9a532915,
$d60f573f, $bc9bc6e4, $2b60a476, $81e67400, $08ba6fb5, $571be91f, $f296ec6b, $2a0dd915,
$b6636521, $e7b9f9b6, $ff34052e, $c5855664, $53b02d5d, $a99f8fa1, $08ba4799, $6e85076a);
SBox1: array[0..255] of UInt32 = (
$4b7a70e9, $b5b32944, $db75092e, $c4192623, $ad6ea6b0, $49a7df7d, $9cee60b8, $8fedb266,
$ecaa8c71, $699a17ff, $5664526c, $c2b19ee1, $193602a5, $75094c29, $a0591340, $e4183a3e,
$3f54989a, $5b429d65, $6b8fe4d6, $99f73fd6, $a1d29c07, $efe830f5, $4d2d38e6, $f0255dc1,
$4cdd2086, $8470eb26, $6382e9c6, $021ecc5e, $09686b3f, $3ebaefc9, $3c971814, $6b6a70a1,
$687f3584, $52a0e286, $b79c5305, $aa500737, $3e07841c, $7fdeae5c, $8e7d44ec, $5716f2b8,
$b03ada37, $f0500c0d, $f01c1f04, $0200b3ff, $ae0cf51a, $3cb574b2, $25837a58, $dc0921bd,
$d19113f9, $7ca92ff6, $94324773, $22f54701, $3ae5e581, $37c2dadc, $c8b57634, $9af3dda7,
$a9446146, $0fd0030e, $ecc8c73e, $a4751e41, $e238cd99, $3bea0e2f, $3280bba1, $183eb331,
$4e548b38, $4f6db908, $6f420d03, $f60a04bf, $2cb81290, $24977c79, $5679b072, $bcaf89af,
$de9a771f, $d9930810, $b38bae12, $dccf3f2e, $5512721f, $2e6b7124, $501adde6, $9f84cd87,
$7a584718, $7408da17, $bc9f9abc, $e94b7d8c, $ec7aec3a, $db851dfa, $63094366, $c464c3d2,
$ef1c1847, $3215d908, $dd433b37, $24c2ba16, $12a14d43, $2a65c451, $50940002, $133ae4dd,
$71dff89e, $10314e55, $81ac77d6, $5f11199b, $043556f1, $d7a3c76b, $3c11183b, $5924a509,
$f28fe6ed, $97f1fbfa, $9ebabf2c, $1e153c6e, $86e34570, $eae96fb1, $860e5e0a, $5a3e2ab3,
$771fe71c, $4e3d06fa, $2965dcb9, $99e71d0f, $803e89d6, $5266c825, $2e4cc978, $9c10b36a,
$c6150eba, $94e2ea78, $a5fc3c53, $1e0a2df4, $f2f74ea7, $361d2b3d, $1939260f, $19c27960,
$5223a708, $f71312b6, $ebadfe6e, $eac31f66, $e3bc4595, $a67bc883, $b17f37d1, $018cff28,
$c332ddef, $be6c5aa5, $65582185, $68ab9802, $eecea50f, $db2f953b, $2aef7dad, $5b6e2f84,
$1521b628, $29076170, $ecdd4775, $619f1510, $13cca830, $eb61bd96, $0334fe1e, $aa0363cf,
$b5735c90, $4c70a239, $d59e9e0b, $cbaade14, $eecc86bc, $60622ca7, $9cab5cab, $b2f3846e,
$648b1eaf, $19bdf0ca, $a02369b9, $655abb50, $40685a32, $3c2ab4b3, $319ee9d5, $c021b8f7,
$9b540b19, $875fa099, $95f7997e, $623d7da8, $f837889a, $97e32d77, $11ed935f, $16681281,
$0e358829, $c7e61fd6, $96dedfa1, $7858ba99, $57f584a5, $1b227263, $9b83c3ff, $1ac24696,
$cdb30aeb, $532e3054, $8fd948e4, $6dbc3128, $58ebf2ef, $34c6ffea, $fe28ed61, $ee7c3c73,
$5d4a14d9, $e864b7e3, $42105d14, $203e13e0, $45eee2b6, $a3aaabea, $db6c4f15, $facb4fd0,
$c742f442, $ef6abbb5, $654f3b1d, $41cd2105, $d81e799e, $86854dc7, $e44b476a, $3d816250,
$cf62a1f2, $5b8d2646, $fc8883a0, $c1c7b6a3, $7f1524c3, $69cb7492, $47848a0b, $5692b285,
$095bbf00, $ad19489d, $1462b174, $23820e00, $58428d2a, $0c55f5ea, $1dadf43e, $233f7061,
$3372f092, $8d937e41, $d65fecf1, $6c223bdb, $7cde3759, $cbee7460, $4085f2a7, $ce77326e,
$a6078084, $19f8509e, $e8efd855, $61d99735, $a969a7aa, $c50c06c2, $5a04abfc, $800bcadc,
$9e447a2e, $c3453484, $fdd56705, $0e1e9ec9, $db73dbd3, $105588cd, $675fda79, $e3674340,
$c5c43465, $713e38d8, $3d28f89e, $f16dff20, $153e21e7, $8fb03d4a, $e6e39f2b, $db83adf7);
SBox2: array[0..255] of UInt32 = (
$e93d5a68, $948140f7, $f64c261c, $94692934, $411520f7, $7602d4f7, $bcf46b2e, $d4a20068,
$d4082471, $3320f46a, $43b7d4b7, $500061af, $1e39f62e, $97244546, $14214f74, $bf8b8840,
$4d95fc1d, $96b591af, $70f4ddd3, $66a02f45, $bfbc09ec, $03bd9785, $7fac6dd0, $31cb8504,
$96eb27b3, $55fd3941, $da2547e6, $abca0a9a, $28507825, $530429f4, $0a2c86da, $e9b66dfb,
$68dc1462, $d7486900, $680ec0a4, $27a18dee, $4f3ffea2, $e887ad8c, $b58ce006, $7af4d6b6,
$aace1e7c, $d3375fec, $ce78a399, $406b2a42, $20fe9e35, $d9f385b9, $ee39d7ab, $3b124e8b,
$1dc9faf7, $4b6d1856, $26a36631, $eae397b2, $3a6efa74, $dd5b4332, $6841e7f7, $ca7820fb,
$fb0af54e, $d8feb397, $454056ac, $ba489527, $55533a3a, $20838d87, $fe6ba9b7, $d096954b,
$55a867bc, $a1159a58, $cca92963, $99e1db33, $a62a4a56, $3f3125f9, $5ef47e1c, $9029317c,
$fdf8e802, $04272f70, $80bb155c, $05282ce3, $95c11548, $e4c66d22, $48c1133f, $c70f86dc,
$07f9c9ee, $41041f0f, $404779a4, $5d886e17, $325f51eb, $d59bc0d1, $f2bcc18f, $41113564,
$257b7834, $602a9c60, $dff8e8a3, $1f636c1b, $0e12b4c2, $02e1329e, $af664fd1, $cad18115,
$6b2395e0, $333e92e1, $3b240b62, $eebeb922, $85b2a20e, $e6ba0d99, $de720c8c, $2da2f728,
$d0127845, $95b794fd, $647d0862, $e7ccf5f0, $5449a36f, $877d48fa, $c39dfd27, $f33e8d1e,
$0a476341, $992eff74, $3a6f6eab, $f4f8fd37, $a812dc60, $a1ebddf8, $991be14c, $db6e6b0d,
$c67b5510, $6d672c37, $2765d43b, $dcd0e804, $f1290dc7, $cc00ffa3, $b5390f92, $690fed0b,
$667b9ffb, $cedb7d9c, $a091cf0b, $d9155ea3, $bb132f88, $515bad24, $7b9479bf, $763bd6eb,
$37392eb3, $cc115979, $8026e297, $f42e312d, $6842ada7, $c66a2b3b, $12754ccc, $782ef11c,
$6a124237, $b79251e7, $06a1bbe6, $4bfb6350, $1a6b1018, $11caedfa, $3d25bdd8, $e2e1c3c9,
$44421659, $0a121386, $d90cec6e, $d5abea2a, $64af674e, $da86a85f, $bebfe988, $64e4c3fe,
$9dbc8057, $f0f7c086, $60787bf8, $6003604d, $d1fd8346, $f6381fb0, $7745ae04, $d736fccc,
$83426b33, $f01eab71, $b0804187, $3c005e5f, $77a057be, $bde8ae24, $55464299, $bf582e61,
$4e58f48f, $f2ddfda2, $f474ef38, $8789bdc2, $5366f9c3, $c8b38e74, $b475f255, $46fcd9b9,
$7aeb2661, $8b1ddf84, $846a0e79, $915f95e2, $466e598e, $20b45770, $8cd55591, $c902de4c,
$b90bace1, $bb8205d0, $11a86248, $7574a99e, $b77f19b6, $e0a9dc09, $662d09a1, $c4324633,
$e85a1f02, $09f0be8c, $4a99a025, $1d6efe10, $1ab93d1d, $0ba5a4df, $a186f20f, $2868f169,
$dcb7da83, $573906fe, $a1e2ce9b, $4fcd7f52, $50115e01, $a70683fa, $a002b5c4, $0de6d027,
$9af88c27, $773f8641, $c3604c06, $61a806b5, $f0177a28, $c0f586e0, $006058aa, $30dc7d62,
$11e69ed7, $2338ea63, $53c2dd94, $c2c21634, $bbcbee56, $90bcb6de, $ebfc7da1, $ce591d76,
$6f05e409, $4b7c0188, $39720a3d, $7c927c24, $86e3725f, $724d9db9, $1ac15bb4, $d39eb8fc,
$ed545578, $08fca5b5, $d83d7cd3, $4dad0fc4, $1e50ef5e, $b161e6f8, $a28514d9, $6c51133c,
$6fd5c7e7, $56e14ec4, $362abfce, $ddc6c837, $d79a3234, $92638212, $670efa8e, $406000e0);
SBox3: array[0..255] of UInt32 = (
$3a39ce37, $d3faf5cf, $abc27737, $5ac52d1b, $5cb0679e, $4fa33742, $d3822740, $99bc9bbe,
$d5118e9d, $bf0f7315, $d62d1c7e, $c700c47b, $b78c1b6b, $21a19045, $b26eb1be, $6a366eb4,
$5748ab2f, $bc946e79, $c6a376d2, $6549c2c8, $530ff8ee, $468dde7d, $d5730a1d, $4cd04dc6,
$2939bbdb, $a9ba4650, $ac9526e8, $be5ee304, $a1fad5f0, $6a2d519a, $63ef8ce2, $9a86ee22,
$c089c2b8, $43242ef6, $a51e03aa, $9cf2d0a4, $83c061ba, $9be96a4d, $8fe51550, $ba645bd6,
$2826a2f9, $a73a3ae1, $4ba99586, $ef5562e9, $c72fefd3, $f752f7da, $3f046f69, $77fa0a59,
$80e4a915, $87b08601, $9b09e6ad, $3b3ee593, $e990fd5a, $9e34d797, $2cf0b7d9, $022b8b51,
$96d5ac3a, $017da67d, $d1cf3ed6, $7c7d2d28, $1f9f25cf, $adf2b89b, $5ad6b472, $5a88f54c,
$e029ac71, $e019a5e6, $47b0acfd, $ed93fa9b, $e8d3c48d, $283b57cc, $f8d56629, $79132e28,
$785f0191, $ed756055, $f7960e44, $e3d35e8c, $15056dd4, $88f46dba, $03a16125, $0564f0bd,
$c3eb9e15, $3c9057a2, $97271aec, $a93a072a, $1b3f6d9b, $1e6321f5, $f59c66fb, $26dcf319,
$7533d928, $b155fdf5, $03563482, $8aba3cbb, $28517711, $c20ad9f8, $abcc5167, $ccad925f,
$4de81751, $3830dc8e, $379d5862, $9320f991, $ea7a90c2, $fb3e7bce, $5121ce64, $774fbe32,
$a8b6e37e, $c3293d46, $48de5369, $6413e680, $a2ae0810, $dd6db224, $69852dfd, $09072166,
$b39a460a, $6445c0dd, $586cdecf, $1c20c8ae, $5bbef7dd, $1b588d40, $ccd2017f, $6bb4e3bb,
$dda26a7e, $3a59ff45, $3e350a44, $bcb4cdd5, $72eacea8, $fa6484bb, $8d6612ae, $bf3c6f47,
$d29be463, $542f5d9e, $aec2771b, $f64e6370, $740e0d8d, $e75b1357, $f8721671, $af537d5d,
$4040cb08, $4eb4e2cc, $34d2466a, $0115af84, $e1b00428, $95983a1d, $06b89fb4, $ce6ea048,
$6f3f3b82, $3520ab82, $011a1d4b, $277227f8, $611560b1, $e7933fdc, $bb3a792b, $344525bd,
$a08839e1, $51ce794b, $2f32c9b7, $a01fbac9, $e01cc87e, $bcc7d1f6, $cf0111c3, $a1e8aac7,
$1a908749, $d44fbd9a, $d0dadecb, $d50ada38, $0339c32a, $c6913667, $8df9317c, $e0b12b4f,
$f79e59b7, $43f5bb3a, $f2d519ff, $27d9459c, $bf97222c, $15e6fc2a, $0f91fc71, $9b941525,
$fae59361, $ceb69ceb, $c2a86459, $12baa8d1, $b6c1075e, $e3056a0c, $10d25065, $cb03a442,
$e0ec6e0e, $1698db3b, $4c98a0be, $3278e964, $9f1f9532, $e0d392df, $d3a0342b, $8971f21e,
$1b0a7441, $4ba3348c, $c5be7120, $c37632d8, $df359f8d, $9b992f2e, $e60b6f47, $0fe3f11d,
$e54cda54, $1edad891, $ce6279cf, $cd3e7e6f, $1618b166, $fd2c1d05, $848fd2c5, $f6fb2299,
$f523f357, $a6327623, $93a83531, $56cccd02, $acf08162, $5a75ebb5, $6e163697, $88d273cc,
$de966292, $81b949d0, $4c50901b, $71c65614, $e6c6c7bd, $327a140a, $45e1d006, $c3f27b9a,
$c9aa53fd, $62a80f00, $bb25bfe2, $35bdd2f6, $71126905, $b2040222, $b6cbcf7c, $cd769c2b,
$53113ec0, $1640e3d3, $38abbd60, $2547adf0, $ba38209c, $f746ce76, $77afa1c5, $20756060,
$85cbfe4e, $8ae88dd8, $7aaaf9b0, $4cf9aa7e, $1948c25c, $02fb8a8c, $01c36ae4, $d6ebe1f9,
$90d4f869, $a65cdea0, $3f09252d, $c208e69f, $b74e6132, $ce77e25b, $578fdfe3, $3ac372e6);
begin
//Expensive key setup
if (cost < 4) or (cost > 31) then
raise EBCryptException.CreateFmt(SBcryptCostRangeError, [cost]); //'BCrypt cost factor must be between 4..31 (%d)'
len := Length(Key);
if (Len > BCRYPT_MaxKeyLen) then //maximum of 72 bytes
raise EBCryptException.CreateFmt(SKeyRangeError, [Len]); //'Key must be between 0 and 72 bytes long (%d)'
if Length(Salt) <> BCRYPT_SALT_LEN then
raise EBCryptException.Create(SSaltLengthError); //'Salt must be 16 bytes'
//Copy S and P boxes into local state
Move(SBox0, Result.SBox[0], Sizeof(SBox0));
Move(SBox1, Result.SBox[1], Sizeof(SBox1));
Move(SBox2, Result.SBox[2], Sizeof(SBox2));
Move(SBox3, Result.SBox[3], Sizeof(SBox3));
Move(PBox, Result.PBox, Sizeof(PBox));
//This is the normal Blowfish state setup
Self.ExpandKey({var} Result, salt, key);
//This is the "Expensive" part of the "Expensive Key Setup"
rounds := 1 shl cost; //rounds = 2^cost
for i := 1 to rounds do
begin
Self.ExpandKey({var} Result, zero, key);
Self.ExpandKey({var} Result, zero, salt);
end;
//Result := what it is
end;
class function TBCrypt.EnhancedHashPassword(const password: UnicodeString; Cost: Integer): string;
var
salt: TBytes;
prehashedPassword: UnicodeString;
hash: TBytes;
begin
{
Generate a hash for the supplied password using the specified cost.
Sample usage:
hash := TBCrypt.EnhancedHashPassword('Correct battery Horse staple', 13); //Cost factor 13
Returned hash format:
$bcrypt-sha256$2a,12$LrmaIX5x4TRtAwEfwJZa1.$2ehnw6LvuIUTM0iz4iz9hTxv21B6KFO
\_____________/\/ \/ \____________________/ \_____________________________/
Identifier | | Salt Hash
Version Cost factor
}
salt := GenerateSalt();
prehashedPassword := TBCrypt.Prehash256(password);
try
hash := TBCrypt.HashPassword(prehashedPassword, salt, cost);
finally
BurnString({var}prehashedPassword);
end;
Result := FormatEnhancedPasswordHash('2b', Cost, salt, hash);
end;
class function TBCrypt.EnhancedHashPassword(const password: UnicodeString): string;