├── 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 | ![Speedtest results](https://github.com/JackTrapper/bcrypt-for-delphi/blob/master/CalculationTimes.PNG) 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 | --------------------------------------------------------------------------------