diff --git a/BENCHMARK.md b/BENCHMARK.md
index f6229c5..43af871 100644
--- a/BENCHMARK.md
+++ b/BENCHMARK.md
@@ -9,100 +9,101 @@ Libraries:
With 5MB message (10 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | ------------- | ------------------------------ | --------------------------- | --------------------------- |
-| MD4 | **2.26 Gbps** | 1.01 Gbps
`2.23x slow` | | |
-| MD5 | **1.53 Gbps** | 870 Mbps
`1.76x slow` | 1.4 Gbps
`1.09x slow` | 928 Mbps
`1.65x slow` |
-| HMAC(MD5) | **1.51 Gbps** | | 1.44 Gbps
`1.06x slow` | 934 Mbps
`1.62x slow` |
-| SHA-1 | **1.28 Gbps** | 524 Mbps
`2.45x slow` | 1.14 Gbps
`1.12x slow` | 532 Mbps
`2.41x slow` |
-| HMAC(SHA-1) | **1.28 Gbps** | | 1.16 Gbps
`1.11x slow` | |
-| SHA-224 | **971 Mbps** | 245 Mbps
`3.96x slow` | 914 Mbps
`1.06x slow` | 245 Mbps
`3.97x slow` |
-| SHA-256 | **1.03 Gbps** | 240 Mbps
`4.28x slow` | 905 Mbps
`1.14x slow` | 242 Mbps
`4.25x slow` |
-| HMAC(SHA-256) | **1.04 Gbps** | | 925 Mbps
`1.12x slow` | |
-| SHA-384 | **1.9 Gbps** | 57.57 Mbps
`33.05x slow` | 661 Mbps
`2.88x slow` | 183 Mbps
`10.41x slow` |
-| SHA-512 | **1.93 Gbps** | 58.29 Mbps
`33.13x slow` | 663 Mbps
`2.91x slow` | 185 Mbps
`10.43x slow` |
-| SHA3-256 | **1.04 Gbps** | 32.59 Mbps
`31.86x slow` | | |
-| SHA3-512 | **1.92 Gbps** | 17.27 Mbps
`111.36x slow` | | |
-| RIPEMD-128 | **1.29 Gbps** | 509 Mbps
`2.54x slow` | | |
-| RIPEMD-160 | **699 Mbps** | 356 Mbps
`1.96x slow` | | 375 Mbps
`1.86x slow` |
-| RIPEMD-256 | **1.42 Gbps** | 505 Mbps
`2.81x slow` | | |
-| RIPEMD-320 | **666 Mbps** | 346 Mbps
`1.93x slow` | | |
-| BLAKE-2s | **1.52 Gbps** | | | |
-| BLAKE-2b | **2.01 Gbps** | 162 Mbps
`12.41x slow` | | |
-| Poly1305 | **4.51 Gbps** | 1.52 Gbps
`2.97x slow` | | |
-| XXH32 | **5.42 Gbps** | | | |
-| XXH64 | **5.85 Gbps** | | | |
-| XXH3 | **1.4 Gbps** | | | |
-| XXH128 | **1.47 Gbps** | | | |
-| SM3 | **886 Mbps** | 253 Mbps
`3.51x slow` | | |
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | ------------- | ----------------------------- | --------------------------- | -------------------------- |
+| MD4 | **2.07 Gbps** | 828 Mbps
`2.5x slow` | | |
+| MD5 | **1.41 Gbps** | 724 Mbps
`1.95x slow` | 1.13 Gbps
`1.25x slow` | 627 Mbps
`2.26x slow` |
+| HMAC(MD5) | **1.3 Gbps** | | 1.12 Gbps
`1.16x slow` | 625 Mbps
`2.08x slow` |
+| SHA-1 | **1.21 Gbps** | 456 Mbps
`2.65x slow` | 849 Mbps
`1.42x slow` | 362 Mbps
`3.34x slow` |
+| HMAC(SHA-1) | **1.21 Gbps** | | 844 Mbps
`1.43x slow` | |
+| SHA-224 | **810 Mbps** | 176 Mbps
`4.6x slow` | 723 Mbps
`1.12x slow` | 170 Mbps
`4.76x slow` |
+| SHA-256 | **809 Mbps** | 179 Mbps
`4.52x slow` | 724 Mbps
`1.12x slow` | 170 Mbps
`4.77x slow` |
+| HMAC(SHA-256) | **813 Mbps** | | 708 Mbps
`1.15x slow` | |
+| SHA-384 | **1.27 Gbps** | 44.66 Mbps
`28.36x slow` | 445 Mbps
`2.85x slow` | 150 Mbps
`8.43x slow` |
+| SHA-512 | **1.26 Gbps** | 45.17 Mbps
`27.94x slow` | 445 Mbps
`2.84x slow` | 151 Mbps
`8.38x slow` |
+| SHA3-256 | **812 Mbps** | 26.32 Mbps
`30.86x slow` | | |
+| SHA3-512 | **1.27 Gbps** | 13.91 Mbps
`91.63x slow` | | |
+| RIPEMD-128 | **1.77 Gbps** | 368 Mbps
`4.8x slow` | | |
+| RIPEMD-160 | **551 Mbps** | 234 Mbps
`2.35x slow` | | 276 Mbps
`2x slow` |
+| RIPEMD-256 | **1.9 Gbps** | 370 Mbps
`5.14x slow` | | |
+| RIPEMD-320 | **552 Mbps** | 231 Mbps
`2.39x slow` | | |
+| BLAKE-2s | **1.2 Gbps** | | | |
+| BLAKE-2b | **1.37 Gbps** | 105 Mbps
`13.06x slow` | | |
+| Poly1305 | **3.83 Gbps** | 1.26 Gbps
`3.03x slow` | | |
+| XXH32 | **4.48 Gbps** | | | |
+| XXH64 | **4.42 Gbps** | | | |
+| XXH3 | **1.02 Gbps** | | | |
+| XXH128 | **1.02 Gbps** | | | |
+| SM3 | **706 Mbps** | 188 Mbps
`3.76x slow` | | |
With 1KB message (5000 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | ------------- | ----------------------------- | --------------------------- | --------------------------- |
-| MD4 | **2.12 Gbps** | 965 Mbps
`2.19x slow` | | |
-| MD5 | **1.42 Gbps** | 828 Mbps
`1.71x slow` | 1.3 Gbps
`1.09x slow` | 1.03 Gbps
`1.37x slow` |
-| HMAC(MD5) | **1.16 Gbps** | | 1.09 Gbps
`1.07x slow` | 754 Mbps
`1.54x slow` |
-| SHA-1 | **1.18 Gbps** | 500 Mbps
`2.36x slow` | 1.05 Gbps
`1.12x slow` | 554 Mbps
`2.13x slow` |
-| HMAC(SHA-1) | **841 Mbps** | | 768 Mbps
`1.09x slow` | |
-| SHA-224 | **955 Mbps** | 229 Mbps
`4.16x slow` | 835 Mbps
`1.14x slow` | 239 Mbps
`4x slow` |
-| SHA-256 | **957 Mbps** | 232 Mbps
`4.12x slow` | 838 Mbps
`1.14x slow` | 239 Mbps
`4x slow` |
-| HMAC(SHA-256) | **681 Mbps** | | 606 Mbps
`1.12x slow` | |
-| SHA-384 | **1.58 Gbps** | 52.84 Mbps
`29.89x slow` | 590 Mbps
`2.68x slow` | 167 Mbps
`9.43x slow` |
-| SHA-512 | **1.6 Gbps** | 51.79 Mbps
`30.84x slow` | 588 Mbps
`2.72x slow` | 168 Mbps
`9.53x slow` |
-| SHA3-256 | **956 Mbps** | 30.62 Mbps
`31.23x slow` | | |
-| SHA3-512 | **1.6 Gbps** | 16.4 Mbps
`97.3x slow` | | |
-| RIPEMD-128 | **1.2 Gbps** | 480 Mbps
`2.5x slow` | | |
-| RIPEMD-160 | **656 Mbps** | 334 Mbps
`1.96x slow` | | 376 Mbps
`1.75x slow` |
-| RIPEMD-256 | **1.33 Gbps** | 478 Mbps
`2.78x slow` | | |
-| RIPEMD-320 | **627 Mbps** | 330 Mbps
`1.9x slow` | | |
-| BLAKE-2s | **1.57 Gbps** | | | |
-| BLAKE-2b | **1.98 Gbps** | 160 Mbps
`12.36x slow` | | |
-| Poly1305 | **4.28 Gbps** | 1.5 Gbps
`2.85x slow` | | |
-| XXH32 | **4.94 Gbps** | | | |
-| XXH64 | **5.44 Gbps** | | | |
-| XXH3 | **1.32 Gbps** | | | |
-| XXH128 | **1.32 Gbps** | | | |
-| SM3 | **830 Mbps** | 236 Mbps
`3.52x slow` | | |
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | ------------- | ----------------------------- | --------------------------- | -------------------------- |
+| MD4 | **1.95 Gbps** | 794 Mbps
`2.45x slow` | | |
+| MD5 | **1.35 Gbps** | 691 Mbps
`1.95x slow` | 1.04 Gbps
`1.29x slow` | 835 Mbps
`1.62x slow` |
+| HMAC(MD5) | **1.07 Gbps** | | 882 Mbps
`1.21x slow` | 624 Mbps
`1.71x slow` |
+| SHA-1 | **1.13 Gbps** | 429 Mbps
`2.62x slow` | 790 Mbps
`1.43x slow` | 407 Mbps
`2.76x slow` |
+| HMAC(SHA-1) | **830 Mbps** | | 570 Mbps
`1.46x slow` | |
+| SHA-224 | **751 Mbps** | 169 Mbps
`4.45x slow` | 672 Mbps
`1.12x slow` | 173 Mbps
`4.35x slow` |
+| SHA-256 | **749 Mbps** | 169 Mbps
`4.43x slow` | 674 Mbps
`1.11x slow` | 173 Mbps
`4.34x slow` |
+| HMAC(SHA-256) | **541 Mbps** | | 486 Mbps
`1.11x slow` | |
+| SHA-384 | **1.11 Gbps** | 41.31 Mbps
`26.78x slow` | 394 Mbps
`2.81x slow` | 168 Mbps
`6.59x slow` |
+| SHA-512 | **1.11 Gbps** | 40.96 Mbps
`27.07x slow` | 394 Mbps
`2.82x slow` | 168 Mbps
`6.61x slow` |
+| SHA3-256 | **752 Mbps** | 24.86 Mbps
`30.26x slow` | | |
+| SHA3-512 | **1.11 Gbps** | 13.32 Mbps
`83.25x slow` | | |
+| RIPEMD-128 | **1.64 Gbps** | 350 Mbps
`4.68x slow` | | |
+| RIPEMD-160 | **516 Mbps** | 220 Mbps
`2.34x slow` | | 297 Mbps
`1.74x slow` |
+| RIPEMD-256 | **1.75 Gbps** | 350 Mbps
`5x slow` | | |
+| RIPEMD-320 | **518 Mbps** | 218 Mbps
`2.37x slow` | | |
+| BLAKE-2s | **1.18 Gbps** | | | |
+| BLAKE-2b | **1.33 Gbps** | 103 Mbps
`12.9x slow` | | |
+| Poly1305 | **3.57 Gbps** | 1.26 Gbps
`2.84x slow` | | |
+| XXH32 | **4.23 Gbps** | | | |
+| XXH64 | **4.19 Gbps** | | | |
+| XXH3 | **956 Mbps** | | | |
+| XXH128 | **949 Mbps** | | | |
+| SM3 | **659 Mbps** | 176 Mbps
`3.73x slow` | | |
With 10B message (100000 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | -------------- | ----------------------------- | ---------------------------- | ---------------------------- |
-| MD4 | **334 Mbps** | 185 Mbps
`1.81x slow` | | |
-| MD5 | **264 Mbps** | 156 Mbps
`1.69x slow` | 160 Mbps
`1.65x slow` | 78.48 Mbps
`3.36x slow` |
-| HMAC(MD5) | **50.09 Mbps** | | 46.17 Mbps
`1.08x slow` | 21.6 Mbps
`2.32x slow` |
-| SHA-1 | **156 Mbps** | 83.85 Mbps
`1.86x slow` | 122 Mbps
`1.28x slow` | 58.19 Mbps
`2.68x slow` |
-| HMAC(SHA-1) | **24.18 Mbps** | | 22.01 Mbps
`1.1x slow` | |
-| SHA-224 | **125 Mbps** | 37.54 Mbps
`3.34x slow` | 102 Mbps
`1.23x slow` | 31.53 Mbps
`3.98x slow` |
-| SHA-256 | **125 Mbps** | 37.7 Mbps
`3.33x slow` | 103 Mbps
`1.22x slow` | 31.64 Mbps
`3.96x slow` |
-| HMAC(SHA-256) | **19.5 Mbps** | | 17.87 Mbps
`1.09x slow` | |
-| SHA-384 | **107 Mbps** | 4.55 Mbps
`23.63x slow` | 44.43 Mbps
`2.42x slow` | 14.32 Mbps
`7.5x slow` |
-| SHA-512 | **107 Mbps** | 4.46 Mbps
`24.01x slow` | 44.1 Mbps
`2.43x slow` | 14.58 Mbps
`7.35x slow` |
-| SHA3-256 | **126 Mbps** | 2.32 Mbps
`54.28x slow` | | |
-| SHA3-512 | **107 Mbps** | 2.31 Mbps
`46.31x slow` | | |
-| RIPEMD-128 | **194 Mbps** | 85.85 Mbps
`2.26x slow` | | |
-| RIPEMD-160 | **105 Mbps** | 58.39 Mbps
`1.8x slow` | | 45.19 Mbps
`2.32x slow` |
-| RIPEMD-256 | **210 Mbps** | 80.76 Mbps
`2.6x slow` | | |
-| RIPEMD-320 | **101 Mbps** | 54.23 Mbps
`1.87x slow` | | |
-| BLAKE-2s | **188 Mbps** | | | |
-| BLAKE-2b | **153 Mbps** | 11.09 Mbps
`13.76x slow` | | |
-| Poly1305 | **733 Mbps** | 438 Mbps
`1.67x slow` | | |
-| XXH32 | **1.1 Gbps** | | | |
-| XXH64 | **858 Mbps** | | | |
-| XXH3 | **109 Mbps** | | | |
-| XXH128 | **105 Mbps** | | | |
-| SM3 | **129 Mbps** | 39.46 Mbps
`3.27x slow` | | |
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | -------------- | ---------------------------- | ---------------------------- | ---------------------------- |
+| MD4 | **273 Mbps** | 135 Mbps
`2.02x slow` | | |
+| MD5 | **246 Mbps** | 118 Mbps
`2.08x slow` | 125 Mbps
`1.97x slow` | 69.2 Mbps
`3.55x slow` |
+| HMAC(MD5) | **44.45 Mbps** | | 38.39 Mbps
`1.16x slow` | 18.19 Mbps
`2.44x slow` |
+| SHA-1 | **144 Mbps** | 66.9 Mbps
`2.15x slow` | 98.88 Mbps
`1.45x slow` | 43.49 Mbps
`3.31x slow` |
+| HMAC(SHA-1) | **22.92 Mbps** | | 17.47 Mbps
`1.31x slow` | |
+| SHA-224 | **103 Mbps** | 27.22 Mbps
`3.8x slow` | 80.2 Mbps
`1.29x slow` | 22.93 Mbps
`4.51x slow` |
+| SHA-256 | **103 Mbps** | 27.14 Mbps
`3.79x slow` | 80.67 Mbps
`1.27x slow` | 23.12 Mbps
`4.45x slow` |
+| HMAC(SHA-256) | **15.92 Mbps** | | 14.53 Mbps
`1.1x slow` | |
+| SHA-384 | **80.46 Mbps** | 3.5 Mbps
`23.01x slow` | 30.22 Mbps
`2.66x slow` | 12.08 Mbps
`6.66x slow` |
+| SHA-512 | **80.02 Mbps** | 3.48 Mbps
`22.98x slow` | 29.82 Mbps
`2.68x slow` | 12.12 Mbps
`6.6x slow` |
+| SHA3-256 | **103 Mbps** | 1.88 Mbps
`54.75x slow` | | |
+| SHA3-512 | **80.01 Mbps** | 1.88 Mbps
`42.63x slow` | | |
+| RIPEMD-128 | **234 Mbps** | 58.97 Mbps
`3.96x slow` | | |
+| RIPEMD-160 | **80.04 Mbps** | 35.41 Mbps
`2.26x slow` | | 36.04 Mbps
`2.22x slow` |
+| RIPEMD-256 | **243 Mbps** | 57.34 Mbps
`4.23x slow` | | |
+| RIPEMD-320 | **79.08 Mbps** | 33.59 Mbps
`2.35x slow` | | |
+| BLAKE-2s | **159 Mbps** | | | |
+| BLAKE-2b | **125 Mbps** | 7.61 Mbps
`16.38x slow` | | |
+| Poly1305 | **508 Mbps** | 318 Mbps
`1.6x slow` | | |
+| XXH32 | **837 Mbps** | | | |
+| XXH64 | **643 Mbps** | | | |
+| XXH3 | **87.49 Mbps** | | | |
+| XXH128 | **83.2 Mbps** | | | |
+| SM3 | **98.76 Mbps** | 28.65 Mbps
`3.45x slow` | | |
-Argon2 and scrypt benchmarks on different security parameters:
+Key derivator algorithm benchmarks on different security parameters:
| Algorithms | test | little | moderate | good | strong |
| ---------- | -------- | -------- | --------- | ---------- | ----------- |
-| scrypt | 0.048 ms | 1.123 ms | 8.501 ms | 69.45 ms | 1080.798 ms |
-| argon2i | 0.272 ms | 2.26 ms | 15.143 ms | 193.404 ms | 2092.091 ms |
-| argon2d | 0.199 ms | 2.085 ms | 14.863 ms | 192.37 ms | 2057.96 ms |
-| argon2id | 0.212 ms | 2.127 ms | 15.0 ms | 191.927 ms | 2071.661 ms |
+| scrypt | 0.056 ms | 1.564 ms | 11.017 ms | 92.747 ms | 1375.198 ms |
+| bcrypt | 0.184 ms | 2.165 ms | 16.617 ms | 262.765 ms | 2101.005 ms |
+| argon2i | 0.333 ms | 3.034 ms | 16.871 ms | 205.397 ms | 2602.407 ms |
+| argon2d | 0.289 ms | 2.351 ms | 16.702 ms | 207.512 ms | 2650.916 ms |
+| argon2id | 0.284 ms | 2.354 ms | 16.642 ms | 209.787 ms | 2574.973 ms |
-> All benchmarks are done on 36GB _Apple M3 Pro_ using compiled _exe_
+> All benchmarks are done on _AMD Ryzen 7 5800X_ processor and _3200MHz_ RAM using compiled _exe_
>
-> Dart SDK version: 3.4.0 (stable) (Mon May 6 07:59:58 2024 -0700) on "macos_arm64"
+> Dart SDK version: 3.3.3 (stable) (Tue Mar 26 14:21:33 2024 +0000) on "windows_x64"
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0474461..9aaba82 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,11 @@
-# next
+# 1.18.0
+- Implement BCrypt algorithm.
+ - New class: `Bcrypt`
+ - New methods: `bcrypt`, `bcryptSalt`, `bcryptVerify`, `bcryptDigest`
- Convert all asUint...List to Uint...List.view
+- Refactor: `Argon2.fromEncoded` will now accept `CryptData` only.
+- New method: `Argon2Context.fromEncoded` accepting `CryptData`.
# 1.17.0
diff --git a/README.md b/README.md
index 9e1defa..629411d 100644
--- a/README.md
+++ b/README.md
@@ -41,11 +41,12 @@ There is only 1 dependency used by this package:
### Password / Key Derivation Algorithms
-| Algorithm | Available methods | Source |
-| --------- | ------------------------------------------ | -------- |
-| Argon2 | `Argon2`, `argon2d`, `argon2i`, `argon2id` | RFC-9106 |
-| PBKDF2 | `PBKDF2`, `pbkdf2`, `#.pbkdf2` | RFC-8081 |
-| scrypt | `scrypt`, `Scrypt` | RFC-7914 |
+| Algorithm | Available methods | Source |
+| --------- | ---------------------------------------------------------------- | -------- |
+| Argon2 | `Argon2`, `argon2d`, `argon2i`, `argon2id`, `argon2verify` | RFC-9106 |
+| PBKDF2 | `PBKDF2`, `pbkdf2`, `#.pbkdf2` | RFC-8081 |
+| scrypt | `Scrypt`, `scrypt`, | RFC-7914 |
+| bcrypt | `Bcrypt`, `bcrypt`, `bcryptSalt`, `bcryptVerify`, `bcryptDigest` | |
### Message Authentication Code (MAC) Generators
@@ -187,100 +188,101 @@ Libraries:
With 5MB message (10 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | ------------- | ------------------------------ | --------------------------- | --------------------------- |
-| MD4 | **2.26 Gbps** | 1.01 Gbps
`2.23x slow` | | |
-| MD5 | **1.53 Gbps** | 870 Mbps
`1.76x slow` | 1.4 Gbps
`1.09x slow` | 928 Mbps
`1.65x slow` |
-| HMAC(MD5) | **1.51 Gbps** | | 1.44 Gbps
`1.06x slow` | 934 Mbps
`1.62x slow` |
-| SHA-1 | **1.28 Gbps** | 524 Mbps
`2.45x slow` | 1.14 Gbps
`1.12x slow` | 532 Mbps
`2.41x slow` |
-| HMAC(SHA-1) | **1.28 Gbps** | | 1.16 Gbps
`1.11x slow` | |
-| SHA-224 | **971 Mbps** | 245 Mbps
`3.96x slow` | 914 Mbps
`1.06x slow` | 245 Mbps
`3.97x slow` |
-| SHA-256 | **1.03 Gbps** | 240 Mbps
`4.28x slow` | 905 Mbps
`1.14x slow` | 242 Mbps
`4.25x slow` |
-| HMAC(SHA-256) | **1.04 Gbps** | | 925 Mbps
`1.12x slow` | |
-| SHA-384 | **1.9 Gbps** | 57.57 Mbps
`33.05x slow` | 661 Mbps
`2.88x slow` | 183 Mbps
`10.41x slow` |
-| SHA-512 | **1.93 Gbps** | 58.29 Mbps
`33.13x slow` | 663 Mbps
`2.91x slow` | 185 Mbps
`10.43x slow` |
-| SHA3-256 | **1.04 Gbps** | 32.59 Mbps
`31.86x slow` | | |
-| SHA3-512 | **1.92 Gbps** | 17.27 Mbps
`111.36x slow` | | |
-| RIPEMD-128 | **1.29 Gbps** | 509 Mbps
`2.54x slow` | | |
-| RIPEMD-160 | **699 Mbps** | 356 Mbps
`1.96x slow` | | 375 Mbps
`1.86x slow` |
-| RIPEMD-256 | **1.42 Gbps** | 505 Mbps
`2.81x slow` | | |
-| RIPEMD-320 | **666 Mbps** | 346 Mbps
`1.93x slow` | | |
-| BLAKE-2s | **1.52 Gbps** | | | |
-| BLAKE-2b | **2.01 Gbps** | 162 Mbps
`12.41x slow` | | |
-| Poly1305 | **4.51 Gbps** | 1.52 Gbps
`2.97x slow` | | |
-| XXH32 | **5.42 Gbps** | | | |
-| XXH64 | **5.85 Gbps** | | | |
-| XXH3 | **1.4 Gbps** | | | |
-| XXH128 | **1.47 Gbps** | | | |
-| SM3 | **886 Mbps** | 253 Mbps
`3.51x slow` | | |
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | ------------- | ----------------------------- | --------------------------- | -------------------------- |
+| MD4 | **2.07 Gbps** | 828 Mbps
`2.5x slow` | | |
+| MD5 | **1.41 Gbps** | 724 Mbps
`1.95x slow` | 1.13 Gbps
`1.25x slow` | 627 Mbps
`2.26x slow` |
+| HMAC(MD5) | **1.3 Gbps** | | 1.12 Gbps
`1.16x slow` | 625 Mbps
`2.08x slow` |
+| SHA-1 | **1.21 Gbps** | 456 Mbps
`2.65x slow` | 849 Mbps
`1.42x slow` | 362 Mbps
`3.34x slow` |
+| HMAC(SHA-1) | **1.21 Gbps** | | 844 Mbps
`1.43x slow` | |
+| SHA-224 | **810 Mbps** | 176 Mbps
`4.6x slow` | 723 Mbps
`1.12x slow` | 170 Mbps
`4.76x slow` |
+| SHA-256 | **809 Mbps** | 179 Mbps
`4.52x slow` | 724 Mbps
`1.12x slow` | 170 Mbps
`4.77x slow` |
+| HMAC(SHA-256) | **813 Mbps** | | 708 Mbps
`1.15x slow` | |
+| SHA-384 | **1.27 Gbps** | 44.66 Mbps
`28.36x slow` | 445 Mbps
`2.85x slow` | 150 Mbps
`8.43x slow` |
+| SHA-512 | **1.26 Gbps** | 45.17 Mbps
`27.94x slow` | 445 Mbps
`2.84x slow` | 151 Mbps
`8.38x slow` |
+| SHA3-256 | **812 Mbps** | 26.32 Mbps
`30.86x slow` | | |
+| SHA3-512 | **1.27 Gbps** | 13.91 Mbps
`91.63x slow` | | |
+| RIPEMD-128 | **1.77 Gbps** | 368 Mbps
`4.8x slow` | | |
+| RIPEMD-160 | **551 Mbps** | 234 Mbps
`2.35x slow` | | 276 Mbps
`2x slow` |
+| RIPEMD-256 | **1.9 Gbps** | 370 Mbps
`5.14x slow` | | |
+| RIPEMD-320 | **552 Mbps** | 231 Mbps
`2.39x slow` | | |
+| BLAKE-2s | **1.2 Gbps** | | | |
+| BLAKE-2b | **1.37 Gbps** | 105 Mbps
`13.06x slow` | | |
+| Poly1305 | **3.83 Gbps** | 1.26 Gbps
`3.03x slow` | | |
+| XXH32 | **4.48 Gbps** | | | |
+| XXH64 | **4.42 Gbps** | | | |
+| XXH3 | **1.02 Gbps** | | | |
+| XXH128 | **1.02 Gbps** | | | |
+| SM3 | **706 Mbps** | 188 Mbps
`3.76x slow` | | |
With 1KB message (5000 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | ------------- | ----------------------------- | --------------------------- | --------------------------- |
-| MD4 | **2.12 Gbps** | 965 Mbps
`2.19x slow` | | |
-| MD5 | **1.42 Gbps** | 828 Mbps
`1.71x slow` | 1.3 Gbps
`1.09x slow` | 1.03 Gbps
`1.37x slow` |
-| HMAC(MD5) | **1.16 Gbps** | | 1.09 Gbps
`1.07x slow` | 754 Mbps
`1.54x slow` |
-| SHA-1 | **1.18 Gbps** | 500 Mbps
`2.36x slow` | 1.05 Gbps
`1.12x slow` | 554 Mbps
`2.13x slow` |
-| HMAC(SHA-1) | **841 Mbps** | | 768 Mbps
`1.09x slow` | |
-| SHA-224 | **955 Mbps** | 229 Mbps
`4.16x slow` | 835 Mbps
`1.14x slow` | 239 Mbps
`4x slow` |
-| SHA-256 | **957 Mbps** | 232 Mbps
`4.12x slow` | 838 Mbps
`1.14x slow` | 239 Mbps
`4x slow` |
-| HMAC(SHA-256) | **681 Mbps** | | 606 Mbps
`1.12x slow` | |
-| SHA-384 | **1.58 Gbps** | 52.84 Mbps
`29.89x slow` | 590 Mbps
`2.68x slow` | 167 Mbps
`9.43x slow` |
-| SHA-512 | **1.6 Gbps** | 51.79 Mbps
`30.84x slow` | 588 Mbps
`2.72x slow` | 168 Mbps
`9.53x slow` |
-| SHA3-256 | **956 Mbps** | 30.62 Mbps
`31.23x slow` | | |
-| SHA3-512 | **1.6 Gbps** | 16.4 Mbps
`97.3x slow` | | |
-| RIPEMD-128 | **1.2 Gbps** | 480 Mbps
`2.5x slow` | | |
-| RIPEMD-160 | **656 Mbps** | 334 Mbps
`1.96x slow` | | 376 Mbps
`1.75x slow` |
-| RIPEMD-256 | **1.33 Gbps** | 478 Mbps
`2.78x slow` | | |
-| RIPEMD-320 | **627 Mbps** | 330 Mbps
`1.9x slow` | | |
-| BLAKE-2s | **1.57 Gbps** | | | |
-| BLAKE-2b | **1.98 Gbps** | 160 Mbps
`12.36x slow` | | |
-| Poly1305 | **4.28 Gbps** | 1.5 Gbps
`2.85x slow` | | |
-| XXH32 | **4.94 Gbps** | | | |
-| XXH64 | **5.44 Gbps** | | | |
-| XXH3 | **1.32 Gbps** | | | |
-| XXH128 | **1.32 Gbps** | | | |
-| SM3 | **830 Mbps** | 236 Mbps
`3.52x slow` | | |
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | ------------- | ----------------------------- | --------------------------- | -------------------------- |
+| MD4 | **1.95 Gbps** | 794 Mbps
`2.45x slow` | | |
+| MD5 | **1.35 Gbps** | 691 Mbps
`1.95x slow` | 1.04 Gbps
`1.29x slow` | 835 Mbps
`1.62x slow` |
+| HMAC(MD5) | **1.07 Gbps** | | 882 Mbps
`1.21x slow` | 624 Mbps
`1.71x slow` |
+| SHA-1 | **1.13 Gbps** | 429 Mbps
`2.62x slow` | 790 Mbps
`1.43x slow` | 407 Mbps
`2.76x slow` |
+| HMAC(SHA-1) | **830 Mbps** | | 570 Mbps
`1.46x slow` | |
+| SHA-224 | **751 Mbps** | 169 Mbps
`4.45x slow` | 672 Mbps
`1.12x slow` | 173 Mbps
`4.35x slow` |
+| SHA-256 | **749 Mbps** | 169 Mbps
`4.43x slow` | 674 Mbps
`1.11x slow` | 173 Mbps
`4.34x slow` |
+| HMAC(SHA-256) | **541 Mbps** | | 486 Mbps
`1.11x slow` | |
+| SHA-384 | **1.11 Gbps** | 41.31 Mbps
`26.78x slow` | 394 Mbps
`2.81x slow` | 168 Mbps
`6.59x slow` |
+| SHA-512 | **1.11 Gbps** | 40.96 Mbps
`27.07x slow` | 394 Mbps
`2.82x slow` | 168 Mbps
`6.61x slow` |
+| SHA3-256 | **752 Mbps** | 24.86 Mbps
`30.26x slow` | | |
+| SHA3-512 | **1.11 Gbps** | 13.32 Mbps
`83.25x slow` | | |
+| RIPEMD-128 | **1.64 Gbps** | 350 Mbps
`4.68x slow` | | |
+| RIPEMD-160 | **516 Mbps** | 220 Mbps
`2.34x slow` | | 297 Mbps
`1.74x slow` |
+| RIPEMD-256 | **1.75 Gbps** | 350 Mbps
`5x slow` | | |
+| RIPEMD-320 | **518 Mbps** | 218 Mbps
`2.37x slow` | | |
+| BLAKE-2s | **1.18 Gbps** | | | |
+| BLAKE-2b | **1.33 Gbps** | 103 Mbps
`12.9x slow` | | |
+| Poly1305 | **3.57 Gbps** | 1.26 Gbps
`2.84x slow` | | |
+| XXH32 | **4.23 Gbps** | | | |
+| XXH64 | **4.19 Gbps** | | | |
+| XXH3 | **956 Mbps** | | | |
+| XXH128 | **949 Mbps** | | | |
+| SM3 | **659 Mbps** | 176 Mbps
`3.73x slow` | | |
With 10B message (100000 iterations):
-| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
-| ------------- | -------------- | ----------------------------- | ---------------------------- | ---------------------------- |
-| MD4 | **334 Mbps** | 185 Mbps
`1.81x slow` | | |
-| MD5 | **264 Mbps** | 156 Mbps
`1.69x slow` | 160 Mbps
`1.65x slow` | 78.48 Mbps
`3.36x slow` |
-| HMAC(MD5) | **50.09 Mbps** | | 46.17 Mbps
`1.08x slow` | 21.6 Mbps
`2.32x slow` |
-| SHA-1 | **156 Mbps** | 83.85 Mbps
`1.86x slow` | 122 Mbps
`1.28x slow` | 58.19 Mbps
`2.68x slow` |
-| HMAC(SHA-1) | **24.18 Mbps** | | 22.01 Mbps
`1.1x slow` | |
-| SHA-224 | **125 Mbps** | 37.54 Mbps
`3.34x slow` | 102 Mbps
`1.23x slow` | 31.53 Mbps
`3.98x slow` |
-| SHA-256 | **125 Mbps** | 37.7 Mbps
`3.33x slow` | 103 Mbps
`1.22x slow` | 31.64 Mbps
`3.96x slow` |
-| HMAC(SHA-256) | **19.5 Mbps** | | 17.87 Mbps
`1.09x slow` | |
-| SHA-384 | **107 Mbps** | 4.55 Mbps
`23.63x slow` | 44.43 Mbps
`2.42x slow` | 14.32 Mbps
`7.5x slow` |
-| SHA-512 | **107 Mbps** | 4.46 Mbps
`24.01x slow` | 44.1 Mbps
`2.43x slow` | 14.58 Mbps
`7.35x slow` |
-| SHA3-256 | **126 Mbps** | 2.32 Mbps
`54.28x slow` | | |
-| SHA3-512 | **107 Mbps** | 2.31 Mbps
`46.31x slow` | | |
-| RIPEMD-128 | **194 Mbps** | 85.85 Mbps
`2.26x slow` | | |
-| RIPEMD-160 | **105 Mbps** | 58.39 Mbps
`1.8x slow` | | 45.19 Mbps
`2.32x slow` |
-| RIPEMD-256 | **210 Mbps** | 80.76 Mbps
`2.6x slow` | | |
-| RIPEMD-320 | **101 Mbps** | 54.23 Mbps
`1.87x slow` | | |
-| BLAKE-2s | **188 Mbps** | | | |
-| BLAKE-2b | **153 Mbps** | 11.09 Mbps
`13.76x slow` | | |
-| Poly1305 | **733 Mbps** | 438 Mbps
`1.67x slow` | | |
-| XXH32 | **1.1 Gbps** | | | |
-| XXH64 | **858 Mbps** | | | |
-| XXH3 | **109 Mbps** | | | |
-| XXH128 | **105 Mbps** | | | |
-| SM3 | **129 Mbps** | 39.46 Mbps
`3.27x slow` | | |
-
-Argon2 and scrypt benchmarks on different security parameters:
+| Algorithms | `hashlib` | `PointyCastle` | `crypto` | `hash` |
+| ------------- | -------------- | ---------------------------- | ---------------------------- | ---------------------------- |
+| MD4 | **273 Mbps** | 135 Mbps
`2.02x slow` | | |
+| MD5 | **246 Mbps** | 118 Mbps
`2.08x slow` | 125 Mbps
`1.97x slow` | 69.2 Mbps
`3.55x slow` |
+| HMAC(MD5) | **44.45 Mbps** | | 38.39 Mbps
`1.16x slow` | 18.19 Mbps
`2.44x slow` |
+| SHA-1 | **144 Mbps** | 66.9 Mbps
`2.15x slow` | 98.88 Mbps
`1.45x slow` | 43.49 Mbps
`3.31x slow` |
+| HMAC(SHA-1) | **22.92 Mbps** | | 17.47 Mbps
`1.31x slow` | |
+| SHA-224 | **103 Mbps** | 27.22 Mbps
`3.8x slow` | 80.2 Mbps
`1.29x slow` | 22.93 Mbps
`4.51x slow` |
+| SHA-256 | **103 Mbps** | 27.14 Mbps
`3.79x slow` | 80.67 Mbps
`1.27x slow` | 23.12 Mbps
`4.45x slow` |
+| HMAC(SHA-256) | **15.92 Mbps** | | 14.53 Mbps
`1.1x slow` | |
+| SHA-384 | **80.46 Mbps** | 3.5 Mbps
`23.01x slow` | 30.22 Mbps
`2.66x slow` | 12.08 Mbps
`6.66x slow` |
+| SHA-512 | **80.02 Mbps** | 3.48 Mbps
`22.98x slow` | 29.82 Mbps
`2.68x slow` | 12.12 Mbps
`6.6x slow` |
+| SHA3-256 | **103 Mbps** | 1.88 Mbps
`54.75x slow` | | |
+| SHA3-512 | **80.01 Mbps** | 1.88 Mbps
`42.63x slow` | | |
+| RIPEMD-128 | **234 Mbps** | 58.97 Mbps
`3.96x slow` | | |
+| RIPEMD-160 | **80.04 Mbps** | 35.41 Mbps
`2.26x slow` | | 36.04 Mbps
`2.22x slow` |
+| RIPEMD-256 | **243 Mbps** | 57.34 Mbps
`4.23x slow` | | |
+| RIPEMD-320 | **79.08 Mbps** | 33.59 Mbps
`2.35x slow` | | |
+| BLAKE-2s | **159 Mbps** | | | |
+| BLAKE-2b | **125 Mbps** | 7.61 Mbps
`16.38x slow` | | |
+| Poly1305 | **508 Mbps** | 318 Mbps
`1.6x slow` | | |
+| XXH32 | **837 Mbps** | | | |
+| XXH64 | **643 Mbps** | | | |
+| XXH3 | **87.49 Mbps** | | | |
+| XXH128 | **83.2 Mbps** | | | |
+| SM3 | **98.76 Mbps** | 28.65 Mbps
`3.45x slow` | | |
+
+Key derivator algorithm benchmarks on different security parameters:
| Algorithms | test | little | moderate | good | strong |
| ---------- | -------- | -------- | --------- | ---------- | ----------- |
-| scrypt | 0.048 ms | 1.123 ms | 8.501 ms | 69.45 ms | 1080.798 ms |
-| argon2i | 0.272 ms | 2.26 ms | 15.143 ms | 193.404 ms | 2092.091 ms |
-| argon2d | 0.199 ms | 2.085 ms | 14.863 ms | 192.37 ms | 2057.96 ms |
-| argon2id | 0.212 ms | 2.127 ms | 15.0 ms | 191.927 ms | 2071.661 ms |
+| scrypt | 0.056 ms | 1.564 ms | 11.017 ms | 92.747 ms | 1375.198 ms |
+| bcrypt | 0.184 ms | 2.165 ms | 16.617 ms | 262.765 ms | 2101.005 ms |
+| argon2i | 0.333 ms | 3.034 ms | 16.871 ms | 205.397 ms | 2602.407 ms |
+| argon2d | 0.289 ms | 2.351 ms | 16.702 ms | 207.512 ms | 2650.916 ms |
+| argon2id | 0.284 ms | 2.354 ms | 16.642 ms | 209.787 ms | 2574.973 ms |
-> All benchmarks are done on 36GB _Apple M3 Pro_ using compiled _exe_
+> All benchmarks are done on _AMD Ryzen 7 5800X_ processor and _3200MHz_ RAM using compiled _exe_
>
-> Dart SDK version: 3.4.0 (stable) (Mon May 6 07:59:58 2024 -0700) on "macos_arm64"
+> Dart SDK version: 3.3.3 (stable) (Tue Mar 26 14:21:33 2024 +0000) on "windows_x64"
diff --git a/benchmark/argon2.dart b/benchmark/argon2.dart
index 02cda0e..3add917 100644
--- a/benchmark/argon2.dart
+++ b/benchmark/argon2.dart
@@ -5,27 +5,13 @@ import 'dart:math';
import 'dart:typed_data';
import 'package:argon2/argon2.dart' as argon2;
-import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:hashlib/hashlib.dart';
-Random random = Random();
-
-class Argon2BenchmarkBase extends BenchmarkBase {
- Argon2BenchmarkBase(String name) : super(name);
+import 'base.dart';
- @override
- double measure() {
- final watch = Stopwatch()..start();
- run();
- watch.reset();
- run();
- run();
- run();
- return (watch.elapsedMicroseconds / 3).floorToDouble();
- }
-}
+Random random = Random();
-class HashlibArgon2iBenchmark extends Argon2BenchmarkBase {
+class HashlibArgon2iBenchmark extends KDFBenchmarkBase {
final Argon2Security security;
HashlibArgon2iBenchmark(this.security) : super('hashlib');
@@ -43,7 +29,7 @@ class HashlibArgon2iBenchmark extends Argon2BenchmarkBase {
}
}
-class HashlibArgon2dBenchmark extends Argon2BenchmarkBase {
+class HashlibArgon2dBenchmark extends KDFBenchmarkBase {
final Argon2Security security;
HashlibArgon2dBenchmark(this.security) : super('hashlib');
@@ -61,7 +47,7 @@ class HashlibArgon2dBenchmark extends Argon2BenchmarkBase {
}
}
-class HashlibArgon2idBenchmark extends Argon2BenchmarkBase {
+class HashlibArgon2idBenchmark extends KDFBenchmarkBase {
final Argon2Security security;
HashlibArgon2idBenchmark(this.security) : super('hashlib');
@@ -79,7 +65,7 @@ class HashlibArgon2idBenchmark extends Argon2BenchmarkBase {
}
}
-class Argon2Argon2idBenchmark extends Argon2BenchmarkBase {
+class Argon2Argon2idBenchmark extends KDFBenchmarkBase {
final Argon2Security security;
Argon2Argon2idBenchmark(this.security) : super('argon2');
diff --git a/benchmark/base.dart b/benchmark/base.dart
index 0d08ada..52a0433 100644
--- a/benchmark/base.dart
+++ b/benchmark/base.dart
@@ -7,6 +7,21 @@ import 'package:benchmark_harness/benchmark_harness.dart';
Random random = Random();
+class KDFBenchmarkBase extends BenchmarkBase {
+ KDFBenchmarkBase(String name) : super(name);
+
+ @override
+ double measure() {
+ final watch = Stopwatch()..start();
+ run();
+ watch.reset();
+ run();
+ run();
+ run();
+ return (watch.elapsedMicroseconds / 3).floorToDouble();
+ }
+}
+
abstract class Benchmark extends BenchmarkBase {
final int size;
final int iter;
diff --git a/benchmark/bcrypt.dart b/benchmark/bcrypt.dart
new file mode 100644
index 0000000..8020220
--- /dev/null
+++ b/benchmark/bcrypt.dart
@@ -0,0 +1,39 @@
+// Copyright (c) 2024, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'dart:math';
+
+import 'package:hashlib/hashlib.dart';
+
+import 'base.dart';
+
+Random random = Random();
+
+class HashlibBenchmark extends KDFBenchmarkBase {
+ final salt = randomBytes(16);
+ final BcryptSecurity security;
+ final password = 'long password'.codeUnits;
+
+ HashlibBenchmark(this.security) : super('hashlib');
+
+ @override
+ void run() {
+ bcryptDigest(password, salt: salt, security: security);
+ }
+}
+
+void main() {
+ double runtime;
+ print('--------- Hashlib/BCRYPT ----------');
+ runtime = HashlibBenchmark(BcryptSecurity.test).measure();
+ print('hashlib/bcrypt[test]: ${runtime / 1000} ms');
+ runtime = HashlibBenchmark(BcryptSecurity.little).measure();
+ print('hashlib/bcrypt[little]: ${runtime / 1000} ms');
+ runtime = HashlibBenchmark(BcryptSecurity.moderate).measure();
+ print('hashlib/bcrypt[moderate]: ${runtime / 1000} ms');
+ runtime = HashlibBenchmark(BcryptSecurity.good).measure();
+ print('hashlib/bcrypt[good]: ${runtime / 1000} ms');
+ runtime = HashlibBenchmark(BcryptSecurity.strong).measure();
+ print('hashlib/bcrypt[strong]: ${runtime / 1000} ms');
+ print('');
+}
diff --git a/benchmark/benchmark.dart b/benchmark/benchmark.dart
index 1224576..e83eb41 100644
--- a/benchmark/benchmark.dart
+++ b/benchmark/benchmark.dart
@@ -8,6 +8,7 @@ import 'package:hashlib/hashlib.dart';
import 'argon2.dart' as argon2;
import 'base.dart';
+import 'bcrypt.dart' as bcrypt;
import 'blake2b.dart' as blake2b;
import 'blake2s.dart' as blake2s;
import 'hmac_md5.dart' as md5_hmac;
@@ -221,7 +222,7 @@ void measureHashFunctions() {
// Key Derivation Algorithm Benchmarks
// ---------------------------------------------------------------------
void measureKeyDerivation() {
- dump('Argon2 and scrypt benchmarks on different security parameters:');
+ dump('Key derivator algorithm benchmarks on different security parameters:');
dump('');
var argon2Levels = [
Argon2Security.test,
@@ -237,14 +238,26 @@ void measureKeyDerivation() {
ScryptSecurity.good,
ScryptSecurity.strong,
];
+ var bcryptLevels = [
+ BcryptSecurity.test,
+ BcryptSecurity.little,
+ BcryptSecurity.moderate,
+ BcryptSecurity.good,
+ BcryptSecurity.strong,
+ ];
var algorithms = {
'scrypt': scryptLevels.map((e) => scrypt.HashlibBenchmark(e)),
+ 'bcrypt': bcryptLevels.map((e) => bcrypt.HashlibBenchmark(e)),
'argon2i': argon2Levels.map((e) => argon2.HashlibArgon2iBenchmark(e)),
'argon2d': argon2Levels.map((e) => argon2.HashlibArgon2dBenchmark(e)),
'argon2id': argon2Levels.map((e) => argon2.HashlibArgon2idBenchmark(e)),
};
- var names = argon2Levels.map((e) => e.name);
+ var names = {
+ ...argon2Levels.map((e) => e.name),
+ ...scryptLevels.map((e) => e.name),
+ ...bcryptLevels.map((e) => e.name),
+ }.toList();
var separator = names.map((e) => ('-' * (e.length + 2)));
dump('| Algorithms | ${argon2Levels.map((e) => e.name).join(' | ')} |');
dump('|------------|${separator.join('|')}|');
diff --git a/benchmark/scrypt.dart b/benchmark/scrypt.dart
index 5acd39c..f240716 100644
--- a/benchmark/scrypt.dart
+++ b/benchmark/scrypt.dart
@@ -4,32 +4,18 @@
import 'dart:math';
import 'dart:typed_data';
-import 'package:benchmark_harness/benchmark_harness.dart';
import 'package:hashlib/hashlib.dart';
import 'package:pointycastle/export.dart';
import 'package:pointycastle/key_derivators/scrypt.dart' as pc;
+import 'base.dart';
+
Random random = Random();
-class ScryptBenchmarkBase extends BenchmarkBase {
+class HashlibBenchmark extends KDFBenchmarkBase {
final ScryptSecurity security;
- ScryptBenchmarkBase(String name, this.security) : super(name);
-
- @override
- double measure() {
- final watch = Stopwatch()..start();
- run();
- watch.reset();
- run();
- run();
- run();
- return (watch.elapsedMicroseconds / 3).floorToDouble();
- }
-}
-
-class HashlibBenchmark extends ScryptBenchmarkBase {
- HashlibBenchmark(ScryptSecurity security) : super('hashlib', security);
+ HashlibBenchmark(this.security) : super('hashlib');
@override
void run() {
@@ -42,9 +28,10 @@ class HashlibBenchmark extends ScryptBenchmarkBase {
}
}
-class PointyCastleBenchmark extends ScryptBenchmarkBase {
- PointyCastleBenchmark(ScryptSecurity security)
- : super('PointyCastle', security);
+class PointyCastleBenchmark extends KDFBenchmarkBase {
+ final ScryptSecurity security;
+
+ PointyCastleBenchmark(this.security) : super('PointyCastle');
@override
void run() {
@@ -82,14 +69,14 @@ void main() {
print('');
print('--------- PointyCastle/SCRYPT ----------');
runtime = PointyCastleBenchmark(ScryptSecurity.test).measure();
- print('hashlib/scrypt[test]: ${runtime / 1000} ms');
+ print('pc/scrypt[test]: ${runtime / 1000} ms');
runtime = PointyCastleBenchmark(ScryptSecurity.little).measure();
- print('hashlib/scrypt[little]: ${runtime / 1000} ms');
+ print('pc/scrypt[little]: ${runtime / 1000} ms');
runtime = PointyCastleBenchmark(ScryptSecurity.moderate).measure();
- print('hashlib/scrypt[moderate]: ${runtime / 1000} ms');
+ print('pc/scrypt[moderate]: ${runtime / 1000} ms');
runtime = PointyCastleBenchmark(ScryptSecurity.good).measure();
- print('hashlib/scrypt[good]: ${runtime / 1000} ms');
+ print('pc/scrypt[good]: ${runtime / 1000} ms');
runtime = PointyCastleBenchmark(ScryptSecurity.strong).measure();
- print('hashlib/scrypt[strong]: ${runtime / 1000} ms');
+ print('pc/scrypt[strong]: ${runtime / 1000} ms');
print('');
}
diff --git a/lib/src/algorithms/argon2/argon2.dart b/lib/src/algorithms/argon2/argon2.dart
index c4d75ed..c6dd133 100644
--- a/lib/src/algorithms/argon2/argon2.dart
+++ b/lib/src/algorithms/argon2/argon2.dart
@@ -20,6 +20,8 @@ export 'security.dart';
/// Example of password hashing using Argon2:
///
/// ```dart
+/// final salt = utf.encode("some salt")
+/// final password = utf8.encode('password');
/// final argon2 = Argon2(
/// version: Argon2Version.v13,
/// type: Argon2Type.argon2id,
@@ -27,10 +29,9 @@ export 'security.dart';
/// iterations: 2,
/// parallelism: 8,
/// memorySizeKB: 1 << 18,
-/// salt: "some salt".codeUnits,
+/// salt: salt,
/// );
-///
-/// final digest = argon2.encode('password'.codeUnits);
+/// final digest = argon2.encode(password);
/// ```
///
/// [phc]: https://www.password-hashing.net/
@@ -128,45 +129,20 @@ class Argon2 extends KeyDerivatorBase {
);
}
- /// Creates an [Argon2] instance from an [encoded] PHC-compliant string.
+ /// Creates an [Argon2] instance from an encoded PHC-compliant string.
///
/// The encoded string may look like this:
/// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
factory Argon2.fromEncoded(
- String encoded, {
+ CryptData data, {
List? key,
List? personalization,
}) {
- var data = fromCrypt(encoded);
- var type =
- Argon2Type.values.singleWhere((e) => "$e".split('.').last == data.id);
- Argon2Version version =
- Argon2Version.values.singleWhere((e) => '${e.value}' == data.version);
- if (data.params == null) {
- throw ArgumentError('No paramters');
- }
- var m = data.params!['m'];
- if (m == null) {
- throw ArgumentError('Missing parameter: m');
- }
- var t = data.params!['t'];
- if (t == null) {
- throw ArgumentError('Missing parameter: t');
- }
- var p = data.params!['p'];
- if (p == null) {
- throw ArgumentError('Missing parameter: p');
- }
- return Argon2(
- type: type,
- version: version,
- iterations: int.parse(t),
- parallelism: int.parse(p),
- memorySizeKB: int.parse(m),
- salt: data.saltBytes(),
- hashLength: data.hashBytes()?.lengthInBytes,
+ var ctx = Argon2Context.fromEncoded(
+ data,
key: key,
personalization: personalization,
);
+ return Argon2._(ctx);
}
}
diff --git a/lib/src/algorithms/argon2/common.dart b/lib/src/algorithms/argon2/common.dart
index 28559a3..073a71d 100644
--- a/lib/src/algorithms/argon2/common.dart
+++ b/lib/src/algorithms/argon2/common.dart
@@ -35,6 +35,43 @@ enum Argon2Version {
v13,
}
+String _typeToName(Argon2Type type) {
+ switch (type) {
+ case Argon2Type.argon2d:
+ return 'argon2d';
+ case Argon2Type.argon2i:
+ return 'argon2i';
+ case Argon2Type.argon2id:
+ return 'argon2id';
+ default:
+ throw ArgumentError('Invalid type');
+ }
+}
+
+Argon2Type _nameToType(String name) {
+ switch (name) {
+ case 'argon2d':
+ return Argon2Type.argon2d;
+ case 'argon2i':
+ return Argon2Type.argon2i;
+ case 'argon2id':
+ return Argon2Type.argon2id;
+ default:
+ throw ArgumentError('Invalid type');
+ }
+}
+
+Argon2Version _valueToVersion(int value) {
+ switch (value) {
+ case 0x10:
+ return Argon2Version.v10;
+ case 0x13:
+ return Argon2Version.v13;
+ default:
+ throw ArgumentError('Invalid version');
+ }
+}
+
extension Argon2VersionValue on Argon2Version {
/// The integer value for the version
int get value {
@@ -58,16 +95,7 @@ class Argon2HashDigest extends HashDigest {
String toString() => encoded();
/// Gets the PHC-compliant string for this [Argon2HashDigest]
- String encoded() => toCrypt(
- CryptDataBuilder(ctx.type.toString().split('.').last)
- .version('${ctx.version.value}')
- .param('m', ctx.memorySizeKB)
- .param('t', ctx.passes)
- .param('p', ctx.lanes)
- .saltBytes(ctx.salt)
- .hashBytes(bytes)
- .build(),
- );
+ String encoded() => ctx.toEncoded(bytes);
}
/// The configuration used by the [Argon2] algorithm
@@ -225,4 +253,57 @@ class Argon2Context {
personalization: personalization,
);
}
+
+ /// Creates an [Argon2Context] instance from an encoded PHC-compliant string.
+ ///
+ /// The encoded string may look like this:
+ /// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
+ factory Argon2Context.fromEncoded(
+ CryptData data, {
+ List? key,
+ List? personalization,
+ }) {
+ var type = _nameToType(data.id);
+ var version = _valueToVersion(int.tryParse(data.version ?? '0') ?? 0);
+ if (data.params == null) {
+ throw ArgumentError('No paramters');
+ }
+ var m = data.params!['m'];
+ if (m == null) {
+ throw ArgumentError('Missing parameter: m');
+ }
+ var t = data.params!['t'];
+ if (t == null) {
+ throw ArgumentError('Missing parameter: t');
+ }
+ var p = data.params!['p'];
+ if (p == null) {
+ throw ArgumentError('Missing parameter: p');
+ }
+ return Argon2Context(
+ type: type,
+ version: version,
+ iterations: int.parse(t),
+ parallelism: int.parse(p),
+ memorySizeKB: int.parse(m),
+ salt: data.saltBytes(),
+ hashLength: data.hashBytes()?.lengthInBytes,
+ key: key,
+ personalization: personalization,
+ );
+ }
+
+ /// Gets the PHC-compliant string for this [Argon2HashDigest]
+ String toEncoded(Uint8List hashBytes) {
+ return toCrypt(
+ CryptDataBuilder(_typeToName(type))
+ .version('${version.value}')
+ .param('m', memorySizeKB)
+ .param('t', passes)
+ .param('p', lanes)
+ .saltBytes(salt)
+ .hashBytes(hashBytes)
+ .build(),
+ );
+ }
}
diff --git a/lib/src/algorithms/bcrypt/bcrypt.dart b/lib/src/algorithms/bcrypt/bcrypt.dart
new file mode 100644
index 0000000..cde8aed
--- /dev/null
+++ b/lib/src/algorithms/bcrypt/bcrypt.dart
@@ -0,0 +1,484 @@
+// Copyright (c) 2024, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'dart:typed_data';
+
+import 'package:hashlib/src/algorithms/bcrypt/common.dart';
+import 'package:hashlib/src/algorithms/bcrypt/security.dart';
+import 'package:hashlib/src/core/kdf_base.dart';
+import 'package:hashlib_codecs/hashlib_codecs.dart';
+
+class Bcrypt extends KeyDerivatorBase {
+ final BcryptContext _ctx;
+
+ @override
+ final int derivedKeyLength = 23;
+
+ const Bcrypt._(this._ctx);
+
+ /// Creates an [Bcrypt] instance with a sink for MAC generation.
+ factory Bcrypt({
+ required int cost,
+ List? salt,
+ BcryptVersion version = BcryptVersion.$2b,
+ }) {
+ var ctx = BcryptContext(
+ cost: cost,
+ salt: salt,
+ version: version,
+ );
+ return Bcrypt._(ctx);
+ }
+
+ /// Creates an [Bcrypt] instance from [BcryptSecurity] parameter.
+ factory Bcrypt.fromSecurity(
+ BcryptSecurity security, {
+ List? salt,
+ BcryptVersion version = BcryptVersion.$2b,
+ }) {
+ var ctx = BcryptContext(
+ cost: security.nb,
+ salt: salt,
+ version: version,
+ );
+ return Bcrypt._(ctx);
+ }
+
+ /// Creates an [Bcrypt] instance from encoded string.
+ factory Bcrypt.fromEncoded(CryptData data) {
+ var ctx = BcryptContext.fromEncoded(data);
+ return Bcrypt._(ctx);
+ }
+
+ /// Generate a derived key using the Bcrypt algorithm.
+ @override
+ BcryptHashDigest convert(List password) {
+ int i, j, l, h, t;
+ int s0, s1, s2, s3;
+ int nb = 1 << _ctx.cost;
+ var pass32 = _ctx.makePassword(password);
+ var salt32 = _makeSaltKey(_ctx.salt);
+
+ // Initialize state: P (Subkeys) + S (Substitution boxes)
+ var state = Uint32List.fromList(_sv);
+ var p = Uint32List.view(state.buffer, 0, 18);
+ var s = Uint32List.view(state.buffer, 18 << 2, 1024);
+
+ // Blowfish encrypt routine
+ l = 0;
+ h = 0;
+ void encrypt() {
+ l ^= p[0];
+ h ^= _fsub(s, l) ^ p[1];
+ l ^= _fsub(s, h) ^ p[2];
+ h ^= _fsub(s, l) ^ p[3];
+ l ^= _fsub(s, h) ^ p[4];
+ h ^= _fsub(s, l) ^ p[5];
+ l ^= _fsub(s, h) ^ p[6];
+ h ^= _fsub(s, l) ^ p[7];
+ l ^= _fsub(s, h) ^ p[8];
+ h ^= _fsub(s, l) ^ p[9];
+ l ^= _fsub(s, h) ^ p[10];
+ h ^= _fsub(s, l) ^ p[11];
+ l ^= _fsub(s, h) ^ p[12];
+ h ^= _fsub(s, l) ^ p[13];
+ l ^= _fsub(s, h) ^ p[14];
+ h ^= _fsub(s, l) ^ p[15];
+ l ^= _fsub(s, h) ^ p[16];
+ h ^= p[17];
+ // swap
+ t = l;
+ l = h;
+ h = t;
+ }
+
+ // Key Expansion
+ l = 0;
+ h = 0;
+ s0 = salt32[0];
+ s1 = salt32[1];
+ s2 = salt32[2];
+ s3 = salt32[3];
+ for (i = 0; i < 18; ++i) {
+ p[i] ^= pass32[i];
+ }
+ for (i = 0; i < state.length; i += 2) {
+ // block ^= salt
+ l ^= s0;
+ h ^= s1;
+ // blowfish encrypt
+ encrypt();
+ // set state
+ state[i] = l;
+ state[i + 1] = h;
+ // s0 <-> s2
+ t = s0;
+ s0 = s2;
+ s2 = t;
+ // s1 <-> s3
+ t = s1;
+ s1 = s3;
+ s3 = t;
+ }
+
+ // Bcrypt Rounds
+ for (j = 0; j < nb; ++j) {
+ // mix with password
+ l = 0;
+ h = 0;
+ for (i = 0; i < 18; ++i) {
+ p[i] ^= pass32[i];
+ }
+ for (i = 0; i < state.length;) {
+ encrypt();
+ state[i++] = l;
+ state[i++] = h;
+ }
+ // mix with salt
+ l = 0;
+ h = 0;
+ for (i = 0; i < 18; ++i) {
+ p[i] ^= salt32[i];
+ }
+ for (i = 0; i < state.length;) {
+ encrypt();
+ state[i++] = l;
+ state[i++] = h;
+ }
+ }
+
+ // Encrypt IV
+ var result = Uint32List.fromList(_iv);
+ for (i = 0; i < 64; i++) {
+ l = result[0];
+ h = result[1];
+ encrypt();
+ result[0] = l;
+ result[1] = h;
+
+ l = result[2];
+ h = result[3];
+ encrypt();
+ result[2] = l;
+ result[3] = h;
+
+ l = result[4];
+ h = result[5];
+ encrypt();
+ result[4] = l;
+ result[5] = h;
+ }
+
+ // Transform to bytes
+ for (i = 0; i < result.length; ++i) {
+ result[i] = _swap32(result[i]);
+ }
+ var output = Uint8List.view(result.buffer, 0, derivedKeyLength);
+ return BcryptHashDigest(_ctx, output);
+ }
+
+ static Uint32List _makeSaltKey(Uint8List salt) {
+ var salt32 = Uint32List.view(salt.buffer);
+ var dest = Uint32List(18);
+ dest[0] = _swap32(salt32[0]);
+ dest[1] = _swap32(salt32[1]);
+ dest[2] = _swap32(salt32[2]);
+ dest[3] = _swap32(salt32[3]);
+ for (int i = 4, j = 0; i < 18; i++, j++) {
+ dest[i] = dest[j];
+ }
+ return dest;
+ }
+
+ @pragma('vm:prefer-inline')
+ static int _swap32(int x) =>
+ ((x << 24) & 0xff000000) |
+ ((x << 8) & 0x00ff0000) |
+ ((x >>> 8) & 0x0000ff00) |
+ ((x >>> 24) & 0x000000ff);
+
+ /// Feistel substitution
+ @pragma('vm:prefer-inline')
+ static int _fsub(Uint32List s, int l) =>
+ ((s[(l >>> 24) & 0xff] + //
+ s[0x100 | ((l >>> 16) & 0xff)]) ^
+ s[0x200 | ((l >>> 8) & 0xff)]) +
+ s[0x300 | (l & 0xff)];
+}
+
+/// Hex form of text: 'OrpheanBeholderScryDoubt'
+const List _iv = [
+ 0x4f727068,
+ 0x65616e42,
+ 0x65686f6c,
+ 0x64657253,
+ 0x63727944,
+ 0x6f756274,
+];
+
+/// Fractional part of PI
+const List _sv = [
+ // first 18 bytes
+ 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
+ 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
+ 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+ 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
+ 0x9216d5d9, 0x8979fb1b,
+ // next 1024 bytes
+ 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
+ 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
+ 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+ 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
+ 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
+ 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+ 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
+ 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
+ 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+ 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
+ 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
+ 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+ 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
+ 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
+ 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+ 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
+ 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
+ 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+ 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
+ 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
+ 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+ 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
+ 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
+ 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+ 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
+ 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
+ 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+ 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
+ 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
+ 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+ 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
+ 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
+ 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+ 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
+ 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
+ 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+ 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
+ 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
+ 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+ 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
+ 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
+ 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+ 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
+ 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
+ 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+ 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
+ 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
+ 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+ 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
+ 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
+ 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+ 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
+ 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
+ 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+ 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
+ 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
+ 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+ 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
+ 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
+ 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+ 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
+ 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
+ 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+ 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
+ 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
+ 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
+ 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+ 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
+ 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
+ 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+ 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
+ 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
+ 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+ 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
+ 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
+ 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+ 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
+ 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
+ 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+ 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
+ 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
+ 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+ 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
+ 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
+ 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+ 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
+ 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
+ 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+ 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
+ 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
+ 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+ 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
+ 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
+ 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+ 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
+ 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
+ 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+ 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
+ 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
+ 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+ 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
+ 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
+ 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+ 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
+ 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
+ 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+ 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
+ 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
+ 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+ 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
+ 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
+ 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+ 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
+ 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
+ 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+ 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
+ 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
+ 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+ 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
+ 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
+ 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+ 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
+ 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
+ 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+ 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
+ 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
+ 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+ 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
+ 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
+ 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
+ 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+ 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
+ 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
+ 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+ 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
+ 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
+ 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+ 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
+ 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
+ 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+ 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
+ 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
+ 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+ 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
+ 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
+ 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+ 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
+ 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
+ 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+ 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
+ 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
+ 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+ 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
+ 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
+ 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+ 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
+ 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
+ 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+ 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
+ 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
+ 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+ 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
+ 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
+ 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+ 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
+ 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
+ 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+ 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
+ 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
+ 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+ 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
+ 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
+ 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+ 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
+ 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
+ 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+ 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
+ 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
+ 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+ 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
+ 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
+ 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+ 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
+ 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
+ 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+ 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
+ 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
+ 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+ 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
+ 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
+ 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+ 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
+ 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
+ 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
+ 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+ 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
+ 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
+ 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+ 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
+ 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
+ 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+ 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
+ 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
+ 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+ 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
+ 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
+ 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+ 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
+ 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
+ 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+ 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
+ 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
+ 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+ 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
+ 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
+ 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+ 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
+ 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
+ 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+ 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
+ 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
+ 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+ 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
+ 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
+ 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+ 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
+ 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
+ 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+ 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
+ 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
+ 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+ 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
+ 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
+ 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+ 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
+ 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
+ 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+ 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
+ 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
+ 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+ 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
+ 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
+ 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+ 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
+ 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
+ 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+ 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
+ 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
+ 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+ 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
+ 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
+ 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+ 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
+ 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
+ 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+ 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6,
+];
diff --git a/lib/src/algorithms/bcrypt/common.dart b/lib/src/algorithms/bcrypt/common.dart
new file mode 100644
index 0000000..8f7e492
--- /dev/null
+++ b/lib/src/algorithms/bcrypt/common.dart
@@ -0,0 +1,177 @@
+// Copyright (c) 2024, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'dart:typed_data';
+
+import 'package:hashlib/src/algorithms/bcrypt/bcrypt.dart';
+import 'package:hashlib/src/core/hash_digest.dart';
+import 'package:hashlib/src/random.dart';
+import 'package:hashlib_codecs/hashlib_codecs.dart';
+
+enum BcryptVersion {
+ /// This is a revised version of original v2 with UTF-8 character support
+ /// and inclusion of the null terminator with the password.
+ $2a,
+
+ /// This is the bug-fixed version of OpenBSD implementation of bcrypt, which
+ /// fixes the support for password longer than 255 characters.
+ $2b,
+
+ /// This was introduced by PHP `crypt_blowfish` implementation to mark hashes
+ /// generated with a bug in their implementation.
+ $2x,
+
+ /// This was introduced by PHP `crypt_blowfish` implementation to mark hashes
+ /// generated after fixing the bug in their implementation.
+ $2y,
+}
+
+String _versionToName(BcryptVersion version) {
+ switch (version) {
+ case BcryptVersion.$2a:
+ return '2a';
+ case BcryptVersion.$2b:
+ return '2b';
+ case BcryptVersion.$2x:
+ return '2x';
+ case BcryptVersion.$2y:
+ return '2y';
+ default:
+ throw ArgumentError('Invalid version');
+ }
+}
+
+BcryptVersion _nameToVersion(String name) {
+ switch (name) {
+ case '2a':
+ return BcryptVersion.$2a;
+ case '2b':
+ return BcryptVersion.$2b;
+ case '2x':
+ return BcryptVersion.$2x;
+ case '2y':
+ return BcryptVersion.$2y;
+ default:
+ throw ArgumentError('Invalid version');
+ }
+}
+
+class BcryptHashDigest extends HashDigest {
+ final BcryptContext ctx;
+
+ const BcryptHashDigest(this.ctx, Uint8List bytes) : super(bytes);
+
+ @override
+ String toString() => encoded();
+
+ /// Gets a PHC-compliant encoded string
+ String encoded() => ctx.toEncoded(bytes);
+}
+
+/// The configuration used by the [Bcrypt] algorithm
+class BcryptContext {
+ /// The BCrypt version
+ final BcryptVersion version;
+
+ /// Number of rounds in terms of power of 2
+ final int cost;
+
+ /// 16-byte salt
+ final Uint8List salt;
+
+ const BcryptContext._({
+ required this.salt,
+ required this.version,
+ required this.cost,
+ });
+
+ /// Creates an [BcryptContext] instance from encoded string.
+ ///
+ /// Parameters:
+ /// - [version] : The [BcryptVersion] to use. Default [BcryptVersion.$2b].
+ /// - [salt] : An uniquely and randomly generated string.
+ /// - [cost] : Number of rounds in terms of power of 2. 0 < [cost] < 31.
+ factory BcryptContext({
+ required int cost,
+ List? salt,
+ BcryptVersion version = BcryptVersion.$2b,
+ }) {
+ // validate parameters
+ if (cost < 0) {
+ throw StateError('The cost must be at least 0');
+ }
+ if (cost > 31) {
+ throw StateError('The cost must be at most 31');
+ }
+ salt ??= randomBytes(16);
+ if (salt.length != 16) {
+ throw StateError('The salt must be exactly 16-bytes');
+ }
+ return BcryptContext._(
+ cost: cost,
+ version: version,
+ salt: salt is Uint8List ? salt : Uint8List.fromList(salt),
+ );
+ }
+
+ /// Creates an [BcryptContext] instance from encoded string.
+ ///
+ /// The encoded string may look like this:
+ /// `$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC`
+ factory BcryptContext.fromEncoded(CryptData data) {
+ var version = _nameToVersion(data.id);
+ var cost = int.tryParse(data.salt ?? '0');
+ if (cost == null) {
+ throw ArgumentError('Invalid cost');
+ }
+ Uint8List? salt;
+ if (data.hash != null) {
+ salt = fromBase64(data.hash!.substring(0, 22), codec: Base64Codec.bcrypt);
+ }
+ return BcryptContext(
+ salt: salt,
+ cost: cost,
+ version: version,
+ );
+ }
+
+ /// Gets a PHC-compliant encoded string
+ String toEncoded([Uint8List? hashBytes]) {
+ var hash = toBase64(salt, codec: Base64Codec.bcrypt);
+ if (hashBytes != null) {
+ hash += toBase64(hashBytes, codec: Base64Codec.bcrypt);
+ }
+ return toCrypt(
+ CryptDataBuilder(_versionToName(version))
+ .salt('$cost'.padLeft(2, '0'))
+ .hash(hash)
+ .build(),
+ );
+ }
+
+ /// Make the 72-byte long password using version-specific strategy.
+ Uint32List makePassword(List password) {
+ int i, j;
+ var long32 = Uint32List(18);
+ var long = Uint8List.view(long32.buffer);
+ var pass8 = Uint8List.fromList(password);
+ for (i = 0; i < 72 && i < pass8.length; i++) {
+ long[i] = pass8[i];
+ }
+ if (i < 72) {
+ long[i++] = 0;
+ }
+ for (j = 0; i < 72; i++, j++) {
+ long[i] = long[j];
+ }
+ for (i = 0; i < 18; ++i) {
+ j = long32[i];
+ j = ((j << 24) & 0xff000000) |
+ ((j << 8) & 0x00ff0000) |
+ ((j >>> 8) & 0x0000ff00) |
+ ((j >>> 24) & 0x000000ff);
+ long32[i] = j;
+ }
+ return long32;
+ }
+}
diff --git a/lib/src/algorithms/bcrypt/security.dart b/lib/src/algorithms/bcrypt/security.dart
new file mode 100644
index 0000000..b153f98
--- /dev/null
+++ b/lib/src/algorithms/bcrypt/security.dart
@@ -0,0 +1,47 @@
+// Copyright (c) 2024, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'bcrypt.dart';
+
+/// This contains some recommended parameters for [Bcrypt] algorithm.
+class BcryptSecurity {
+ final String name;
+
+ /// The number of rounds in terms of power of 2.
+ final int nb;
+
+ const BcryptSecurity(
+ this.name, {
+ required this.nb,
+ });
+
+ @override
+ String toString() => "BCryptSecurity($name):nb=$nb";
+
+ /// Provides a very low security. Use it for test purposes.
+ ///
+ /// It uses 2^1 = 2 round for encryption.
+ ///
+ /// **WARNING: Not recommended for general use.**
+ static const test = BcryptSecurity('test', nb: 1);
+
+ /// Provides low security but faster. Suitable for low-end devices.
+ ///
+ /// It uses 2^5 = 32 rounds for encryption.
+ static const little = BcryptSecurity('little', nb: 5);
+
+ /// Provides moderate security. Suitable for modern mobile devices.
+ ///
+ /// It uses 2^8 = 256 rounds for encryption.
+ static const moderate = BcryptSecurity('moderate', nb: 8);
+
+ /// Provides good security.
+ ///
+ /// It uses 2^12 = 4096 rounds for encryption.
+ static const good = BcryptSecurity('good', nb: 12);
+
+ /// Provides strong security.
+ ///
+ /// It uses 2^15 = 32768 rounds for encryption.
+ static const strong = BcryptSecurity('strong', nb: 15);
+}
diff --git a/lib/src/algorithms/scrypt.dart b/lib/src/algorithms/scrypt/scrypt.dart
similarity index 79%
rename from lib/src/algorithms/scrypt.dart
rename to lib/src/algorithms/scrypt/scrypt.dart
index d1d8dcb..f599a54 100644
--- a/lib/src/algorithms/scrypt.dart
+++ b/lib/src/algorithms/scrypt/scrypt.dart
@@ -5,8 +5,10 @@ import 'dart:typed_data';
import 'package:hashlib/src/algorithms/hmac.dart';
import 'package:hashlib/src/algorithms/pbkdf2.dart';
+import 'package:hashlib/src/algorithms/scrypt/security.dart';
import 'package:hashlib/src/core/hash_digest.dart';
import 'package:hashlib/src/core/kdf_base.dart';
+import 'package:hashlib/src/random.dart';
import 'package:hashlib/src/sha256.dart';
const int _mask32 = 0xFFFFFFFF;
@@ -51,7 +53,7 @@ class Scrypt extends KeyDerivatorBase {
/// Creates an [Scrypt] instance with a sink for MAC generation.
factory Scrypt({
- required List salt,
+ List? salt,
required int cost,
int blockSize = 8,
int parallelism = 1,
@@ -79,6 +81,7 @@ class Scrypt extends KeyDerivatorBase {
if (blockSize * parallelism > 0x1FFFFFF) {
throw StateError('The blockSize * parallelism is too big');
}
+ salt ??= randomBytes(16);
// create instance
return Scrypt._(
@@ -90,6 +93,22 @@ class Scrypt extends KeyDerivatorBase {
);
}
+ /// Creates an [Scrypt] instance from [ScryptSecurity] parameter.
+ factory Scrypt.fromSecurity(
+ ScryptSecurity security, {
+ List? salt,
+ int blockSize = 8,
+ int parallelism = 1,
+ int derivedKeyLength = 64,
+ }) {
+ return Scrypt(
+ salt: salt,
+ cost: security.N,
+ blockSize: security.r,
+ parallelism: security.p,
+ );
+ }
+
/// Generate a derived key using the scrypt algorithm.
@override
HashDigest convert(List password) {
@@ -256,62 +275,3 @@ class Scrypt extends KeyDerivatorBase {
b[15] += x15;
}
}
-
-/// This contains some recommended values of memory, iteration and parallelism
-/// values for [Scrypt] algorithm.
-///
-/// It is best to try out different combinations of these values to achieve the
-/// desired runtime on a target machine.
-class ScryptSecurity {
- final String name;
-
- /// The size of a single block in bytes
- final int r;
-
- /// The CPU/Memory cost parameter as a power of 2. 1 < [N] < 2^32
- final int N;
-
- /// The parallelization parameter. [p] <= (2^32 - 1) / (128 * [r])
- final int p;
-
- const ScryptSecurity(
- this.name, {
- required this.N,
- required this.r,
- required this.p,
- });
-
- @override
- String toString() => "ScryptSecurity($name):{N=$N,r=$r,p=$p}";
-
- /// Provides a very low security. Use it only for test purposes.
- ///
- /// It uses 16 lanes, 2 blocks per lane and 1 iteration.
- ///
- /// **WARNING: Not recommended for general use.**
- static const test = ScryptSecurity('test', N: 1 << 4, r: 2, p: 1);
-
- /// Provides low security. Can be used on low-end devices.
- ///
- /// It uses 256 lanes, 4 blocks per lane and 2 iterations.
- ///
- /// **WARNING: Not recommended for general use.**
- static const little = ScryptSecurity('little', N: 1 << 8, r: 4, p: 2);
-
- /// Provides moderate security.
- ///
- /// It uses 1,024 lanes, 8 blocks per lane and 2 iterations.
- static const moderate = ScryptSecurity('moderate', N: 1 << 10, r: 8, p: 2);
-
- /// Provides good security. The default parameters from [RFC-7914][rfc]
- ///
- /// It uses 16,384 lanes, 8 blocks per lane and 1 iteration.
- ///
- /// [rfc]: https://www.ietf.org/rfc/rfc7914.html
- static const good = ScryptSecurity('good', N: 1 << 14, r: 8, p: 1);
-
- /// Provides strong security.
- ///
- /// It uses 65,536 lanes, 16 blocks per lane and 2 iterations.
- static const strong = ScryptSecurity('strong', N: 1 << 16, r: 16, p: 2);
-}
diff --git a/lib/src/algorithms/scrypt/security.dart b/lib/src/algorithms/scrypt/security.dart
new file mode 100644
index 0000000..31afb49
--- /dev/null
+++ b/lib/src/algorithms/scrypt/security.dart
@@ -0,0 +1,61 @@
+// Copyright (c) 2023, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+/// This contains some recommended values of memory, iteration and parallelism
+/// values for [Scrypt] algorithm.
+///
+/// It is best to try out different combinations of these values to achieve the
+/// desired runtime on a target machine.
+class ScryptSecurity {
+ final String name;
+
+ /// The size of a single block in bytes
+ final int r;
+
+ /// The CPU/Memory cost parameter as a power of 2. 1 < [N] < 2^32
+ final int N;
+
+ /// The parallelization parameter. [p] <= (2^32 - 1) / (128 * [r])
+ final int p;
+
+ const ScryptSecurity(
+ this.name, {
+ required this.N,
+ required this.r,
+ required this.p,
+ });
+
+ @override
+ String toString() => "ScryptSecurity($name):{N=$N,r=$r,p=$p}";
+
+ /// Provides a very low security. Use it only for test purposes.
+ ///
+ /// It uses 16 lanes, 2 blocks per lane and 1 iteration.
+ ///
+ /// **WARNING: Not recommended for general use.**
+ static const test = ScryptSecurity('test', N: 1 << 4, r: 2, p: 1);
+
+ /// Provides low security. Can be used on low-end devices.
+ ///
+ /// It uses 256 lanes, 4 blocks per lane and 2 iterations.
+ ///
+ /// **WARNING: Not recommended for general use.**
+ static const little = ScryptSecurity('little', N: 1 << 8, r: 4, p: 2);
+
+ /// Provides moderate security.
+ ///
+ /// It uses 1,024 lanes, 8 blocks per lane and 2 iterations.
+ static const moderate = ScryptSecurity('moderate', N: 1 << 10, r: 8, p: 2);
+
+ /// Provides good security. The default parameters from [RFC-7914][rfc]
+ ///
+ /// It uses 16,384 lanes, 8 blocks per lane and 1 iteration.
+ ///
+ /// [rfc]: https://www.ietf.org/rfc/rfc7914.html
+ static const good = ScryptSecurity('good', N: 1 << 14, r: 8, p: 1);
+
+ /// Provides strong security.
+ ///
+ /// It uses 65,536 lanes, 16 blocks per lane and 2 iterations.
+ static const strong = ScryptSecurity('strong', N: 1 << 16, r: 16, p: 2);
+}
diff --git a/lib/src/argon2.dart b/lib/src/argon2.dart
index 3d0b4ae..54c695b 100644
--- a/lib/src/argon2.dart
+++ b/lib/src/argon2.dart
@@ -14,9 +14,13 @@ const _defaultSecurity = Argon2Security.good;
/// The encoded hash may look like this:
/// `$argon2i$v=19$m=16,t=2,p=1$c29tZSBzYWx0$u1eU6mZFG4/OOoTdAtM5SQ`
bool argon2verify(String encoded, List password) {
- var instance = Argon2.fromEncoded(encoded);
- var key = fromBase64(encoded.split('\$').last);
- return instance.verify(key, password);
+ var data = fromCrypt(encoded);
+ var hash = data.hashBytes();
+ if (hash == null) {
+ throw ArgumentError('No password hash in the encoded string');
+ }
+ var instance = Argon2.fromEncoded(data);
+ return instance.verify(hash, password);
}
/// Encode a password using default Argon2d algorithm
diff --git a/lib/src/bcrypt.dart b/lib/src/bcrypt.dart
new file mode 100644
index 0000000..f448324
--- /dev/null
+++ b/lib/src/bcrypt.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2024, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'package:hashlib/src/algorithms/bcrypt/bcrypt.dart';
+import 'package:hashlib/src/algorithms/bcrypt/common.dart';
+import 'package:hashlib/src/algorithms/bcrypt/security.dart';
+import 'package:hashlib_codecs/hashlib_codecs.dart';
+
+export 'algorithms/bcrypt/bcrypt.dart' show Bcrypt;
+export 'algorithms/bcrypt/common.dart'
+ show BcryptVersion, BcryptContext, BcryptHashDigest;
+export 'algorithms/bcrypt/security.dart' show BcryptSecurity;
+
+/// Generate a secure password using the [Bcrypt] algorithm.
+///
+/// [Bcrypt][wiki] is a password hashing algorithm based on the Blowfish cipher.
+/// It uses a unique salt and a configurable cost factor to prevent rainbow
+/// table attacks and remain secure as hardware improves. Bcrypt's iterative
+/// hashing and expensive key setup phase ensure resistance to brute-force
+/// attacks.
+///
+/// Parameters:
+/// - [password] : The password to hash. The algorithm uses first 72 bytes, and
+/// the rest are ignored.
+/// - [salt] : An randomly generated 16-byte string.
+/// - [nb] : Number of rounds in terms of power of 2. 0 < [nb] < 31
+/// - [version] : The [BcryptVersion] to use. Default [BcryptVersion.$2b]
+/// - [security] : The default parameter source for [nb] if not provided
+/// (default is [BcryptSecurity.good]).
+///
+/// [wiki]: https://en.wikipedia.org/wiki/Bcrypt
+BcryptHashDigest bcryptDigest(
+ List password, {
+ List? salt,
+ BcryptVersion version = BcryptVersion.$2b,
+ BcryptSecurity security = BcryptSecurity.good,
+ int? nb,
+}) =>
+ Bcrypt(
+ version: version,
+ salt: salt,
+ cost: nb ?? security.nb,
+ ).convert(password);
+
+/// Generate the encoded salt to be used by the [Bcrypt] algorithm.
+///
+/// Parameters:
+/// - [nb] : Number of rounds in terms of power of 2. 0 < [nb] < 31
+/// - [version] : The [BcryptVersion] to use. Default [BcryptVersion.$2b]
+/// - [security] : The default parameter source for [nb] if not provided
+/// (default is [BcryptSecurity.good]).
+String bcryptSalt({
+ int? nb,
+ BcryptVersion version = BcryptVersion.$2b,
+ BcryptSecurity security = BcryptSecurity.good,
+}) =>
+ BcryptContext(
+ version: version,
+ cost: nb ?? security.nb,
+ ).toEncoded();
+
+/// Generates a secure password using the [Bcrypt] algorithm, and returns a
+/// 60-byte encoded string containing the version, cost, salt, and password.
+///
+/// Parameters:
+/// - [password] : The password to hash. The algorithm uses first 72 bytes, and
+/// the rest are ignored.
+/// - [salt] : Encoded salt, e.g.: `$2b$04$SQe9knOzepOVKoYXo9xTte`
+/// If not provided, [bcryptSalt] is used to get a random one.
+///
+/// **Note**: Complete encoded string is also accepted, e.g.:
+/// `$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW`
+String bcrypt(List password, [String? salt]) =>
+ Bcrypt.fromEncoded(fromCrypt(salt ?? bcryptSalt()))
+ .convert(password)
+ .encoded();
+
+/// Verifies if the [plain] password was derived from the [encoded] hash.
+///
+/// The encoded hash may look like this:
+/// `$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW`
+bool bcryptVerify(String encoded, List plain) {
+ var data = fromCrypt(encoded);
+ var hash = data.hash!.substring(22);
+ var hashBytes = fromBase64(hash, codec: Base64Codec.bcrypt);
+ var instance = Bcrypt.fromEncoded(data);
+ return instance.verify(hashBytes, plain);
+}
diff --git a/lib/src/hashlib_base.dart b/lib/src/hashlib_base.dart
index 6ee7ee5..d69e8b7 100644
--- a/lib/src/hashlib_base.dart
+++ b/lib/src/hashlib_base.dart
@@ -3,6 +3,7 @@
export 'alder32.dart';
export 'argon2.dart';
+export 'bcrypt.dart';
export 'blake2b.dart';
export 'blake2s.dart';
export 'core/block_hash.dart';
diff --git a/lib/src/scrypt.dart b/lib/src/scrypt.dart
index 3696c9a..84a57e9 100644
--- a/lib/src/scrypt.dart
+++ b/lib/src/scrypt.dart
@@ -1,10 +1,12 @@
// Copyright (c) 2023, Sudipto Chandra
// All rights reserved. Check LICENSE file for details.
-import 'package:hashlib/src/algorithms/scrypt.dart';
+import 'package:hashlib/src/algorithms/scrypt/scrypt.dart';
+import 'package:hashlib/src/algorithms/scrypt/security.dart';
import 'package:hashlib/src/core/hash_digest.dart';
-export 'package:hashlib/src/algorithms/scrypt.dart' show Scrypt, ScryptSecurity;
+export 'algorithms/scrypt/scrypt.dart' show Scrypt;
+export 'algorithms/scrypt/security.dart' show ScryptSecurity;
/// Generate a secure password using the [scrypt][rfc] algorithm.
///
diff --git a/pubspec.yaml b/pubspec.yaml
index 6cec3f4..3f0204c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,10 +1,10 @@
name: hashlib
description: Secure hash functions, checksum generators, and key derivation algorithms optimized for Dart.
homepage: https://github.com/bitanon/hashlib
-version: 1.17.0
+version: 1.18.0
environment:
- sdk: ">=2.14.0 <4.0.0"
+ sdk: '>=2.14.0 <4.0.0'
platforms:
android:
@@ -15,7 +15,7 @@ platforms:
windows:
dependencies:
- hashlib_codecs: ^2.4.0
+ hashlib_codecs: ^2.5.0
dev_dependencies:
lints: any
diff --git a/scripts/prepublish.bat b/scripts/prepublish.bat
index 6ce41f1..a959d22 100644
--- a/scripts/prepublish.bat
+++ b/scripts/prepublish.bat
@@ -5,7 +5,7 @@ rd /s /q "build" 2>nul
call dart format --fix . || goto :error
call dart analyze --fatal-infos || goto :error
-call dart doc || goto :error
+call dart doc --validate-links || goto :error
call dart test || goto :error
goto :EOF
diff --git a/scripts/prepublish.sh b/scripts/prepublish.sh
index d4e1e5c..e364dce 100644
--- a/scripts/prepublish.sh
+++ b/scripts/prepublish.sh
@@ -3,5 +3,5 @@ set -ex
rm -rf "doc" "build"
dart format --fix .
dart analyze --fatal-infos
+dart doc --validate-links
dart test
-dart doc
diff --git a/test/argon2_test.dart b/test/argon2_test.dart
index 20e030f..fb4a968 100644
--- a/test/argon2_test.dart
+++ b/test/argon2_test.dart
@@ -99,7 +99,7 @@ void main() {
final encoded =
r"$argon2id$v=19$m=128,t=1,p=4$c29tZSBzYWx0$24VHMpaU5EkkdH5rpdnb5zeOf3Y";
final matcher = "db8547329694e44924747e6ba5d9dbe7378e7f76";
- final argon2 = Argon2.fromEncoded(encoded);
+ final argon2 = Argon2.fromEncoded(fromCrypt(encoded));
expect(argon2.type, Argon2Type.argon2id);
expect(argon2.version, Argon2Version.v13);
expect(argon2.memorySizeKB, 128);
diff --git a/test/bcrypt_test.dart b/test/bcrypt_test.dart
new file mode 100644
index 0000000..9bb3620
--- /dev/null
+++ b/test/bcrypt_test.dart
@@ -0,0 +1,461 @@
+// Copyright (c) 2023, Sudipto Chandra
+// All rights reserved. Check LICENSE file for details.
+
+import 'dart:convert';
+
+import 'package:hashlib/hashlib.dart';
+import 'package:test/test.dart';
+
+void main() {
+ group('bcrypt test', () {
+ group('version 2a', () {
+ // http://openwall.info/wiki/john/sample-hashes
+ test(r"$2a$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe", () {
+ const password = r"password";
+ const encoded =
+ r"$2a$05$bvIG6Nmid91Mu9RcmmWZfO5HJIMCT8riNW0hEp8f6/FuA2/mHZFpe";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ // http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/wrapper.c?rev=HEAD
+ test(r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW", () {
+ const password = r"U*U";
+ const encoded =
+ r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK", () {
+ const password = r"U*U*";
+ const encoded =
+ r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a", () {
+ const password = r"U*U*U";
+ const encoded =
+ r"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.", () {
+ const password = r"";
+ const encoded =
+ r"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s.";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ // https://stackoverflow.com/a/12761326/774398
+ test(r"$2a$10$.TtQJ4Jr6isd4Hp.mVfZeuh6Gws4rOQ/vdBczhDx.19NFK0Y84Dle", () {
+ const password = r"ππππππππ";
+ const encoded =
+ r"$2a$10$.TtQJ4Jr6isd4Hp.mVfZeuh6Gws4rOQ/vdBczhDx.19NFK0Y84Dle";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ // https://bitbucket.org/vadim/bcrypt.net/src/464c41416dc9/BCrypt.Net.Test/TestBCrypt.cs?fileviewer=file-view-default
+ test(r"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye", () {
+ const password = r"";
+ const encoded =
+ r"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe", () {
+ const password = r"a";
+ const encoded =
+ r"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i", () {
+ const password = r"abc";
+ const encoded =
+ r"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC", () {
+ const password = r"abcdefghijklmnopqrstuvwxyz";
+ const encoded =
+ r"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO", () {
+ const password = r"~!@#$%^&*() ~!@#$%^&*()PNBFRD";
+ const encoded =
+ r"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ // https://github.com/pyca/bcrypt/blob/main/tests/test_bcrypt.py
+ test(r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW", () {
+ var password = "U*U";
+ var encoded =
+ r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.E5YPO9kmyuRGyh0XouQYb4YMJKvyOeW";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK", () {
+ var password = "U*U*";
+ var encoded =
+ r"$2a$05$CCCCCCCCCCCCCCCCCCCCC.VGOzA784oUp/Z0DY336zx7pLYAy0lwK";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a", () {
+ var password = "U*U*U";
+ var encoded =
+ r"$2a$05$XXXXXXXXXXXXXXXXXXXXXOAcXxm9kjPGEMsLznoKqmqw7tc8WCx4a";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui", () {
+ var password = "0123456789abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ "chars after 72 are ignored";
+ var encoded =
+ r"$2a$05$abcdefghijklmnopqrstuu5s2v8.iXieOjg/.AySBTTZIIVFJeBui";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6", () {
+ var password = "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"
+ "chars after 72 are ignored as usual";
+ var encoded =
+ r"$2a$05$/OK.fbVrR/bpIqNJ5ianF.swQOIzjOiJ9GHEPuhEkvqrUyvWhEMx6";
+ var output = bcrypt(password.codeUnits, encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq", () {
+ var password = "\xa3";
+ var encoded =
+ r"$2a$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq";
+ var output = bcrypt(password.codeUnits, encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2a$04$tecY.9ylRInW/rAAzXCXPOOlyYeCNzmNTzPDNSIFztFMKbvs/s5XG", () {
+ var password =
+ "g7\r\x01\xf3\xd4\xd0\xa9JB^\x18\x007P\xb2N\xc7\x1c\xee\x87&\x83C"
+ "\x8b\xe8\x18\xc5>\x86\x14/\xd6\xcc\x1cJ\xde\xd7ix\xeb\xdeO\xef"
+ "\xe1i\xac\xcb\x03\x96v1' \xd6@.m\xa5!\xa0\xef\xc0(";
+ var encoded =
+ r"$2a$04$tecY.9ylRInW/rAAzXCXPOOlyYeCNzmNTzPDNSIFztFMKbvs/s5XG";
+ var output = bcrypt(password.codeUnits, encoded);
+ expect(output, equals(encoded));
+ });
+ });
+
+ group('version 2y', () {
+ test(r"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq", () {
+ var password = "\xa3";
+ var encoded =
+ r"$2y$05$/OK.fbVrR/bpIqNJ5ianF.Sa7shbm4.OzKpvFnX1pQLmQW96oUlCq";
+ var output = bcrypt(password.codeUnits, encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e", () {
+ var password = "\xff\xff\xa3";
+ var encoded =
+ r"$2y$05$/OK.fbVrR/bpIqNJ5ianF.CE5elHaaO4EbggVDjb8P19RukzXSM3e";
+ var output = bcrypt(password.codeUnits, encoded);
+ expect(output, equals(encoded));
+ });
+ });
+
+ // https://github.com/pyca/bcrypt/blob/main/tests/test_bcrypt.py
+ group('version 2b', () {
+ test(r"$2b$04$cVWp4XaNU8a4v1uMRum2SO026BWLIoQMD/TXg5uZV.0P.uO8m3YEm", () {
+ var password = "Kk4DQuMMfZL9o";
+ var encoded =
+ r"$2b$04$cVWp4XaNU8a4v1uMRum2SO026BWLIoQMD/TXg5uZV.0P.uO8m3YEm";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$pQ7gRO7e6wx/936oXhNjrOUNOHL1D0h1N2IDbJZYs.1ppzSof6SPy", () {
+ var password = "9IeRXmnGxMYbs";
+ var encoded =
+ r"$2b$04$pQ7gRO7e6wx/936oXhNjrOUNOHL1D0h1N2IDbJZYs.1ppzSof6SPy";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW", () {
+ var password = "xVQVbwa1S0M8r";
+ var encoded =
+ r"$2b$04$SQe9knOzepOVKoYXo9xTteNYr6MBwVz4tpriJVe3PNgYufGIsgKcW";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$eH8zX.q5Q.j2hO1NkVYJQOM6KxntS/ow3.YzVmFrE4t//CoF4fvne", () {
+ var password = "Zfgr26LWd22Za";
+ var encoded =
+ r"$2b$04$eH8zX.q5Q.j2hO1NkVYJQOM6KxntS/ow3.YzVmFrE4t//CoF4fvne";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$ahiTdwRXpUG2JLRcIznxc.s1.ydaPGD372bsGs8NqyYjLY1inG5n2", () {
+ var password = "Tg4daC27epFBE";
+ var encoded =
+ r"$2b$04$ahiTdwRXpUG2JLRcIznxc.s1.ydaPGD372bsGs8NqyYjLY1inG5n2";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$nQn78dV0hGHf5wUBe0zOFu8n07ZbWWOKoGasZKRspZxtt.vBRNMIy", () {
+ var password = "xhQPMmwh5ALzW";
+ var encoded =
+ r"$2b$04$nQn78dV0hGHf5wUBe0zOFu8n07ZbWWOKoGasZKRspZxtt.vBRNMIy";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$cvXudZ5ugTg95W.rOjMITuM1jC0piCl3zF5cmGhzCibHZrNHkmckG", () {
+ var password = "59je8h5Gj71tg";
+ var encoded =
+ r"$2b$04$cvXudZ5ugTg95W.rOjMITuM1jC0piCl3zF5cmGhzCibHZrNHkmckG";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$YYjtiq4Uh88yUsExO0RNTuEJ.tZlsONac16A8OcLHleWFjVawfGvO", () {
+ var password = "wT4fHJa2N9WSW";
+ var encoded =
+ r"$2b$04$YYjtiq4Uh88yUsExO0RNTuEJ.tZlsONac16A8OcLHleWFjVawfGvO";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$WLTjgY/pZSyqX/fbMbJzf.qxCeTMQOzgL.CimRjMHtMxd/VGKojMu", () {
+ var password = "uSgFRnQdOgm4S";
+ var encoded =
+ r"$2b$04$WLTjgY/pZSyqX/fbMbJzf.qxCeTMQOzgL.CimRjMHtMxd/VGKojMu";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$2moPs/x/wnCfeQ5pCheMcuSJQ/KYjOZG780UjA/SiR.KsYWNrC7SG", () {
+ var password = "tEPtJZXur16Vg";
+ var encoded =
+ r"$2b$04$2moPs/x/wnCfeQ5pCheMcuSJQ/KYjOZG780UjA/SiR.KsYWNrC7SG";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$HrEYC/AQ2HS77G78cQDZQ.r44WGcruKw03KHlnp71yVQEwpsi3xl2", () {
+ var password = "vvho8C6nlVf9K";
+ var encoded =
+ r"$2b$04$HrEYC/AQ2HS77G78cQDZQ.r44WGcruKw03KHlnp71yVQEwpsi3xl2";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$vVYgSTfB8KVbmhbZE/k3R.ux9A0lJUM4CZwCkHI9fifke2.rTF7MG", () {
+ var password = "5auCCY9by0Ruf";
+ var encoded =
+ r"$2b$04$vVYgSTfB8KVbmhbZE/k3R.ux9A0lJUM4CZwCkHI9fifke2.rTF7MG";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$JfoNrR8.doieoI8..F.C1OQgwE3uTeuardy6lw0AjALUzOARoyf2m", () {
+ var password = "GtTkR6qn2QOZW";
+ var encoded =
+ r"$2b$04$JfoNrR8.doieoI8..F.C1OQgwE3uTeuardy6lw0AjALUzOARoyf2m";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$HP3I0PUs7KBEzMBNFw7o3O7f/uxaZU7aaDot1quHMgB2yrwBXsgyy", () {
+ var password = "zKo8vdFSnjX0f";
+ var encoded =
+ r"$2b$04$HP3I0PUs7KBEzMBNFw7o3O7f/uxaZU7aaDot1quHMgB2yrwBXsgyy";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$xnFVhJsTzsFBTeP3PpgbMeMREb6rdKV9faW54Sx.yg9plf4jY8qT6", () {
+ var password = "I9VfYlacJiwiK";
+ var encoded =
+ r"$2b$04$xnFVhJsTzsFBTeP3PpgbMeMREb6rdKV9faW54Sx.yg9plf4jY8qT6";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$WQp9.igoLqVr6Qk70mz6xuRxE0RttVXXdukpR9N54x17ecad34ZF6", () {
+ var password = "VFPO7YXnHQbQO";
+ var encoded =
+ r"$2b$04$WQp9.igoLqVr6Qk70mz6xuRxE0RttVXXdukpR9N54x17ecad34ZF6";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$xgZtlonpAHSU/njOCdKztOPuPFzCNVpB4LGicO4/OGgHv.uKHkwsS", () {
+ var password = "VDx5BdxfxstYk";
+ var encoded =
+ r"$2b$04$xgZtlonpAHSU/njOCdKztOPuPFzCNVpB4LGicO4/OGgHv.uKHkwsS";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$2Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe", () {
+ var password = "dEe6XfVGrrfSH";
+ var encoded =
+ r"$2b$04$2Siw3Nv3Q/gTOIPetAyPr.GNj3aO0lb1E5E9UumYGKjP9BYqlNWJe";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$7/Qj7Kd8BcSahPO4khB8me4ssDJCW3r4OGYqPF87jxtrSyPj5cS5m", () {
+ var password = "cTT0EAFdwJiLn";
+ var encoded =
+ r"$2b$04$7/Qj7Kd8BcSahPO4khB8me4ssDJCW3r4OGYqPF87jxtrSyPj5cS5m";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$04$VvlCUKbTMjaxaYJ.k5juoecpG/7IzcH1AkmqKi.lIZMVIOLClWAk.", () {
+ var password = "J8eHUDuxBB520";
+ var encoded =
+ r"$2b$04$VvlCUKbTMjaxaYJ.k5juoecpG/7IzcH1AkmqKi.lIZMVIOLClWAk.";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+
+ test(r"$2b$10$keO.ZZs22YtygVF6BLfhGOI/JjshJYPp8DZsUtym6mJV2Eha2Hdd.", () {
+ var password = [
+ 125, 62, 179, 254, 241, 139, 160, 230, 40, 162, 76, 122, 113, 195, //
+ 80, 127, 204, 200, 98, 123, 249, 20, 246, 246, 96, 129, 71, 53, 236,
+ 29, 135, 16, 191, 167, 225, 125, 73, 55, 32, 150, 223, 99, 242, 191,
+ 179, 86, 104, 223, 77, 136, 113, 247, 255, 27, 130, 126, 122, 19, 221,
+ 233, 132, 0, 221, 52
+ ];
+ var encoded =
+ r"$2b$10$keO.ZZs22YtygVF6BLfhGOI/JjshJYPp8DZsUtym6mJV2Eha2Hdd.";
+ var output = bcrypt(password, encoded);
+ expect(output, equals(encoded));
+ });
+ });
+
+ group('big cost', () {
+ test(r"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW", () {
+ const password = r"";
+ const encoded =
+ r"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO", () {
+ const password = r"";
+ const encoded =
+ r"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V.", () {
+ const password = r"a";
+ const encoded =
+ r"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V.";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u", () {
+ const password = r"a";
+ const encoded =
+ r"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS", () {
+ const password = r"a";
+ const encoded =
+ r"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm", () {
+ const password = r"abc";
+ const encoded =
+ r"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi", () {
+ const password = r"abc";
+ const encoded =
+ r"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q", () {
+ const password = r"abc";
+ const encoded =
+ r"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz.", () {
+ const password = r"abcdefghijklmnopqrstuvwxyz";
+ const encoded =
+ r"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz.";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq", () {
+ const password = r"abcdefghijklmnopqrstuvwxyz";
+ const encoded =
+ r"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG", () {
+ const password = r"abcdefghijklmnopqrstuvwxyz";
+ const encoded =
+ r"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW", () {
+ const password = r"~!@#$%^&*() ~!@#$%^&*()PNBFRD";
+ const encoded =
+ r"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS", () {
+ const password = r"~!@#$%^&*() ~!@#$%^&*()PNBFRD";
+ const encoded =
+ r"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ test(r"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC", () {
+ const password = r"~!@#$%^&*() ~!@#$%^&*()PNBFRD";
+ const encoded =
+ r"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC";
+ var output = bcrypt(utf8.encode(password), encoded);
+ expect(output, equals(encoded));
+ });
+ }, skip: true);
+ });
+}