diff --git a/BENCHMARK.md b/BENCHMARK.md index 7509bf7..eb8f1a8 100644 --- a/BENCHMARK.md +++ b/BENCHMARK.md @@ -10,111 +10,120 @@ With 1MB message (10 iterations): | Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | | ----------------- | ------------- | ----------------------------- | ---------------------------- | -| XOR | **2.05 Gbps** | | | -| ChaCha20 | **1.22 Gbps** | 276 Mbps
`4.41x slow` | | -| ChaCha20/Poly1305 | **782 Mbps** | 201 Mbps
`3.88x slow` | 288 Mbps
`2.72x slow` | -| Salsa20 | **1.22 Gbps** | 247 Mbps
`4.95x slow` | | -| Salsa20/Poly1305 | **778 Mbps** | | | -| AES-128:keygen | **138 Tbps** | 23.06 Tbps
`5.97x slow` | 56.09 Tbps
`2.45x slow` | -| AES-192:keygen | **144 Tbps** | 19.97 Tbps
`7.21x slow` | 50.94 Tbps
`2.83x slow` | -| AES-256:keygen | **110 Tbps** | 16.62 Tbps
`6.63x slow` | 43.68 Tbps
`2.52x slow` | -| AES-128/ECB | **929 Mbps** | 172 Mbps
`5.41x slow` | | -| AES-192/ECB | **839 Mbps** | 148 Mbps
`5.66x slow` | | -| AES-256/ECB | **752 Mbps** | 133 Mbps
`5.65x slow` | | -| AES-128/CBC | **909 Mbps** | 160 Mbps
`5.67x slow` | 817 Mbps
`1.11x slow` | -| AES-192/CBC | **822 Mbps** | 140 Mbps
`5.85x slow` | 770 Mbps
`1.07x slow` | -| AES-256/CBC | **754 Mbps** | 126 Mbps
`5.97x slow` | 710 Mbps
`1.06x slow` | -| AES-128/CTR | **934 Mbps** | 158 Mbps
`5.91x slow` | 502 Mbps
`1.86x slow` | -| AES-192/CTR | **847 Mbps** | 138 Mbps
`6.14x slow` | 474 Mbps
`1.79x slow` | -| AES-256/CTR | **769 Mbps** | 123 Mbps
`6.24x slow` | 447 Mbps
`1.72x slow` | -| AES-128/GCM | **144 Mbps** | 12 Mbps
`12.02x slow` | 134 Mbps
`1.08x slow` | -| AES-192/GCM | **142 Mbps** | 11.91 Mbps
`11.91x slow` | 132 Mbps
`1.08x slow` | -| AES-256/GCM | **139 Mbps** | 11.75 Mbps
`11.86x slow` | 130 Mbps
`1.07x slow` | -| AES-128/CFB | **451 Mbps** | 781 kbps
`577.91x slow` | | -| AES-192/CFB | **412 Mbps** | 783 kbps
`526.15x slow` | | -| AES-256/CFB | **373 Mbps** | 780 kbps
`478.83x slow` | | -| AES-128/OFB | **807 Mbps** | 163 Mbps
`4.95x slow` | | -| AES-192/OFB | **739 Mbps** | 141 Mbps
`5.25x slow` | | -| AES-256/OFB | **683 Mbps** | 126 Mbps
`5.42x slow` | | -| AES-128/XTS | **676 Mbps** | | | -| AES-192/XTS | **630 Mbps** | | | -| AES-256/XTS | **584 Mbps** | | | -| AES-128/PCBC | **854 Mbps** | | | -| AES-192/PCBC | **775 Mbps** | | | -| AES-256/PCBC | **711 Mbps** | | | +| XOR | **5.74 Gbps** | +| ChaCha20 | **1.21 Gbps** | 264 Mbps
`4.6x slow` | | +| ChaCha20/Poly1305 | **765 Mbps** | 194 Mbps
`3.94x slow` | 289 Mbps
`2.65x slow` | +| Salsa20 | **1.23 Gbps** | 253 Mbps
`4.85x slow` | | +| Salsa20/Poly1305 | **771 Mbps** | | | +| AES-128:keygen | **131 Tbps** | 22.48 Tbps
`5.82x slow` | 52.37 Tbps
`2.5x slow` | +| AES-192:keygen | **136 Tbps** | 19.32 Tbps
`7.04x slow` | 47.34 Tbps
`2.87x slow` | +| AES-256:keygen | **101 Tbps** | 16.24 Tbps
`6.24x slow` | 40.49 Tbps
`2.5x slow` | +| AES-128/ECB | **944 Mbps** | 161 Mbps
`5.85x slow` | | +| AES-192/ECB | **853 Mbps** | 145 Mbps
`5.88x slow` | | +| AES-256/ECB | **776 Mbps** | 127 Mbps
`6.12x slow` | | +| AES-128/CBC | **964 Mbps** | 157 Mbps
`6.14x slow` | 859 Mbps
`1.12x slow` | +| AES-192/CBC | **872 Mbps** | 138 Mbps
`6.32x slow` | 783 Mbps
`1.11x slow` | +| AES-256/CBC | **793 Mbps** | 123 Mbps
`6.46x slow` | 712 Mbps
`1.11x slow` | +| AES-128/CTR | **944 Mbps** | 153 Mbps
`6.16x slow` | 497 Mbps
`1.9x slow` | +| AES-192/CTR | **858 Mbps** | 136 Mbps
`6.28x slow` | 473 Mbps
`1.81x slow` | +| AES-256/CTR | **781 Mbps** | 121 Mbps
`6.48x slow` | 449 Mbps
`1.74x slow` | +| AES-128/GCM | **143 Mbps** | 11.98 Mbps
`11.9x slow` | 129 Mbps
`1.1x slow` | +| AES-192/GCM | **141 Mbps** | 11.9 Mbps
`11.88x slow` | 129 Mbps
`1.09x slow` | +| AES-256/GCM | **139 Mbps** | 11.75 Mbps
`11.86x slow` | 126 Mbps
`1.11x slow` | +| AES-128/CFB | **453 Mbps** | 658 kbps
`688.39x slow` | | +| AES-192/CFB | **416 Mbps** | 661 kbps
`629.53x slow` | | +| AES-256/CFB | **378 Mbps** | 659 kbps
`573.25x slow` | | +| AES-128/OFB | **807 Mbps** | 155 Mbps
`5.19x slow` | | +| AES-192/OFB | **744 Mbps** | 139 Mbps
`5.34x slow` | | +| AES-256/OFB | **678 Mbps** | 124 Mbps
`5.47x slow` | | +| AES-128/XTS | **667 Mbps** | | | +| AES-192/XTS | **618 Mbps** | | | +| AES-256/XTS | **578 Mbps** | | | +| AES-128/IGE | **836 Mbps** | 150 Mbps
`5.56x slow` | | +| AES-192/IGE | **762 Mbps** | 131 Mbps
`5.8x slow` | | +| AES-256/IGE | **698 Mbps** | 117 Mbps
`5.96x slow` | | +| AES-128/PCBC | **835 Mbps** | | | +| AES-192/PCBC | **774 Mbps** | | | +| AES-256/PCBC | **700 Mbps** | | | With 5KB message (5000 iterations): | Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | | ----------------- | ------------- | ----------------------------- | -------------------------- | -| XOR | **2.15 Gbps** | | | -| ChaCha20 | **1.25 Gbps** | 278 Mbps
`4.5x slow` | | -| ChaCha20/Poly1305 | **783 Mbps** | 199 Mbps
`3.93x slow` | 281 Mbps
`2.78x slow` | -| Salsa20 | **1.25 Gbps** | 252 Mbps
`4.96x slow` | | -| Salsa20/Poly1305 | **784 Mbps** | | | -| AES-128:keygen | **712 Gbps** | 117 Gbps
`6.09x slow` | 282 Gbps
`2.53x slow` | -| AES-192:keygen | **748 Gbps** | 100 Gbps
`7.47x slow` | 255 Gbps
`2.94x slow` | -| AES-256:keygen | **564 Gbps** | 83.55 Gbps
`6.75x slow` | 219 Gbps
`2.58x slow` | -| AES-128/ECB | **938 Mbps** | 174 Mbps
`5.4x slow` | | -| AES-192/ECB | **850 Mbps** | 151 Mbps
`5.64x slow` | | -| AES-256/ECB | **772 Mbps** | 133 Mbps
`5.78x slow` | | -| AES-128/CBC | **933 Mbps** | 162 Mbps
`5.74x slow` | 860 Mbps
`1.08x slow` | -| AES-192/CBC | **840 Mbps** | 142 Mbps
`5.9x slow` | 778 Mbps
`1.08x slow` | -| AES-256/CBC | **762 Mbps** | 126 Mbps
`6.02x slow` | 709 Mbps
`1.07x slow` | -| AES-128/CTR | **957 Mbps** | 159 Mbps
`6.03x slow` | 506 Mbps
`1.89x slow` | -| AES-192/CTR | **863 Mbps** | 139 Mbps
`6.2x slow` | 479 Mbps
`1.8x slow` | -| AES-256/CTR | **781 Mbps** | 124 Mbps
`6.28x slow` | 453 Mbps
`1.72x slow` | -| AES-128/GCM | **146 Mbps** | 11.87 Mbps
`12.29x slow` | 138 Mbps
`1.06x slow` | -| AES-192/GCM | **144 Mbps** | 11.84 Mbps
`12.16x slow` | 135 Mbps
`1.07x slow` | -| AES-256/GCM | **141 Mbps** | 11.67 Mbps
`12.08x slow` | 132 Mbps
`1.07x slow` | -| AES-128/CFB | **458 Mbps** | 142 Mbps
`3.23x slow` | | -| AES-192/CFB | **415 Mbps** | 126 Mbps
`3.29x slow` | | -| AES-256/CFB | **380 Mbps** | 114 Mbps
`3.35x slow` | | -| AES-128/OFB | **824 Mbps** | 164 Mbps
`5.01x slow` | | -| AES-192/OFB | **748 Mbps** | 144 Mbps
`5.2x slow` | | -| AES-256/OFB | **690 Mbps** | 127 Mbps
`5.45x slow` | | -| AES-128/XTS | **698 Mbps** | | | -| AES-192/XTS | **644 Mbps** | | | -| AES-256/XTS | **601 Mbps** | | | -| AES-128/PCBC | **852 Mbps** | | | -| AES-192/PCBC | **777 Mbps** | | | -| AES-256/PCBC | **702 Mbps** | | | +| XOR | **6.74 Gbps** | +| ChaCha20 | **1.26 Gbps** | 277 Mbps
`4.55x slow` | | +| ChaCha20/Poly1305 | **765 Mbps** | 198 Mbps
`3.87x slow` | 280 Mbps
`2.73x slow` | +| Salsa20 | **1.25 Gbps** | 254 Mbps
`4.9x slow` | | +| Salsa20/Poly1305 | **761 Mbps** | | | +| AES-128:keygen | **690 Gbps** | 109 Gbps
`6.35x slow` | 268 Gbps
`2.57x slow` | +| AES-192:keygen | **708 Gbps** | 98.75 Gbps
`7.17x slow` | 242 Gbps
`2.92x slow` | +| AES-256:keygen | **540 Gbps** | 83.27 Gbps
`6.48x slow` | 208 Gbps
`2.59x slow` | +| AES-128/ECB | **953 Mbps** | 165 Mbps
`5.77x slow` | | +| AES-192/ECB | **866 Mbps** | 144 Mbps
`6.01x slow` | | +| AES-256/ECB | **790 Mbps** | 128 Mbps
`6.18x slow` | | +| AES-128/CBC | **973 Mbps** | 158 Mbps
`6.15x slow` | 854 Mbps
`1.14x slow` | +| AES-192/CBC | **879 Mbps** | 138 Mbps
`6.35x slow` | 774 Mbps
`1.14x slow` | +| AES-256/CBC | **798 Mbps** | 123 Mbps
`6.49x slow` | 708 Mbps
`1.13x slow` | +| AES-128/CTR | **950 Mbps** | 156 Mbps
`6.1x slow` | 503 Mbps
`1.89x slow` | +| AES-192/CTR | **866 Mbps** | 138 Mbps
`6.3x slow` | 475 Mbps
`1.82x slow` | +| AES-256/CTR | **780 Mbps** | 122 Mbps
`6.39x slow` | 450 Mbps
`1.73x slow` | +| AES-128/GCM | **145 Mbps** | 11.72 Mbps
`12.34x slow` | 131 Mbps
`1.11x slow` | +| AES-192/GCM | **144 Mbps** | 11.7 Mbps
`12.3x slow` | 128 Mbps
`1.12x slow` | +| AES-256/GCM | **141 Mbps** | 11.59 Mbps
`12.12x slow` | 128 Mbps
`1.1x slow` | +| AES-128/CFB | **453 Mbps** | 136 Mbps
`3.32x slow` | | +| AES-192/CFB | **419 Mbps** | 121 Mbps
`3.47x slow` | | +| AES-256/CFB | **381 Mbps** | 108 Mbps
`3.54x slow` | | +| AES-128/OFB | **760 Mbps** | 158 Mbps
`4.8x slow` | | +| AES-192/OFB | **747 Mbps** | 139 Mbps
`5.39x slow` | | +| AES-256/OFB | **689 Mbps** | 123 Mbps
`5.61x slow` | | +| AES-128/XTS | **688 Mbps** | | | +| AES-192/XTS | **634 Mbps** | | | +| AES-256/XTS | **595 Mbps** | | | +| AES-128/IGE | **843 Mbps** | 151 Mbps
`5.59x slow` | | +| AES-192/IGE | **770 Mbps** | 133 Mbps
`5.77x slow` | | +| AES-256/IGE | **710 Mbps** | 119 Mbps
`5.96x slow` | | +| AES-128/PCBC | **843 Mbps** | | | +| AES-192/PCBC | **778 Mbps** | | | +| AES-256/PCBC | **690 Mbps** | | | With 16B message (100000 iterations): -| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | -| ----------------- | ------------- | ---------------------------- | -------------------------------- | -| XOR | **1.8 Gbps** | | | -| ChaCha20 | **427 Mbps** | 51.3 Mbps
`8.33x slow` | | -| ChaCha20/Poly1305 | **110 Mbps** | 44.61 Mbps
`2.46x slow` | 35.42 Mbps
`3.1x slow` | -| Salsa20 | **410 Mbps** | 49.12 Mbps
`8.36x slow` | | -| Salsa20/Poly1305 | **106 Mbps** | | | -| AES-128:keygen | **2.17 Gbps** | 359 Mbps
`6.04x slow` | 850 Mbps
`2.55x slow` | -| AES-192:keygen | **2.28 Gbps** | 307 Mbps
`7.44x slow` | 782 Mbps
`2.92x slow` | -| AES-256:keygen | **1.75 Gbps** | 258 Mbps
`6.79x slow` | 675 Mbps
`2.59x slow` | -| AES-128/ECB | **335 Mbps** | 54.15 Mbps
`6.18x slow` | | -| AES-192/ECB | **322 Mbps** | 50.27 Mbps
`6.4x slow` | | -| AES-256/ECB | **279 Mbps** | 46.33 Mbps
`6.01x slow` | | -| AES-128/CBC | **301 Mbps** | 50.75 Mbps
`5.93x slow` | 149 Mbps
`2.03x slow` | -| AES-192/CBC | **289 Mbps** | 47.15 Mbps
`6.14x slow` | 139 Mbps
`2.08x slow` | -| AES-256/CBC | **259 Mbps** | 43.69 Mbps
`5.92x slow` | 130 Mbps
`1.99x slow` | -| AES-128/CTR | **498 Mbps** | 51.14 Mbps
`9.75x slow` | 81.76 Mbps
`6.1x slow` | -| AES-192/CTR | **485 Mbps** | 47.56 Mbps
`10.2x slow` | 79.22 Mbps
`6.12x slow` | -| AES-256/CTR | **427 Mbps** | 44.38 Mbps
`9.62x slow` | 76.79 Mbps
`5.56x slow` | -| AES-128/GCM | 27.28 Mbps | 6.53 Mbps
`4.18x slow` | **42.59 Mbps**
`1.56x fast` | -| AES-192/GCM | 27.29 Mbps | 6.42 Mbps
`4.25x slow` | **41.49 Mbps**
`1.52x fast` | -| AES-256/GCM | 26.57 Mbps | 6.29 Mbps
`4.23x slow` | **40.03 Mbps**
`1.51x fast` | -| AES-128/CFB | **314 Mbps** | 50.7 Mbps
`6.2x slow` | | -| AES-192/CFB | **292 Mbps** | 47.38 Mbps
`6.17x slow` | | -| AES-256/CFB | **259 Mbps** | 44.34 Mbps
`5.83x slow` | | -| AES-128/OFB | **446 Mbps** | 50.96 Mbps
`8.76x slow` | | -| AES-192/OFB | **428 Mbps** | 47.28 Mbps
`9.06x slow` | | -| AES-256/OFB | **382 Mbps** | 44.23 Mbps
`8.64x slow` | | -| AES-128/XTS | **238 Mbps** | | | -| AES-192/XTS | **227 Mbps** | | | -| AES-256/XTS | **201 Mbps** | | | -| AES-128/PCBC | **292 Mbps** | | | -| AES-192/PCBC | **293 Mbps** | | | -| AES-256/PCBC | **251 Mbps** | | | +| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | +| ----------------- | ------------- | ----------------------------- | -------------------------------- | +| XOR | **4.65 Gbps** | +| ChaCha20 | **420 Mbps** | 50.69 Mbps
`8.29x slow` | | +| ChaCha20/Poly1305 | **110 Mbps** | 41.08 Mbps
`2.68x slow` | 34.87 Mbps
`3.15x slow` | +| Salsa20 | **410 Mbps** | 48.25 Mbps
`8.51x slow` | | +| Salsa20/Poly1305 | **110 Mbps** | | | +| AES-128:keygen | **2.17 Gbps** | 360 Mbps
`6.03x slow` | 840 Mbps
`2.59x slow` | +| AES-192:keygen | **2.28 Gbps** | 310 Mbps
`7.36x slow` | 759 Mbps
`3.01x slow` | +| AES-256:keygen | **1.58 Gbps** | 259 Mbps
`6.11x slow` | 650 Mbps
`2.44x slow` | +| AES-128/ECB | **358 Mbps** | 54.07 Mbps
`6.61x slow` | | +| AES-192/ECB | **313 Mbps** | 49.44 Mbps
`6.33x slow` | | +| AES-256/ECB | **280 Mbps** | 45.83 Mbps
`6.12x slow` | | +| AES-128/CBC | **312 Mbps** | 50.29 Mbps
`6.2x slow` | 146 Mbps
`2.13x slow` | +| AES-192/CBC | **286 Mbps** | 47.28 Mbps
`6.04x slow` | 142 Mbps
`2.02x slow` | +| AES-256/CBC | **254 Mbps** | 44.26 Mbps
`5.74x slow` | 132 Mbps
`1.92x slow` | +| AES-128/CTR | **493 Mbps** | 50.47 Mbps
`9.78x slow` | 80.75 Mbps
`6.11x slow` | +| AES-192/CTR | **480 Mbps** | 46.99 Mbps
`10.22x slow` | 78.85 Mbps
`6.09x slow` | +| AES-256/CTR | **425 Mbps** | 43.79 Mbps
`9.7x slow` | 76 Mbps
`5.59x slow` | +| AES-128/GCM | 27.19 Mbps | 6.44 Mbps
`4.22x slow` | **41.33 Mbps**
`1.52x fast` | +| AES-192/GCM | 27.06 Mbps | 6.38 Mbps
`4.24x slow` | **40.41 Mbps**
`1.49x fast` | +| AES-256/GCM | 26.68 Mbps | 6.27 Mbps
`4.26x slow` | **39.05 Mbps**
`1.46x fast` | +| AES-128/CFB | **307 Mbps** | 50.4 Mbps
`6.1x slow` | | +| AES-192/CFB | **288 Mbps** | 46.91 Mbps
`6.13x slow` | | +| AES-256/CFB | **254 Mbps** | 43.66 Mbps
`5.81x slow` | | +| AES-128/OFB | **433 Mbps** | 51.04 Mbps
`8.48x slow` | | +| AES-192/OFB | **423 Mbps** | 47.07 Mbps
`8.99x slow` | | +| AES-256/OFB | **364 Mbps** | 44.54 Mbps
`8.18x slow` | | +| AES-128/XTS | **229 Mbps** | | | +| AES-192/XTS | **224 Mbps** | | | +| AES-256/XTS | **196 Mbps** | | | +| AES-128/IGE | **275 Mbps** | 49.15 Mbps
`5.6x slow` | | +| AES-192/IGE | **270 Mbps** | 45.42 Mbps
`5.94x slow` | | +| AES-256/IGE | **238 Mbps** | 42.69 Mbps
`5.58x slow` | | +| AES-128/PCBC | **303 Mbps** | | | +| AES-192/PCBC | **288 Mbps** | | | +| AES-256/PCBC | **248 Mbps** | | | > > 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 11d7ba6..bb34b8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ -## 0.0.10 +## 0.0.11 - `XOR` cipher. - `Salsa20` cipher with `Poly1305` tag. - `ChaCha20` cipher with `Poly1305` tag. -- `AES` in ECB, CBC, CTR, CFB, OFB, GCM, XTS, PCBC modes. +- `AES` in ECB, CBC, CTR, CFB, OFB, GCM, XTS, IGE, PCBC modes. diff --git a/README.md b/README.md index afeef5f..4fbb724 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ Available modes for AES: - `GCM` : Galois/Counter Mode - `CFB` : Cipher Feedback - `OFB` : Output Feedback +- `IGE` : Infinite Garble Extension - `PCBC` : Propagating Cipher Block Chaining - `XTS` : XEX (XOR-Encrypt-XOR) Tweakable Block Cipher with Ciphertext Stealing @@ -71,6 +72,7 @@ void main() { print(' CFB: ${toHex(AES(key).cfb(iv).encryptString(plain))}'); print(' OFB: ${toHex(AES(key).ofb(iv).encryptString(plain))}'); print(' XTS: ${toHex(AES(key).xts(iv).encryptString(plain))}'); + print(' IGE: ${toHex(AES(key).ige(iv).encryptString(plain))}'); print(' PCBC: ${toHex(AES(key).pcbc(iv).encryptString(plain))}'); } print(''); @@ -131,41 +133,125 @@ Libraries: - **PointyCastle** : https://pub.dev/packages/pointycastle - **Cryptography** : https://pub.dev/packages/cryptography -With 5MB message (10 iterations): - -| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | -| ------------------------- | -------------- | ---------------------------- | ---------------------------- | -| XOR | **241MB/s** | ➖ | ➖ | -| ChaCha20 | **107.60MB/s** | 30.48MB/s
`253% slower` | ➖ | -| ChaCha20/Poly1305 | **75.32MB/s** | ➖ | 33.24MB/s
`127% slower` | -| ChaCha20/Poly1305(digest) | **247.47MB/s** | ➖ | ➖ | -| Salsa20 | **107.24MB/s** | 27.91MB/s
`284% slower` | ➖ | -| Salsa20/Poly1305 | **76.42MB/s** | ➖ | ➖ | -| Salsa20/Poly1305(digest) | **248.50MB/s** | ➖ | ➖ | - -With 1KB message (5000 iterations): - -| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | -| ------------------------- | -------------- | ---------------------------- | ---------------------------- | -| XOR | **250.20MB/s** | ➖ | ➖ | -| ChaCha20 | **108.38MB/s** | 30.87MB/s
`251% slower` | ➖ | -| ChaCha20/Poly1305 | **71.48MB/s** | ➖ | 31.39MB/s
`128% slower` | -| ChaCha20/Poly1305(digest) | **213.58MB/s** | ➖ | ➖ | -| Salsa20 | **108.21MB/s** | 29.29MB/s
`269% slower` | ➖ | -| Salsa20/Poly1305 | **72.17MB/s** | ➖ | ➖ | -| Salsa20/Poly1305(digest) | **217.38MB/s** | ➖ | ➖ | - -With 10B message (100000 iterations): - -| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | -| ------------------------- | -------------- | --------------------------- | --------------------------- | -| XOR | **185.62MB/s** | ➖ | ➖ | -| ChaCha20 | **32.03MB/s** | 3.91MB/s
`719% slower` | ➖ | -| ChaCha20/Poly1305 | **9.71MB/s** | ➖ | 4.14MB/s
`134% slower` | -| ChaCha20/Poly1305(digest) | **14.31MB/s** | ➖ | ➖ | -| Salsa20 | **32.33MB/s** | 3.81MB/s
`748% slower` | ➖ | -| Salsa20/Poly1305 | **9.81MB/s** | ➖ | ➖ | -| Salsa20/Poly1305(digest) | **14.25MB/s** | ➖ | ➖ | +With 1MB message (10 iterations): + +| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | +| ----------------- | ------------- | ----------------------------- | ---------------------------- | +| XOR | **5.74 Gbps** | +| ChaCha20 | **1.21 Gbps** | 264 Mbps
`4.6x slow` | | +| ChaCha20/Poly1305 | **765 Mbps** | 194 Mbps
`3.94x slow` | 289 Mbps
`2.65x slow` | +| Salsa20 | **1.23 Gbps** | 253 Mbps
`4.85x slow` | | +| Salsa20/Poly1305 | **771 Mbps** | | | +| AES-128:keygen | **131 Tbps** | 22.48 Tbps
`5.82x slow` | 52.37 Tbps
`2.5x slow` | +| AES-192:keygen | **136 Tbps** | 19.32 Tbps
`7.04x slow` | 47.34 Tbps
`2.87x slow` | +| AES-256:keygen | **101 Tbps** | 16.24 Tbps
`6.24x slow` | 40.49 Tbps
`2.5x slow` | +| AES-128/ECB | **944 Mbps** | 161 Mbps
`5.85x slow` | | +| AES-192/ECB | **853 Mbps** | 145 Mbps
`5.88x slow` | | +| AES-256/ECB | **776 Mbps** | 127 Mbps
`6.12x slow` | | +| AES-128/CBC | **964 Mbps** | 157 Mbps
`6.14x slow` | 859 Mbps
`1.12x slow` | +| AES-192/CBC | **872 Mbps** | 138 Mbps
`6.32x slow` | 783 Mbps
`1.11x slow` | +| AES-256/CBC | **793 Mbps** | 123 Mbps
`6.46x slow` | 712 Mbps
`1.11x slow` | +| AES-128/CTR | **944 Mbps** | 153 Mbps
`6.16x slow` | 497 Mbps
`1.9x slow` | +| AES-192/CTR | **858 Mbps** | 136 Mbps
`6.28x slow` | 473 Mbps
`1.81x slow` | +| AES-256/CTR | **781 Mbps** | 121 Mbps
`6.48x slow` | 449 Mbps
`1.74x slow` | +| AES-128/GCM | **143 Mbps** | 11.98 Mbps
`11.9x slow` | 129 Mbps
`1.1x slow` | +| AES-192/GCM | **141 Mbps** | 11.9 Mbps
`11.88x slow` | 129 Mbps
`1.09x slow` | +| AES-256/GCM | **139 Mbps** | 11.75 Mbps
`11.86x slow` | 126 Mbps
`1.11x slow` | +| AES-128/CFB | **453 Mbps** | 658 kbps
`688.39x slow` | | +| AES-192/CFB | **416 Mbps** | 661 kbps
`629.53x slow` | | +| AES-256/CFB | **378 Mbps** | 659 kbps
`573.25x slow` | | +| AES-128/OFB | **807 Mbps** | 155 Mbps
`5.19x slow` | | +| AES-192/OFB | **744 Mbps** | 139 Mbps
`5.34x slow` | | +| AES-256/OFB | **678 Mbps** | 124 Mbps
`5.47x slow` | | +| AES-128/XTS | **667 Mbps** | | | +| AES-192/XTS | **618 Mbps** | | | +| AES-256/XTS | **578 Mbps** | | | +| AES-128/IGE | **836 Mbps** | 150 Mbps
`5.56x slow` | | +| AES-192/IGE | **762 Mbps** | 131 Mbps
`5.8x slow` | | +| AES-256/IGE | **698 Mbps** | 117 Mbps
`5.96x slow` | | +| AES-128/PCBC | **835 Mbps** | | | +| AES-192/PCBC | **774 Mbps** | | | +| AES-256/PCBC | **700 Mbps** | | | + +With 5KB message (5000 iterations): + +| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | +| ----------------- | ------------- | ----------------------------- | -------------------------- | +| XOR | **6.74 Gbps** | +| ChaCha20 | **1.26 Gbps** | 277 Mbps
`4.55x slow` | | +| ChaCha20/Poly1305 | **765 Mbps** | 198 Mbps
`3.87x slow` | 280 Mbps
`2.73x slow` | +| Salsa20 | **1.25 Gbps** | 254 Mbps
`4.9x slow` | | +| Salsa20/Poly1305 | **761 Mbps** | | | +| AES-128:keygen | **690 Gbps** | 109 Gbps
`6.35x slow` | 268 Gbps
`2.57x slow` | +| AES-192:keygen | **708 Gbps** | 98.75 Gbps
`7.17x slow` | 242 Gbps
`2.92x slow` | +| AES-256:keygen | **540 Gbps** | 83.27 Gbps
`6.48x slow` | 208 Gbps
`2.59x slow` | +| AES-128/ECB | **953 Mbps** | 165 Mbps
`5.77x slow` | | +| AES-192/ECB | **866 Mbps** | 144 Mbps
`6.01x slow` | | +| AES-256/ECB | **790 Mbps** | 128 Mbps
`6.18x slow` | | +| AES-128/CBC | **973 Mbps** | 158 Mbps
`6.15x slow` | 854 Mbps
`1.14x slow` | +| AES-192/CBC | **879 Mbps** | 138 Mbps
`6.35x slow` | 774 Mbps
`1.14x slow` | +| AES-256/CBC | **798 Mbps** | 123 Mbps
`6.49x slow` | 708 Mbps
`1.13x slow` | +| AES-128/CTR | **950 Mbps** | 156 Mbps
`6.1x slow` | 503 Mbps
`1.89x slow` | +| AES-192/CTR | **866 Mbps** | 138 Mbps
`6.3x slow` | 475 Mbps
`1.82x slow` | +| AES-256/CTR | **780 Mbps** | 122 Mbps
`6.39x slow` | 450 Mbps
`1.73x slow` | +| AES-128/GCM | **145 Mbps** | 11.72 Mbps
`12.34x slow` | 131 Mbps
`1.11x slow` | +| AES-192/GCM | **144 Mbps** | 11.7 Mbps
`12.3x slow` | 128 Mbps
`1.12x slow` | +| AES-256/GCM | **141 Mbps** | 11.59 Mbps
`12.12x slow` | 128 Mbps
`1.1x slow` | +| AES-128/CFB | **453 Mbps** | 136 Mbps
`3.32x slow` | | +| AES-192/CFB | **419 Mbps** | 121 Mbps
`3.47x slow` | | +| AES-256/CFB | **381 Mbps** | 108 Mbps
`3.54x slow` | | +| AES-128/OFB | **760 Mbps** | 158 Mbps
`4.8x slow` | | +| AES-192/OFB | **747 Mbps** | 139 Mbps
`5.39x slow` | | +| AES-256/OFB | **689 Mbps** | 123 Mbps
`5.61x slow` | | +| AES-128/XTS | **688 Mbps** | | | +| AES-192/XTS | **634 Mbps** | | | +| AES-256/XTS | **595 Mbps** | | | +| AES-128/IGE | **843 Mbps** | 151 Mbps
`5.59x slow` | | +| AES-192/IGE | **770 Mbps** | 133 Mbps
`5.77x slow` | | +| AES-256/IGE | **710 Mbps** | 119 Mbps
`5.96x slow` | | +| AES-128/PCBC | **843 Mbps** | | | +| AES-192/PCBC | **778 Mbps** | | | +| AES-256/PCBC | **690 Mbps** | | | + +With 16B message (100000 iterations): + +| Algorithms | `cipherlib` | `PointyCastle` | `cryptography` | +| ----------------- | ------------- | ----------------------------- | -------------------------------- | +| XOR | **4.65 Gbps** | +| ChaCha20 | **420 Mbps** | 50.69 Mbps
`8.29x slow` | | +| ChaCha20/Poly1305 | **110 Mbps** | 41.08 Mbps
`2.68x slow` | 34.87 Mbps
`3.15x slow` | +| Salsa20 | **410 Mbps** | 48.25 Mbps
`8.51x slow` | | +| Salsa20/Poly1305 | **110 Mbps** | | | +| AES-128:keygen | **2.17 Gbps** | 360 Mbps
`6.03x slow` | 840 Mbps
`2.59x slow` | +| AES-192:keygen | **2.28 Gbps** | 310 Mbps
`7.36x slow` | 759 Mbps
`3.01x slow` | +| AES-256:keygen | **1.58 Gbps** | 259 Mbps
`6.11x slow` | 650 Mbps
`2.44x slow` | +| AES-128/ECB | **358 Mbps** | 54.07 Mbps
`6.61x slow` | | +| AES-192/ECB | **313 Mbps** | 49.44 Mbps
`6.33x slow` | | +| AES-256/ECB | **280 Mbps** | 45.83 Mbps
`6.12x slow` | | +| AES-128/CBC | **312 Mbps** | 50.29 Mbps
`6.2x slow` | 146 Mbps
`2.13x slow` | +| AES-192/CBC | **286 Mbps** | 47.28 Mbps
`6.04x slow` | 142 Mbps
`2.02x slow` | +| AES-256/CBC | **254 Mbps** | 44.26 Mbps
`5.74x slow` | 132 Mbps
`1.92x slow` | +| AES-128/CTR | **493 Mbps** | 50.47 Mbps
`9.78x slow` | 80.75 Mbps
`6.11x slow` | +| AES-192/CTR | **480 Mbps** | 46.99 Mbps
`10.22x slow` | 78.85 Mbps
`6.09x slow` | +| AES-256/CTR | **425 Mbps** | 43.79 Mbps
`9.7x slow` | 76 Mbps
`5.59x slow` | +| AES-128/GCM | 27.19 Mbps | 6.44 Mbps
`4.22x slow` | **41.33 Mbps**
`1.52x fast` | +| AES-192/GCM | 27.06 Mbps | 6.38 Mbps
`4.24x slow` | **40.41 Mbps**
`1.49x fast` | +| AES-256/GCM | 26.68 Mbps | 6.27 Mbps
`4.26x slow` | **39.05 Mbps**
`1.46x fast` | +| AES-128/CFB | **307 Mbps** | 50.4 Mbps
`6.1x slow` | | +| AES-192/CFB | **288 Mbps** | 46.91 Mbps
`6.13x slow` | | +| AES-256/CFB | **254 Mbps** | 43.66 Mbps
`5.81x slow` | | +| AES-128/OFB | **433 Mbps** | 51.04 Mbps
`8.48x slow` | | +| AES-192/OFB | **423 Mbps** | 47.07 Mbps
`8.99x slow` | | +| AES-256/OFB | **364 Mbps** | 44.54 Mbps
`8.18x slow` | | +| AES-128/XTS | **229 Mbps** | | | +| AES-192/XTS | **224 Mbps** | | | +| AES-256/XTS | **196 Mbps** | | | +| AES-128/IGE | **275 Mbps** | 49.15 Mbps
`5.6x slow` | | +| AES-192/IGE | **270 Mbps** | 45.42 Mbps
`5.94x slow` | | +| AES-256/IGE | **238 Mbps** | 42.69 Mbps
`5.58x slow` | | +| AES-128/PCBC | **303 Mbps** | | | +| AES-192/PCBC | **288 Mbps** | | | +| AES-256/PCBC | **248 Mbps** | | | > All benchmarks are done on _AMD Ryzen 7 5800X_ processor and _3200MHz_ RAM using compiled _exe_ > diff --git a/benchmark/aes_ige.dart b/benchmark/aes_ige.dart new file mode 100644 index 0000000..75099a6 --- /dev/null +++ b/benchmark/aes_ige.dart @@ -0,0 +1,78 @@ +// Copyright (c) 2023, Sudipto Chandra +// All rights reserved. Check LICENSE file for details. + +import 'dart:math'; +import 'dart:typed_data'; + +import 'package:cipherlib/cipherlib.dart'; +import 'package:pointycastle/pointycastle.dart' as pc; + +import 'base.dart'; + +Random random = Random(); + +class CipherlibBenchmark extends Benchmark { + final Uint8List key; + final Uint8List iv; + + CipherlibBenchmark(int size, int iter, int keySize) + : key = Uint8List.fromList(List.filled(keySize, 0x9f)), + iv = Uint8List.fromList(List.filled(32, 0x87)), + super('cipherlib', size, iter); + + @override + void run() { + AES(key).ige(iv).encrypt(input); + } +} + +class PointyCastleBenchmark extends Benchmark { + final Uint8List key; + final Uint8List iv; + + PointyCastleBenchmark(int size, int iter, int keySize) + : key = Uint8List.fromList(List.filled(keySize, 0x9f)), + iv = Uint8List.fromList(List.filled(32, 0x87)), + super('PointyCastle', size, iter); + + @override + void run() { + var inp = Uint8List.fromList(input); + var out = Uint8List(inp.length); + var instance = pc.BlockCipher('AES/IGE'); + instance.init( + true, + pc.ParametersWithIV(pc.KeyParameter(key), iv), + ); + for (int i = 0; i < inp.length; i += 16) { + instance.processBlock(inp, i, out, i); + } + } +} + +void main() async { + print('--------- AES/IGE ----------'); + final conditions = [ + [5 << 20, 10], + [1 << 10, 5000], + [16, 100000], + ]; + for (var condition in conditions) { + int size = condition[0]; + int iter = condition[1]; + print('---- message: ${formatSize(size)} | iterations: $iter ----'); + print('[AES-128]'); + await CipherlibBenchmark(size, iter, 16).measureDiff([ + PointyCastleBenchmark(size, iter, 16), + ]); + print('[AES-192]'); + await CipherlibBenchmark(size, iter, 24).measureDiff([ + PointyCastleBenchmark(size, iter, 24), + ]); + print('[AES-256]'); + await CipherlibBenchmark(size, iter, 32).measureDiff([ + PointyCastleBenchmark(size, iter, 32), + ]); + print(''); + } +} diff --git a/benchmark/benchmark.dart b/benchmark/benchmark.dart index 6b51baa..64128d4 100644 --- a/benchmark/benchmark.dart +++ b/benchmark/benchmark.dart @@ -11,6 +11,7 @@ import 'aes_cfb.dart' as aes_cfb; import 'aes_ctr.dart' as aes_ctr; import 'aes_gcm.dart' as aes_gcm; import 'aes_ofb.dart' as aes_ofb; +import 'aes_ige.dart' as aes_ige; import 'aes_xts.dart' as aes_xts; import 'aes_keygen.dart' as aes_keygen; import 'base.dart'; @@ -166,6 +167,18 @@ Future measureSymmetricCiphers() async { "AES-256/XTS": [ aes_xts.CipherlibBenchmark(size, iter, 32), ], + "AES-128/IGE": [ + aes_ige.CipherlibBenchmark(size, iter, 16), + aes_ige.PointyCastleBenchmark(size, iter, 16), + ], + "AES-192/IGE": [ + aes_ige.CipherlibBenchmark(size, iter, 24), + aes_ige.PointyCastleBenchmark(size, iter, 24), + ], + "AES-256/IGE": [ + aes_ige.CipherlibBenchmark(size, iter, 32), + aes_ige.PointyCastleBenchmark(size, iter, 32), + ], "AES-128/PCBC": [ aes_pcbc.CipherlibBenchmark(size, iter, 16), ], diff --git a/benchmark/xor.dart b/benchmark/xor.dart index a50485f..5c17956 100644 --- a/benchmark/xor.dart +++ b/benchmark/xor.dart @@ -15,7 +15,7 @@ class CipherlibBenchmark extends Benchmark { final Uint8List key; CipherlibBenchmark(int size, int iter) - : key = Uint8List.fromList(List.filled(1000, 0x9f)), + : key = Uint8List.fromList(List.filled(100, 0x9f)), super('cipherlib', size, iter); @override @@ -28,7 +28,7 @@ class CipherlibStreamBenchmark extends AsyncBenchmark { final Uint8List key; CipherlibStreamBenchmark(int size, int iter) - : key = Uint8List.fromList(List.filled(1000, 0x9f)), + : key = Uint8List.fromList(List.filled(100, 0x9f)), super('cipherlib', size, iter); @override diff --git a/example/cipherlib_example.dart b/example/cipherlib_example.dart index 2a5cc6e..3b86166 100644 --- a/example/cipherlib_example.dart +++ b/example/cipherlib_example.dart @@ -17,6 +17,7 @@ void main() { print(' CFB: ${toHex(AES(key).cfb(iv).encryptString(plain))}'); print(' OFB: ${toHex(AES(key).ofb(iv).encryptString(plain))}'); print(' XTS: ${toHex(AES(key).xts(iv).encryptString(plain))}'); + print(' IGE: ${toHex(AES(key).ige(iv).encryptString(plain))}'); print(' PCBC: ${toHex(AES(key).pcbc(iv).encryptString(plain))}'); } print(''); diff --git a/lib/src/aes.dart b/lib/src/aes.dart index d6bec17..1cb68f9 100644 --- a/lib/src/aes.dart +++ b/lib/src/aes.dart @@ -6,6 +6,7 @@ import 'package:cipherlib/src/algorithms/aes/cfb.dart'; import 'package:cipherlib/src/algorithms/aes/ctr.dart'; import 'package:cipherlib/src/algorithms/aes/ecb.dart'; import 'package:cipherlib/src/algorithms/aes/gcm.dart'; +import 'package:cipherlib/src/algorithms/aes/ige.dart'; import 'package:cipherlib/src/algorithms/aes/ofb.dart'; import 'package:cipherlib/src/algorithms/aes/pcbc.dart'; import 'package:cipherlib/src/algorithms/aes/xts.dart'; @@ -16,6 +17,7 @@ export 'package:cipherlib/src/algorithms/aes/cfb.dart'; export 'package:cipherlib/src/algorithms/aes/ctr.dart'; export 'package:cipherlib/src/algorithms/aes/ecb.dart'; export 'package:cipherlib/src/algorithms/aes/gcm.dart'; +export 'package:cipherlib/src/algorithms/aes/ige.dart'; export 'package:cipherlib/src/algorithms/aes/ofb.dart'; export 'package:cipherlib/src/algorithms/aes/pcbc.dart'; export 'package:cipherlib/src/algorithms/aes/xts.dart'; @@ -67,20 +69,10 @@ class AES { /// Techniques][spec]. /// /// ``` - /// key - /// | - /// v - /// Plaintext ---> [block cipher] ---> Ciphertext - /// - /// key - /// | - /// v - /// Plaintext ---> [block cipher] ---> Ciphertext - /// - /// key - /// | - /// v - /// Plaintext ---> [block cipher] ---> Ciphertext + /// (key) + /// | + /// v + /// PT ---> [AES] ---> CT /// ``` /// /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final @@ -100,20 +92,15 @@ class AES { /// - [iv] (initialization vector) is the random 16-byte salt. /// /// ``` - /// IV Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext - /// ________________________________| - /// | Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext - /// ________________________________| - /// | Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext + /// IV (Key) + /// | | + /// v v + /// PT1 ---> (XOR) ---> [AES] ---> CT1 + /// ____________________| + /// | (Key) + /// | | + /// v v + /// PT2 ---> (XOR) ---> [AES] ---> CT2 /// ``` /// /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final @@ -139,20 +126,15 @@ class AES { /// Big-Endian order. /// /// ``` - /// Key Plaintext - /// | | - /// v v - /// -----> [block cipher] ---> (XOR) ---> Ciphertext - /// - /// Key Plaintext - /// | | - /// v v - /// ---> [block cipher] ---> (XOR) ---> Ciphertext - /// - /// Key Plaintext - /// | | - /// v v - /// ---> [block cipher] ---> (XOR) ---> Ciphertext + /// (Key) PT1 + /// | | + /// v v + /// -----> [AES] ---> (XOR) ---> CT1 + /// + /// (Key) PT2 + /// | | + /// v v + /// ---> [AES] ---> (XOR) ---> CT2 /// ``` /// /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final @@ -173,24 +155,17 @@ class AES { /// - [sbyte] number of bytes to take per block. (Default: 16) /// /// ``` - /// Key Plaintext (s-bit) - /// | | - /// v v - /// IV ---> [block cipher] -- [>>(16-s)] --> (XOR) ---> Ciphertext - /// | _____________________________| (s-bit) + /// Key PT1 (s-bit) + /// | | + /// v v + /// IV ---> [AES] -- [>>(16-s)] --> (XOR) ---> CT1 (s-bit) + /// | ______________________________| /// | | /// v v - /// [<< s] -> (XOR) Key Plaintext (s-bit) - /// ________| | | - /// | | v v - /// | -> [block cipher] --[>>(16-s)]--> (XOR) --> (s-bit) - /// | ___________________________________| - /// | | - /// v v - /// [<< s] -> (XOR) Key Plaintext (s-bit) - /// | | | - /// | v v - /// -> [block cipher] --[>>(16-s)]--> (XOR) --> (s-bit) + /// [<< s] -> (XOR) (Key) PT2 (s-bit) + /// | | | + /// | v v + /// ---> [AES] --[>>(16-s)]--> (XOR) --> CT2 (s-bit) /// ``` /// /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final @@ -226,20 +201,16 @@ class AES { /// - [iv] (initialization vector) is the random 16-byte salt. /// /// ``` - /// Key Plaintext - /// | | - /// v v - /// IV ---> [block cipher] ------> (XOR) ---> Ciphertext - /// _____________________| - /// | Key Plaintext - /// | | | - /// | v v - /// ---> [block cipher] ------> (XOR) ---> Ciphertext - /// _____________________| - /// | Key Plaintext - /// | | | - /// | v v - /// ---> [block cipher] ------> (XOR) ---> Ciphertext + /// (Key) PT1 + /// | | + /// v v + /// IV ---> [AES] ------> (XOR) ---> CT1 + /// ____________________| + /// | + /// | (Key) PT2 + /// | | | + /// | v v + /// ---> [AES] ------> (XOR) ---> CT2 /// ``` /// /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final @@ -271,22 +242,16 @@ class AES { /// - [iv] (initialization vector) is the random 16-byte salt. /// /// ``` - /// IV Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext - /// | _________________________________| - /// | | - /// --> (XOR) Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext - /// | _________________________________| - /// | | - /// --> (XOR) Key - /// | | - /// v v - /// Plaintext ---> (XOR) ---> [block cipher] ---> Ciphertext + /// IV (Key) + /// | | + /// v v + /// PT1 ---> (XOR) ---> [AES] ---> CT1 + /// | ____________________| + /// | | + /// +-----> (XOR) (Key) + /// | | + /// v v + /// PT2 ---> (XOR) ---> [AES] ---> CT2 /// ``` AESInPCBCMode pcbc(List iv) => AESInPCBCMode( key, @@ -347,4 +312,36 @@ class AES { /// /// [spec]: https://ieeexplore.ieee.org/document/8637988 AESInXTSMode xts(List tweak) => AESInXTSMode(key, tweak); + + /// The Infinite Garble Extension (IGE) mode is specifically designed to + /// provide error propagation, which is useful in certain cryptographic + /// applications. + /// + /// Error propagation is designed so that a single-bit error in the ciphertext + /// affects all subsequent blocks during decryption. It is possible due to the + /// usage of unique Initialization Vector (IV) for each pass. + /// + /// Parameters: + /// - [iv] (initialization vector) is the random 16-byte salt. + /// + /// ``` + /// +----------------------------------------------+ + /// | IV1 (Key) IV2 | + /// | | | | | + /// | v v V | + /// PT1 ---> (XOR) ---> [AES] ---> (XOR) ---> CT1 | + /// _______________________________| | + /// | | + /// | (Key) ______________| + /// | | | + /// v v v + /// PT2 ---> (XOR) ---> [AES] ---> (XOR) ---> CT2 + /// ``` + /// + /// [spec]: https://csrc.nist.gov/pubs/sp/800/38/a/final + AESInIGEMode ige(List iv) => AESInIGEMode( + key, + iv: iv, + padding: padding, + ); } diff --git a/lib/src/algorithms/aes/cbc.dart b/lib/src/algorithms/aes/cbc.dart index b37c43d..1fb6a2d 100644 --- a/lib/src/algorithms/aes/cbc.dart +++ b/lib/src/algorithms/aes/cbc.dart @@ -191,7 +191,9 @@ class AESInCBCModeDecryptSink extends CipherSink { if (_pos != 0 || _rpos != 0) { throw StateError('Invalid input size'); } - p -= _padding.getPadLength(output); + if (p > 0) { + p -= _padding.getPadLength(output, p); + } } if (n == p) { diff --git a/lib/src/algorithms/aes/ecb.dart b/lib/src/algorithms/aes/ecb.dart index 23e9035..7926ce8 100644 --- a/lib/src/algorithms/aes/ecb.dart +++ b/lib/src/algorithms/aes/ecb.dart @@ -168,7 +168,9 @@ class AESInECBModeDecryptSink extends CipherSink { if (_pos != 0 || _rpos != 0) { throw StateError('Invalid input size'); } - p -= _padding.getPadLength(output); + if (p > 0) { + p -= _padding.getPadLength(output, p); + } } if (n == p) { diff --git a/lib/src/algorithms/aes/ige.dart b/lib/src/algorithms/aes/ige.dart new file mode 100644 index 0000000..0bc742e --- /dev/null +++ b/lib/src/algorithms/aes/ige.dart @@ -0,0 +1,305 @@ +// Copyright (c) 2024, Sudipto Chandra +// All rights reserved. Check LICENSE file for details. + +import 'dart:typed_data'; + +import 'package:cipherlib/src/algorithms/padding.dart'; +import 'package:cipherlib/src/core/cipher_sink.dart'; +import 'package:cipherlib/src/core/salted_cipher.dart'; +import 'package:hashlib/hashlib.dart'; + +import '_core.dart'; + +/// The sink used for encryption by the [AESInIGEModeEncrypt] algorithm. +class AESInIGEModeEncryptSink extends CipherSink { + AESInIGEModeEncryptSink( + this._key, + this._iv, + this._padding, + ) { + reset(); + } + + int _pos = 0; + bool _closed = false; + final Uint8List _iv; + final Uint8List _key; + final Padding _padding; + final _block = Uint8List(16); // 128-bit + final _salt = Uint8List(32); + late final _key32 = Uint32List.view(_key.buffer); + late final _block32 = Uint32List.view(_block.buffer); + late final _xkey32 = AESCore.$expandEncryptionKey(_key32); + + @override + bool get closed => _closed; + + @override + void reset() { + _pos = 0; + _closed = false; + for (int i = 0; i < 16; ++i) { + _block[i] = _iv[i]; // iv1 + _salt[i] = 0; + _salt[i + 16] = 0; + } + for (int i = 16; i < _iv.length && i < 32; ++i) { + _salt[i - 16] = _iv[i]; // iv2 + } + } + + @override + Uint8List add( + List data, [ + int start = 0, + int? end, + bool last = false, + ]) { + if (_closed) { + throw StateError('The sink is closed'); + } + _closed = last; + end ??= data.length; + + int i, j, p, n; + + n = _pos + end - start; + if (last) { + n += 16 - (n & 15); + } + var output = Uint8List(n); + + p = 0; + for (i = start; i < end; ++i) { + _salt[_pos + 16] = data[i]; + _block[_pos++] ^= data[i]; + if (_pos == 16) { + AESCore.$encryptLE(_block32, _xkey32); + for (j = 0; j < 16; ++j) { + _block[j] ^= _salt[j]; + _salt[j] = _salt[j + 16]; + output[p++] = _block[j]; + } + _pos = 0; + } + } + + if (last) { + if (_padding.pad(_salt, _pos + 16)) { + for (; _pos < 16; _pos++) { + _block[_pos] ^= _salt[_pos + 16]; + } + AESCore.$encryptLE(_block32, _xkey32); + for (j = 0; j < 16; ++j) { + output[p++] = _block[j] ^ _salt[j]; + } + _pos = 0; + } + if (_pos != 0) { + throw StateError('Invalid input size'); + } + } + + if (n == p) { + return output; + } else if (p == 0) { + return Uint8List(0); + } else { + return output.sublist(0, p); + } + } +} + +/// The sink used for decryption by the [AESInIGEModeDecrypt] algorithm. +class AESInIGEModeDecryptSink extends CipherSink { + AESInIGEModeDecryptSink( + this._key, + this._iv, + this._padding, + ) { + reset(); + } + + int _pos = 0; + int _rpos = 0; + bool _closed = false; + final Uint8List _key; + final Uint8List _iv; + final Padding _padding; + late final Uint32List _key32 = Uint32List.view(_key.buffer); + final _block = Uint8List(16); // 128-bit + final _salt = Uint8List(32); + final _residue = Uint8List(16); + late final _block32 = Uint32List.view(_block.buffer); + late final _xkey32 = AESCore.$expandDecryptionKey(_key32); + + @override + bool get closed => _closed; + + @override + void reset() { + _pos = 0; + _rpos = 0; + _closed = false; + for (int i = 0; i < 16; ++i) { + _block[i] = 0; + _salt[i] = _iv[i]; // iv1 + _salt[i + 16] = 0; + } + for (int i = 16; i < _iv.length && i < 32; ++i) { + _block[i - 16] = _iv[i]; //iv2 + } + } + + @override + Uint8List add( + List data, [ + int start = 0, + int? end, + bool last = false, + ]) { + if (_closed) { + throw StateError('The sink is closed'); + } + _closed = last; + end ??= data.length; + + int i, j, k, p, n; + + n = _rpos + end - start; + var output = Uint8List(n); + + p = 0; + for (i = start; i < end; ++i) { + _salt[_pos + 16] = data[i]; + _block[_pos++] ^= data[i]; + if (_pos == 16) { + AESCore.$decryptLE(_block32, _xkey32); + for (j = 0; j < 16; ++j) { + if (_rpos == 16) { + for (k = 0; k < 16; ++k) { + output[p++] = _residue[k]; + } + _rpos = 0; + } + _block[j] ^= _salt[j]; + _salt[j] = _salt[j + 16]; + _residue[_rpos++] = _block[j]; + } + _pos = 0; + } + } + + if (last) { + if (_rpos == 16) { + for (k = 0; k < 16; ++k) { + output[p++] = _residue[k]; + } + _rpos = 0; + } + if (_pos != 0 || _rpos != 0) { + throw StateError('Invalid input size'); + } + if (p > 0) { + p -= _padding.getPadLength(output, p); + } + } + + if (n == p) { + return output; + } else if (p == 0) { + return Uint8List(0); + } else { + return output.sublist(0, p); + } + } +} + +/// Provides encryption for AES cipher in IGE mode. +class AESInIGEModeEncrypt extends SaltedCipher { + @override + String get name => "AES#encrypt/IGE/${padding.name}"; + + /// Key for the cipher + final Uint8List key; + + /// Padding scheme for the input message + final Padding padding; + + const AESInIGEModeEncrypt( + this.key, + Uint8List iv, [ + this.padding = Padding.pkcs7, + ]) : super(iv); + + @override + @pragma('vm:prefer-inline') + AESInIGEModeEncryptSink createSink() => + AESInIGEModeEncryptSink(key, iv, padding); +} + +/// Provides decryption for AES cipher in IGE mode. +class AESInIGEModeDecrypt extends SaltedCipher { + @override + String get name => "AES#decrypt/IGE/${padding.name}"; + + /// Key for the cipher + final Uint8List key; + + /// Padding scheme for the output message + final Padding padding; + + const AESInIGEModeDecrypt( + this.key, + Uint8List iv, [ + this.padding = Padding.pkcs7, + ]) : super(iv); + + @override + @pragma('vm:prefer-inline') + AESInIGEModeDecryptSink createSink() => + AESInIGEModeDecryptSink(key, iv, padding); +} + +/// Provides encryption and decryption for AES cipher in IGE mode. +class AESInIGEMode extends SaltedCollateCipher { + @override + String get name => "AES/IGE/${padding.name}"; + + @override + final AESInIGEModeEncrypt encryptor; + + @override + final AESInIGEModeDecrypt decryptor; + + const AESInIGEMode._({ + required this.encryptor, + required this.decryptor, + }); + + /// Creates AES cipher in IGE mode. + /// + /// Parameters: + /// - [key] The key for encryption and decryption + /// - [iv] 128-bit random initialization vector or salt + /// - [padding] The padding scheme for the messages + factory AESInIGEMode( + List key, { + List? iv, + Padding padding = Padding.pkcs7, + }) { + iv ??= randomBytes(32); + if (iv.length < 16) { + throw StateError('IV must be at least 16-bytes'); + } + var iv8 = iv is Uint8List ? iv : Uint8List.fromList(iv); + var key8 = key is Uint8List ? key : Uint8List.fromList(key); + return AESInIGEMode._( + encryptor: AESInIGEModeEncrypt(key8, iv8, padding), + decryptor: AESInIGEModeDecrypt(key8, iv8, padding), + ); + } + + /// Padding scheme for the messages + Padding get padding => encryptor.padding; +} diff --git a/lib/src/algorithms/aes/pcbc.dart b/lib/src/algorithms/aes/pcbc.dart index 11d4669..0d191fe 100644 --- a/lib/src/algorithms/aes/pcbc.dart +++ b/lib/src/algorithms/aes/pcbc.dart @@ -189,7 +189,9 @@ class AESInPCBCModeDecryptSink extends CipherSink { if (_pos != 0 || _rpos != 0) { throw StateError('Invalid input size'); } - p -= _padding.getPadLength(output); + if (p > 0) { + p -= _padding.getPadLength(output, p); + } } if (n == p) { diff --git a/lib/src/algorithms/xor.dart b/lib/src/algorithms/xor.dart index 81bdb3b..b1f5040 100644 --- a/lib/src/algorithms/xor.dart +++ b/lib/src/algorithms/xor.dart @@ -45,7 +45,7 @@ class XORSink extends CipherSink { if (_pos == _key.length) { _pos = 0; } - result[i] = data[i] ^ _key[_pos]; + result[i] = data[i] ^ _key[_pos++]; } return result; } @@ -76,16 +76,4 @@ class XOR extends Cipher { @override @pragma('vm:prefer-inline') XORSink createSink() => XORSink(key); - - @override - Uint8List convert(List message) { - var result = Uint8List.fromList(message); - for (int i = 0, j = 0; i < message.length; ++i) { - result[i] ^= key[j++]; - if (j == key.length) { - j = 0; - } - } - return result; - } } diff --git a/pubspec.yaml b/pubspec.yaml index 0020ef6..b7a206c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,14 @@ name: cipherlib description: Implementations of cryptographic algorithms for encryption and decryption in Dart. -version: 0.0.10 +version: 0.0.11 repository: https://github.com/bitanon/cipherlib environment: sdk: '>=2.14.0 <4.0.0' dependencies: - hashlib: ^1.19.1 - hashlib_codecs: ^2.2.0 + hashlib: ^1.19.2 + hashlib_codecs: ^2.5.0 dev_dependencies: lints: any diff --git a/test/aes_cbc_test.dart b/test/aes_cbc_test.dart index 9c55365..1fab99e 100644 --- a/test/aes_cbc_test.dart +++ b/test/aes_cbc_test.dart @@ -95,11 +95,14 @@ void main() { var aes = AES.noPadding(Uint8List(16)).cbc(Uint8List(16)); expect(() => aes.encrypt(Uint8List(10)), throwsStateError); expect(() => aes.decrypt(Uint8List(10)), throwsStateError); + expect(() => aes.encrypt(Uint8List(17)), throwsStateError); + expect(() => aes.decrypt(Uint8List(17)), throwsStateError); }); test('throws error on invalid salt size', () { var aes = AES(Uint8List(16)); expect(() => aes.cbc(Uint8List(15)).encrypt([0]), throwsStateError); expect(() => aes.cbc(Uint8List(8)).decrypt([0]), throwsStateError); + expect(aes.cbc(Uint8List(16)).encrypt([]).length, 16); }); group('empty message', () { diff --git a/test/aes_ecb_test.dart b/test/aes_ecb_test.dart index 2fbabf0..4baf18f 100644 --- a/test/aes_ecb_test.dart +++ b/test/aes_ecb_test.dart @@ -107,6 +107,8 @@ void main() { var aes = AES.noPadding(Uint8List(16)).ecb(); expect(() => aes.encrypt(Uint8List(10)), throwsStateError); expect(() => aes.decrypt(Uint8List(10)), throwsStateError); + expect(() => aes.encrypt(Uint8List(17)), throwsStateError); + expect(() => aes.decrypt(Uint8List(17)), throwsStateError); }); group('empty message', () { diff --git a/test/aes_ige_test.dart b/test/aes_ige_test.dart new file mode 100644 index 0000000..c37fdbe --- /dev/null +++ b/test/aes_ige_test.dart @@ -0,0 +1,145 @@ +// Copyright (c) 2024, Sudipto Chandra +// All rights reserved. Check LICENSE file for details. + +import 'dart:typed_data'; + +import 'package:cipherlib/cipherlib.dart'; +import 'package:hashlib/hashlib.dart'; +import 'package:hashlib_codecs/hashlib_codecs.dart'; +import 'package:test/test.dart'; + +void main() { + test('throws error on invalid input size', () { + var aes = AES.noPadding(Uint8List(16)).ige(Uint8List(32)); + expect(() => aes.encrypt(Uint8List(10)), throwsStateError); + expect(() => aes.decrypt(Uint8List(10)), throwsStateError); + expect(() => aes.encrypt(Uint8List(17)), throwsStateError); + expect(() => aes.decrypt(Uint8List(17)), throwsStateError); + }); + test('throws error on invalid salt size', () { + var aes = AES(Uint8List(16)); + expect(() => aes.ige(Uint8List(0)).decrypt([0]), throwsStateError); + expect(() => aes.ige(Uint8List(15)).encrypt([0]), throwsStateError); + expect(aes.ige(Uint8List(16)).encrypt([]).length, 16); + }); + + group('empty message', () { + var aes = AES.noPadding(Uint8List(32)).ige(Uint8List(32)); + test('encrypt', () { + var actual = aes.encrypt([]); + expect(toHex(actual), equals(toHex([]))); + }); + test('decrypt', () { + var reverse = aes.decrypt([]); + expect(toHex(reverse), equals(toHex([]))); + }); + }); + + group('encryption <-> decryption', () { + test("128-bit", () { + var key = randomBytes(16); + for (int j = 0; j < 100; j++) { + var inp = randomBytes(j); + var iv = randomBytes(32); + var cipher = AES(key).ige(iv).encrypt(inp); + var plain = AES(key).ige(iv).decrypt(cipher); + expect(toHex(plain), equals(toHex(inp)), reason: '[size: $j]'); + } + }); + test("192-bit", () { + var key = randomBytes(24); + for (int j = 0; j < 100; j++) { + var inp = randomBytes(j); + var iv = randomBytes(32); + var cipher = AES(key).ige(iv).encrypt(inp); + var plain = AES(key).ige(iv).decrypt(cipher); + expect(toHex(plain), equals(toHex(inp)), reason: '[size: $j]'); + } + }); + test("256-bit", () { + var key = randomBytes(32); + for (int j = 0; j < 100; j++) { + var inp = randomBytes(j); + var iv = randomBytes(32); + var cipher = AES(key).ige(iv).encrypt(inp); + var plain = AES(key).ige(iv).decrypt(cipher); + expect(toHex(plain), equals(toHex(inp)), reason: '[size: $j]'); + } + }); + }); + + group('sink test', () { + test('encryption', () { + var key = randomBytes(32); + for (int j = 0; j < 100; j++) { + var iv = randomBytes(32); + final aes = AES(key).ige(iv); + + var input = randomBytes(j); + var cipher = aes.encrypt(input); + + var enc = aes.encryptor.createSink(); + var output = []; + for (int i = 0; i < input.length; i += 13) { + output.addAll(enc.add(input.skip(i).take(13).toList())); + } + output.addAll(enc.close()); + expect(toHex(output), equals(toHex(cipher)), reason: '[size: $j]'); + + var plain = aes.decrypt(output); + expect(toHex(plain), equals(toHex(input)), reason: '[size: $j]'); + } + }); + test('decryption', () { + var key = randomBytes(32); + for (int j = 0; j < 100; j++) { + var iv = randomBytes(32); + final aes = AES(key).ige(iv); + + var input = randomBytes(j); + var cipher = aes.encrypt(input); + + var dec = aes.decryptor.createSink(); + var output = []; + for (int i = 0; i < cipher.length; i += 23) { + output.addAll(dec.add(cipher.skip(i).take(23).toList())); + } + output.addAll(dec.close()); + expect(toHex(output), equals(toHex(input)), reason: '[size: $j]'); + } + }); + test('encryption + decryption', () { + var key = randomBytes(32); + for (int j = 0; j < 100; j++) { + var iv = randomBytes(32); + var input = randomBytes(j); + + final aes = AES(key).ige(iv); + var enc = aes.encryptor.createSink(); + var dec = aes.decryptor.createSink(); + + var output = []; + for (int i = 0; i < input.length; i += 23) { + var part = input.skip(i).take(23).toList(); + output.addAll(dec.add(enc.add(part))); + } + output.addAll(dec.add(enc.close())); + output.addAll(dec.close()); + expect(toHex(output), equals(toHex(input)), reason: '[size: $j]'); + } + }); + }); + + test('reset iv', () { + var iv = randomBytes(32); + var key = randomBytes(24); + var aes = AES(key).ige(iv); + for (int j = 0; j < 100; j++) { + aes.resetIV(); + var inp = randomBytes(j); + var cipher = aes.encrypt(inp); + var plain = aes.decrypt(cipher); + expect(toHex(plain), equals(toHex(inp)), reason: '[size: $j]'); + } + }); +} diff --git a/test/aes_pcbc_test.dart b/test/aes_pcbc_test.dart index 98cdb6b..070f863 100644 --- a/test/aes_pcbc_test.dart +++ b/test/aes_pcbc_test.dart @@ -13,11 +13,14 @@ void main() { var aes = AES.noPadding(Uint8List(16)).pcbc(Uint8List(16)); expect(() => aes.encrypt(Uint8List(10)), throwsStateError); expect(() => aes.decrypt(Uint8List(10)), throwsStateError); + expect(() => aes.encrypt(Uint8List(17)), throwsStateError); + expect(() => aes.decrypt(Uint8List(17)), throwsStateError); }); test('throws error on invalid salt size', () { var aes = AES(Uint8List(16)); expect(() => aes.pcbc(Uint8List(15)).encrypt([0]), throwsStateError); expect(() => aes.pcbc(Uint8List(8)).decrypt([0]), throwsStateError); + expect(aes.pcbc(Uint8List(16)).encrypt([]).length, 16); }); group('encryption <-> decryption', () { diff --git a/test/compare_test.dart b/test/compare_test.dart index b5f72fb..7a4a5f9 100644 --- a/test/compare_test.dart +++ b/test/compare_test.dart @@ -185,6 +185,54 @@ void main() { }); }); + group('AES/IGE', () { + test('pointycastle: encryption with 128-bit key', () { + var key = randomBytes(16); + for (int j = 16; j < 300; j += 16) { + var text = randomBytes(j); + var iv = randomBytes(32); + var result = my.AES.noPadding(key).ige(iv).encrypt(text); + var instance = pc.BlockCipher('AES/IGE'); + instance.init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv)); + var out = Uint8List(j); + for (int i = 0; i < j; i += 16) { + instance.processBlock(text, i, out, i); + } + expect(toHex(out), equals(toHex(result)), reason: '[size: $j]'); + } + }); + test('pointycastle: encryption with 192-bit key', () { + var key = randomBytes(24); + for (int j = 16; j < 300; j += 16) { + var text = randomBytes(j); + var iv = randomBytes(32); + var result = my.AES.noPadding(key).ige(iv).encrypt(text); + var instance = pc.BlockCipher('AES/IGE'); + instance.init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv)); + var out = Uint8List(j); + for (int i = 0; i < j; i += 16) { + instance.processBlock(text, i, out, i); + } + expect(toHex(out), equals(toHex(result)), reason: '[size: $j]'); + } + }); + test('pointycastle: encryption with 256-bit key', () { + var key = randomBytes(32); + for (int j = 16; j < 300; j += 16) { + var text = randomBytes(j); + var iv = randomBytes(32); + var result = my.AES.noPadding(key).ige(iv).encrypt(text); + var instance = pc.BlockCipher('AES/IGE'); + instance.init(true, pc.ParametersWithIV(pc.KeyParameter(key), iv)); + var out = Uint8List(j); + for (int i = 0; i < j; i += 16) { + instance.processBlock(text, i, out, i); + } + expect(toHex(out), equals(toHex(result)), reason: '[size: $j]'); + } + }); + }); + group('AES/CFB-64', () { test('pointycastle: encryption with 128-bit key', () { var key = randomBytes(16); diff --git a/test/xor_test.dart b/test/xor_test.dart index a402619..601d086 100644 --- a/test/xor_test.dart +++ b/test/xor_test.dart @@ -4,13 +4,14 @@ import 'dart:typed_data'; import 'package:cipherlib/cipherlib.dart'; +import 'package:hashlib_codecs/hashlib_codecs.dart'; import 'package:test/test.dart'; import 'utils.dart'; void main() { test('empty key with empty message', () { - expect(xor([], []), equals([])); + expect(() => xor([], []), throwsArgumentError); }); test('empty key with some message', () { expect(() => xor([1], []), throwsArgumentError); @@ -18,6 +19,15 @@ void main() { test('empty message', () { expect(xor([], [1]), equals([])); }); + test('known message', () { + var key = 'key'.codeUnits; + var plain = 'plaintext'.codeUnits; + var cipher = fromHex('1b0918020b0d0e1d0d'); + var out = xor(plain, key); + expect(toHex(out), equals(toHex(cipher))); + var rev = xor(cipher, key); + expect(toHex(rev), equals(toHex(plain))); + }); test('encryption <-> decryption (convert)', () { for (int i = 1; i < 100; i += 10) { var key = randomNumbers(i);