├── .docker └── easypwned_bloom_001 │ └── Dockerfile ├── .github └── workflows │ └── compile.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── downloader ├── Cargo.toml └── src │ ├── download_coordinator.rs │ ├── downloader_http.rs │ ├── main.rs │ └── sink │ ├── bloom.rs │ ├── mod.rs │ └── stdout.rs ├── easypwned ├── Cargo.toml └── src │ └── main.rs └── easypwned_bloom ├── Cargo.toml └── src ├── bloom └── mod.rs └── lib.rs /.docker/easypwned_bloom_001/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM scratch 2 | ADD ./easypwned.bloom /easypwned.bloom -------------------------------------------------------------------------------- /.github/workflows/compile.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | tags: 4 | - 'v*.*.*' 5 | 6 | name: compile 7 | 8 | jobs: 9 | 10 | build: 11 | name: Build 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | container: 16 | - { os: 'ubuntu-latest', rust_target: 'aarch64-unknown-linux-musl' } 17 | - { os: 'ubuntu-latest', rust_target: 'x86_64-unknown-linux-musl' } 18 | - { os: 'macos-latest', rust_target: 'x86_64-apple-darwin' } 19 | - { os: 'macos-latest', rust_target: 'aarch64-apple-darwin' } 20 | runs-on: "${{ matrix.container.os }}" 21 | steps: 22 | - name: Set output 23 | id: vars 24 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/} 25 | - uses: actions/checkout@v2 26 | - uses: actions-rs/toolchain@v1 27 | with: 28 | toolchain: stable 29 | target: "${{ matrix.container.rust_target }}" 30 | override: true 31 | - uses: actions-rs/cargo@v1 32 | with: 33 | use-cross: true 34 | command: build 35 | args: "--release --target ${{ matrix.container.rust_target }}" 36 | - name: Build and push 37 | run: | 38 | cp target/${{ matrix.container.rust_target }}/release/easypwned target/${{ matrix.container.rust_target }}/release/easypwned_${{ matrix.container.rust_target }} 39 | cp target/${{ matrix.container.rust_target }}/release/easypwned_haveibeenpwned_downloader target/${{ matrix.container.rust_target }}/release/easypwned_haveibeenpwned_downloader_${{ matrix.container.rust_target }} 40 | - name: Prepare output artifact (easypwned) 41 | uses: actions/upload-artifact@v3 42 | with: 43 | name: easypwned-${{ matrix.container.rust_target }} 44 | path: | 45 | target/${{ matrix.container.rust_target }}/release/easypwned_${{ matrix.container.rust_target }} 46 | target/${{ matrix.container.rust_target }}/release/easypwned_haveibeenpwned_downloader_${{ matrix.container.rust_target }} 47 | 48 | release: 49 | name: Release 50 | runs-on: ubuntu-latest 51 | needs: ["build"] 52 | steps: 53 | - name: download artifacts 54 | uses: actions/download-artifact@v3 55 | with: 56 | path: binaries 57 | - name: Release 58 | uses: softprops/action-gh-release@v1 59 | with: 60 | fail_on_unmatched_files: true 61 | files: | 62 | binaries/*/* 63 | 64 | docker_build: 65 | name: docker 66 | runs-on: ubuntu-latest 67 | needs: ["build"] 68 | strategy: 69 | fail-fast: true 70 | matrix: 71 | container: 72 | - { os: 'ubuntu-latest', arch: 'arm64', rust_target: 'aarch64-unknown-linux-musl' } 73 | - { os: 'ubuntu-latest', arch: 'amd64', rust_target: 'x86_64-unknown-linux-musl' } 74 | steps: 75 | - uses: actions/checkout@v2 76 | - uses: docker/setup-buildx-action@v1 77 | id: buildx 78 | with: 79 | install: true 80 | - name: Login to DockerHub 81 | uses: docker/login-action@v1 82 | with: 83 | username: ${{ secrets.DOCKERHUB_USERNAME }} 84 | password: ${{ secrets.DOCKERHUB_TOKEN }} 85 | - name: download artifacts 86 | uses: actions/download-artifact@v3 87 | with: 88 | path: binaries 89 | - name: Build and push 90 | run: | 91 | cp binaries/easypwned-${{ matrix.container.rust_target }}/easypwned_${{ matrix.container.rust_target }} binary_easypwned 92 | chmod +x binary_easypwned 93 | cp binaries/easypwned-${{ matrix.container.rust_target }}/easypwned_haveibeenpwned_downloader_${{ matrix.container.rust_target }} binary_easypwned_haveibeenpwned_downloader 94 | chmod +x binary_easypwned_haveibeenpwned_downloader 95 | docker buildx build -o type=docker --platform linux/${{ matrix.container.arch }} -t easybill/easypwned:${{github.ref_name}}_${{ matrix.container.arch }} . 96 | docker push easybill/easypwned:${{github.ref_name}}_${{ matrix.container.arch }} 97 | 98 | docker_manifest: 99 | name: Docker Image 100 | runs-on: ubuntu-latest 101 | needs: ["docker_build"] 102 | steps: 103 | - name: Login to DockerHub 104 | uses: docker/login-action@v1 105 | with: 106 | username: ${{ secrets.DOCKERHUB_USERNAME }} 107 | password: ${{ secrets.DOCKERHUB_TOKEN }} 108 | - name: Build and Push manifest 109 | run: | 110 | docker manifest create easybill/easypwned:${{github.ref_name}} easybill/easypwned:${{github.ref_name}}_amd64 easybill/easypwned:${{github.ref_name}}_arm64 111 | docker manifest push easybill/easypwned:${{github.ref_name}} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea/* 3 | pwned-passwords-sha1-ordered-by-hash-v8.txt 4 | easypwned.bloom 5 | easypwned.zip 6 | dist/easypwned_linux_x86_64 7 | dist/easypwned_osx_x86_64 8 | .DS_Store 9 | out.txt 10 | **/*.bloom -------------------------------------------------------------------------------- /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 = "addr2line" 7 | version = "0.24.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "f5fb1d8e4442bd405fdfd1dacb42792696b0cf9cb15882e5d097b742a676d375" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "alloc-no-stdlib" 22 | version = "2.0.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 25 | 26 | [[package]] 27 | name = "alloc-stdlib" 28 | version = "0.2.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 31 | dependencies = [ 32 | "alloc-no-stdlib", 33 | ] 34 | 35 | [[package]] 36 | name = "anstream" 37 | version = "0.6.15" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 40 | dependencies = [ 41 | "anstyle", 42 | "anstyle-parse", 43 | "anstyle-query", 44 | "anstyle-wincon", 45 | "colorchoice", 46 | "is_terminal_polyfill", 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle" 52 | version = "1.0.8" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 55 | 56 | [[package]] 57 | name = "anstyle-parse" 58 | version = "0.2.5" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 61 | dependencies = [ 62 | "utf8parse", 63 | ] 64 | 65 | [[package]] 66 | name = "anstyle-query" 67 | version = "1.1.1" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 70 | dependencies = [ 71 | "windows-sys 0.52.0", 72 | ] 73 | 74 | [[package]] 75 | name = "anstyle-wincon" 76 | version = "3.0.4" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 79 | dependencies = [ 80 | "anstyle", 81 | "windows-sys 0.52.0", 82 | ] 83 | 84 | [[package]] 85 | name = "anyhow" 86 | version = "1.0.89" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6" 89 | 90 | [[package]] 91 | name = "async-compression" 92 | version = "0.4.12" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa" 95 | dependencies = [ 96 | "brotli", 97 | "flate2", 98 | "futures-core", 99 | "memchr", 100 | "pin-project-lite", 101 | "tokio", 102 | ] 103 | 104 | [[package]] 105 | name = "async-trait" 106 | version = "0.1.83" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 109 | dependencies = [ 110 | "proc-macro2", 111 | "quote", 112 | "syn", 113 | ] 114 | 115 | [[package]] 116 | name = "autocfg" 117 | version = "1.4.0" 118 | source = "registry+https://github.com/rust-lang/crates.io-index" 119 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 120 | 121 | [[package]] 122 | name = "axum" 123 | version = "0.7.7" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "504e3947307ac8326a5437504c517c4b56716c9d98fac0028c2acc7ca47d70ae" 126 | dependencies = [ 127 | "async-trait", 128 | "axum-core", 129 | "bytes", 130 | "futures-util", 131 | "http", 132 | "http-body", 133 | "http-body-util", 134 | "hyper", 135 | "hyper-util", 136 | "itoa", 137 | "matchit", 138 | "memchr", 139 | "mime", 140 | "percent-encoding", 141 | "pin-project-lite", 142 | "rustversion", 143 | "serde", 144 | "serde_json", 145 | "serde_path_to_error", 146 | "serde_urlencoded", 147 | "sync_wrapper 1.0.1", 148 | "tokio", 149 | "tower", 150 | "tower-layer", 151 | "tower-service", 152 | "tracing", 153 | ] 154 | 155 | [[package]] 156 | name = "axum-core" 157 | version = "0.4.5" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "09f2bd6146b97ae3359fa0cc6d6b376d9539582c7b4220f041a33ec24c226199" 160 | dependencies = [ 161 | "async-trait", 162 | "bytes", 163 | "futures-util", 164 | "http", 165 | "http-body", 166 | "http-body-util", 167 | "mime", 168 | "pin-project-lite", 169 | "rustversion", 170 | "sync_wrapper 1.0.1", 171 | "tower-layer", 172 | "tower-service", 173 | "tracing", 174 | ] 175 | 176 | [[package]] 177 | name = "backtrace" 178 | version = "0.3.74" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 181 | dependencies = [ 182 | "addr2line", 183 | "cfg-if", 184 | "libc", 185 | "miniz_oxide", 186 | "object", 187 | "rustc-demangle", 188 | "windows-targets", 189 | ] 190 | 191 | [[package]] 192 | name = "base16ct" 193 | version = "0.2.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" 196 | 197 | [[package]] 198 | name = "base64" 199 | version = "0.22.1" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 202 | 203 | [[package]] 204 | name = "bincode" 205 | version = "1.3.3" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 208 | dependencies = [ 209 | "serde", 210 | ] 211 | 212 | [[package]] 213 | name = "bit-vec" 214 | version = "0.7.0" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "d2c54ff287cfc0a34f38a6b832ea1bd8e448a330b3e40a50859e6488bee07f22" 217 | 218 | [[package]] 219 | name = "bitflags" 220 | version = "2.6.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 223 | 224 | [[package]] 225 | name = "block-buffer" 226 | version = "0.10.4" 227 | source = "registry+https://github.com/rust-lang/crates.io-index" 228 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 229 | dependencies = [ 230 | "generic-array", 231 | ] 232 | 233 | [[package]] 234 | name = "bloomfilter" 235 | version = "1.0.14" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "bc0bdbcf2078e0ba8a74e1fe0cf36f54054a04485759b61dfd60b174658e9607" 238 | dependencies = [ 239 | "bit-vec", 240 | "getrandom", 241 | "siphasher", 242 | ] 243 | 244 | [[package]] 245 | name = "brotli" 246 | version = "6.0.0" 247 | source = "registry+https://github.com/rust-lang/crates.io-index" 248 | checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" 249 | dependencies = [ 250 | "alloc-no-stdlib", 251 | "alloc-stdlib", 252 | "brotli-decompressor", 253 | ] 254 | 255 | [[package]] 256 | name = "brotli-decompressor" 257 | version = "4.0.1" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 260 | dependencies = [ 261 | "alloc-no-stdlib", 262 | "alloc-stdlib", 263 | ] 264 | 265 | [[package]] 266 | name = "bumpalo" 267 | version = "3.16.0" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 270 | 271 | [[package]] 272 | name = "byteorder" 273 | version = "1.5.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 276 | 277 | [[package]] 278 | name = "bytes" 279 | version = "1.7.2" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" 282 | 283 | [[package]] 284 | name = "cc" 285 | version = "1.1.24" 286 | source = "registry+https://github.com/rust-lang/crates.io-index" 287 | checksum = "812acba72f0a070b003d3697490d2b55b837230ae7c6c6497f05cc2ddbb8d938" 288 | dependencies = [ 289 | "shlex", 290 | ] 291 | 292 | [[package]] 293 | name = "cfg-if" 294 | version = "1.0.0" 295 | source = "registry+https://github.com/rust-lang/crates.io-index" 296 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 297 | 298 | [[package]] 299 | name = "clap" 300 | version = "4.5.18" 301 | source = "registry+https://github.com/rust-lang/crates.io-index" 302 | checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3" 303 | dependencies = [ 304 | "clap_builder", 305 | "clap_derive", 306 | ] 307 | 308 | [[package]] 309 | name = "clap_builder" 310 | version = "4.5.18" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b" 313 | dependencies = [ 314 | "anstream", 315 | "anstyle", 316 | "clap_lex", 317 | "strsim", 318 | ] 319 | 320 | [[package]] 321 | name = "clap_derive" 322 | version = "4.5.18" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" 325 | dependencies = [ 326 | "heck", 327 | "proc-macro2", 328 | "quote", 329 | "syn", 330 | ] 331 | 332 | [[package]] 333 | name = "clap_lex" 334 | version = "0.7.2" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 337 | 338 | [[package]] 339 | name = "colorchoice" 340 | version = "1.0.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 343 | 344 | [[package]] 345 | name = "cpufeatures" 346 | version = "0.2.14" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" 349 | dependencies = [ 350 | "libc", 351 | ] 352 | 353 | [[package]] 354 | name = "crc32fast" 355 | version = "1.4.2" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 358 | dependencies = [ 359 | "cfg-if", 360 | ] 361 | 362 | [[package]] 363 | name = "crypto-common" 364 | version = "0.1.6" 365 | source = "registry+https://github.com/rust-lang/crates.io-index" 366 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 367 | dependencies = [ 368 | "generic-array", 369 | "typenum", 370 | ] 371 | 372 | [[package]] 373 | name = "digest" 374 | version = "0.10.7" 375 | source = "registry+https://github.com/rust-lang/crates.io-index" 376 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 377 | dependencies = [ 378 | "block-buffer", 379 | "crypto-common", 380 | ] 381 | 382 | [[package]] 383 | name = "easypwned" 384 | version = "0.1.0" 385 | dependencies = [ 386 | "anyhow", 387 | "axum", 388 | "base16ct", 389 | "clap", 390 | "easypwned_bloom", 391 | "serde", 392 | "serde_derive", 393 | "serde_json", 394 | "sha-1", 395 | "tokio", 396 | "tracing", 397 | ] 398 | 399 | [[package]] 400 | name = "easypwned_bloom" 401 | version = "0.1.0" 402 | dependencies = [ 403 | "anyhow", 404 | "bincode", 405 | "bloomfilter", 406 | "serde", 407 | "serde_derive", 408 | ] 409 | 410 | [[package]] 411 | name = "easypwned_haveibeenpwned_downloader" 412 | version = "0.1.0" 413 | dependencies = [ 414 | "anyhow", 415 | "bincode", 416 | "bloomfilter", 417 | "byteorder", 418 | "clap", 419 | "easypwned_bloom", 420 | "hex", 421 | "reqwest", 422 | "serde_derive", 423 | "tokio", 424 | "tracing", 425 | "tracing-subscriber", 426 | ] 427 | 428 | [[package]] 429 | name = "flate2" 430 | version = "1.0.34" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" 433 | dependencies = [ 434 | "crc32fast", 435 | "miniz_oxide", 436 | ] 437 | 438 | [[package]] 439 | name = "fnv" 440 | version = "1.0.7" 441 | source = "registry+https://github.com/rust-lang/crates.io-index" 442 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 443 | 444 | [[package]] 445 | name = "form_urlencoded" 446 | version = "1.2.1" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 449 | dependencies = [ 450 | "percent-encoding", 451 | ] 452 | 453 | [[package]] 454 | name = "futures-channel" 455 | version = "0.3.30" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" 458 | dependencies = [ 459 | "futures-core", 460 | ] 461 | 462 | [[package]] 463 | name = "futures-core" 464 | version = "0.3.30" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 467 | 468 | [[package]] 469 | name = "futures-sink" 470 | version = "0.3.30" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 473 | 474 | [[package]] 475 | name = "futures-task" 476 | version = "0.3.30" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 479 | 480 | [[package]] 481 | name = "futures-util" 482 | version = "0.3.30" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 485 | dependencies = [ 486 | "futures-core", 487 | "futures-task", 488 | "pin-project-lite", 489 | "pin-utils", 490 | ] 491 | 492 | [[package]] 493 | name = "generic-array" 494 | version = "0.14.7" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 497 | dependencies = [ 498 | "typenum", 499 | "version_check", 500 | ] 501 | 502 | [[package]] 503 | name = "getrandom" 504 | version = "0.2.15" 505 | source = "registry+https://github.com/rust-lang/crates.io-index" 506 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 507 | dependencies = [ 508 | "cfg-if", 509 | "js-sys", 510 | "libc", 511 | "wasi", 512 | "wasm-bindgen", 513 | ] 514 | 515 | [[package]] 516 | name = "gimli" 517 | version = "0.31.0" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "32085ea23f3234fc7846555e85283ba4de91e21016dc0455a16286d87a292d64" 520 | 521 | [[package]] 522 | name = "heck" 523 | version = "0.5.0" 524 | source = "registry+https://github.com/rust-lang/crates.io-index" 525 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 526 | 527 | [[package]] 528 | name = "hermit-abi" 529 | version = "0.3.9" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 532 | 533 | [[package]] 534 | name = "hex" 535 | version = "0.4.3" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 538 | 539 | [[package]] 540 | name = "http" 541 | version = "1.1.0" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" 544 | dependencies = [ 545 | "bytes", 546 | "fnv", 547 | "itoa", 548 | ] 549 | 550 | [[package]] 551 | name = "http-body" 552 | version = "1.0.1" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" 555 | dependencies = [ 556 | "bytes", 557 | "http", 558 | ] 559 | 560 | [[package]] 561 | name = "http-body-util" 562 | version = "0.1.2" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" 565 | dependencies = [ 566 | "bytes", 567 | "futures-util", 568 | "http", 569 | "http-body", 570 | "pin-project-lite", 571 | ] 572 | 573 | [[package]] 574 | name = "httparse" 575 | version = "1.9.5" 576 | source = "registry+https://github.com/rust-lang/crates.io-index" 577 | checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" 578 | 579 | [[package]] 580 | name = "httpdate" 581 | version = "1.0.3" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 584 | 585 | [[package]] 586 | name = "hyper" 587 | version = "1.4.1" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" 590 | dependencies = [ 591 | "bytes", 592 | "futures-channel", 593 | "futures-util", 594 | "http", 595 | "http-body", 596 | "httparse", 597 | "httpdate", 598 | "itoa", 599 | "pin-project-lite", 600 | "smallvec", 601 | "tokio", 602 | "want", 603 | ] 604 | 605 | [[package]] 606 | name = "hyper-rustls" 607 | version = "0.27.3" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" 610 | dependencies = [ 611 | "futures-util", 612 | "http", 613 | "hyper", 614 | "hyper-util", 615 | "rustls", 616 | "rustls-pki-types", 617 | "tokio", 618 | "tokio-rustls", 619 | "tower-service", 620 | "webpki-roots", 621 | ] 622 | 623 | [[package]] 624 | name = "hyper-util" 625 | version = "0.1.9" 626 | source = "registry+https://github.com/rust-lang/crates.io-index" 627 | checksum = "41296eb09f183ac68eec06e03cdbea2e759633d4067b2f6552fc2e009bcad08b" 628 | dependencies = [ 629 | "bytes", 630 | "futures-channel", 631 | "futures-util", 632 | "http", 633 | "http-body", 634 | "hyper", 635 | "pin-project-lite", 636 | "socket2", 637 | "tokio", 638 | "tower-service", 639 | "tracing", 640 | ] 641 | 642 | [[package]] 643 | name = "idna" 644 | version = "0.5.0" 645 | source = "registry+https://github.com/rust-lang/crates.io-index" 646 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 647 | dependencies = [ 648 | "unicode-bidi", 649 | "unicode-normalization", 650 | ] 651 | 652 | [[package]] 653 | name = "ipnet" 654 | version = "2.10.0" 655 | source = "registry+https://github.com/rust-lang/crates.io-index" 656 | checksum = "187674a687eed5fe42285b40c6291f9a01517d415fad1c3cbc6a9f778af7fcd4" 657 | 658 | [[package]] 659 | name = "is_terminal_polyfill" 660 | version = "1.70.1" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 663 | 664 | [[package]] 665 | name = "itoa" 666 | version = "1.0.11" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 669 | 670 | [[package]] 671 | name = "js-sys" 672 | version = "0.3.70" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 675 | dependencies = [ 676 | "wasm-bindgen", 677 | ] 678 | 679 | [[package]] 680 | name = "lazy_static" 681 | version = "1.5.0" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 684 | 685 | [[package]] 686 | name = "libc" 687 | version = "0.2.159" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" 690 | 691 | [[package]] 692 | name = "lock_api" 693 | version = "0.4.12" 694 | source = "registry+https://github.com/rust-lang/crates.io-index" 695 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 696 | dependencies = [ 697 | "autocfg", 698 | "scopeguard", 699 | ] 700 | 701 | [[package]] 702 | name = "log" 703 | version = "0.4.22" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 706 | 707 | [[package]] 708 | name = "matchit" 709 | version = "0.7.3" 710 | source = "registry+https://github.com/rust-lang/crates.io-index" 711 | checksum = "0e7465ac9959cc2b1404e8e2367b43684a6d13790fe23056cc8c6c5a6b7bcb94" 712 | 713 | [[package]] 714 | name = "memchr" 715 | version = "2.7.4" 716 | source = "registry+https://github.com/rust-lang/crates.io-index" 717 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 718 | 719 | [[package]] 720 | name = "mime" 721 | version = "0.3.17" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 724 | 725 | [[package]] 726 | name = "miniz_oxide" 727 | version = "0.8.0" 728 | source = "registry+https://github.com/rust-lang/crates.io-index" 729 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 730 | dependencies = [ 731 | "adler2", 732 | ] 733 | 734 | [[package]] 735 | name = "mio" 736 | version = "1.0.2" 737 | source = "registry+https://github.com/rust-lang/crates.io-index" 738 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 739 | dependencies = [ 740 | "hermit-abi", 741 | "libc", 742 | "wasi", 743 | "windows-sys 0.52.0", 744 | ] 745 | 746 | [[package]] 747 | name = "nu-ansi-term" 748 | version = "0.46.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" 751 | dependencies = [ 752 | "overload", 753 | "winapi", 754 | ] 755 | 756 | [[package]] 757 | name = "object" 758 | version = "0.36.4" 759 | source = "registry+https://github.com/rust-lang/crates.io-index" 760 | checksum = "084f1a5821ac4c651660a94a7153d27ac9d8a53736203f58b31945ded098070a" 761 | dependencies = [ 762 | "memchr", 763 | ] 764 | 765 | [[package]] 766 | name = "once_cell" 767 | version = "1.20.1" 768 | source = "registry+https://github.com/rust-lang/crates.io-index" 769 | checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" 770 | dependencies = [ 771 | "portable-atomic", 772 | ] 773 | 774 | [[package]] 775 | name = "overload" 776 | version = "0.1.1" 777 | source = "registry+https://github.com/rust-lang/crates.io-index" 778 | checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" 779 | 780 | [[package]] 781 | name = "parking_lot" 782 | version = "0.12.3" 783 | source = "registry+https://github.com/rust-lang/crates.io-index" 784 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 785 | dependencies = [ 786 | "lock_api", 787 | "parking_lot_core", 788 | ] 789 | 790 | [[package]] 791 | name = "parking_lot_core" 792 | version = "0.9.10" 793 | source = "registry+https://github.com/rust-lang/crates.io-index" 794 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 795 | dependencies = [ 796 | "cfg-if", 797 | "libc", 798 | "redox_syscall", 799 | "smallvec", 800 | "windows-targets", 801 | ] 802 | 803 | [[package]] 804 | name = "percent-encoding" 805 | version = "2.3.1" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 808 | 809 | [[package]] 810 | name = "pin-project-lite" 811 | version = "0.2.14" 812 | source = "registry+https://github.com/rust-lang/crates.io-index" 813 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 814 | 815 | [[package]] 816 | name = "pin-utils" 817 | version = "0.1.0" 818 | source = "registry+https://github.com/rust-lang/crates.io-index" 819 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 820 | 821 | [[package]] 822 | name = "portable-atomic" 823 | version = "1.9.0" 824 | source = "registry+https://github.com/rust-lang/crates.io-index" 825 | checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" 826 | 827 | [[package]] 828 | name = "ppv-lite86" 829 | version = "0.2.20" 830 | source = "registry+https://github.com/rust-lang/crates.io-index" 831 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 832 | dependencies = [ 833 | "zerocopy", 834 | ] 835 | 836 | [[package]] 837 | name = "proc-macro2" 838 | version = "1.0.86" 839 | source = "registry+https://github.com/rust-lang/crates.io-index" 840 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 841 | dependencies = [ 842 | "unicode-ident", 843 | ] 844 | 845 | [[package]] 846 | name = "quinn" 847 | version = "0.11.5" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "8c7c5fdde3cdae7203427dc4f0a68fe0ed09833edc525a03456b153b79828684" 850 | dependencies = [ 851 | "bytes", 852 | "pin-project-lite", 853 | "quinn-proto", 854 | "quinn-udp", 855 | "rustc-hash", 856 | "rustls", 857 | "socket2", 858 | "thiserror", 859 | "tokio", 860 | "tracing", 861 | ] 862 | 863 | [[package]] 864 | name = "quinn-proto" 865 | version = "0.11.8" 866 | source = "registry+https://github.com/rust-lang/crates.io-index" 867 | checksum = "fadfaed2cd7f389d0161bb73eeb07b7b78f8691047a6f3e73caaeae55310a4a6" 868 | dependencies = [ 869 | "bytes", 870 | "rand", 871 | "ring", 872 | "rustc-hash", 873 | "rustls", 874 | "slab", 875 | "thiserror", 876 | "tinyvec", 877 | "tracing", 878 | ] 879 | 880 | [[package]] 881 | name = "quinn-udp" 882 | version = "0.5.5" 883 | source = "registry+https://github.com/rust-lang/crates.io-index" 884 | checksum = "4fe68c2e9e1a1234e218683dbdf9f9dfcb094113c5ac2b938dfcb9bab4c4140b" 885 | dependencies = [ 886 | "libc", 887 | "once_cell", 888 | "socket2", 889 | "tracing", 890 | "windows-sys 0.59.0", 891 | ] 892 | 893 | [[package]] 894 | name = "quote" 895 | version = "1.0.37" 896 | source = "registry+https://github.com/rust-lang/crates.io-index" 897 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 898 | dependencies = [ 899 | "proc-macro2", 900 | ] 901 | 902 | [[package]] 903 | name = "rand" 904 | version = "0.8.5" 905 | source = "registry+https://github.com/rust-lang/crates.io-index" 906 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 907 | dependencies = [ 908 | "libc", 909 | "rand_chacha", 910 | "rand_core", 911 | ] 912 | 913 | [[package]] 914 | name = "rand_chacha" 915 | version = "0.3.1" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 918 | dependencies = [ 919 | "ppv-lite86", 920 | "rand_core", 921 | ] 922 | 923 | [[package]] 924 | name = "rand_core" 925 | version = "0.6.4" 926 | source = "registry+https://github.com/rust-lang/crates.io-index" 927 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 928 | dependencies = [ 929 | "getrandom", 930 | ] 931 | 932 | [[package]] 933 | name = "redox_syscall" 934 | version = "0.5.7" 935 | source = "registry+https://github.com/rust-lang/crates.io-index" 936 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 937 | dependencies = [ 938 | "bitflags", 939 | ] 940 | 941 | [[package]] 942 | name = "reqwest" 943 | version = "0.12.8" 944 | source = "registry+https://github.com/rust-lang/crates.io-index" 945 | checksum = "f713147fbe92361e52392c73b8c9e48c04c6625bce969ef54dc901e58e042a7b" 946 | dependencies = [ 947 | "async-compression", 948 | "base64", 949 | "bytes", 950 | "futures-core", 951 | "futures-util", 952 | "http", 953 | "http-body", 954 | "http-body-util", 955 | "hyper", 956 | "hyper-rustls", 957 | "hyper-util", 958 | "ipnet", 959 | "js-sys", 960 | "log", 961 | "mime", 962 | "once_cell", 963 | "percent-encoding", 964 | "pin-project-lite", 965 | "quinn", 966 | "rustls", 967 | "rustls-pemfile", 968 | "rustls-pki-types", 969 | "serde", 970 | "serde_json", 971 | "serde_urlencoded", 972 | "sync_wrapper 1.0.1", 973 | "tokio", 974 | "tokio-rustls", 975 | "tokio-util", 976 | "tower-service", 977 | "url", 978 | "wasm-bindgen", 979 | "wasm-bindgen-futures", 980 | "web-sys", 981 | "webpki-roots", 982 | "windows-registry", 983 | ] 984 | 985 | [[package]] 986 | name = "ring" 987 | version = "0.17.8" 988 | source = "registry+https://github.com/rust-lang/crates.io-index" 989 | checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" 990 | dependencies = [ 991 | "cc", 992 | "cfg-if", 993 | "getrandom", 994 | "libc", 995 | "spin", 996 | "untrusted", 997 | "windows-sys 0.52.0", 998 | ] 999 | 1000 | [[package]] 1001 | name = "rustc-demangle" 1002 | version = "0.1.24" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1005 | 1006 | [[package]] 1007 | name = "rustc-hash" 1008 | version = "2.0.0" 1009 | source = "registry+https://github.com/rust-lang/crates.io-index" 1010 | checksum = "583034fd73374156e66797ed8e5b0d5690409c9226b22d87cb7f19821c05d152" 1011 | 1012 | [[package]] 1013 | name = "rustls" 1014 | version = "0.23.13" 1015 | source = "registry+https://github.com/rust-lang/crates.io-index" 1016 | checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8" 1017 | dependencies = [ 1018 | "once_cell", 1019 | "ring", 1020 | "rustls-pki-types", 1021 | "rustls-webpki", 1022 | "subtle", 1023 | "zeroize", 1024 | ] 1025 | 1026 | [[package]] 1027 | name = "rustls-pemfile" 1028 | version = "2.2.0" 1029 | source = "registry+https://github.com/rust-lang/crates.io-index" 1030 | checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" 1031 | dependencies = [ 1032 | "rustls-pki-types", 1033 | ] 1034 | 1035 | [[package]] 1036 | name = "rustls-pki-types" 1037 | version = "1.9.0" 1038 | source = "registry+https://github.com/rust-lang/crates.io-index" 1039 | checksum = "0e696e35370c65c9c541198af4543ccd580cf17fc25d8e05c5a242b202488c55" 1040 | 1041 | [[package]] 1042 | name = "rustls-webpki" 1043 | version = "0.102.8" 1044 | source = "registry+https://github.com/rust-lang/crates.io-index" 1045 | checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" 1046 | dependencies = [ 1047 | "ring", 1048 | "rustls-pki-types", 1049 | "untrusted", 1050 | ] 1051 | 1052 | [[package]] 1053 | name = "rustversion" 1054 | version = "1.0.17" 1055 | source = "registry+https://github.com/rust-lang/crates.io-index" 1056 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1057 | 1058 | [[package]] 1059 | name = "ryu" 1060 | version = "1.0.18" 1061 | source = "registry+https://github.com/rust-lang/crates.io-index" 1062 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1063 | 1064 | [[package]] 1065 | name = "scopeguard" 1066 | version = "1.2.0" 1067 | source = "registry+https://github.com/rust-lang/crates.io-index" 1068 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1069 | 1070 | [[package]] 1071 | name = "serde" 1072 | version = "1.0.210" 1073 | source = "registry+https://github.com/rust-lang/crates.io-index" 1074 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1075 | dependencies = [ 1076 | "serde_derive", 1077 | ] 1078 | 1079 | [[package]] 1080 | name = "serde_derive" 1081 | version = "1.0.210" 1082 | source = "registry+https://github.com/rust-lang/crates.io-index" 1083 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 1084 | dependencies = [ 1085 | "proc-macro2", 1086 | "quote", 1087 | "syn", 1088 | ] 1089 | 1090 | [[package]] 1091 | name = "serde_json" 1092 | version = "1.0.128" 1093 | source = "registry+https://github.com/rust-lang/crates.io-index" 1094 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 1095 | dependencies = [ 1096 | "itoa", 1097 | "memchr", 1098 | "ryu", 1099 | "serde", 1100 | ] 1101 | 1102 | [[package]] 1103 | name = "serde_path_to_error" 1104 | version = "0.1.16" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" 1107 | dependencies = [ 1108 | "itoa", 1109 | "serde", 1110 | ] 1111 | 1112 | [[package]] 1113 | name = "serde_urlencoded" 1114 | version = "0.7.1" 1115 | source = "registry+https://github.com/rust-lang/crates.io-index" 1116 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1117 | dependencies = [ 1118 | "form_urlencoded", 1119 | "itoa", 1120 | "ryu", 1121 | "serde", 1122 | ] 1123 | 1124 | [[package]] 1125 | name = "sha-1" 1126 | version = "0.10.1" 1127 | source = "registry+https://github.com/rust-lang/crates.io-index" 1128 | checksum = "f5058ada175748e33390e40e872bd0fe59a19f265d0158daa551c5a88a76009c" 1129 | dependencies = [ 1130 | "cfg-if", 1131 | "cpufeatures", 1132 | "digest", 1133 | ] 1134 | 1135 | [[package]] 1136 | name = "sharded-slab" 1137 | version = "0.1.7" 1138 | source = "registry+https://github.com/rust-lang/crates.io-index" 1139 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 1140 | dependencies = [ 1141 | "lazy_static", 1142 | ] 1143 | 1144 | [[package]] 1145 | name = "shlex" 1146 | version = "1.3.0" 1147 | source = "registry+https://github.com/rust-lang/crates.io-index" 1148 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 1149 | 1150 | [[package]] 1151 | name = "signal-hook-registry" 1152 | version = "1.4.2" 1153 | source = "registry+https://github.com/rust-lang/crates.io-index" 1154 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1155 | dependencies = [ 1156 | "libc", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "siphasher" 1161 | version = "1.0.1" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" 1164 | 1165 | [[package]] 1166 | name = "slab" 1167 | version = "0.4.9" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1170 | dependencies = [ 1171 | "autocfg", 1172 | ] 1173 | 1174 | [[package]] 1175 | name = "smallvec" 1176 | version = "1.13.2" 1177 | source = "registry+https://github.com/rust-lang/crates.io-index" 1178 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1179 | 1180 | [[package]] 1181 | name = "socket2" 1182 | version = "0.5.7" 1183 | source = "registry+https://github.com/rust-lang/crates.io-index" 1184 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1185 | dependencies = [ 1186 | "libc", 1187 | "windows-sys 0.52.0", 1188 | ] 1189 | 1190 | [[package]] 1191 | name = "spin" 1192 | version = "0.9.8" 1193 | source = "registry+https://github.com/rust-lang/crates.io-index" 1194 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 1195 | 1196 | [[package]] 1197 | name = "strsim" 1198 | version = "0.11.1" 1199 | source = "registry+https://github.com/rust-lang/crates.io-index" 1200 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 1201 | 1202 | [[package]] 1203 | name = "subtle" 1204 | version = "2.6.1" 1205 | source = "registry+https://github.com/rust-lang/crates.io-index" 1206 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 1207 | 1208 | [[package]] 1209 | name = "syn" 1210 | version = "2.0.79" 1211 | source = "registry+https://github.com/rust-lang/crates.io-index" 1212 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 1213 | dependencies = [ 1214 | "proc-macro2", 1215 | "quote", 1216 | "unicode-ident", 1217 | ] 1218 | 1219 | [[package]] 1220 | name = "sync_wrapper" 1221 | version = "0.1.2" 1222 | source = "registry+https://github.com/rust-lang/crates.io-index" 1223 | checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" 1224 | 1225 | [[package]] 1226 | name = "sync_wrapper" 1227 | version = "1.0.1" 1228 | source = "registry+https://github.com/rust-lang/crates.io-index" 1229 | checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" 1230 | dependencies = [ 1231 | "futures-core", 1232 | ] 1233 | 1234 | [[package]] 1235 | name = "thiserror" 1236 | version = "1.0.64" 1237 | source = "registry+https://github.com/rust-lang/crates.io-index" 1238 | checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84" 1239 | dependencies = [ 1240 | "thiserror-impl", 1241 | ] 1242 | 1243 | [[package]] 1244 | name = "thiserror-impl" 1245 | version = "1.0.64" 1246 | source = "registry+https://github.com/rust-lang/crates.io-index" 1247 | checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3" 1248 | dependencies = [ 1249 | "proc-macro2", 1250 | "quote", 1251 | "syn", 1252 | ] 1253 | 1254 | [[package]] 1255 | name = "thread_local" 1256 | version = "1.1.8" 1257 | source = "registry+https://github.com/rust-lang/crates.io-index" 1258 | checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" 1259 | dependencies = [ 1260 | "cfg-if", 1261 | "once_cell", 1262 | ] 1263 | 1264 | [[package]] 1265 | name = "tinyvec" 1266 | version = "1.8.0" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 1269 | dependencies = [ 1270 | "tinyvec_macros", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "tinyvec_macros" 1275 | version = "0.1.1" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1278 | 1279 | [[package]] 1280 | name = "tokio" 1281 | version = "1.40.0" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 1284 | dependencies = [ 1285 | "backtrace", 1286 | "bytes", 1287 | "libc", 1288 | "mio", 1289 | "parking_lot", 1290 | "pin-project-lite", 1291 | "signal-hook-registry", 1292 | "socket2", 1293 | "tokio-macros", 1294 | "windows-sys 0.52.0", 1295 | ] 1296 | 1297 | [[package]] 1298 | name = "tokio-macros" 1299 | version = "2.4.0" 1300 | source = "registry+https://github.com/rust-lang/crates.io-index" 1301 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1302 | dependencies = [ 1303 | "proc-macro2", 1304 | "quote", 1305 | "syn", 1306 | ] 1307 | 1308 | [[package]] 1309 | name = "tokio-rustls" 1310 | version = "0.26.0" 1311 | source = "registry+https://github.com/rust-lang/crates.io-index" 1312 | checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" 1313 | dependencies = [ 1314 | "rustls", 1315 | "rustls-pki-types", 1316 | "tokio", 1317 | ] 1318 | 1319 | [[package]] 1320 | name = "tokio-util" 1321 | version = "0.7.12" 1322 | source = "registry+https://github.com/rust-lang/crates.io-index" 1323 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 1324 | dependencies = [ 1325 | "bytes", 1326 | "futures-core", 1327 | "futures-sink", 1328 | "pin-project-lite", 1329 | "tokio", 1330 | ] 1331 | 1332 | [[package]] 1333 | name = "tower" 1334 | version = "0.5.1" 1335 | source = "registry+https://github.com/rust-lang/crates.io-index" 1336 | checksum = "2873938d487c3cfb9aed7546dc9f2711d867c9f90c46b889989a2cb84eba6b4f" 1337 | dependencies = [ 1338 | "futures-core", 1339 | "futures-util", 1340 | "pin-project-lite", 1341 | "sync_wrapper 0.1.2", 1342 | "tokio", 1343 | "tower-layer", 1344 | "tower-service", 1345 | "tracing", 1346 | ] 1347 | 1348 | [[package]] 1349 | name = "tower-layer" 1350 | version = "0.3.3" 1351 | source = "registry+https://github.com/rust-lang/crates.io-index" 1352 | checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" 1353 | 1354 | [[package]] 1355 | name = "tower-service" 1356 | version = "0.3.3" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" 1359 | 1360 | [[package]] 1361 | name = "tracing" 1362 | version = "0.1.40" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1365 | dependencies = [ 1366 | "log", 1367 | "pin-project-lite", 1368 | "tracing-attributes", 1369 | "tracing-core", 1370 | ] 1371 | 1372 | [[package]] 1373 | name = "tracing-attributes" 1374 | version = "0.1.27" 1375 | source = "registry+https://github.com/rust-lang/crates.io-index" 1376 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1377 | dependencies = [ 1378 | "proc-macro2", 1379 | "quote", 1380 | "syn", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "tracing-core" 1385 | version = "0.1.32" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1388 | dependencies = [ 1389 | "once_cell", 1390 | "valuable", 1391 | ] 1392 | 1393 | [[package]] 1394 | name = "tracing-log" 1395 | version = "0.2.0" 1396 | source = "registry+https://github.com/rust-lang/crates.io-index" 1397 | checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" 1398 | dependencies = [ 1399 | "log", 1400 | "once_cell", 1401 | "tracing-core", 1402 | ] 1403 | 1404 | [[package]] 1405 | name = "tracing-subscriber" 1406 | version = "0.3.18" 1407 | source = "registry+https://github.com/rust-lang/crates.io-index" 1408 | checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" 1409 | dependencies = [ 1410 | "nu-ansi-term", 1411 | "sharded-slab", 1412 | "smallvec", 1413 | "thread_local", 1414 | "tracing-core", 1415 | "tracing-log", 1416 | ] 1417 | 1418 | [[package]] 1419 | name = "try-lock" 1420 | version = "0.2.5" 1421 | source = "registry+https://github.com/rust-lang/crates.io-index" 1422 | checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" 1423 | 1424 | [[package]] 1425 | name = "typenum" 1426 | version = "1.17.0" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1429 | 1430 | [[package]] 1431 | name = "unicode-bidi" 1432 | version = "0.3.15" 1433 | source = "registry+https://github.com/rust-lang/crates.io-index" 1434 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1435 | 1436 | [[package]] 1437 | name = "unicode-ident" 1438 | version = "1.0.13" 1439 | source = "registry+https://github.com/rust-lang/crates.io-index" 1440 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 1441 | 1442 | [[package]] 1443 | name = "unicode-normalization" 1444 | version = "0.1.24" 1445 | source = "registry+https://github.com/rust-lang/crates.io-index" 1446 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 1447 | dependencies = [ 1448 | "tinyvec", 1449 | ] 1450 | 1451 | [[package]] 1452 | name = "untrusted" 1453 | version = "0.9.0" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" 1456 | 1457 | [[package]] 1458 | name = "url" 1459 | version = "2.5.2" 1460 | source = "registry+https://github.com/rust-lang/crates.io-index" 1461 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 1462 | dependencies = [ 1463 | "form_urlencoded", 1464 | "idna", 1465 | "percent-encoding", 1466 | ] 1467 | 1468 | [[package]] 1469 | name = "utf8parse" 1470 | version = "0.2.2" 1471 | source = "registry+https://github.com/rust-lang/crates.io-index" 1472 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 1473 | 1474 | [[package]] 1475 | name = "valuable" 1476 | version = "0.1.0" 1477 | source = "registry+https://github.com/rust-lang/crates.io-index" 1478 | checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" 1479 | 1480 | [[package]] 1481 | name = "version_check" 1482 | version = "0.9.5" 1483 | source = "registry+https://github.com/rust-lang/crates.io-index" 1484 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1485 | 1486 | [[package]] 1487 | name = "want" 1488 | version = "0.3.1" 1489 | source = "registry+https://github.com/rust-lang/crates.io-index" 1490 | checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" 1491 | dependencies = [ 1492 | "try-lock", 1493 | ] 1494 | 1495 | [[package]] 1496 | name = "wasi" 1497 | version = "0.11.0+wasi-snapshot-preview1" 1498 | source = "registry+https://github.com/rust-lang/crates.io-index" 1499 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1500 | 1501 | [[package]] 1502 | name = "wasm-bindgen" 1503 | version = "0.2.93" 1504 | source = "registry+https://github.com/rust-lang/crates.io-index" 1505 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 1506 | dependencies = [ 1507 | "cfg-if", 1508 | "once_cell", 1509 | "wasm-bindgen-macro", 1510 | ] 1511 | 1512 | [[package]] 1513 | name = "wasm-bindgen-backend" 1514 | version = "0.2.93" 1515 | source = "registry+https://github.com/rust-lang/crates.io-index" 1516 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 1517 | dependencies = [ 1518 | "bumpalo", 1519 | "log", 1520 | "once_cell", 1521 | "proc-macro2", 1522 | "quote", 1523 | "syn", 1524 | "wasm-bindgen-shared", 1525 | ] 1526 | 1527 | [[package]] 1528 | name = "wasm-bindgen-futures" 1529 | version = "0.4.43" 1530 | source = "registry+https://github.com/rust-lang/crates.io-index" 1531 | checksum = "61e9300f63a621e96ed275155c108eb6f843b6a26d053f122ab69724559dc8ed" 1532 | dependencies = [ 1533 | "cfg-if", 1534 | "js-sys", 1535 | "wasm-bindgen", 1536 | "web-sys", 1537 | ] 1538 | 1539 | [[package]] 1540 | name = "wasm-bindgen-macro" 1541 | version = "0.2.93" 1542 | source = "registry+https://github.com/rust-lang/crates.io-index" 1543 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 1544 | dependencies = [ 1545 | "quote", 1546 | "wasm-bindgen-macro-support", 1547 | ] 1548 | 1549 | [[package]] 1550 | name = "wasm-bindgen-macro-support" 1551 | version = "0.2.93" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 1554 | dependencies = [ 1555 | "proc-macro2", 1556 | "quote", 1557 | "syn", 1558 | "wasm-bindgen-backend", 1559 | "wasm-bindgen-shared", 1560 | ] 1561 | 1562 | [[package]] 1563 | name = "wasm-bindgen-shared" 1564 | version = "0.2.93" 1565 | source = "registry+https://github.com/rust-lang/crates.io-index" 1566 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 1567 | 1568 | [[package]] 1569 | name = "web-sys" 1570 | version = "0.3.70" 1571 | source = "registry+https://github.com/rust-lang/crates.io-index" 1572 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 1573 | dependencies = [ 1574 | "js-sys", 1575 | "wasm-bindgen", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "webpki-roots" 1580 | version = "0.26.6" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958" 1583 | dependencies = [ 1584 | "rustls-pki-types", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "winapi" 1589 | version = "0.3.9" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1592 | dependencies = [ 1593 | "winapi-i686-pc-windows-gnu", 1594 | "winapi-x86_64-pc-windows-gnu", 1595 | ] 1596 | 1597 | [[package]] 1598 | name = "winapi-i686-pc-windows-gnu" 1599 | version = "0.4.0" 1600 | source = "registry+https://github.com/rust-lang/crates.io-index" 1601 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1602 | 1603 | [[package]] 1604 | name = "winapi-x86_64-pc-windows-gnu" 1605 | version = "0.4.0" 1606 | source = "registry+https://github.com/rust-lang/crates.io-index" 1607 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1608 | 1609 | [[package]] 1610 | name = "windows-registry" 1611 | version = "0.2.0" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" 1614 | dependencies = [ 1615 | "windows-result", 1616 | "windows-strings", 1617 | "windows-targets", 1618 | ] 1619 | 1620 | [[package]] 1621 | name = "windows-result" 1622 | version = "0.2.0" 1623 | source = "registry+https://github.com/rust-lang/crates.io-index" 1624 | checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" 1625 | dependencies = [ 1626 | "windows-targets", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "windows-strings" 1631 | version = "0.1.0" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" 1634 | dependencies = [ 1635 | "windows-result", 1636 | "windows-targets", 1637 | ] 1638 | 1639 | [[package]] 1640 | name = "windows-sys" 1641 | version = "0.52.0" 1642 | source = "registry+https://github.com/rust-lang/crates.io-index" 1643 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1644 | dependencies = [ 1645 | "windows-targets", 1646 | ] 1647 | 1648 | [[package]] 1649 | name = "windows-sys" 1650 | version = "0.59.0" 1651 | source = "registry+https://github.com/rust-lang/crates.io-index" 1652 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 1653 | dependencies = [ 1654 | "windows-targets", 1655 | ] 1656 | 1657 | [[package]] 1658 | name = "windows-targets" 1659 | version = "0.52.6" 1660 | source = "registry+https://github.com/rust-lang/crates.io-index" 1661 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 1662 | dependencies = [ 1663 | "windows_aarch64_gnullvm", 1664 | "windows_aarch64_msvc", 1665 | "windows_i686_gnu", 1666 | "windows_i686_gnullvm", 1667 | "windows_i686_msvc", 1668 | "windows_x86_64_gnu", 1669 | "windows_x86_64_gnullvm", 1670 | "windows_x86_64_msvc", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "windows_aarch64_gnullvm" 1675 | version = "0.52.6" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 1678 | 1679 | [[package]] 1680 | name = "windows_aarch64_msvc" 1681 | version = "0.52.6" 1682 | source = "registry+https://github.com/rust-lang/crates.io-index" 1683 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 1684 | 1685 | [[package]] 1686 | name = "windows_i686_gnu" 1687 | version = "0.52.6" 1688 | source = "registry+https://github.com/rust-lang/crates.io-index" 1689 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 1690 | 1691 | [[package]] 1692 | name = "windows_i686_gnullvm" 1693 | version = "0.52.6" 1694 | source = "registry+https://github.com/rust-lang/crates.io-index" 1695 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 1696 | 1697 | [[package]] 1698 | name = "windows_i686_msvc" 1699 | version = "0.52.6" 1700 | source = "registry+https://github.com/rust-lang/crates.io-index" 1701 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 1702 | 1703 | [[package]] 1704 | name = "windows_x86_64_gnu" 1705 | version = "0.52.6" 1706 | source = "registry+https://github.com/rust-lang/crates.io-index" 1707 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 1708 | 1709 | [[package]] 1710 | name = "windows_x86_64_gnullvm" 1711 | version = "0.52.6" 1712 | source = "registry+https://github.com/rust-lang/crates.io-index" 1713 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 1714 | 1715 | [[package]] 1716 | name = "windows_x86_64_msvc" 1717 | version = "0.52.6" 1718 | source = "registry+https://github.com/rust-lang/crates.io-index" 1719 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 1720 | 1721 | [[package]] 1722 | name = "zerocopy" 1723 | version = "0.7.35" 1724 | source = "registry+https://github.com/rust-lang/crates.io-index" 1725 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 1726 | dependencies = [ 1727 | "byteorder", 1728 | "zerocopy-derive", 1729 | ] 1730 | 1731 | [[package]] 1732 | name = "zerocopy-derive" 1733 | version = "0.7.35" 1734 | source = "registry+https://github.com/rust-lang/crates.io-index" 1735 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 1736 | dependencies = [ 1737 | "proc-macro2", 1738 | "quote", 1739 | "syn", 1740 | ] 1741 | 1742 | [[package]] 1743 | name = "zeroize" 1744 | version = "1.8.1" 1745 | source = "registry+https://github.com/rust-lang/crates.io-index" 1746 | checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" 1747 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = [ 5 | "downloader", 6 | "easypwned", 7 | "easypwned_bloom", 8 | ] -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM easybill/easypwned_bloom_001:v4 AS bloom 2 | 3 | FROM ubuntu:24.04 4 | COPY --from=bloom /easypwned.bloom /easypwned.bloom 5 | ADD binary_easypwned /easypwned 6 | ADD binary_easypwned_haveibeenpwned_downloader /easypwned_haveibeenpwned_downloader 7 | ENTRYPOINT ["/easypwned"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 easybill GmbH 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build_bloom: 2 | cargo run --release --bin easypwned_haveibeenpwned_downloader -- --sink-bloom-file=easypwned.bloom 3 | 4 | run_release: 5 | cargo run --release --bin easypwned 6 | 7 | build-easypwned_bloom: 8 | cp easypwned.bloom .docker/easypwned_bloom_001/easypwned.bloom 9 | cd .docker/easypwned_bloom_001 && docker build -t easybill/easypwned_bloom_001:v4 . 10 | rm .docker/easypwned_bloom_001/easypwned.bloom 11 | docker push easybill/easypwned_bloom_001:v4 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # easypwned (haveibeenpwned / HIBP) 2 | Rest API to check if a password is in a data breach. Works offline - everything stays on your machine! Database is included. 3 | We also provide a [downloader for the hibp database](https://github.com/easybill/easypwned#download-the-haveibeenpwned--hibp-database-pwnedpasswordsdownloader). 4 | 5 | ## Example 6 | The simplest way to run it is using docker: 7 | 8 | ```bash 9 | docker run --rm --network=host easybill/easypwned:v0.0.26 10 | curl http://127.0.0.1:3342/pw/[BLANK_PASSWORD] # use /hash/SHA1 in prod apps (pw/[PW] is for testing). 11 | curl http://127.0.0.1:3342/hash/0000001C5F765AA063E4F8470451F85F7DB4ED3A # << UPPERCASE(SHA1(PLAINTEXT)), 12 | ``` 13 | The [dockerimage](https://hub.docker.com/r/easybill/easypwned) comes with a list of compromised passwords in the form of a [bloomfilter](https://en.wikipedia.org/wiki/Bloom_filter) (~ 1GB). 14 | 15 | ## Is it safe? 16 | Easypwned does not need external network access. passwords and hashes are never leaving your server. 17 | Use the `/hash/[SHA1]` endpoint in production to avoid sending them through the network stack. 18 | 19 | ## How it works 20 | Easypwned checks passwords based on the password list provided by [haveibeenpwned](https://haveibeenpwned.com/Passwords). 21 | We use a bloomfilter, so it is freaking fast. The bloomfilter is generated with a chance of 1% that you get false positives. 22 | 23 | ## Endpoints 24 | ### /pw/[blank_password] 25 | You'll get a `"secure":true` if the password is not breached. 26 | use the /hash/ endpoint in production instead! 27 | ``` 28 | curl http://127.0.0.1:3342/pw/test 29 | {"hash":"A94A8FE5CCB19BA61C4C0873D391E987982FBBD3","pw":"test","secure":false} 30 | ``` 31 | ### /hash/[UPPERCASE(SHA1(blank_password))] 32 | You'll get a `"secure":true` if the password is not breached. 33 | 34 | ``` 35 | curl http://127.0.0.1:3342/hash/0000000CAEF405439D57847A8657218C618160B2 36 | {"hash":"A94A8FE5CCB19BA61C4C0873D391E987982FBBD3","pw":"test","secure":false} 37 | ``` 38 | 39 | ### /check (POST) 40 | In prod prefer POST, some tracing / logging / debug libs like to collecting url parameters. 41 | 42 | ``` 43 | curl -X POST http://127.0.0.1:3342/check -H 'Content-Type: application/json' -d '{"hash": "0000001C5F765AA063E4F8470451F85F7DB4ED3X"}' 44 | 45 | ``` 46 | 47 | 48 | 49 | php example 50 | ```php 51 | (new \GuzzleHttp\Client(['base_uri' => 'localhost:3342']))->get('/hash/' . mb_strtoupper(sha1($password))); 52 | ``` 53 | 54 | ## Using without docker 55 | We build Binaries for Linux ([arm64](https://github.com/easybill/easypwned/releases/latest/download/easypwned_aarch64-unknown-linux-musl), [x86](https://github.com/easybill/easypwned/releases/latest/download/easypwned_x86_64-unknown-linux-musl 56 | )) and OSX ([arm64](https://github.com/easybill/easypwned/releases/latest/download/easypwned_aarch64-apple-darwin), [x86](https://github.com/easybill/easypwned/releases/latest/download/easypwned_x86_64-apple-darwin)). 57 | If you use the Binaries you need to provide the bloom filter. You could extract it from the docker container or build it on your own. 58 | 59 | ## Download the haveibeenpwned / HIBP Database (PwnedPasswordsDownloader) 60 | 61 | We also provide a downloader for the haveibeenpwned / HIBP Database, you can build the downloader on your own or use out pre build binaries for Linux ([arm64](https://github.com/easybill/easypwned/releases/latest/download/easypwned_haveibeenpwned_downloader_aarch64-unknown-linux-musl), [x86](https://github.com/easybill/easypwned/releases/latest/download/easypwned_haveibeenpwned_downloader_x86_64-unknown-linux-musl 62 | )) and OSX ([arm64](https://github.com/easybill/easypwned/releases/latest/download/easypwned_haveibeenpwned_downloader_aarch64-apple-darwin), [x86](https://github.com/easybill/easypwned/releases/latest/download/easypwned_haveibeenpwned_downloader_x86_64-apple-darwin)) 63 | 64 | there is also an [official downloader (PwnedPasswordsDownloader)](https://github.com/HaveIBeenPwned/PwnedPasswordsDownloader) but it is written in c# has no pre build binaries and no support for building bloom filters on the fly. 65 | 66 | If you download the hibp database multiple times your file would end up with different file hashes. 67 | The order of the data will be different. the downloader needs do around a million http requests and the order of the incoming data 68 | is directly piped to the output. You can adjust the number of the parallel requests using the argument `--parallel`. the default value is 60. 69 | 70 | Download as Text File: 71 | ```bash 72 | ./easypwned_haveibeenpwned_downloader_aarch64-apple-darwin --sink-stdout 73 | 74 | // you may want to pipe it to a file ... 75 | ./easypwned_haveibeenpwned_downloader_aarch64-apple-darwin --sink-stdout > hibp.txt 76 | ``` 77 | 78 | Download and Create Bloom File 79 | ```bash 80 | ./easypwned_haveibeenpwned_downloader_aarch64-apple-darwin --sink-bloom-file=easypwned.bloom 81 | ``` 82 | 83 | ## How fast is it? 84 | i don't exacly know, but it's very fast. I just did some very basic benchmarks on my m1 studio + apache benchmark. 85 | It is probably much faster in real world scenarios. 86 | ``` 87 | Requests per second: 24505.60 [#/sec] (mean) 88 | Time per request: 4.081 [ms] (mean) 89 | Time per request: 0.041 [ms] (mean, across all concurrent requests) 90 | ``` -------------------------------------------------------------------------------- /downloader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "easypwned_haveibeenpwned_downloader" 3 | version = "0.1.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 | bloomfilter = "1.*" 10 | anyhow = "*" 11 | tokio = { version = "1.*", features = ["full"]} 12 | tracing = "0.1.*" 13 | tracing-subscriber = "0.3.*" 14 | serde_derive = "1.0.*" 15 | clap = { version = "4.*", features = ["derive"] } 16 | byteorder = "1.*" 17 | hex = "0.4.1" 18 | reqwest = { version = "0.*", default-features = false, features = ["rustls-tls", "gzip", "brotli", "deflate"] } 19 | easypwned_bloom = {path = "../easypwned_bloom"} 20 | bincode = { version = "1.3.3"} -------------------------------------------------------------------------------- /downloader/src/download_coordinator.rs: -------------------------------------------------------------------------------- 1 | use tokio::sync::mpsc::{Receiver, Sender}; 2 | use tokio::task::JoinHandle; 3 | use crate::downloader_http::{DownloaderCommanderMsgRequest, DownloaderCommanderMsgWork}; 4 | use crate::sink::SinkMsg; 5 | 6 | const RANGES : u32 = 1024*1024; 7 | 8 | pub struct DownloadCoordinator { 9 | sinks: Vec>, 10 | recv: Receiver, 11 | current_range: u32, 12 | resolved_ranges: u32, 13 | } 14 | 15 | impl DownloadCoordinator { 16 | pub fn spawn(sinks : Vec>) -> (JoinHandle<()>, Sender) { 17 | 18 | let (send, recv) = ::tokio::sync::mpsc::channel(10_000); 19 | 20 | let jh = ::tokio::spawn(async move { 21 | (Self { 22 | recv, 23 | current_range: 0, 24 | resolved_ranges: 0, 25 | sinks 26 | }).run().await; 27 | }); 28 | 29 | (jh, send) 30 | } 31 | 32 | pub async fn run(&mut self) { 33 | loop { 34 | let msg = match self.recv.recv().await { 35 | Some(s) => s, 36 | None => continue, 37 | }; 38 | 39 | match msg { 40 | DownloaderCommanderMsgRequest::AskForWork(sender) => { 41 | 42 | let msg_work = if self.current_range <= RANGES - 1 { 43 | self.current_range += 1; 44 | Some(DownloaderCommanderMsgWork { range: self.current_range -1}) 45 | } else { 46 | None 47 | }; 48 | 49 | sender.send(msg_work).expect("could not send work"); 50 | }, 51 | DownloaderCommanderMsgRequest::SendWork(w) => { 52 | 53 | for sink in &self.sinks { 54 | sink.send(SinkMsg::Data(w.prefix.clone(), w.bytes.clone())).await.expect("sink was killed"); 55 | } 56 | 57 | if self.resolved_ranges % 1000 == 0 { 58 | eprintln!("{}/{} - {}%", self.resolved_ranges, RANGES, self.resolved_ranges as f64 / RANGES as f64 * 100.0); 59 | } 60 | 61 | self.resolved_ranges += 1; 62 | 63 | if self.resolved_ranges >= RANGES { 64 | for sink in &self.sinks { 65 | match sink.send(SinkMsg::Finish).await { 66 | Ok(_) => {}, 67 | Err(_e) => {} 68 | }; 69 | } 70 | } 71 | } 72 | }; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /downloader/src/downloader_http.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use anyhow::Context; 3 | use tokio::sync::mpsc::Sender; 4 | use byteorder::WriteBytesExt; 5 | use reqwest::Client; 6 | 7 | #[derive(Debug)] 8 | pub struct DownloaderCommanderMsgWorkResult { 9 | pub range: u32, 10 | pub prefix: String, 11 | pub bytes: Vec, 12 | } 13 | 14 | #[derive(Debug)] 15 | pub struct DownloaderCommanderMsgWork { 16 | pub range : u32, 17 | } 18 | 19 | #[derive(Debug)] 20 | pub enum DownloaderCommanderMsgResponse { 21 | AskForWork(Option) 22 | } 23 | #[derive(Debug)] 24 | pub enum DownloaderCommanderMsgRequest { 25 | AskForWork(::tokio::sync::oneshot::Sender>), 26 | SendWork(DownloaderCommanderMsgWorkResult) 27 | } 28 | 29 | #[derive(Debug)] 30 | pub struct DownloaderHttp { 31 | commander: Sender, 32 | } 33 | 34 | impl DownloaderHttp { 35 | 36 | pub fn spawn(commander: Sender) { 37 | ::tokio::spawn(async move { 38 | (Self {commander}).run().await.expect("downloader crashed"); 39 | }); 40 | } 41 | 42 | pub async fn run(&mut self) -> Result<(), ()> { 43 | let client = reqwest::ClientBuilder::new() 44 | .brotli(true) 45 | .gzip(true) 46 | .deflate(true) 47 | .timeout(Duration::from_secs(10)) 48 | .tcp_keepalive(Duration::from_secs(100)) 49 | .danger_accept_invalid_certs(true) 50 | .build().expect("could not build client"); 51 | 52 | loop { 53 | 54 | let (response_sender, response_recv) = ::tokio::sync::oneshot::channel(); 55 | 56 | let _msg = self.commander.send( 57 | DownloaderCommanderMsgRequest::AskForWork(response_sender) 58 | ).await; 59 | 60 | let mut work = match response_recv.await { 61 | Ok(v) => match v { 62 | Some(v) => v, 63 | None => { 64 | // all done. 65 | return Ok(()) 66 | } 67 | }, 68 | Err(_e) => { 69 | // all done. coordinator does not exist anymore. 70 | return Ok(()); 71 | } 72 | }; 73 | 74 | loop { 75 | match self.do_work(&mut work, &client).await { 76 | Ok(_) => break, 77 | Err(_e) => { 78 | eprint!("could not fetch work {}, retry...", work.range); 79 | ::tokio::time::sleep(::tokio::time::Duration::from_secs(1)).await; 80 | } 81 | } 82 | } 83 | } 84 | } 85 | 86 | pub fn build_hash(id : u32) -> String { 87 | let mut buf = vec![]; 88 | buf.write_u32::<::byteorder::BigEndian>(id).expect("invalid write u32"); 89 | ::hex::encode(&buf[1..])[1..].to_uppercase() 90 | } 91 | 92 | pub async fn do_work(&mut self, work : &mut DownloaderCommanderMsgWork, client : &Client) -> Result<(), ::anyhow::Error> { 93 | let hash = Self::build_hash(work.range); 94 | 95 | let body = client 96 | .get(format!("https://api.pwnedpasswords.com/range/{}", hash)) 97 | .send() 98 | .await.context("could not fetch hash")? 99 | .bytes() 100 | .await.context("could not decode response")?; 101 | 102 | match self.commander.send(DownloaderCommanderMsgRequest::SendWork( 103 | DownloaderCommanderMsgWorkResult { 104 | range: work.range, 105 | prefix: hash.to_string(), 106 | bytes: body.to_vec(), 107 | } 108 | )).await { 109 | Ok(_) => {}, 110 | Err(e) => { 111 | eprintln!("could not send work result: {:?}", e); 112 | panic!("could not send work result"); 113 | } 114 | } 115 | 116 | Ok(()) 117 | } 118 | } -------------------------------------------------------------------------------- /downloader/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | 3 | use crate::download_coordinator::DownloadCoordinator; 4 | use crate::downloader_http::DownloaderHttp; 5 | use crate::sink::bloom::SinkBloom; 6 | use crate::sink::stdout::SinkStdout; 7 | 8 | pub mod download_coordinator; 9 | pub mod downloader_http; 10 | pub mod sink; 11 | 12 | #[derive(Clone)] 13 | pub struct DownloadConfig { 14 | pub opt: Opt, 15 | pub number_of_downloader: u32, 16 | } 17 | 18 | 19 | #[derive(Parser, Debug, Clone)] 20 | pub struct Opt { 21 | #[arg(long = "sink-bloom-file")] 22 | sink_bloom_file: Option, 23 | #[arg(long = "sink-stdout", default_value_t = false)] 24 | sink_stdout: bool, 25 | #[arg(long = "parallel", default_value = "60")] 26 | parallel: u32, 27 | } 28 | 29 | pub async fn download(config: DownloadConfig) { 30 | 31 | let (sinks_jhs, sinks_senders) = { 32 | let mut jhs = vec![]; 33 | let mut senders = vec![]; 34 | 35 | match config.opt.sink_stdout { 36 | true => { 37 | 38 | let (jh, sender) = SinkStdout::spawn(); 39 | jhs.push(jh); 40 | senders.push(sender); 41 | }, 42 | false => {} 43 | }; 44 | 45 | match config.opt.sink_bloom_file { 46 | Some(ref _v) => { 47 | let (jh, sender) = SinkBloom::spawn(config.clone()); 48 | jhs.push(jh); 49 | senders.push(sender); 50 | }, 51 | None => {} 52 | }; 53 | 54 | (jhs, senders) 55 | }; 56 | 57 | if sinks_jhs.len() == 0 { 58 | eprintln!("you need to define a sink, try --sink_stdout"); 59 | return; 60 | } 61 | 62 | let (_coordinator_jh, coordinator) = DownloadCoordinator::spawn( 63 | sinks_senders 64 | ); 65 | 66 | for _i in 0..config.number_of_downloader { 67 | DownloaderHttp::spawn(coordinator.clone()); 68 | } 69 | 70 | for jh in sinks_jhs { 71 | jh.await.expect("sink crashed"); 72 | eprintln!("finish sink") 73 | } 74 | } 75 | 76 | #[tokio::main] 77 | async fn main() -> ::anyhow::Result<(), ::anyhow::Error> { 78 | 79 | let opt: Opt = Opt::parse(); 80 | 81 | let download_config = DownloadConfig { 82 | number_of_downloader: opt.parallel, 83 | opt, 84 | }; 85 | 86 | download(download_config).await; 87 | 88 | return Ok(()); 89 | } 90 | -------------------------------------------------------------------------------- /downloader/src/sink/bloom.rs: -------------------------------------------------------------------------------- 1 | use ::tokio::sync::mpsc::{Sender, Receiver}; 2 | use anyhow::anyhow; 3 | use bloomfilter::Bloom; 4 | use tokio::io::AsyncWriteExt; 5 | use tokio::task::JoinHandle; 6 | use easypwned_bloom::bloom::BloomWithMetadata; 7 | use crate::DownloadConfig; 8 | use crate::sink::SinkMsg; 9 | 10 | pub struct SinkBloom { 11 | config : DownloadConfig, 12 | recv: Receiver 13 | } 14 | 15 | impl SinkBloom { 16 | pub fn spawn(config : DownloadConfig) -> (JoinHandle<()>, Sender) { 17 | let (sender, recv) = ::tokio::sync::mpsc::channel(50_000); 18 | 19 | let jh = ::tokio::spawn(async move { 20 | (Self { recv, config }).run().await.expect("stdout sink crashed."); 21 | }); 22 | 23 | (jh, sender) 24 | } 25 | 26 | pub async fn run(&mut self) -> Result<(), ()> { 27 | 28 | let mut bloom : Bloom<[u8]> = Bloom::new_for_fp_rate(1_010_000_000, 0.01); 29 | 30 | loop { 31 | match self.recv.recv().await { 32 | None => continue, 33 | Some(s) => match s { 34 | SinkMsg::Finish => { 35 | self.finish(&mut bloom).await.expect("could not write bloom file"); 36 | return Ok(()) 37 | }, 38 | SinkMsg::Data(prefix, data) => { 39 | self.process_data(prefix, &mut bloom, data).expect("could not parse data"); 40 | } 41 | } 42 | }; 43 | } 44 | } 45 | 46 | pub async fn finish(&self, bloom: &mut Bloom<[u8]>) -> Result<(), ::anyhow::Error> { 47 | 48 | eprintln!("start writing bloom filter."); 49 | 50 | let bincode_with_metadata = BloomWithMetadata { 51 | number_of_bits: bloom.number_of_bits(), 52 | number_of_hash_functions: bloom.number_of_hash_functions(), 53 | sip_keys: bloom.sip_keys(), 54 | bloom: bloom.bitmap().to_vec(), 55 | }; 56 | 57 | let bloom_file = self.config.opt.sink_bloom_file.as_ref().expect("must be given"); 58 | 59 | let mut bloomfile = ::tokio::fs::File::create(bloom_file).await?; 60 | bloomfile.write_all( 61 | bincode::serialize(&bincode_with_metadata) 62 | .expect("could not bincode") 63 | .as_slice(), 64 | ).await?; 65 | 66 | eprintln!("finished writing bloom filter."); 67 | 68 | Ok(()) 69 | } 70 | 71 | pub fn process_data(&mut self, prefix: String, bloom: &mut Bloom<[u8]>, data: Vec) -> Result<(), ::anyhow::Error> { 72 | let data_string = match String::from_utf8(data) { 73 | Ok(v) => v, 74 | Err(_e) => { 75 | return Err(anyhow!("invalid utf8")); 76 | } 77 | }; 78 | 79 | for line in data_string.lines() { 80 | 81 | if line.trim() == "" { 82 | continue; 83 | } 84 | 85 | let hash = match line.split_once(":") { 86 | Some((hash, _often)) => { 87 | let mut s = prefix.clone(); 88 | s.push_str(hash); 89 | s 90 | }, 91 | _ => return Err(anyhow!("invalid hash pattern")), 92 | }; 93 | 94 | bloom.set(hash.as_bytes()); 95 | } 96 | 97 | Ok(()) 98 | } 99 | } -------------------------------------------------------------------------------- /downloader/src/sink/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod stdout; 2 | pub mod bloom; 3 | 4 | #[derive(Debug)] 5 | pub enum SinkMsg { 6 | Data(String, Vec), 7 | Finish, 8 | } -------------------------------------------------------------------------------- /downloader/src/sink/stdout.rs: -------------------------------------------------------------------------------- 1 | 2 | use ::tokio::sync::mpsc::{Sender, Receiver}; 3 | use tokio::io::AsyncWriteExt; 4 | use tokio::task::JoinHandle; 5 | use crate::sink::SinkMsg; 6 | 7 | pub struct SinkStdout { 8 | recv: Receiver 9 | } 10 | 11 | impl SinkStdout { 12 | pub fn spawn() -> (JoinHandle<()>, Sender) { 13 | 14 | let (sender, recv) = ::tokio::sync::mpsc::channel(1000); 15 | 16 | let jh = ::tokio::spawn(async move { 17 | (Self {recv}).run().await.expect("stdout sink crashed."); 18 | }); 19 | 20 | (jh, sender) 21 | } 22 | 23 | pub async fn run(&mut self) -> Result<(), ()> { 24 | 25 | let mut stdout = ::tokio::io::stdout(); 26 | 27 | loop { 28 | match self.recv.recv().await { 29 | None => continue, 30 | Some(s) => match s { 31 | SinkMsg::Finish => return Ok(()), 32 | SinkMsg::Data(prefix, data) => { 33 | 34 | let new_data = String::from_utf8(data) 35 | .expect("invalid data") 36 | .lines() 37 | .map(|line| { 38 | let mut l = prefix.clone(); 39 | l.push_str(line); 40 | l 41 | }) 42 | .collect::>() 43 | .join("\n") 44 | ; 45 | 46 | stdout.write_all(new_data.as_bytes()).await.expect("could not write to stout"); 47 | stdout.write_all("\n".as_bytes()).await.expect("could not write to stout"); 48 | } 49 | } 50 | }; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /easypwned/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "easypwned" 3 | version = "0.1.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 | anyhow = "*" 10 | axum = "0.7.*" 11 | tokio = { version = "1.*", features = ["full"]} 12 | serde = "1.*" 13 | serde_json = "1.*" 14 | serde_derive = "1.*" 15 | tracing = "0.1.*" 16 | sha-1 = "0.*" 17 | base16ct = { version = "0.2", features = ["alloc", "std"]} 18 | clap = { version = "4.*", features = ["derive"] } 19 | easypwned_bloom = {path = "../easypwned_bloom"} -------------------------------------------------------------------------------- /easypwned/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::future::IntoFuture; 2 | use axum::extract::{Extension, Path}; 3 | use axum::{routing::{get}, Json, Router, extract}; 4 | 5 | 6 | use serde_json::{json, Value}; 7 | use sha1::{Digest, Sha1}; 8 | use std::net::SocketAddr; 9 | 10 | use std::sync::Arc; 11 | use axum::routing::post; 12 | use clap::Parser; 13 | use serde_derive::Deserialize; 14 | use tokio::net::TcpListener; 15 | use tokio::signal::unix::{signal, SignalKind}; 16 | use easypwned_bloom::bloom::{bloom_get, EasyBloom}; 17 | 18 | #[derive(Parser, Debug)] 19 | pub struct Opt { 20 | #[arg(long = "bloomfile", default_value = "easypwned.bloom")] 21 | bloomfile: String, 22 | #[arg(long = "bind", default_value = "0.0.0.0:3342")] 23 | bind: String, 24 | } 25 | 26 | #[tokio::main] 27 | async fn main() -> ::anyhow::Result<(), ::anyhow::Error> { 28 | 29 | let opt: Opt = Opt::parse(); 30 | 31 | println!("reading bloom filter file {}", &opt.bloomfile); 32 | let bloom = match bloom_get(&opt.bloomfile) { 33 | Ok(b) => b, 34 | Err(e) => { 35 | println!("could not get bloom {}", e); 36 | panic!(); 37 | } 38 | }; 39 | println!("finished reading bloom filter file {}", &opt.bloomfile); 40 | 41 | let bloom = bloom.to_bloom(); 42 | 43 | let checks = vec![ 44 | "0000000CAEF405439D57847A8657218C618160B2", 45 | "0000000CAEF405439D57847A8657218C618160BX", 46 | ]; 47 | 48 | for check in checks { 49 | println!( 50 | "check: {} -> {:?}", 51 | check, 52 | bloom.check(&check.as_bytes().to_vec()) 53 | ); 54 | } 55 | 56 | let bloom_ext = Arc::new(bloom); 57 | let app = Router::new() 58 | .route("/hash/:hash", get(handler_hash)) 59 | .route("/pw/:pw", get(handler_pw)) 60 | .route("/check", post(handler_check)) 61 | .layer(Extension(bloom_ext)); 62 | 63 | let addr = opt.bind.parse::().expect(""); 64 | println!("listening on {}", addr); 65 | 66 | let listener = TcpListener::bind(&addr).await?; 67 | let axum_handle = axum::serve(listener, app); 68 | 69 | let mut sig_quit = signal(SignalKind::quit())?; 70 | let mut sig_term = signal(SignalKind::terminate())?; 71 | 72 | ::tokio::select! { 73 | axum = axum_handle.into_future() => { 74 | axum?; 75 | panic!("axum quitted") 76 | }, 77 | _ = sig_quit.recv() => { 78 | println!("Signal quit, quit."); 79 | }, 80 | _ = sig_term.recv() => { 81 | println!("Signal term, quit."); 82 | } 83 | _ = ::tokio::signal::ctrl_c() => { 84 | println!("Signal ctrl_c, quit."); 85 | } 86 | } 87 | 88 | Ok(()) 89 | } 90 | 91 | async fn handler_hash( 92 | Extension(bloom): Extension>, 93 | Path(hash): Path, 94 | ) -> Json { 95 | let check = bloom.check(&hash.as_bytes().to_vec()); 96 | Json(json!({ 97 | "hash": hash, 98 | "secure": !check, 99 | })) 100 | } 101 | 102 | #[derive(Deserialize)] 103 | struct CheckRequestBody { 104 | hash: String, 105 | } 106 | 107 | async fn handler_check( 108 | Extension(bloom): Extension>, 109 | extract::Json(payload): extract::Json, 110 | ) -> Json { 111 | let check = bloom.check(&payload.hash.as_bytes().to_vec()); 112 | Json(json!({ 113 | "hash": payload.hash, 114 | "secure": !check, 115 | })) 116 | } 117 | 118 | async fn handler_pw( 119 | Extension(bloom): Extension>, 120 | Path(pw): Path, 121 | ) -> Json { 122 | let mut hasher = Sha1::new(); 123 | hasher.update(pw.as_bytes()); 124 | let hash_raw = &hasher.finalize(); 125 | let hash = base16ct::lower::encode_string(hash_raw).to_uppercase(); 126 | 127 | let check = bloom.check(&hash.as_bytes().to_vec()); 128 | Json(json!({ 129 | "pw": pw, 130 | "hash": hash, 131 | "secure": !check, 132 | })) 133 | } 134 | -------------------------------------------------------------------------------- /easypwned_bloom/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "easypwned_bloom" 3 | version = "0.1.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 | bloomfilter = "1.*" 10 | bincode = { version = "1.*"} 11 | anyhow = "1.*" 12 | serde = "1.*" 13 | serde_derive = "1.*" -------------------------------------------------------------------------------- /easypwned_bloom/src/bloom/mod.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Error}; 2 | use bloomfilter::Bloom; 3 | use serde_derive::{Deserialize, Serialize}; 4 | use std::fs::File; 5 | use std::io::{Read}; 6 | 7 | 8 | #[derive(Serialize, Deserialize)] 9 | pub struct BloomWithMetadata { 10 | pub number_of_bits: u64, 11 | pub number_of_hash_functions: u32, 12 | pub sip_keys: [(u64, u64); 2], 13 | pub bloom: Vec, 14 | } 15 | 16 | pub type EasyBloom = Bloom>; 17 | 18 | impl BloomWithMetadata { 19 | pub fn to_bloom(self) -> EasyBloom { 20 | Bloom::from_existing( 21 | self.bloom.as_slice(), 22 | self.number_of_bits, 23 | self.number_of_hash_functions, 24 | self.sip_keys, 25 | ) 26 | } 27 | } 28 | 29 | pub fn bloom_get(path: &str) -> Result { 30 | let mut file = File::open(&path)?; 31 | let mut buf = vec![]; 32 | file.read_to_end(&mut buf)?; 33 | 34 | Ok(bincode::deserialize::(buf.as_slice())?) 35 | } 36 | -------------------------------------------------------------------------------- /easypwned_bloom/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod bloom; --------------------------------------------------------------------------------