├── .deepsource.toml ├── .github ├── CODE-OF-CONDUCT.md ├── CODEOWNERS ├── SECURITY.md ├── dependabot.yml ├── funding.yml └── workflows │ ├── audit.yml │ ├── check.yml │ ├── coverage.yml │ ├── document.yml │ ├── lint.yml │ ├── release.yml │ └── test.yml ├── .gitignore ├── AUTHORS.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── TEMPLATE.md ├── benches └── criterion.rs ├── build.rs ├── deny.toml ├── examples └── hsh.rs ├── rustfmt.toml ├── src ├── algorithms │ ├── argon2i.rs │ ├── bcrypt.rs │ ├── mod.rs │ └── scrypt.rs ├── lib.rs ├── macros.rs ├── main.rs └── models │ ├── hash.rs │ ├── hash_algorithm.rs │ └── mod.rs └── tests ├── test_argon2i.rs ├── test_bcrypt.rs ├── test_hash.rs ├── test_hash_algorithm.rs ├── test_lib.rs ├── test_macros.rs ├── test_main.rs └── test_scrypt.rs /.deepsource.toml: -------------------------------------------------------------------------------- 1 | version = 1 2 | 3 | [[analyzers]] 4 | name = "rust" 5 | enabled = true 6 | 7 | [analyzers.meta] 8 | msrv = "stable" -------------------------------------------------------------------------------- /.github/CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our 7 | project and our community a harassment-free experience for everyone, 8 | regardless of age, body size, disability, ethnicity, gender identity and 9 | expression, level of experience, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behaviour that contributes to creating a positive 15 | environment include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behaviour by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual 26 | attention or advances 27 | - Trolling, insulting/derogatory comments, and personal or political 28 | attacks 29 | - Public or private harassment 30 | - Publishing others' private information, such as a physical or 31 | electronic address, without explicit permission 32 | - Other conduct which could reasonably be considered inappropriate in a 33 | professional setting 34 | 35 | ## Our Responsibilities 36 | 37 | Project maintainers are responsible for clarifying the standards of 38 | acceptable behaviour and are expected to take appropriate and fair 39 | corrective action in response to any instances of unacceptable 40 | behaviour. 41 | 42 | Project maintainers have the right and responsibility to remove, edit, 43 | or reject comments, commits, code, wiki edits, issues, and other 44 | contributions that are not aligned to this Code of Conduct, or to ban 45 | temporarily or permanently any contributor for other behaviors that they 46 | deem inappropriate, threatening, offensive, or harmful. 47 | 48 | ## Scope 49 | 50 | This Code of Conduct applies both within project spaces and in public 51 | spaces when an individual is representing the project or its community. 52 | Examples of representing a project or community include using an 53 | official project e-mail address, posting via an official social media 54 | account, or acting as an appointed representative at an online or 55 | offline event. Representation of a project may be further defined and 56 | clarified by project maintainers. 57 | 58 | ## Enforcement 59 | 60 | Instances of abusive, harassing, or otherwise unacceptable behaviour may 61 | be reported by contacting the project team. The project team will review 62 | and investigate all complaints, and will respond in a way that it deems 63 | appropriate to the circumstances. The project team is obligated to 64 | maintain confidentiality with regard to the reporter of an incident. 65 | Further details of specific enforcement policies may be posted 66 | separately. 67 | 68 | Project maintainers who do not follow or enforce the Code of Conduct in 69 | good faith may face temporary or permanent repercussions as determined 70 | by other members of the project's leadership. 71 | 72 | ## Attribution 73 | 74 | This Code of Conduct is adapted from the Contributor Covenant homepage, 75 | version 1.4, available at 76 | [version](http://contributor-covenant.org/version/1/4) 77 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | @sebastienrousseau/hsh -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security 2 | 3 | We take the security of our software products and services seriously, 4 | which includes all source code repositories managed through our GitHub 5 | repositories. 6 | 7 | If you believe you have found a security vulnerability in any of our 8 | repository, please report it to us as described below. 9 | 10 | ## Reporting Security Issues 11 | 12 | Please include the requested information listed below (as much as you 13 | can provide) to help us better understand the nature and scope of the 14 | possible issue: 15 | 16 | - Type of issue (e.g. buffer overflow, SQL injection, cross-site 17 | scripting, etc.) 18 | - Full paths of source file(s) related to the manifestation of the issue 19 | - The location of the affected source code (tag/branch/commit or direct 20 | URL) 21 | - Any special configuration required to reproduce the issue 22 | - Step-by-step instructions to reproduce the issue 23 | - Proof-of-concept or exploit code (if possible) 24 | - Impact of the issue, including how an attacker might exploit the issue 25 | 26 | This information will help us triage your report more quickly. 27 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "cargo" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | 8 | - package-ecosystem: "github-actions" 9 | directory: "/" 10 | schedule: 11 | interval: "daily" 12 | -------------------------------------------------------------------------------- /.github/funding.yml: -------------------------------------------------------------------------------- 1 | github: sebastienrousseau 2 | custom: https://paypal.me/wwdseb 3 | -------------------------------------------------------------------------------- /.github/workflows/audit.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Audit 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - feat/hsh 8 | pull_request: 9 | branches: 10 | - feat/hsh 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | dependencies: 16 | name: Audit dependencies 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: hecrj/setup-rust-action@v2 20 | - name: Install cargo-audit 21 | run: cargo install cargo-audit 22 | 23 | - uses: actions/checkout@v4 24 | - name: Resolve dependencies 25 | run: cargo update 26 | 27 | - name: Audit vulnerabilities 28 | run: cargo audit -------------------------------------------------------------------------------- /.github/workflows/check.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Check 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - feat/hsh 8 | pull_request: 9 | branches: 10 | - feat/hsh 11 | release: 12 | types: [created] 13 | 14 | jobs: 15 | all: 16 | name: Check 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: hecrj/setup-rust-action@v2 20 | with: 21 | components: clippy 22 | - uses: actions/checkout@v4 23 | - name: Check lints 24 | run: cargo check --all-targets --workspace --all-features -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: 📶 Coverage 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | coverage: 14 | name: Code Coverage 15 | runs-on: ubuntu-latest 16 | env: 17 | CARGO_INCREMENTAL: "0" 18 | RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests" 19 | RUSTDOCFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests" 20 | 21 | steps: 22 | # Checkout the repository 23 | - name: Checkout repository 24 | uses: actions/checkout@v4 25 | 26 | # Setup Rust nightly 27 | - name: Install Rust 28 | uses: actions-rs/toolchain@v1 29 | id: toolchain 30 | with: 31 | toolchain: nightly 32 | override: true 33 | 34 | # Configure cache for Cargo 35 | - name: Cache Cargo registry, index 36 | uses: actions/cache@v4 37 | id: cache-cargo 38 | with: 39 | path: | 40 | ~/.cargo/registry 41 | ~/.cargo/bin 42 | ~/.cargo/git 43 | key: linux-${{ steps.toolchain.outputs.rustc_hash }}-rust-cov-${{ hashFiles('**/Cargo.lock') }} 44 | 45 | # Run tests with all features 46 | - name: Test (cargo test) 47 | uses: actions-rs/cargo@v1 48 | with: 49 | command: test 50 | args: "--workspace" 51 | 52 | # Install grcov 53 | - uses: actions-rs/grcov@v0.1 54 | id: coverage 55 | 56 | # Upload to Codecov.io 57 | - name: Upload to Codecov.io 58 | uses: codecov/codecov-action@v5 59 | with: 60 | token: ${{ secrets.CODECOV_TOKEN }} 61 | file: ${{ steps.coverage.outputs.report }} -------------------------------------------------------------------------------- /.github/workflows/document.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Document 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | release: 11 | types: [created] 12 | 13 | jobs: 14 | all: 15 | name: Document 16 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 17 | runs-on: ubuntu-latest 18 | concurrency: 19 | group: ${{ github.workflow }}-${{ github.ref }} 20 | steps: 21 | - uses: hecrj/setup-rust-action@v2 22 | with: 23 | rust-version: nightly 24 | 25 | - uses: actions/checkout@v4 26 | 27 | - name: Update libssl 28 | run: | 29 | sudo apt-get update 30 | sudo apt-get install -y libssl1.1 31 | 32 | - name: Generate documentation for all features and publish it 33 | run: | 34 | RUSTDOCFLAGS="--cfg docsrs" \ 35 | cargo doc --no-deps --all-features --workspace 36 | # Write index.html with redirect 37 | echo '' > ./target/doc/index.html 38 | 39 | - name: Deploy 40 | uses: actions/upload-artifact@v4 41 | with: 42 | name: documentation 43 | path: target/doc 44 | if-no-files-found: error 45 | retention-days: 1 46 | 47 | - name: Write CNAME file 48 | run: echo 'doc.hshlib.com' > ./target/doc/CNAME 49 | 50 | - name: Deploy to GitHub Pages 51 | uses: peaceiris/actions-gh-pages@v4 52 | with: 53 | cname: true 54 | commit_message: Deploy documentation at ${{ github.sha }} 55 | github_token: ${{ secrets.GITHUB_TOKEN }} 56 | publish_branch: gh-pages 57 | publish_dir: ./target/doc 58 | user_email: actions@users.noreply.github.com 59 | user_name: github-actions -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Lint 2 | 3 | on: 4 | push: 5 | branches: 6 | - feat/hsh 7 | pull_request: 8 | branches: 9 | - feat/hsh 10 | release: 11 | types: [created] 12 | 13 | jobs: 14 | all: 15 | name: Lint 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: hecrj/setup-rust-action@v2 19 | with: 20 | components: clippy 21 | - uses: actions/checkout@v4 22 | - name: Check lints 23 | run: cargo clippy --workspace --all-features --all-targets --no-deps -- -D warnings -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - feat/hsh 8 | pull_request: 9 | branches: 10 | - feat/hsh 11 | release: 12 | types: [created] 13 | 14 | concurrency: 15 | group: ${{ github.ref }} 16 | cancel-in-progress: true 17 | 18 | jobs: 19 | # Build the project for all the targets and generate artifacts. 20 | build: 21 | # This job builds the project for all the targets and generates a 22 | # release artifact that contains the binaries for all the targets. 23 | name: ❯ Build 🛠 24 | 25 | # Only run this job on the main branch when a commit is pushed. 26 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 27 | 28 | # Set up the job environment variables. 29 | env: 30 | BUILD_ID: ${{ github.run_id }} 31 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }} 32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 33 | OS: ${{ matrix.platform.os }} 34 | TARGET: ${{ matrix.platform.target }} 35 | 36 | strategy: 37 | fail-fast: false 38 | matrix: 39 | platform: 40 | - target: x86_64-pc-windows-msvc 41 | os: windows-latest 42 | - target: aarch64-pc-windows-msvc 43 | os: windows-latest 44 | - target: x86_64-apple-darwin 45 | os: macos-latest 46 | - target: aarch64-apple-darwin 47 | os: macos-latest 48 | - target: x86_64-unknown-linux-gnu 49 | os: ubuntu-latest 50 | 51 | runs-on: ${{ matrix.platform.os }} 52 | 53 | steps: 54 | # Check out the repository code. 55 | - name: Checkout sources 56 | id: checkout 57 | uses: actions/checkout@v4 58 | 59 | # Install the stable Rust toolchain. 60 | - name: Install stable toolchain 61 | id: install-toolchain 62 | uses: actions-rs/toolchain@v1 63 | with: 64 | toolchain: stable 65 | override: true 66 | 67 | # Cache dependencies to speed up subsequent builds. 68 | - name: Cache dependencies 69 | id: cache-dependencies 70 | uses: actions/cache@v4 71 | with: 72 | path: ~/.cargo 73 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 74 | restore-keys: ${{ runner.os }}-cargo- 75 | 76 | # Install the targets for the cross-compilation toolchain 77 | - name: Install target 78 | id: install-target 79 | run: rustup target add ${{ env.TARGET }} 80 | 81 | # Build the targets 82 | - name: Build targets 83 | id: build-targets 84 | uses: actions-rs/cargo@v1 85 | with: 86 | command: build 87 | args: --verbose --workspace --release --target ${{ env.TARGET }} 88 | 89 | # Package the binary for each target 90 | - name: Package the binary 91 | id: package-binary 92 | run: | 93 | mkdir -p target/package 94 | tar czf target/package/${{ env.TARGET }}.tar.gz -C target/${{ env.TARGET }}/release . 95 | echo "${{ env.TARGET }}.tar.gz=target/package/${{ env.TARGET }}.tar.gz" >> $GITHUB_ENV 96 | 97 | # Upload the binary for each target 98 | - name: Upload the binary 99 | id: upload-binary 100 | uses: actions/upload-artifact@v4 101 | with: 102 | name: ${{ env.TARGET }}.tar.gz 103 | path: target/package/${{ env.TARGET }}.tar.gz 104 | 105 | # Release the binary to GitHub Releases 106 | release: 107 | name: ❯ Release 🚀 108 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 109 | needs: build 110 | runs-on: ubuntu-latest 111 | steps: 112 | # Check out the repository code 113 | - name: Checkout sources 114 | uses: actions/checkout@v4 115 | 116 | # Install the stable Rust toolchain 117 | - name: Install stable toolchain 118 | uses: actions-rs/toolchain@v1 119 | with: 120 | toolchain: stable 121 | override: true 122 | 123 | # Update the version number based on the Cargo.toml file 124 | - name: Update version number 125 | run: | 126 | NEW_VERSION=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) 127 | echo "VERSION=$NEW_VERSION" >> "$GITHUB_ENV" 128 | shell: /bin/bash -e {0} 129 | 130 | # Cache dependencies to speed up subsequent builds 131 | - name: Cache dependencies 132 | uses: actions/cache@v4 133 | with: 134 | path: ~/.cargo 135 | key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} 136 | restore-keys: ${{ runner.os }}-cargo- 137 | 138 | # Download the artifacts from the build job 139 | - name: Download artifacts 140 | run: | 141 | for target in ${{ env.TARGET }}; do 142 | echo "Downloading $target artifact" 143 | name="${target}.tar.gz" 144 | echo "Artifact name: $name" 145 | mkdir -p target/package 146 | curl -sSL -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" -L "${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/actions/runs/${BUILD_ID}/artifacts/${name}" -o "target/package/${name}" 147 | done 148 | 149 | env: 150 | VERSION: ${{ env.VERSION }} 151 | TARGET: ${{ env.TARGET }} 152 | OS: ${{ env.OS }} 153 | 154 | # Generate the changelog based on the git log 155 | - name: Generate Changelog 156 | id: generate-changelog 157 | env: 158 | BUILD_ID: ${{ github.run_id }} 159 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 160 | RELEASE_URL: https://github.com/sebastienrousseau/hsh/releases 161 | TARGET: ${{ env.TARGET }} 162 | 163 | run: | 164 | if [[ ! -f CHANGELOG.md ]]; then 165 | # Set path to changelog file 166 | changelog_file="${{ github.workspace }}/CHANGELOG.md" 167 | 168 | # Get version from Cargo.toml 169 | version=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) 170 | 171 | # Append version information to changelog 172 | echo "## Release v${version} - $(date +'%Y-%m-%d')" >> "${changelog_file}" 173 | 174 | # Copy content of template file to changelog 175 | cat TEMPLATE.md >> "${changelog_file}" 176 | 177 | # Append git log to changelog 178 | echo "$(git log --pretty=format:'%s' --reverse HEAD)" >> "${changelog_file}" 179 | 180 | # Append empty line to changelog 181 | echo "" >> "${changelog_file}" 182 | 183 | fi 184 | shell: bash 185 | 186 | # Create the release on GitHub releases 187 | - name: Create Release 188 | id: create-release 189 | uses: actions/create-release@v1 190 | env: 191 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 192 | VERSION: ${{ env.VERSION }} 193 | with: 194 | tag_name: v${{ env.VERSION }} 195 | release_name: Hash (HSH) 🦀 v${{ env.VERSION }} 196 | body_path: ${{ github.workspace }}/CHANGELOG.md 197 | draft: true 198 | prerelease: false 199 | 200 | # Publish the release to Crates.io automatically 201 | crate: 202 | name: ❯ Crate.io 🦀 203 | if: github.ref == 'refs/heads/main' && github.event_name == 'push' 204 | needs: release 205 | runs-on: ubuntu-latest 206 | 207 | steps: 208 | # Check out the repository code 209 | - name: Checkout 210 | uses: actions/checkout@v4 211 | 212 | # Install the stable Rust toolchain 213 | - name: Install stable toolchain 214 | id: install-toolchain 215 | uses: actions-rs/toolchain@v1 216 | with: 217 | toolchain: stable 218 | override: true 219 | 220 | # Cache dependencies to speed up subsequent builds 221 | - name: Cache dependencies 222 | id: cache-dependencies 223 | uses: actions/cache@v4 224 | with: 225 | path: /home/runner/.cargo/registry/index/ 226 | key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} 227 | restore-keys: ${{ runner.os }}-cargo-index- 228 | 229 | # Update the version number based on the Cargo.toml file 230 | - name: Update version number 231 | id: update-version 232 | run: | 233 | NEW_VERSION=$(grep version Cargo.toml | sed -n 2p | cut -d '"' -f 2) 234 | echo "VERSION=$NEW_VERSION" >> "$GITHUB_ENV" 235 | shell: /bin/bash -e {0} 236 | 237 | # Log in to crates.io 238 | - name: Log in to crates.io 239 | id: login-crate-io 240 | run: cargo login ${{ secrets.CARGO_API_TOKEN }} 241 | 242 | # Publish the Rust library to Crate.io 243 | - name: Publish Library to Crate.io 244 | id: publish-library 245 | uses: actions-rs/cargo@v1 246 | env: 247 | CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_API_TOKEN }} 248 | with: 249 | command: publish 250 | args: "--no-verify --allow-dirty" 251 | use-cross: false -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: 🧪 Test 2 | 3 | on: [pull_request, push] 4 | jobs: 5 | test-lib: 6 | name: Test library 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | os: [ubuntu-latest] 11 | toolchain: [stable, nightly] 12 | continue-on-error: true 13 | 14 | steps: 15 | # Checkout the repository 16 | - name: Checkout repository 17 | uses: actions/checkout@v4 18 | 19 | # Setup Rust 20 | - name: Setup Rust 21 | run: | 22 | rustup toolchain add ${{ matrix.toolchain }} --component llvm-tools-preview 23 | rustup override set ${{ matrix.toolchain }} 24 | 25 | # Configure cache 26 | - name: Configure cache 27 | uses: actions/cache@v4 28 | with: 29 | path: | 30 | ~/.cargo/bin/ 31 | ~/.cargo/registry/index/ 32 | ~/.cargo/registry/cache/ 33 | ~/.cargo/git/db/ 34 | target/ 35 | key: test-${{ runner.os }}-cargo-${{ matrix.toolchain }}-${{ hashFiles('**/Cargo.lock') }} 36 | 37 | # Run tests with all features 38 | - name: Run tests with all features 39 | id: run-tests-all-features 40 | run: cargo test --verbose --workspace --all-features -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | *.profraw 3 | /.vscode/ 4 | /output/ 5 | /public/ 6 | /target/ 7 | build 8 | Icon? 9 | src/.DS_Store 10 | tarpaulin-report.html 11 | Cargo.lock -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | # Authors 2 | 3 | * [Sebastien Rousseau](mailto:sebastian.rousseau@gmail.com) (Original Contributor) 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to `Hash (HSH)` 2 | 3 | Welcome! We're thrilled that you're interested in contributing to the `Hash (HSH)` library. Whether you're looking to evangelize, submit feedback, or contribute code, we appreciate your involvement in making `Hash (HSH)` a better tool for everyone. Here's how you can get started. 4 | 5 | ## Evangelize 6 | 7 | One of the simplest ways to help us out is by spreading the word about Hash (HSH). We believe that a bigger, more involved community makes for a better framework, and that better frameworks make the world a better place. If you know people who might benefit from using Hash (HSH), please let them know! 8 | 9 | ## How to Contribute 10 | 11 | If you're interested in making a more direct contribution, there are several ways you can help us improve Hash (HSH). Here are some guidelines for submitting feedback, bug reports, and code contributions. 12 | 13 | ### Feedback 14 | 15 | Your feedback is incredibly valuable to us, and we're always looking for ways to make Hash (HSH) better. If you have ideas, suggestions, or questions about Hash (HSH), we'd love to hear them. Here's how you can provide feedback: 16 | 17 | - Click [here][2] to submit a new feedback. 18 | - Use a descriptive title that clearly summarizes your feedback. 19 | - Provide a detailed description of the issue or suggestion. 20 | - Be patient while we review and respond to your feedback. 21 | 22 | ### Bug Reports 23 | 24 | If you encounter a bug while using Hash (HSH), please let us know so we can fix it. Here's how you can submit a bug report: 25 | 26 | - Click [here][2] to submit a new issue. 27 | - Use a descriptive title that clearly summarizes the bug. 28 | - Provide a detailed description of the issue, including steps to reproduce it. 29 | - Be patient while we review and respond to your bug report. 30 | 31 | ### Code Contributions 32 | 33 | If you're interested in contributing code to Hash (HSH), we're excited to have your help! Here's what you need to know: 34 | 35 | #### Feature Requests 36 | 37 | If you have an idea for a new feature or improvement, we'd love to hear it. Here's how you can contribute code for a new feature to Hash (HSH): 38 | 39 | - Fork the repo. 40 | - Clone the Hash [HSH](1) repo by running: 41 | `git clone {repository}` 42 | - Edit files in the `src/` folder. The `src/` folder contains the source code for Hash (HSH). 43 | - Submit a pull request, and we'll review and merge your changes if they fit with our vision for Hash (HSH). 44 | 45 | #### Submitting Code 46 | 47 | If you've identified a bug or have a specific code improvement in mind, we welcome your pull requests. Here's how to submit your code changes: 48 | 49 | - Fork the repo. 50 | - Clone the Hash (HSH) repo by running: 51 | `git clone {repository}` 52 | - Edit files in the `src/` folder. The `src/` folder contains the source code for Hash (HSH). 53 | - Submit a pull request, and we'll review and merge your changes if they fit with our vision for Hash (HSH). 54 | 55 | We hope that this guide has been helpful in explaining how you can contribute to Hash (HSH). Thank you for your interest and involvement in our project! 56 | 57 | [2]: https://github.com/sebastienrousseau/dtt/issues/new 58 | -------------------------------------------------------------------------------- /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.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "aho-corasick" 22 | version = "1.1.3" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 25 | dependencies = [ 26 | "memchr", 27 | ] 28 | 29 | [[package]] 30 | name = "anes" 31 | version = "0.1.6" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.7" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" 40 | 41 | [[package]] 42 | name = "argon2rs" 43 | version = "0.2.5" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "3f67b0b6a86dae6e67ff4ca2b6201396074996379fba2b92ff649126f37cb392" 46 | dependencies = [ 47 | "blake2-rfc", 48 | "scoped_threadpool", 49 | ] 50 | 51 | [[package]] 52 | name = "arrayvec" 53 | version = "0.4.12" 54 | source = "registry+https://github.com/rust-lang/crates.io-index" 55 | checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" 56 | dependencies = [ 57 | "nodrop", 58 | ] 59 | 60 | [[package]] 61 | name = "assert_cmd" 62 | version = "2.0.14" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" 65 | dependencies = [ 66 | "anstyle", 67 | "bstr", 68 | "doc-comment", 69 | "predicates", 70 | "predicates-core", 71 | "predicates-tree", 72 | "wait-timeout", 73 | ] 74 | 75 | [[package]] 76 | name = "autocfg" 77 | version = "1.3.0" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 80 | 81 | [[package]] 82 | name = "backtrace" 83 | version = "0.3.71" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 86 | dependencies = [ 87 | "addr2line", 88 | "cc", 89 | "cfg-if", 90 | "libc", 91 | "miniz_oxide", 92 | "object", 93 | "rustc-demangle", 94 | ] 95 | 96 | [[package]] 97 | name = "base64" 98 | version = "0.22.1" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 101 | 102 | [[package]] 103 | name = "base64ct" 104 | version = "1.6.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" 107 | 108 | [[package]] 109 | name = "bcrypt" 110 | version = "0.16.0" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "2b1866ecef4f2d06a0bb77880015fdf2b89e25a1c2e5addacb87e459c86dc67e" 113 | dependencies = [ 114 | "base64", 115 | "blowfish", 116 | "getrandom", 117 | "subtle", 118 | "zeroize", 119 | ] 120 | 121 | [[package]] 122 | name = "bitflags" 123 | version = "2.7.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be" 126 | 127 | [[package]] 128 | name = "blake2-rfc" 129 | version = "0.2.18" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" 132 | dependencies = [ 133 | "arrayvec", 134 | "constant_time_eq", 135 | ] 136 | 137 | [[package]] 138 | name = "block-buffer" 139 | version = "0.10.4" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 142 | dependencies = [ 143 | "generic-array", 144 | ] 145 | 146 | [[package]] 147 | name = "blowfish" 148 | version = "0.9.1" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "e412e2cd0f2b2d93e02543ceae7917b3c70331573df19ee046bcbc35e45e87d7" 151 | dependencies = [ 152 | "byteorder", 153 | "cipher", 154 | ] 155 | 156 | [[package]] 157 | name = "bstr" 158 | version = "1.9.1" 159 | source = "registry+https://github.com/rust-lang/crates.io-index" 160 | checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" 161 | dependencies = [ 162 | "memchr", 163 | "regex-automata", 164 | "serde", 165 | ] 166 | 167 | [[package]] 168 | name = "bumpalo" 169 | version = "3.16.0" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 172 | 173 | [[package]] 174 | name = "byteorder" 175 | version = "1.5.0" 176 | source = "registry+https://github.com/rust-lang/crates.io-index" 177 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 178 | 179 | [[package]] 180 | name = "bytes" 181 | version = "1.6.0" 182 | source = "registry+https://github.com/rust-lang/crates.io-index" 183 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 184 | 185 | [[package]] 186 | name = "cast" 187 | version = "0.3.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 190 | 191 | [[package]] 192 | name = "cc" 193 | version = "1.0.97" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" 196 | 197 | [[package]] 198 | name = "cfg-if" 199 | version = "1.0.0" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 202 | 203 | [[package]] 204 | name = "ciborium" 205 | version = "0.2.2" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 208 | dependencies = [ 209 | "ciborium-io", 210 | "ciborium-ll", 211 | "serde", 212 | ] 213 | 214 | [[package]] 215 | name = "ciborium-io" 216 | version = "0.2.2" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 219 | 220 | [[package]] 221 | name = "ciborium-ll" 222 | version = "0.2.2" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 225 | dependencies = [ 226 | "ciborium-io", 227 | "half", 228 | ] 229 | 230 | [[package]] 231 | name = "cipher" 232 | version = "0.4.4" 233 | source = "registry+https://github.com/rust-lang/crates.io-index" 234 | checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" 235 | dependencies = [ 236 | "crypto-common", 237 | "inout", 238 | ] 239 | 240 | [[package]] 241 | name = "clap" 242 | version = "4.5.4" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" 245 | dependencies = [ 246 | "clap_builder", 247 | ] 248 | 249 | [[package]] 250 | name = "clap_builder" 251 | version = "4.5.2" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" 254 | dependencies = [ 255 | "anstyle", 256 | "clap_lex", 257 | ] 258 | 259 | [[package]] 260 | name = "clap_lex" 261 | version = "0.7.0" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" 264 | 265 | [[package]] 266 | name = "constant_time_eq" 267 | version = "0.1.5" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" 270 | 271 | [[package]] 272 | name = "cpufeatures" 273 | version = "0.2.12" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 276 | dependencies = [ 277 | "libc", 278 | ] 279 | 280 | [[package]] 281 | name = "criterion" 282 | version = "0.5.1" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 285 | dependencies = [ 286 | "anes", 287 | "cast", 288 | "ciborium", 289 | "clap", 290 | "criterion-plot", 291 | "is-terminal", 292 | "itertools", 293 | "num-traits", 294 | "once_cell", 295 | "oorandom", 296 | "plotters", 297 | "rayon", 298 | "regex", 299 | "serde", 300 | "serde_derive", 301 | "serde_json", 302 | "tinytemplate", 303 | "walkdir", 304 | ] 305 | 306 | [[package]] 307 | name = "criterion-plot" 308 | version = "0.5.0" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 311 | dependencies = [ 312 | "cast", 313 | "itertools", 314 | ] 315 | 316 | [[package]] 317 | name = "crossbeam-deque" 318 | version = "0.8.5" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 321 | dependencies = [ 322 | "crossbeam-epoch", 323 | "crossbeam-utils", 324 | ] 325 | 326 | [[package]] 327 | name = "crossbeam-epoch" 328 | version = "0.9.18" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 331 | dependencies = [ 332 | "crossbeam-utils", 333 | ] 334 | 335 | [[package]] 336 | name = "crossbeam-utils" 337 | version = "0.8.19" 338 | source = "registry+https://github.com/rust-lang/crates.io-index" 339 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 340 | 341 | [[package]] 342 | name = "crunchy" 343 | version = "0.2.2" 344 | source = "registry+https://github.com/rust-lang/crates.io-index" 345 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 346 | 347 | [[package]] 348 | name = "crypto-common" 349 | version = "0.1.6" 350 | source = "registry+https://github.com/rust-lang/crates.io-index" 351 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 352 | dependencies = [ 353 | "generic-array", 354 | "typenum", 355 | ] 356 | 357 | [[package]] 358 | name = "deranged" 359 | version = "0.3.11" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 362 | dependencies = [ 363 | "powerfmt", 364 | "serde", 365 | ] 366 | 367 | [[package]] 368 | name = "difflib" 369 | version = "0.4.0" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" 372 | 373 | [[package]] 374 | name = "digest" 375 | version = "0.10.7" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 378 | dependencies = [ 379 | "block-buffer", 380 | "crypto-common", 381 | "subtle", 382 | ] 383 | 384 | [[package]] 385 | name = "doc-comment" 386 | version = "0.3.3" 387 | source = "registry+https://github.com/rust-lang/crates.io-index" 388 | checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" 389 | 390 | [[package]] 391 | name = "dtt" 392 | version = "0.0.5" 393 | source = "registry+https://github.com/rust-lang/crates.io-index" 394 | checksum = "d6b2dd9ee2d76888dc4c17d6da74629fa11b3cb1e8094fdc159b7f8ff259fc88" 395 | dependencies = [ 396 | "regex", 397 | "serde", 398 | "time", 399 | ] 400 | 401 | [[package]] 402 | name = "dtt" 403 | version = "0.0.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "21191da49ce48aa9200e9ac040032d680b3b71a158fbecaa1a99282821c3c251" 406 | dependencies = [ 407 | "regex", 408 | "serde", 409 | "time", 410 | ] 411 | 412 | [[package]] 413 | name = "dtt" 414 | version = "0.0.9" 415 | source = "registry+https://github.com/rust-lang/crates.io-index" 416 | checksum = "a3d0ded54a7c92b3bbc6d8a6a5ae97f120caa634ab7ee49d0b20ac2041037e2d" 417 | dependencies = [ 418 | "lazy_static", 419 | "paste", 420 | "regex", 421 | "serde", 422 | "serde_json", 423 | "thiserror", 424 | "time", 425 | "version_check", 426 | ] 427 | 428 | [[package]] 429 | name = "either" 430 | version = "1.11.0" 431 | source = "registry+https://github.com/rust-lang/crates.io-index" 432 | checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" 433 | 434 | [[package]] 435 | name = "generic-array" 436 | version = "0.14.7" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 439 | dependencies = [ 440 | "typenum", 441 | "version_check", 442 | ] 443 | 444 | [[package]] 445 | name = "getrandom" 446 | version = "0.2.15" 447 | source = "registry+https://github.com/rust-lang/crates.io-index" 448 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 449 | dependencies = [ 450 | "cfg-if", 451 | "libc", 452 | "wasi", 453 | ] 454 | 455 | [[package]] 456 | name = "gimli" 457 | version = "0.28.1" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 460 | 461 | [[package]] 462 | name = "half" 463 | version = "2.4.1" 464 | source = "registry+https://github.com/rust-lang/crates.io-index" 465 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 466 | dependencies = [ 467 | "cfg-if", 468 | "crunchy", 469 | ] 470 | 471 | [[package]] 472 | name = "hermit-abi" 473 | version = "0.3.9" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 476 | 477 | [[package]] 478 | name = "hmac" 479 | version = "0.12.1" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" 482 | dependencies = [ 483 | "digest", 484 | ] 485 | 486 | [[package]] 487 | name = "hostname" 488 | version = "0.3.1" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" 491 | dependencies = [ 492 | "libc", 493 | "match_cfg", 494 | "winapi", 495 | ] 496 | 497 | [[package]] 498 | name = "hostname" 499 | version = "0.4.0" 500 | source = "registry+https://github.com/rust-lang/crates.io-index" 501 | checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" 502 | dependencies = [ 503 | "cfg-if", 504 | "libc", 505 | "windows", 506 | ] 507 | 508 | [[package]] 509 | name = "hsh" 510 | version = "0.0.8" 511 | dependencies = [ 512 | "argon2rs", 513 | "assert_cmd", 514 | "base64", 515 | "bcrypt", 516 | "criterion", 517 | "dtt 0.0.9", 518 | "log", 519 | "scrypt", 520 | "serde", 521 | "serde_json", 522 | "vrd 0.0.8", 523 | ] 524 | 525 | [[package]] 526 | name = "inout" 527 | version = "0.1.3" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" 530 | dependencies = [ 531 | "generic-array", 532 | ] 533 | 534 | [[package]] 535 | name = "is-terminal" 536 | version = "0.4.12" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" 539 | dependencies = [ 540 | "hermit-abi", 541 | "libc", 542 | "windows-sys", 543 | ] 544 | 545 | [[package]] 546 | name = "itertools" 547 | version = "0.10.5" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 550 | dependencies = [ 551 | "either", 552 | ] 553 | 554 | [[package]] 555 | name = "itoa" 556 | version = "1.0.11" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 559 | 560 | [[package]] 561 | name = "js-sys" 562 | version = "0.3.69" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" 565 | dependencies = [ 566 | "wasm-bindgen", 567 | ] 568 | 569 | [[package]] 570 | name = "lazy_static" 571 | version = "1.5.0" 572 | source = "registry+https://github.com/rust-lang/crates.io-index" 573 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 574 | 575 | [[package]] 576 | name = "libc" 577 | version = "0.2.169" 578 | source = "registry+https://github.com/rust-lang/crates.io-index" 579 | checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" 580 | 581 | [[package]] 582 | name = "lock_api" 583 | version = "0.4.12" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 586 | dependencies = [ 587 | "autocfg", 588 | "scopeguard", 589 | ] 590 | 591 | [[package]] 592 | name = "log" 593 | version = "0.4.25" 594 | source = "registry+https://github.com/rust-lang/crates.io-index" 595 | checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" 596 | 597 | [[package]] 598 | name = "match_cfg" 599 | version = "0.1.0" 600 | source = "registry+https://github.com/rust-lang/crates.io-index" 601 | checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" 602 | 603 | [[package]] 604 | name = "memchr" 605 | version = "2.7.2" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 608 | 609 | [[package]] 610 | name = "miniz_oxide" 611 | version = "0.7.2" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" 614 | dependencies = [ 615 | "adler", 616 | ] 617 | 618 | [[package]] 619 | name = "mio" 620 | version = "1.0.3" 621 | source = "registry+https://github.com/rust-lang/crates.io-index" 622 | checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" 623 | dependencies = [ 624 | "libc", 625 | "wasi", 626 | "windows-sys", 627 | ] 628 | 629 | [[package]] 630 | name = "nodrop" 631 | version = "0.1.14" 632 | source = "registry+https://github.com/rust-lang/crates.io-index" 633 | checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" 634 | 635 | [[package]] 636 | name = "num-conv" 637 | version = "0.1.0" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 640 | 641 | [[package]] 642 | name = "num-traits" 643 | version = "0.2.19" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 646 | dependencies = [ 647 | "autocfg", 648 | ] 649 | 650 | [[package]] 651 | name = "object" 652 | version = "0.32.2" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 655 | dependencies = [ 656 | "memchr", 657 | ] 658 | 659 | [[package]] 660 | name = "once_cell" 661 | version = "1.19.0" 662 | source = "registry+https://github.com/rust-lang/crates.io-index" 663 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 664 | 665 | [[package]] 666 | name = "oorandom" 667 | version = "11.1.3" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 670 | 671 | [[package]] 672 | name = "parking_lot" 673 | version = "0.12.2" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" 676 | dependencies = [ 677 | "lock_api", 678 | "parking_lot_core", 679 | ] 680 | 681 | [[package]] 682 | name = "parking_lot_core" 683 | version = "0.9.10" 684 | source = "registry+https://github.com/rust-lang/crates.io-index" 685 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 686 | dependencies = [ 687 | "cfg-if", 688 | "libc", 689 | "redox_syscall", 690 | "smallvec", 691 | "windows-targets", 692 | ] 693 | 694 | [[package]] 695 | name = "password-hash" 696 | version = "0.5.0" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" 699 | dependencies = [ 700 | "base64ct", 701 | "rand_core", 702 | "subtle", 703 | ] 704 | 705 | [[package]] 706 | name = "paste" 707 | version = "1.0.15" 708 | source = "registry+https://github.com/rust-lang/crates.io-index" 709 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 710 | 711 | [[package]] 712 | name = "pbkdf2" 713 | version = "0.12.2" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" 716 | dependencies = [ 717 | "digest", 718 | "hmac", 719 | ] 720 | 721 | [[package]] 722 | name = "pin-project-lite" 723 | version = "0.2.14" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 726 | 727 | [[package]] 728 | name = "plotters" 729 | version = "0.3.5" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" 732 | dependencies = [ 733 | "num-traits", 734 | "plotters-backend", 735 | "plotters-svg", 736 | "wasm-bindgen", 737 | "web-sys", 738 | ] 739 | 740 | [[package]] 741 | name = "plotters-backend" 742 | version = "0.3.5" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" 745 | 746 | [[package]] 747 | name = "plotters-svg" 748 | version = "0.3.5" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" 751 | dependencies = [ 752 | "plotters-backend", 753 | ] 754 | 755 | [[package]] 756 | name = "powerfmt" 757 | version = "0.2.0" 758 | source = "registry+https://github.com/rust-lang/crates.io-index" 759 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 760 | 761 | [[package]] 762 | name = "ppv-lite86" 763 | version = "0.2.17" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 766 | 767 | [[package]] 768 | name = "predicates" 769 | version = "3.1.0" 770 | source = "registry+https://github.com/rust-lang/crates.io-index" 771 | checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" 772 | dependencies = [ 773 | "anstyle", 774 | "difflib", 775 | "predicates-core", 776 | ] 777 | 778 | [[package]] 779 | name = "predicates-core" 780 | version = "1.0.6" 781 | source = "registry+https://github.com/rust-lang/crates.io-index" 782 | checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" 783 | 784 | [[package]] 785 | name = "predicates-tree" 786 | version = "1.0.9" 787 | source = "registry+https://github.com/rust-lang/crates.io-index" 788 | checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" 789 | dependencies = [ 790 | "predicates-core", 791 | "termtree", 792 | ] 793 | 794 | [[package]] 795 | name = "proc-macro2" 796 | version = "1.0.92" 797 | source = "registry+https://github.com/rust-lang/crates.io-index" 798 | checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" 799 | dependencies = [ 800 | "unicode-ident", 801 | ] 802 | 803 | [[package]] 804 | name = "quote" 805 | version = "1.0.36" 806 | source = "registry+https://github.com/rust-lang/crates.io-index" 807 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 808 | dependencies = [ 809 | "proc-macro2", 810 | ] 811 | 812 | [[package]] 813 | name = "rand" 814 | version = "0.8.5" 815 | source = "registry+https://github.com/rust-lang/crates.io-index" 816 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 817 | dependencies = [ 818 | "libc", 819 | "rand_chacha", 820 | "rand_core", 821 | ] 822 | 823 | [[package]] 824 | name = "rand_chacha" 825 | version = "0.3.1" 826 | source = "registry+https://github.com/rust-lang/crates.io-index" 827 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 828 | dependencies = [ 829 | "ppv-lite86", 830 | "rand_core", 831 | ] 832 | 833 | [[package]] 834 | name = "rand_core" 835 | version = "0.6.4" 836 | source = "registry+https://github.com/rust-lang/crates.io-index" 837 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 838 | dependencies = [ 839 | "getrandom", 840 | ] 841 | 842 | [[package]] 843 | name = "rayon" 844 | version = "1.10.0" 845 | source = "registry+https://github.com/rust-lang/crates.io-index" 846 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 847 | dependencies = [ 848 | "either", 849 | "rayon-core", 850 | ] 851 | 852 | [[package]] 853 | name = "rayon-core" 854 | version = "1.12.1" 855 | source = "registry+https://github.com/rust-lang/crates.io-index" 856 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 857 | dependencies = [ 858 | "crossbeam-deque", 859 | "crossbeam-utils", 860 | ] 861 | 862 | [[package]] 863 | name = "redox_syscall" 864 | version = "0.5.1" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" 867 | dependencies = [ 868 | "bitflags", 869 | ] 870 | 871 | [[package]] 872 | name = "regex" 873 | version = "1.11.1" 874 | source = "registry+https://github.com/rust-lang/crates.io-index" 875 | checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" 876 | dependencies = [ 877 | "aho-corasick", 878 | "memchr", 879 | "regex-automata", 880 | "regex-syntax", 881 | ] 882 | 883 | [[package]] 884 | name = "regex-automata" 885 | version = "0.4.9" 886 | source = "registry+https://github.com/rust-lang/crates.io-index" 887 | checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" 888 | dependencies = [ 889 | "aho-corasick", 890 | "memchr", 891 | "regex-syntax", 892 | ] 893 | 894 | [[package]] 895 | name = "regex-syntax" 896 | version = "0.8.5" 897 | source = "registry+https://github.com/rust-lang/crates.io-index" 898 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 899 | 900 | [[package]] 901 | name = "rlg" 902 | version = "0.0.3" 903 | source = "registry+https://github.com/rust-lang/crates.io-index" 904 | checksum = "9e02c717e23f67b23032a4acb01cf63534d6259938d592e6d2451c02f09fc368" 905 | dependencies = [ 906 | "dtt 0.0.5", 907 | "hostname 0.3.1", 908 | "serde_json", 909 | "tokio", 910 | "vrd 0.0.5", 911 | ] 912 | 913 | [[package]] 914 | name = "rlg" 915 | version = "0.0.4" 916 | source = "registry+https://github.com/rust-lang/crates.io-index" 917 | checksum = "fa9550dfcf50ac8601b165168e8825d66e45db390f7740a3d45c640946c4a971" 918 | dependencies = [ 919 | "dtt 0.0.6", 920 | "hostname 0.4.0", 921 | "serde", 922 | "serde_json", 923 | "tokio", 924 | "version_check", 925 | "vrd 0.0.7", 926 | ] 927 | 928 | [[package]] 929 | name = "rustc-demangle" 930 | version = "0.1.24" 931 | source = "registry+https://github.com/rust-lang/crates.io-index" 932 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 933 | 934 | [[package]] 935 | name = "ryu" 936 | version = "1.0.18" 937 | source = "registry+https://github.com/rust-lang/crates.io-index" 938 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 939 | 940 | [[package]] 941 | name = "salsa20" 942 | version = "0.10.2" 943 | source = "registry+https://github.com/rust-lang/crates.io-index" 944 | checksum = "97a22f5af31f73a954c10289c93e8a50cc23d971e80ee446f1f6f7137a088213" 945 | dependencies = [ 946 | "cipher", 947 | ] 948 | 949 | [[package]] 950 | name = "same-file" 951 | version = "1.0.6" 952 | source = "registry+https://github.com/rust-lang/crates.io-index" 953 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 954 | dependencies = [ 955 | "winapi-util", 956 | ] 957 | 958 | [[package]] 959 | name = "scoped_threadpool" 960 | version = "0.1.9" 961 | source = "registry+https://github.com/rust-lang/crates.io-index" 962 | checksum = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8" 963 | 964 | [[package]] 965 | name = "scopeguard" 966 | version = "1.2.0" 967 | source = "registry+https://github.com/rust-lang/crates.io-index" 968 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 969 | 970 | [[package]] 971 | name = "scrypt" 972 | version = "0.11.0" 973 | source = "registry+https://github.com/rust-lang/crates.io-index" 974 | checksum = "0516a385866c09368f0b5bcd1caff3366aace790fcd46e2bb032697bb172fd1f" 975 | dependencies = [ 976 | "password-hash", 977 | "pbkdf2", 978 | "salsa20", 979 | "sha2", 980 | ] 981 | 982 | [[package]] 983 | name = "serde" 984 | version = "1.0.217" 985 | source = "registry+https://github.com/rust-lang/crates.io-index" 986 | checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" 987 | dependencies = [ 988 | "serde_derive", 989 | ] 990 | 991 | [[package]] 992 | name = "serde-big-array" 993 | version = "0.5.1" 994 | source = "registry+https://github.com/rust-lang/crates.io-index" 995 | checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" 996 | dependencies = [ 997 | "serde", 998 | ] 999 | 1000 | [[package]] 1001 | name = "serde_derive" 1002 | version = "1.0.217" 1003 | source = "registry+https://github.com/rust-lang/crates.io-index" 1004 | checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" 1005 | dependencies = [ 1006 | "proc-macro2", 1007 | "quote", 1008 | "syn", 1009 | ] 1010 | 1011 | [[package]] 1012 | name = "serde_json" 1013 | version = "1.0.137" 1014 | source = "registry+https://github.com/rust-lang/crates.io-index" 1015 | checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" 1016 | dependencies = [ 1017 | "itoa", 1018 | "memchr", 1019 | "ryu", 1020 | "serde", 1021 | ] 1022 | 1023 | [[package]] 1024 | name = "sha2" 1025 | version = "0.10.8" 1026 | source = "registry+https://github.com/rust-lang/crates.io-index" 1027 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 1028 | dependencies = [ 1029 | "cfg-if", 1030 | "cpufeatures", 1031 | "digest", 1032 | ] 1033 | 1034 | [[package]] 1035 | name = "signal-hook-registry" 1036 | version = "1.4.2" 1037 | source = "registry+https://github.com/rust-lang/crates.io-index" 1038 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1039 | dependencies = [ 1040 | "libc", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "smallvec" 1045 | version = "1.13.2" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1048 | 1049 | [[package]] 1050 | name = "socket2" 1051 | version = "0.5.7" 1052 | source = "registry+https://github.com/rust-lang/crates.io-index" 1053 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1054 | dependencies = [ 1055 | "libc", 1056 | "windows-sys", 1057 | ] 1058 | 1059 | [[package]] 1060 | name = "subtle" 1061 | version = "2.5.0" 1062 | source = "registry+https://github.com/rust-lang/crates.io-index" 1063 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 1064 | 1065 | [[package]] 1066 | name = "syn" 1067 | version = "2.0.93" 1068 | source = "registry+https://github.com/rust-lang/crates.io-index" 1069 | checksum = "9c786062daee0d6db1132800e623df74274a0a87322d8e183338e01b3d98d058" 1070 | dependencies = [ 1071 | "proc-macro2", 1072 | "quote", 1073 | "unicode-ident", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "termtree" 1078 | version = "0.4.1" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" 1081 | 1082 | [[package]] 1083 | name = "thiserror" 1084 | version = "2.0.9" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "f072643fd0190df67a8bab670c20ef5d8737177d6ac6b2e9a236cb096206b2cc" 1087 | dependencies = [ 1088 | "thiserror-impl", 1089 | ] 1090 | 1091 | [[package]] 1092 | name = "thiserror-impl" 1093 | version = "2.0.9" 1094 | source = "registry+https://github.com/rust-lang/crates.io-index" 1095 | checksum = "7b50fa271071aae2e6ee85f842e2e28ba8cd2c5fb67f11fcb1fd70b276f9e7d4" 1096 | dependencies = [ 1097 | "proc-macro2", 1098 | "quote", 1099 | "syn", 1100 | ] 1101 | 1102 | [[package]] 1103 | name = "time" 1104 | version = "0.3.37" 1105 | source = "registry+https://github.com/rust-lang/crates.io-index" 1106 | checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" 1107 | dependencies = [ 1108 | "deranged", 1109 | "itoa", 1110 | "num-conv", 1111 | "powerfmt", 1112 | "serde", 1113 | "time-core", 1114 | "time-macros", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "time-core" 1119 | version = "0.1.2" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1122 | 1123 | [[package]] 1124 | name = "time-macros" 1125 | version = "0.2.19" 1126 | source = "registry+https://github.com/rust-lang/crates.io-index" 1127 | checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" 1128 | dependencies = [ 1129 | "num-conv", 1130 | "time-core", 1131 | ] 1132 | 1133 | [[package]] 1134 | name = "tinytemplate" 1135 | version = "1.2.1" 1136 | source = "registry+https://github.com/rust-lang/crates.io-index" 1137 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 1138 | dependencies = [ 1139 | "serde", 1140 | "serde_json", 1141 | ] 1142 | 1143 | [[package]] 1144 | name = "tokio" 1145 | version = "1.43.0" 1146 | source = "registry+https://github.com/rust-lang/crates.io-index" 1147 | checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" 1148 | dependencies = [ 1149 | "backtrace", 1150 | "bytes", 1151 | "libc", 1152 | "mio", 1153 | "parking_lot", 1154 | "pin-project-lite", 1155 | "signal-hook-registry", 1156 | "socket2", 1157 | "tokio-macros", 1158 | "windows-sys", 1159 | ] 1160 | 1161 | [[package]] 1162 | name = "tokio-macros" 1163 | version = "2.5.0" 1164 | source = "registry+https://github.com/rust-lang/crates.io-index" 1165 | checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" 1166 | dependencies = [ 1167 | "proc-macro2", 1168 | "quote", 1169 | "syn", 1170 | ] 1171 | 1172 | [[package]] 1173 | name = "typenum" 1174 | version = "1.17.0" 1175 | source = "registry+https://github.com/rust-lang/crates.io-index" 1176 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1177 | 1178 | [[package]] 1179 | name = "unicode-ident" 1180 | version = "1.0.12" 1181 | source = "registry+https://github.com/rust-lang/crates.io-index" 1182 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1183 | 1184 | [[package]] 1185 | name = "uuid" 1186 | version = "1.11.1" 1187 | source = "registry+https://github.com/rust-lang/crates.io-index" 1188 | checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" 1189 | dependencies = [ 1190 | "getrandom", 1191 | ] 1192 | 1193 | [[package]] 1194 | name = "version_check" 1195 | version = "0.9.5" 1196 | source = "registry+https://github.com/rust-lang/crates.io-index" 1197 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 1198 | 1199 | [[package]] 1200 | name = "vrd" 1201 | version = "0.0.5" 1202 | source = "registry+https://github.com/rust-lang/crates.io-index" 1203 | checksum = "ee1067b8d17481f5be71b59d11c329e955ffe36348907e0a4a41b619682bb4af" 1204 | dependencies = [ 1205 | "rand", 1206 | "serde", 1207 | ] 1208 | 1209 | [[package]] 1210 | name = "vrd" 1211 | version = "0.0.7" 1212 | source = "registry+https://github.com/rust-lang/crates.io-index" 1213 | checksum = "08fd4c00822f48600521b6dfa7ed8103e9f38c720e198ff4db0400c925414c80" 1214 | dependencies = [ 1215 | "bitflags", 1216 | "dtt 0.0.5", 1217 | "rand", 1218 | "rlg 0.0.3", 1219 | "serde", 1220 | "serde-big-array", 1221 | "serde_json", 1222 | "tokio", 1223 | "uuid", 1224 | ] 1225 | 1226 | [[package]] 1227 | name = "vrd" 1228 | version = "0.0.8" 1229 | source = "registry+https://github.com/rust-lang/crates.io-index" 1230 | checksum = "46865c0eccde4965b89ced6849abf44ffe603dd838a3666dfff3cc2d9437549d" 1231 | dependencies = [ 1232 | "bitflags", 1233 | "dtt 0.0.6", 1234 | "rand", 1235 | "rlg 0.0.4", 1236 | "serde", 1237 | "serde-big-array", 1238 | "serde_json", 1239 | "tokio", 1240 | "uuid", 1241 | "version_check", 1242 | ] 1243 | 1244 | [[package]] 1245 | name = "wait-timeout" 1246 | version = "0.2.0" 1247 | source = "registry+https://github.com/rust-lang/crates.io-index" 1248 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 1249 | dependencies = [ 1250 | "libc", 1251 | ] 1252 | 1253 | [[package]] 1254 | name = "walkdir" 1255 | version = "2.5.0" 1256 | source = "registry+https://github.com/rust-lang/crates.io-index" 1257 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 1258 | dependencies = [ 1259 | "same-file", 1260 | "winapi-util", 1261 | ] 1262 | 1263 | [[package]] 1264 | name = "wasi" 1265 | version = "0.11.0+wasi-snapshot-preview1" 1266 | source = "registry+https://github.com/rust-lang/crates.io-index" 1267 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1268 | 1269 | [[package]] 1270 | name = "wasm-bindgen" 1271 | version = "0.2.92" 1272 | source = "registry+https://github.com/rust-lang/crates.io-index" 1273 | checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" 1274 | dependencies = [ 1275 | "cfg-if", 1276 | "wasm-bindgen-macro", 1277 | ] 1278 | 1279 | [[package]] 1280 | name = "wasm-bindgen-backend" 1281 | version = "0.2.92" 1282 | source = "registry+https://github.com/rust-lang/crates.io-index" 1283 | checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" 1284 | dependencies = [ 1285 | "bumpalo", 1286 | "log", 1287 | "once_cell", 1288 | "proc-macro2", 1289 | "quote", 1290 | "syn", 1291 | "wasm-bindgen-shared", 1292 | ] 1293 | 1294 | [[package]] 1295 | name = "wasm-bindgen-macro" 1296 | version = "0.2.92" 1297 | source = "registry+https://github.com/rust-lang/crates.io-index" 1298 | checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" 1299 | dependencies = [ 1300 | "quote", 1301 | "wasm-bindgen-macro-support", 1302 | ] 1303 | 1304 | [[package]] 1305 | name = "wasm-bindgen-macro-support" 1306 | version = "0.2.92" 1307 | source = "registry+https://github.com/rust-lang/crates.io-index" 1308 | checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" 1309 | dependencies = [ 1310 | "proc-macro2", 1311 | "quote", 1312 | "syn", 1313 | "wasm-bindgen-backend", 1314 | "wasm-bindgen-shared", 1315 | ] 1316 | 1317 | [[package]] 1318 | name = "wasm-bindgen-shared" 1319 | version = "0.2.92" 1320 | source = "registry+https://github.com/rust-lang/crates.io-index" 1321 | checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" 1322 | 1323 | [[package]] 1324 | name = "web-sys" 1325 | version = "0.3.69" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" 1328 | dependencies = [ 1329 | "js-sys", 1330 | "wasm-bindgen", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "winapi" 1335 | version = "0.3.9" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1338 | dependencies = [ 1339 | "winapi-i686-pc-windows-gnu", 1340 | "winapi-x86_64-pc-windows-gnu", 1341 | ] 1342 | 1343 | [[package]] 1344 | name = "winapi-i686-pc-windows-gnu" 1345 | version = "0.4.0" 1346 | source = "registry+https://github.com/rust-lang/crates.io-index" 1347 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1348 | 1349 | [[package]] 1350 | name = "winapi-util" 1351 | version = "0.1.8" 1352 | source = "registry+https://github.com/rust-lang/crates.io-index" 1353 | checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" 1354 | dependencies = [ 1355 | "windows-sys", 1356 | ] 1357 | 1358 | [[package]] 1359 | name = "winapi-x86_64-pc-windows-gnu" 1360 | version = "0.4.0" 1361 | source = "registry+https://github.com/rust-lang/crates.io-index" 1362 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1363 | 1364 | [[package]] 1365 | name = "windows" 1366 | version = "0.52.0" 1367 | source = "registry+https://github.com/rust-lang/crates.io-index" 1368 | checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" 1369 | dependencies = [ 1370 | "windows-core", 1371 | "windows-targets", 1372 | ] 1373 | 1374 | [[package]] 1375 | name = "windows-core" 1376 | version = "0.52.0" 1377 | source = "registry+https://github.com/rust-lang/crates.io-index" 1378 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 1379 | dependencies = [ 1380 | "windows-targets", 1381 | ] 1382 | 1383 | [[package]] 1384 | name = "windows-sys" 1385 | version = "0.52.0" 1386 | source = "registry+https://github.com/rust-lang/crates.io-index" 1387 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1388 | dependencies = [ 1389 | "windows-targets", 1390 | ] 1391 | 1392 | [[package]] 1393 | name = "windows-targets" 1394 | version = "0.52.5" 1395 | source = "registry+https://github.com/rust-lang/crates.io-index" 1396 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 1397 | dependencies = [ 1398 | "windows_aarch64_gnullvm", 1399 | "windows_aarch64_msvc", 1400 | "windows_i686_gnu", 1401 | "windows_i686_gnullvm", 1402 | "windows_i686_msvc", 1403 | "windows_x86_64_gnu", 1404 | "windows_x86_64_gnullvm", 1405 | "windows_x86_64_msvc", 1406 | ] 1407 | 1408 | [[package]] 1409 | name = "windows_aarch64_gnullvm" 1410 | version = "0.52.5" 1411 | source = "registry+https://github.com/rust-lang/crates.io-index" 1412 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 1413 | 1414 | [[package]] 1415 | name = "windows_aarch64_msvc" 1416 | version = "0.52.5" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 1419 | 1420 | [[package]] 1421 | name = "windows_i686_gnu" 1422 | version = "0.52.5" 1423 | source = "registry+https://github.com/rust-lang/crates.io-index" 1424 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 1425 | 1426 | [[package]] 1427 | name = "windows_i686_gnullvm" 1428 | version = "0.52.5" 1429 | source = "registry+https://github.com/rust-lang/crates.io-index" 1430 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 1431 | 1432 | [[package]] 1433 | name = "windows_i686_msvc" 1434 | version = "0.52.5" 1435 | source = "registry+https://github.com/rust-lang/crates.io-index" 1436 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 1437 | 1438 | [[package]] 1439 | name = "windows_x86_64_gnu" 1440 | version = "0.52.5" 1441 | source = "registry+https://github.com/rust-lang/crates.io-index" 1442 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 1443 | 1444 | [[package]] 1445 | name = "windows_x86_64_gnullvm" 1446 | version = "0.52.5" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 1449 | 1450 | [[package]] 1451 | name = "windows_x86_64_msvc" 1452 | version = "0.52.5" 1453 | source = "registry+https://github.com/rust-lang/crates.io-index" 1454 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 1455 | 1456 | [[package]] 1457 | name = "zeroize" 1458 | version = "1.7.0" 1459 | source = "registry+https://github.com/rust-lang/crates.io-index" 1460 | checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" 1461 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | # Metadata about the package. 3 | authors = ["The Hash (HSH) library contributors "] 4 | build = "build.rs" 5 | categories = [ 6 | "algorithms", 7 | "authentication", 8 | "cryptography", 9 | "data-structures", 10 | "encoding", 11 | ] 12 | description = """ 13 | Quantum-Resistant Cryptographic Hash Library for Password Encryption and 14 | Verification in Rust. 15 | """ 16 | documentation = "https://docs.rs/hsh" 17 | edition = "2021" 18 | exclude = [ 19 | "/.git/*", 20 | "/.github/*", 21 | "/.gitignore", 22 | "/.vscode/*" 23 | ] 24 | homepage = "https://hshlib.com/" 25 | include = [ 26 | "/CONTRIBUTING.md", 27 | "/LICENSE-APACHE", 28 | "/LICENSE-MIT", 29 | "/benches/**", 30 | "/build.rs", 31 | "/Cargo.toml", 32 | "/examples/**", 33 | "/README.md", 34 | "/src/**", 35 | "/tests/**", 36 | ] 37 | keywords = ["argon2", "argon2i", "hash", "password", "security"] 38 | license = "MIT OR Apache-2.0" 39 | name = "hsh" 40 | readme = "README.md" 41 | repository = "https://github.com/sebastienrousseau/hsh/" 42 | rust-version = "1.60" 43 | version = "0.0.8" 44 | 45 | [[bench]] 46 | # Benchmarking configuration. 47 | name = "benchmark" 48 | harness = false 49 | path = "benches/criterion.rs" 50 | 51 | [profile.bench] 52 | debug = true 53 | 54 | [[example]] 55 | # Example configuration. 56 | name = "hsh" 57 | path = "examples/hsh.rs" 58 | 59 | [dependencies] 60 | # Dependencies of the package. 61 | argon2rs = "0.2.5" 62 | base64 = "0.22.1" 63 | bcrypt = "0.16.0" 64 | dtt = "0.0.9" 65 | log = {version="0.4.25", features = ["std"] } 66 | scrypt = "0.11.0" 67 | serde = { version = "1.0.216", features = ["derive"] } 68 | serde_json = "1.0.137" 69 | vrd = "0.0.8" 70 | 71 | [dev-dependencies] 72 | # Dependencies for testing and development. 73 | assert_cmd = "2.0.14" 74 | criterion = "0.5.1" 75 | 76 | [lib] 77 | # Metadata about the library. 78 | crate-type = ["lib"] 79 | name = "hsh" 80 | path = "src/lib.rs" 81 | 82 | [features] 83 | # No default features 84 | default = [] 85 | 86 | [package.metadata.docs.rs] 87 | targets = ["x86_64-unknown-linux-gnu"] 88 | rustdoc-args = ["--generate-link-to-definition"] 89 | 90 | # Linting config 91 | [lints.rust] 92 | 93 | ## Warn 94 | # box_pointers = "warn" 95 | missing_copy_implementations = "warn" 96 | missing_docs = "warn" 97 | unstable_features = "warn" 98 | # unused_crate_dependencies = "warn" 99 | unused_extern_crates = "warn" 100 | unused_results = "warn" 101 | 102 | ## Allow 103 | bare_trait_objects = "allow" 104 | elided_lifetimes_in_paths = "allow" 105 | non_camel_case_types = "allow" 106 | non_upper_case_globals = "allow" 107 | trivial_bounds = "allow" 108 | unsafe_code = "allow" 109 | 110 | ## Forbid 111 | missing_debug_implementations = "forbid" 112 | non_ascii_idents = "forbid" 113 | unreachable_pub = "forbid" 114 | 115 | ## Deny 116 | dead_code = "deny" 117 | deprecated_in_future = "deny" 118 | ellipsis_inclusive_range_patterns = "deny" 119 | explicit_outlives_requirements = "deny" 120 | future_incompatible = { level = "deny", priority = -1 } 121 | keyword_idents = "deny" 122 | macro_use_extern_crate = "deny" 123 | meta_variable_misuse = "deny" 124 | missing_fragment_specifier = "deny" 125 | noop_method_call = "deny" 126 | pointer_structural_match = "deny" 127 | rust_2018_idioms = { level = "deny", priority = -1 } 128 | rust_2021_compatibility = { level = "deny", priority = -1 } 129 | single_use_lifetimes = "deny" 130 | trivial_casts = "deny" 131 | trivial_numeric_casts = "deny" 132 | unused = { level = "deny", priority = -1 } 133 | unused_features = "deny" 134 | unused_import_braces = "deny" 135 | unused_labels = "deny" 136 | unused_lifetimes = "deny" 137 | unused_macro_rules = "deny" 138 | unused_qualifications = "deny" 139 | variant_size_differences = "deny" 140 | 141 | [package.metadata.clippy] 142 | warn-lints = ["clippy::all", "clippy::pedantic", "clippy::cargo", "clippy::nursery"] 143 | 144 | [profile.dev] 145 | codegen-units = 256 146 | debug = true 147 | debug-assertions = true 148 | incremental = true 149 | lto = false 150 | opt-level = 0 151 | overflow-checks = true 152 | panic = 'unwind' 153 | rpath = false 154 | strip = false 155 | 156 | [profile.release] 157 | codegen-units = 1 158 | debug = false 159 | debug-assertions = false 160 | incremental = false 161 | lto = true 162 | opt-level = "s" 163 | overflow-checks = false 164 | panic = "abort" 165 | rpath = false 166 | strip = "symbols" 167 | 168 | [profile.test] 169 | codegen-units = 256 170 | debug = true 171 | debug-assertions = true 172 | incremental = true 173 | lto = false 174 | opt-level = 0 175 | overflow-checks = true 176 | rpath = false 177 | strip = false 178 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright © 2022-2023 Mini Functions. All rights reserved. 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | https://opensource.org/licenses/Apache-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022-2023 Sebastien Rousseau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, 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, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hash (HSH) logo 5 | 6 | 7 | # Hash (HSH) 8 | 9 | Quantum-Resistant Cryptographic Hash Library for Password Hashing and 10 | Verification 11 | 12 | *Part of the [Mini Functions][0] family of libraries.* 13 | 14 | 15 |
16 | 17 | 18 | ![Hash (HSH) Banner][banner] 19 | 20 | [![Made With Rust][made-with-rust]][6] [![Crates.io][crates-badge]][8] 21 | [![Lib.rs][libs-badge]][10] [![Docs.rs][docs-badge]][9] 22 | [![License][license-badge]][2] [![Codecov][codecov-badge]][11] 23 | 24 | • [Website][0] • [Documentation][9] • [Report Bug][3] 25 | • [Request Feature][3] • [Contributing Guidelines][4] 26 | 27 | 28 |
29 | 30 | 31 | ![divider][divider] 32 | 33 | ## Overview 📖 34 | 35 | The `Hash (HSH)` Rust library provides an interface for implementing 36 | secure hash and digest algorithms, specifically designed for password 37 | encryption and verification. 38 | 39 | The library provides a simple API that makes it easy to store and verify 40 | hashed passwords. It enables robust security for passwords, using the 41 | latest advancements in `Quantum-resistant cryptography`. Quantum- 42 | resistant cryptography refers to cryptographic algorithms, usually 43 | public-key algorithms, that are thought to be secure against an attack 44 | by a quantum computer. As quantum computing continues to advance, this 45 | feature of the library assures that the passwords managed through this 46 | system remain secure even against cutting-edge computational 47 | capabilities. 48 | 49 | The library supports the following Password Hashing Schemes (Password 50 | Based Key Derivation Functions): 51 | 52 | - [**Argon2i**](): A cutting-edge 53 | and highly secure key derivation function designed to protect against 54 | both traditional brute-force attacks and rainbow table attacks. 55 | (Recommended) 56 | - [**Bcrypt**](): A password 57 | hashing function designed to be secure against brute-force attacks. 58 | It is a work-factor function, which means that it takes a certain 59 | amount of time to compute. This makes it difficult to attack with a 60 | brute-force algorithm. 61 | - [**Scrypt**](): A password 62 | hashing function designed to be secure against both brute-force 63 | attacks and rainbow table attacks. It is a memory-hard and work- 64 | factor function, which means that it requires a lot of memory and 65 | time to compute. This makes it very difficult to attack with a GPU 66 | or other parallel computing device. 67 | 68 | The library is a valuable tool for developers who need to store and 69 | verify passwords in a secure manner. It is easy to use and can be 70 | integrated into a variety of applications. 71 | 72 | ## Features ✨ 73 | 74 | - **Compliant with multiple Password Hashing Schemes (Password Based Key Derivation Functions) such as Argon2i, Bcrypt and Scrypt.** This makes the library more versatile and can be used in a variety of applications. 75 | - **Quantum-resistant, making it secure against future attacks using quantum computers.** This is an important feature as quantum computers become more powerful. 76 | - **Easy to use.** The library provides a simple API that makes it easy to store and verify hashed passwords. 77 | - **Can be integrated into a variety of applications.** The library is written in Rust, which makes it easy to integrate into any Rust project and is fast, efficient, and secure. 78 | 79 | ### Secure password storage 80 | 81 | Hash (HSH) provides a secure way to store and verify hashed passwords. 82 | Passwords are hashed using the argon2i, bcrypt, scrypt algorithms, which 83 | are considered one of the most secure hashing algorithms available 84 | today. The library provides a simple interface for generating and 85 | verifying hashes, making it easy to implement secure password storage 86 | in any Rust application. 87 | 88 | ### Easy to use 89 | 90 | Hash (HSH) includes simple functions for generating and verifying 91 | password hashes, and managing password and salt values. Developers can 92 | easily integrate the library into their Rust projects and start using 93 | it right away. The library is designed to be intuitive and easy to use, 94 | so developers can build apps without worrying about password security. 95 | 96 | ### Flexible 97 | 98 | Hash (HSH) allows users to customize the length of passwords and salts 99 | used in generating hashes. This flexibility allows developers to tailor 100 | the library to their specific needs, whether they require shorter or 101 | longer password and salt values. The library also includes macros that 102 | make it easy to work with the Hash structure, allowing developers to 103 | quickly and easily set and retrieve password and salt values. 104 | 105 | ### Lightweight 106 | 107 | Hash (HSH) is a lightweight library that can easily integrate into any 108 | Rust project. The library has no external dependencies and is efficient. 109 | It means that developers can add secure password storage to their 110 | applications without having to worry about significant performance 111 | overheads. 112 | 113 | ## Installation 📦 114 | 115 | It takes just a few minutes to get up and running with `hsh`. 116 | 117 | ### Requirements 118 | 119 | The minimum supported Rust toolchain version is currently Rust 120 | **1.60** or later (stable). It is recommended that you install the 121 | latest stable version of Rust. 122 | 123 | ### Platform support 124 | 125 | `hsh` supports a variety of CPU architectures. It is supported and tested on 126 | MacOS, Linux, and Windows. 127 | 128 | ### Documentation 129 | 130 | > ℹ️ **Info:** Please check out our [website][0] for more information 131 | and find our documentation on [docs.rs][9], [lib.rs][10] and 132 | [crates.io][8]. 133 | 134 | ## Usage 📖 135 | 136 | To use `hsh` in your project, add the following to your `Cargo.toml` 137 | file: 138 | 139 | ```toml 140 | [dependencies] 141 | hsh = "0.0.8" 142 | ``` 143 | 144 | Add the following to your `main.rs` file: 145 | 146 | ```rust 147 | extern crate hsh; 148 | use hsh::*; 149 | ``` 150 | 151 | then you can use the functions in your application code. 152 | 153 | ### Examples 154 | 155 | `Hash (HSH)` comes with a set of examples that you can use to get 156 | started. The examples are located in the `examples` directory of the 157 | project. To run the examples, clone the repository and run the following 158 | command in your terminal from the project root directory. 159 | 160 | ```shell 161 | cargo run --example hsh 162 | ``` 163 | 164 | ## Semantic Versioning Policy 🚥 165 | 166 | For transparency into our release cycle and in striving to maintain 167 | backward compatibility, `Hash (HSH)` follows [semantic versioning][7]. 168 | 169 | ## License 📝 170 | 171 | The project is licensed under the terms of both the MIT license and the 172 | Apache License (Version 2.0). 173 | 174 | - [Apache License, Version 2.0][1] 175 | - [MIT license][2] 176 | 177 | ## Contribution 🤝 178 | 179 | Unless you explicitly state otherwise, any contribution intentionally 180 | submitted for inclusion in the work by you, as defined in the Apache-2.0 181 | license, shall be dual licensed as above, without any additional terms 182 | or conditions. 183 | 184 | ![divider][divider] 185 | 186 | ## Acknowledgements 💙 187 | 188 | A big thank you to all the awesome contributors of [Mini Functions][6] 189 | for their help and support. 190 | 191 | And a special thank you goes to the 192 | [Rust Reddit](https://www.reddit.com/r/rust/) community for providing a 193 | lot of useful suggestions on how to improve this project. 194 | 195 | [0]: https://minifunctions.com/hsh 196 | [1]: http://www.apache.org/licenses/LICENSE-2.0 197 | [2]: http://opensource.org/licenses/MIT 198 | [3]: https://github.com/sebastienrousseau/hsh/issues 199 | [4]: https://raw.githubusercontent.com/sebastienrousseau/hsh/main/.github/CONTRIBUTING.md 200 | [6]: https://github.com/sebastienrousseau/hsh/graphs/contributors 201 | [7]: http://semver.org/ 202 | [8]: https://crates.io/crates/hsh 203 | [9]: https://docs.rs/hsh 204 | [10]: https://lib.rs/crates/hsh 205 | [11]: https://codecov.io/github/sebastienrousseau/hsh 206 | 207 | [banner]: https://kura.pro/hsh/images/titles/title-hsh.svg "Hash (HSH) Banner" 208 | [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/cmn?style=for-the-badge&token=DMNW4DN0LO 'Codecov' 209 | [crates-badge]: https://img.shields.io/crates/v/hsh.svg?style=for-the-badge 'Crates.io' 210 | [divider]: https://kura.pro/common/images/elements/divider.svg "divider" 211 | [docs-badge]: https://img.shields.io/docsrs/hsh.svg?style=for-the-badge 'Docs.rs' 212 | [libs-badge]: https://img.shields.io/badge/lib.rs-v0.0.8-orange.svg?style=for-the-badge 'Lib.rs' 213 | [license-badge]: https://img.shields.io/crates/l/hsh.svg?style=for-the-badge 'License' 214 | [made-with-rust]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust 'Made With Rust' 215 | -------------------------------------------------------------------------------- /TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | Hash (HSH) logo 5 | 6 | 7 | # Hash (HSH) v0.0.8 8 | 9 | Quantum-Resistant Cryptographic Hash Library for Password Hashing and 10 | Verification 11 | 12 | *Part of the [Mini Functions][0] family of libraries.* 13 | 14 | 15 |
16 | 17 | 18 | ![Hash (HSH) Banner][banner] 19 | 20 | [![Made With Rust][made-with-rust]][6] [![Crates.io][crates-badge]][8] 21 | [![Lib.rs][libs-badge]][10] [![Docs.rs][docs-badge]][9] 22 | [![License][license-badge]][2] [![Codecov][codecov-badge]][11] 23 | 24 | • [Website][0] • [Documentation][9] • [Report Bug][3] 25 | • [Request Feature][3] • [Contributing Guidelines][4] 26 | 27 | 28 |
29 | 30 | 31 | ![divider][divider] 32 | 33 | ## Overview 📖 34 | 35 | The `Hash (HSH)` Rust library provides an interface for implementing secure hash and digest algorithms, specifically designed for password encryption and verification. 36 | 37 | The library provides a simple API that makes it easy to store and verify hashed passwords. It enables robust security for passwords, using the latest advancements in **Quantum-resistant cryptography**. Quantum-resistant cryptography refers to cryptographic algorithms, that are thought to be secure against an attack by a quantum computer. As quantum computing continues to advance, this feature of the library assures that the passwords managed through this system remain secure even against cutting-edge computational capabilities. 38 | 39 | The library supports the following Password Hashing Schemes (Password Based Key Derivation Functions): 40 | 41 | - [**Argon2i**](): A cutting-edge and highly secure key derivation function designed to protect against both traditional brute-force attacks and rainbow table attacks. (Recommended) 42 | - [**Bcrypt**](): A password hashing function designed to be secure against brute-force attacks. It is a work-factor function, which means that it takes a certain amount of time to compute. This makes it difficult to attack with a brute-force algorithm. 43 | - [**Scrypt**](): A password hashing function designed to be secure against both brute-force attacks and rainbow table attacks. It is a memory-hard and work-factor function, which means that it requires a lot of memory and time to compute. This makes it very difficult to attack with a GPU or other parallel computing device. 44 | 45 | The library is a valuable tool for developers who need to store and verify passwords in a secure manner. It is easy to use and can be integrated into a variety of applications. 46 | 47 | ## Features ✨ 48 | 49 | - **Compliant with multiple Password Hashing Schemes (Password Based Key Derivation Functions) such as Argon2i, Bcrypt and Scrypt.** This makes the library more versatile and can be used in a variety of applications. 50 | - **Quantum-resistant, making it secure against future attacks using quantum computers.** This is an important feature as quantum computers become more powerful. 51 | - **Easy to use.** The library provides a simple API that makes it easy to store and verify hashed passwords. 52 | - **Can be integrated into a variety of applications.** The library is written in Rust, which makes it easy to integrate into any Rust project and is fast, efficient, and secure. 53 | 54 | ### Secure password storage 55 | 56 | Hash (HSH) provides a secure way to store and verify hashed passwords. Passwords are hashed using the argon2i, bcrypt, scrypt algorithms, which are considered one of the most secure hashing algorithms available today. The library provides a simple interface for generating and verifying hashes, making it easy to implement secure password storage in any Rust application. 57 | 58 | ### Easy to use 59 | 60 | Hash (HSH) includes simple functions for generating and verifying password hashes, and managing password and salt values. Developers can easily integrate the library into their Rust projects and start using it right away. The library is designed to be intuitive and easy to use, so developers can build apps without worrying about password security. 61 | 62 | ### Flexible 63 | 64 | Hash (HSH) allows users to customize the length of passwords and salts used in generating hashes. This flexibility allows developers to tailor the library to their specific needs, whether they require shorter or longer password and salt values. The library also includes macros that make it easy to work with the Hash structure, allowing developers to quickly and easily set and retrieve password and salt values. 65 | 66 | ### Lightweight 67 | 68 | Hash (HSH) is a lightweight library that can easily integrate into any Rust project. The library has no external dependencies and is efficient. It means that developers can add secure password storage to their applications without having to worry about significant performance overheads. 69 | 70 | [0]: https://minifunctions.com/hsh 71 | [2]: http://opensource.org/licenses/MIT 72 | [3]: https://github.com/sebastienrousseau/hsh/issues 73 | [4]: https://raw.githubusercontent.com/sebastienrousseau/hsh/main/.github/CONTRIBUTING.md 74 | [6]: https://github.com/sebastienrousseau/hsh/graphs/contributors 75 | [8]: https://crates.io/crates/hsh 76 | [9]: https://docs.rs/hsh 77 | [10]: https://lib.rs/crates/hsh 78 | [11]: https://codecov.io/github/sebastienrousseau/hsh 79 | 80 | [banner]: https://kura.pro/hsh/images/titles/title-hsh.svg "Hash (HSH) Banner" 81 | [codecov-badge]: https://img.shields.io/codecov/c/github/sebastienrousseau/cmn?style=for-the-badge&token=DMNW4DN0LO 'Codecov' 82 | [crates-badge]: https://img.shields.io/crates/v/hsh.svg?style=for-the-badge 'Crates.io' 83 | [divider]: https://kura.pro/common/images/elements/divider.svg "divider" 84 | [docs-badge]: https://img.shields.io/docsrs/hsh.svg?style=for-the-badge 'Docs.rs' 85 | [libs-badge]: https://img.shields.io/badge/lib.rs-v0.0.8-orange.svg?style=for-the-badge 'Lib.rs' 86 | [license-badge]: https://img.shields.io/crates/l/hsh.svg?style=for-the-badge 'License' 87 | [made-with-rust]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust 'Made With Rust' 88 | 89 | ## Changelog 📚 90 | 91 | - 92 | -------------------------------------------------------------------------------- /benches/criterion.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | //! Benchmarking the Hash (HSH) library using Criterion.rs 5 | 6 | #![allow(missing_docs)] 7 | 8 | use criterion::{ 9 | black_box, criterion_group, criterion_main, Criterion, 10 | }; 11 | use hsh::models::hash::Hash; 12 | 13 | #[allow(unused_results)] 14 | fn generate_hash_benchmark(c: &mut Criterion) { 15 | c.bench_function("generate_hash", |b| { 16 | b.iter(|| { 17 | Hash::generate_hash( 18 | black_box("password"), 19 | black_box("salt12345"), 20 | black_box("argon2i"), 21 | ) 22 | }) 23 | }); 24 | } 25 | 26 | #[allow(unused_results)] 27 | fn new_hash_benchmark(c: &mut Criterion) { 28 | c.bench_function("new_hash", |b| { 29 | b.iter(|| { 30 | Hash::new( 31 | black_box("password"), 32 | black_box("salt12345"), 33 | black_box("argon2i"), 34 | ) 35 | }) 36 | }); 37 | } 38 | 39 | #[allow(unused_results)] 40 | fn set_password_benchmark(c: &mut Criterion) { 41 | let mut hash = 42 | Hash::new("password", "salt12345", "argon2i").unwrap(); // Unwrap the Result 43 | 44 | c.bench_function("set_password", |b| { 45 | b.iter(|| { 46 | Hash::set_password( 47 | &mut hash, // Pass the `hash` instance 48 | black_box("new_password"), 49 | black_box("new_salt12345"), 50 | black_box("argon2i"), 51 | ) 52 | .unwrap() // Unwrap the Result 53 | }) 54 | }); 55 | } 56 | 57 | #[allow(unused_results)] 58 | fn verify_benchmark(c: &mut Criterion) { 59 | let hash = Hash::new("password", "salt12345", "argon2i").unwrap(); // Unwrap the Result 60 | 61 | c.bench_function("verify", |b| { 62 | b.iter(|| hash.verify(black_box("password")).unwrap()) // Call verify on the instance 63 | }); 64 | } 65 | 66 | // Run the benchmarks in a group 67 | criterion_group!( 68 | // Run `benches` 69 | benches, 70 | // Run `generate_hash_benchmark` 71 | generate_hash_benchmark, 72 | // Run `new_hash_benchmark` 73 | new_hash_benchmark, 74 | // Run `set_password_benchmark` 75 | set_password_benchmark, 76 | // Run `verify_benchmark` 77 | verify_benchmark 78 | ); 79 | 80 | criterion_main!(benches); 81 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | //! This is the main function for the build script. 5 | //! 6 | //! Currently, it only instructs Cargo to re-run this build script if `build.rs` is changed. 7 | fn main() { 8 | // Avoid unnecessary re-building. 9 | println!("cargo:rerun-if-changed=build.rs"); 10 | } 11 | -------------------------------------------------------------------------------- /deny.toml: -------------------------------------------------------------------------------- 1 | [licenses] 2 | # The lint level for crates which do not have a detectable license 3 | unlicensed = "deny" 4 | 5 | # List of explicitly allowed licenses 6 | # See https://spdx.org/licenses/ for list of possible licenses 7 | # [possible values: any SPDX 3.7 short identifier (+ optional exception)]. 8 | allow = [ 9 | "Apache-2.0", 10 | "MIT", 11 | "CC0-1.0", 12 | "ISC", 13 | "0BSD", 14 | "BSD-2-Clause", 15 | "BSD-3-Clause", 16 | "Unlicense", 17 | "Unicode-DFS-2016", 18 | ] 19 | 20 | # List of banned licenses 21 | [bans] 22 | multiple-versions = "deny" 23 | 24 | 25 | # The lint level for licenses considered copyleft 26 | copyleft = "deny" 27 | 28 | # Blanket approval or denial for OSI-approved or FSF Free/Libre licenses 29 | # * both - The license will only be approved if it is both OSI-approved *AND* FSF/Free 30 | # * either - The license will be approved if it is either OSI-approved *OR* FSF/Free 31 | # * osi-only - The license will be approved if is OSI-approved *AND NOT* FSF/Free 32 | # * fsf-only - The license will be approved if is FSF/Free *AND NOT* OSI-approved 33 | # * neither - The license will be denied if is FSF/Free *OR* OSI-approved 34 | allow-osi-fsf-free = "either" 35 | 36 | # The confidence threshold for detecting a license from license text. 37 | # The higher the value, the more closely the license text must be to the 38 | # canonical license text of a valid SPDX license file. 39 | # [possible values: any between 0.0 and 1.0]. 40 | confidence-threshold = 0.8 41 | 42 | # The graph highlighting used when creating dotgraphs for crates 43 | # with multiple versions 44 | # * lowest-version - The path to the lowest versioned duplicate is highlighted 45 | # * simplest-path - The path to the version with the fewest edges is highlighted 46 | # * all - Both lowest-version and simplest-path are used 47 | highlight = "all" 48 | 49 | # List of crates that are allowed. Use with care! 50 | allow = [] 51 | 52 | # List of crates to deny 53 | deny = [ 54 | # Each entry the name of a crate and a version range. If version is 55 | # not specified, all versions will be matched. 56 | ] 57 | 58 | # Certain crates/versions that will be skipped when doing duplicate detection. 59 | skip = [] 60 | 61 | # Similarly to `skip` allows you to skip certain crates during duplicate detection, 62 | # unlike skip, it also includes the entire tree of transitive dependencies starting at 63 | # the specified crate, up to a certain depth, which is by default infinite 64 | skip-tree = [] 65 | 66 | 67 | [advisories] 68 | notice = "deny" 69 | unmaintained = "deny" 70 | unsound = "deny" 71 | vulnerability = "deny" -------------------------------------------------------------------------------- /examples/hsh.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | //! Using the Hash (HSH) library 5 | 6 | use hsh::{ 7 | models::{hash::Hash, hash_algorithm::HashAlgorithm}, 8 | new_hash, 9 | }; 10 | use std::str::FromStr; 11 | 12 | /// This function demonstrates how to create and verify password hashes using Argon2i, Bcrypt, and Scrypt algorithms. 13 | /// 14 | /// # Example 15 | /// 16 | /// ```rust 17 | /// use hsh::models::{hash::Hash, salt::Salt}; 18 | /// 19 | /// // Function to create and verify hash 20 | /// fn create_and_verify_hash() { 21 | /// // Create new hashes for Argon2i, Bcrypt, and Scrypt 22 | /// let password = "password"; 23 | /// let salt_argon2i: Salt = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 24 | /// let salt_scrypt: Salt = vec![10, 11, 12, 13, 14, 15, 16, 17, 18, 19]; 25 | /// let cost_bcrypt = 16; 26 | /// 27 | /// let hash_argon2i = Hash::new_argon2i(password, salt_argon2i).unwrap(); 28 | /// let hash_bcrypt = Hash::new_bcrypt(password, cost_bcrypt).unwrap(); 29 | /// let hash_scrypt = Hash::new_scrypt(password, salt_scrypt).unwrap(); 30 | /// 31 | /// // Verify these hashes 32 | /// verify_password(&hash_argon2i, "password", "Argon2i"); 33 | /// verify_password(&hash_bcrypt, "password", "BCrypt"); 34 | /// verify_password(&hash_scrypt, "password", "Scrypt"); 35 | /// 36 | /// // ... (the rest of the function) 37 | /// } 38 | /// ``` 39 | /// 40 | /// Note: This is a simplified example, and in a real-world application, you should handle errors and edge cases more carefully. 41 | fn create_and_verify_hash() { 42 | // Create new hashes for Argon2i, Bcrypt, and Scrypt 43 | let hash_argon2i = 44 | Hash::new_argon2i("password", "salt1234".into()).unwrap(); 45 | let hash_bcrypt = Hash::new_bcrypt("password", 16).unwrap(); 46 | let hash_scrypt = 47 | Hash::new_scrypt("password", "salt1234".into()).unwrap(); 48 | 49 | // Verify these hashes 50 | verify_password(&hash_argon2i, "password", "Argon2i"); 51 | verify_password(&hash_bcrypt, "password", "BCrypt"); 52 | verify_password(&hash_scrypt, "password", "Scrypt"); 53 | 54 | // Update the hashes 55 | let mut new_hash_argon2i = hash_argon2i.clone(); 56 | new_hash_argon2i 57 | .set_password("new_password", "salt1234", "argon2i") 58 | .unwrap(); 59 | 60 | let mut new_hash_bcrypt = hash_bcrypt.clone(); 61 | new_hash_bcrypt 62 | .set_password("new_password", "salt1234", "bcrypt") 63 | .unwrap(); 64 | 65 | let mut new_hash_scrypt = hash_scrypt.clone(); 66 | new_hash_scrypt 67 | .set_password("new_password", "salt1234", "scrypt") 68 | .unwrap(); 69 | 70 | // Verify the updated hashes 71 | verify_password(&new_hash_argon2i, "new_password", "Argon2i"); 72 | verify_password(&new_hash_bcrypt, "new_password", "BCrypt"); 73 | verify_password(&new_hash_scrypt, "new_password", "Scrypt"); 74 | } 75 | 76 | // Function to verify the password 77 | fn verify_password(hash: &Hash, password: &str, algorithm: &str) { 78 | // Print header 79 | println!( 80 | "\n===[ Verifying Password with {} Algorithm ]===\n", 81 | algorithm 82 | ); 83 | 84 | let is_valid = hash.verify(password); 85 | match is_valid { 86 | Ok(valid) => { 87 | println!("Algorithm: {}", algorithm); 88 | println!( 89 | "Provided password for verification: {}", 90 | password 91 | ); 92 | println!( 93 | "Salt used for verification: {}", 94 | String::from_utf8_lossy(hash.salt()) 95 | ); 96 | println!( 97 | "🦀 Password verification result for {}: ✅ {:?}", 98 | algorithm, valid 99 | ); 100 | } 101 | Err(e) => { 102 | eprintln!( 103 | "🦀 Error during password verification for {}: ❌ {}", 104 | algorithm, e 105 | ); 106 | } 107 | } 108 | 109 | // Print footer 110 | println!("\n==================================================\n"); 111 | } 112 | 113 | // Function to parse and display hash algorithms and their string representations 114 | fn parse_and_display_hash() { 115 | // Print header for parsing algorithms 116 | println!("\n===[ Parsing Hash Algorithms ]===\n"); 117 | 118 | let parsed_argon2i = HashAlgorithm::from_str("argon2i").unwrap(); 119 | let parsed_bcrypt = HashAlgorithm::from_str("bcrypt").unwrap(); 120 | let parsed_scrypt = HashAlgorithm::from_str("scrypt").unwrap(); 121 | 122 | println!("🦀 Parsed Argon2i hash algorithm: {}", parsed_argon2i); 123 | println!("🦀 Parsed Bcrypt hash algorithm: {}", parsed_bcrypt); 124 | println!("🦀 Parsed Scrypt hash algorithm: {}", parsed_scrypt); 125 | 126 | // Print footer for parsing algorithms 127 | println!("\n======================================\n"); 128 | 129 | // Print header for hash to string conversion 130 | println!("\n===[ Hash to String Conversion ]===\n"); 131 | 132 | let argon2i_hash = new_hash!("password", "salt12345", "argon2i"); 133 | let bcrypt_hash = new_hash!("password", "salt12345", "bcrypt"); 134 | let scrypt_hash = new_hash!("password", "salt12345", "scrypt"); 135 | 136 | let argon2i_hash_string = match argon2i_hash { 137 | Ok(hash) => hash.to_string_representation(), 138 | Err(e) => format!("Error: {}", e), 139 | }; 140 | let bcrypt_hash_string = match bcrypt_hash { 141 | Ok(hash) => hash.to_string_representation(), 142 | Err(e) => format!("Error: {}", e), 143 | }; 144 | let scrypt_hash_string = match scrypt_hash { 145 | Ok(hash) => hash.to_string_representation(), 146 | Err(e) => format!("Error: {}", e), 147 | }; 148 | 149 | println!("🦀 Argon2i Hash to a string: {}", argon2i_hash_string); 150 | println!("🦀 Bcrypt Hash to a string: {}", bcrypt_hash_string); 151 | println!("🦀 Scrypt Hash to a string: {}", scrypt_hash_string); 152 | 153 | // Print footer for hash to string conversion 154 | println!("\n========================================\n"); 155 | } 156 | 157 | // Main function 158 | fn main() { 159 | create_and_verify_hash(); 160 | parse_and_display_hash(); 161 | } 162 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | max_width = 72 3 | tab_spaces = 4 4 | use_field_init_shorthand = true -------------------------------------------------------------------------------- /src/algorithms/argon2i.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | use crate::models::hash_algorithm::HashingAlgorithm; 5 | use argon2rs::argon2i_simple; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /// Implementation of the Argon2i hashing algorithm. 9 | /// 10 | /// `Argon2i` is a struct that represents the Argon2i hashing algorithm, 11 | /// which is a memory-hard algorithm resistant to GPU-based attacks and side-channel attacks. 12 | /// It is one of the multiple hashing algorithms that can be used for password hashing in this library. 13 | /// 14 | /// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation 15 | /// for hashing passwords using the Argon2i algorithm. 16 | #[derive( 17 | Clone, 18 | Copy, 19 | Debug, 20 | Eq, 21 | Hash, 22 | Ord, 23 | PartialEq, 24 | PartialOrd, 25 | Serialize, 26 | Deserialize, 27 | )] 28 | pub struct Argon2i; 29 | 30 | impl HashingAlgorithm for Argon2i { 31 | /// Hashes a given password using the Argon2i algorithm. 32 | /// 33 | /// This method computes a hashed representation of the plaintext `password` using the Argon2i algorithm, 34 | /// combined with the provided `salt` for added security. 35 | /// 36 | /// # Parameters 37 | /// 38 | /// - `password`: The plaintext password to be hashed. 39 | /// - `salt`: A cryptographic salt to prevent rainbow table attacks. 40 | /// 41 | /// # Returns 42 | /// 43 | /// Returns a `Result` with `Ok`, containing the hashed password as a vector of bytes. 44 | /// Currently, this function does not handle hashing errors and will always return `Ok`. 45 | fn hash_password( 46 | password: &str, 47 | salt: &str, 48 | ) -> Result, String> { 49 | Ok(argon2i_simple(password, salt).into_iter().collect()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/algorithms/bcrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | use crate::models::hash_algorithm::HashingAlgorithm; 5 | use bcrypt::{hash, DEFAULT_COST}; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | /// Implementation of the Bcrypt hashing algorithm. 9 | /// 10 | /// `Bcrypt` is a struct that represents the Bcrypt hashing algorithm, 11 | /// which is based on the Blowfish cipher and is particularly effective against brute-force attacks. 12 | /// 13 | /// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation 14 | /// for hashing passwords using the Bcrypt algorithm. 15 | /// 16 | /// # Features 17 | /// 18 | /// - Computationally intensive, making brute-force attacks more difficult. 19 | /// - Uses key stretching to make pre-computed attacks (like rainbow tables) less effective. 20 | /// 21 | /// # Examples 22 | /// 23 | /// ``` 24 | /// use hsh::models::hash_algorithm::HashingAlgorithm; 25 | /// use hsh::algorithms::bcrypt::Bcrypt; 26 | /// 27 | /// let password = "supersecret"; 28 | /// let salt = "randomsalt"; 29 | /// 30 | /// let hashed_password = Bcrypt::hash_password(password, salt).unwrap(); 31 | /// ``` 32 | #[derive( 33 | Clone, 34 | Copy, 35 | Debug, 36 | Eq, 37 | Hash, 38 | Ord, 39 | PartialEq, 40 | PartialOrd, 41 | Serialize, 42 | Deserialize, 43 | )] 44 | pub struct Bcrypt; 45 | 46 | impl HashingAlgorithm for Bcrypt { 47 | /// Hashes a given password using the Bcrypt algorithm. 48 | /// 49 | /// This method computes a hashed representation of the plaintext `password` using the Bcrypt algorithm. 50 | /// Note that the `salt` parameter is not used in this implementation, as Bcrypt generates its own salt internally. 51 | /// 52 | /// # Parameters 53 | /// 54 | /// - `password`: The plaintext password to be hashed. 55 | /// - `_salt`: Unused in this implementation, provided for interface compatibility. 56 | /// 57 | /// # Returns 58 | /// 59 | /// Returns a `Result` containing the hashed password as a vector of bytes. 60 | /// If hashing fails for some reason, returns a `String` detailing the error. 61 | fn hash_password( 62 | password: &str, 63 | _salt: &str, 64 | ) -> Result, String> { 65 | hash(password, DEFAULT_COST) 66 | .map_err(|e| e.to_string()) 67 | .map(|hash_parts| hash_parts.into_bytes()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/algorithms/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | /// The `argon2i` module contains the Argon2i password hashing algorithm. 5 | pub mod argon2i; 6 | 7 | /// The `bcrypt` module contains the Bcrypt password hashing algorithm. 8 | pub mod bcrypt; 9 | 10 | /// The `scrypt` module contains the Scrypt password hashing algorithm. 11 | pub mod scrypt; 12 | -------------------------------------------------------------------------------- /src/algorithms/scrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | use crate::models::hash_algorithm::HashingAlgorithm; 5 | use scrypt::scrypt; 6 | use scrypt::Params; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | /// Implementation of the Scrypt hashing algorithm. 10 | /// 11 | /// `Scrypt` is a struct that represents the Scrypt hashing algorithm, 12 | /// which is a memory-hard algorithm designed to be computationally intensive, 13 | /// thereby making it difficult to perform large-scale custom hardware attacks. 14 | /// 15 | /// This struct implements the `HashingAlgorithm` trait, providing a concrete implementation 16 | /// for hashing passwords using the Scrypt algorithm. 17 | #[derive( 18 | Clone, 19 | Copy, 20 | Debug, 21 | Eq, 22 | Hash, 23 | Ord, 24 | PartialEq, 25 | PartialOrd, 26 | Serialize, 27 | Deserialize, 28 | )] 29 | pub struct Scrypt; 30 | 31 | impl HashingAlgorithm for Scrypt { 32 | /// Hashes a given password using the Scrypt algorithm. 33 | /// 34 | /// Given a plaintext `password` and a `salt`, this method returns a hashed representation 35 | /// of the password using the Scrypt algorithm. 36 | /// 37 | /// # Parameters 38 | /// 39 | /// - `password`: The plaintext password to be hashed. 40 | /// - `salt`: A cryptographic salt to prevent rainbow table attacks. 41 | /// 42 | /// # Returns 43 | /// 44 | /// Returns a `Result` containing the hashed password as a vector of bytes. 45 | /// If hashing fails for some reason, it returns a `String` detailing the error. 46 | fn hash_password( 47 | password: &str, 48 | salt: &str, 49 | ) -> Result, String> { 50 | // The `Params` struct is initialized with specific parameters that define the 51 | // computational cost of the hashing process. The parameters used here are chosen 52 | // to provide a balance between security and performance. Adjust these values based 53 | // on the security requirements and the expected computational capacity. 54 | let params = 55 | Params::new(14, 8, 1, 64).map_err(|e| e.to_string())?; 56 | let mut output = [0u8; 64]; 57 | scrypt( 58 | password.as_bytes(), 59 | salt.as_bytes(), 60 | ¶ms, 61 | &mut output, 62 | ) 63 | .map_err(|e| e.to_string()) 64 | .map(|_| output.to_vec()) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 5 | // SPDX-License-Identifier: Apache-2.0 OR MIT 6 | 7 | //! # Hash (HSH), a Quantum-Resistant Cryptographic Hash Library 8 | //! 9 | //! A Highly Secure Quantum-Resistant Cryptographic Hash Library for Password Encryption and Verification in Rust. Designed with quantum-resistant cryptography, this library provides a robust line of defence against current and emerging computational threats. 10 | //! 11 | //! [![Hash (HSH) Banner][banner]][00] 12 | //! 13 | //! Part of the [Mini Functions][01] family of libraries. 14 | //! 15 | //! [![Available on Crates.io][crate-shield]](https://crates.io/crates/hsh) 16 | //! [![GitHub Repository][github-shield]](https://github.com/sebastienrousseau/hsh) 17 | //! [![Available on Lib.rs][lib-rs-shield]](https://lib.rs/hsh) 18 | //! [![MIT License][license-shield]](http://opensource.org/licenses/MIT) 19 | //! [![Built with Rust][rust-shield]](https://www.rust-lang.org) 20 | //! 21 | //! ## Overview 22 | //! 23 | //! The Hash (HSH) Rust library provides an interface for implementing secure hash and digest algorithms, specifically designed for password encryption and verification. 24 | //! 25 | //! The library provides a simple API that makes it easy to store and verify hashed passwords. It enables robust security for passwords, using the latest advancements in Quantum-resistant cryptography. Quantum- resistant cryptography refers to cryptographic algorithms, usually public-key algorithms, that are thought to be secure against an attack by a quantum computer. As quantum computing continues to advance, this feature of the library assures that the passwords managed through this system remain secure even against cutting-edge computational capabilities. 26 | //! 27 | //! The library supports the following Password Hashing Schemes (Password Based Key Derivation Functions): 28 | //! 29 | //! - **Argon2i**: A cutting-edge and highly secure key derivation function designed to protect against both traditional brute-force attacks and rainbow table attacks. (Recommended) 30 | //! - **Bcrypt**: A password hashing function designed to be secure against brute-force attacks. It is a work-factor function, which means that it takes a certain amount of time to compute. This makes it difficult to attack with a brute-force algorithm. 31 | //! - **Scrypt**: A password hashing function designed to be secure against both brute-force attacks and rainbow table attacks. It is a memory-hard and work- factor function, which means that it requires a lot of memory and time to compute. This makes it very difficult to attack with a GPU or other parallel computing device. 32 | //! 33 | //! ## Features 34 | //! 35 | //! - **Ease of Use**: Simple API for storing and verifying hashed passwords. 36 | //! - **Future-Proof**: Quantum-resistant cryptography to secure against future technological advancements. 37 | //! - **Integrable**: Written in Rust, the library is fast, efficient, and easily integrable into other Rust projects. 38 | //! - **Versatility**: Supports multiple Password Hashing Schemes like Argon2i, Bcrypt, and Scrypt. 39 | //! 40 | //! ## Core Components 41 | //! 42 | //! ### `Hash` Struct 43 | //! 44 | //! Contains: 45 | //! 46 | //! - **algorithm**: Enum representing the hashing algorithm (Argon2i, Bcrypt, Scrypt). 47 | //! - **hash**: Byte vector containing the hashed password. 48 | //! - **salt**: Byte vector containing the salt used in hashing. 49 | //! 50 | //! ### `HashAlgorithm` Enum 51 | //! 52 | //! Provides variants for supported hashing algorithms: Argon2i, Bcrypt, and Scrypt. 53 | //! 54 | //! ## Methods 55 | //! 56 | //! The `Hash` struct offers methods for password hashing and management, including but not limited to: 57 | //! 58 | //! - Creating new Hash objects. 59 | //! - Generating and setting salts and hashes. 60 | //! - Verifying passwords against stored hashes. 61 | //! 62 | //! ## Getting Started 63 | //! 64 | //! Add `Hash (HSH)` as a dependency in your `Cargo.toml` and import it in your main Rust file. 65 | //! 66 | //! ### Example 67 | //! 68 | //! Here's a simple example demonstrating basic usage: 69 | //! 70 | //! ```rust 71 | //! use hsh::models::hash::Hash; // Import the Hash struct 72 | //! 73 | //! let password = "password123"; 74 | //! let salt = "somesalt"; 75 | //! let algo = "argon2i"; 76 | // 77 | //! let original_hash = Hash::new(password, salt, algo).expect("Failed to create hash"); 78 | //! let hashed_password = original_hash.hash.clone(); 79 | // 80 | //! assert_eq!(original_hash.hash(), &hashed_password); 81 | //! ``` 82 | //! 83 | //! ## License 84 | //! 85 | //! Licensed under the MIT and Apache License (Version 2.0). 86 | //! 87 | //! [banner]: https://kura.pro/hsh/images/banners/banner-hsh.webp 88 | //! [crate-shield]: https://img.shields.io/crates/v/hsh.svg?style=for-the-badge&color=success&labelColor=27A006 89 | //! [github-shield]: https://img.shields.io/badge/github-555555?style=for-the-badge&labelColor=000000&logo=github 90 | //! [lib-rs-shield]: https://img.shields.io/badge/lib.rs-v0.0.8-success.svg?style=for-the-badge&color=8A48FF&labelColor=6F36E4 91 | //! [license-shield]: https://img.shields.io/crates/l/hsh.svg?style=for-the-badge&color=007EC6&labelColor=03589B 92 | //! [rust-shield]: https://img.shields.io/badge/rust-f04041?style=for-the-badge&labelColor=c0282d&logo=rust 93 | //! 94 | //! [00]: https://hshlib.com/ 95 | //! [01]: https://minifunctions.com/ 96 | //! [02]: http://www.apache.org/licenses/LICENSE-2.0 97 | //! [03]: http://opensource.org/licenses/MIT 98 | 99 | #![cfg_attr(feature = "bench", feature(test))] 100 | #![doc( 101 | html_favicon_url = "https://kura.pro/hsh/images/favicon.ico", 102 | html_logo_url = "https://kura.pro/hsh/images/logos/hsh.svg", 103 | html_root_url = "https://docs.rs/hsh" 104 | )] 105 | #![crate_name = "hsh"] 106 | #![crate_type = "lib"] 107 | 108 | /// The `algorithms` module contains the password hashing algorithms. 109 | pub mod algorithms; 110 | 111 | /// The `macros` module contains functions for generating macros. 112 | pub mod macros; 113 | 114 | /// The `models` module contains the data models for the library. 115 | pub mod models; 116 | 117 | /// This is the main entry point for the `Hash (HSH)` library. 118 | pub fn run() -> Result<(), Box> { 119 | if std::env::var("HSH_TEST_MODE").unwrap_or_default() == "1" { 120 | return Err("Simulated error".into()); 121 | } 122 | 123 | let name = "hsh"; 124 | println!("Welcome to `{}` 👋!", name.to_uppercase()); 125 | println!( 126 | "Quantum-Resistant Cryptographic Hash Library for Password Encryption and Verification." 127 | ); 128 | Ok(()) 129 | } 130 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | //! # Macros for the `hsh` crate. 5 | //! 6 | //! This module contains macros that simplify working with Hash structs. 7 | //! 8 | //! These macros can greatly simplify code that uses the Hash struct, 9 | //! making it easier to read and maintain. 10 | //! 11 | //! ## Generic macros for the hsh crate. 12 | //! 13 | //! This crate provides the following macros: 14 | //! 15 | //! | Macro | Description | 16 | //! |--------|------------| 17 | //! | `hsh` | Calls the `parse` method on the `Hash` struct from the hsh crate. | 18 | //! | `hsh_assert` | Asserts that a given condition is true. If the condition is false, the macro will cause the program to panic with the message "Assertion failed!". | 19 | //! | `hsh_contains` | Checks if a given string contains a specified substring. | 20 | //! | `hsh_in_range` | Checks if a given value is within a specified range (inclusive). | 21 | //! | `hsh_join` | Joins multiple strings together using a specified separator. | 22 | //! | `hsh_max` | Returns the maximum value from a set of given values. | 23 | //! | `hsh_min` | Returns the minimum value from a set of given values. | 24 | //! | `hsh_print` | Prints the given arguments to the console, similar to the `println!` macro. | 25 | //! | `hsh_print_vec` | Prints the elements of a given vector to the console, each on a new line. | 26 | //! | `hsh_split` | Splits a given string into a vector of words, dividing at each occurrence of whitespace. | 27 | //! | `hsh_vec` | Creates a new vector containing the given elements. | 28 | //! | `hsh_parse` | Attempts to parse a given input into a u64 value, returning a Result. | 29 | //! 30 | //! ## HSH Macros 31 | //! 32 | //! The library also provides several macros for common operations on the `Hash` struct: 33 | //! 34 | //! - `to_str_error`: Abstracts away the error handling for the `to_string` method. 35 | //! - `random_string`: Generates a random string of a specified length, consisting of alphanumeric characters. 36 | //! - `match_algo`: Matches given hash algorithm strings to their corresponding enum variants. 37 | //! - `generate_hash`: Generates a new hash for a given password, salt, and algorithm. 38 | //! - `new_hash`: Creates a new instance of the `Hash` struct with a given password, salt, and algorithm. 39 | //! - `hash_length`: Returns the length of the hash for a given `Hash` struct instance. 40 | //! 41 | 42 | /// This macro takes any number of arguments and parses them into a Rust 43 | /// value. The parsed value is returned wrapped in 44 | /// `hsh::Hash::parse()` function call. 45 | /// 46 | #[macro_export] 47 | macro_rules! hsh { 48 | ($($token:tt)*) => { 49 | hsh::Hash::parse($($token)*) 50 | }; 51 | } 52 | 53 | /// This macro asserts that the given condition is true. If the 54 | /// condition is false, the macro panics with the message "Assertion 55 | /// failed!". 56 | /// 57 | /// # Example 58 | /// 59 | /// ``` 60 | /// extern crate hsh; 61 | /// use hsh::{ hsh_assert }; 62 | /// 63 | /// hsh_assert!(1 == 1); // This will not panic 64 | /// hsh_assert!(1 == 2); // This will panic 65 | /// ``` 66 | /// 67 | #[macro_export] 68 | macro_rules! hsh_assert { 69 | ($($arg:tt)*) => { 70 | if !$($arg)* { 71 | panic!("Assertion failed!"); 72 | } 73 | }; 74 | } 75 | 76 | /// This macro checks if the given string contains the given substring. 77 | /// 78 | /// # Example 79 | /// 80 | /// ``` 81 | /// extern crate hsh; 82 | /// use hsh::{ hsh_contains }; 83 | /// 84 | /// let contains = hsh_contains!("Hello world", "world"); 85 | /// ``` 86 | /// 87 | #[macro_export] 88 | macro_rules! hsh_contains { 89 | ($s:expr, $sub:expr) => { 90 | $s.contains($sub) 91 | }; 92 | } 93 | 94 | /// This macro checks if the given value is within the given range. The 95 | /// range is inclusive of both endpoints. 96 | /// 97 | /// # Example 98 | /// 99 | /// ``` 100 | /// extern crate hsh; 101 | /// use hsh::{ hsh_in_range }; 102 | /// 103 | /// let in_range = hsh_in_range!(5, 1, 10); // `in_range` will be true 104 | /// ``` 105 | /// 106 | #[macro_export] 107 | macro_rules! hsh_in_range { 108 | ($value:expr, $min:expr, $max:expr) => { 109 | $value >= $min && $value <= $max 110 | }; 111 | } 112 | 113 | /// This macro joins the given strings together with the given 114 | /// separator. 115 | /// 116 | /// # Example 117 | /// 118 | /// ``` 119 | /// extern crate hsh; 120 | /// use hsh::{ hsh_join }; 121 | /// 122 | /// let joined = hsh_join!(", ", "Hello", "world"); 123 | /// ``` 124 | /// 125 | #[macro_export] 126 | macro_rules! hsh_join { 127 | ($sep:expr, $($s:expr),*) => {{ 128 | let vec = vec![$($s.to_string()),*]; 129 | vec.join($sep) 130 | }}; 131 | } 132 | 133 | /// This macro finds the maximum value of the given values. 134 | /// 135 | /// # Example 136 | /// 137 | /// ``` 138 | /// extern crate hsh; 139 | /// use hsh::{ hsh_max }; 140 | /// 141 | /// let max = hsh_max!(1, 2, 3); // `max` will be 3 142 | /// ``` 143 | #[macro_export] 144 | macro_rules! hsh_max { 145 | ($x:expr $(, $y:expr)*) => {{ 146 | let mut max = $x; 147 | $( 148 | if max < $y { max = $y; } 149 | )* 150 | max 151 | }}; 152 | } 153 | 154 | /// This macro finds the minimum value of the given values. 155 | /// 156 | /// # Example 157 | /// 158 | /// ``` 159 | /// extern crate hsh; 160 | /// use hsh::{ hsh_min }; 161 | /// 162 | /// let min = hsh_min!(1, 2, 3); // `min` will be 1 163 | /// ``` 164 | /// 165 | #[macro_export] 166 | macro_rules! hsh_min { 167 | ($x:expr $(, $y:expr)*) => {{ 168 | let mut min = $x; 169 | $( 170 | if min > $y { min = $y; } 171 | )* 172 | min 173 | }}; 174 | } 175 | 176 | /// This macro prints the given arguments to the console. 177 | /// 178 | /// # Example 179 | /// 180 | /// ``` 181 | /// extern crate hsh; 182 | /// use hsh::{ hsh_print }; 183 | /// 184 | /// hsh_print!("Hello {}", "world"); // This will print "Hello world" 185 | /// ``` 186 | #[macro_export] 187 | macro_rules! hsh_print { 188 | ($($arg:tt)*) => { 189 | println!($($arg)*); 190 | }; 191 | } 192 | 193 | /// This macro prints the given vector of values to the console. Each 194 | /// value is printed on a new line. 195 | /// 196 | /// # Example 197 | /// 198 | /// ``` 199 | /// extern crate hsh; 200 | /// use hsh::{ hsh_print_vec }; 201 | /// 202 | /// let vec = vec![1, 2, 3]; 203 | /// hsh_print_vec!(vec); // This will print 1, 2, 3 on separate lines 204 | /// ``` 205 | /// 206 | #[macro_export] 207 | macro_rules! hsh_print_vec { 208 | ($($v:expr),*) => { 209 | for v in $($v),* { 210 | println!("{}", v); 211 | } 212 | }; 213 | } 214 | 215 | /// This macro splits the given string into a vector of strings. The 216 | /// string is split on whitespace characters. 217 | /// 218 | /// # Example 219 | /// 220 | /// ``` 221 | /// extern crate hsh; 222 | /// use hsh::{ hsh_split }; 223 | /// 224 | /// let split = hsh_split!("Hello world"); 225 | /// ``` 226 | /// 227 | #[macro_export] 228 | macro_rules! hsh_split { 229 | ($s:expr) => { 230 | $s.split_whitespace() 231 | .map(|w| w.to_string()) 232 | .collect::>() 233 | }; 234 | } 235 | 236 | /// This macro creates a new vector with the given elements. 237 | /// 238 | /// # Example 239 | /// 240 | /// ``` 241 | /// extern crate hsh; 242 | /// use hsh::{ hsh_vec }; 243 | /// 244 | /// let vec = hsh_vec!(1, 2, 3); // `vec` will be [1, 2, 3] 245 | /// ``` 246 | /// 247 | #[macro_export] 248 | macro_rules! hsh_vec { 249 | ($($elem:expr),*) => {{ 250 | let mut v = Vec::new(); 251 | $(v.push($elem);)* 252 | v 253 | }}; 254 | } 255 | 256 | /// This macro attempts to parse the given input into a u64 value. If 257 | /// parsing fails, an error is returned with a message indicating the 258 | /// failure. 259 | /// 260 | /// # Example 261 | /// 262 | /// ``` 263 | /// extern crate hsh; 264 | /// use hsh::{ hsh_parse }; 265 | /// 266 | /// let parsed = hsh_parse!("123"); // `parsed` will be Ok(123) 267 | /// ``` 268 | #[macro_export] 269 | macro_rules! hsh_parse { 270 | ($input:expr) => { 271 | $input 272 | .parse::() 273 | .map_err(|e| format!("Failed to parse input: {}", e)) 274 | }; 275 | } 276 | 277 | /// This macro abstracts away the error handling for the `to_string` 278 | /// method. If the method fails, an error is returned with the failure 279 | /// message. 280 | /// 281 | /// # Example 282 | /// 283 | /// ``` 284 | /// extern crate hsh; 285 | /// use hsh::{ to_str_error }; 286 | /// 287 | /// let result: Result<(), String> = Ok(()); 288 | /// let error: Result<(), String> = 289 | /// Err("Error message".to_string()); 290 | /// 291 | /// let result_str = to_str_error!(result); 292 | /// assert_eq!(result_str, Ok(())); 293 | /// 294 | /// ``` 295 | #[macro_export] 296 | macro_rules! to_str_error { 297 | ($expr:expr) => { 298 | $expr.map_err(|e| e.to_string()) 299 | }; 300 | } 301 | 302 | /// This macro generates a random string of the given length. The string 303 | /// consists of alphanumeric characters (both upper and lower case). 304 | /// 305 | /// # Example 306 | /// 307 | /// ``` 308 | /// extern crate hsh; 309 | /// use hsh::{ random_string }; 310 | /// 311 | /// let random = random_string!(10); 312 | /// ``` 313 | /// 314 | #[macro_export] 315 | macro_rules! random_string { 316 | ($len:expr) => {{ 317 | let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 318 | let mut rng = vrd::random::Random::default(); 319 | (0..$len as usize) 320 | .map(|_| { 321 | let index = vrd::rand_int!(rng, 0, (chars.len() - 1) as i32) as usize; 322 | chars 323 | .chars() 324 | .nth(index) 325 | .unwrap() 326 | }) 327 | .collect::() 328 | }}; 329 | } 330 | 331 | /// This macro matches the hash algorithm strings to their corresponding 332 | /// enum variants. 333 | /// 334 | /// # Example 335 | /// 336 | /// ``` 337 | /// extern crate hsh; 338 | /// use hsh::{ match_algo, models::hash_algorithm::HashAlgorithm }; 339 | /// 340 | /// let algo = match_algo!("bcrypt"); 341 | /// ``` 342 | /// 343 | #[macro_export] 344 | macro_rules! match_algo { 345 | ($algo_str:expr) => { 346 | match $algo_str { 347 | "argon2i" => Ok(HashAlgorithm::Argon2i), 348 | "bcrypt" => Ok(HashAlgorithm::Bcrypt), 349 | "scrypt" => Ok(HashAlgorithm::Scrypt), 350 | _ => Err(format!( 351 | "Unsupported hash algorithm: {}", 352 | $algo_str 353 | )), 354 | } 355 | }; 356 | } 357 | 358 | /// This macro generates a new hash for a given password, salt, and 359 | /// algorithm. 360 | /// 361 | /// # Example 362 | /// 363 | /// ``` 364 | /// extern crate hsh; 365 | /// use hsh::models::hash::Hash; 366 | /// use hsh::{generate_hash, models::hash_algorithm::{HashAlgorithm}}; 367 | /// 368 | /// let password = "password"; 369 | /// let salt = "salt"; 370 | /// let algo = "bcrypt"; 371 | /// let hash_bytes = generate_hash!(password, salt, algo); 372 | /// 373 | /// assert!(hash_bytes.is_ok()); 374 | /// ``` 375 | /// 376 | #[macro_export] 377 | macro_rules! generate_hash { 378 | ($password:expr, $salt:expr, $algo:expr) => { 379 | Hash::generate_hash($password, $salt, $algo) 380 | }; 381 | } 382 | 383 | /// This macro creates a new instance of the `Hash` struct with the 384 | /// given password, salt, and algorithm. 385 | /// 386 | /// # Example 387 | /// 388 | /// ``` 389 | /// extern crate hsh; 390 | /// use hsh::{new_hash, models::{hash::Hash, hash_algorithm::{HashAlgorithm}}}; 391 | /// 392 | /// 393 | /// let password = "password"; 394 | /// let salt = "salt"; 395 | /// let algo = "bcrypt"; 396 | /// let hash = new_hash!(password, salt, algo); 397 | /// 398 | /// assert!(hash.is_ok()); 399 | /// ``` 400 | #[macro_export] 401 | macro_rules! new_hash { 402 | ($password:expr, $salt:expr, $algo:expr) => { 403 | Hash::new($password, $salt, $algo) 404 | }; 405 | } 406 | 407 | /// This macro returns the length of the password for a given `Hash` 408 | /// struct instance. 409 | /// 410 | /// # Example 411 | /// 412 | /// ``` 413 | /// extern crate hsh; 414 | /// use hsh::models::{hash::Hash, hash_algorithm::{HashAlgorithm}}; 415 | /// use hsh::{ hash_length, new_hash }; 416 | /// 417 | /// 418 | /// let password = "password"; 419 | /// let salt = "salt"; 420 | /// let algo = "bcrypt"; 421 | /// 422 | /// let hash = new_hash!(password, salt, algo); 423 | /// assert!(hash.is_ok()); 424 | /// let hash = hash.unwrap(); 425 | /// 426 | /// let password_length = hash_length!(hash); 427 | /// assert_eq!(password_length, 60); 428 | /// ``` 429 | /// 430 | #[macro_export] 431 | macro_rules! hash_length { 432 | ($hash:expr) => { 433 | $hash.hash_length() 434 | }; 435 | } 436 | 437 | /// Macros related to executing shell commands. 438 | /// 439 | /// Executes a shell command, logs the start and completion of the operation, and handles any errors that occur. 440 | /// 441 | /// # Parameters 442 | /// 443 | /// * `$command`: The shell command to execute. 444 | /// * `$package`: The name of the package the command is being run on. 445 | /// * `$operation`: A description of the operation being performed. 446 | /// * `$start_message`: The log message to be displayed at the start of the operation. 447 | /// * `$complete_message`: The log message to be displayed upon successful completion of the operation. 448 | /// * `$error_message`: The log message to be displayed in case of an error. 449 | /// 450 | /// # Returns 451 | /// 452 | /// Returns a `Result<(), anyhow::Error>` indicating the success or failure of the operation. 453 | /// 454 | #[macro_export] 455 | macro_rules! macro_execute_and_log { 456 | ($command:expr, $package:expr, $operation:expr, $start_message:expr, $complete_message:expr, $error_message:expr) => {{ 457 | use anyhow::{Context, Result as AnyResult}; 458 | use $crate::loggers::{LogFormat, LogLevel}; 459 | use $crate::macro_log_info; 460 | 461 | macro_log_info!( 462 | LogLevel::INFO, 463 | $operation, 464 | $start_message, 465 | LogFormat::CLF 466 | ); 467 | 468 | $command 469 | .run() 470 | .map(|_| ()) 471 | .map_err(|err| { 472 | macro_log_info!( 473 | LogLevel::ERROR, 474 | $operation, 475 | $error_message, 476 | LogFormat::CLF 477 | ); 478 | err 479 | }) 480 | .with_context(|| { 481 | format!( 482 | "Failed to execute '{}' for {} on package '{}'", 483 | stringify!($command), 484 | $operation, 485 | $package 486 | ) 487 | })?; 488 | 489 | macro_log_info!( 490 | LogLevel::INFO, 491 | $operation, 492 | $complete_message, 493 | LogFormat::CLF 494 | ); 495 | Ok(()) 496 | }}; 497 | } 498 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | //! This is the main entry point for the hsh application. 5 | fn main() { 6 | // Call the `run()` function from the `Hash (HSH)` module. 7 | if let Err(err) = hsh::run() { 8 | eprintln!("Error running hsh: {}", err); 9 | std::process::exit(1); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/models/hash.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | use super::hash_algorithm::HashAlgorithm; 5 | use crate::algorithms; 6 | use crate::models::hash_algorithm::HashingAlgorithm; 7 | use algorithms::{argon2i::Argon2i, bcrypt::Bcrypt, scrypt::Scrypt}; 8 | use serde::{Deserialize, Serialize}; 9 | 10 | // use algorithms::{argon2i::Argon2i, bcrypt::Bcrypt, scrypt::Scrypt}; 11 | use argon2rs::argon2i_simple; 12 | use base64::{engine::general_purpose, Engine as _}; 13 | // use models::{hash::*, hash_algorithm::*}; 14 | use scrypt::scrypt; 15 | use std::{fmt, str::FromStr}; 16 | use vrd::random::Random; 17 | 18 | /// A type alias for a salt. 19 | pub type Salt = Vec; 20 | 21 | /// A struct for storing and verifying hashed passwords. 22 | /// It uses `#[non_exhaustive]` and derive macros for common functionalities. 23 | #[non_exhaustive] 24 | #[derive( 25 | Clone, 26 | Debug, 27 | Eq, 28 | Hash, 29 | Ord, 30 | PartialEq, 31 | PartialOrd, 32 | Serialize, 33 | Deserialize, 34 | )] 35 | pub struct Hash { 36 | /// The password hash. 37 | pub hash: Vec, 38 | /// The salt used for hashing. 39 | pub salt: Salt, 40 | /// The hash algorithm used. 41 | pub algorithm: HashAlgorithm, 42 | } 43 | 44 | impl Hash { 45 | /// Creates a new `Hash` instance using Argon2i algorithm for password hashing. 46 | /// 47 | /// # Example 48 | /// 49 | /// ``` 50 | /// use hsh::models::hash::{Hash, Salt}; 51 | /// 52 | /// let password = "my_password"; 53 | /// let salt: Salt = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 54 | /// 55 | /// let result = Hash::new_argon2i(password, salt); 56 | /// match result { 57 | /// Ok(hash) => println!("Successfully created Argon2i hash"), 58 | /// Err(e) => println!("An error occurred: {}", e), 59 | /// } 60 | /// ``` 61 | pub fn new_argon2i( 62 | password: &str, 63 | salt: Salt, 64 | ) -> Result { 65 | // Convert the Vec salt to a &str 66 | let salt_str = std::str::from_utf8(&salt) 67 | .map_err(|_| "Failed to convert salt to string")?; 68 | 69 | // Perform Argon2i hashing 70 | let calculated_hash = 71 | argon2i_simple(password, salt_str).to_vec(); 72 | 73 | HashBuilder::new() 74 | .hash(calculated_hash) 75 | .salt(salt) 76 | .algorithm(HashAlgorithm::Argon2i) 77 | .build() 78 | } 79 | 80 | /// Creates a new `Hash` instance using Bcrypt algorithm for password hashing. 81 | /// 82 | /// # Example 83 | /// 84 | /// ``` 85 | /// use hsh::models::hash::Hash; 86 | /// 87 | /// let password = "my_password"; 88 | /// let cost: u32 = 16; 89 | /// 90 | /// let result = Hash::new_bcrypt(password, cost); 91 | /// match result { 92 | /// Ok(hash) => println!("Successfully created Bcrypt hash"), 93 | /// Err(e) => println!("An error occurred: {}", e), 94 | /// } 95 | /// ``` 96 | pub fn new_bcrypt( 97 | password: &str, 98 | cost: u32, 99 | ) -> Result { 100 | // Perform Bcrypt hashing 101 | let hashed_password = 102 | bcrypt::hash(password, cost).map_err(|e| { 103 | format!("Failed to hash password with Bcrypt: {}", e) 104 | })?; 105 | 106 | // In Bcrypt, the salt is embedded in the hashed password. 107 | // So, you can just use an empty salt when building the Hash object. 108 | let empty_salt = Vec::new(); 109 | 110 | HashBuilder::new() 111 | .hash(hashed_password.as_bytes().to_vec()) 112 | .salt(empty_salt) 113 | .algorithm(HashAlgorithm::Bcrypt) 114 | .build() 115 | } 116 | 117 | /// Creates a new `Hash` instance using Scrypt algorithm for password hashing. 118 | /// 119 | /// # Example 120 | /// 121 | /// ``` 122 | /// use hsh::models::hash::{Hash, Salt}; 123 | /// 124 | /// let password = "my_password"; 125 | /// let salt: Salt = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 126 | /// 127 | /// let result = Hash::new_scrypt(password, salt); 128 | /// match result { 129 | /// Ok(hash) => println!("Successfully created Scrypt hash"), 130 | /// Err(e) => println!("An error occurred: {}", e), 131 | /// } 132 | /// ``` 133 | pub fn new_scrypt( 134 | password: &str, 135 | salt: Salt, 136 | ) -> Result { 137 | // Convert the Vec salt to a &str for hashing 138 | let salt_str = std::str::from_utf8(&salt) 139 | .map_err(|_| "Failed to convert salt to string")?; 140 | 141 | // Perform Scrypt hashing using a wrapper function that sets the parameters 142 | let calculated_hash = 143 | Scrypt::hash_password(password, salt_str)?; 144 | 145 | // Use the builder pattern to construct the Hash instance 146 | HashBuilder::new() 147 | .hash(calculated_hash) 148 | .salt(salt) 149 | .algorithm(HashAlgorithm::Scrypt) 150 | .build() 151 | } 152 | 153 | /// A function that returns the hash algorithm used by the hash map. 154 | pub fn algorithm(&self) -> HashAlgorithm { 155 | self.algorithm 156 | } 157 | 158 | /// A function that creates a new hash object from a hash value and a hash algorithm. 159 | pub fn from_hash(hash: &[u8], algo: &str) -> Result { 160 | let algorithm = match algo { 161 | "argon2i" => Ok(HashAlgorithm::Argon2i), 162 | "bcrypt" => Ok(HashAlgorithm::Bcrypt), 163 | "scrypt" => Ok(HashAlgorithm::Scrypt), 164 | _ => Err(format!("Unsupported hash algorithm: {}", algo)), 165 | }?; 166 | 167 | Ok(Hash { 168 | salt: Vec::new(), 169 | hash: hash.to_vec(), 170 | algorithm, 171 | }) 172 | } 173 | 174 | /// A function that creates a new hash object from a hash string in the format algorithm$salt$hash. 175 | pub fn from_string(hash_str: &str) -> Result { 176 | // Split the hash string into six parts, using the `$` character as the delimiter. 177 | let parts: Vec<&str> = hash_str.split('$').collect(); 178 | 179 | // If the hash string does not contain six parts, return an error. 180 | if parts.len() != 6 { 181 | return Err(String::from("Invalid hash string")); 182 | } 183 | 184 | // Parse the algorithm from the first part of the hash string. 185 | let algorithm = Self::parse_algorithm(hash_str)?; 186 | 187 | // Parse the salt from the second, third, fourth, and fifth parts of the hash string. 188 | let salt = format!( 189 | "${}${}${}${}", 190 | parts[1], parts[2], parts[3], parts[4] 191 | ); 192 | 193 | // Decode the hash bytes from the sixth part of the hash string. 194 | let hash_bytes = 195 | general_purpose::STANDARD.decode(parts[5]).map_err( 196 | |_| format!("Failed to decode base64: {}", parts[5]), 197 | )?; 198 | 199 | // Create the `Hash` object and return it. 200 | Ok(Hash { 201 | salt: salt.into_bytes(), 202 | hash: hash_bytes, 203 | algorithm, 204 | }) 205 | } 206 | 207 | /// A function that generates a hash value for a password using the specified hash algorithm. 208 | /// The function takes three arguments: 209 | /// 210 | /// - password: The password to be hashed. 211 | /// - salt: A random string used to make the hash value unique. 212 | /// - algo: The name of the hash algorithm to use. 213 | /// 214 | /// The function returns a `Result` object containing the hash value if successful, or an error message if unsuccessful. 215 | pub fn generate_hash( 216 | password: &str, 217 | salt: &str, 218 | algo: &str, 219 | ) -> Result, String> { 220 | match algo { 221 | "argon2i" => Argon2i::hash_password(password, salt), 222 | "bcrypt" => Bcrypt::hash_password(password, salt), 223 | "scrypt" => Scrypt::hash_password(password, salt), 224 | _ => Err(format!("Unsupported hash algorithm: {}", algo)), 225 | } 226 | } 227 | 228 | /// A function that generates a random string of the specified length. 229 | pub fn generate_random_string(len: usize) -> String { 230 | let mut rng = Random::default(); 231 | let chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; 232 | (0..len) 233 | .map(|_| { 234 | chars 235 | .chars() 236 | .nth(rng.random_range(0, chars.len() as u32) 237 | as usize) 238 | .unwrap() 239 | }) 240 | .collect() 241 | } 242 | 243 | /// A function that generates a random salt for a password using the specified hash algorithm. 244 | pub fn generate_salt(algo: &str) -> Result { 245 | let mut rng = Random::default(); 246 | match algo { 247 | "argon2i" => Ok(Self::generate_random_string(16)), 248 | "bcrypt" => { 249 | let salt: Vec = rng.bytes(16); 250 | let salt_array: [u8; 16] = 251 | salt.try_into().map_err(|_| { 252 | "Error: failed to convert salt to an array" 253 | })?; 254 | Ok(general_purpose::STANDARD.encode(&salt_array[..])) 255 | } 256 | "scrypt" => { 257 | let salt: Vec = rng.bytes(32); 258 | let salt_array: [u8; 32] = 259 | salt.try_into().map_err(|_| { 260 | "Error: failed to convert salt to an array" 261 | })?; 262 | Ok(general_purpose::STANDARD.encode(&salt_array[..])) 263 | } 264 | _ => Err(format!("Unsupported hash algorithm: {}", algo)), 265 | } 266 | } 267 | 268 | /// A function that returns the hash value of a hash object. 269 | pub fn hash(&self) -> &[u8] { 270 | &self.hash 271 | } 272 | 273 | /// A function that returns the length of the hash value of a hash object. 274 | pub fn hash_length(&self) -> usize { 275 | self.hash.len() 276 | } 277 | 278 | /// A function that creates a new hash object from a password, salt, and hash algorithm. 279 | pub fn new( 280 | password: &str, 281 | salt: &str, 282 | algo: &str, 283 | ) -> Result { 284 | // Enforce a minimum password length of 8 characters. 285 | if password.len() < 8 { 286 | return Err(String::from("Password is too short. It must be at least 8 characters.")); 287 | } 288 | let hash = Self::generate_hash(password, salt, algo)?; 289 | 290 | let algorithm = match algo { 291 | "argon2i" => Ok(HashAlgorithm::Argon2i), 292 | "bcrypt" => Ok(HashAlgorithm::Bcrypt), 293 | "scrypt" => Ok(HashAlgorithm::Scrypt), 294 | _ => Err(format!("Unsupported hash algorithm: {}", algo)), 295 | }?; 296 | 297 | Ok(Self { 298 | hash, 299 | salt: salt.as_bytes().to_vec(), 300 | algorithm, 301 | }) 302 | } 303 | 304 | /// A function that parses a JSON string into a hash object. 305 | pub fn parse( 306 | input: &str, 307 | ) -> Result> { 308 | let hash: Hash = serde_json::from_str(input)?; 309 | Ok(hash) 310 | } 311 | 312 | /// A function that parses a hash string into a hash algorithm. 313 | pub fn parse_algorithm( 314 | hash_str: &str, 315 | ) -> Result { 316 | let parts: Vec<&str> = hash_str.split('$').collect(); 317 | if parts.len() < 2 { 318 | return Err(String::from("Invalid hash string")); 319 | } 320 | match parts[1] { 321 | "argon2i" => Ok(HashAlgorithm::Argon2i), 322 | "bcrypt" => Ok(HashAlgorithm::Bcrypt), 323 | "scrypt" => Ok(HashAlgorithm::Scrypt), 324 | _ => { 325 | Err(format!("Unsupported hash algorithm: {}", parts[1])) 326 | } 327 | } 328 | } 329 | 330 | /// A function that returns the salt used to hash a password. 331 | pub fn salt(&self) -> &[u8] { 332 | &self.salt 333 | } 334 | 335 | /// A function that sets the hash value of a hash object. 336 | pub fn set_hash(&mut self, hash: &[u8]) { 337 | self.hash = hash.to_vec(); 338 | } 339 | 340 | /// A function that sets the password of a hash object. 341 | pub fn set_password( 342 | &mut self, 343 | password: &str, 344 | salt: &str, 345 | algo: &str, 346 | ) -> Result<(), String> { 347 | self.hash = Self::generate_hash(password, salt, algo)?; 348 | Ok(()) 349 | } 350 | 351 | /// A function that sets the salt of a hash object. 352 | pub fn set_salt(&mut self, salt: &[u8]) { 353 | self.salt = salt.to_vec(); 354 | } 355 | 356 | /// A function that converts a hash object to a string representation. 357 | pub fn to_string_representation(&self) -> String { 358 | let hash_str = self 359 | .hash 360 | .iter() 361 | .map(|b| format!("{:02x}", b)) 362 | .collect::>() 363 | .join(""); 364 | 365 | format!("{}:{}", String::from_utf8_lossy(&self.salt), hash_str) 366 | } 367 | 368 | /// A function that verifies a password against a hash object. 369 | pub fn verify(&self, password: &str) -> Result { 370 | let salt = std::str::from_utf8(&self.salt) 371 | .map_err(|_| "Failed to convert salt to string")?; 372 | 373 | match self.algorithm { 374 | HashAlgorithm::Argon2i => { 375 | // Hash the password once 376 | let calculated_hash = 377 | argon2i_simple(password, salt).to_vec(); 378 | 379 | // Debugging information 380 | println!("Algorithm: Argon2i"); 381 | println!( 382 | "Provided password for verification: {}", 383 | password 384 | ); 385 | println!("Salt used for verification: {}", salt); 386 | println!("Calculated Hash: {:?}", calculated_hash); 387 | println!("Stored Hash: {:?}", self.hash); 388 | 389 | // Perform the verification 390 | Ok(calculated_hash == self.hash) 391 | } 392 | HashAlgorithm::Bcrypt => { 393 | // Debugging information 394 | println!("Algorithm: Bcrypt"); 395 | println!( 396 | "Provided password for verification: {}", 397 | password 398 | ); 399 | 400 | let hash_str = std::str::from_utf8(&self.hash) 401 | .map_err(|_| "Failed to convert hash to string")?; 402 | bcrypt::verify(password, hash_str) 403 | .map_err(|_| "Failed to verify Bcrypt password") 404 | } 405 | HashAlgorithm::Scrypt => { 406 | // Debugging information 407 | println!("Algorithm: Scrypt"); 408 | println!( 409 | "Provided password for verification: {}", 410 | password 411 | ); 412 | println!("Salt used for verification: {}", salt); 413 | 414 | let scrypt_params = scrypt::Params::new(14, 8, 1, 64) 415 | .map_err(|_| { 416 | "Failed to create Scrypt params" 417 | })?; 418 | let mut output = [0u8; 64]; 419 | match scrypt( 420 | password.as_bytes(), 421 | salt.as_bytes(), 422 | &scrypt_params, 423 | &mut output, 424 | ) { 425 | Ok(_) => { 426 | println!( 427 | "Calculated Hash: {:?}", 428 | output.to_vec() 429 | ); 430 | println!("Stored Hash: {:?}", self.hash); 431 | Ok(output.to_vec() == self.hash) 432 | } 433 | Err(_) => Err("Scrypt hashing failed"), 434 | } 435 | } 436 | } 437 | } 438 | } 439 | 440 | impl fmt::Display for Hash { 441 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 442 | write!(f, "Hash {{ hash: {:?} }}", self.hash) 443 | } 444 | } 445 | 446 | impl fmt::Display for HashAlgorithm { 447 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 448 | write!(f, "{:?}", self) 449 | } 450 | } 451 | 452 | impl FromStr for HashAlgorithm { 453 | type Err = String; 454 | 455 | fn from_str(s: &str) -> Result { 456 | let algorithm = match s { 457 | "argon2i" => HashAlgorithm::Argon2i, 458 | "bcrypt" => HashAlgorithm::Bcrypt, 459 | "scrypt" => HashAlgorithm::Scrypt, 460 | _ => return Err(String::from("Invalid hash algorithm")), 461 | }; 462 | Ok(algorithm) 463 | } 464 | } 465 | 466 | /// A builder struct for the `Hash` struct. 467 | /// It contains optional fields that correspond to the fields in `Hash`. 468 | /// The `#[derive(Default)]` allows us to initialize all fields to `None`. 469 | #[derive( 470 | Clone, 471 | Debug, 472 | Eq, 473 | Hash, 474 | Ord, 475 | PartialEq, 476 | PartialOrd, 477 | Serialize, 478 | Deserialize, 479 | )] 480 | pub struct HashBuilder { 481 | /// The password hash. 482 | hash: Option>, 483 | /// The salt used for hashing. 484 | salt: Option, 485 | /// The hash algorithm used. 486 | algorithm: Option, 487 | } 488 | 489 | impl HashBuilder { 490 | /// Creates a new `HashBuilder` with all fields set to `None`. 491 | pub fn new() -> Self { 492 | HashBuilder { 493 | hash: None, 494 | salt: None, 495 | algorithm: None, 496 | } 497 | } 498 | 499 | /// Sets the `hash` field in the builder. 500 | /// The `self` parameter is consumed and returned to allow for method chaining. 501 | pub fn hash(mut self, hash: Vec) -> Self { 502 | self.hash = Some(hash); 503 | self 504 | } 505 | 506 | /// Sets the `salt` field in the builder. 507 | /// The `self` parameter is consumed and returned to allow for method chaining. 508 | pub fn salt(mut self, salt: Salt) -> Self { 509 | self.salt = Some(salt); 510 | self 511 | } 512 | 513 | /// Sets the `algorithm` field in the builder. 514 | /// The `self` parameter is consumed and returned to allow for method chaining. 515 | pub fn algorithm(mut self, algorithm: HashAlgorithm) -> Self { 516 | self.algorithm = Some(algorithm); 517 | self 518 | } 519 | 520 | /// Consumes the builder and returns a `Hash` if all fields are set. 521 | /// Otherwise, it returns an error. 522 | pub fn build(self) -> Result { 523 | if let (Some(hash), Some(salt), Some(algorithm)) = 524 | (self.hash, self.salt, self.algorithm) 525 | { 526 | Ok(Hash { 527 | hash, 528 | salt, 529 | algorithm, 530 | }) 531 | } else { 532 | Err("Missing fields".to_string()) 533 | } 534 | } 535 | } 536 | 537 | /// Creates a new `HashBuilder` with all fields set to `None`. 538 | impl Default for HashBuilder { 539 | fn default() -> Self { 540 | Self::new() 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /src/models/hash_algorithm.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | use serde::{Deserialize, Serialize}; 5 | 6 | /// Represents the different algorithms available for password hashing. 7 | /// 8 | /// This enum is used to specify which hashing algorithm should be used 9 | /// when creating a new hashed password. 10 | /// 11 | #[derive( 12 | Clone, 13 | Copy, 14 | Debug, 15 | Eq, 16 | Hash, 17 | Ord, 18 | PartialEq, 19 | PartialOrd, 20 | Serialize, 21 | Deserialize, 22 | )] 23 | pub enum HashAlgorithm { 24 | /// Argon2i - A memory-hard password hashing algorithm. 25 | /// 26 | /// Resistant against various types of attacks, including: 27 | /// - GPU-based attacks 28 | /// - Side-channel attacks 29 | /// 30 | /// Incorporates multiple parameters to deter attackers: 31 | /// - Memory usage 32 | /// - Parallelism 33 | /// - Time cost 34 | Argon2i, 35 | 36 | /// Bcrypt - A widely used, computationally intensive password hashing algorithm. 37 | /// 38 | /// Features: 39 | /// - Based on the Blowfish encryption cipher 40 | /// - Uses key stretching technique 41 | /// - Time-consuming and resource-intensive, which makes it resistant to cracking 42 | Bcrypt, 43 | 44 | /// Scrypt - A memory-hard password hashing algorithm designed for resistance to brute-force attacks. 45 | /// 46 | /// Features: 47 | /// - Consumes a large amount of memory 48 | /// - Makes parallelized attacks difficult and costly 49 | Scrypt, 50 | } 51 | 52 | /// Represents a generic hashing algorithm. 53 | /// 54 | /// The `HashingAlgorithm` trait defines a common interface for hashing algorithms. 55 | /// Implementing this trait for different hashing algorithms ensures that they can be used 56 | /// interchangeably for hashing passwords. 57 | /// 58 | /// The primary consumer of this trait is the `Hash` struct, which uses it to handle the hashing 59 | /// logic in a decoupled and extendable manner. 60 | pub trait HashingAlgorithm { 61 | /// Hashes a given password using a specific salt. 62 | /// 63 | /// Given a plaintext `password` and a `salt`, this method returns a hashed representation 64 | /// of the password. The hashing algorithm used is determined by the implementing type. 65 | /// 66 | /// # Parameters 67 | /// 68 | /// - `password`: The plaintext password to be hashed. 69 | /// - `salt`: A cryptographic salt to prevent rainbow table attacks. 70 | /// 71 | /// # Returns 72 | /// 73 | /// Returns a `Result` containing the hashed password as a vector of bytes. 74 | /// If hashing fails, returns a `String` detailing the error. 75 | fn hash_password( 76 | password: &str, 77 | salt: &str, 78 | ) -> Result, String>; 79 | } 80 | -------------------------------------------------------------------------------- /src/models/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | /// The `data` module contains the structs. 5 | pub mod hash; 6 | 7 | /// The `hash_algorithm` module contains the `HashAlgorithm` enum. 8 | pub mod hash_algorithm; 9 | -------------------------------------------------------------------------------- /tests/test_argon2i.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::algorithms::argon2i::Argon2i; 7 | use hsh::models::hash::Hash; 8 | use hsh::models::hash_algorithm::{ 9 | HashAlgorithm, HashingAlgorithm, 10 | }; 11 | 12 | #[test] 13 | fn test_hash_differs_from_password() { 14 | let password = "password123"; 15 | let salt = "somesalt"; 16 | let hashed_password = 17 | Argon2i::hash_password(password, salt).unwrap(); 18 | 19 | assert_ne!(hashed_password, password.as_bytes()); 20 | } 21 | 22 | #[test] 23 | fn test_different_salts_produce_different_hashes() { 24 | let password = "password123"; 25 | let salt1 = "salt123456789012345678901234567"; 26 | let salt2 = "salt234567890123456789012345678"; 27 | 28 | let hash1 = Argon2i::hash_password(password, salt1).unwrap(); 29 | let hash2 = Argon2i::hash_password(password, salt2).unwrap(); 30 | 31 | assert_ne!(hash1, hash2); 32 | } 33 | 34 | #[test] 35 | fn test_same_password_and_salt_produce_same_hash() { 36 | let password = "password123"; 37 | let salt = "somesalt"; 38 | 39 | let hash1 = Argon2i::hash_password(password, salt).unwrap(); 40 | let hash2 = Argon2i::hash_password(password, salt).unwrap(); 41 | 42 | assert_eq!(hash1, hash2); 43 | } 44 | 45 | #[test] 46 | fn test_hash_password_length() { 47 | let password = "password123"; 48 | let salt = "somesalt"; 49 | let hashed_password = 50 | Argon2i::hash_password(password, salt).unwrap(); 51 | 52 | assert_eq!(hashed_password.len(), 32); // Assuming a 32-byte hash 53 | } 54 | 55 | #[test] 56 | fn test_hash_password_not_empty() { 57 | let password = "password123"; 58 | let salt = "somesalt"; 59 | let hashed_password = 60 | Argon2i::hash_password(password, salt).unwrap(); 61 | 62 | assert!(!hashed_password.is_empty()); 63 | } 64 | 65 | #[test] 66 | fn test_hash_password_error() { 67 | let password = "password123"; 68 | let salt = "somesalt"; 69 | let hashed_password = 70 | Argon2i::hash_password(password, salt).unwrap(); 71 | 72 | assert!(!hashed_password.is_empty()); 73 | } 74 | 75 | #[test] 76 | fn test_from_hash() { 77 | let hash_bytes = vec![1, 2, 3, 4]; 78 | let hash = Hash::from_hash(&hash_bytes, "argon2i").unwrap(); 79 | assert_eq!(hash.hash, hash_bytes); 80 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 81 | } 82 | 83 | #[test] 84 | fn test_from_hash_error() { 85 | let hash_bytes = vec![1, 2, 3, 4]; 86 | let hash = Hash::from_hash(&hash_bytes, "argon2i").unwrap(); 87 | assert_eq!(hash.hash, hash_bytes); 88 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/test_bcrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::algorithms::bcrypt::Bcrypt; 7 | use hsh::models::hash:: 8 | Hash; 9 | use hsh::models::hash_algorithm::{ 10 | HashAlgorithm, HashingAlgorithm, 11 | }; 12 | 13 | #[test] 14 | fn test_hash_differs_from_password() { 15 | let password = "password123"; 16 | let salt = "somesalt"; 17 | let hashed_password = 18 | Bcrypt::hash_password(password, salt).unwrap(); 19 | 20 | assert_ne!(hashed_password, password.as_bytes()); 21 | } 22 | 23 | #[test] 24 | fn test_different_salts_produce_different_hashes() { 25 | let password = "password123"; 26 | let salt1 = "salt1"; 27 | let salt2 = "salt2"; 28 | 29 | let hash1 = Bcrypt::hash_password(password, salt1).unwrap(); 30 | let hash2 = Bcrypt::hash_password(password, salt2).unwrap(); 31 | 32 | assert_ne!(hash1, hash2); 33 | } 34 | 35 | #[test] 36 | fn test_hashing_error() { 37 | // Setup conditions for hashing to fail 38 | let password = "password123"; 39 | 40 | // Intentionally using an invalid cost to force an error 41 | let invalid_cost: u32 = 1; 42 | let result = bcrypt::hash(password, invalid_cost); 43 | 44 | assert!(result.is_err()); 45 | } 46 | 47 | #[test] 48 | fn test_new_bcrypt() { 49 | let password = "password123"; 50 | let cost: u32 = 12; 51 | let hash = Hash::new_bcrypt(password, cost).unwrap(); 52 | 53 | assert_eq!(hash.algorithm, HashAlgorithm::Bcrypt); 54 | assert!(!hash.hash.is_empty()); 55 | assert_eq!(hash.salt.len(), 0); 56 | } 57 | 58 | #[test] 59 | fn test_new_bcrypt_error() { 60 | let password = "password123"; 61 | let invalid_cost: u32 = 0; 62 | let result = Hash::new_bcrypt(password, invalid_cost); 63 | 64 | assert!(result.is_err()); 65 | } 66 | 67 | #[test] 68 | fn test_from_hash() { 69 | let hash_bytes = vec![1, 2, 3, 4]; 70 | let hash = Hash::from_hash(&hash_bytes, "bcrypt").unwrap(); 71 | assert_eq!(hash.hash, hash_bytes); 72 | assert_eq!(hash.algorithm, HashAlgorithm::Bcrypt); 73 | } 74 | 75 | #[test] 76 | fn test_from_hash_error() { 77 | let hash_bytes = vec![1, 2, 3, 4]; 78 | let hash = Hash::from_hash(&hash_bytes, "invalid").unwrap_err(); 79 | assert_eq!(hash, "Unsupported hash algorithm: invalid"); 80 | } 81 | 82 | #[test] 83 | fn test_verify_bcrypt() { 84 | let password = "password123"; 85 | let hash = Hash::new_bcrypt(password, 12).unwrap(); 86 | 87 | assert!(hash.verify(password).unwrap()); 88 | assert!(!hash.verify("wrong_password").unwrap()); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /tests/test_hash.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::models::hash::{Hash, HashBuilder, Salt}; 7 | use hsh::models::hash_algorithm::HashAlgorithm; 8 | use std::str::FromStr; 9 | 10 | #[test] 11 | fn test_new_argon2i() { 12 | let password = "password123"; 13 | let salt: Salt = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 14 | let hash = Hash::new_argon2i(password, salt.clone()).unwrap(); 15 | assert_eq!(hash.salt, salt); 16 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 17 | } 18 | 19 | #[test] 20 | fn test_new_bcrypt() { 21 | let password = "password123"; 22 | let cost = 4; 23 | let hash = Hash::new_bcrypt(password, cost).unwrap(); 24 | assert_eq!(hash.algorithm, HashAlgorithm::Bcrypt); 25 | } 26 | 27 | #[test] 28 | fn test_new_scrypt() { 29 | let password = "password123"; 30 | let salt: Salt = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 31 | let hash = Hash::new_scrypt(password, salt.clone()).unwrap(); 32 | assert_eq!(hash.salt, salt); 33 | assert_eq!(hash.algorithm, HashAlgorithm::Scrypt); 34 | } 35 | 36 | #[test] 37 | fn test_from_hash() { 38 | let hash_bytes = vec![1, 2, 3, 4]; 39 | let hash = Hash::from_hash(&hash_bytes, "argon2i").unwrap(); 40 | assert_eq!(hash.hash, hash_bytes); 41 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 42 | } 43 | 44 | #[test] 45 | fn test_hash_algorithm_from_str() { 46 | let algorithm = HashAlgorithm::from_str("argon2i").unwrap(); 47 | assert_eq!(algorithm, HashAlgorithm::Argon2i); 48 | } 49 | 50 | #[test] 51 | fn test_hash_builder() { 52 | let hash = vec![1, 2, 3, 4]; 53 | let salt: Salt = vec![0, 1, 2, 3]; 54 | let algorithm = HashAlgorithm::Argon2i; 55 | let built_hash = HashBuilder::new() 56 | .hash(hash.clone()) 57 | .salt(salt.clone()) 58 | .algorithm(algorithm) 59 | .build() 60 | .unwrap(); 61 | 62 | assert_eq!(built_hash.hash, hash); 63 | assert_eq!(built_hash.salt, salt); 64 | assert_eq!(built_hash.algorithm, algorithm); 65 | } 66 | 67 | // Add more tests such as verification, string representation, etc. 68 | } 69 | -------------------------------------------------------------------------------- /tests/test_hash_algorithm.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::models::hash_algorithm::{ 7 | HashAlgorithm, HashingAlgorithm, 8 | }; 9 | 10 | // Dummy struct to implement HashingAlgorithm for testing 11 | struct DummyAlgorithm; 12 | 13 | impl HashingAlgorithm for DummyAlgorithm { 14 | fn hash_password( 15 | _password: &str, 16 | _salt: &str, 17 | ) -> Result, String> { 18 | Ok(vec![1, 2, 3, 4]) // Dummy logic 19 | } 20 | } 21 | 22 | #[test] 23 | fn test_hash_algorithm_enum() { 24 | let argon2i = HashAlgorithm::Argon2i; 25 | let bcrypt = HashAlgorithm::Bcrypt; 26 | let scrypt = HashAlgorithm::Scrypt; 27 | 28 | assert_eq!(argon2i as i32, 0); 29 | assert_eq!(bcrypt as i32, 1); 30 | assert_eq!(scrypt as i32, 2); 31 | } 32 | 33 | #[test] 34 | fn test_hashing_algorithm_trait() { 35 | let password = "password123"; 36 | let salt = "salt123"; 37 | let hashed = 38 | DummyAlgorithm::hash_password(password, salt).unwrap(); 39 | assert_eq!(hashed, vec![1, 2, 3, 4]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests/test_lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::models::hash::Hash; 7 | use hsh::models::hash_algorithm::HashAlgorithm; 8 | 9 | #[test] 10 | fn test_new() { 11 | let password = "password123"; 12 | let salt = "somesalt"; 13 | let algo = "argon2i"; 14 | 15 | let hash = Hash::new(password, salt, algo).unwrap(); 16 | 17 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 18 | assert_eq!(hash.salt, salt.as_bytes().to_vec()); 19 | } 20 | 21 | #[test] 22 | fn test_new_with_unsupported_algo() { 23 | let password = "password123"; 24 | let salt = "somesalt"; 25 | let algo = "unsupported_algo"; 26 | 27 | let hash = Hash::new(password, salt, algo); 28 | 29 | match hash { 30 | Ok(_) => { 31 | panic!("Expected an error for unsupported hash algorithm, but got Ok"); 32 | } 33 | Err(e) => { 34 | assert_eq!( 35 | e, 36 | format!("Unsupported hash algorithm: {}", algo) 37 | ); 38 | } 39 | } 40 | } 41 | 42 | #[test] 43 | fn test_verify() { 44 | let password = "password123"; 45 | let salt = "somesalt"; 46 | let algo = "argon2i"; 47 | 48 | let hash = Hash::new(password, salt, algo).unwrap(); 49 | 50 | assert!(hash.verify(password).unwrap()); 51 | assert!(!hash.verify("wrongpassword").unwrap()); 52 | } 53 | 54 | #[test] 55 | fn test_from_string() { 56 | // You'll need to provide a valid hash string here for this test 57 | let hash_string = "$argon2i$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$RdescudvJCsgt3ub+b+dWRWJTmaaJObG"; 58 | 59 | let hash = Hash::from_string(hash_string); 60 | 61 | match hash { 62 | Ok(hash) => { 63 | // Assert that the hash, salt, and algorithm are as expected 64 | assert_eq!(hash.algorithm, HashAlgorithm::Argon2i); 65 | } 66 | Err(e) => { 67 | panic!("Failed to parse hash string: {}", e); 68 | } 69 | } 70 | } 71 | 72 | #[test] 73 | fn test_from_string_invalid_hash_string() { 74 | // Provide an invalid hash string 75 | let invalid_hash_string = "invalid$hash$string"; 76 | 77 | let hash = Hash::from_string(invalid_hash_string); 78 | 79 | // Expect an error to be returned 80 | assert!(hash.is_err()); 81 | 82 | // Check the error message 83 | match hash { 84 | Err(e) => { 85 | assert_eq!(e, String::from("Invalid hash string")) 86 | } 87 | _ => panic!("Expected Err, got Ok"), 88 | } 89 | } 90 | 91 | #[test] 92 | fn test_generate_salt() { 93 | let algo = "argon2i"; 94 | 95 | let salt = Hash::generate_salt(algo).unwrap(); 96 | 97 | // Assert that the salt is of the correct length and format 98 | assert_eq!(salt.len(), 16); 99 | } 100 | 101 | #[test] 102 | fn test_generate_salt_invalid_algorithm() { 103 | let invalid_algo = "unsupported_algo"; 104 | 105 | let salt = Hash::generate_salt(invalid_algo); 106 | 107 | // Expect an error to be returned 108 | assert!(salt.is_err()); 109 | 110 | // Check the error message 111 | match salt { 112 | Err(e) => assert_eq!( 113 | e, 114 | format!("Unsupported hash algorithm: {}", invalid_algo) 115 | ), 116 | _ => panic!("Expected Err, got Ok"), 117 | } 118 | } 119 | 120 | #[test] 121 | fn test_generate_salt_bcrypt() { 122 | let algo = "bcrypt"; 123 | 124 | let salt = Hash::generate_salt(algo).unwrap(); 125 | 126 | // Assert that the salt is of the correct length and format 127 | assert_eq!(salt.len(), 24); // bcrypt salt will be longer due to base64 encoding 128 | } 129 | 130 | #[test] 131 | fn test_generate_salt_scrypt() { 132 | let algo = "scrypt"; 133 | 134 | let salt = Hash::generate_salt(algo).unwrap(); 135 | 136 | // Assert that the salt is of the correct length and format 137 | assert_eq!(salt.len(), 44); // scrypt salt will be longer due to base64 encoding 138 | } 139 | 140 | #[test] 141 | fn test_argon2i_hashing() { 142 | let password = "password123"; 143 | let salt = "somesalt"; 144 | let algo = "argon2i"; 145 | 146 | let hash = Hash::new(password, salt, algo).unwrap(); 147 | 148 | assert!(hash.verify(password).unwrap()); 149 | assert!(!hash.verify("wrongpassword").unwrap()); 150 | } 151 | 152 | #[test] 153 | fn test_bcrypt_hashing() { 154 | let password = "password123"; 155 | let salt = Hash::generate_salt("bcrypt").unwrap(); 156 | let algo = "bcrypt"; 157 | 158 | let hash = Hash::new(password, &salt, algo).unwrap(); 159 | 160 | assert!(hash.verify(password).unwrap()); 161 | assert!(!hash.verify("wrongpassword").unwrap()); 162 | } 163 | 164 | #[test] 165 | fn test_scrypt_hashing() { 166 | let password = "password123"; 167 | let salt = Hash::generate_salt("scrypt").unwrap(); 168 | let algo = "scrypt"; 169 | 170 | let hash = Hash::new(password, &salt, algo).unwrap(); 171 | 172 | assert!(hash.verify(password).unwrap()); 173 | assert!(!hash.verify("wrongpassword").unwrap()); 174 | } 175 | 176 | #[test] 177 | fn test_set_password() { 178 | let password = "password123"; 179 | let salt = "somesalt"; 180 | let algo = "argon2i"; 181 | 182 | let mut hash = Hash::new(password, salt, algo).unwrap(); 183 | 184 | let new_password = "newpassword123"; 185 | hash.set_password(new_password, salt, algo).unwrap(); 186 | 187 | assert!(hash.verify(new_password).unwrap()); 188 | assert!(!hash.verify(password).unwrap()); 189 | } 190 | 191 | #[test] 192 | fn test_invalid_algorithm() { 193 | let password = "password123"; 194 | let salt = "somesalt"; 195 | let algo = "invalid_algo"; 196 | 197 | let hash = Hash::new(password, salt, algo); 198 | 199 | assert!(hash.is_err()); 200 | } 201 | 202 | #[test] 203 | fn test_short_password() { 204 | let password = "short"; 205 | let salt = "somesalt"; 206 | let algo = "argon2i"; 207 | 208 | let hash = Hash::new(password, salt, algo); 209 | 210 | assert!(hash.is_err()); 211 | } 212 | 213 | #[test] 214 | fn test_algorithm() { 215 | let password = "password123"; 216 | let salt = "somesalt"; 217 | let algo = "bcrypt"; 218 | 219 | let hash = Hash::new(password, salt, algo).unwrap(); 220 | 221 | assert_eq!(HashAlgorithm::Bcrypt, hash.algorithm()); 222 | } 223 | 224 | #[test] 225 | fn test_from_hash() { 226 | let password = "password123"; 227 | let salt = "somesalt"; 228 | let algo = "scrypt"; 229 | 230 | // Generate a hash from the password 231 | let original_hash = Hash::new(password, salt, algo).unwrap(); 232 | 233 | // Get the hashed password bytes 234 | let hashed_password = original_hash.hash; 235 | 236 | // Now try to create a new Hash struct from the hashed password bytes 237 | let from_hash = Hash::from_hash(&hashed_password, algo); 238 | 239 | // Check that from_hash is Ok 240 | assert!(from_hash.is_ok()); 241 | 242 | // Unwrap the Result and get the Hash struct 243 | let from_hash = from_hash.unwrap(); 244 | 245 | // Check that the algorithm is correct 246 | assert_eq!(from_hash.algorithm(), HashAlgorithm::Scrypt); 247 | 248 | // Check that the hash is correct 249 | assert_eq!(from_hash.hash, hashed_password); 250 | 251 | // Check that the salt is empty (since from_hash doesn't set the salt) 252 | assert_eq!(from_hash.salt, Vec::::new()); 253 | } 254 | 255 | #[test] 256 | fn test_from_hash_invalid_algorithm() { 257 | let password = "password123"; 258 | let salt = "somesalt"; 259 | let algo = "unsupported_algo"; 260 | 261 | // Generate a hash from the password 262 | let original_hash = 263 | Hash::new(password, salt, "bcrypt").unwrap(); 264 | 265 | // Get the hashed password bytes 266 | let hashed_password = original_hash.hash; 267 | 268 | // Now try to create a new Hash struct from the hashed password bytes 269 | let from_hash = Hash::from_hash(&hashed_password, algo); 270 | 271 | // Check that from_hash is Err 272 | assert!(from_hash.is_err()); 273 | 274 | // Check the error message 275 | match from_hash { 276 | Err(e) => assert_eq!( 277 | e, 278 | format!("Unsupported hash algorithm: {}", algo) 279 | ), 280 | _ => panic!("Expected Err, got Ok"), 281 | } 282 | } 283 | 284 | #[test] 285 | fn test_hash() { 286 | let password = "password123"; 287 | let salt = "somesalt"; 288 | let algo = "bcrypt"; 289 | 290 | // Create a new Hash 291 | let original_hash = Hash::new(password, salt, algo).unwrap(); 292 | 293 | // Get the hashed password bytes 294 | let hashed_password = original_hash.hash.clone(); 295 | 296 | // Test the `hash` method 297 | assert_eq!(original_hash.hash(), &hashed_password); 298 | } 299 | 300 | #[test] 301 | fn test_salt() { 302 | let password = "password123"; 303 | let salt = "somesalt"; 304 | let algo = "bcrypt"; 305 | 306 | // Create a new Hash 307 | let original_hash = Hash::new(password, salt, algo).unwrap(); 308 | 309 | // Convert the salt to bytes for comparison 310 | let salt_bytes = salt.as_bytes(); 311 | 312 | // Test the `salt` method 313 | assert_eq!(original_hash.salt(), salt_bytes); 314 | } 315 | 316 | #[test] 317 | fn test_set_hash() { 318 | let password = "password123"; 319 | let salt = "somesalt"; 320 | let algo = "bcrypt"; 321 | 322 | // Create a new Hash 323 | let mut original_hash = 324 | Hash::new(password, salt, algo).unwrap(); 325 | 326 | // Create a new hash value 327 | let new_hash = vec![1, 2, 3, 4, 5]; 328 | 329 | // Set the hash of the Hash struct to the new value 330 | original_hash.set_hash(&new_hash); 331 | 332 | // Test that the `hash` method returns the new hash value 333 | assert_eq!(original_hash.hash(), &new_hash); 334 | } 335 | 336 | #[test] 337 | fn test_set_salt() { 338 | let password = "password123"; 339 | let salt = "somesalt"; 340 | let algo = "bcrypt"; 341 | 342 | // Create a new Hash 343 | let mut original_hash = 344 | Hash::new(password, salt, algo).unwrap(); 345 | 346 | // Create a new salt value 347 | let new_salt = vec![1, 2, 3, 4, 5]; 348 | 349 | // Set the salt of the Hash struct to the new value 350 | original_hash.set_salt(&new_salt); 351 | 352 | // Test that the `salt` method returns the new salt value 353 | assert_eq!(original_hash.salt(), &new_salt); 354 | } 355 | #[test] 356 | fn test_to_string_representation() { 357 | let password = "password123"; 358 | let salt = "somesalt"; 359 | let algo = "bcrypt"; 360 | 361 | // Create a new Hash 362 | let original_hash = Hash::new(password, salt, algo).unwrap(); 363 | 364 | // Get the string representation 365 | let string_repr = original_hash.to_string_representation(); 366 | 367 | // Get the expected string representation 368 | let expected_repr = format!( 369 | "{}:{}", 370 | salt, 371 | original_hash 372 | .hash() 373 | .iter() 374 | .map(|b| format!("{:02x}", b)) 375 | .collect::>() 376 | .join("") 377 | ); 378 | 379 | assert_eq!(string_repr, expected_repr); 380 | } 381 | #[test] 382 | fn test_hash_display() { 383 | let password = "password123"; 384 | let salt = "somesalt"; 385 | let algo = "bcrypt"; 386 | 387 | // Create a new Hash 388 | let original_hash = Hash::new(password, salt, algo).unwrap(); 389 | 390 | // Test the Display implementation for Hash 391 | assert_eq!( 392 | format!("{}", original_hash), 393 | format!("Hash {{ hash: {:?} }}", original_hash.hash()) 394 | ); 395 | } 396 | 397 | #[test] 398 | fn test_hash_algorithm_display() { 399 | let algo = HashAlgorithm::Bcrypt; 400 | 401 | // Test the Display implementation for HashAlgorithm 402 | assert_eq!(format!("{}", algo), format!("{:?}", algo)); 403 | } 404 | 405 | #[test] 406 | fn test_hash_algorithm_from_str() { 407 | let algo_str = "bcrypt"; 408 | let expected_algo = HashAlgorithm::Bcrypt; 409 | 410 | // Test the FromStr implementation for HashAlgorithm 411 | assert_eq!( 412 | algo_str.parse::().unwrap(), 413 | expected_algo 414 | ); 415 | } 416 | 417 | #[test] 418 | fn test_hash_algorithm_from_str_invalid() { 419 | let invalid_algo_str = "invalid"; 420 | 421 | // Test the FromStr implementation for HashAlgorithm with an invalid string 422 | assert!(invalid_algo_str.parse::().is_err()); 423 | } 424 | 425 | #[test] 426 | fn test_parse() { 427 | let password = "password123"; 428 | let salt = "somesalt"; 429 | let algo = "bcrypt"; 430 | 431 | // Create a new Hash 432 | let original_hash = Hash::new(password, salt, algo).unwrap(); 433 | 434 | // Convert the Hash to a JSON string 435 | let hash_json = serde_json::to_string(&original_hash).unwrap(); 436 | 437 | // Parse the JSON string back into a Hash 438 | let parsed_hash = Hash::parse(&hash_json).unwrap(); 439 | 440 | // Check that the parsed Hash is equal to the original 441 | assert_eq!(original_hash, parsed_hash); 442 | } 443 | 444 | #[test] 445 | fn test_parse_invalid() { 446 | let invalid_json = "invalid"; 447 | 448 | // Try to parse the invalid JSON string 449 | assert!(Hash::parse(invalid_json).is_err()); 450 | } 451 | 452 | #[test] 453 | fn test_parse_algorithm_argon2i() { 454 | let hash_str = "$argon2i$somehashstring"; 455 | let algorithm = Hash::parse_algorithm(hash_str); 456 | 457 | assert_eq!(algorithm.unwrap(), HashAlgorithm::Argon2i); 458 | } 459 | 460 | #[test] 461 | fn test_parse_algorithm_bcrypt() { 462 | let hash_str = "$bcrypt$somehashstring"; 463 | let algorithm = Hash::parse_algorithm(hash_str); 464 | 465 | assert_eq!(algorithm.unwrap(), HashAlgorithm::Bcrypt); 466 | } 467 | 468 | #[test] 469 | fn test_parse_algorithm_scrypt() { 470 | let hash_str = "$scrypt$somehashstring"; 471 | let algorithm = Hash::parse_algorithm(hash_str); 472 | 473 | assert_eq!(algorithm.unwrap(), HashAlgorithm::Scrypt); 474 | } 475 | 476 | #[test] 477 | fn test_parse_algorithm_unsupported() { 478 | let hash_str = "$unsupported$somehashstring"; 479 | let algorithm = Hash::parse_algorithm(hash_str); 480 | 481 | assert!(algorithm.is_err()); 482 | assert_eq!( 483 | algorithm.err().unwrap(), 484 | "Unsupported hash algorithm: unsupported" 485 | ); 486 | } 487 | 488 | #[test] 489 | fn test_parse_algorithm_invalid() { 490 | let hash_str = "invalidhashstring"; 491 | let algorithm = Hash::parse_algorithm(hash_str); 492 | 493 | assert!(algorithm.is_err()); 494 | assert_eq!(algorithm.err().unwrap(), "Invalid hash string"); 495 | } 496 | } 497 | -------------------------------------------------------------------------------- /tests/test_macros.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | 7 | // Importing hsh crate and all of its macros 8 | use hsh::models::hash::*; 9 | use hsh::models::hash_algorithm::HashAlgorithm; 10 | use hsh::{generate_hash, hash_length, new_hash}; 11 | use hsh::{ 12 | hsh_assert, hsh_contains, hsh_in_range, hsh_join, hsh_max, 13 | hsh_min, hsh_parse, hsh_print, hsh_print_vec, hsh_split, 14 | hsh_vec, match_algo, random_string, to_str_error, 15 | }; 16 | 17 | #[test] 18 | #[should_panic(expected = "Assertion failed!")] 19 | fn macro_hsh_assert_fail() { 20 | // Test that hsh_assert! macro correctly triggers a panic when the argument is false 21 | hsh_assert!(false); 22 | } 23 | 24 | #[test] 25 | fn macro_hsh_assert() { 26 | // Test that hsh_assert! macro does not trigger a panic when the argument is true 27 | hsh_assert!(true); 28 | } 29 | 30 | #[test] 31 | fn macro_hsh_join() { 32 | // Test that hsh_join! macro correctly joins the string arguments together 33 | let s = hsh_join!(" ", "Hello", "world"); 34 | assert_eq!(s, "Hello world"); 35 | } 36 | 37 | #[test] 38 | fn macro_hsh_min() { 39 | // Test that hsh_min! macro correctly identifies the minimum value among the arguments 40 | assert_eq!(hsh_min!(10, 20, 30), 10); 41 | } 42 | 43 | #[test] 44 | fn macro_hsh_max() { 45 | // Test that hsh_max! macro correctly identifies the maximum value among the arguments 46 | assert_eq!(hsh_max!(10, 20, 30), 30); 47 | } 48 | 49 | #[test] 50 | fn macro_hsh_print() { 51 | // Test that hsh_print! macro correctly prints the argument 52 | hsh_print!("Hello, World!"); 53 | } 54 | 55 | #[test] 56 | fn macro_hsh_print_vec() { 57 | // Test that hsh_print_vec! macro correctly prints the elements of the vector argument 58 | hsh_print_vec!(&[1, 2, 3]); 59 | } 60 | 61 | #[test] 62 | fn macro_hsh_split() { 63 | // Test that hsh_split! macro correctly splits the string argument into a vector of words 64 | let v = hsh_split!("Hello World"); 65 | assert_eq!(v, vec!["Hello", "World"]); 66 | } 67 | 68 | #[test] 69 | fn macro_hsh_vec() { 70 | // Test that hsh_vec! macro correctly creates a vector from the arguments 71 | let v = hsh_vec!(1, 2, 3); 72 | assert_eq!(v, &[1, 2, 3]); 73 | } 74 | 75 | #[test] 76 | fn macro_hsh_contains() { 77 | // Test that hsh_contains! macro correctly checks if the first string contains the second 78 | assert!(hsh_contains!("Hello", "H")); 79 | assert!(!hsh_contains!("Hello", "x")); 80 | } 81 | 82 | #[test] 83 | fn macro_hsh_in_range() { 84 | let lower_bound = 0; 85 | let upper_bound = 100; 86 | let test_val1 = 10; 87 | let test_val2 = -10; 88 | 89 | assert!(hsh_in_range!(test_val1, lower_bound, upper_bound)); 90 | assert!(!hsh_in_range!(test_val2, lower_bound, upper_bound)); 91 | } 92 | 93 | #[test] 94 | fn macro_hsh_parse() { 95 | let input: Result = hsh_parse!("42"); 96 | assert_eq!(input, Ok(42)); 97 | } 98 | 99 | #[test] 100 | fn macro_to_str_error() { 101 | let result: Result<(), String> = Ok(()); 102 | let error: Result<(), String> = 103 | Err("Error message".to_string()); 104 | 105 | let result_str = to_str_error!(result); 106 | assert_eq!(result_str, Ok(())); 107 | 108 | let error_str = to_str_error!(error); 109 | assert_eq!(error_str, Err("Error message".to_string())); 110 | } 111 | 112 | #[test] 113 | fn macro_random_string() { 114 | let random = random_string!(10); 115 | assert_eq!(random.len(), 10); 116 | } 117 | 118 | #[test] 119 | fn macro_match_algo() { 120 | let algo_str = "bcrypt"; 121 | let algo_result = match_algo!(algo_str); 122 | assert_eq!(algo_result, Ok(HashAlgorithm::Bcrypt)); 123 | 124 | let unsupported_str = "md5"; 125 | let unsupported_result = match_algo!(unsupported_str); 126 | assert_eq!( 127 | unsupported_result, 128 | Err("Unsupported hash algorithm: md5".to_string()) 129 | ); 130 | } 131 | 132 | #[test] 133 | fn macro_generate_hash() { 134 | let password = "password"; 135 | let salt = "salt"; 136 | let algo = "bcrypt"; 137 | let hash_bytes = generate_hash!(password, salt, algo); 138 | 139 | assert!(hash_bytes.is_ok()); 140 | } 141 | 142 | #[test] 143 | fn macro_new_hash() { 144 | let password = "password"; 145 | let salt = "salt"; 146 | let algo = "bcrypt"; 147 | let hash = new_hash!(password, salt, algo); 148 | 149 | assert!(hash.is_ok()); 150 | } 151 | 152 | #[test] 153 | fn macro_hash_length() { 154 | let password = "password"; 155 | let salt = "salt"; 156 | let algo = "bcrypt"; 157 | 158 | let hash = new_hash!(password, salt, algo); 159 | assert!(hash.is_ok()); 160 | let hash = hash.unwrap(); 161 | 162 | let password_length = hash_length!(hash); 163 | assert_eq!(password_length, 60); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /tests/test_main.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use assert_cmd::prelude::*; 7 | use std::process::Command; 8 | 9 | #[test] 10 | fn test_run_with_hsh_test_mode() { 11 | let output = Command::cargo_bin("hsh") 12 | .unwrap() 13 | .env("HSH_TEST_MODE", "1") 14 | .output() 15 | .expect("Failed to execute command"); 16 | 17 | // Assert that the command execution was not successful 18 | assert!(!output.status.success()); 19 | 20 | // Assert that the error message was printed to stderr 21 | let stderr = String::from_utf8(output.stderr).unwrap(); 22 | assert!(stderr.contains("Error running hsh: Simulated error")); 23 | } 24 | 25 | #[test] 26 | fn test_run_without_hsh_test_mode() { 27 | let output = Command::cargo_bin("hsh") 28 | .unwrap() 29 | .output() 30 | .expect("Failed to execute command"); 31 | 32 | // Assert that the command execution was successful 33 | assert!(output.status.success()); 34 | 35 | // Assert that the welcome messages were printed to stdout 36 | let stdout = String::from_utf8(output.stdout).unwrap(); 37 | assert!(stdout.contains("Welcome to `HSH` 👋!")); 38 | assert!(stdout.contains("Quantum-Resistant Cryptographic Hash Library for Password Encryption and Verification.")); 39 | } 40 | 41 | fn run_test_scenario() -> Result<(), Box> { 42 | // Simulate an error scenario 43 | // Return an error explicitly 44 | Err("Test error".into()) 45 | } 46 | 47 | #[test] 48 | fn test_main() { 49 | // Test calling the `run()` function directly 50 | let result = run_test_scenario(); 51 | assert!(result.is_err()); 52 | 53 | // Test calling the `main()` function 54 | let output = Command::cargo_bin("hsh") 55 | .unwrap() 56 | .env("HSH_TEST_MODE", "1") 57 | .output() 58 | .expect("Failed to execute command"); 59 | 60 | // Assert that the command execution was not successful 61 | assert!(!output.status.success()); 62 | 63 | // Assert that the error message was printed to stderr 64 | let stderr = String::from_utf8(output.stderr).unwrap(); 65 | assert!(stderr.contains("Error running hsh: Simulated error")); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tests/test_scrypt.rs: -------------------------------------------------------------------------------- 1 | // Copyright © 2023-2024 Hash (HSH) library. All rights reserved. 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT 3 | 4 | #[cfg(test)] 5 | mod tests { 6 | use hsh::models::hash_algorithm::HashingAlgorithm; 7 | 8 | #[test] 9 | fn test_hash_password_success() { 10 | let password = "secure_password"; 11 | let salt = "random_salt"; 12 | 13 | let result = hsh::algorithms::scrypt::Scrypt::hash_password( 14 | password, salt, 15 | ); 16 | assert!(result.is_ok()); 17 | } 18 | 19 | #[test] 20 | fn test_same_salt_and_password_produce_same_hash() { 21 | let password = "password123"; 22 | let salt = "salt123"; 23 | 24 | let hash1_result = 25 | hsh::algorithms::scrypt::Scrypt::hash_password( 26 | password, salt, 27 | ) 28 | .unwrap(); 29 | let hash2_result = 30 | hsh::algorithms::scrypt::Scrypt::hash_password( 31 | password, salt, 32 | ) 33 | .unwrap(); 34 | 35 | assert_eq!(hash1_result, hash2_result); 36 | } 37 | 38 | #[test] 39 | fn test_different_salts_produce_different_hashes() { 40 | let password = "password123"; 41 | let salt1 = "salt123"; 42 | let salt2 = "another_salt123"; 43 | 44 | let hash1_result = 45 | hsh::algorithms::scrypt::Scrypt::hash_password( 46 | password, salt1, 47 | ) 48 | .unwrap(); 49 | let hash2_result = 50 | hsh::algorithms::scrypt::Scrypt::hash_password( 51 | password, salt2, 52 | ) 53 | .unwrap(); 54 | 55 | assert_ne!(hash1_result, hash2_result); 56 | } 57 | 58 | #[test] 59 | fn test_different_passwords_produce_different_hashes() { 60 | let password1 = "password123"; 61 | let password2 = "other_password123"; 62 | let salt = "salt123"; 63 | 64 | let hash1_result = 65 | hsh::algorithms::scrypt::Scrypt::hash_password( 66 | password1, salt, 67 | ) 68 | .unwrap(); 69 | let hash2_result = 70 | hsh::algorithms::scrypt::Scrypt::hash_password( 71 | password2, salt, 72 | ) 73 | .unwrap(); 74 | 75 | assert_ne!(hash1_result, hash2_result); 76 | } 77 | } 78 | --------------------------------------------------------------------------------