├── Bcrypt.pas
├── CalculationTimes.PNG
├── README.md
└── UNLICENSE
/Bcrypt.pas:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackTrapper/bcrypt-for-delphi/528643eed2235987d39fb034419502d5eb081295/Bcrypt.pas
--------------------------------------------------------------------------------
/CalculationTimes.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackTrapper/bcrypt-for-delphi/528643eed2235987d39fb034419502d5eb081295/CalculationTimes.PNG
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Bcrypt for Delphi
2 | ==================
3 |
4 | [Bcrypt](http://en.wikipedia.org/wiki/Bcrypt) is an algorithm designed for hashing passwords, and only passwords; i.e. it:
5 |
6 | - is **not** a high-speed, generic, hashing algorithm
7 | - is **not** a key derivation function (see [PBDKF2](http://en.wikipedia.org/wiki/PBKDF2), [scrypt](http://en.wikipedia.org/wiki/Scrypt), [Argon2](https://en.wikipedia.org/wiki/Argon2))
8 | - is computationally and memory expensive
9 | - is limited to passwords of 72 bytes
10 |
11 | It was first [described by Niels Provos and David Mazières in 1999](http://static.usenix.org/events/usenix99/provos/provos.pdf) for OpenBSD.
12 |
13 | It uses the Blowfish encryption algorithm, but with an "expensive key setup" modification, contained in the function `EksBlowfishSetup`.
14 |
15 | Sample Usage
16 | ----------------
17 |
18 | - To hash a password:
19 |
20 | hash := TBCrypt.HashPassword('correct battery horse staple'); //using default cost factor
21 |
22 | - To hash a password specifying your own cost factor:
23 |
24 | hash := TBCrypt.HashPassword('correct battery horse staple', 14); //specify cost factor 14
25 |
26 | - To verify a password:
27 |
28 | var
29 | passwordRehashNeeded: Boolean;
30 |
31 | isPasswordValid := TBCrypt.CheckPassword('correct battery horse stapler', expectedHash, {out}passwordRehashNeeded);
32 |
33 | The out parameter `passwordRehashNeeded` indicates if the stored password hash needs to be upgraded. A hash would need to be upgraded if:
34 |
35 | - the password hash took less than 250 ms to compute
36 | - the stored hash version (e.g. `2`, `2x`, `2y`) needs to be upgraded (i.e. `2a`, soon to be `2b`)
37 |
38 |
39 | By convention BCrypt outputs a hash as string such as:
40 |
41 | $2a$12$EA6qjRCeBi8bGgs4rhfn8udEGKmu0ayrZYCEJqf6nNIoytowKFncm
42 |
43 | The parts of the string are:
44 |
45 | | Value | Meaning | Notes |
46 | |-------|---------|-------|
47 | | 2a | Hash algorithm | "2a" = current version of BCrypt, "2" = obsolete version of BCrypt, "1" = MD5 |
48 | | 12 | cost factor | Will iterate for 212=4,096 rounds. (Default is 11) |
49 | | EA6qjRCeBi8bGgs4rhfn8u | Salt | 22 base64 encoded characters |
50 | | dEGKmu0ayrZYCEJqf6nNIoytowKFncm | Hashed password | 31 base64 encoded characters |
51 |
52 | Because the **cost factor** is stored with the hash output, bcrypt hashes are backwards and forwards compatible with
53 | changing the number of rounds. It also makes BCrypt extraordinarily convenient in that a random salt is automatically generated and stored for you (you don't have to worry about storing or retrieving it).
54 |
55 | Speed Tests
56 | --------------
57 |
58 | The current (3/21/2015) hard-coded default for cost is **11**. This results in 211 = 2,048 rounds during the key setup.
59 |
60 | 3/14/2015 Intel Core i5-2500 CPU @ 3.50 GHz Delphi XE6 (32-bit, Release)
61 |
62 | | Cost | Iterations | Duration |
63 | |------|-------------------|------------|
64 | | 8 | 256 iterations | 22.0 ms | <-- minimum allowed by BCrypt
65 | | 9 | 512 iterations | 43.3 ms |
66 | | 10 | 1,024 iterations | 85.5 ms |
67 | | 11 | 2,048 iterations | 173.3 ms | <-- current default (BCRYPT_COST=11)
68 | | 12 | 4,096 iterations | 345.6 ms |
69 | | 13 | 8,192 iterations | 694.3 ms |
70 | | 14 | 16,384 iterations | 1,390.5 ms |
71 | | 15 | 32,768 iterations | 2,781.4 ms |
72 | | 16 | 65,536 iterations | 5,564.9 ms |
73 |
74 |
75 | At the time of publication of BCrypt (1999) the default costs were:
76 |
77 | - Normal User: 6
78 | - the Superuser: 8
79 |
80 | > *"Of course, whatever cost people choose should be reevaluated from time to time."*
81 |
82 | - At the time of deployment in 1976, **crypt** could hash fewer than 4 passwords per second. (250 ms per password)
83 | - In 1977, on a VAX-11/780, crypt (MD5) could be evaluated about 3.6 times per second. (277 ms per password)
84 |
85 | We want to target between 250-500 ms per hash. To that end, when calling `HashPassword` the system will automatically determine a cost factor that results in a hash that takes 250-500 ms to compute. It does this by profiling the computer performance. Regardless of the results of the profiling, it will never use a cost lower than the `BCRYPT_COST` constant.
86 |
87 | 
88 |
89 | Bcrypt variants
90 | -------------
91 |
92 | - **$2$** *(June 1999)*
93 |
94 | The original specification used the prefix `$2$`.
95 |
96 | This was in contrast to the other algorithm prefixes in the OpenBSD password file, e.g.:
97 |
98 | - **`$1$`**: [MD5](https://en.wikipedia.org/wiki/MD5)
99 | - **`$5$`**: [SHA2](https://en.wikipedia.org/wiki/SHA-2)-256
100 | - **`$6$`**: [SHA2](https://en.wikipedia.org/wiki/SHA-2)-512
101 |
102 |
103 | - **$2a$**
104 |
105 | The original specification did not define how to handle non-ASCII character, or how to handle a null terminator.
106 | The specification was revised to specify that when hashing strings:
107 |
108 | - the string must be UTF-8 encoded
109 | - the null terminator must be included
110 |
111 | - **$2x$**, **$2y$** *(June 2011)*
112 |
113 | A [bug was discovered](http://seclists.org/oss-sec/2011/q2/632) in [crypt_blowfish](https://pear.php.net/package/Crypt_Blowfish), a PHP implementation of BCrypt. It was mis-handling characters with the 8th bit set.
114 |
115 | 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).
116 | 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.
117 | Hashes saved with `2x` or `2y` are not *"better"*, *"stronger"*, or *"more modern"* than `2a`. They are artifacts of one buggy implementation of bcrypt 9 years ago.
118 |
119 | - **$2b$** *(February 2014)*
120 |
121 | A [bug was discovered](http://undeadly.org/cgi?action=article&sid=20140224132743) in the canonical OpenBSD implemenation of bcrypt.
122 |
123 | They were storing the length of their strings in an unsigned char.
124 | If a password was longer than 255 characters, it would overflow and wrap at 255.
125 | BCrypt was created for OpenBSD. When they have a bug in *their* library, they decided its ok to bump the version.
126 | This means that everyone else needs to follow suit if you want to remain current to "their" specification.
127 |
128 | **Bonus Reading**: OpenBSD mailing list item [*"bcrypt version changes"*](http://marc.info/?l=openbsd-misc&m=139320023202696) (2/24/2014)
129 |
130 | The version `2b` is not *"better"*, *"stronger"*, or *"more modern"* than `2`, or `2a` (or `2x` or `2y`). It is simply a relic of one particular buggy implementation of bcrypt.
131 |
132 | In fact, if you have been:
133 |
134 | - converting the text to bytes using UTF-8 encoding
135 | - including the null terminator
136 | - using a proper string type (as opposed to C's failed attempt to mimic strings with `char *` type)
137 |
138 | then all five versions are functionally identical - for the same input they all generate the same output.
139 |
140 |
141 | Created by [Ian Boyd 5/3/2012](http://stackoverflow.com/a/10441765/9990)
142 |
143 | Public Domain
144 | For more information, please refer to
145 |
146 |
--------------------------------------------------------------------------------
/UNLICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
--------------------------------------------------------------------------------