├── .cargo └── config.toml ├── .github └── workflows │ └── build.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── bin └── file-encryptor.rs ├── command ├── keygen.rs ├── mod.rs ├── open.rs └── seal.rs ├── crypto ├── block.rs ├── cipher.rs ├── mod.rs └── pkcs7.rs ├── error.rs ├── ioutils.rs └── lib.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | seal = "run --bin file-encryptor -- seal -k test-output-key -i test-input.txt -o test-output.ciphertext" 3 | open = "run --bin file-encryptor -- open -k test-output-key -i test-output.ciphertext -o test-output.plaintext" 4 | lint = "clippy --all-features --all-targets" 5 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | on: [push] 2 | 3 | name: Lint, Test, Build 4 | 5 | # Make sure CI fails on all warnings, including Clippy lints 6 | env: 7 | RUSTFLAGS: "-Dwarnings" 8 | 9 | jobs: 10 | build: 11 | name: Build binary in release 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | toolchain: nightly 18 | - name: Lint 19 | run: cargo clippy --all-targets --all-features 20 | - name: Test 21 | run: cargo test --release 22 | - name: Build 23 | run: cargo build --release --all-features 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /test-input* 3 | /test-output* 4 | 5 | 6 | # Added by cargo 7 | # 8 | # already existing elements were commented out 9 | 10 | #/target 11 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aead" 7 | version = "0.5.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" 10 | dependencies = [ 11 | "crypto-common", 12 | "generic-array", 13 | ] 14 | 15 | [[package]] 16 | name = "aes" 17 | version = "0.8.4" 18 | source = "registry+https://github.com/rust-lang/crates.io-index" 19 | checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" 20 | dependencies = [ 21 | "cfg-if", 22 | "cipher", 23 | "cpufeatures", 24 | ] 25 | 26 | [[package]] 27 | name = "aes-gcm" 28 | version = "0.10.3" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" 31 | dependencies = [ 32 | "aead", 33 | "aes", 34 | "cipher", 35 | "ctr", 36 | "ghash", 37 | "subtle", 38 | ] 39 | 40 | [[package]] 41 | name = "anstream" 42 | version = "0.6.13" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | checksum = "d96bd03f33fe50a863e394ee9718a706f988b9079b20c3784fb726e7678b62fb" 45 | dependencies = [ 46 | "anstyle", 47 | "anstyle-parse", 48 | "anstyle-query", 49 | "anstyle-wincon", 50 | "colorchoice", 51 | "utf8parse", 52 | ] 53 | 54 | [[package]] 55 | name = "anstyle" 56 | version = "1.0.6" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" 59 | 60 | [[package]] 61 | name = "anstyle-parse" 62 | version = "0.2.3" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" 65 | dependencies = [ 66 | "utf8parse", 67 | ] 68 | 69 | [[package]] 70 | name = "anstyle-query" 71 | version = "1.0.2" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" 74 | dependencies = [ 75 | "windows-sys", 76 | ] 77 | 78 | [[package]] 79 | name = "anstyle-wincon" 80 | version = "3.0.2" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7" 83 | dependencies = [ 84 | "anstyle", 85 | "windows-sys", 86 | ] 87 | 88 | [[package]] 89 | name = "anyhow" 90 | version = "1.0.86" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 93 | 94 | [[package]] 95 | name = "base64ct" 96 | version = "1.6.0" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 99 | 100 | [[package]] 101 | name = "block-buffer" 102 | version = "0.10.4" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 105 | dependencies = [ 106 | "generic-array", 107 | ] 108 | 109 | [[package]] 110 | name = "byteorder" 111 | version = "1.5.0" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 114 | 115 | [[package]] 116 | name = "cfg-if" 117 | version = "1.0.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 120 | 121 | [[package]] 122 | name = "cipher" 123 | version = "0.4.4" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 126 | dependencies = [ 127 | "crypto-common", 128 | "inout", 129 | ] 130 | 131 | [[package]] 132 | name = "clap" 133 | version = "4.5.4" 134 | source = "registry+https://github.com/rust-lang/crates.io-index" 135 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 136 | dependencies = [ 137 | "clap_builder", 138 | "clap_derive", 139 | ] 140 | 141 | [[package]] 142 | name = "clap_builder" 143 | version = "4.5.2" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 146 | dependencies = [ 147 | "anstream", 148 | "anstyle", 149 | "clap_lex", 150 | "strsim", 151 | ] 152 | 153 | [[package]] 154 | name = "clap_derive" 155 | version = "4.5.4" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" 158 | dependencies = [ 159 | "heck", 160 | "proc-macro2", 161 | "quote", 162 | "syn", 163 | ] 164 | 165 | [[package]] 166 | name = "clap_lex" 167 | version = "0.7.0" 168 | source = "registry+https://github.com/rust-lang/crates.io-index" 169 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 170 | 171 | [[package]] 172 | name = "colorchoice" 173 | version = "1.0.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" 176 | 177 | [[package]] 178 | name = "cpufeatures" 179 | version = "0.2.12" 180 | source = "registry+https://github.com/rust-lang/crates.io-index" 181 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 182 | dependencies = [ 183 | "libc", 184 | ] 185 | 186 | [[package]] 187 | name = "crossbeam-deque" 188 | version = "0.8.5" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 191 | dependencies = [ 192 | "crossbeam-epoch", 193 | "crossbeam-utils", 194 | ] 195 | 196 | [[package]] 197 | name = "crossbeam-epoch" 198 | version = "0.9.18" 199 | source = "registry+https://github.com/rust-lang/crates.io-index" 200 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 201 | dependencies = [ 202 | "crossbeam-utils", 203 | ] 204 | 205 | [[package]] 206 | name = "crossbeam-utils" 207 | version = "0.8.20" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 210 | 211 | [[package]] 212 | name = "crypto-common" 213 | version = "0.1.6" 214 | source = "registry+https://github.com/rust-lang/crates.io-index" 215 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 216 | dependencies = [ 217 | "generic-array", 218 | "rand_core", 219 | "typenum", 220 | ] 221 | 222 | [[package]] 223 | name = "ctr" 224 | version = "0.9.2" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" 227 | dependencies = [ 228 | "cipher", 229 | ] 230 | 231 | [[package]] 232 | name = "digest" 233 | version = "0.10.7" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 236 | dependencies = [ 237 | "block-buffer", 238 | "crypto-common", 239 | "subtle", 240 | ] 241 | 242 | [[package]] 243 | name = "either" 244 | version = "1.13.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 247 | 248 | [[package]] 249 | name = "file-encryptor" 250 | version = "1.0.0" 251 | dependencies = [ 252 | "aes", 253 | "aes-gcm", 254 | "anyhow", 255 | "clap", 256 | "rand", 257 | "rayon", 258 | "scrypt", 259 | ] 260 | 261 | [[package]] 262 | name = "generic-array" 263 | version = "0.14.7" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 266 | dependencies = [ 267 | "typenum", 268 | "version_check", 269 | ] 270 | 271 | [[package]] 272 | name = "getrandom" 273 | version = "0.2.14" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" 276 | dependencies = [ 277 | "cfg-if", 278 | "libc", 279 | "wasi", 280 | ] 281 | 282 | [[package]] 283 | name = "ghash" 284 | version = "0.5.1" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1" 287 | dependencies = [ 288 | "opaque-debug", 289 | "polyval", 290 | ] 291 | 292 | [[package]] 293 | name = "heck" 294 | version = "0.5.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 297 | 298 | [[package]] 299 | name = "hmac" 300 | version = "0.12.1" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 303 | dependencies = [ 304 | "digest", 305 | ] 306 | 307 | [[package]] 308 | name = "inout" 309 | version = "0.1.3" 310 | source = "registry+https://github.com/rust-lang/crates.io-index" 311 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 312 | dependencies = [ 313 | "generic-array", 314 | ] 315 | 316 | [[package]] 317 | name = "libc" 318 | version = "0.2.153" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" 321 | 322 | [[package]] 323 | name = "opaque-debug" 324 | version = "0.3.1" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" 327 | 328 | [[package]] 329 | name = "password-hash" 330 | version = "0.5.0" 331 | source = "registry+https://github.com/rust-lang/crates.io-index" 332 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 333 | dependencies = [ 334 | "base64ct", 335 | "rand_core", 336 | "subtle", 337 | ] 338 | 339 | [[package]] 340 | name = "pbkdf2" 341 | version = "0.12.2" 342 | source = "registry+https://github.com/rust-lang/crates.io-index" 343 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 344 | dependencies = [ 345 | "digest", 346 | "hmac", 347 | ] 348 | 349 | [[package]] 350 | name = "polyval" 351 | version = "0.6.2" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" 354 | dependencies = [ 355 | "cfg-if", 356 | "cpufeatures", 357 | "opaque-debug", 358 | "universal-hash", 359 | ] 360 | 361 | [[package]] 362 | name = "ppv-lite86" 363 | version = "0.2.20" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 366 | dependencies = [ 367 | "zerocopy", 368 | ] 369 | 370 | [[package]] 371 | name = "proc-macro2" 372 | version = "1.0.81" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" 375 | dependencies = [ 376 | "unicode-ident", 377 | ] 378 | 379 | [[package]] 380 | name = "quote" 381 | version = "1.0.36" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 384 | dependencies = [ 385 | "proc-macro2", 386 | ] 387 | 388 | [[package]] 389 | name = "rand" 390 | version = "0.8.5" 391 | source = "registry+https://github.com/rust-lang/crates.io-index" 392 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 393 | dependencies = [ 394 | "libc", 395 | "rand_chacha", 396 | "rand_core", 397 | ] 398 | 399 | [[package]] 400 | name = "rand_chacha" 401 | version = "0.3.1" 402 | source = "registry+https://github.com/rust-lang/crates.io-index" 403 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 404 | dependencies = [ 405 | "ppv-lite86", 406 | "rand_core", 407 | ] 408 | 409 | [[package]] 410 | name = "rand_core" 411 | version = "0.6.4" 412 | source = "registry+https://github.com/rust-lang/crates.io-index" 413 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 414 | dependencies = [ 415 | "getrandom", 416 | ] 417 | 418 | [[package]] 419 | name = "rayon" 420 | version = "1.10.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 423 | dependencies = [ 424 | "either", 425 | "rayon-core", 426 | ] 427 | 428 | [[package]] 429 | name = "rayon-core" 430 | version = "1.12.1" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 433 | dependencies = [ 434 | "crossbeam-deque", 435 | "crossbeam-utils", 436 | ] 437 | 438 | [[package]] 439 | name = "salsa20" 440 | version = "0.10.2" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" 443 | dependencies = [ 444 | "cipher", 445 | ] 446 | 447 | [[package]] 448 | name = "scrypt" 449 | version = "0.11.0" 450 | source = "registry+https://github.com/rust-lang/crates.io-index" 451 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" 452 | dependencies = [ 453 | "password-hash", 454 | "pbkdf2", 455 | "salsa20", 456 | "sha2", 457 | ] 458 | 459 | [[package]] 460 | name = "sha2" 461 | version = "0.10.8" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 464 | dependencies = [ 465 | "cfg-if", 466 | "cpufeatures", 467 | "digest", 468 | ] 469 | 470 | [[package]] 471 | name = "strsim" 472 | version = "0.11.1" 473 | source = "registry+https://github.com/rust-lang/crates.io-index" 474 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 475 | 476 | [[package]] 477 | name = "subtle" 478 | version = "2.5.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 481 | 482 | [[package]] 483 | name = "syn" 484 | version = "2.0.60" 485 | source = "registry+https://github.com/rust-lang/crates.io-index" 486 | checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" 487 | dependencies = [ 488 | "proc-macro2", 489 | "quote", 490 | "unicode-ident", 491 | ] 492 | 493 | [[package]] 494 | name = "typenum" 495 | version = "1.17.0" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 498 | 499 | [[package]] 500 | name = "unicode-ident" 501 | version = "1.0.12" 502 | source = "registry+https://github.com/rust-lang/crates.io-index" 503 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 504 | 505 | [[package]] 506 | name = "universal-hash" 507 | version = "0.5.1" 508 | source = "registry+https://github.com/rust-lang/crates.io-index" 509 | checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" 510 | dependencies = [ 511 | "crypto-common", 512 | "subtle", 513 | ] 514 | 515 | [[package]] 516 | name = "utf8parse" 517 | version = "0.2.1" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" 520 | 521 | [[package]] 522 | name = "version_check" 523 | version = "0.9.4" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 526 | 527 | [[package]] 528 | name = "wasi" 529 | version = "0.11.0+wasi-snapshot-preview1" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 532 | 533 | [[package]] 534 | name = "windows-sys" 535 | version = "0.52.0" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 538 | dependencies = [ 539 | "windows-targets", 540 | ] 541 | 542 | [[package]] 543 | name = "windows-targets" 544 | version = "0.52.5" 545 | source = "registry+https://github.com/rust-lang/crates.io-index" 546 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 547 | dependencies = [ 548 | "windows_aarch64_gnullvm", 549 | "windows_aarch64_msvc", 550 | "windows_i686_gnu", 551 | "windows_i686_gnullvm", 552 | "windows_i686_msvc", 553 | "windows_x86_64_gnu", 554 | "windows_x86_64_gnullvm", 555 | "windows_x86_64_msvc", 556 | ] 557 | 558 | [[package]] 559 | name = "windows_aarch64_gnullvm" 560 | version = "0.52.5" 561 | source = "registry+https://github.com/rust-lang/crates.io-index" 562 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 563 | 564 | [[package]] 565 | name = "windows_aarch64_msvc" 566 | version = "0.52.5" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 569 | 570 | [[package]] 571 | name = "windows_i686_gnu" 572 | version = "0.52.5" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 575 | 576 | [[package]] 577 | name = "windows_i686_gnullvm" 578 | version = "0.52.5" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 581 | 582 | [[package]] 583 | name = "windows_i686_msvc" 584 | version = "0.52.5" 585 | source = "registry+https://github.com/rust-lang/crates.io-index" 586 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 587 | 588 | [[package]] 589 | name = "windows_x86_64_gnu" 590 | version = "0.52.5" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 593 | 594 | [[package]] 595 | name = "windows_x86_64_gnullvm" 596 | version = "0.52.5" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 599 | 600 | [[package]] 601 | name = "windows_x86_64_msvc" 602 | version = "0.52.5" 603 | source = "registry+https://github.com/rust-lang/crates.io-index" 604 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 605 | 606 | [[package]] 607 | name = "zerocopy" 608 | version = "0.7.35" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 611 | dependencies = [ 612 | "byteorder", 613 | "zerocopy-derive", 614 | ] 615 | 616 | [[package]] 617 | name = "zerocopy-derive" 618 | version = "0.7.35" 619 | source = "registry+https://github.com/rust-lang/crates.io-index" 620 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 621 | dependencies = [ 622 | "proc-macro2", 623 | "quote", 624 | "syn", 625 | ] 626 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "file-encryptor" 3 | version = "1.0.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | clap = { version = "4.5.4", features = ["derive"] } 10 | scrypt = { version = "0.11.0", default-features = false, features = ["std"] } 11 | aes = { version = "0.8.4" } 12 | rayon = { version = "1.10.0" } 13 | anyhow = "1.0.86" 14 | rand = { version = "0.8.5", features = ["default"] } 15 | 16 | # for testing 17 | aes-gcm = { version = "0.10.3", features = ["aes", "getrandom"] } 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # file-encryptor 2 | 3 | A small Rust program to stream and encrypt/decrypt files securely using **Advanced Encryption 4 | Standard-Galois/Counter Mode**, or [AES-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode), 5 | with a 256-bit key produced by 6 | [scrypt](https://en.wikipedia.org/wiki/Scrypt) key derivation function (KDF). 7 | 8 | ## Usage 9 | 10 | I wanted a small cli program that would _stream_ the file, and is flexible enough for 11 | scripting! 12 | 13 | Note that the use of additional authenticated data is not supported as of the current version. 14 | 15 | ### General Usage 16 | 17 | ```sh 18 | file-encryptor --help 19 | A Rust CLI program that streams files for encryption and decryption 20 | 21 | Usage: file-encryptor 22 | 23 | Commands: 24 | keygen generate a key, from pure random bytes, or from an input password 25 | open open an encrypted file 26 | seal seal a plaintext file 27 | help Print this message or the help of the given subcommand(s) 28 | 29 | Options: 30 | -h, --help Print help 31 | ``` 32 | 33 | ### Cookbook 34 | 35 | #### 1. Key generation 36 | 37 | To encrypt a file, first a key must be generated. For example, we are using the entire text of 38 | Frankenstein in a text file as the cryptographic key, though this may be done with different file 39 | types, since it's all 1's and 0's for computers anyway. 40 | 41 | ```sh 42 | file-encryptor keygen < frankenstein.txt > secret.key 43 | 44 | # or -i and -o 45 | file-encryptor keygen -i frankenstein.txt -o secret.key 46 | 47 | # You can also pass in the option `-p` (or `--password`) 48 | # to generate key from a certain passphrase. 49 | file-encryptor keygen -p "this is my password" > secret.key 50 | 51 | # and for all pseudoramdom bytes 52 | file-encryptor keygen -r > secret.key 53 | ``` 54 | 55 | #### 2. Encrypting the file `foo.plaintext` 56 | 57 | To seal (encrypt) a file, say `foo.plaintext`: 58 | 59 | ```sh 60 | # By default, it will read the first 32 bytes from stdin for as the key, 61 | # then the rest as plaintext. 62 | file-encryptor seal < secret.key < foo.plaintext > foo.ciphertext 63 | 64 | # Or passing in the file names from cli 65 | file-encryptor seal -k secret.key -i foo.plaintext -o foo.ciphertext 66 | ``` 67 | 68 | #### 3. Decrypting the ciphertext file `foo.ciphertext` 69 | 70 | To open the `foo.ciphertext` file: 71 | 72 | ```sh 73 | # By default, it will read the first 32 bytes from stdin for as the key, 74 | # then the rest as ciphertext. 75 | file-encryptor open < secret.key < foo.ciphertext > foo.plaintext.decrypted 76 | 77 | # Or passing in the file names from cli 78 | file-encryptor open -k secret.key -i foo.ciphertext -o foo.plaintext.decrypted 79 | ``` 80 | 81 | ## Breaking Changes 82 | 83 | The key generation schema is different, since `file-encryptor` now also streams the 84 | input key file. 85 | -------------------------------------------------------------------------------- /src/bin/file-encryptor.rs: -------------------------------------------------------------------------------- 1 | use std::process; 2 | 3 | use clap::Parser; 4 | use file_encryptor::{ 5 | command::{open, seal, Cli, Command}, 6 | error, 7 | }; 8 | 9 | fn main() -> error::Result<()> { 10 | let cmd = Cli::parse(); 11 | 12 | let result = match cmd.cmd { 13 | Command::Open(f) => open::open(&f), 14 | Command::Seal(f) => seal::seal(&f), 15 | Command::Keygen(k) => k.gen(), 16 | }; 17 | 18 | if let Err(err) = &result { 19 | eprintln!("{}", err); 20 | process::exit(err.status_code().into()); 21 | } 22 | 23 | Ok(()) 24 | } 25 | -------------------------------------------------------------------------------- /src/command/keygen.rs: -------------------------------------------------------------------------------- 1 | use crate::{crypto::KEY_SIZE, error, ioutils::IO}; 2 | use clap::Parser; 3 | use rand::Rng; 4 | use scrypt; 5 | use std::sync::{Arc, Mutex}; 6 | 7 | type Key = [u8; KEY_SIZE]; 8 | const MAX_KEY_SIZE: usize = 0xffff; 9 | 10 | struct Hash(scrypt::Params); 11 | 12 | impl Default for Hash { 13 | fn default() -> Self { 14 | // https://tobtu.com/minimum-password-settings/ 15 | Self(scrypt::Params::new(16, 8, 2, KEY_SIZE).expect("invalid param for scrypt")) 16 | } 17 | } 18 | 19 | impl Hash { 20 | fn hash(&self, payload: &[u8]) -> Key { 21 | let mut key = Key::default(); 22 | scrypt::scrypt(payload, &[], &self.0, &mut key) 23 | .expect("invalid keysize buffer, use constant `KEY_SIZE`"); 24 | key 25 | } 26 | } 27 | 28 | struct Engine { 29 | key_buf: Key, 30 | } 31 | 32 | impl Engine { 33 | pub fn new() -> Self { 34 | let key_buf: Key = Default::default(); 35 | Engine { key_buf } 36 | } 37 | 38 | pub fn update(&mut self, subkey: &Key) -> &mut Self { 39 | Self::xor_key(&mut self.key_buf, subkey); 40 | self 41 | } 42 | 43 | pub fn bytes(&self) -> &Key { 44 | &self.key_buf 45 | } 46 | 47 | fn xor_key(key_buf: &mut Key, sub_key: &Key) { 48 | for i in 0..KEY_SIZE { 49 | key_buf[i] ^= sub_key[i]; 50 | } 51 | } 52 | } 53 | 54 | /// If no option, stdin... 55 | #[derive(Parser, Debug, Clone)] 56 | pub struct KeyGen { 57 | /// (optional) a passphrase used to generate key 58 | #[arg(short, long)] 59 | password: Option, 60 | 61 | /// Randomly generated, takes precedence over all other options 62 | #[arg(short, long, default_value_t = false)] 63 | rand: bool, 64 | 65 | /// (Optional) File to read in as key, default stdin 66 | #[arg(short, long)] 67 | input_file: Option, 68 | 69 | /// (Optional) File to write out, default stdout 70 | #[arg(short, long)] 71 | output_file: Option, 72 | } 73 | 74 | impl KeyGen { 75 | pub fn gen(&self) -> error::Result<()> { 76 | let mut io = IO::new(&self.input_file, &self.output_file)?; 77 | let hash = Hash(scrypt::Params::new(16, 8, 2, KEY_SIZE).expect("invalid param for scrypt")); 78 | 79 | if self.rand { 80 | Ok(with_rand(&mut io, &hash)?) 81 | } else if let Some(pw) = &self.password { 82 | Ok(with_password(&mut io, &hash, pw)?) 83 | } else { 84 | Ok(with_stdin(&mut io, &hash)?) 85 | } 86 | } 87 | } 88 | 89 | fn with_rand(io: &mut IO, hash: &Hash) -> error::Result<()> { 90 | let mut buf = [0u8; MAX_KEY_SIZE]; 91 | let mut rng = rand::thread_rng(); 92 | buf.iter_mut().for_each(|i| *i = rng.gen()); 93 | 94 | let key = hash.hash(&buf); 95 | io.write_bytes(Engine::new().update(&key).bytes())?; 96 | 97 | Ok(()) 98 | } 99 | 100 | fn with_password(io: &mut IO, hash: &Hash, pw: &String) -> error::Result<()> { 101 | let key = hash.hash(pw.as_bytes()); 102 | io.write_bytes(Engine::new().update(&key).bytes())?; 103 | Ok(()) 104 | } 105 | 106 | fn with_stdin(io: &mut IO, hash: &Hash) -> error::Result<()> { 107 | let keygen = Arc::new(Mutex::new(Engine::new())); 108 | let hash = Arc::new(hash); 109 | 110 | let scope: error::Result<()> = rayon::scope(|s| { 111 | loop { 112 | let mut buf = [0_u8; MAX_KEY_SIZE]; 113 | let bytes_read = io.read_bytes(&mut buf)?; 114 | 115 | let keygen = Arc::clone(&keygen); 116 | let hash = Arc::clone(&hash); 117 | s.spawn(move |_| { 118 | let subkey = hash.hash(&buf); 119 | keygen 120 | .lock() 121 | .expect("unable to obtain thread lock for key generation engine") 122 | .update(&subkey); 123 | }); 124 | 125 | if bytes_read < MAX_KEY_SIZE { 126 | break; 127 | } 128 | } 129 | 130 | Ok(()) 131 | }); 132 | 133 | scope?; 134 | 135 | let keygen = keygen 136 | .lock() 137 | .expect("unable to obtain thread lock for key generation engine"); 138 | let bytes = keygen.bytes(); 139 | 140 | io.write_bytes(bytes)?; 141 | Ok(()) 142 | } 143 | -------------------------------------------------------------------------------- /src/command/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::ioutils::FileArg; 2 | use clap::{Parser, Subcommand}; 3 | 4 | pub mod keygen; 5 | pub mod open; 6 | pub mod seal; 7 | 8 | /// A Rust CLI program that streams files for encryption and decryption. 9 | #[derive(Parser, Debug)] 10 | pub struct Cli { 11 | /// Action to perform on the input file 12 | #[command(subcommand)] 13 | pub cmd: Command, 14 | } 15 | 16 | #[derive(Subcommand, Clone, Debug)] 17 | pub enum Command { 18 | /// generate a key, from pure random bytes, or from an input password. 19 | Keygen(keygen::KeyGen), 20 | 21 | /// open an encrypted file 22 | Open(FileArg), 23 | 24 | /// seal a plaintext file 25 | Seal(FileArg), 26 | } 27 | -------------------------------------------------------------------------------- /src/command/open.rs: -------------------------------------------------------------------------------- 1 | use std::{fs::OpenOptions, io::Read}; 2 | 3 | use crate::{ 4 | crypto::{block::Block, cipher::Cipher, Key, BLOCK_SIZE, IV_SIZE, KEY_SIZE}, 5 | error, 6 | ioutils::{FileArg, IO}, 7 | }; 8 | 9 | pub fn open(arg: &FileArg) -> error::Result<()> { 10 | let mut io = IO::new(&arg.input_file, &arg.output_file)?; 11 | 12 | let key = match &arg.key { 13 | None => { 14 | let mut key = Key::default(); 15 | let _ = std::io::stdin().read(&mut key)?; 16 | key 17 | } 18 | Some(filename) => { 19 | let mut file = OpenOptions::new().read(true).open(filename)?; 20 | if file.metadata()?.len() != (KEY_SIZE as u64) { 21 | return Err(error::Error::Key); 22 | } 23 | 24 | let mut key = Key::default(); 25 | file.read_exact(&mut key)?; 26 | key 27 | } 28 | }; 29 | 30 | let mut iv = Block::default(); 31 | io.read_bytes(&mut iv.bytes_mut()[0..IV_SIZE])?; 32 | 33 | // let mut cipher = Cipher::new(key, iv, &arg.aad); 34 | let mut cipher = Cipher::new(key, iv, &None); 35 | 36 | // read in the first block 37 | let mut buf_proc = Block::default(); 38 | let mut bytes_read = io.read_block(&mut buf_proc)?; 39 | if bytes_read != BLOCK_SIZE { 40 | // invalid padding 41 | return Err(error::Error::Encryption(String::from( 42 | "invalid ciphertext file", 43 | ))); 44 | } 45 | 46 | let mut buf_read = Block::default(); 47 | bytes_read = io.read_block(&mut buf_read)?; 48 | if bytes_read != BLOCK_SIZE { 49 | // missing auth tag 50 | return Err(error::Error::Encryption(String::from( 51 | "invalid ciphertext file", 52 | ))); 53 | } 54 | 55 | let mut loop_buf = Block::default(); 56 | loop { 57 | bytes_read = io.read_block(&mut loop_buf)?; 58 | if bytes_read != BLOCK_SIZE { 59 | break; 60 | } 61 | 62 | cipher.decrypt_block_inplace(&mut buf_proc); 63 | io.write_block(&buf_proc, BLOCK_SIZE)?; 64 | 65 | buf_proc.bytes_mut().copy_from_slice(buf_read.bytes()); 66 | buf_read.bytes_mut().copy_from_slice(loop_buf.bytes()); 67 | } 68 | 69 | // buf_proc contains the last ciphertext 70 | // buf_read contains the tag 71 | let buf_len = cipher.decrypt_block_inplace(&mut buf_proc); 72 | io.write_block(&buf_proc, buf_len)?; 73 | 74 | let decrypted_tag = buf_read.bytes(); 75 | for (i, byte) in cipher.tag().bytes().iter().enumerate() { 76 | if *byte != decrypted_tag[i] { 77 | return Err(error::Error::Encryption("invalid tag".to_string())); 78 | } 79 | } 80 | 81 | Ok(()) 82 | } 83 | -------------------------------------------------------------------------------- /src/command/seal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | crypto::{self, block::Block, cipher, Key, BLOCK_SIZE, IV_SIZE, KEY_SIZE}, 3 | error, 4 | ioutils::{FileArg, IO}, 5 | }; 6 | use clap::Parser; 7 | use std::{fs::OpenOptions, io::Read}; 8 | 9 | #[derive(Parser, Debug, Clone)] 10 | pub struct Encryptor { 11 | /// (optional) input file, read from stdin by default 12 | #[arg(short, long)] 13 | input_file: Option, 14 | 15 | /// (optional) output file, write to stdout by default 16 | #[arg(short, long)] 17 | output_file: Option, 18 | 19 | /// (optional) additional authenticated data 20 | #[arg(short, long)] 21 | aad: Option, 22 | 23 | /// (optional) key file, read (the first) 32 byte from stdin by default 24 | #[arg(short, long)] 25 | key: Option, 26 | } 27 | 28 | pub fn seal(filearg: &FileArg) -> error::Result<()> { 29 | let mut io = IO::new(&filearg.input_file, &filearg.output_file)?; 30 | 31 | // reads in key, if `-k` flag is passed, reads from file 32 | // otherwise reads (the first) 32 bytes from stdin 33 | let key = match &filearg.key { 34 | None => { 35 | let mut buf = Key::default(); 36 | std::io::stdin().read_exact(&mut buf).map_err(|err| { 37 | eprintln!("{}", err); 38 | error::Error::Key 39 | })?; 40 | 41 | buf 42 | } 43 | Some(key) => { 44 | let mut key_file = OpenOptions::new().read(true).open(key)?; 45 | if key_file.metadata()?.len() != KEY_SIZE as u64 { 46 | return Err(error::Error::Key); 47 | } 48 | let mut buf = Key::default(); 49 | key_file.read_exact(&mut buf)?; 50 | 51 | buf 52 | } 53 | }; 54 | 55 | // iv 56 | let iv = Block::new_iv(); 57 | io.write_block(&iv, IV_SIZE)?; 58 | 59 | // let mut cipher = cipher::Cipher::new(key, iv, &filearg.aad); 60 | let mut cipher = cipher::Cipher::new(key, iv, &None); 61 | 62 | // stream file/stdin 63 | let mut eof = false; 64 | loop { 65 | let mut buf = Block::default(); 66 | let bytes_read = io.read_block(&mut buf)?; 67 | if bytes_read != BLOCK_SIZE { 68 | crypto::pkcs7::pad(&mut buf, bytes_read); 69 | eof = true; 70 | } 71 | 72 | // cipher block 73 | cipher.encrypt_block_inplace(&mut buf, bytes_read); 74 | io.write_block(&buf, BLOCK_SIZE)?; 75 | 76 | if eof { 77 | break; 78 | } 79 | } 80 | 81 | io.write_block(cipher.tag(), BLOCK_SIZE)?; 82 | Ok(()) 83 | } 84 | -------------------------------------------------------------------------------- /src/crypto/block.rs: -------------------------------------------------------------------------------- 1 | use aes::cipher::generic_array::{typenum::U16, GenericArray}; 2 | 3 | use crate::crypto::{BLOCK_SIZE, IV_SIZE}; 4 | 5 | #[derive(Debug, Clone, Copy, Default)] 6 | pub struct Block([u8; BLOCK_SIZE]); 7 | 8 | pub const REDUCTION_POLYNOMIAL: Block = 9 | Block([0b1110_0001, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 10 | 11 | impl<'a> From<&'a mut Block> for &'a mut GenericArray { 12 | fn from(value: &'a mut Block) -> Self { 13 | GenericArray::from_mut_slice(&mut value.0) 14 | } 15 | } 16 | 17 | impl<'a> From<&'a Block> for &'a [u8; BLOCK_SIZE] { 18 | fn from(value: &'a Block) -> Self { 19 | &value.0 20 | } 21 | } 22 | 23 | #[cfg(test)] 24 | impl From<[u8; BLOCK_SIZE]> for Block { 25 | fn from(buf: [u8; BLOCK_SIZE]) -> Self { 26 | Self(buf) 27 | } 28 | } 29 | 30 | #[cfg(test)] 31 | impl From<[u8; IV_SIZE]> for Block { 32 | fn from(iv: [u8; IV_SIZE]) -> Self { 33 | let mut buf = [0_u8; BLOCK_SIZE]; 34 | buf[..IV_SIZE].copy_from_slice(iv.as_ref()); 35 | Self(buf) 36 | } 37 | } 38 | 39 | impl Block { 40 | pub fn new_iv() -> Self { 41 | use rand::RngCore; 42 | 43 | let mut buf = Block::default(); 44 | rand::thread_rng().fill_bytes(&mut buf.0[..IV_SIZE]); 45 | 46 | buf 47 | } 48 | 49 | pub fn bytes_mut(&mut self) -> &mut [u8; BLOCK_SIZE] { 50 | &mut self.0 51 | } 52 | 53 | pub fn bytes(&self) -> &[u8; BLOCK_SIZE] { 54 | &self.0 55 | } 56 | 57 | pub fn iv_bytes(&self) -> &[u8] { 58 | &self.0[..IV_SIZE] 59 | } 60 | 61 | pub fn inc_counter(&mut self) { 62 | let ctr_bytes = self.0[IV_SIZE..].as_mut(); 63 | let ctr_bound = ctr_bytes.len(); 64 | for i in 0..ctr_bound { 65 | let byte = &mut ctr_bytes[ctr_bound - i - 1]; 66 | if *byte == 255 { 67 | *byte = 0; 68 | continue; 69 | } 70 | *byte += 1; 71 | break; 72 | } 73 | } 74 | 75 | pub fn xor(&mut self, other: &Block) { 76 | for i in 0..BLOCK_SIZE { 77 | self.0[i] ^= &other.0[i] 78 | } 79 | } 80 | 81 | pub fn bin_shift_left(&mut self) { 82 | let bytes = self.bytes_mut(); 83 | let mut carry_bit = 0_u8; 84 | 85 | bytes.iter_mut().rev().for_each(|byte| { 86 | let new_carry = *byte & 0b1000_0000; 87 | *byte <<= 1; 88 | *byte |= carry_bit >> 7; 89 | carry_bit = new_carry; 90 | }); 91 | } 92 | 93 | pub fn bitset(&self, bit: u8) -> bool { 94 | let byte_blocks = bit / 8; 95 | let byte_blocks = BLOCK_SIZE - 1 - (byte_blocks as usize); 96 | 97 | let bit_mask = (1 << (bit % 8)) as u8; 98 | self.0[byte_blocks] & bit_mask != 0 99 | } 100 | } 101 | 102 | #[cfg(test)] 103 | mod tests { 104 | use super::*; 105 | use rand::Rng; 106 | 107 | #[test] 108 | fn block_bin_left_shift() { 109 | let mut bytes = Block::from([ 110 | 0b1101_0110, 111 | 0b0010_1101, 112 | 0b1011_1000, 113 | 0b0101_0011, 114 | 0b0110_1110, 115 | 0b1001_0111, 116 | 0b1110_0001, 117 | 0b0001_1100, 118 | 0b0111_1010, 119 | 0b1000_1001, 120 | 0b1010_0110, 121 | 0b0100_1111, 122 | 0b1100_1010, 123 | 0b0011_0001, 124 | 0b1001_1111, 125 | 0b0110_0110, 126 | ]); 127 | 128 | let expected: [u8; 16] = [ 129 | 0b1010_1100, 130 | 0b0101_1011, 131 | 0b0111_0000, 132 | 0b1010_0110, 133 | 0b1101_1101, 134 | 0b0010_1111, 135 | 0b1100_0010, 136 | 0b0011_1000, 137 | 0b1111_0101, 138 | 0b0001_0011, 139 | 0b0100_1100, 140 | 0b1001_1111, 141 | 0b1001_0100, 142 | 0b0110_0011, 143 | 0b0011_1110, 144 | 0b1100_1100, 145 | ]; 146 | 147 | bytes.bin_shift_left(); 148 | for (i, byte) in expected.iter().enumerate() { 149 | assert_eq!(bytes.0[i], *byte); 150 | } 151 | } 152 | 153 | #[test] 154 | fn bitset_in_blocks() { 155 | let mut buf = Block::default(); 156 | buf.0[BLOCK_SIZE - 1] = 1; 157 | 158 | for i in 0..128 { 159 | assert!(buf.bitset(i)); 160 | buf.bin_shift_left(); 161 | } 162 | } 163 | 164 | #[test] 165 | fn bytes_xor() { 166 | let mut rng = rand::thread_rng(); 167 | 168 | let mut left = Block::default(); 169 | left.bytes_mut() 170 | .iter_mut() 171 | .for_each(|byte| *byte = rng.gen()); 172 | 173 | let mut right = Block::default(); 174 | right 175 | .bytes_mut() 176 | .iter_mut() 177 | .for_each(|byte| *byte = rng.gen()); 178 | 179 | let mut data = left; 180 | data.xor(&right); 181 | for i in 0..BLOCK_SIZE { 182 | let expected = left.0[i] ^ right.0[i]; 183 | assert_eq!(data.0[i], expected); 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/crypto/cipher.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{ 2 | block::{Block, REDUCTION_POLYNOMIAL}, 3 | Key, BLOCK_SIZE, 4 | }; 5 | use aes::{ 6 | cipher::{generic_array::GenericArray, BlockEncrypt, KeyInit}, 7 | Aes256, 8 | }; 9 | 10 | use super::pkcs7; 11 | 12 | #[derive(Clone)] 13 | #[allow(dead_code)] 14 | pub struct Cipher { 15 | payload_len: usize, 16 | aad_len: usize, 17 | aes: Aes256, 18 | iv: Block, 19 | counter0: Block, 20 | tag: Tag, 21 | } 22 | 23 | impl Cipher { 24 | pub fn new(key: Key, iv: Block, aad: &Option) -> Self { 25 | let aes = Aes256::new(&key.into()); 26 | 27 | let mut counter0 = iv; 28 | aes.encrypt_block((&mut counter0).into()); 29 | 30 | let mut h = Block::default(); 31 | aes.encrypt_block((&mut h).into()); 32 | 33 | let mut tag = Tag::new(counter0, h); 34 | if let Some(aad) = aad { 35 | tag.with_aad(aad.as_bytes()); 36 | } 37 | 38 | let aad_len = if let Some(aad) = aad { aad.len() } else { 0 }; 39 | let payload_len = 0; 40 | 41 | Self { 42 | payload_len, 43 | aad_len, 44 | aes, 45 | iv, 46 | counter0, 47 | tag, 48 | } 49 | } 50 | 51 | pub fn encrypt_block_inplace(&mut self, block: &mut Block, size: usize) { 52 | self.payload_len += size; 53 | self.iv.inc_counter(); 54 | 55 | let mut ctr = self.iv; 56 | self.encrypt_block(&mut ctr); 57 | 58 | block.xor(&ctr); 59 | 60 | self.tag.compute(block); 61 | } 62 | 63 | pub fn decrypt_block_inplace(&mut self, block: &mut Block) -> usize { 64 | self.tag.compute(block); 65 | self.iv.inc_counter(); 66 | let mut ctr = self.iv; 67 | self.aes.encrypt_block((&mut ctr).into()); 68 | block.xor(&ctr); 69 | 70 | let size = BLOCK_SIZE - pkcs7::unpad(block); 71 | self.payload_len += size; 72 | 73 | size 74 | } 75 | 76 | pub fn tag(&mut self) -> &Block { 77 | let mut block = Block::default(); 78 | block.bytes_mut()[..8].copy_from_slice(&self.payload_len.to_be_bytes()); 79 | block.bytes_mut()[8..].copy_from_slice(&self.aad_len.to_be_bytes()); 80 | block.xor(self.tag.block()); 81 | self.tag.compute(&block); 82 | 83 | self.tag.tag_buf.xor(&self.tag.counter_0); 84 | 85 | &self.tag.tag_buf 86 | } 87 | 88 | fn encrypt_block(&self, block: &mut Block) { 89 | self.aes 90 | .encrypt_block(GenericArray::from_mut_slice(block.bytes_mut())); 91 | } 92 | } 93 | 94 | #[derive(Clone, Debug)] 95 | pub struct Tag { 96 | counter_0: Block, 97 | h: Block, 98 | tag_buf: Block, 99 | } 100 | 101 | impl Tag { 102 | fn new(counter_0: Block, h: Block) -> Self { 103 | Self { 104 | counter_0, 105 | h, 106 | tag_buf: Block::default(), 107 | } 108 | } 109 | 110 | fn with_aad(&mut self, mut auth_data: &[u8]) { 111 | let mut eof = false; 112 | let mut block = Block::default(); 113 | while !eof { 114 | let aad_block = if auth_data.len() != BLOCK_SIZE { 115 | block = Block::default(); 116 | block.bytes_mut().copy_from_slice(auth_data.as_ref()); 117 | eof = true; 118 | block 119 | } else { 120 | block 121 | .bytes_mut() 122 | .copy_from_slice(auth_data[..BLOCK_SIZE].as_ref()); 123 | block 124 | }; 125 | 126 | self.compute(&aad_block); 127 | 128 | if eof { 129 | break; 130 | } 131 | 132 | auth_data = &auth_data[BLOCK_SIZE..]; 133 | } 134 | } 135 | 136 | fn block(&self) -> &Block { 137 | &self.tag_buf 138 | } 139 | 140 | fn compute(&mut self, block: &Block) { 141 | self.tag_buf.xor(block); 142 | self.tag_buf = Self::galois_multiply(&self.tag_buf, &self.h); 143 | } 144 | 145 | fn galois_multiply(x: &Block, y: &Block) -> Block { 146 | let mut z = Block::default(); 147 | let mut v = *x; 148 | 149 | for i in 0..128 { 150 | if y.bitset(i) { 151 | z.xor(&v); 152 | } 153 | 154 | let msb_set = v.bitset(127); 155 | v.bin_shift_left(); 156 | if msb_set { 157 | v.xor(&REDUCTION_POLYNOMIAL); 158 | } 159 | } 160 | 161 | z 162 | } 163 | } 164 | 165 | #[cfg(test)] 166 | mod tests { 167 | use super::*; 168 | use crate::crypto::{BLOCK_SIZE, IV_SIZE}; 169 | 170 | #[test] 171 | fn test_iv_new() { 172 | let iv = Block::new_iv(); 173 | let buf: &[u8; BLOCK_SIZE] = (&iv).into(); 174 | assert_eq!(buf[IV_SIZE..].len(), 4); 175 | buf[IV_SIZE..] 176 | .iter() 177 | .for_each(|&byte| assert_eq!(byte, 0u8)) 178 | } 179 | 180 | #[test] 181 | fn test_iv_bytes() { 182 | let bytes = Block::new_iv(); 183 | let bytes = bytes.iv_bytes(); 184 | assert_eq!(bytes.len(), IV_SIZE); 185 | 186 | let bytes2 = Block::new_iv(); 187 | let bytes2 = bytes2.iv_bytes(); 188 | assert_ne!(bytes, bytes2); 189 | } 190 | 191 | #[test] 192 | fn test_iv_inc_counter() { 193 | let mut iv = Block::new_iv(); 194 | for ele in iv.bytes()[IV_SIZE..].iter() { 195 | assert_eq!(*ele, 0); 196 | } 197 | 198 | iv.inc_counter(); 199 | assert_eq!(iv.bytes()[BLOCK_SIZE - 1], 1); 200 | 201 | iv.inc_counter(); 202 | assert_eq!(iv.bytes()[BLOCK_SIZE - 1], 2); 203 | 204 | // bit wrapping 205 | iv.bytes_mut()[BLOCK_SIZE - 1] = 255; 206 | iv.inc_counter(); 207 | assert_eq!(iv.bytes()[BLOCK_SIZE - 1], 0); 208 | assert_eq!(iv.bytes()[BLOCK_SIZE - 2], 1); 209 | 210 | // at max counter, wraps back to 0 211 | for ele in iv.bytes_mut()[IV_SIZE..].iter_mut() { 212 | *ele = 255; 213 | } 214 | 215 | iv.inc_counter(); 216 | for ele in iv.bytes()[IV_SIZE..].iter() { 217 | assert_eq!(*ele, 0); 218 | } 219 | } 220 | 221 | #[test] 222 | fn test_cipher_encrypt_in_place() { 223 | let key: Key = [ 224 | 0x6B, 0x38, 0x46, 0x7A, 0x58, 0x77, 0x32, 0x61, 0x4C, 0x70, 0x51, 0x39, 0x76, 0x4A, 225 | 0x33, 0x6E, 0x47, 0x6D, 0x34, 0x52, 0x30, 0x79, 0x55, 0x63, 0x48, 0x74, 0x42, 0x73, 226 | 0x56, 0x37, 0x64, 0x59, 227 | ]; 228 | 229 | let iv = Block::from([ 230 | 0x3A, 0x9F, 0xB4, 0x7E, 0x2D, 0x1C, 0xF8, 0x05, 0x9C, 0x7B, 0xA2, 0x6D, 231 | ]); 232 | 233 | let mut cipher = Cipher::new(key, iv, &None); 234 | 235 | let plaintext = Block::from([ 236 | 0x5A, 0x37, 0x71, 0x50, 0x39, 0x6B, 0x54, 0x62, 0x58, 0x31, 0x4C, 0x72, 0x34, 0x57, 237 | 0x6D, 0x4A, 238 | ]); 239 | 240 | let ciphertext = Block::from([ 241 | 0xEE, 0x0D, 0x0A, 0x0D, 0xD1, 0x2D, 0xE2, 0x48, 0x2B, 0xFF, 0xE9, 0x82, 0x8F, 0x2F, 242 | 0x9A, 0xB9, 243 | ]); 244 | 245 | let mut block = plaintext; 246 | 247 | cipher.encrypt_block_inplace(&mut block, BLOCK_SIZE); 248 | 249 | assert_ne!(block.bytes(), ciphertext.bytes()); 250 | } 251 | 252 | #[test] 253 | fn test_cipher_encryption() { 254 | let key: Key = [ 255 | 0x6B, 0x38, 0x46, 0x7A, 0x58, 0x77, 0x32, 0x61, 0x4C, 0x70, 0x51, 0x39, 0x76, 0x4A, 256 | 0x33, 0x6E, 0x47, 0x6D, 0x34, 0x52, 0x30, 0x79, 0x55, 0x63, 0x48, 0x74, 0x42, 0x73, 257 | 0x56, 0x37, 0x64, 0x59, 258 | ]; 259 | 260 | let iv = Block::from([ 261 | 0x3A, 0x9F, 0xB4, 0x7E, 0x2D, 0x1C, 0xF8, 0x05, 0x9C, 0x7B, 0xA2, 0x6D, 262 | ]); 263 | 264 | let mut cipher = Cipher::new(key, iv, &None); 265 | let mut cipher2 = cipher.clone(); 266 | 267 | let plaintext = Block::from([ 268 | 0xEE, 0x0D, 0x0A, 0x0D, 0xD1, 0x2D, 0xE2, 0x48, 0x2B, 0xFF, 0xE9, 0x82, 0x8F, 0x2F, 269 | 0x9A, 0xB9, 270 | ]); 271 | 272 | let mut block = plaintext; 273 | 274 | cipher.encrypt_block_inplace(&mut block, BLOCK_SIZE); 275 | cipher2.decrypt_block_inplace(&mut block); 276 | 277 | assert_eq!(block.bytes(), plaintext.bytes()); 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod block; 2 | pub mod cipher; 3 | pub mod pkcs7; 4 | 5 | pub const IV_SIZE: usize = 12; 6 | pub const BLOCK_SIZE: usize = 16; 7 | pub const KEY_SIZE: usize = 32; 8 | 9 | pub type Key = [u8; KEY_SIZE]; 10 | -------------------------------------------------------------------------------- /src/crypto/pkcs7.rs: -------------------------------------------------------------------------------- 1 | use crate::crypto::{block::Block, BLOCK_SIZE}; 2 | 3 | pub fn pad(block: &mut Block, block_size: usize) { 4 | let block_len = BLOCK_SIZE; 5 | let pad_value = block_len - block_size; 6 | let bytes = block.bytes_mut(); 7 | for i in 0..pad_value { 8 | bytes[block_len - 1 - i] = pad_value as u8; 9 | } 10 | } 11 | 12 | /// returns the number of padding bytes removed 13 | pub fn unpad(block: &mut Block) -> usize { 14 | let block_len = BLOCK_SIZE; 15 | 16 | let bytes = block.bytes_mut(); 17 | let last_byte = bytes[block_len - 1]; 18 | 19 | // check if padding exists 20 | let mut bytes_padded = 0; 21 | for i in 0..(last_byte as usize) { 22 | if bytes[block_len - 1 - i] != last_byte { 23 | return 0; 24 | } 25 | bytes_padded += 1; 26 | } 27 | 28 | // remove padding 29 | for i in 0..(last_byte as usize) { 30 | bytes[block_len - 1 - i] = 0; 31 | } 32 | 33 | bytes_padded 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | 40 | #[test] 41 | fn pkcs7_pad() { 42 | let mut block = Block::default(); 43 | pad(&mut block, 3); 44 | for i in 3..16 { 45 | assert_eq!(block.bytes()[i], 13); 46 | } 47 | 48 | let mut block = Block::default(); 49 | pad(&mut block, 0); 50 | for i in 0..16 { 51 | assert_eq!(block.bytes()[i], 16); 52 | } 53 | } 54 | 55 | #[test] 56 | fn pkcs7_unpad() { 57 | let mut block = Block::default(); 58 | pad(&mut block, 3); 59 | let padded_bytes = unpad(&mut block); 60 | assert_eq!(padded_bytes, BLOCK_SIZE - 3); 61 | for i in 3..16 { 62 | assert_eq!(block.bytes()[i], 0); 63 | } 64 | 65 | let mut block = Block::default(); 66 | pad(&mut block, 0); 67 | unpad(&mut block); 68 | for i in 0..16 { 69 | assert_eq!(block.bytes()[i], 0); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::{error, fmt, io, process, result}; 2 | 3 | pub type Result = result::Result; 4 | 5 | #[derive(Debug)] 6 | pub enum Error { 7 | IO(String), 8 | Key, 9 | Encryption(String), 10 | Other(String), 11 | } 12 | 13 | impl Error { 14 | pub fn status_code(&self) -> u8 { 15 | match self { 16 | Self::Other(_) => 1, 17 | Self::IO(_) => 2, 18 | Self::Key => 3, 19 | Self::Encryption(_) => 4, 20 | } 21 | } 22 | } 23 | 24 | impl error::Error for Error {} 25 | 26 | unsafe impl Send for Error {} 27 | 28 | impl fmt::Display for Error { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | match self { 31 | Self::IO(msg) => write!(f, "{}", msg), 32 | Self::Key => write!(f, "Invalid key"), 33 | Self::Other(msg) => write!(f, "{}", msg), 34 | Self::Encryption(msg) => write!(f, "{}", msg), 35 | } 36 | } 37 | } 38 | 39 | impl process::Termination for Error { 40 | fn report(self) -> process::ExitCode { 41 | self.status_code().into() 42 | } 43 | } 44 | 45 | impl From for Error { 46 | fn from(value: std::io::Error) -> Self { 47 | Self::IO(value.to_string()) 48 | } 49 | } 50 | 51 | impl From for Error { 52 | fn from(value: anyhow::Error) -> Self { 53 | Self::Other(value.to_string()) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/ioutils.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | fs::{File, OpenOptions}, 3 | io::{Read, Write}, 4 | }; 5 | 6 | use clap::Parser; 7 | 8 | use crate::crypto::block::Block; 9 | 10 | #[derive(Parser, Debug, Clone)] 11 | pub struct FileArg { 12 | /// (optional) input file, read from stdin by default 13 | #[arg(short, long)] 14 | pub input_file: Option, 15 | 16 | /// (optional) output file, write to stdout by default 17 | #[arg(short, long)] 18 | pub output_file: Option, 19 | 20 | /* 21 | /// (optional) additional authenticated data 22 | #[arg(short, long)] 23 | pub aad: Option, 24 | */ 25 | 26 | /// (optional) key file, read (the first) 32 byte from stdin by default 27 | #[arg(short, long)] 28 | pub key: Option, 29 | } 30 | 31 | #[derive(Debug)] 32 | pub struct IO { 33 | filein: Option, 34 | fileout: Option, 35 | } 36 | 37 | impl IO { 38 | pub fn new(filein: &Option, fileout: &Option) -> std::io::Result { 39 | let filein = if let Some(filename) = filein { 40 | Some(OpenOptions::new().read(true).open(filename)?) 41 | } else { 42 | None 43 | }; 44 | 45 | let fileout = if let Some(filename) = fileout { 46 | Some( 47 | OpenOptions::new() 48 | .create(true) 49 | .truncate(true) 50 | .write(true) 51 | .open(filename)?, 52 | ) 53 | } else { 54 | None 55 | }; 56 | 57 | Ok(Self { filein, fileout }) 58 | } 59 | 60 | pub fn read_block(&mut self, block: &mut Block) -> std::io::Result { 61 | match &mut self.filein { 62 | None => std::io::stdin().read(block.bytes_mut()), 63 | Some(fd) => fd.read(block.bytes_mut()), 64 | } 65 | } 66 | 67 | pub fn write_block(&mut self, block: &Block, n: usize) -> std::io::Result { 68 | match &mut self.fileout { 69 | None => std::io::stdout().write(&block.bytes()[..n]), 70 | Some(fd) => fd.write(&block.bytes()[..n]), 71 | } 72 | } 73 | 74 | pub fn read_bytes(&mut self, bytes: &mut [u8]) -> std::io::Result { 75 | match &mut self.filein { 76 | None => std::io::stdin().read(bytes), 77 | Some(fd) => fd.read(bytes), 78 | } 79 | } 80 | 81 | pub fn write_bytes(&mut self, bytes: &[u8]) -> std::io::Result { 82 | match &mut self.fileout { 83 | None => std::io::stdout().write(bytes), 84 | Some(fd) => fd.write(bytes), 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | pub mod command; 3 | pub mod crypto; 4 | pub mod ioutils; 5 | --------------------------------------------------------------------------------