├── .github ├── dependabot.yml └── workflows │ └── build_deploy.yml ├── .gitignore ├── .mvn ├── maven.config └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── CHANGELOG ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── misc ├── banner.psd ├── doc │ └── bcrypt-paper.pdf ├── icon.png └── icon.psd ├── modules ├── bcrypt-cli │ ├── pom.xml │ └── src │ │ ├── main │ │ └── java │ │ │ └── at │ │ │ └── favre │ │ │ └── lib │ │ │ └── crypto │ │ │ └── bcrypt │ │ │ └── cli │ │ │ ├── Arg.java │ │ │ ├── BcryptTool.java │ │ │ └── CLIParser.java │ │ └── test │ │ └── java │ │ └── at │ │ └── favre │ │ └── lib │ │ └── crypto │ │ └── bcrypt │ │ └── cli │ │ ├── ArgTest.java │ │ ├── BcryptToolTest.java │ │ └── CLIParserTest.java ├── bcrypt │ ├── pom.xml │ ├── proguard-rules.pro │ └── src │ │ ├── main │ │ └── java │ │ │ └── at │ │ │ └── favre │ │ │ └── lib │ │ │ └── crypto │ │ │ └── bcrypt │ │ │ ├── BCrypt.java │ │ │ ├── BCryptFormatter.java │ │ │ ├── BCryptOpenBSDProtocol.java │ │ │ ├── BCryptParser.java │ │ │ ├── IllegalBCryptFormatException.java │ │ │ ├── LongPasswordStrategies.java │ │ │ ├── LongPasswordStrategy.java │ │ │ └── Radix64Encoder.java │ │ └── test │ │ └── java │ │ └── at │ │ └── favre │ │ └── lib │ │ └── crypto │ │ └── bcrypt │ │ ├── BCryptFormatterTest.java │ │ ├── BCryptHighCostTest.java │ │ ├── BCryptParserTest.java │ │ ├── BcBcryptTestCases.java │ │ ├── BcryptTest.java │ │ ├── BenchmarkTest.java │ │ ├── FavreBcryptReferenceTests.java │ │ ├── JBcryptTestCases.java │ │ ├── LongPasswordStrategyTest.java │ │ ├── Radix64Test.java │ │ └── misc │ │ ├── BcryptMicroBenchmark.java │ │ ├── BcryptTestEntriesGenerator.java │ │ ├── BcryptTestEntry.java │ │ ├── Radix64RefDataCreator.java │ │ ├── Repeat.java │ │ └── RepeatRule.java └── benchmark-jmh │ ├── pom.xml │ └── src │ └── main │ └── java │ └── at │ └── favre │ └── lib │ └── crypto │ └── bcrypt │ └── benchmark │ └── BcryptBenchmark.java ├── mvnw ├── mvnw.cmd ├── pom.xml └── proguard-rules.pro /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # dependabot analyzing maven dependencies 2 | version: 2 3 | updates: 4 | - package-ecosystem: "maven" 5 | directory: "/" 6 | open-pull-requests-limit: 3 7 | schedule: 8 | interval: "weekly" 9 | labels: 10 | - "dependencies" 11 | -------------------------------------------------------------------------------- /.github/workflows/build_deploy.yml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy with Maven 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - '*' # Trigger on all tags 9 | pull_request: {} 10 | 11 | env: 12 | SONARQUBE_PROJECT: patrickfav_bcrypt 13 | 14 | jobs: 15 | build: 16 | runs-on: ubuntu-latest 17 | 18 | steps: 19 | - name: Checkout Code 20 | uses: actions/checkout@v3 21 | with: 22 | fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis 23 | - name: Cache SonarCloud packages 24 | uses: actions/cache@v3 25 | with: 26 | path: ~/.sonar/cache 27 | key: ${{ runner.os }}-sonar 28 | restore-keys: ${{ runner.os }}-sonar 29 | - name: Cache Maven 30 | id: cache-primes 31 | uses: actions/cache@v3 32 | with: 33 | path: ~/.m2 34 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 35 | restore-keys: ${{ runner.os }}-m2 36 | - name: Set up JDK 37 | uses: actions/setup-java@v3 38 | with: 39 | java-version: '17' 40 | distribution: 'temurin' 41 | - name: Build with Maven 42 | run: ./mvnw -B clean verify -DcommonConfig.jarSign.skip=true 43 | - name: Analyze with SonaQube 44 | if: ${{ github.actor != 'dependabot[bot]' }} 45 | env: 46 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any 47 | SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} 48 | run: mvn -B org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=$SONARQUBE_PROJECT 49 | 50 | deploy: 51 | needs: build 52 | if: startsWith(github.ref, 'refs/tags/') 53 | runs-on: ubuntu-latest 54 | 55 | steps: 56 | - uses: actions/checkout@v3 57 | - name: Retrieve Keystore from secrets 58 | env: 59 | KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} 60 | run: | 61 | echo $KEYSTORE_BASE64 | base64 --decode > keystore.jks 62 | - name: Cache Maven 63 | id: cache-primes 64 | uses: actions/cache@v3 65 | with: 66 | path: ~/.m2 67 | key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} 68 | restore-keys: ${{ runner.os }}-m2 69 | - name: Set up Maven Central Repository 70 | uses: actions/setup-java@v3 71 | with: 72 | java-version: '11' 73 | distribution: 'temurin' 74 | server-id: ossrh # Value of the distributionManagement/repository/id field of the pom.xml 75 | server-username: MAVEN_USERNAME # env variable for username in deploy 76 | server-password: MAVEN_PASSWORD # env variable for token in deploy 77 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} # Value of the GPG private key to import 78 | gpg-passphrase: MAVEN_GPG_PASSPHRASE # env variable for GPG private key passphrase 79 | - name: Publish package 80 | # prepare bcrypt with verify, stage and then release, after that build all modules to upload to github 81 | run: | 82 | ./mvnw -B verify nexus-staging:deploy -P deploy,!allmodules,mainmodule -DskipTests && \ 83 | ./mvnw -B nexus-staging:release -P deploy,!allmodules,mainmodule && \ 84 | ./mvnw -B verify -DskipTests 85 | env: 86 | OPENSOURCE_PROJECTS_KS_PW: ${{ secrets.KEYSTORE_PASSWORD }} 87 | OPENSOURCE_PROJECTS_KEY_PW: ${{ secrets.KEYSTORE_KEY_PASSWORD }} 88 | MAVEN_USERNAME: ${{ secrets.OSSRH_USERNAME }} 89 | MAVEN_PASSWORD: ${{ secrets.OSSRH_TOKEN }} 90 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} 91 | - name: Create and upload Github Release 92 | uses: xresloader/upload-to-github-release@v1 93 | env: 94 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 95 | with: 96 | file: "modules/bcrypt/target/bcrypt-*.jar;modules/bcrypt/target/*.sha256;modules/bcrypt/target/checksum-sha256.txt;modules/bcrypt-cli/target/bcrypt-*-full.jar;modules/bcrypt-cli/target/*.sha256" 97 | tags: true 98 | draft: false 99 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .settings 3 | .metadata 4 | bin 5 | gen 6 | NewFile.* 7 | lint.xml 8 | local.properties 9 | app.properties 10 | junit-report.xml 11 | build 12 | target 13 | 14 | *.dex 15 | *.ap_ 16 | *.class 17 | 18 | out 19 | proguard 20 | /RemoteSystemsTempFiles 21 | .gradle 22 | Thumbs.db 23 | 24 | # ignored intellij 25 | .idea 26 | .navigation 27 | *.iml 28 | *.ipr 29 | 30 | 31 | # Eclipse project files 32 | .classpath 33 | .project 34 | 35 | signing.properties 36 | keystore.jks 37 | ci-settings.xml 38 | secrets.tar 39 | pom.xml.versionsBackup 40 | -------------------------------------------------------------------------------- /.mvn/maven.config: -------------------------------------------------------------------------------- 1 | -DcommonConfig.compiler.profile=jdk8 2 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickfav/bcrypt/d068562a2d700db0981a32b534e3fc046d327c8f/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # https://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar 19 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | # Releases 2 | 3 | ## v0.10.2 4 | 5 | * Fix deployment setup 6 | 7 | ## v0.10.1 8 | 9 | * Re-Introduce DEFAULT_MAX_PW_LENGTH_BYTE to be code compatible with 0.9.0- (thx for the hint @Andrew-Cottrell) 10 | 11 | ## v0.10.0 12 | 13 | * [BREAKING CHANGE] the null terminator will not be counted to the 72 byte max length anymore. This changes the behaviour IF you used passwords with EXACTLY 72 bytes. #43, #44 (thx @quinot) 14 | * migrate to Maven Central, Github Actions and Codecov #46 15 | * update many dependencies and remove warnings for CVE-2020-15522 (bc) CVE-2020-15250 (junit) -> were never part of production code #41 16 | 17 | 18 | ## v0.9.0 19 | * fix license headers and correct credits to jBcrypt 20 | * add long-password strategy to verifier #21 21 | * fix not returning correct hash version when verifying #24 22 | * allow for custom max password length in Version #22 23 | 24 | ### Breaking Changes 25 | 26 | * `verify(byte[] password, int cost, byte[] salt, byte[] rawBcryptHash23Bytes)` signature changed, added `version` property (see #24) 27 | * `LongPasswordStrategies` factory methods now require the version for the max password length (see #22) 28 | * Verifier now accepts `Version` as a constructor parameter and `verifyStrict` therefore does not need one (see #22) 29 | 30 | ## v0.8.0 31 | 32 | * add new verify API signature accepting char array password and byte array hash #16 33 | * add OSGi support #15 34 | 35 | ## v0.7.0 36 | 37 | * add OSWAP dependency check plugin to Maven POM #14 38 | 39 | ## v0.6.0 40 | 41 | * change verifier that accepts `String` type to accept more flexible `CharSequence` 42 | 43 | ## v0.5.0 44 | 45 | * allow actual 2^31 rounds (fix integer overflow issue with `1<<31`) #7 46 | * use Apache v2 compatible Radix 64 impl and skip OpenJDK one #8 47 | * add JMH benchmark module #11 48 | 49 | ## v0.4.0 50 | 51 | * add cli tool #5 52 | * add (_experimental_) ProGuard optimized version (use maven classifier 'optimized') #4 53 | 54 | ## v0.3.0 55 | 56 | * support 24byte hash out and make null terminator optional (BC style) #3 57 | * use OpenJDK Radix64 encoder implementation #2 58 | * fix issue where string was created internally which cannot be wiped 59 | 60 | ## v0.2.0 61 | 62 | * add String API 63 | * refactor Version to be POJO (so caller can provide own impl) 64 | * add BcryptFormtter 65 | * add more tests 66 | 67 | ## v0.1.0 68 | 69 | initial version 70 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Bcrypt 2 | 3 | We ❤ pull requests from everyone. 4 | 5 | If possible proof features and bugfixes with unit tests. 6 | This repo validates against checkstyle (import the xml found in the root to your IDE if possible) 7 | 8 | To run the tests (and checkstyle): 9 | 10 | ```shell 11 | mvn clean install 12 | ``` 13 | 14 | Tests are automatically run against feature branches and pull requests 15 | via TravisCI, so you can also depend on that. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file encoding. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2018 Patrick Favre-Bulle 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bcrypt Java Library and CLI Tool 2 | 3 | This is an implementation of the OpenBSD Blowfish password hashing algorithm, as described in "[A Future-Adaptable Password Scheme](http://www.openbsd.org/papers/bcrypt-paper.ps)" by Niels Provos and David Mazieres. It's core is based on [jBcrypt](https://github.com/djmdjm/jBCrypt), but heavily refactored, modernized and with a lot of updates and enhancements. It supports all common [versions](https://en.wikipedia.org/wiki/Bcrypt#Versioning_history), has a security sensitive API and is fully tested against a range of test vectors and reference implementations. 4 | 5 | [![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/bcrypt)](https://mvnrepository.com/artifact/at.favre.lib/bcrypt) 6 | [![Github Actions](https://github.com/patrickfav/bcrypt/actions/workflows/build_deploy.yml/badge.svg)](https://github.com/patrickfav/bcrypt/actions) 7 | [![libs.tech recommends](https://libs.tech/project/139898859/badge.svg)](https://libs.tech/project/139898859/bcrypt) 8 | [![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bcrypt.svg)](https://www.javadoc.io/doc/at.favre.lib/bcrypt) 9 | [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bcrypt&metric=coverage)](https://sonarcloud.io/summary/new_code?id=patrickfav_bcrypt) 10 | [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bcrypt&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=patrickfav_bcrypt) 11 | [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bcrypt&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=patrickfav_bcrypt) 12 | 13 | The code is compiled with target [Java 7](https://en.wikipedia.org/wiki/Java_version_history#Java_SE_7) to be compatible with most [_Android_](https://www.android.com/) versions as well as normal Java applications. 14 | 15 | ## Quickstart 16 | 17 | This library is published to Maven Central 18 | 19 | Add the dependency of the [latest version](https://github.com/patrickfav/bcrypt/releases/latest) to your `pom.xml`: 20 | 21 | ```xml 22 | 23 | at.favre.lib 24 | bcrypt 25 | {latest-version} 26 | 27 | ``` 28 | 29 | Or if you are using Gradle: 30 | 31 | ```groovy 32 | implementation("at.favre.lib:bcrypt:{latest-version}") 33 | ``` 34 | 35 | A simple example: 36 | 37 | ```java 38 | String password = "1234"; 39 | String bcryptHashString = BCrypt.withDefaults().hashToString(12, password.toCharArray()); 40 | // $2a$12$US00g/uMhoSBm.HiuieBjeMtoN69SN.GE25fCpldebzkryUyopws6 41 | ... 42 | BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), bcryptHashString); 43 | // result.verified == true 44 | ``` 45 | 46 | ## API Description for the Java Library 47 | 48 | The following APIs are for advanced use-cases and require the developer to be familiar with the material. If you are not 49 | sure, just stick to the quick start example. 50 | 51 | ### Bcrypt Versions 52 | This implementation supports the various versions, which basically only differ through their identifier: 53 | 54 | ```java 55 | char[] bcryptChars = BCrypt.with(BCrypt.Version.VERSION_2Y).hashToChar(6, password.toCharArray()); 56 | // $2y$06$doGnefu9cbLkJTn8sef7U.dynHJFe5hS6xp7vLWb2Zu7e8cOuMVmS 57 | 58 | char[] bcryptChars = BCrypt.with(BCrypt.Version.VERSION_2B).hashToChar(6, password.toCharArray()); 59 | // $2b$06$GskjDDM9oejRN8pxNhiSZuIw/cnjbsNb8IfWGd3TFQXtRfKTN95r. 60 | ``` 61 | 62 | For example the [PHP implementation of bcrypt](http://php.net/manual/en/function.password-hash.php) will return hashes with version `$2y$`. 63 | By using `BCrypt.withDefaults()` it will default to version `$2a$`. The older `$2$` version is not supported. 64 | For advanced use cases you may add your own version by providing a version identifier and a custom message formatter 65 | as well as parser. 66 | 67 | ```java 68 | Version customVersion2f = new Version(new byte[]{0x32, 0x66} /* 2f */, true, true, myCustomFormatter, myCustomParser); 69 | ``` 70 | 71 | ### byte[] vs char[] API 72 | 73 | You can use either `char[]` or `byte[]` as input or output parameter. The reason `String` is usually omitted in security 74 | relevant APIs is, that a primitive array can usually be overwritten, as to discard it immediately after use. It is however 75 | not possible to wipe the content of the immutable `String`. The encoding always defaults to `UTF-8`. 76 | 77 | ```java 78 | byte[] bcryptHashBytes = BCrypt.withDefaults().hash(6, password.getBytes(StandardCharsets.UTF_8)); 79 | ... 80 | BCrypt.Result result = BCrypt.verifyer().verify(password.getBytes(StandardCharsets.UTF_8), bcryptHashBytes); 81 | ``` 82 | 83 | and 84 | 85 | ```java 86 | char[] bcryptChars = BCrypt.withDefaults().hashToChar(12, password.toCharArray()); 87 | ... 88 | BCrypt.Result result = BCrypt.verifyer().verify(password.toCharArray(), bcryptChars); 89 | ``` 90 | 91 | Note, that there are APIs that return `String` type hash and can verify it directly. This is done 92 | out of convenience and to present easy to understand API for all audiences. Usually the hash is 93 | not as critical as the raw password, so it might be ok to not be able to wipe it immediately. But 94 | usually you should prefer `char[]` or `byte[]` APIs. 95 | 96 | ### Strict Verification 97 | 98 | If you want the hash verification to only verify for a specific version you can use `verifyStrict()` 99 | 100 | ```java 101 | byte[] hash2y = BCrypt.with(BCrypt.Version.VERSION_2Y).hash(6, password.getBytes(StandardCharsets.UTF_8)); 102 | BCrypt.Result resultStrict = BCrypt.verifyer(BCrypt.Version.VERSION_2A).verifyStrict(password.getBytes(StandardCharsets.UTF_8), hash2y); 103 | // resultStrict.verified == false 104 | ``` 105 | 106 | ### Handling for Overlong passwords 107 | 108 | Due to the limitation in the Blowfish cipher, the maximum password length is 72 bytes (note that UTF-8 encoded, a 109 | character can be as much as 4 bytes). Per 110 | default, the `hash()` method will throw an exception if the provided password is too long. 111 | 112 | The API supports passing a custom handling in that case, to mimic the behaviour of some popular implementations to just 113 | truncate the password. 114 | 115 | ```java 116 | BCrypt.with(LongPasswordStrategies.truncate(Version.VERSION_2A)).hash(6, pw); 117 | BCrypt.with(LongPasswordStrategies.hashSha512(Version.VERSION_2A)).hash(6, pw); //allows to honour all pw bytes 118 | ``` 119 | 120 | Don't forget to use the same strategy when verifying: 121 | 122 | ```java 123 | BCrypt.verifyer(LongPasswordStrategies.truncate(Version.VERSION_2A)).verify(pw, hash); 124 | ``` 125 | 126 | The password will only be transformed if it is longer than 72 bytes. *It is important to note, however, that using any 127 | of these techniques will essentially create a custom flavor of Bcrypt, possibly not compatible with other implementations.* 128 | 129 | However, you can also disable this warning by using the `LongPasswordStrategies.none` strategy. It will pass the raw data to the internal cryptographic primitive (which in turn will ignore anything longer than 72 bytes). This is the standard behaviour of BCrypt. 130 | 131 | ### Custom Salt or SecureRandom 132 | 133 | The caller may provide their own salt (which must be exactly 16 bytes) with: 134 | 135 | ```java 136 | BCrypt.withDefaults().hash(6, salt16Bytes, password.getBytes(StandardCharsets.UTF_8)); 137 | ``` 138 | 139 | or provide a custom instance of a cryptographically secure pseudorandom number generator ([CPRNG](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator)) 140 | which is used for the internal secure creation of the salt if none is passed: 141 | 142 | ```java 143 | BCrypt.with(new SecureRandom()).hash(6, password.getBytes(StandardCharsets.UTF_8)); 144 | ``` 145 | 146 | ### Retrieve and Verify the Raw Hash 147 | 148 | Per default the result of `hash()` methods will return in the [Modular Crypt Format](https://passlib.readthedocs.io/en/stable/modular_crypt_format.html) 149 | (e.g. `$2y$06$doGnefu9cbLkJTn8sef7U.dynHJFe5hS6xp7vLWb2Zu7e8cOuMVmS`), but if you prefer encoding the hash yourself you can just use 150 | 151 | ```java 152 | BCrypt.HashData hashData = BCrypt.withDefaults().hashRaw(6, salt, password.getBytes(StandardCharsets.UTF_8)); 153 | ``` 154 | 155 | there is even a verify method optimized for this use-case: 156 | 157 | ```java 158 | BCrypt.Result result = BCrypt.verifyer().verify(pw, hashData); 159 | ``` 160 | 161 | You could even use the default formatter later on: 162 | 163 | ```java 164 | byet[] hashMsg = Version.VERSION_2A.formatter.createHashMessage(hashData); 165 | ``` 166 | 167 | ## Command Line Interface (CLI) Tool 168 | 169 | In addition to the Java library there is a companion command line interface (CLI) tool (found in the `bcrypt-cli` 170 | sub-module) which uses this bcrypt library. It features creating bcrypt password hashes with chosen cost factor and 171 | optionally passed salt value as well as verifying given hash against given password. 172 | 173 | This command will create a bcrypt hash: 174 | 175 | java -jar bcrypt-cli.jar 'mySecretPw' -b 12 176 | 177 | This command will verify given bcrypt hash (returns `!= 0` if could not be verified): 178 | 179 | java -jar bcrypt-cli.jar 'mySecretPw' -c '$2a$08$hgaLWQl7PdKIkx9iQyoLkeuIqizWtPErpyC7aDBasi2Pav97wwW9G' 180 | 181 | The full API can be read in the doc by passing `-h` 182 | 183 | -b,--bhash <[16-hex-byte-salt]> Use this flag if you want to compute the bcrypt hash. Pass the 184 | logarithm cost factor (4-31) and optionally the used salt as hex 185 | encoded byte array (must be exactly 16 bytes/32 characters hex). 186 | Example: '--bhash 12 8e270d6129fd45f30a9b3fe44b4a8d9a' 187 | -c,--check Use this flag if you want to verify a hash against a given 188 | password. Example: '--check 189 | $2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i' 190 | -h,--help Prints help docs. 191 | -v,--version Prints current version. 192 | 193 | ## Download 194 | 195 | The artifacts are deployed to [Maven Central](https://search.maven.org/). 196 | 197 | ### Maven 198 | 199 | Add the dependency of the [latest version](https://github.com/patrickfav/bcrypt/releases) to your `pom.xml`: 200 | 201 | ```xml 202 | 203 | at.favre.lib 204 | bcrypt 205 | {latest-version} 206 | 207 | ``` 208 | 209 | ### Gradle 210 | 211 | Add to your `build.gradle` module dependencies: 212 | 213 | implementation group: 'at.favre.lib', name: 'bcrypt', version: '{latest-version}' 214 | 215 | ### Local Jar Library 216 | 217 | [Grab jar from latest release.](https://github.com/patrickfav/bcrypt/releases/latest) 218 | 219 | ### OSGi 220 | 221 | The library should be prepared to be used with the OSGi framework with the help of the [bundle plugin](http://felix.apache.org/documentation/subprojects/apache-felix-maven-bundle-plugin-bnd.html). 222 | 223 | ### CLI Tool 224 | 225 | Get the binary from the [release page](https://github.com/patrickfav/bcrypt/releases/latest) or build it yourself by with mvn (see below). The `jar` 226 | will be in the `bcrypt-cli/target` folder. 227 | 228 | ## Description 229 | 230 | ### Security Analysis 231 | 232 | I'll quote security expert [Thomas Pornin](http://www.bolet.org/~pornin/) on this (an excerpt [from this post](https://security.stackexchange.com/a/6415/60108)): 233 | 234 | **tl;dr bcrypt is better than PBKDF2 because PBKDF2 can be better accelerated with GPUs. As such, PBKDF2 is easier to brute 235 | force offline with consumer hardware. [srcypt tried to address bcrypt's shortcommings, but didn't succeed all the way.](https://security.stackexchange.com/a/26253/60108) 236 | [Argon2 is too new to tell.](https://security.stackexchange.com/a/119784/60108)** 237 | 238 | > Bcrypt has the best kind of repute that can be achieved for a cryptographic algorithm: it has been around for quite some time, used quite widely, "attracted attention", and yet remains unbroken to date. 239 | > 240 | > 241 | > #### Why bcrypt is somewhat better than PBKDF2 242 | > 243 | > If you look at the situation in details, you can actually see some points where bcrypt is better than, say, PBKDF2. Bcrypt is a password hashing function which aims at being slow. To be precise, we want the password hashing function to be as slow as possible for the attacker while not being intolerably slow for the honest systems. (...) 244 | > What we want to avoid is that an attacker might use some non-PC hardware which would allow him to suffer less than us from the extra work implied by bcrypt or PBKDF2. In particular, an industrious attacker may want to use a GPU or a FPGA. SHA-256, for instance, can be very efficiently implemented on a GPU, since it uses only 32-bit logic and arithmetic operations that GPU are very good at. (...) 245 | > Bcrypt happens to heavily rely on accesses to a table which is constantly altered throughout the algorithm execution. This is very fast on a PC, much less so on a GPU, where memory is shared and all cores compete for control of the internal memory bus. Thus, the boost that an attacker can get from using GPU is quite reduced, compared to what the attacker gets with PBKDF2 or similar designs. 246 | > 247 | > 248 | > #### Why bcrypt is not optimally secure 249 | > 250 | > Bcrypt needs only 4 kB of fast RAM. While bcrypt does a decent job at making life difficult for a GPU-enhanced attacker, it does little against a FPGA-wielding attacker. 251 | > 252 | > 253 | > #### What NIST recommends 254 | > 255 | > NIST has issued Special Publication SP 800-132 on the subject of storing hashed passwords. Basically they recommend PBKDF2. This does not mean that they deem bcrypt insecure; they say nothing at all about bcrypt. It just means that NIST deems PBKDF2 "secure enough" (and it certainly is much better than a simple hash !). Also, NIST is an administrative organization, so they are bound to just love anything which builds on already "Approved" algorithms like SHA-256. On the other hand, bcrypt comes from Blowfish which has never received any kind of NIST blessing (or curse). 256 | 257 | #### What Cost Factor should I use? 258 | 259 | Again, quote from Thomas Pornin [from this post](https://security.stackexchange.com/a/31846/60108): 260 | 261 | > As much as possible! This salted-and-slow hashing is an arms race between the attacker and the defender. You use many iterations to make the hashing of a password harder for everybody. To improve security, you should set that number as high as you can tolerate on your server, given the tasks that your server must otherwise fulfill. Higher is better. 262 | 263 | So find your tolerable slowest performance (for some this is 3 sec, for some 250 ms, for some 1 minute) and try it out on an average lower end device your user-base would use (if the client has to calculate the hash) and/or benchmark your server. 264 | 265 | Note, that it is unfortunately [NOT possible to increase the cost-factor](https://security.stackexchange.com/a/23308/60108) 266 | of a calculated bcrypt hash without knowing the original password. A possible solution is to persist hashes with multiple work factors for 267 | different use cases/migration. 268 | 269 | ### Performance 270 | 271 | Compared to two other implementations in Java they all share similar performance characteristics. Using the simple micro 272 | benchmark in this repo (see `BcryptMicroBenchmark`), I got the following results with a Intel Core [i7-7700K](https://ark.intel.com/products/97129/Intel-Core-i7-7700K-Processor-8M-Cache-up-to-4_50-GHz), Win 10, 273 | Java 8 (172): 274 | 275 | | | cost 6 | cost 8 | cost 10 | cost 12 | cost 14 | 276 | |--------------|----------|-----------|----------|-----------|-----------| 277 | | favreBcrypt | 3.38 ms | 13.54 ms | 53.91 ms | 216.01 ms | 873.93 ms | 278 | | jBcrypt | 3.43 ms | 13.75 ms | 54.76 ms | 218.62 ms | 883.55 ms | 279 | | BouncyCastle | 3.14 ms | 12.5 ms | 49.8 ms | 199.09 ms | 799.71 ms | 280 | 281 | compare that with a 2017 flag ship Android phone Samsung Galaxy S8+ ([SM-G955F](https://www.gsmarena.com/samsung_galaxy_s8+-8523.php)) with Android 8: 282 | 283 | | | cost 6 | cost 8 | cost 10 | cost 12 | cost 14 | 284 | |--------------|----------|-----------|-----------|-----------|-----------| 285 | | favreBcrypt | 8.13 ms | 29.05 ms | 110.62 ms | 438.45 ms | 1768.44 ms| 286 | | jBcrypt | 7.91 ms | 30.91 ms | 116.45 ms | 462.93 ms | 1855.36 ms| 287 | | BouncyCastle | 10.41 ms| 38.03 ms | 149.09 ms | 595.19 ms | 2383.72 ms| 288 | 289 | More benchmarks can be found in the [wiki](https://github.com/patrickfav/bcrypt/wiki/Benchmark). 290 | 291 | So it makes sense that this implementation and jBcrypt's has the same performance as it is the same core 292 | implementation. Bouncy Castle is _slightly_ faster (on the JVM, not on Android interestingly), but keep in mind that they do a little less work (only generating the hash, not the whole out message). 293 | 294 | Compare this to other benchmarks, [like this one in node.js](https://github.com/dcodeIO/bcrypt.js/wiki/Benchmark) where a bcrypt hash with cost factor 12 is between 300-400ms. 295 | 296 | **Disclaimer:** Micro benchmarks are [usually a really bad way to measure performance](https://mrale.ph/blog/2012/12/15/microbenchmarks-fairy-tale.html). 297 | These numbers are only informal tests and should not be used to derive any security relevant decisions. 298 | 299 | #### JMH Benchmark 300 | 301 | Additionally there is JMH benchmark module, which is probably better than my home-brew micro benchmark. Build it with 302 | maven `./mvnw clean install` (you may want to disable jar signing with `` property) and execute 303 | it with `java -jar modules/benchmark-jmh/target/benchmark-jmh-x.y.z-full.jar`. 304 | 305 | ### Test Vectors and Reference Implementations 306 | 307 | This implementation is tested against the bcrypt implementation jBcrypt and Bouncy Castle. It includes test vectors 308 | found in the test cases of bcrypt and [various](https://stackoverflow.com/a/12761326/774398) [places](http://openwall.info/wiki/john/sample-hashes) [on](http://cvsweb.openwall.com/cgi/cvsweb.cgi/Owl/packages/glibc/crypt_blowfish/wrapper.c?rev=HEAD) [the web](https://github.com/BcryptNet/bcrypt.net/blob/main/src/BCrypt.Net.UnitTests/BCryptTests.cs). Additionally I [created a reference test suite](https://github.com/patrickfav/bcrypt/wiki/Published-Test-Vectors) for regression tests and to check compatibility with 309 | other libraries. 310 | 311 | ### The Modular Crypt Format for bcrypt 312 | 313 | Since bcrypt evolved from OpenBSD most implementations output the hash in the modular crypt format (MCF). In contrast to e.g. normal `sha` hash 314 | it includes the used hash function, cost factor, salt and hash itself. This makes it specifically convenient for password storage use. Formally 315 | the [format](http://passlib.readthedocs.io/en/stable/modular_crypt_format.html) is: 316 | 317 | > (...) a standard for encoding password hash strings, which requires hashes have the format `${identifier}${content}`; where `{identifier}` 318 | > is an short alphanumeric string uniquely identifying a particular scheme, and `{content}` is the contents of the scheme, using only the 319 | > characters in the regexp range `[a-zA-Z0-9./]`. 320 | 321 | Analyzing the bcrypt format in detail we get: 322 | 323 | ${identifier}${cost-factor}${16-bytes-salt-radix64}{23-bytes-hash-radix64} 324 | 325 | With bcrypt the version identifier was `$2$`, but unfortunately early implementations [did not define how to handle non-ASCII characters](http://undeadly.org/cgi?action=article&sid=20140224132743), 326 | so to tag the old hashes, a new minor version was introduced which was not compatible with the earlier one: `$2a$`. 327 | This is the default version used by most implementations. There are other minor versions which are only used to tag various non-backwards 328 | compatible bugs in different implementations (namely `$2x$` and `$2y$` used by `crypt_blowfish` (PHP) and `$2b$` by OpenBSD). These are usually 329 | irrelevant for implementations that did not have these bugs, so there is no advantage in setting the version to e.g. `$2y$` apart from making 330 | it compatible with different systems. The actual format is the same as `$2a$`. 331 | 332 | The cost factor is the logarithmic work factor value as defined (4-30) printed as normal ASCII characters `[0-9]`. After that the 16 byte salt 333 | encoded with a base64 dialect follows (22 characters) as well as the actual bcrypt hash (23 bytes / 31 characters encoded with the base64 dialect). 334 | 335 | Here is a full example: 336 | 337 | $2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V. 338 | 339 | Here `$2a$` is the version, the cost factor is `8`, the salt is `cfcvVd2aQ8CMvoMpP2EBfe` and the bcrypt hash is `odLEkkFJ9umNEfPD18.hUF62qqlC/V.`. 340 | 341 | The used encoding is similar to the RFC * base64 encoding schema, but [with different mappings](https://en.wikipedia.org/wiki/Base64#Radix-64_applications_not_compatible_with_Base64) 342 | (`./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz` vs. `ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/`) 343 | only used by OpenBSD. In the code base this encoding is usually referenced as "Radix64" (see `Radix64Encoder`). The usual padding with `=` is 344 | omitted. 345 | 346 | ### Enhancements over jBcrypt 347 | 348 | The core of this implementation is based on the popular jBcrypt. Many things around if have been heavily refactored and various new 349 | features and APIs have been added: 350 | 351 | * Optimized and fixed implementation 352 | * Support of most [version](https://en.wikipedia.org/wiki/Bcrypt#Versioning_history) variations (`$2a$`, `$2b$`, `$2x$`, `$2y$`) with support of custom versions 353 | * Customizable handling for passwords over 72 bytes 354 | * Only uses byte and char arrays which can be wiped after use 355 | * Faster Radix64 implementation 356 | * Allow a cost factor of 31 (jBcrypt only allows up to 30) 357 | * Easily get the raw hash 358 | * Provide your own salt or `SecureRandom` for salt generation 359 | * Clearer and easier API 360 | * Signed Jar and signed commits 361 | * More tests (and probably higher coverage) 362 | 363 | ## Security Relevant Information 364 | 365 | ### OWASP Dependency Check 366 | 367 | This project uses the [OWASP Dependency-Check](https://www.owasp.org/index.php/OWASP_Dependency_Check) which is a utility that identifies project dependencies and checks if there are any known, publicly disclosed, vulnerabilities against a [NIST database](https://nvd.nist.gov/vuln/data-feeds). 368 | The build will fail if any issue is found. 369 | 370 | ### Digital Signatures 371 | 372 | #### Signed Jar 373 | 374 | The provided JARs in the Github release page are signed with my private key: 375 | 376 | CN=Patrick Favre-Bulle, OU=Private, O=PF Github Open Source, L=Vienna, ST=Vienna, C=AT 377 | Validity: Thu Sep 07 16:40:57 SGT 2017 to: Fri Feb 10 16:40:57 SGT 2034 378 | SHA1: 06:DE:F2:C5:F7:BC:0C:11:ED:35:E2:0F:B1:9F:78:99:0F:BE:43:C4 379 | SHA256: 2B:65:33:B0:1C:0D:2A:69:4E:2D:53:8F:29:D5:6C:D6:87:AF:06:42:1F:1A:EE:B3:3C:E0:6D:0B:65:A1:AA:88 380 | 381 | Use the jarsigner tool (found in your `$JAVA_HOME/bin` folder) folder to verify. 382 | 383 | #### Signed Commits 384 | 385 | All tags and commits by me are signed with git with my private key: 386 | 387 | GPG key ID: 4FDF85343912A3AB 388 | Fingerprint: 2FB392FB05158589B767960C4FDF85343912A3AB 389 | 390 | ## Build 391 | 392 | ### Jar Sign 393 | 394 | If you want to jar sign you need to provide a file `keystore.jks` in the 395 | root folder with the correct credentials set in environment variables ( 396 | `OPENSOURCE_PROJECTS_KS_PW` and `OPENSOURCE_PROJECTS_KEY_PW`); alias is 397 | set as `pfopensource`. 398 | 399 | If you want to skip jar signing just change the skip configuration in the 400 | `pom.xml` jar sign plugin to true: 401 | 402 | true 403 | 404 | ### Build with Maven 405 | 406 | Use the Maven wrapper to create a jar including all dependencies 407 | 408 | ./mvnw clean install 409 | 410 | ### Checkstyle Config File 411 | 412 | This project uses my [`common-parent`](https://github.com/patrickfav/mvn-common-parent) which centralized a lot of 413 | the plugin versions aswell as providing the checkstyle config rules. Specifically they are maintained in [`checkstyle-config`](https://github.com/patrickfav/checkstyle-config). Locally the files will be copied after you `mvnw install` into your `target` folder and is called 414 | `target/checkstyle-checker.xml`. So if you use a plugin for your IDE, use this file as your local configuration. 415 | 416 | ## Tech Stack 417 | 418 | * Java 7 Source, JDK 11 required to build (not yet JDK17 compatible) 419 | * Maven 3 420 | 421 | ## Libraries & Credits 422 | 423 | * [jBcrypt](https://github.com/djmdjm/jBCrypt) (derived the "Blowfish Expensive key setup") (under BSD licence) 424 | * Radix64 implementation derived from [Square's Okio Base64](https://github.com/square/okio) (under Apache v2) 425 | * [Bytes](https://github.com/patrickfav/bytes-java) (byte array utility library) (under Apache v2) 426 | 427 | 428 | ### BCrypt Implementations in Java 429 | 430 | * [jBcrypt](https://github.com/djmdjm/jBCrypt) - the below implementations are based on jBcrypt 431 | * [Spring Bcrypt](https://docs.spring.io/spring-security/site/docs/4.2.5.RELEASE/apidocs/org/springframework/security/crypto/bcrypt/BCrypt.html) 432 | * [Apache Ldap](https://directory.apache.org/api/gen-docs/latest/apidocs/org/apache/directory/api/ldap/model/password/BCrypt.html) 433 | * [Tomcat Bcrypt](https://github.com/andreacomo/tomcat-bcrypt) 434 | * [Bouncy Castle](https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java) 435 | 436 | ## Further Reading 437 | 438 | * [The Bcrypt Protocol… is kind of a mess](https://hackernoon.com/the-bcrypt-protocol-is-kind-of-a-mess-4aace5eb31bd) 439 | 440 | ## Related Libraries 441 | 442 | * [Single Step KDF [NIST SP 800-56C] (Java)](https://github.com/patrickfav/singlestep-kdf) 443 | * [HKDF [RFC5869] Two-Step KDF (Java)](https://github.com/patrickfav/hkdf) 444 | 445 | # License 446 | 447 | Copyright 2018 Patrick Favre-Bulle 448 | 449 | Licensed under the Apache License, Version 2.0 (the "License"); 450 | you may not use this file except in compliance with the License. 451 | You may obtain a copy of the License at 452 | 453 | http://www.apache.org/licenses/LICENSE-2.0 454 | 455 | Unless required by applicable law or agreed to in writing, software 456 | distributed under the License is distributed on an "AS IS" BASIS, 457 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 458 | See the License for the specific language governing permissions and 459 | limitations under the License. 460 | -------------------------------------------------------------------------------- /misc/banner.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickfav/bcrypt/d068562a2d700db0981a32b534e3fc046d327c8f/misc/banner.psd -------------------------------------------------------------------------------- /misc/doc/bcrypt-paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickfav/bcrypt/d068562a2d700db0981a32b534e3fc046d327c8f/misc/doc/bcrypt-paper.pdf -------------------------------------------------------------------------------- /misc/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickfav/bcrypt/d068562a2d700db0981a32b534e3fc046d327c8f/misc/icon.png -------------------------------------------------------------------------------- /misc/icon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/patrickfav/bcrypt/d068562a2d700db0981a32b534e3fc046d327c8f/misc/icon.psd -------------------------------------------------------------------------------- /modules/bcrypt-cli/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bcrypt-parent 9 | at.favre.lib 10 | 0.10.2 11 | ../../ 12 | 13 | 14 | bcrypt-cli 15 | jar 16 | 17 | BCrypt CLI Tool 18 | A companion CLI tool for the bcrypt library for java. 19 | 20 | 21 | true 22 | 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-checkstyle-plugin 29 | 30 | 31 | com.github.chrisdchristo 32 | capsule-maven-plugin 33 | 1.5.1 34 | 35 | at.favre.lib.crypto.bcrypt.cli.BcryptTool 36 | -full 37 | fat 38 | 39 | 40 | 41 | build-fat-jar 42 | package 43 | 44 | build 45 | 46 | 47 | 48 | 49 | 50 | net.nicoulaj.maven.plugins 51 | checksum-maven-plugin 52 | 53 | 54 | 55 | 56 | 57 | 58 | at.favre.lib 59 | bcrypt 60 | 0.10.2 61 | 62 | 63 | commons-cli 64 | commons-cli 65 | 1.6.0 66 | 67 | 68 | 69 | junit 70 | junit 71 | test 72 | 73 | 74 | org.apache.ant 75 | ant 76 | 1.10.14 77 | test 78 | 79 | 80 | org.mockito 81 | mockito-all 82 | 1.10.19 83 | test 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/main/java/at/favre/lib/crypto/bcrypt/cli/Arg.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import java.util.Arrays; 4 | import java.util.Objects; 5 | 6 | /** 7 | * The model for the passed arguments 8 | */ 9 | public class Arg { 10 | //CHECKSTYLE:OFF -- I do want a concise class with only public access 11 | public char[] password; 12 | public String checkBcryptHash; 13 | public byte[] salt; 14 | public int costFactor; 15 | 16 | Arg() { 17 | } 18 | 19 | Arg(char[] password, String checkBcryptHash) { 20 | this(password, checkBcryptHash, null, 0); 21 | } 22 | 23 | 24 | Arg(char[] password, byte[] salt, int costFactor) { 25 | this(password, null, salt, costFactor); 26 | } 27 | 28 | Arg(char[] password, String checkBcryptHash, byte[] salt, int costFactor) { 29 | this.password = password; 30 | this.checkBcryptHash = checkBcryptHash; 31 | this.salt = salt; 32 | this.costFactor = costFactor; 33 | } 34 | 35 | @Override 36 | public boolean equals(Object o) { 37 | if (this == o) return true; 38 | if (o == null || getClass() != o.getClass()) return false; 39 | Arg arg = (Arg) o; 40 | return costFactor == arg.costFactor && 41 | Arrays.equals(password, arg.password) && 42 | Objects.equals(checkBcryptHash, arg.checkBcryptHash) && 43 | Arrays.equals(salt, arg.salt); 44 | } 45 | 46 | @Override 47 | public int hashCode() { 48 | 49 | int result = Objects.hash(checkBcryptHash, costFactor); 50 | result = 31 * result + Arrays.hashCode(password); 51 | result = 31 * result + Arrays.hashCode(salt); 52 | return result; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "Arg{" + 58 | "password=" + Arrays.toString(password) + 59 | ", checkBcryptHash='" + checkBcryptHash + '\'' + 60 | ", salt=" + Arrays.toString(salt) + 61 | ", costFactor=" + costFactor + 62 | '}'; 63 | } 64 | 65 | 66 | //CHECKSTYLE:ON 67 | } 68 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/main/java/at/favre/lib/crypto/bcrypt/cli/BcryptTool.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.BCrypt; 5 | 6 | import java.io.PrintStream; 7 | import java.nio.ByteBuffer; 8 | import java.nio.CharBuffer; 9 | import java.nio.charset.Charset; 10 | import java.nio.charset.StandardCharsets; 11 | 12 | /** 13 | * A simple cli tool that can hash and verify bcrypt hashes 14 | */ 15 | public final class BcryptTool { 16 | private BcryptTool() { 17 | } 18 | 19 | /** 20 | * Will exit with 21 | *

22 | * 0 - everything ok (verified) 23 | * 1 - could not verify hash 24 | * 2 - error while parsing cli arguments 25 | * 3 - invalid bcrypt hash while verifying 26 | * 4 - general error 27 | * 28 | * @param args 29 | */ 30 | public static void main(String[] args) { 31 | System.exit(execute(CLIParser.parse(args), System.out, System.err)); 32 | } 33 | 34 | /** 35 | * Execute the given arguments and executes the appropriate actions 36 | * 37 | * @param arguments 38 | * @param stream 39 | * @param errorStream 40 | * @return the exit code of the tool 41 | */ 42 | static int execute(Arg arguments, PrintStream stream, PrintStream errorStream) { 43 | if (arguments == null) { 44 | return 2; 45 | } 46 | 47 | if (arguments.checkBcryptHash != null) { // verify mode 48 | BCrypt.Result result = BCrypt.verifyer().verify(arguments.password, arguments.checkBcryptHash); 49 | if (!result.validFormat) { 50 | System.err.println("Invalid bcrypt format."); 51 | return 3; 52 | } 53 | 54 | if (result.verified) { 55 | stream.println("Hash verified."); 56 | } else { 57 | errorStream.println("Provided hash does not verify against given password."); 58 | return 1; 59 | } 60 | } else { // hash mode 61 | byte[] salt = arguments.salt == null ? Bytes.random(16).array() : arguments.salt; 62 | byte[] hash = BCrypt.withDefaults().hash(arguments.costFactor, salt, charArrayToByteArray(arguments.password, StandardCharsets.UTF_8)); 63 | stream.println(new String(hash, StandardCharsets.UTF_8)); 64 | } 65 | return 0; 66 | } 67 | 68 | private static byte[] charArrayToByteArray(char[] charArray, Charset charset) { 69 | ByteBuffer bb = charset.encode(CharBuffer.wrap(charArray)); 70 | byte[] bytes = new byte[bb.remaining()]; 71 | bb.get(bytes); 72 | return bytes; 73 | } 74 | 75 | static String jarVersion() { 76 | return BcryptTool.class.getPackage().getImplementationVersion(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/main/java/at/favre/lib/crypto/bcrypt/cli/CLIParser.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import org.apache.commons.cli.CommandLine; 5 | import org.apache.commons.cli.CommandLineParser; 6 | import org.apache.commons.cli.DefaultParser; 7 | import org.apache.commons.cli.HelpFormatter; 8 | import org.apache.commons.cli.Option; 9 | import org.apache.commons.cli.OptionGroup; 10 | import org.apache.commons.cli.Options; 11 | 12 | /** 13 | * Parses the command line input and converts it to a structured model ({@link Arg} 14 | */ 15 | public final class CLIParser { 16 | 17 | public static final String ARG_HASH = "b"; 18 | public static final String ARG_CHECK = "c"; 19 | 20 | private CLIParser() { 21 | } 22 | 23 | public static Arg parse(String[] inputArgs) { 24 | Options options = setupOptions(); 25 | CommandLineParser parser = new DefaultParser(); 26 | Arg argument = new Arg(); 27 | 28 | try { 29 | CommandLine commandLine = parser.parse(options, inputArgs); 30 | 31 | if (commandLine.hasOption("h") || commandLine.hasOption("help")) { 32 | printHelp(options); 33 | return null; 34 | } 35 | 36 | if (commandLine.hasOption("v") || commandLine.hasOption("version")) { 37 | System.out.println("Version: " + CLIParser.class.getPackage().getImplementationVersion()); 38 | return null; 39 | } 40 | 41 | if (commandLine.getArgs().length == 0) { 42 | throw new IllegalArgumentException("First parameter must be password (e.g. bcrypt 'mysecretpassword' -" + ARG_HASH + " 12)"); 43 | } 44 | 45 | char[] password = commandLine.getArgs()[0].toCharArray(); 46 | 47 | if (commandLine.hasOption(ARG_HASH)) { 48 | 49 | return handleHash(commandLine, password); 50 | } else if (commandLine.hasOption(ARG_CHECK)) { 51 | return handleCheck(commandLine, password); 52 | } 53 | } catch (Exception e) { 54 | String msg = e.getMessage(); 55 | System.err.println(msg.length() > 80 ? msg.substring(0, 80) + "..." : msg); 56 | 57 | CLIParser.printHelp(options); 58 | 59 | argument = null; 60 | } 61 | 62 | return argument; 63 | } 64 | 65 | private static Arg handleHash(CommandLine commandLine, char[] password) { 66 | String[] hashParams = commandLine.getOptionValues(ARG_HASH); 67 | 68 | if (hashParams == null || hashParams.length == 0) { 69 | throw new IllegalArgumentException("Hash mode expects at least the cost parameter. (e.g. '-" + ARG_HASH + " 12')"); 70 | } 71 | 72 | final int costFactor; 73 | try { 74 | costFactor = Integer.parseInt(hashParams[0]); 75 | } catch (Exception e) { 76 | throw new IllegalArgumentException("First parameter of hash expected to be integer type, was " + hashParams[0]); 77 | } 78 | 79 | byte[] salt = null; 80 | if (hashParams.length > 1) { 81 | try { 82 | salt = Bytes.parseHex(hashParams[1]).array(); 83 | } catch (Exception e) { 84 | throw new IllegalArgumentException("Salt parameter could not be parsed as hex [0-9a-f], was " + hashParams[1]); 85 | } 86 | 87 | if (salt.length != 16) { 88 | throw new IllegalArgumentException("Salt parameter must be exactly 16 bytes (32 characters hex)"); 89 | } 90 | } 91 | return new Arg(password, salt, costFactor); 92 | } 93 | 94 | private static Arg handleCheck(CommandLine commandLine, char[] password) { 95 | String refBcrypt = commandLine.getOptionValue(ARG_CHECK); 96 | 97 | if (refBcrypt == null || refBcrypt.trim().length() != 60) { 98 | throw new IllegalArgumentException("Reference bcrypt hash must be exactly 60 characters, e.g. '$2a$10$6XBbrUraPyfq7nxeaYsR4u.3.ZuGNCy3tOT4reneAI/qoWvP6AX/e' was " + refBcrypt); 99 | } 100 | 101 | return new Arg(password, refBcrypt); 102 | } 103 | 104 | static Options setupOptions() { 105 | Options options = new Options(); 106 | Option optHash = Option.builder(ARG_HASH).longOpt("bhash").argName("cost> <[16-hex-byte-salt]").hasArgs().desc("Use this flag if you want to compute the bcrypt hash. Pass the logarithm cost factor (4-31) and optionally the used salt" + 107 | " as hex encoded byte array (must be exactly 16 bytes/32 characters hex). Example: '--bhash 12 8e270d6129fd45f30a9b3fe44b4a8d9a'").required().build(); 108 | Option optCheck = Option.builder(ARG_CHECK).longOpt("check").argName("bcrypt-hash").hasArg().desc("Use this flag if you want to verify a hash against a given password. Example: '--check $2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'").build(); 109 | 110 | Option help = Option.builder("h").longOpt("help").desc("Prints help docs.").build(); 111 | Option version = Option.builder("v").longOpt("version").desc("Prints current version.").build(); 112 | 113 | OptionGroup mainArgs = new OptionGroup(); 114 | mainArgs.addOption(optCheck).addOption(optHash).addOption(help).addOption(version); 115 | mainArgs.setRequired(true); 116 | 117 | options.addOptionGroup(mainArgs); 118 | return options; 119 | } 120 | 121 | private static void printHelp(Options options) { 122 | HelpFormatter help = new HelpFormatter(); 123 | help.setWidth(110); 124 | help.setLeftPadding(4); 125 | help.printHelp("bcrypt ", "Version: " + BcryptTool.jarVersion(), options, "", true); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/test/java/at/favre/lib/crypto/bcrypt/cli/ArgTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import org.junit.Test; 5 | 6 | import java.util.Random; 7 | 8 | import static junit.framework.TestCase.assertEquals; 9 | import static junit.framework.TestCase.assertNotNull; 10 | 11 | public class ArgTest { 12 | private final byte[] salt = Bytes.random(16, new Random(0)).array(); 13 | private final Arg argCheck = new Arg("asdsad".toCharArray(), "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"); 14 | private final Arg argHash = new Arg("asdsad".toCharArray(), salt, 12); 15 | 16 | @Test 17 | public void equals() { 18 | assertEquals(argCheck, new Arg("asdsad".toCharArray(), "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i")); 19 | assertEquals(argHash, new Arg("asdsad".toCharArray(), salt, 12)); 20 | } 21 | 22 | @Test 23 | public void testHashCode() { 24 | assertEquals(argCheck.hashCode(), new Arg("asdsad".toCharArray(), "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i").hashCode()); 25 | assertEquals(argHash.hashCode(), new Arg("asdsad".toCharArray(), salt, 12).hashCode()); 26 | } 27 | 28 | @Test 29 | public void testToString() { 30 | assertNotNull(argCheck.toString()); 31 | assertNotNull(argHash.toString()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/test/java/at/favre/lib/crypto/bcrypt/cli/BcryptToolTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import org.apache.tools.ant.types.Commandline; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.io.PrintStream; 8 | 9 | import static junit.framework.TestCase.assertEquals; 10 | import static org.mockito.Matchers.any; 11 | import static org.mockito.Mockito.*; 12 | 13 | public class BcryptToolTest { 14 | private PrintStream out; 15 | private PrintStream err; 16 | 17 | @Before 18 | public void setup() { 19 | out = mock(PrintStream.class); 20 | err = mock(PrintStream.class); 21 | } 22 | 23 | @Test 24 | public void testNullArgument() { 25 | assertEquals(2, BcryptTool.execute(null, out, err)); 26 | } 27 | 28 | @Test 29 | public void testExecuteHash() { 30 | int exitCode = BcryptTool.execute(CLIParser.parse(Commandline.translateCommandline("\"mySecretPw\" -b 8 8e270d6129fd45f30a9b3fe44b4a8d9a")), out, err); 31 | verify(out).println("$2a$08$hgaLWQl7PdKIkx9iQyoLkeuIqizWtPErpyC7aDBasi2Pav97wwW9G"); 32 | verify(err, never()).println(any(String.class)); 33 | assertEquals(0, exitCode); 34 | } 35 | 36 | @Test 37 | public void testExecuteCheck() { 38 | int exitCode = BcryptTool.execute(CLIParser.parse(Commandline.translateCommandline("\"mySecretPw\" -c '$2a$08$hgaLWQl7PdKIkx9iQyoLkeuIqizWtPErpyC7aDBasi2Pav97wwW9G'")), out, err); 39 | verify(out).println(any(String.class)); 40 | verify(err, never()).println(any(String.class)); 41 | assertEquals(0, exitCode); 42 | } 43 | 44 | @Test 45 | public void testCheckInvalidFormat() { 46 | assertEquals(3, BcryptTool.execute(CLIParser.parse(Commandline.translateCommandline("\"mySecretPw\" -c '$2$$08$hgaLWQl7PdKIkx9iQyoLkeuIqizWtPErpyC7aDBasi2Pav97wwW9G'")), out, err)); 47 | } 48 | 49 | @Test 50 | public void testCheckDoesNotVerify() { 51 | assertEquals(1, BcryptTool.execute(CLIParser.parse(Commandline.translateCommandline("\"mySecretPw\" -c '$2a$07$hgaLWQl7PdKIkx9iQyoLkeuIqizWtPErpyC7aDBasi2Pav97wwW9G'")), out, err)); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /modules/bcrypt-cli/src/test/java/at/favre/lib/crypto/bcrypt/cli/CLIParserTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.cli; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import org.apache.tools.ant.types.Commandline; 5 | import org.junit.Test; 6 | 7 | import java.util.Random; 8 | 9 | import static junit.framework.TestCase.assertNotNull; 10 | import static org.junit.Assert.assertEquals; 11 | import static org.junit.Assert.assertNull; 12 | 13 | public class CLIParserTest { 14 | private final String defaultPw = "secretPw1234_äöü+~"; 15 | private final String defaultCheckBcrypt = "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"; 16 | 17 | @Test 18 | public void testWithDoubleDigitCostFactor() { 19 | Arg parsedArg = CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 12")); 20 | Arg expectedArg = new Arg(defaultPw.toCharArray(), null, 12); 21 | assertEquals(expectedArg, parsedArg); 22 | } 23 | 24 | @Test 25 | public void testDifferentPasswordsAndCostFactors() { 26 | for (int i = 0; i < 1000; i++) { 27 | int costFactor = new Random().nextInt(24) + 4; 28 | String pw = Bytes.random(new Random().nextInt(30) + 2).encodeBase64(); 29 | Arg parsedArg = CLIParser.parse(asArgArray("'" + pw + "' -" + CLIParser.ARG_HASH + " " + costFactor)); 30 | Arg expectedArg = new Arg(pw.toCharArray(), null, costFactor); 31 | assertEquals(expectedArg, parsedArg); 32 | } 33 | } 34 | 35 | 36 | @Test 37 | public void testWithSingleDigitCostFactor() { 38 | Arg parsedArg = CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 6")); 39 | Arg expectedArg = new Arg(defaultPw.toCharArray(), null, 6); 40 | assertEquals(expectedArg, parsedArg); 41 | } 42 | 43 | @Test 44 | public void testWithSingleDigitCostFactor2() { 45 | Arg parsedArg = CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 04")); 46 | Arg expectedArg = new Arg(defaultPw.toCharArray(), null, 4); 47 | assertEquals(expectedArg, parsedArg); 48 | } 49 | 50 | @Test 51 | public void testWithoutCostFactorShouldReturnNull() { 52 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH))); 53 | } 54 | 55 | @Test 56 | public void testWithDoubleDigitCostFactorAndSalt() { 57 | String salt = "490d9611ab0930a9d9ef87768553366f"; 58 | Arg parsedArg = CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 11 '" + salt + "'")); 59 | Arg expectedArg = new Arg(defaultPw.toCharArray(), Bytes.parseHex(salt).array(), 11); 60 | assertEquals(expectedArg, parsedArg); 61 | } 62 | 63 | @Test 64 | public void testWithSaltWrongLengthReturnNull() { 65 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 4 490d9611ab0930a9d9ef8776855336"))); 66 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 6 490d9611ab0930a9d9ef87768553366fe2"))); 67 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 7 490d9611ab0930a9d9ef87768553366fe"))); 68 | } 69 | 70 | @Test 71 | public void testWith31LengthSalt_shouldParseWillAppendLeadingZero() { 72 | assertNotNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 5 490d9611ab0930a9d9ef87768553366"))); 73 | } 74 | 75 | @Test 76 | public void testWithSaltWrongCharReturnNull() { 77 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 4 490d9611ab0930a9d9ef87768553366x"))); 78 | } 79 | 80 | @Test 81 | public void testWithoutCostFactorButSaltShouldReturnNull() { 82 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + " 490d9611ab0930a9d9ef87768553366f"))); 83 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_HASH + "490d9611ab0930a9d9ef87768553366f 12"))); 84 | } 85 | 86 | @Test 87 | public void testCheckShouldWork() { 88 | Arg parsedArg = CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_CHECK + "'" + defaultCheckBcrypt + "'")); 89 | Arg expectedArg = new Arg(defaultPw.toCharArray(), defaultCheckBcrypt); 90 | assertEquals(expectedArg, parsedArg); 91 | } 92 | 93 | @Test 94 | public void testCheckTooShortBcrypt() { 95 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_CHECK + "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0"))); 96 | } 97 | 98 | @Test 99 | public void testCheckTooLongBcrypt() { 100 | assertNull(CLIParser.parse(asArgArray("'" + defaultPw + "' -" + CLIParser.ARG_CHECK + " " + defaultCheckBcrypt + "A"))); 101 | } 102 | 103 | @Test 104 | public void testHelp() { 105 | Arg parsedArg = CLIParser.parse(asArgArray("--help")); 106 | assertNull(parsedArg); 107 | } 108 | 109 | @Test 110 | public void testVersion() { 111 | Arg parsedArg = CLIParser.parse(asArgArray("--version")); 112 | assertNull(parsedArg); 113 | } 114 | 115 | public static String[] asArgArray(String cmd) { 116 | return Commandline.translateCommandline(cmd); 117 | } 118 | 119 | } 120 | -------------------------------------------------------------------------------- /modules/bcrypt/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bcrypt-parent 9 | at.favre.lib 10 | 0.10.2 11 | ../../ 12 | 13 | 14 | bcrypt 15 | bundle 16 | 17 | BCrypt Password Hashing Function 18 | Bcrypt is a password hashing function designed by Niels Provos and David Mazières, based on the 19 | Blowfish cipher. The core of this implementation is based on jBcrypt, but heavily refactored, modernized and 20 | with a lot of updates and enhancements. 21 | 22 | 23 | 24 | false 25 | 26 | ${session.executionRootDirectory}/keystore.jks 27 | 28 | 29 | 30 | 31 | 32 | org.apache.felix 33 | maven-bundle-plugin 34 | 5.1.9 35 | true 36 | 37 | 38 | org.apache.maven.plugins 39 | maven-source-plugin 40 | 41 | 42 | org.apache.maven.plugins 43 | maven-javadoc-plugin 44 | 45 | 46 | org.apache.maven.plugins 47 | maven-checkstyle-plugin 48 | 49 | 50 | org.owasp 51 | dependency-check-maven 52 | 53 | 54 | net.nicoulaj.maven.plugins 55 | checksum-maven-plugin 56 | 57 | 58 | org.apache.maven.plugins 59 | maven-jarsigner-plugin 60 | 61 | 62 | 63 | 64 | 65 | 66 | at.favre.lib 67 | bytes 68 | 69 | 70 | 71 | 72 | junit 73 | junit 74 | test 75 | 76 | 77 | org.mindrot 78 | jbcrypt 79 | ${project.jbcryptVersion} 80 | test 81 | 82 | 83 | org.bouncycastle 84 | bcprov-jdk15on 85 | ${project.bcVersion} 86 | test 87 | 88 | 89 | com.github.fzakaria 90 | ascii85 91 | 1.2 92 | test 93 | 94 | 95 | org.apache.commons 96 | commons-text 97 | 1.11.0 98 | test 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /modules/bcrypt/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* 2 | -dontusemixedcaseclassnames 3 | -dontobfuscate 4 | -verbose 5 | 6 | -keepattributes *Annotation*,EnclosingMethod, InnerClasses, Exceptions, Signature, SourceFile, LineNumberTable, MethodParameters 7 | -renamesourcefileattribute SourceFile 8 | -optimizationpasses 3 9 | -overloadaggressively 10 | 11 | -keepclasseswithmembernames class * { 12 | native ; 13 | } 14 | 15 | -keepclassmembers enum * { 16 | public static **[] values(); 17 | public static ** valueOf(java.lang.String); 18 | } 19 | 20 | ################################################ 21 | 22 | -dontnote com.sun.** 23 | -dontwarn com.sun.** 24 | 25 | -dontnote sun.** 26 | -dontwarn sun.** 27 | 28 | -dontnote java.** 29 | -dontwarn java.** 30 | 31 | -dontnote javax.** 32 | -dontwarn javax.** 33 | 34 | # keep all public classes in main package 35 | -keep public class at.favre.lib.crypto.bcrypt.** { public *; } 36 | -keep interface at.favre.lib.crypto.bcrypt.** { ; } 37 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptFormatter.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.charset.Charset; 7 | import java.util.Locale; 8 | 9 | /** 10 | * Formats the out hash message of bcrypt. Usually this is the Modular Crypt Format. 11 | * Example 12 | *

13 |  *     $2a$12$US00g/uMhoSBm.HiuieBjeMtoN69SN.GE25fCpldebzkryUyopws6
14 |  * 
15 | *

16 | * Which consists of the version identifier: 17 | * 18 | *

19 |  *     $2a$
20 |  * 
21 | *

22 | * the cost factor: 23 | * 24 | *

25 |  *     12$
26 |  * 
27 | *

28 | * 16 bytes Radix64 encoded UTF-8 bytes of salt: 29 | * 30 | *

31 |  *     uMhoSBm.HiuieBjeMtoN69
32 |  * 
33 | *

34 | * and 23 Radix64 encoded UTF-8 bytes of actual bcrypt hash 35 | * 36 | *

37 |  *     MtoN69SN.GE25fCpldebzkryUyopws6
38 |  * 
39 | *

40 | * The literal $ is a simple separator 41 | *

42 | * see: modular_crypt_format 43 | */ 44 | public interface BCryptFormatter { 45 | 46 | /** 47 | * Create a message for the given raw hash data 48 | * 49 | * @param hashData to create a message from 50 | * @return message as bytes which might be UTF-8 encoded 51 | */ 52 | byte[] createHashMessage(BCrypt.HashData hashData); 53 | 54 | /** 55 | * Default implantation following the Modular Crypt Format 56 | */ 57 | final class Default implements BCryptFormatter { 58 | 59 | private final Radix64Encoder encoder; 60 | private final Charset defaultCharset; 61 | 62 | public Default(Radix64Encoder encoder, Charset defaultCharset) { 63 | this.encoder = encoder; 64 | this.defaultCharset = defaultCharset; 65 | } 66 | 67 | @Override 68 | public byte[] createHashMessage(BCrypt.HashData hashData) { 69 | byte[] saltEncoded = encoder.encode(hashData.rawSalt); 70 | byte[] hashEncoded = encoder.encode(hashData.rawHash); 71 | byte[] costFactorBytes = String.format(Locale.US, "%02d", hashData.cost).getBytes(defaultCharset); 72 | 73 | try { 74 | ByteBuffer byteBuffer = ByteBuffer.allocate(hashData.version.versionIdentifier.length + 75 | costFactorBytes.length + 3 + saltEncoded.length + hashEncoded.length); 76 | byteBuffer.put(BCrypt.SEPARATOR); 77 | byteBuffer.put(hashData.version.versionIdentifier); 78 | byteBuffer.put(BCrypt.SEPARATOR); 79 | byteBuffer.put(costFactorBytes); 80 | byteBuffer.put(BCrypt.SEPARATOR); 81 | byteBuffer.put(saltEncoded); 82 | byteBuffer.put(hashEncoded); 83 | return byteBuffer.array(); 84 | } finally { 85 | Bytes.wrapNullSafe(saltEncoded).mutable().secureWipe(); 86 | Bytes.wrapNullSafe(hashEncoded).mutable().secureWipe(); 87 | Bytes.wrapNullSafe(costFactorBytes).mutable().secureWipe(); 88 | } 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptOpenBSDProtocol.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2006 Damien Miller 2 | // 3 | // Permission to use, copy, modify, and distribute this software for any 4 | // purpose with or without fee is hereby granted, provided that the above 5 | // copyright notice and this permission notice appear in all copies. 6 | // 7 | // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | package at.favre.lib.crypto.bcrypt; 16 | 17 | /** 18 | * The basic protocol for the OpenBSD bcrypt password hashing schema which is based on Blowfish 19 | *

20 | * Blowfish is notable among block ciphers for its expensive key setup phase. It starts off with subkeys in a standard state, then uses this 21 | * state to perform a block encryption using part of the key, and uses the result of that encryption (which is more accurately a hashing) to 22 | * replace some of the subkeys. Then it uses this modified state to encrypt another part of the key, and uses the result to replace more of 23 | * the subkeys. It proceeds in this fashion, using a progressively modified state to hash the key and replace bits of state, until all subkeys 24 | * have been set. 25 | *

26 | * Provos and Mazières took advantage of this, and took it further. They developed a new key setup algorithm for Blowfish, dubbing the resulting 27 | * cipher "Eksblowfish" ("expensive key schedule Blowfish"). The key setup begins with a modified form of the standard Blowfish key setup, in 28 | * which both the salt and password are used to set all subkeys. There are then a number of rounds in which the standard Blowfish keying algorithm 29 | * is applied, using alternatively the salt and the password as the key, each round starting with the subkey state from the previous round. 30 | * In theory, this is no stronger than the standard Blowfish key schedule, but the number of rekeying rounds is configurable; this process can 31 | * therefore be made arbitrarily slow, which helps deter brute-force attacks upon the hash or salt. 32 | *

33 | * The bcrypt algorithm is the result of encrypting the text "OrpheanBeholderScryDoubt" 64 times using Blowfish. In bcrypt the usual Blowfish 34 | * key setup function is replaced with an expensive key setup (EksBlowfishSetup) function. 35 | */ 36 | final class BCryptOpenBSDProtocol { 37 | 38 | // Blowfish parameters 39 | private static final int BLOWFISH_NUM_ROUNDS = 16; 40 | 41 | // Initial contents of key schedule 42 | private static final int[] P_orig = { 43 | 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 44 | 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, 45 | 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 46 | 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 47 | 0x9216d5d9, 0x8979fb1b 48 | }; 49 | private static final int[] S_orig = { 50 | 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 51 | 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, 52 | 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 53 | 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 54 | 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 55 | 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 56 | 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 57 | 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, 58 | 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 59 | 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 60 | 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 61 | 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, 62 | 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 63 | 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, 64 | 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 65 | 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 66 | 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 67 | 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, 68 | 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 69 | 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, 70 | 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 71 | 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 72 | 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 73 | 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, 74 | 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 75 | 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 76 | 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 77 | 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 78 | 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 79 | 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, 80 | 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 81 | 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, 82 | 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 83 | 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 84 | 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 85 | 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 86 | 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 87 | 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, 88 | 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 89 | 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 90 | 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 91 | 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 92 | 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 93 | 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, 94 | 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 95 | 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 96 | 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 97 | 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, 98 | 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 99 | 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, 100 | 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 101 | 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 102 | 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 103 | 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, 104 | 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 105 | 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 106 | 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 107 | 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 108 | 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 109 | 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, 110 | 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 111 | 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, 112 | 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 113 | 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, 114 | 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 115 | 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 116 | 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 117 | 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 118 | 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 119 | 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, 120 | 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 121 | 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 122 | 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 123 | 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 124 | 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 125 | 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 126 | 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 127 | 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, 128 | 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 129 | 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 130 | 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 131 | 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, 132 | 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 133 | 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, 134 | 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 135 | 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 136 | 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 137 | 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, 138 | 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 139 | 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, 140 | 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 141 | 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 142 | 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 143 | 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, 144 | 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 145 | 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 146 | 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 147 | 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 148 | 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 149 | 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, 150 | 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 151 | 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 152 | 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 153 | 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 154 | 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 155 | 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 156 | 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 157 | 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, 158 | 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 159 | 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 160 | 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 161 | 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 162 | 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 163 | 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, 164 | 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 165 | 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 166 | 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 167 | 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, 168 | 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 169 | 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, 170 | 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 171 | 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 172 | 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 173 | 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, 174 | 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 175 | 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 176 | 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 177 | 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, 178 | 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 179 | 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, 180 | 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 181 | 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 182 | 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 183 | 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, 184 | 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 185 | 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 186 | 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 187 | 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 188 | 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 189 | 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, 190 | 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 191 | 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, 192 | 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 193 | 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 194 | 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 195 | 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 196 | 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 197 | 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, 198 | 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 199 | 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 200 | 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 201 | 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, 202 | 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 203 | 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, 204 | 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 205 | 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 206 | 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 207 | 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, 208 | 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 209 | 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, 210 | 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 211 | 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 212 | 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 213 | 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, 214 | 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 215 | 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 216 | 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 217 | 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 218 | 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 219 | 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, 220 | 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 221 | 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, 222 | 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 223 | 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 224 | 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 225 | 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 226 | 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 227 | 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, 228 | 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 229 | 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 230 | 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 231 | 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, 232 | 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 233 | 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, 234 | 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 235 | 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 236 | 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 237 | 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, 238 | 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 239 | 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, 240 | 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 241 | 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, 242 | 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 243 | 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, 244 | 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 245 | 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 246 | 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 247 | 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, 248 | 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 249 | 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, 250 | 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 251 | 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 252 | 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 253 | 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, 254 | 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 255 | 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 256 | 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 257 | 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 258 | 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 259 | 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, 260 | 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 261 | 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, 262 | 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 263 | 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 264 | 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 265 | 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 266 | 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 267 | 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, 268 | 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 269 | 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 270 | 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 271 | 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 272 | 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 273 | 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, 274 | 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 275 | 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 276 | 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 277 | 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, 278 | 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 279 | 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, 280 | 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 281 | 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 282 | 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 283 | 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, 284 | 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 285 | 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 286 | 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 287 | 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 288 | 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 289 | 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, 290 | 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 291 | 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 292 | 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 293 | 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 294 | 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 295 | 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 296 | 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 297 | 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, 298 | 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 299 | 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 300 | 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 301 | 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, 302 | 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 303 | 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, 304 | 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 305 | 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 306 | }; 307 | 308 | // bcrypt IV: "OrpheanBeholderScryDoubt". The C implementation calls 309 | // this "ciphertext", but it is really plaintext or an IV. We keep 310 | // the name to make code comparison easier. 311 | private static final int[] bf_crypt_ciphertext = { 312 | 0x4f727068, 0x65616e42, 0x65686f6c, 313 | 0x64657253, 0x63727944, 0x6f756274 314 | }; 315 | 316 | BCryptOpenBSDProtocol() { 317 | } 318 | 319 | byte[] cryptRaw(long rounds, byte[] salt, byte[] password) { 320 | return cryptRaw(rounds, salt, password, bf_crypt_ciphertext.clone()); 321 | } 322 | 323 | /** 324 | * Perform the central password hashing step in the 325 | * bcrypt scheme 326 | * 327 | * @param rounds the actual rounds, not the binary logarithm 328 | * @param salt the binary salt to hash with the password 329 | * @param password the password to hash 330 | * of rounds of hashing to apply 331 | * @param cdata the plaintext to encrypt 332 | * @return an array containing the binary hashed password 333 | */ 334 | byte[] cryptRaw(long rounds, byte[] salt, byte[] password, int[] cdata) { 335 | if (rounds < 0) { 336 | throw new IllegalArgumentException("rounds must not be negative"); 337 | } 338 | 339 | int clen = cdata.length; 340 | 341 | if (salt.length != BCrypt.SALT_LENGTH) { 342 | throw new IllegalArgumentException("bad salt length"); 343 | } 344 | 345 | int[] P = P_orig.clone(); 346 | int[] S = S_orig.clone(); 347 | 348 | enhancedKeySchedule(P, S, salt, password); 349 | 350 | for (long roundCount = 0; roundCount != rounds; roundCount++) { 351 | key(P, S, password); 352 | key(P, S, salt); 353 | } 354 | 355 | for (int i = 0; i < 64; i++) { 356 | for (int j = 0; j < (clen >> 1); j++) { 357 | encipher(P, S, cdata, j << 1); 358 | } 359 | } 360 | 361 | byte[] ret = new byte[clen * 4]; 362 | for (int i = 0, j = 0; i < clen; i++) { 363 | ret[j++] = (byte) ((cdata[i] >> 24) & 0xff); 364 | ret[j++] = (byte) ((cdata[i] >> 16) & 0xff); 365 | ret[j++] = (byte) ((cdata[i] >> 8) & 0xff); 366 | ret[j++] = (byte) (cdata[i] & 0xff); 367 | } 368 | 369 | return ret; 370 | } 371 | 372 | /** 373 | * Perform the "enhanced key schedule" step described by 374 | * Provos and Mazieres in "A Future-Adaptable Password Scheme" 375 | * bcrypt-paper.ps 376 | * 377 | * 378 | * @param data salt information 379 | * @param key password information 380 | */ 381 | private void enhancedKeySchedule(int[] P, int[] S, byte[] data, byte[] key) { 382 | int i; 383 | int[] koffp = {0}, doffp = {0}; 384 | int[] lr = {0, 0}; 385 | int plen = P.length, slen = S.length; 386 | 387 | for (i = 0; i < plen; i++) { 388 | P[i] = P[i] ^ streamToWord(key, koffp); 389 | } 390 | 391 | //noinspection Duplicates 392 | for (i = 0; i < plen; i += 2) { 393 | lr[0] ^= streamToWord(data, doffp); 394 | lr[1] ^= streamToWord(data, doffp); 395 | encipher(P, S, lr, 0); 396 | P[i] = lr[0]; 397 | P[i + 1] = lr[1]; 398 | } 399 | 400 | //noinspection Duplicates 401 | for (i = 0; i < slen; i += 2) { 402 | lr[0] ^= streamToWord(data, doffp); 403 | lr[1] ^= streamToWord(data, doffp); 404 | encipher(P, S, lr, 0); 405 | S[i] = lr[0]; 406 | S[i + 1] = lr[1]; 407 | } 408 | } 409 | 410 | /** 411 | * Cyclically extract a word of key material 412 | * 413 | * @param data the string to extract the data from 414 | * @param offp a "pointer" (as a one-entry array) to the 415 | * current offset into data 416 | * @return the next word of material from data 417 | */ 418 | private static int streamToWord(byte[] data, int[] offp) { 419 | int i; 420 | int word = 0; 421 | int off = offp[0]; 422 | 423 | for (i = 0; i < 4; i++) { 424 | word = (word << 8) | (data[off] & 0xff); 425 | off = (off + 1) % data.length; 426 | } 427 | 428 | offp[0] = off; 429 | return word; 430 | } 431 | 432 | /** 433 | * Key the Blowfish cipher 434 | * 435 | * @param key an array containing the key 436 | */ 437 | private void key(int[] P, int[] S, byte[] key) { 438 | int i; 439 | int[] koffp = {0}; 440 | int[] lr = {0, 0}; 441 | int plen = P.length, slen = S.length; 442 | 443 | for (i = 0; i < plen; i++) 444 | P[i] = P[i] ^ streamToWord(key, koffp); 445 | 446 | for (i = 0; i < plen; i += 2) { 447 | encipher(P, S, lr, 0); 448 | P[i] = lr[0]; 449 | P[i + 1] = lr[1]; 450 | } 451 | 452 | for (i = 0; i < slen; i += 2) { 453 | encipher(P, S, lr, 0); 454 | S[i] = lr[0]; 455 | S[i + 1] = lr[1]; 456 | } 457 | } 458 | 459 | /** 460 | * Blowfish encipher a single 64-bit block encoded as 461 | * two 32-bit halves 462 | * 463 | * @param lr an array containing the two 32-bit half blocks 464 | * @param off the position in the array of the blocks 465 | */ 466 | private void encipher(int[] P, int[] S, int[] lr, int off) { 467 | int i, n, l = lr[off], r = lr[off + 1]; 468 | 469 | l ^= P[0]; 470 | for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2; ) { 471 | // Feistel substitution on left word 472 | n = S[(l >> 24) & 0xff]; 473 | n += S[0x100 | ((l >> 16) & 0xff)]; 474 | n ^= S[0x200 | ((l >> 8) & 0xff)]; 475 | n += S[0x300 | (l & 0xff)]; 476 | r ^= n ^ P[++i]; 477 | 478 | // Feistel substitution on right word 479 | n = S[(r >> 24) & 0xff]; 480 | n += S[0x100 | ((r >> 16) & 0xff)]; 481 | n ^= S[0x200 | ((r >> 8) & 0xff)]; 482 | n += S[0x300 | (r & 0xff)]; 483 | l ^= n ^ P[++i]; 484 | } 485 | lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1]; 486 | lr[off + 1] = l; 487 | } 488 | } 489 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/BCryptParser.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | 5 | import java.nio.ByteBuffer; 6 | import java.nio.charset.Charset; 7 | 8 | import static at.favre.lib.crypto.bcrypt.BCrypt.SEPARATOR; 9 | 10 | /** 11 | * A simple parser which is able to parse Modular Crypt Format specifically for bcrypt. 12 | *

13 | * It will gather the parts of the format: 14 | *

    15 | *
  • version
  • 16 | *
  • cost factor
  • 17 | *
  • salt (decoded)
  • 18 | *
  • hash (decoded)
  • 19 | *
20 | * 21 | * see: {@link BCryptFormatter} 22 | * see: modular_crypt_format 23 | */ 24 | public interface BCryptParser { 25 | 26 | /** 27 | * Read and parse given bcrypt hash 28 | * 29 | * @param bcryptHash raw UTF-8 encoded byte array of the encoded hash 30 | * @return decoded parts of the bcrypt hash string 31 | * @throws IllegalBCryptFormatException if the format is not correct Modular Crypt Format 32 | */ 33 | BCrypt.HashData parse(byte[] bcryptHash) throws IllegalBCryptFormatException; 34 | 35 | /** 36 | * Default implementation 37 | */ 38 | final class Default implements BCryptParser { 39 | 40 | private final Charset defaultCharset; 41 | private final Radix64Encoder encoder; 42 | 43 | Default(Radix64Encoder encoder, Charset defaultCharset) { 44 | this.defaultCharset = defaultCharset; 45 | this.encoder = encoder; 46 | } 47 | 48 | @Override 49 | public BCrypt.HashData parse(byte[] bcryptHash) throws IllegalBCryptFormatException { 50 | 51 | if (bcryptHash == null || bcryptHash.length == 0) { 52 | throw new IllegalArgumentException("must provide non-null, non-empty hash"); 53 | } 54 | 55 | if (bcryptHash.length < 7) { 56 | throw new IllegalBCryptFormatException("hash prefix meta must be at least 7 bytes long e.g. '$2a$10$'"); 57 | } 58 | 59 | ByteBuffer byteBuffer = ByteBuffer.wrap(bcryptHash); 60 | 61 | if (byteBuffer.get() != SEPARATOR) { 62 | throw new IllegalBCryptFormatException("hash must start with " + Bytes.from(SEPARATOR).encodeUtf8()); 63 | } 64 | 65 | BCrypt.Version usedVersion = null; 66 | for (BCrypt.Version versionToTest : BCrypt.Version.SUPPORTED_VERSIONS) { 67 | for (int i = 0; i < versionToTest.versionIdentifier.length; i++) { 68 | if (byteBuffer.get() != versionToTest.versionIdentifier[i]) { 69 | byteBuffer.position(byteBuffer.position() - (i + 1)); 70 | break; 71 | } 72 | 73 | if (i == versionToTest.versionIdentifier.length - 1) { 74 | usedVersion = versionToTest; 75 | } 76 | } 77 | if (usedVersion != null) break; 78 | } 79 | 80 | if (usedVersion == null) { 81 | throw new IllegalBCryptFormatException("unknown bcrypt version"); 82 | } 83 | 84 | if (byteBuffer.get() != SEPARATOR) { 85 | throw new IllegalBCryptFormatException("expected separator " + Bytes.from(SEPARATOR).encodeUtf8() + " after version identifier and before cost factor"); 86 | } 87 | 88 | byte[] costBytes = new byte[]{byteBuffer.get(), byteBuffer.get()}; 89 | 90 | int parsedCostFactor; 91 | try { 92 | parsedCostFactor = Integer.parseInt(new String(costBytes, defaultCharset)); 93 | } catch (NumberFormatException e) { 94 | throw new IllegalBCryptFormatException("cannot parse cost factor '" + new String(costBytes, defaultCharset) + "'"); 95 | } 96 | 97 | if (byteBuffer.get() != SEPARATOR) { 98 | throw new IllegalBCryptFormatException("expected separator " + Bytes.from(SEPARATOR).encodeUtf8() + " after cost factor"); 99 | } 100 | 101 | if (bcryptHash.length != 7 + 22 + 31) { 102 | throw new IllegalBCryptFormatException("hash expected to be exactly 60 bytes"); 103 | } 104 | 105 | byte[] salt = new byte[22]; 106 | byte[] hash = new byte[31]; 107 | byteBuffer.get(salt); 108 | byteBuffer.get(hash); 109 | 110 | return new BCrypt.HashData(parsedCostFactor, usedVersion, encoder.decode(salt), encoder.decode(hash)); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/IllegalBCryptFormatException.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | /** 4 | * Exception thrown on parsing if an illegal format has been detected. 5 | *

6 | * Heavily used in {@link BCryptParser} 7 | */ 8 | public class IllegalBCryptFormatException extends Exception { 9 | 10 | public IllegalBCryptFormatException(String s) { 11 | super(s); 12 | } 13 | 14 | @Override 15 | public String getMessage() { 16 | return super.getMessage() + " - example of expected hash format: '$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i'" + 17 | " which includes 16 bytes salt and 23 bytes hash value encoded in a base64 flavor"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategies.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import java.util.Objects; 4 | 5 | /** 6 | * Factory for default {@link LongPasswordStrategy} implementations 7 | */ 8 | @SuppressWarnings("WeakerAccess") 9 | public final class LongPasswordStrategies { 10 | private LongPasswordStrategies() { 11 | } 12 | 13 | /** 14 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.TruncateStrategy} 15 | * 16 | * @param version required to get the max allowed pw length 17 | * @return new instance 18 | */ 19 | public static LongPasswordStrategy truncate(BCrypt.Version version) { 20 | return new LongPasswordStrategy.TruncateStrategy(Objects.requireNonNull(version).allowedMaxPwLength); 21 | } 22 | 23 | /** 24 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.Sha512DerivationStrategy} 25 | * 26 | * @param version required to get the max allowed pw length 27 | * @return new instance 28 | */ 29 | public static LongPasswordStrategy hashSha512(BCrypt.Version version) { 30 | return new LongPasswordStrategy.Sha512DerivationStrategy(Objects.requireNonNull(version).allowedMaxPwLength); 31 | } 32 | 33 | /** 34 | * See {@link at.favre.lib.crypto.bcrypt.LongPasswordStrategy.StrictMaxPasswordLengthStrategy} 35 | * 36 | * @param version required to get the max allowed pw length 37 | * @return new instance 38 | */ 39 | public static LongPasswordStrategy strict(BCrypt.Version version) { 40 | return new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(Objects.requireNonNull(version).allowedMaxPwLength); 41 | } 42 | 43 | /** 44 | * See {@link LongPasswordStrategy.PassThroughStrategy} 45 | * 46 | * @return new instance 47 | */ 48 | public static LongPasswordStrategy none() { 49 | return new LongPasswordStrategy.PassThroughStrategy(); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategy.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.bytes.BytesTransformer; 5 | 6 | /** 7 | * Strategy if the password is longer than supported by Bcrypt itself (71 bytes + null terminator with $2a$) 8 | */ 9 | public interface LongPasswordStrategy { 10 | 11 | /** 12 | * Derives (hashes, shortens, etc) the given password to a desired max length. 13 | * 14 | * @param rawPassword to check and derive 15 | * @return the derived (shortened) password, or the same reference if short enough 16 | */ 17 | byte[] derive(byte[] rawPassword); 18 | 19 | /** 20 | * Default base implementation 21 | */ 22 | abstract class BaseLongPasswordStrategy implements LongPasswordStrategy { 23 | final int maxLength; 24 | 25 | private BaseLongPasswordStrategy(int maxLength) { 26 | this.maxLength = maxLength; 27 | } 28 | 29 | abstract byte[] innerDerive(byte[] input); 30 | 31 | @Override 32 | public byte[] derive(byte[] rawPassword) { 33 | if (rawPassword.length >= maxLength) { 34 | return innerDerive(rawPassword); 35 | } 36 | return rawPassword; 37 | } 38 | } 39 | 40 | /** 41 | * This strategy will always throw an exception to force passwords under the max length 42 | */ 43 | final class StrictMaxPasswordLengthStrategy extends BaseLongPasswordStrategy { 44 | StrictMaxPasswordLengthStrategy(int maxLength) { 45 | super(maxLength); 46 | } 47 | 48 | @Override 49 | public byte[] innerDerive(byte[] rawPassword) { 50 | throw new IllegalArgumentException("password must not be longer than " + maxLength + " bytes plus null terminator encoded in utf-8, was " + rawPassword.length); 51 | } 52 | } 53 | 54 | /** 55 | * Will use sha512 to hash given password to generate fixed 64 byte length hash value 56 | */ 57 | final class Sha512DerivationStrategy extends BaseLongPasswordStrategy { 58 | Sha512DerivationStrategy(int maxLength) { 59 | super(maxLength); 60 | } 61 | 62 | @Override 63 | public byte[] innerDerive(byte[] rawPassword) { 64 | return Bytes.wrap(rawPassword).hash("SHA-512").array(); 65 | } 66 | } 67 | 68 | /** 69 | * Truncates the password the max possible length. 70 | *

71 | * NOTE: This is not recommended, only for compatibility with current hashes; uses {@link Sha512DerivationStrategy} 72 | * if you need to support passwords with arbitrary lengths. 73 | */ 74 | final class TruncateStrategy extends BaseLongPasswordStrategy { 75 | 76 | TruncateStrategy(int maxLength) { 77 | super(maxLength); 78 | } 79 | 80 | @Override 81 | public byte[] innerDerive(byte[] rawPassword) { 82 | return Bytes.wrap(rawPassword).resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array(); 83 | } 84 | } 85 | 86 | /** 87 | * A simple strategy that just returns the provided password without changing it. 88 | */ 89 | final class PassThroughStrategy implements LongPasswordStrategy { 90 | @Override 91 | public byte[] derive(byte[] rawPassword) { 92 | return rawPassword; 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /modules/bcrypt/src/main/java/at/favre/lib/crypto/bcrypt/Radix64Encoder.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | /** 4 | * Encoder for the custom Base64 variant of BCrypt (called Radix64 here). It has the same rules as Base64 but uses a 5 | * different mapping table than the various RFCs 6 | *

7 | * According to Wikipedia: 8 | * 9 | *

10 | * Unix stores password hashes computed with crypt in the /etc/passwd file using radix-64 encoding called B64. It uses a 11 | * mostly-alphanumeric set of characters, plus . and /. Its 64-character set is "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". 12 | * Padding is not used. 13 | *
14 | */ 15 | public interface Radix64Encoder { 16 | 17 | 18 | /** 19 | * Encode given raw byte array to a Radix64 style, UTF-8 encoded byte array. 20 | * 21 | * @param rawBytes to encode 22 | * @return UTF-8 encoded string representing radix64 encoded data 23 | */ 24 | byte[] encode(byte[] rawBytes); 25 | 26 | /** 27 | * From a UTF-8 encoded string representing radix64 encoded data as byte array, decodes the raw bytes from it. 28 | * 29 | * @param utf8EncodedRadix64String from a string get it with "m0CrhHm10qJ3lXRY.5zDGO".getBytes(StandardCharsets.UTF8) 30 | * @return the raw bytes encoded by this utf-8 radix4 string 31 | */ 32 | byte[] decode(byte[] utf8EncodedRadix64String); 33 | 34 | /* 35 | * Licensed to the Apache Software Foundation (ASF) under one or more 36 | * contributor license agreements. See the NOTICE file distributed with 37 | * this work for additional information regarding copyright ownership. 38 | * The ASF licenses this file to You under the Apache License, Version 2.0 39 | * (the "License"); you may not use this file except in compliance with 40 | * the License. You may obtain a copy of the License at 41 | * 42 | * http://www.apache.org/licenses/LICENSE-2.0 43 | * 44 | * Unless required by applicable law or agreed to in writing, software 45 | * distributed under the License is distributed on an "AS IS" BASIS, 46 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | * See the License for the specific language governing permissions and 48 | * limitations under the License. 49 | */ 50 | 51 | /** 52 | * A mod of Square's Okio Base64 encoder 53 | *

54 | * Original author: Alexander Y. Kleymenov 55 | * 56 | * @see Okio 57 | */ 58 | class Default implements Radix64Encoder { 59 | private static final byte[] DECODE_TABLE = { 60 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 61 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 54, 55, 56, 57, 63 | 58, 59, 60, 61, 62, 63, -1, -1, -1, -2, -1, -1, -1, 2, 3, 4, 5, 6, 7, 64 | 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 65 | 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 66 | 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53}; 67 | 68 | private static final byte[] MAP = new byte[]{ 69 | '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 70 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 71 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 72 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 73 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', 74 | '6', '7', '8', '9' 75 | }; 76 | 77 | @Override 78 | public byte[] encode(byte[] in) { 79 | return encode(in, MAP); 80 | } 81 | 82 | @Override 83 | public byte[] decode(byte[] in) { 84 | // Ignore trailing '=' padding and whitespace from the input. 85 | int limit = in.length; 86 | for (; limit > 0; limit--) { 87 | byte c = in[limit - 1]; 88 | if (c != '=' && c != '\n' && c != '\r' && c != ' ' && c != '\t') { 89 | break; 90 | } 91 | } 92 | 93 | // If the input includes whitespace, this output array will be longer than necessary. 94 | byte[] out = new byte[(int) (limit * 6L / 8L)]; 95 | int outCount = 0; 96 | int inCount = 0; 97 | 98 | int word = 0; 99 | for (int pos = 0; pos < limit; pos++) { 100 | byte c = in[pos]; 101 | 102 | int bits; 103 | if (c == '.' || c == '/' || (c >= 'A' && c <= 'z') || (c >= '0' && c <= '9')) { 104 | bits = DECODE_TABLE[c]; 105 | } else if (c == '\n' || c == '\r' || c == ' ' || c == '\t') { 106 | continue; 107 | } else { 108 | throw new IllegalArgumentException("invalid character to decode: " + c); 109 | } 110 | 111 | // Append this char's 6 bits to the word. 112 | word = (word << 6) | (byte) bits & 0xff; 113 | 114 | // For every 4 chars of input, we accumulate 24 bits of output. Emit 3 bytes. 115 | inCount++; 116 | if (inCount % 4 == 0) { 117 | out[outCount++] = (byte) (word >> 16); 118 | out[outCount++] = (byte) (word >> 8); 119 | out[outCount++] = (byte) word; 120 | } 121 | } 122 | 123 | int lastWordChars = inCount % 4; 124 | if (lastWordChars == 1) { 125 | // We read 1 char followed by "===". But 6 bits is a truncated byte! Fail. 126 | return new byte[0]; 127 | } else if (lastWordChars == 2) { 128 | // We read 2 chars followed by "==". Emit 1 byte with 8 of those 12 bits. 129 | word = word << 12; 130 | out[outCount++] = (byte) (word >> 16); 131 | } else if (lastWordChars == 3) { 132 | // We read 3 chars, followed by "=". Emit 2 bytes for 16 of those 18 bits. 133 | word = word << 6; 134 | out[outCount++] = (byte) (word >> 16); 135 | out[outCount++] = (byte) (word >> 8); 136 | } 137 | 138 | // If we sized our out array perfectly, we're done. 139 | if (outCount == out.length) return out; 140 | 141 | // Copy the decoded bytes to a new, right-sized array. 142 | byte[] prefix = new byte[outCount]; 143 | System.arraycopy(out, 0, prefix, 0, outCount); 144 | return prefix; 145 | } 146 | 147 | private static byte[] encode(byte[] in, byte[] map) { 148 | int length = 4 * (in.length / 3) + (in.length % 3 == 0 ? 0 : in.length % 3 + 1); 149 | byte[] out = new byte[length]; 150 | int index = 0, end = in.length - in.length % 3; 151 | for (int i = 0; i < end; i += 3) { 152 | out[index++] = map[(in[i] & 0xff) >> 2]; 153 | out[index++] = map[((in[i] & 0x03) << 4) | ((in[i + 1] & 0xff) >> 4)]; 154 | out[index++] = map[((in[i + 1] & 0x0f) << 2) | ((in[i + 2] & 0xff) >> 6)]; 155 | out[index++] = map[(in[i + 2] & 0x3f)]; 156 | } 157 | switch (in.length % 3) { 158 | case 1: 159 | out[index++] = map[(in[end] & 0xff) >> 2]; 160 | out[index] = map[(in[end] & 0x03) << 4]; 161 | break; 162 | case 2: 163 | out[index++] = map[(in[end] & 0xff) >> 2]; 164 | out[index++] = map[((in[end] & 0x03) << 4) | ((in[end + 1] & 0xff) >> 4)]; 165 | out[index] = map[((in[end + 1] & 0x0f) << 2)]; 166 | break; 167 | } 168 | return out; 169 | } 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptFormatterTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.misc.Repeat; 5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | 9 | import java.nio.charset.StandardCharsets; 10 | import java.util.Random; 11 | 12 | import static at.favre.lib.crypto.bcrypt.BcryptTest.UTF_8; 13 | import static org.junit.Assert.assertArrayEquals; 14 | import static org.junit.Assert.assertEquals; 15 | 16 | public class BCryptFormatterTest { 17 | 18 | @Rule 19 | public RepeatRule repeatRule = new RepeatRule(); 20 | private final BCryptFormatter formatter = new BCryptFormatter.Default(new Radix64Encoder.Default(), UTF_8); 21 | private final BCryptParser parser = new BCryptParser.Default(new Radix64Encoder.Default(), UTF_8); 22 | 23 | @Test 24 | @Repeat(25) 25 | public void createRandomMessageAndVerify() throws IllegalBCryptFormatException { 26 | int cost = new Random().nextInt(27) + 4; 27 | byte[] salt = Bytes.random(16).array(); 28 | byte[] hash = Bytes.random(23).array(); 29 | BCrypt.Version version = BCrypt.Version.SUPPORTED_VERSIONS.get(new Random().nextInt(BCrypt.Version.SUPPORTED_VERSIONS.size())); 30 | BCrypt.HashData hashData = new BCrypt.HashData(cost, version, salt, hash); 31 | byte[] bcryptHash = formatter.createHashMessage(hashData); 32 | BCrypt.HashData parsed = parser.parse(bcryptHash); 33 | 34 | assertEquals(hashData, parsed); 35 | } 36 | 37 | @Test 38 | public void testAgainstReferenceHash1() { 39 | testAgainstReferenceHash( 40 | new BCrypt.HashData(6, BCrypt.Version.VERSION_2A, 41 | new byte[]{0x14, 0x4B, 0x3D, 0x69, 0x1A, 0x7B, 0x4E, (byte) 0xCF, 0x39, (byte) 0xCF, 0x73, 0x5C, (byte) 0x7F, (byte) 0xA7, (byte) 0xA7, (byte) 0x9C}, 42 | new byte[]{0x55, 0x7E, (byte) 0x94, (byte) 0xF3, 0x4B, (byte) 0xF2, (byte) 0x86, (byte) 0xE8, 0x71, (byte) 0x9A, 0x26, (byte) 0xBE, (byte) 0x94, (byte) 0xAC, 0x1E, 0x16, (byte) 0xD9, 0x5E, (byte) 0xF9, (byte) 0xF8, 0x19, (byte) 0xDE, (byte) 0xE0}), 43 | "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." 44 | ); 45 | } 46 | 47 | @Test 48 | public void testAgainstReferenceHash2() { 49 | testAgainstReferenceHash( 50 | new BCrypt.HashData(12, BCrypt.Version.VERSION_2Y, 51 | new byte[]{0x17, (byte) 0xA2, 0x3B, (byte) 0x87, (byte) 0x7F, (byte) 0xAA, (byte) 0xF5, (byte) 0xC3, (byte) 0x8E, (byte) 0x87, 0x27, 0x2E, 0x0C, (byte) 0xDF, 0x48, (byte) 0xAF}, 52 | new byte[]{0x49, (byte) 0x8C, 0x11, (byte) 0xE6, (byte) 0xB9, (byte) 0xAD, 0x6E, (byte) 0xD4, 0x02, (byte) 0xA6, (byte) 0xC4, 0x40, 0x76, (byte) 0x88, 0x35, 0x74, (byte) 0xEA, 0x62, 0x01, 0x2C, (byte) 0x8B, 0x06, (byte) 0xB2}), 53 | "$2y$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" 54 | ); 55 | } 56 | 57 | @Test 58 | public void testAgainstReferenceHash3() { 59 | testAgainstReferenceHash( 60 | new BCrypt.HashData(8, BCrypt.Version.VERSION_2B, 61 | new byte[]{0x26, (byte) 0xC6, 0x30, 0x33, (byte) 0xC0, 0x4F, (byte) 0x8B, (byte) 0xCB, (byte) 0xA2, (byte) 0xFE, 0x24, (byte) 0xB5, 0x74, (byte) 0xDB, 0x62, 0x74}, 62 | new byte[]{0x56, 0x70, 0x1B, 0x26, 0x16, 0x4D, (byte) 0x8F, 0x1B, (byte) 0xC1, 0x52, 0x25, (byte) 0xF4, 0x62, 0x34, (byte) 0xAC, (byte) 0x8A, (byte) 0xC7, (byte) 0x9B, (byte) 0xF5, (byte) 0xBC, 0x16, (byte) 0xBF, 0x48}), 63 | "$2b$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" 64 | ); 65 | } 66 | 67 | private void testAgainstReferenceHash(BCrypt.HashData hashData, String refHash) { 68 | byte[] bcryptHash = formatter.createHashMessage(hashData); 69 | assertArrayEquals(refHash.getBytes(StandardCharsets.UTF_8), bcryptHash); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptHighCostTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import org.junit.Rule; 4 | import org.junit.Test; 5 | import org.junit.internal.runners.statements.FailOnTimeout; 6 | import org.junit.rules.Timeout; 7 | import org.junit.runner.Description; 8 | import org.junit.runners.model.Statement; 9 | import org.junit.runners.model.TestTimedOutException; 10 | 11 | import java.util.concurrent.TimeoutException; 12 | 13 | public class BCryptHighCostTest { 14 | 15 | private final char[] password = "1234567890abcdefABCDEF_.,".toCharArray(); 16 | 17 | private static final int MIN_TIMEOUT = 100; 18 | 19 | @Rule 20 | public Timeout timeout = new Timeout(MIN_TIMEOUT) { 21 | public Statement apply(Statement base, Description description) { 22 | return new FailOnTimeout(base, MIN_TIMEOUT) { 23 | @Override 24 | public void evaluate() throws Throwable { 25 | try { 26 | super.evaluate(); 27 | throw new TimeoutException(); 28 | } catch (Exception e) { 29 | } 30 | } 31 | }; 32 | } 33 | }; 34 | 35 | @Test(expected = TestTimedOutException.class) 36 | public void testHashWithMaxCostFactorAndTimeout() { 37 | BCrypt.withDefaults().hash(31, password); 38 | } 39 | 40 | @Test(expected = TestTimedOutException.class) 41 | public void testHashWith30CostFactorAndTimeout() { 42 | BCrypt.withDefaults().hash(30, password); 43 | } 44 | 45 | @Test(expected = TestTimedOutException.class) 46 | public void testHashWith29CostFactorAndTimeout() { 47 | BCrypt.withDefaults().hash(29, password); 48 | } 49 | 50 | @Test(expected = TestTimedOutException.class) 51 | public void testHashWith28CostFactorAndTimeout() { 52 | BCrypt.withDefaults().hash(28, password); 53 | } 54 | 55 | @Test(expected = TestTimedOutException.class) 56 | public void testHashWith27CostFactorAndTimeout() { 57 | BCrypt.withDefaults().hash(27, password); 58 | } 59 | 60 | @Test(expected = TestTimedOutException.class) 61 | public void testHashWith26CostFactorAndTimeout() { 62 | BCrypt.withDefaults().hash(26, password); 63 | } 64 | 65 | @Test(expected = TestTimedOutException.class) 66 | public void testHashWith25CostFactorAndTimeout() { 67 | BCrypt.withDefaults().hash(25, password); 68 | } 69 | 70 | @Test(expected = TestTimedOutException.class) 71 | public void testHashWith24CostFactorAndTimeout() { 72 | BCrypt.withDefaults().hash(24, password); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BCryptParserTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import org.junit.Before; 5 | import org.junit.Test; 6 | 7 | import java.nio.charset.StandardCharsets; 8 | 9 | import static org.junit.Assert.*; 10 | 11 | public class BCryptParserTest { 12 | private BCryptParser parser; 13 | 14 | @Before 15 | public void setUp() { 16 | parser = new BCryptParser.Default(new Radix64Encoder.Default(), StandardCharsets.UTF_8); 17 | } 18 | 19 | @Test 20 | public void parseDifferentCostFactors() throws Exception { 21 | for (int cost = 4; cost < 10; cost++) { 22 | byte[] salt = Bytes.random(16).array(); 23 | byte[] hash = BCrypt.withDefaults().hash(cost, salt, "12345".getBytes()); 24 | 25 | BCrypt.HashData parts = parser.parse(hash); 26 | assertEquals(cost, parts.cost); 27 | assertEquals(BCrypt.Version.VERSION_2A, parts.version); 28 | assertArrayEquals(salt, parts.rawSalt); 29 | assertEquals(23, parts.rawHash.length); 30 | 31 | System.out.println(parts); 32 | } 33 | } 34 | 35 | @Test 36 | public void parseDifferentVersions() throws Exception { 37 | for (BCrypt.Version version : BCrypt.Version.SUPPORTED_VERSIONS) { 38 | byte[] salt = Bytes.random(16).array(); 39 | byte[] hash = BCrypt.with(version).hash(6, salt, "hs61i1oAJhdasdÄÄ".getBytes(StandardCharsets.UTF_8)); 40 | BCrypt.HashData parts = parser.parse(hash); 41 | assertEquals(version, parts.version); 42 | assertEquals(6, parts.cost); 43 | assertArrayEquals(salt, parts.rawSalt); 44 | assertEquals(23, parts.rawHash.length); 45 | 46 | System.out.println(parts); 47 | } 48 | } 49 | 50 | @Test 51 | public void parseDoubleDigitCost() throws Exception { 52 | byte[] salt = Bytes.random(16).array(); 53 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(11, salt, "i27ze8172eaidh asdhsd".getBytes(StandardCharsets.UTF_8)); 54 | BCrypt.HashData parts = parser.parse(hash); 55 | assertEquals(BCrypt.Version.VERSION_2A, parts.version); 56 | assertEquals(11, parts.cost); 57 | assertArrayEquals(salt, parts.rawSalt); 58 | assertEquals(23, parts.rawHash.length); 59 | 60 | System.out.println(parts); 61 | } 62 | 63 | @Test(expected = IllegalBCryptFormatException.class) 64 | public void parseErrorMissingVersion() throws Exception { 65 | parser.parse("$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 66 | } 67 | 68 | @Test(expected = IllegalBCryptFormatException.class) 69 | public void parseErrorMissingLeadingZero() throws Exception { 70 | parser.parse("$2a$6$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 71 | } 72 | 73 | @Test(expected = IllegalBCryptFormatException.class) 74 | public void parseErrorMissingSeparator() throws Exception { 75 | parser.parse("$2a$06If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 76 | } 77 | 78 | @Test(expected = IllegalBCryptFormatException.class) 79 | public void parseErrorMissingSeparator2() throws Exception { 80 | parser.parse("$2a06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 81 | } 82 | 83 | @Test(expected = IllegalBCryptFormatException.class) 84 | public void parseErrorInvalidVersion() throws Exception { 85 | parser.parse("$2$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 86 | } 87 | 88 | @Test(expected = IllegalBCryptFormatException.class) 89 | public void parseErrorInvalidVersion2() throws Exception { 90 | parser.parse("$3a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 91 | } 92 | 93 | @Test(expected = IllegalBCryptFormatException.class) 94 | public void parseErrorInvalidVersion3() throws Exception { 95 | parser.parse("$2l$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i".getBytes()); 96 | } 97 | 98 | @Test(expected = IllegalBCryptFormatException.class) 99 | public void parseErrorMissingSaltAndHas() throws Exception { 100 | parser.parse("$2a$06$".getBytes()); 101 | } 102 | 103 | @Test(expected = IllegalBCryptFormatException.class) 104 | public void parseErrorMissingHash() throws Exception { 105 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu".getBytes()); 106 | } 107 | 108 | @Test(expected = IllegalBCryptFormatException.class) 109 | public void parseErrorMissingChar() throws Exception { 110 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0".getBytes()); 111 | } 112 | 113 | @Test(expected = IllegalBCryptFormatException.class) 114 | public void parseErrorTooLong() throws Exception { 115 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i9".getBytes()); 116 | } 117 | 118 | @Test(expected = IllegalArgumentException.class) 119 | public void parseErrorNullHash() throws Exception { 120 | parser.parse(null); 121 | } 122 | 123 | @Test(expected = IllegalArgumentException.class) 124 | public void parseErrorZeroLengthHash() throws Exception { 125 | parser.parse(new byte[0]); 126 | } 127 | 128 | @Test(expected = IllegalBCryptFormatException.class) 129 | public void parseErrorWayTooShort() throws Exception { 130 | parser.parse("$2a".getBytes()); 131 | } 132 | 133 | @Test 134 | public void parseErrorTooLongGetExceptionMessage() { 135 | try { 136 | parser.parse("$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i9".getBytes()); 137 | fail(); 138 | } catch (IllegalBCryptFormatException e) { 139 | assertNotNull(e.getMessage()); 140 | assertTrue(e.getMessage().length() > 20); 141 | System.out.println(e.getMessage()); 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BcBcryptTestCases.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.misc.Repeat; 5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | 9 | import java.util.Arrays; 10 | import java.util.Date; 11 | import java.util.Random; 12 | 13 | import static at.favre.lib.crypto.bcrypt.BcryptTest.UTF_8; 14 | import static org.junit.Assert.assertArrayEquals; 15 | 16 | /** 17 | * Tests against the Bouncy Castle implementation of BCrypt 18 | *

19 | * See: https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/crypto/generators/BCrypt.java 20 | */ 21 | public class BcBcryptTestCases { 22 | @Rule 23 | public RepeatRule repeatRule = new RepeatRule(); 24 | 25 | // see: https://www.programcreek.com/java-api-examples/?code=ttt43ttt/gwt-crypto/gwt-crypto-master/src/test/java/org/bouncycastle/crypto/test/BCryptTest.java 26 | private static final Object[][] testVectors = { 27 | {"00", "144b3d691a7b4ecf39cf735c7fa7a79c", 6, "557e94f34bf286e8719a26be94ac1e16d95ef9f819dee092"}, 28 | {"00", "26c63033c04f8bcba2fe24b574db6274", 8, "56701b26164d8f1bc15225f46234ac8ac79bf5bc16bf48ba"}, 29 | {"00", "9b7c9d2ada0fd07091c915d1517701d6", 10, "7b2e03106a43c9753821db688b5cc7590b18fdf9ba544632"}, 30 | {"6100", "a3612d8c9a37dac2f99d94da03bd4521", 6, "e6d53831f82060dc08a2e8489ce850ce48fbf976978738f3"}, 31 | {"6100", "7a17b15dfe1c4be10ec6a3ab47818386", 8, "a9f3469a61cbff0a0f1a1445dfe023587f38b2c9c40570e1"}, 32 | {"6100", "9bef4d04e1f8f92f3de57323f8179190", 10, "5169fd39606d630524285147734b4c981def0ee512c3ace1"}, 33 | {"61626300", "2a1f1dc70a3d147956a46febe3016017", 6, "d9a275b493bcbe1024b0ff80d330253cfdca34687d8f69e5"}, 34 | {"61626300", "4ead845a142c9bc79918c8797f470ef5", 8, "8d4131a723bfbbac8a67f2e035cae08cc33b69f37331ea91"}, 35 | {"61626300", "631c554493327c32f9c26d9be7d18e4c", 10, "8cd0b863c3ff0860e31a2b42427974e0283b3af7142969a6"}, 36 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "02d1176d74158ee29cffdac6150cf123", 6, "4d38b523ce9dc6f2f6ff9fb3c2cd71dfe7f96eb4a3baf19f"}, 37 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "715b96caed2ac92c354ed16c1e19e38a", 8, "98bf9ffc1f5be485f959e8b1d526392fbd4ed2d5719f506b"}, 38 | {"6162636465666768696a6b6c6d6e6f707172737475767778797a00", "85727e838f9049397fbec90566ede0df", 10, "cebba53f67bd28af5a44c6707383c231ac4ef244a6f5fb2b"}, 39 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "8512ae0d0fac4ec9a5978f79b6171028", 6, "26f517fe5345ad575ba7dfb8144f01bfdb15f3d47c1e146a"}, 40 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "1ace2de8807df18c79fced54678f388f", 8, "d51d7cdf839b91a25758b80141e42c9f896ae80fd6cd561f"}, 41 | {"7e21402324255e262a28292020202020207e21402324255e262a2829504e4246524400", "36285a6267751b14ba2dc989f6d43126", 10, "db4fab24c1ff41c1e2c966f8b3d6381c76e86f52da9e15a9"}, 42 | {"c2a300", "144b3d691a7b4ecf39cf735c7fa7a79c", 6, "5a6c4fedb23980a7da9217e0442565ac6145b687c7313339"}}; 43 | 44 | @Test 45 | @Repeat(10) 46 | public void testRandomAgainstJBcrypt() { 47 | int cost = new Random().nextInt(3) + 4; 48 | String pw = Bytes.random(8 + new Random().nextInt(24)).encodeBase64(); 49 | byte[] salt = Bytes.random(16).array(); 50 | 51 | //BC will only return the hash without the salt, cost factor and version identifier and does not add a null terminator 52 | byte[] bcryptHashOnly = org.bouncycastle.crypto.generators.BCrypt.generate(Bytes.from(pw).array(), salt, cost); 53 | BCrypt.HashData hash = BCrypt.with(BCrypt.Version.VERSION_BC).hashRaw(cost, salt, pw.getBytes(UTF_8)); 54 | assertArrayEquals(hash.rawHash, bcryptHashOnly); 55 | } 56 | 57 | @Test 58 | public void testBcRefVectors() { 59 | Date start = new Date(); 60 | System.out.println("Bouncy Castle Test Vector Suite ID: " + Bytes.from(Arrays.hashCode(testVectors)).encodeHex() + " [" + testVectors.length + "] (" + start.toString() + ")"); 61 | for (Object[] testVector : testVectors) { 62 | byte[] pw = Bytes.parseHex((String) testVector[0]).array(); 63 | byte[] salt = Bytes.parseHex((String) testVector[1]).array(); 64 | int cost = (int) testVector[2]; 65 | byte[] refHash = Bytes.parseHex((String) testVector[3]).array(); 66 | 67 | BCrypt.HashData hash = BCrypt.with(BCrypt.Version.VERSION_BC).hashRaw(cost, salt, pw); 68 | assertArrayEquals(refHash, hash.rawHash); 69 | } 70 | System.out.println("finished (" + (new Date().getTime() - start.getTime()) + " ms)"); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/BenchmarkTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.crypto.bcrypt.misc.BcryptMicroBenchmark; 4 | import org.junit.Ignore; 5 | import org.junit.Test; 6 | 7 | public class BenchmarkTest { 8 | 9 | @Test 10 | public void quickBenchmark() { 11 | new BcryptMicroBenchmark(1500, new int[]{4, 5, 6, 7}, 0, true).benchmark(); 12 | } 13 | 14 | @Test 15 | @Ignore 16 | public void fullBenchmark() { 17 | new BcryptMicroBenchmark(819200, new int[]{4, 6, 8, 9, 10, 11, 12, 14, 15}, 2, false).benchmark(); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/FavreBcryptReferenceTests.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.misc.BcryptTestEntriesGenerator; 5 | import at.favre.lib.crypto.bcrypt.misc.BcryptTestEntry; 6 | import org.junit.Ignore; 7 | import org.junit.Test; 8 | 9 | import java.util.Arrays; 10 | import java.util.Date; 11 | 12 | /** 13 | * Test Vectors generated for this implementation - use to test other implementations 14 | */ 15 | public class FavreBcryptReferenceTests { 16 | 17 | private BcryptTestEntry[] testEntries = new BcryptTestEntry[]{ 18 | // empty pw - same salt 19 | new BcryptTestEntry("", 4, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$04$zVHmKQtGGQob.b/Nc7l9NO8UlrYcW05FiuCj/SxsFO/ZtiN9.mNzy"), 20 | new BcryptTestEntry("", 5, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$05$zVHmKQtGGQob.b/Nc7l9NOWES.1hkVBgy5IWImh9DOjKNU8atY4Iy"), 21 | new BcryptTestEntry("", 6, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$06$zVHmKQtGGQob.b/Nc7l9NOjOl7l4oz3WSh5fJ6414Uw8IXRAUoiaO"), 22 | new BcryptTestEntry("", 7, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$07$zVHmKQtGGQob.b/Nc7l9NOBsj1dQpBA1HYNGpIETIByoNX9jc.hOi"), 23 | new BcryptTestEntry("", 8, "zVHmKQtGGQob.b/Nc7l9NO", "$2a$08$zVHmKQtGGQob.b/Nc7l9NOiLTUh/9MDpX86/DLyEzyiFjqjBFePgO"), 24 | 25 | // random pw & salt - short pw 26 | new BcryptTestEntry("<.S.2K(Zq'", 4, "VYAclAMpaXY/oqAo9yUpku", "$2a$04$VYAclAMpaXY/oqAo9yUpkuWmoYywaPzyhu56HxXpVltnBIfmO9tgu"), 27 | new BcryptTestEntry("5.rApO%5jA", 5, "kVNDrnYKvbNr5AIcxNzeIu", "$2a$05$kVNDrnYKvbNr5AIcxNzeIuRcyIF5cZk6UrwHGxENbxP5dVv.WQM/G"), 28 | new BcryptTestEntry("oW++kSrQW^", 6, "QLKkRMH9Am6irtPeSKN5sO", "$2a$06$QLKkRMH9Am6irtPeSKN5sObJGr3j47cO6Pdf5JZ0AsJXuze0IbsNm"), 29 | new BcryptTestEntry("ggJ\\KbTnDG", 7, "4H896R09bzjhapgCPS/LYu", "$2a$07$4H896R09bzjhapgCPS/LYuMzAQluVgR5iu/ALF8L8Aln6lzzYXwbq"), 30 | new BcryptTestEntry("49b0:;VkH/", 8, "hfvO2retKrSrx5f2RXikWe", "$2a$08$hfvO2retKrSrx5f2RXikWeFWdtSesPlbj08t/uXxCeZoHRWDz/xFe"), 31 | new BcryptTestEntry(">9N^5jc##'", 9, "XZLvl7rMB3EvM0c1.JHivu", "$2a$09$XZLvl7rMB3EvM0c1.JHivuIDPJWeNJPTVrpjZIEVRYYB/mF6cYgJK"), 32 | new BcryptTestEntry("\\$ch)s4WXp", 10, "aIjpMOLK5qiS9zjhcHR5TO", "$2a$10$aIjpMOLK5qiS9zjhcHR5TOU7v2NFDmcsBmSFDt5EHOgp/jeTF3O/q"), 33 | new BcryptTestEntry("RYoj\\_>2P7", 12, "esIAHiQAJNNBrsr5V13l7.", "$2a$12$esIAHiQAJNNBrsr5V13l7.RFWWJI2BZFtQlkFyiWXjou05GyuREZa"), 34 | 35 | // random pw & salt - long pw 36 | new BcryptTestEntry("^Q&\"]A`%/A(BVGt>QaX0M-#1ghq_+\":Y0CRmY", 5, "YuQvhokOGVnevctykUYpKu", "$2a$05$YuQvhokOGVnevctykUYpKutZD2pWeGGYn3auyLOasguMY3/0BbIyq"), 38 | new BcryptTestEntry("F%uN/j>[GuB7-jB'_Yj!Tnb7Y!u^6)", 6, "5L3vpQ0tG9O7k5gQ8nAHAe", "$2a$06$5L3vpQ0tG9O7k5gQ8nAHAe9xxQiOcOLh8LGcI0PLWhIznsDt.S.C6"), 39 | new BcryptTestEntry("Z>BobP32ub\"Cfe*Q<-q-=tRSjOBh8\\mLNW.", 9, "nArqOfdCsD9kIbVnAixnwe", "$2a$09$nArqOfdCsD9kIbVnAixnwe6s8QvyPYWtQBpEXKir2OJF9/oNBsEFe"), 42 | new BcryptTestEntry("/MH51`!BP&0tj3%YCA;Xk%e3S`o\\EI", 10, "ePiAc.s.yoBi3B6p1iQUCe", "$2a$10$ePiAc.s.yoBi3B6p1iQUCezn3mraLwpVJ5XGelVyYFKyp5FZn/y.u"), 43 | new BcryptTestEntry("ptAP\"mcg6oH.\";c0U2_oll.OKi5?Ui\"^ai#iQH7ZFtNMfs3AROnIncE9\"BNNoEgO[[*Yk8;RQ(#S,;I+aT", 5, "wgkOlGNXIVE2fWkT3gyRoO", "$2a$05$wgkOlGNXIVE2fWkT3gyRoOqWi4gbi1Wv2Q2Jx3xVs3apl1w.Wtj8C"), 63 | new BcryptTestEntry("M.E1=dt<.L0Q&p;94NfGm_Oo23+Kpl@M5?WIAL.[@/:'S)W96G8N^AWb7_smmC]>7#fGoB", 6, "W9zTCl35nEvUukhhFzkKMe", "$2a$06$W9zTCl35nEvUukhhFzkKMekjT9/pj7M0lihRVEZrX3m8/SBNZRX7i"), 64 | 65 | // increasing pw length 66 | 67 | new BcryptTestEntry("a", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.l4WvgHIVg17ZawDIrDM2IjlE64GDNQS"), 68 | new BcryptTestEntry("aa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.AyUxBk.ThHlsLvRTH7IqcG7yVHJ3SXq"), 69 | new BcryptTestEntry("aaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.BxOVac5xPB6XFdRc/ZrzM9FgZkqmvbW"), 70 | new BcryptTestEntry("aaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.Qbr209bpCtfl5hN7UQlG/L4xiD3AKau"), 71 | new BcryptTestEntry("aaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.oWszihPjDZI0ypReKsaDOW1jBl7oOii"), 72 | new BcryptTestEntry("aaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ./k.Xxn9YiqtV/sxh3EHbnOHd0Qsq27K"), 73 | new BcryptTestEntry("aaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.PYJqRFQbgRbIjMd5VNKmdKS4sBVOyDe"), 74 | new BcryptTestEntry("aaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ..VMYfzaw1wP/SGxowpLeGf13fxCCt.q"), 75 | new BcryptTestEntry("aaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.5B0p054nO5WgAD1n04XslDY/bqY9RJi"), 76 | new BcryptTestEntry("aaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.INBTgqm7sdlBJDg.J5mLMSRK25ri04y"), 77 | new BcryptTestEntry("aaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.s3y7CdFD0OR5p6rsZw/eZ.Dla40KLfm"), 78 | new BcryptTestEntry("aaaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.Jx742Djra6Q7PqJWnTAS.85c28g.Siq"), 79 | new BcryptTestEntry("aaaaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.oKMXW3EZcPHcUV0ib5vDBnh9HojXnLu"), 80 | new BcryptTestEntry("aaaaaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.w6nIjWpDPNSH5pZUvLjC1q25ONEQpeS"), 81 | new BcryptTestEntry("aaaaaaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.k1b2/r9A/hxdwKEKurg6OCn4MwMdiGq"), 82 | new BcryptTestEntry("aaaaaaaaaaaaaaaa", 4, "5DCebwootqWMCp59ISrMJ.", "$2a$04$5DCebwootqWMCp59ISrMJ.3prCNHVX1Ws.7Hm2bJxFUnQOX9f7DFa"), 83 | 84 | // unicode chars 85 | new BcryptTestEntry("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 4, "D3qS2aoTVyqM7z8v8crLm.", "$2a$04$D3qS2aoTVyqM7z8v8crLm.3nKt4CzBZJbyFB.ZebmfCvRw7BGs.Xm"), 86 | new BcryptTestEntry("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 5, "VA1FujiOCMPkUHQ8kF7IaO", "$2a$05$VA1FujiOCMPkUHQ8kF7IaOg7NGaNvpxwWzSluQutxEVmbZItRTsAa"), 87 | new BcryptTestEntry("àèìòùÀÈÌÒÙáéíóúýÁÉÍÓÚÝðÐ", 6, "TXiaNrPeBSz5ugiQlehRt.", "$2a$06$TXiaNrPeBSz5ugiQlehRt.gwpeDQnXWteQL4z2FulouBr6G7D9KUi"), 88 | new BcryptTestEntry("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 4, "YTn1Qlvps8e1odqMn6G5x.", "$2a$04$YTn1Qlvps8e1odqMn6G5x.85pqKql6w773EZJAExk7/BatYAI4tyO"), 89 | new BcryptTestEntry("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 5, "C.8k5vJKD2NtfrRI9o17DO", "$2a$05$C.8k5vJKD2NtfrRI9o17DOfIW0XnwItA529vJnh2jzYTb1QdoY0py"), 90 | new BcryptTestEntry("âêîôûÂÊÎÔÛãñõÃÑÕäëïöüÿ", 6, "xqfRPj3RYAgwurrhcA6uRO", "$2a$06$xqfRPj3RYAgwurrhcA6uROtGlXDp/U6/gkoDYHwlubtcVcNft5.vW"), 91 | new BcryptTestEntry("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 4, "y8vGgMmr9EdyxP9rmMKjH.", "$2a$04$y8vGgMmr9EdyxP9rmMKjH.wv2y3r7yRD79gykQtmb3N3zrwjKsyay"), 92 | new BcryptTestEntry("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 5, "iYH4XIKAOOm/xPQs7xKP1u", "$2a$05$iYH4XIKAOOm/xPQs7xKP1upD0cWyMn3Jf0ZWiizXbEkVpS41K1dcO"), 93 | new BcryptTestEntry("ÄËÏÖÜŸåÅæÆœŒßçÇøØ¢¿¡€", 6, "wCOob.D0VV8twafNDB2ape", "$2a$06$wCOob.D0VV8twafNDB2apegiGD5nqF6Y1e6K95q6Y.R8C4QGd265q"), 94 | new BcryptTestEntry("ΔημοσιεύθηκεστηνΕφημερίδατης", 4, "E5SQtS6P4568MDXW7cyUp.", "$2a$04$E5SQtS6P4568MDXW7cyUp.18wfDisKZBxifnPZjAI1d/KTYMfHPYO"), 95 | new BcryptTestEntry("АБбВвГгДдЕеЁёЖжЗзИиЙйКкЛлМмН", 4, "03e26gQFHhQwRNf81/ww9.", "$2a$04$03e26gQFHhQwRNf81/ww9.p1UbrNwxpzWjLuT.zpTLH4t/w5WhAhC"), 96 | new BcryptTestEntry("нОоПпРрСсТтУуФфХхЦцЧчШшЩщЪъЫыЬьЭэЮю", 4, "PHNoJwpXCfe32nUtLv2Upu", "$2a$04$PHNoJwpXCfe32nUtLv2UpuhJXOzd4k7IdFwnEpYwfJVCZ/f/.8Pje"), 97 | new BcryptTestEntry("電电電島岛島兔兔兎龜龟亀國国国區区区", 4, "wU4/0i1TmNl2u.1jIwBX.u", "$2a$04$wU4/0i1TmNl2u.1jIwBX.uZUaOL3Rc5ID7nlQRloQh6q5wwhV/zLW"), 98 | new BcryptTestEntry("诶比伊艾弗豆贝尔维吾艾尺开艾丝维贼德", 4, "P4kreGLhCd26d4WIy7DJXu", "$2a$04$P4kreGLhCd26d4WIy7DJXusPkhxLvBouzV6OXkL5EB0jux0osjsry"), 99 | 100 | // zero byte salts 101 | new BcryptTestEntry("-O_=*N!2JP", 4, "......................", "$2a$04$......................JjuKLOX9OOwo5PceZZXSkaLDvdmgb82"), 102 | new BcryptTestEntry("7B[$Q<4b>U", 5, "......................", "$2a$05$......................DRiedDQZRL3xq5A5FL8y7/6NM8a2Y5W"), 103 | new BcryptTestEntry(">d5-I_8^.h", 6, "......................", "$2a$06$......................5Mq1Ng8jgDY.uHNU4h5p/x6BedzNH2W"), 104 | 105 | // (byte) 1 array salts 106 | new BcryptTestEntry(")V`/UM/]1t", 4, ".OC/.OC/.OC/.OC/.OC/.O", "$2a$04$.OC/.OC/.OC/.OC/.OC/.OQIvKRDAam.Hm5/IaV/.hc7P8gwwIbmi"), 107 | new BcryptTestEntry(":@t2.bWuH]", 5, ".OC/.OC/.OC/.OC/.OC/.O", "$2a$05$.OC/.OC/.OC/.OC/.OC/.ONDbUvdOchUiKmQORX6BlkPofa/QxW9e"), 108 | new BcryptTestEntry("b(#KljF5s\"", 6, ".OC/.OC/.OC/.OC/.OC/.O", "$2a$06$.OC/.OC/.OC/.OC/.OC/.OHfTd9e7svOu34vi1PCvOcAEq07ST7.K"), 109 | 110 | // 0x80 bytes salt 111 | new BcryptTestEntry("@3YaJ^Xs]*", 4, "eGA.eGA.eGA.eGA.eGA.e.", "$2a$04$eGA.eGA.eGA.eGA.eGA.e.stcmvh.R70m.0jbfSFVxlONdj1iws0C"), 112 | new BcryptTestEntry("'\"5\\!k*C(p", 5, "eGA.eGA.eGA.eGA.eGA.e.", "$2a$05$eGA.eGA.eGA.eGA.eGA.e.vR37mVSbfdHwu.F0sNMvgn8oruQRghy"), 113 | new BcryptTestEntry("edEu7C?$'W", 6, "eGA.eGA.eGA.eGA.eGA.e.", "$2a$06$eGA.eGA.eGA.eGA.eGA.e.tSq0FN8MWHQXJXNFnHTPQKtA.n2a..G"), 114 | 115 | // 0xFF bytes salt 116 | new BcryptTestEntry("N7dHmg\\PI^", 4, "999999999999999999999u", "$2a$04$999999999999999999999uCZfA/pLrlyngNDMq89r1uUk.bQ9icOu"), 117 | new BcryptTestEntry("\"eJuHh!)7*", 5, "999999999999999999999u", "$2a$05$999999999999999999999uj8Pfx.ufrJFAoWFLjapYBS5vVEQQ/hK"), 118 | new BcryptTestEntry("ZeDRJ:_tu:", 6, "999999999999999999999u", "$2a$06$999999999999999999999u6RB0P9UmbdbQgjoQFEJsrvrKe.BoU6q"), 119 | }; 120 | 121 | @Test 122 | public void testAgainstReferenceHashes() { 123 | Date start = new Date(); 124 | System.out.println("Favre Test Vector Suite ID: " + Bytes.from(Arrays.hashCode(testEntries)).encodeHex() + " [" + testEntries.length + "] (" + start.toString() + ")"); 125 | BcryptTestEntry.testEntries(testEntries); 126 | System.out.println("finished (" + (new Date().getTime() - start.getTime()) + " ms)"); 127 | } 128 | 129 | @Test 130 | @Ignore 131 | public void printBcryptRefTestData() { 132 | System.out.println("// random pw & salt - short pw"); 133 | new BcryptTestEntriesGenerator(8, new int[]{4, 5, 6, 7, 8, 9, 10, 12}, 1, BCrypt.Version.VERSION_2A, false, false).printRefData(); 134 | System.out.println("// random pw & salt - long pw"); 135 | new BcryptTestEntriesGenerator(24, new int[]{4, 5, 6, 7, 8, 9, 10, 12}, 1, BCrypt.Version.VERSION_2A, false, false).printRefData(); 136 | System.out.println("// same pw & random salt"); 137 | new BcryptTestEntriesGenerator(16, new int[]{4, 5, 6}, 1, BCrypt.Version.VERSION_2A, false, true).printRefData(); 138 | System.out.println("// same pw & salt - increasing cost factor"); 139 | new BcryptTestEntriesGenerator(16, new int[]{4, 5, 6, 7, 8, 9, 10, 12}, 1, BCrypt.Version.VERSION_2A, true, true).printRefData(); 140 | System.out.println("// long pw"); 141 | new BcryptTestEntriesGenerator(56, new int[]{4, 5, 6}, 1, BCrypt.Version.VERSION_2A, false, false).printRefData(); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/JBcryptTestCases.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.misc.BcryptTestEntry; 5 | import at.favre.lib.crypto.bcrypt.misc.Repeat; 6 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.Arrays; 12 | import java.util.Date; 13 | import java.util.Random; 14 | 15 | import static at.favre.lib.crypto.bcrypt.BcryptTest.UTF_8; 16 | import static org.junit.Assert.assertArrayEquals; 17 | 18 | /** 19 | * These are the adapted test cases from the 'original' jBcrypt implementation 20 | *

21 | * See: https://github.com/jeremyh/jBCrypt/blob/master/src/test/java/org/mindrot/TestBCrypt.java 22 | */ 23 | public class JBcryptTestCases { 24 | @Rule 25 | public RepeatRule repeatRule = new RepeatRule(); 26 | 27 | private final BcryptTestEntry[] testEntries = new BcryptTestEntry[]{ 28 | new BcryptTestEntry("abc", 6, "If6bvum7DFjUnE9p2uDeDu", "$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i"), 29 | new BcryptTestEntry("abc", 8, "Ro0CUfOqk6cXEKf3dyaM7O", "$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm"), 30 | new BcryptTestEntry("abc", 10, "WvvTPHKwdBJ3uk0Z37EMR.", "$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi"), 31 | new BcryptTestEntry("abc", 12, "EXRkfkdmXn2gzds2SSitu.", "$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q"), 32 | 33 | new BcryptTestEntry("a", 6, "m0CrhHm10qJ3lXRY.5zDGO", "$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe"), 34 | new BcryptTestEntry("a", 8, "cfcvVd2aQ8CMvoMpP2EBfe", "$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V."), 35 | new BcryptTestEntry("a", 10, "k87L/MF28Q673VKh8/cPi.", "$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u"), 36 | new BcryptTestEntry("a", 12, "8NJH3LsPrANStV6XtBakCe", "$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS"), 37 | 38 | new BcryptTestEntry("", 6, "DCq7YPn5Rq63x1Lad4cll.", "$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s."), 39 | new BcryptTestEntry("", 8, "HqWuK6/Ng6sg9gQzbLrgb.", "$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye"), 40 | new BcryptTestEntry("", 10, "k1wbIrmNyFAPwPVPSVa/ze", "$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW"), 41 | new BcryptTestEntry("", 12, "k42ZFHFWqBp3vWli.nIn8u", "$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO"), 42 | 43 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 6, ".rCVZVOThsIa97pEDOxvGu", "$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC"), 44 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 8, "aTsUwsyowQuzRrDqFflhge", "$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz."), 45 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 10, "fVH8e28OQRj9tqiDXs1e1u", "$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq"), 46 | new BcryptTestEntry("abcdefghijklmnopqrstuvwxyz", 12, "D4G5f18o7aMMfwasBL7Gpu", "$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG"), 47 | 48 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 6, "fPIsBO8qRqkjj273rfaOI.", "$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO"), 49 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 8, "Eq2r4G/76Wv39MzSX262hu", "$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW"), 50 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 10, "LgfYWkbzEvQ4JakH7rOvHe", "$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS"), 51 | new BcryptTestEntry("~!@#$%^&*() ~!@#$%^&*()PNBFRD", 12, "WApznUOJfkEGSmYRfnkrPO", "$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC"), 52 | }; 53 | 54 | @Test 55 | public void testAgainstReferenceHashes() { 56 | Date start = new Date(); 57 | System.out.println("jBcrypt Test Vector Suite ID: " + Bytes.from(Arrays.hashCode(testEntries)).encodeHex() + " [" + testEntries.length + "] (" + start.toString() + ")"); 58 | BcryptTestEntry.testEntries(testEntries); 59 | System.out.println("finished (" + (new Date().getTime() - start.getTime()) + " ms)"); 60 | } 61 | 62 | @Test 63 | @Repeat(8) 64 | public void testRandomAgainstJBcrypt() throws IllegalBCryptFormatException { 65 | int cost = new Random().nextInt(3) + 4; 66 | String pw = Bytes.random(8 + new Random().nextInt(24)).encodeBase64(); 67 | String jbcryptHash = org.mindrot.jbcrypt.BCrypt.hashpw(pw, org.mindrot.jbcrypt.BCrypt.gensalt(cost)); 68 | BCrypt.HashData hashData = new BCryptParser.Default(new Radix64Encoder.Default(), StandardCharsets.UTF_8) 69 | .parse(jbcryptHash.getBytes(UTF_8)); 70 | 71 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(cost, hashData.rawSalt, pw.getBytes(UTF_8)); 72 | 73 | assertArrayEquals(jbcryptHash.getBytes(UTF_8), hash); 74 | } 75 | 76 | @Test 77 | @Repeat(8) 78 | public void testCheckPwAgainstFavreLib() { 79 | int cost = new Random().nextInt(5) + 4; 80 | String pw = "aAöoi. --~!@#$%^&*(kjlöoi" + new Random(999999999); 81 | byte[] hash = BCrypt.with(BCrypt.Version.VERSION_2A).hash(cost, pw.toCharArray()); 82 | org.mindrot.jbcrypt.BCrypt.checkpw(pw, new String(hash, UTF_8)); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/LongPasswordStrategyTest.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.bytes.BytesTransformer; 5 | import org.junit.Test; 6 | 7 | import static org.junit.Assert.*; 8 | 9 | public class LongPasswordStrategyTest { 10 | 11 | private final int maxLength = 72; 12 | private static final BCrypt.Version DEFAULT_VERSION = BCrypt.Version.VERSION_2A; 13 | 14 | @Test 15 | public void testFactory() { 16 | assertNotNull(LongPasswordStrategies.hashSha512(DEFAULT_VERSION).derive(Bytes.random(100).array())); 17 | assertNotNull(LongPasswordStrategies.truncate(DEFAULT_VERSION).derive(Bytes.random(100).array())); 18 | assertNotNull(LongPasswordStrategies.none().derive(Bytes.random(100).array())); 19 | } 20 | 21 | @Test(expected = IllegalArgumentException.class) 22 | public void testFactoryForStrictShouldThrowException() { 23 | LongPasswordStrategies.strict(DEFAULT_VERSION).derive(Bytes.random(100).array()); 24 | } 25 | 26 | @Test 27 | public void testStrictLengthStrategy() { 28 | LongPasswordStrategy strategy = new LongPasswordStrategy.StrictMaxPasswordLengthStrategy(maxLength); 29 | byte[] byteArray; 30 | 31 | for (int i = 1; i < maxLength; i++) { 32 | byteArray = Bytes.random(i).array(); 33 | assertSame(byteArray, strategy.derive(byteArray)); 34 | } 35 | 36 | checkExpectToFail(maxLength, strategy); 37 | 38 | for (int i = 1; i < maxLength; i++) { 39 | checkExpectToFail(maxLength + i, strategy); 40 | } 41 | } 42 | 43 | private void checkExpectToFail(int maxLength, LongPasswordStrategy strategy) { 44 | byte[] byteArray; 45 | try { 46 | byteArray = Bytes.random(maxLength).array(); 47 | assertArrayEquals(byteArray, strategy.derive(byteArray)); 48 | fail(); 49 | } catch (IllegalArgumentException ignored) { 50 | } catch (Exception e) { 51 | fail(); 52 | } 53 | } 54 | 55 | @Test 56 | public void testTruncateStrategy() { 57 | LongPasswordStrategy strategy = new LongPasswordStrategy.TruncateStrategy(maxLength); 58 | byte[] byteArray; 59 | 60 | for (int i = 1; i < maxLength; i++) { 61 | byteArray = Bytes.random(i).array(); 62 | assertSame(byteArray, strategy.derive(byteArray)); 63 | } 64 | 65 | testTooLongTruncate(maxLength, maxLength, strategy); 66 | 67 | for (int i = 1; i < maxLength; i++) { 68 | testTooLongTruncate(maxLength + i, maxLength, strategy); 69 | } 70 | } 71 | 72 | private void testTooLongTruncate(int length, int maxLength, LongPasswordStrategy strategy) { 73 | byte[] byteArray; 74 | byteArray = Bytes.random(length).array(); 75 | byte[] out = strategy.derive(byteArray); 76 | assertEquals(maxLength, out.length); 77 | assertArrayEquals(Bytes.wrap(byteArray).resize(maxLength, BytesTransformer.ResizeTransformer.Mode.RESIZE_KEEP_FROM_ZERO_INDEX).array(), out); 78 | } 79 | 80 | @Test 81 | public void testSha512HashStrategy() { 82 | LongPasswordStrategy strategy = new LongPasswordStrategy.Sha512DerivationStrategy(maxLength); 83 | byte[] byteArray; 84 | 85 | for (int i = 1; i < maxLength; i++) { 86 | byteArray = Bytes.random(i).array(); 87 | assertSame(byteArray, strategy.derive(byteArray)); 88 | } 89 | 90 | for (int i = maxLength; i < maxLength * 2; i++) { 91 | byteArray = Bytes.random(maxLength).array(); 92 | assertArrayEquals(Bytes.wrap(byteArray).hash("SHA-512").array(), strategy.derive(byteArray)); 93 | assertTrue(byteArray.length <= maxLength); 94 | System.out.println(Bytes.wrap(byteArray).encodeHex()); 95 | } 96 | } 97 | 98 | @Test 99 | public void testPassThroughStrategy() { 100 | LongPasswordStrategy strategy = new LongPasswordStrategy.PassThroughStrategy(); 101 | byte[] byteArray; 102 | 103 | for (int i = 1; i < 64; i++) { 104 | byteArray = Bytes.random(i).array(); 105 | assertSame(byteArray, strategy.derive(byteArray)); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/Radix64Test.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.misc.Repeat; 5 | import at.favre.lib.crypto.bcrypt.misc.RepeatRule; 6 | import org.junit.Before; 7 | import org.junit.Ignore; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | import java.nio.charset.StandardCharsets; 12 | import java.util.Arrays; 13 | 14 | import static org.junit.Assert.assertArrayEquals; 15 | 16 | public class Radix64Test { 17 | 18 | @Rule 19 | public RepeatRule repeatRule = new RepeatRule(); 20 | private Radix64Encoder encoder; 21 | 22 | private TestCase[] referenceRadix64Table = new TestCase[]{ 23 | new TestCase("Gu", new byte[]{0x23}), 24 | new TestCase("u2O", new byte[]{(byte) 0xC3, (byte) 0x84}), 25 | new TestCase("9txf", new byte[]{(byte) 0xFE, (byte) 0xFC, (byte) 0xE1}), 26 | new TestCase("hNxTH.", new byte[]{(byte) 0x8C, (byte) 0xFC, (byte) 0xD5, 0x24}), 27 | new TestCase("id8RFNq", new byte[]{(byte) 0x91, (byte) 0xFF, (byte) 0x93, 0x1C, (byte) 0xFB}), 28 | new TestCase("wQVuTsH4", new byte[]{(byte) 0xC9, 0x25, (byte) 0xF0, 0x56, (byte) 0xE2, 0x7A}), 29 | new TestCase("AN1d.KXq4u", new byte[]{0x08, (byte) 0xFD, (byte) 0xDF, 0x00, (byte) 0xC6, 0x6C, (byte) 0xEB}), 30 | new TestCase("h6BXVr084r2", new byte[]{(byte) 0x8F, (byte) 0xC0, (byte) 0xD9, 0x5E, (byte) 0xDD, (byte) 0xBE, (byte) 0xEA, (byte) 0xDE}), 31 | new TestCase("0dT6vifCI8aW", new byte[]{(byte) 0xD9, (byte) 0xF5, 0x7C, (byte) 0xC6, 0x48, 0x44, 0x2B, (byte) 0xE7, 0x18}), 32 | new TestCase("zzfD4GfFebPj6e", new byte[]{(byte) 0xD7, 0x58, 0x45, (byte) 0xE8, (byte) 0x88, 0x47, (byte) 0x81, (byte) 0xD4, 0x65, (byte) 0xF2}), 33 | new TestCase("P09R9uV5cCDV11K", new byte[]{0x47, 0x6F, (byte) 0xD3, (byte) 0xFF, 0x05, (byte) 0xFB, 0x78, 0x41, 0x57, (byte) 0xDF, 0x73}), 34 | new TestCase("D7EUIpVIW0l7qkNM", new byte[]{0x17, (byte) 0xD1, (byte) 0x96, 0x2A, (byte) 0xB5, (byte) 0xCA, 0x63, 0x69, (byte) 0xFD, (byte) 0xB2, 0x63, (byte) 0xCE}), 35 | new TestCase("Vtolm7ldZl8KcHYvcu", new byte[]{0x5E, (byte) 0xFA, (byte) 0xA7, (byte) 0xA3, (byte) 0xD9, (byte) 0xDF, 0x6E, (byte) 0x7F, (byte) 0x8C, 0x78, (byte) 0x96, (byte) 0xB1, 0x7B}), 36 | new TestCase("87MSgioX97qxvzFfoPG", new byte[]{(byte) 0xFB, (byte) 0xD3, (byte) 0x94, (byte) 0x8A, 0x4A, (byte) 0x99, (byte) 0xFF, (byte) 0xDB, 0x33, (byte) 0xC7, 0x51, (byte) 0xE1, (byte) 0xA9, 0x12}), 37 | new TestCase("hv442zAAczwYOFY6SHF5", new byte[]{(byte) 0x8F, 0x1E, (byte) 0xBA, (byte) 0xE3, 0x50, (byte) 0x82, 0x7B, 0x5C, (byte) 0x9A, 0x40, 0x76, (byte) 0xBC, 0x50, (byte) 0x91, (byte) 0xFB}), 38 | new TestCase("5Ao7p22a75KWm6/LcL/uXO", new byte[]{(byte) 0xEC, 0x2A, (byte) 0xBD, (byte) 0xAF, (byte) 0x8E, 0x1C, (byte) 0xF7, (byte) 0xB3, 0x18, (byte) 0xA3, (byte) 0xC0, 0x4D, 0x78, (byte) 0xD0, 0x70, 0x65}), 39 | new TestCase("K7Mk6gzVXVCZoz4br8mjxVe", new byte[]{0x33, (byte) 0xD3, (byte) 0xA6, (byte) 0xF2, 0x2D, 0x57, 0x65, 0x71, 0x1B, (byte) 0xAB, 0x5E, (byte) 0x9D, (byte) 0xB7, (byte) 0xEA, 0x25, (byte) 0xCD, 0x78}), 40 | new TestCase("CJHqewYFU0WqKS3fJ0cgLE3Q", new byte[]{0x10, (byte) 0xB2, 0x6C, (byte) 0x83, 0x26, (byte) 0x87, 0x5B, 0x66, 0x2C, 0x31, 0x4E, 0x61, 0x2F, 0x67, (byte) 0xA2, 0x34, 0x6E, 0x52}), 41 | new TestCase("h0kTxLSkPD4mL1WMyflwMdssje", new byte[]{(byte) 0x8F, 0x69, (byte) 0x95, (byte) 0xCC, (byte) 0xD5, 0x26, 0x44, 0x5E, (byte) 0xA8, 0x37, 0x76, 0x0E, (byte) 0xD2, 0x19, (byte) 0xF2, 0x39, (byte) 0xFB, (byte) 0xAE, (byte) 0x96}), 42 | new TestCase("LuOXwx4u9ZzV5/t36yMTAslb8Wy", new byte[]{0x37, 0x04, 0x19, (byte) 0xCB, 0x3E, (byte) 0xB0, (byte) 0xFD, (byte) 0xBD, 0x57, (byte) 0xEC, 0x1B, (byte) 0xF9, (byte) 0xF3, 0x43, (byte) 0x95, 0x0A, (byte) 0xE9, (byte) 0xDD, (byte) 0xF9, (byte) 0x8D}), 43 | new TestCase("mTt2C3/SMa9uLnnfr6WEy3kL163K", new byte[]{(byte) 0xA1, 0x5B, (byte) 0xF8, 0x13, (byte) 0x90, 0x54, 0x39, (byte) 0xCF, (byte) 0xF0, 0x36, (byte) 0x9A, 0x61, (byte) 0xB7, (byte) 0xC6, 0x06, (byte) 0xD3, (byte) 0x99, (byte) 0x8D, (byte) 0xDF, (byte) 0xCE, 0x4C}), 44 | new TestCase("nnuqg7AQGgp6FBBOj/w5tfPg/xnGtu", new byte[]{(byte) 0xA6, (byte) 0x9C, 0x2C, (byte) 0x8B, (byte) 0xD0, (byte) 0x92, 0x22, 0x2A, (byte) 0xFC, 0x1C, 0x30, (byte) 0xD0, (byte) 0x94, 0x1C, (byte) 0xBB, (byte) 0xBE, 0x14, 0x62, 0x07, 0x3A, 0x48, (byte) 0xBF}), 45 | new TestCase("PFXsTdmWq6za6S5F1B9D28li7Dlwv4S", new byte[]{0x44, 0x76, 0x6E, 0x55, (byte) 0xFA, 0x18, (byte) 0xB3, (byte) 0xCD, 0x5C, (byte) 0xF1, 0x4E, (byte) 0xC7, (byte) 0xDC, 0x3F, (byte) 0xC5, (byte) 0xE3, (byte) 0xE9, (byte) 0xE4, (byte) 0xF4, 0x59, (byte) 0xF2, (byte) 0xC7, (byte) 0xA5}), 46 | new TestCase("LusfR3hiP7bGKst1tlckI6S.Ju0rmCIe", new byte[]{0x37, 0x0B, (byte) 0xA1, 0x4F, (byte) 0x98, (byte) 0xE4, 0x47, (byte) 0xD7, 0x48, 0x32, (byte) 0xEB, (byte) 0xF7, (byte) 0xBE, 0x77, (byte) 0xA6, 0x2B, (byte) 0xC5, 0x00, 0x2F, 0x0D, (byte) 0xAD, (byte) 0xA0, 0x42, (byte) 0xA0}), 47 | new TestCase("ArmQQ/Y7hPv4gii00nXAPe", new byte[]{0x0A, (byte) 0xDA, 0x12, 0x48, 0x16, (byte) 0xBD, (byte) 0x8D, 0x1C, 0x7A, (byte) 0x8A, 0x49, 0x36, (byte) 0xDA, (byte) 0x96, 0x42, 0x46}), 48 | new TestCase("WNwJW0zlHtW2pawQ76HXV.", new byte[]{0x60, (byte) 0xFC, (byte) 0x8B, 0x63, 0x6D, 0x67, 0x26, (byte) 0xF6, 0x38, (byte) 0xAD, (byte) 0xCC, (byte) 0x92, (byte) 0xF7, (byte) 0xC2, 0x59, 0x5C}), 49 | new TestCase("Ihd0Lx0pXQquyPZgTFXd5.", new byte[]{0x2A, 0x37, (byte) 0xF6, 0x37, 0x3D, (byte) 0xAB, 0x65, 0x2B, 0x30, (byte) 0xD1, 0x16, (byte) 0xE2, 0x54, 0x76, 0x5F, (byte) 0xEC}), 50 | new TestCase("sdKbHDooFgr0OOEVXgTKTe", new byte[]{(byte) 0xB9, (byte) 0xF3, 0x1D, 0x24, 0x5A, (byte) 0xAA, 0x1E, 0x2B, 0x76, 0x41, 0x01, (byte) 0x97, 0x66, 0x25, 0x4C, 0x56}), 51 | new TestCase("gA3GRpUX1hmMUUmyF98Gn.", new byte[]{(byte) 0x88, 0x2E, 0x48, 0x4E, (byte) 0xB5, (byte) 0x99, (byte) 0xDE, 0x3A, 0x0E, 0x59, 0x6A, 0x34, 0x1F, (byte) 0xFF, (byte) 0x88, (byte) 0xA4}), 52 | new TestCase("SVYk8TP3/CfBLBi/YGiqle", new byte[]{0x51, 0x76, (byte) 0xA6, (byte) 0xF9, 0x54, 0x79, 0x04, 0x48, 0x43, 0x34, 0x39, 0x01, 0x68, (byte) 0x89, 0x2C, (byte) 0x9E}), 53 | new TestCase("Qk8HfY6w82UI1K8Lf6ZIlO", new byte[]{0x4A, 0x6F, (byte) 0x89, (byte) 0x85, (byte) 0xAF, 0x32, (byte) 0xFB, (byte) 0x85, (byte) 0x8A, (byte) 0xDC, (byte) 0xCF, (byte) 0x8D, (byte) 0x87, (byte) 0xC6, (byte) 0xCA, (byte) 0x9D}), 54 | new TestCase("5ad9sLQa1ecexexrFsntee", new byte[]{(byte) 0xED, (byte) 0xC7, (byte) 0xFF, (byte) 0xB8, (byte) 0xD4, (byte) 0x9C, (byte) 0xDE, 0x07, (byte) 0xA0, (byte) 0xCE, 0x0C, (byte) 0xED, 0x1E, (byte) 0xEA, 0x6F, (byte) 0x82}), 55 | new TestCase("sgGn3fI2PiM1ss2HRnlycu", new byte[]{(byte) 0xBA, 0x22, 0x29, (byte) 0xE6, 0x12, (byte) 0xB8, 0x46, 0x43, (byte) 0xB7, (byte) 0xBA, (byte) 0xEE, 0x09, 0x4E, (byte) 0x99, (byte) 0xF4, 0x7B}), 56 | new TestCase("jsKqqO5SQg.ZDtpq/k5r4O", new byte[]{(byte) 0x96, (byte) 0xE3, 0x2C, (byte) 0xB1, 0x0E, (byte) 0xD4, 0x4A, 0x20, 0x1B, 0x16, (byte) 0xFA, (byte) 0xEC, 0x06, 0x6E, (byte) 0xED, (byte) 0xE9}), 57 | new TestCase("iL4JMZdQBwXVVXKTfFQRpO", new byte[]{(byte) 0x90, (byte) 0xDE, (byte) 0x8B, 0x39, (byte) 0xB7, (byte) 0xD2, 0x0F, 0x26, 0x57, 0x5D, (byte) 0x93, 0x15, (byte) 0x84, 0x74, (byte) 0x93, (byte) 0xAD}), 58 | new TestCase("vmSnTF28TuP2KKBAOPbPNu", new byte[]{(byte) 0xC6, (byte) 0x85, 0x29, 0x54, 0x7E, 0x3E, 0x57, 0x04, 0x78, 0x30, (byte) 0xC0, (byte) 0xC2, 0x41, 0x17, 0x51, 0x3F}), 59 | new TestCase("UwioCLPPqh8EWrHxalX/Qu", new byte[]{0x5B, 0x29, 0x2A, 0x10, (byte) 0xD4, 0x51, (byte) 0xB2, 0x3F, (byte) 0x86, 0x62, (byte) 0xD2, 0x73, 0x72, 0x76, 0x41, 0x4B}), 60 | new TestCase("Ljfcg2dt2q9mPyk4blNd76/aOFGN8ca", new byte[]{0x36, 0x58, 0x5E, (byte) 0x8B, (byte) 0x87, (byte) 0xEF, (byte) 0xE2, (byte) 0xCF, (byte) 0xE8, 0x47, 0x49, (byte) 0xBA, 0x76, 0x73, (byte) 0xDF, (byte) 0xF7, (byte) 0xC0, 0x5C, 0x40, 0x72, 0x0F, (byte) 0xF9, (byte) 0xE7}), 61 | new TestCase("4ydWZwAWzZa9YGSf8oEBTOCGY8uBDcO", new byte[]{(byte) 0xEB, 0x47, (byte) 0xD8, 0x6F, 0x20, (byte) 0x98, (byte) 0xD5, (byte) 0xB7, 0x3F, 0x68, (byte) 0x85, 0x21, (byte) 0xFA, (byte) 0xA1, (byte) 0x83, 0x55, 0x01, 0x08, 0x6B, (byte) 0xEC, 0x03, 0x15, (byte) 0xE4}), 62 | new TestCase("gk3KmMtQnp9Bf3R3z83Qd7WsPcRcPSO", new byte[]{(byte) 0x8A, 0x6E, 0x4C, (byte) 0xA0, (byte) 0xEB, (byte) 0xD2, (byte) 0xA6, (byte) 0xBF, (byte) 0xC3, (byte) 0x87, (byte) 0x94, (byte) 0xF9, (byte) 0xD7, (byte) 0xEE, 0x52, (byte) 0x7F, (byte) 0xD6, 0x2E, 0x45, (byte) 0xE4, (byte) 0xDE, 0x45, 0x44}), 63 | new TestCase("etOoPrjz6LO5t3FfVB5fP2LGrIPzUBC", new byte[]{(byte) 0x82, (byte) 0xF4, 0x2A, 0x46, (byte) 0xD9, 0x75, (byte) 0xF0, (byte) 0xD4, 0x3B, (byte) 0xBF, (byte) 0x91, (byte) 0xE1, 0x5C, 0x3E, (byte) 0xE1, 0x47, (byte) 0x83, 0x48, (byte) 0xB4, (byte) 0xA4, 0x75, 0x58, 0x31}), 64 | new TestCase("nztm8zNUXB.UUj3y5rBkrU/ZqHhYDIS", new byte[]{(byte) 0xA7, 0x5B, (byte) 0xE8, (byte) 0xFB, 0x53, (byte) 0xD6, 0x64, 0x30, 0x16, 0x5A, 0x5E, 0x74, (byte) 0xEE, (byte) 0xD0, (byte) 0xE6, (byte) 0xB5, 0x60, 0x5B, (byte) 0xB0, (byte) 0x98, (byte) 0xDA, 0x14, (byte) 0xA5}), 65 | new TestCase("uxrP8zpLLgWciOUIwobyMvRhEXLiyGS", new byte[]{(byte) 0xC3, 0x3B, 0x51, (byte) 0xFB, 0x5A, (byte) 0xCD, 0x36, 0x26, 0x1E, (byte) 0x91, 0x05, (byte) 0x8A, (byte) 0xCA, (byte) 0xA7, 0x74, 0x3B, 0x14, (byte) 0xE3, 0x19, (byte) 0x93, 0x64, (byte) 0xD0, (byte) 0x85}), 66 | new TestCase("rs4Ic/w8OunqfpYVu4dn4YoWbvyt6ba", new byte[]{(byte) 0xB6, (byte) 0xEE, (byte) 0x8A, 0x78, 0x1C, (byte) 0xBE, 0x43, 0x0A, 0x6C, (byte) 0x86, (byte) 0xB6, (byte) 0x97, (byte) 0xC3, (byte) 0xA7, (byte) 0xE9, (byte) 0xE9, (byte) 0xAA, (byte) 0x98, 0x77, 0x1D, 0x2F, (byte) 0xF1, (byte) 0xD7}), 67 | new TestCase("oVrtbl/4uLejWb.wZDhoH3.IhVzHo2K", new byte[]{(byte) 0xA9, 0x7B, 0x6F, 0x76, 0x70, 0x7A, (byte) 0xC0, (byte) 0xD8, 0x25, 0x61, (byte) 0xD0, 0x32, 0x6C, 0x58, (byte) 0xEA, 0x27, (byte) 0x90, 0x0A, (byte) 0x8D, 0x7D, 0x49, (byte) 0xAB, (byte) 0x83}), 68 | new TestCase("D2iAsq5QhDY3irJSpNuqhiG1MDaJ0C6", new byte[]{0x17, (byte) 0x89, 0x02, (byte) 0xBA, (byte) 0xCE, (byte) 0xD2, (byte) 0x8C, 0x56, (byte) 0xB9, (byte) 0x92, (byte) 0xD2, (byte) 0xD4, (byte) 0xAC, (byte) 0xFC, 0x2C, (byte) 0x8E, 0x42, 0x37, 0x38, 0x57, 0x0B, (byte) 0xD8, 0x4F}), 69 | new TestCase("1y4csFZglKVmKSXsA6K9suWMzoLA66a", new byte[]{(byte) 0xDF, 0x4E, (byte) 0x9E, (byte) 0xB8, 0x76, (byte) 0xE2, (byte) 0x9C, (byte) 0xC5, (byte) 0xE8, 0x31, 0x46, 0x6E, 0x0B, (byte) 0xC3, 0x3F, (byte) 0xBB, 0x06, 0x0E, (byte) 0xD6, (byte) 0xA3, 0x42, (byte) 0xF3, (byte) 0xC7}), 70 | new TestCase("9d96OFn4zlo73U69M4eaq/WXqQKcj8C", new byte[]{(byte) 0xFD, (byte) 0xFF, (byte) 0xFC, 0x40, 0x7A, 0x7A, (byte) 0xD6, 0x7A, (byte) 0xBD, (byte) 0xE5, 0x6F, 0x3F, 0x3B, (byte) 0xA8, 0x1C, (byte) 0xB0, 0x16, 0x19, (byte) 0xB1, 0x23, 0x1E, (byte) 0x97, (byte) 0xE1}), 71 | new TestCase("XP9MELvw..kp0ycxUOlroP7L7Kd0pem", new byte[]{0x65, 0x1F, (byte) 0xCE, 0x18, (byte) 0xDC, 0x72, 0x00, 0x09, (byte) 0xAB, (byte) 0xDB, 0x47, (byte) 0xB3, 0x59, 0x09, (byte) 0xED, (byte) 0xA9, 0x1F, 0x4D, (byte) 0xF4, (byte) 0xC7, (byte) 0xF6, (byte) 0xAE, 0x0A}), 72 | new TestCase("1ynTIxbFvG/5IXCSzNe1VIFlEEgm9cm", new byte[]{(byte) 0xDF, 0x4A, 0x55, 0x2B, 0x37, 0x47, (byte) 0xC4, (byte) 0x80, 0x7B, 0x29, (byte) 0x91, 0x14, (byte) 0xD4, (byte) 0xF8, 0x37, 0x5C, (byte) 0xA1, (byte) 0xE7, 0x18, 0x68, (byte) 0xA8, (byte) 0xFD, (byte) 0xEA}), 73 | new TestCase("p2RefvNV3Sg1D4xqLRVCZxnd1LAq.Tq", new byte[]{(byte) 0xAF, (byte) 0x84, (byte) 0xE0, (byte) 0x87, 0x13, (byte) 0xD7, (byte) 0xE5, 0x48, (byte) 0xB7, 0x17, (byte) 0xAC, (byte) 0xEC, 0x35, 0x35, (byte) 0xC4, 0x6F, 0x3A, 0x5F, (byte) 0xDC, (byte) 0xD0, (byte) 0xAC, 0x01, 0x5B}), 74 | new TestCase("WrcjAiGNm/.ZGAi9Hacv0uPJyFcgplS", new byte[]{0x62, (byte) 0xD7, (byte) 0xA5, 0x0A, 0x42, 0x0F, (byte) 0xA0, 0x10, 0x1B, 0x20, 0x29, 0x3F, 0x25, (byte) 0xC7, (byte) 0xB1, (byte) 0xDB, 0x04, 0x4B, (byte) 0xD0, 0x77, (byte) 0xA2, (byte) 0xAE, 0x75}), 75 | new TestCase("QwEnfhbWteZue4ywQ1O081lIovkxPUC", new byte[]{0x4B, 0x21, (byte) 0xA9, (byte) 0x86, 0x37, 0x58, (byte) 0xBE, 0x06, (byte) 0xF0, (byte) 0x83, (byte) 0xAD, 0x32, 0x4B, 0x74, 0x36, (byte) 0xFB, 0x79, (byte) 0xCA, (byte) 0xAB, 0x19, (byte) 0xB3, 0x45, 0x61}), 76 | new TestCase("zG7NQPAtyuGdrsjiC8v3BAvenhdEdJi", new byte[]{(byte) 0xD4, (byte) 0x8F, 0x4F, 0x49, 0x10, (byte) 0xAF, (byte) 0xD3, 0x02, 0x1F, (byte) 0xB6, (byte) 0xE9, 0x64, 0x13, (byte) 0xEC, 0x79, 0x0C, 0x2C, 0x60, (byte) 0xA6, 0x37, (byte) 0xC6, 0x7C, (byte) 0xB9}), 77 | new TestCase("X5oJYjyuptr0gkCRbE5Kst0JmJ48fLO", new byte[]{0x67, (byte) 0xBA, (byte) 0x8B, 0x6A, 0x5D, 0x30, (byte) 0xAE, (byte) 0xFB, 0x76, (byte) 0x8A, 0x61, 0x13, 0x74, 0x6E, (byte) 0xCC, (byte) 0xBA, (byte) 0xFD, (byte) 0x8B, (byte) 0xA0, (byte) 0xBE, (byte) 0xBE, (byte) 0x84, (byte) 0xD4}), 78 | new TestCase("0W6X11nLm2q.a8Uj2duJn8jiiswNjyG", new byte[]{(byte) 0xD9, (byte) 0x8F, 0x19, (byte) 0xDF, 0x7A, 0x4D, (byte) 0xA3, (byte) 0x8B, 0x00, 0x73, (byte) 0xE5, (byte) 0xA5, (byte) 0xE1, (byte) 0xFC, 0x0B, (byte) 0xA7, (byte) 0xE9, 0x64, (byte) 0x92, (byte) 0xEC, (byte) 0x8F, (byte) 0x97, 0x42}), 79 | new TestCase("JbnQ16saEdRAjVd4pQsyt95cJkV3qRW", new byte[]{0x2D, (byte) 0xDA, 0x52, (byte) 0xDF, (byte) 0xCB, (byte) 0x9C, 0x19, (byte) 0xF4, (byte) 0xC2, (byte) 0x95, 0x77, (byte) 0xFA, (byte) 0xAD, 0x2B, (byte) 0xB4, (byte) 0xBF, (byte) 0xFE, (byte) 0xDE, 0x2E, 0x65, (byte) 0xF9, (byte) 0xB1, 0x36})}; 80 | 81 | @Before 82 | public void setUp() { 83 | encoder = new Radix64Encoder.Default(); 84 | } 85 | 86 | @Test 87 | @Repeat(3) 88 | public void testEncodeDifferentLengths() { 89 | for (int i = 1; i < 128; i++) { 90 | testSingleEncode(i, encoder); 91 | } 92 | } 93 | 94 | @Test 95 | public void testEncode16Bytes() { 96 | for (int i = 0; i < 256; i++) { 97 | testSingleEncode(16, encoder); 98 | } 99 | } 100 | 101 | @Test 102 | public void testEncode23Bytes() { 103 | for (int i = 0; i < 256; i++) { 104 | testSingleEncode(23, encoder); 105 | } 106 | } 107 | 108 | private static void testSingleEncode(int length, Radix64Encoder encoder) { 109 | byte[] rnd = Bytes.random(length).array(); 110 | byte[] encoded = encoder.encode(rnd); 111 | byte[] decoded = encoder.decode(encoded); 112 | 113 | assertArrayEquals(rnd, decoded); 114 | if (length < 1024) { 115 | System.out.println(Bytes.wrap(encoded).encodeUtf8()); 116 | } else { 117 | System.out.println(Bytes.wrap(encoded).toString()); 118 | } 119 | //System.out.println("new EncodeTestCase(\"" + Bytes.wrap(encoded).encodeUtf8() + "\"," + new JavaByteArrayEncoder().encode(rnd) + "),"); 120 | } 121 | 122 | 123 | @Test 124 | public void testEncodeAgainstRefTable() { 125 | for (TestCase encodeTestCase : referenceRadix64Table) { 126 | byte[] encoded = encoder.encode(encodeTestCase.raw); 127 | assertArrayEquals("ref test for '" + encodeTestCase.encoded + "' did not pass - expected " + 128 | Bytes.wrap(encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8)).encodeHex() + " actual " + 129 | Bytes.wrap(encoded).encodeHex(), encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8), encoded); 130 | } 131 | } 132 | 133 | @Test 134 | public void testDecodeAgainstRefTable() { 135 | for (TestCase encodeTestCase : referenceRadix64Table) { 136 | byte[] decoded = encoder.decode(encodeTestCase.encoded.getBytes(StandardCharsets.UTF_8)); 137 | assertArrayEquals(encodeTestCase.raw, decoded); 138 | } 139 | } 140 | 141 | @Test 142 | public void testBigBlob() { 143 | testSingleEncode(1024 * 1024 * 10, encoder); 144 | } 145 | 146 | @Test 147 | public void testEmptyDecode() { 148 | assertArrayEquals(new byte[0], encoder.decode(new byte[0])); 149 | } 150 | 151 | @Test 152 | public void testSingleCharDecode() { 153 | assertArrayEquals(new byte[0], encoder.decode("A".getBytes(StandardCharsets.UTF_8))); 154 | } 155 | 156 | @Test 157 | public void testDecodeEmpytString() { 158 | assertArrayEquals(new byte[0], encoder.decode(new byte[0])); 159 | } 160 | 161 | static final class TestCase { 162 | private final String encoded; 163 | private final byte[] raw; 164 | 165 | TestCase(String encoded, byte[] raw) { 166 | this.encoded = encoded; 167 | this.raw = raw; 168 | } 169 | } 170 | 171 | @Test 172 | @Ignore 173 | public void calculate6bitBaseDecodeTable() { 174 | final char[] toBase64 = { 175 | '.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 176 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 177 | 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 178 | 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 179 | 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', 180 | '6', '7', '8', '9' 181 | }; 182 | final int[] fromBase64 = new int[256]; 183 | Arrays.fill(fromBase64, -1); 184 | for (int i = 0; i < toBase64.length; i++) { 185 | fromBase64[toBase64[i]] = i; 186 | } 187 | fromBase64['='] = -2; 188 | 189 | for (int i = 0; i < fromBase64.length; i++) { 190 | System.out.print(fromBase64[i] + ", "); 191 | } 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/BcryptMicroBenchmark.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.BCrypt; 5 | 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.Arrays; 8 | import java.util.Collections; 9 | import java.util.HashMap; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.Random; 13 | import java.util.TreeMap; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public class BcryptMicroBenchmark { 17 | 18 | private final Random rnd = new Random(); 19 | private Map> map; 20 | private final int roundsFactor; 21 | private final int[] costFactorsTotest; 22 | private final int waitSec; 23 | private final boolean skipWarmup; 24 | 25 | public BcryptMicroBenchmark(int roundsFactor, int[] costFactorsTotest, int waitSec, boolean skipWarmup) { 26 | this.roundsFactor = roundsFactor; 27 | this.costFactorsTotest = costFactorsTotest; 28 | this.waitSec = waitSec; 29 | this.skipWarmup = skipWarmup; 30 | } 31 | 32 | public void benchmark() { 33 | List contender = Arrays.asList(new FavreBcrypt(), new JBcrypt(), new BC()); 34 | prepareMap(contender); 35 | 36 | if (!skipWarmup) { 37 | System.out.println("warmup\n"); 38 | 39 | warmup(contender); 40 | 41 | sleep(waitSec * 2); 42 | } 43 | 44 | for (int cost : costFactorsTotest) { 45 | int currentRounds = calculateRounds(roundsFactor, cost); 46 | System.out.println("\n\nbenchmark with " + currentRounds + " rounds and cost-factor " + cost + "\n"); 47 | 48 | for (AbstractBcrypt abstractBcrypt : contender) { 49 | benchmarkSingle(abstractBcrypt, cost, currentRounds); 50 | sleep(waitSec); 51 | } 52 | } 53 | 54 | System.out.println("\n\nResults:"); 55 | System.out.println("\t" + System.getProperty("os.arch") + ", Java " + System.getProperty("java.version") + " (" + System.getProperty("java.vendor") + "), " + System.getProperty("os.name") + " (" + System.getProperty("os.version") + ")\n\n"); 56 | 57 | StringBuilder sb = new StringBuilder(); 58 | int count = 0; 59 | for (Map.Entry> entry : map.entrySet()) { 60 | 61 | if (count == 0) { 62 | sb.append("| |"); 63 | for (Integer cost : entry.getValue().keySet()) { 64 | sb.append(" cost ").append(String.format("%-2s", cost)).append(" |"); 65 | } 66 | sb.append("\n"); 67 | 68 | for (int i = 0; i < entry.getValue().keySet().size() + 1; i++) { 69 | sb.append("| ------------ "); 70 | } 71 | sb.append("|\n"); 72 | } 73 | 74 | 75 | sb.append("| ").append(String.format("%-12s", entry.getKey().getClass().getSimpleName())).append(" |"); 76 | for (Map.Entry iEntry : entry.getValue().entrySet()) { 77 | sb.append(String.format(" %-8s", Math.round(((double) iEntry.getValue() / (double) calculateRounds(roundsFactor, iEntry.getKey())) * 100.0) / 100.0)).append(" ms |"); 78 | } 79 | sb.append("\n"); 80 | count++; 81 | } 82 | 83 | System.out.println(sb.toString()); 84 | } 85 | 86 | private int calculateRounds(int rounds, int cost) { 87 | return Math.max(4, Math.min(250, rounds / (1 << cost))); 88 | } 89 | 90 | private void prepareMap(List contender) { 91 | map = new HashMap<>(); 92 | for (AbstractBcrypt abstractBcrypt : contender) { 93 | map.put(abstractBcrypt, new TreeMap()); 94 | } 95 | } 96 | 97 | private void sleep(int seconds) { 98 | try { 99 | Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); 100 | } catch (InterruptedException e) { 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | private void warmup(List contender) { 106 | byte[][] cache = new byte[25][]; 107 | for (int i = 0; i < 100; i++) { 108 | Collections.shuffle(contender); 109 | for (AbstractBcrypt abstractBcrypt : contender) { 110 | byte[] out = abstractBcrypt.bcrypt(rnd.nextInt(3) + 4, Bytes.random(20).array()); 111 | cache[rnd.nextInt(cache.length)] = out; 112 | } 113 | } 114 | 115 | for (byte[] bytes : cache) { 116 | System.out.print(Bytes.wrapNullSafe(bytes).encodeBase64()); 117 | } 118 | } 119 | 120 | private void benchmarkSingle(AbstractBcrypt contender, int cost, int rounds) { 121 | byte[][] cache = new byte[10][]; 122 | 123 | long start = System.currentTimeMillis(); 124 | for (int i = 0; i < rounds; i++) { 125 | byte[] out = contender.bcrypt(cost, Bytes.random(20).array()); 126 | cache[rnd.nextInt(cache.length)] = out; 127 | } 128 | 129 | map.get(contender).put(cost, System.currentTimeMillis() - start); 130 | 131 | for (byte[] bytes : cache) { 132 | System.out.print(Bytes.wrapNullSafe(bytes).encodeBase64()); 133 | } 134 | } 135 | 136 | 137 | static final class FavreBcrypt implements AbstractBcrypt { 138 | @Override 139 | public byte[] bcrypt(int cost, byte[] password) { 140 | return BCrypt.withDefaults().hash(cost, password); 141 | } 142 | } 143 | 144 | static final class JBcrypt implements AbstractBcrypt { 145 | @Override 146 | public byte[] bcrypt(int cost, byte[] password) { 147 | return org.mindrot.jbcrypt.BCrypt.hashpw(new String(password, StandardCharsets.UTF_8), org.mindrot.jbcrypt.BCrypt.gensalt(cost)).getBytes(StandardCharsets.UTF_8); 148 | } 149 | } 150 | 151 | static final class BC implements AbstractBcrypt { 152 | @Override 153 | public byte[] bcrypt(int cost, byte[] password) { 154 | return org.bouncycastle.crypto.generators.BCrypt.generate(Bytes.from(password).append((byte) 0).array(), Bytes.random(16).array(), cost); 155 | } 156 | } 157 | 158 | interface AbstractBcrypt { 159 | byte[] bcrypt(int cost, byte[] password); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/BcryptTestEntriesGenerator.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.BCrypt; 5 | import at.favre.lib.crypto.bcrypt.Radix64Encoder; 6 | import org.apache.commons.text.StringEscapeUtils; 7 | 8 | import java.nio.charset.StandardCharsets; 9 | 10 | public class BcryptTestEntriesGenerator { 11 | 12 | private final int pwLengthByte; 13 | private final int[] costFactors; 14 | private final int examplesPerCostFactor; 15 | private final BCrypt.Version version; 16 | private final boolean sameSaltAllExamples; 17 | private final boolean samePasswordAllExamples; 18 | 19 | public BcryptTestEntriesGenerator(int pwLengthByte, int[] costFactors, int examplesPerCostFactor, BCrypt.Version version, boolean sameSaltAllExamples, boolean samePasswordAllExamples) { 20 | this.pwLengthByte = pwLengthByte; 21 | this.costFactors = costFactors; 22 | this.examplesPerCostFactor = examplesPerCostFactor; 23 | this.version = version; 24 | this.sameSaltAllExamples = sameSaltAllExamples; 25 | this.samePasswordAllExamples = samePasswordAllExamples; 26 | } 27 | 28 | public void printRefData() { 29 | StringBuilder sb = new StringBuilder("new BcryptTestEntry[] {\n"); 30 | byte[] salt = generateSalt(); 31 | String pw = generatePw(); 32 | 33 | Radix64Encoder encoder = new Radix64Encoder.Default(); 34 | for (int costFactor : costFactors) { 35 | for (int i = 0; i < examplesPerCostFactor; i++) { 36 | if (!sameSaltAllExamples) { 37 | salt = generateSalt(); 38 | } 39 | if (!samePasswordAllExamples) { 40 | pw = generatePw(); 41 | } 42 | BCrypt.HashData data = BCrypt.with(version).hashRaw(costFactor, salt, Bytes.from(pw).array()); 43 | 44 | sb.append("new BcryptTestEntry(\"") 45 | .append(StringEscapeUtils.escapeJava(pw)) 46 | .append("\", ") 47 | .append(costFactor).append(", ") 48 | .append("\"") 49 | .append(new String(encoder.encode(salt), StandardCharsets.UTF_8)).append("\", \"") 50 | .append(new String(version.formatter.createHashMessage(data), StandardCharsets.UTF_8)).append("\"), \n"); 51 | } 52 | } 53 | sb.append("}"); 54 | 55 | System.out.println(sb.toString()); 56 | } 57 | 58 | private String generatePw() { 59 | return "诶比伊艾弗豆贝尔维吾艾尺开艾丝维贼德"; 60 | }// 61 | 62 | private byte[] generateSalt() { 63 | return Bytes.random(16).array(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/BcryptTestEntry.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import at.favre.lib.crypto.bcrypt.BCrypt; 4 | import at.favre.lib.crypto.bcrypt.Radix64Encoder; 5 | 6 | import java.nio.charset.StandardCharsets; 7 | import java.util.Objects; 8 | 9 | import static org.junit.Assert.assertArrayEquals; 10 | 11 | public final class BcryptTestEntry { 12 | final String plainPw; 13 | final int cost; 14 | final String radix64Salt; 15 | final String hash; 16 | 17 | public BcryptTestEntry(String plainPw, int cost, String radix64Salt, String hash) { 18 | this.plainPw = plainPw; 19 | this.cost = cost; 20 | this.radix64Salt = radix64Salt; 21 | this.hash = hash; 22 | } 23 | 24 | public static void testEntries(BcryptTestEntry[] entries) { 25 | for (BcryptTestEntry testEntry : entries) { 26 | byte[] hashed = BCrypt.withDefaults().hash( 27 | testEntry.cost, 28 | new Radix64Encoder.Default().decode(testEntry.radix64Salt.getBytes(StandardCharsets.UTF_8)), 29 | testEntry.plainPw.getBytes(StandardCharsets.UTF_8)); 30 | 31 | assertArrayEquals( 32 | "hash does not match: \n\r" + testEntry.hash + " was \n\r" + new String(hashed, StandardCharsets.UTF_8), 33 | testEntry.hash.getBytes(StandardCharsets.UTF_8), hashed); 34 | } 35 | } 36 | 37 | @Override 38 | public boolean equals(Object o) { 39 | if (this == o) return true; 40 | if (o == null || getClass() != o.getClass()) return false; 41 | BcryptTestEntry that = (BcryptTestEntry) o; 42 | return cost == that.cost && 43 | Objects.equals(plainPw, that.plainPw) && 44 | Objects.equals(radix64Salt, that.radix64Salt) && 45 | Objects.equals(hash, that.hash); 46 | } 47 | 48 | @Override 49 | public int hashCode() { 50 | 51 | return Objects.hash(plainPw, cost, radix64Salt, hash); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/Radix64RefDataCreator.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import at.favre.lib.bytes.Bytes; 4 | import at.favre.lib.crypto.bcrypt.Radix64Encoder; 5 | 6 | @SuppressWarnings("unused") 7 | public class Radix64RefDataCreator { 8 | private final Radix64Encoder encoder; 9 | private final int byteLength; 10 | 11 | public Radix64RefDataCreator(Radix64Encoder encoder, int byteLength) { 12 | this.encoder = encoder; 13 | this.byteLength = byteLength; 14 | } 15 | 16 | public void printRefData() { 17 | for (int i = 0; i < 256; i++) { 18 | testSingleEncode(byteLength); 19 | } 20 | } 21 | 22 | private void testSingleEncode(int length) { 23 | byte[] rnd = Bytes.random(length).array(); 24 | byte[] encoded = encoder.encode(rnd); 25 | byte[] decoded = encoder.decode(encoded); 26 | 27 | if (!Bytes.wrap(rnd).equals(decoded)) throw new IllegalStateException("encoded/decoded does not match"); 28 | 29 | System.out.println("new EncodeTestCase(\"" + Bytes.wrap(encoded).encodeUtf8() + "\"," + new JavaByteArrayEncoder().encode(rnd) + "),"); 30 | } 31 | 32 | public static final class JavaByteArrayEncoder { 33 | interface ByteEncoder { 34 | String encodeByte(byte b); 35 | } 36 | 37 | String encodeInternal(byte[] array, String prefix, String postfix, String sep, ByteEncoder byteEncoder) { 38 | StringBuilder sb = new StringBuilder(prefix); 39 | for (byte anArray : array) { 40 | sb.append(byteEncoder.encodeByte(anArray)).append(sep).append(" "); 41 | } 42 | 43 | sb.replace(sb.length() - 2, sb.length(), postfix); 44 | return sb.toString(); 45 | } 46 | 47 | public String encode(byte[] array) { 48 | return encodeInternal(array, "new byte[]{", "}", ",", new ByteEncoder() { 49 | @Override 50 | public String encodeByte(byte b) { 51 | StringBuilder sb = new StringBuilder(); 52 | if ((b & 0xFF) >= 127) { 53 | sb.append("(byte) "); 54 | } 55 | sb.append("0x").append(Bytes.from(b).encodeHex(true)); 56 | return sb.toString(); 57 | } 58 | }); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/Repeat.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import java.lang.annotation.Retention; 4 | import java.lang.annotation.RetentionPolicy; 5 | import java.lang.annotation.Target; 6 | 7 | import static java.lang.annotation.ElementType.ANNOTATION_TYPE; 8 | import static java.lang.annotation.ElementType.METHOD; 9 | 10 | @Retention(RetentionPolicy.RUNTIME) 11 | @Target({METHOD, ANNOTATION_TYPE}) 12 | public @interface Repeat { 13 | int value() default 1; 14 | } 15 | -------------------------------------------------------------------------------- /modules/bcrypt/src/test/java/at/favre/lib/crypto/bcrypt/misc/RepeatRule.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.misc; 2 | 3 | import org.junit.rules.TestRule; 4 | import org.junit.runner.Description; 5 | import org.junit.runners.model.Statement; 6 | 7 | public class RepeatRule implements TestRule { 8 | 9 | private static class RepeatStatement extends Statement { 10 | private final Statement statement; 11 | private final int repeat; 12 | 13 | public RepeatStatement(Statement statement, int repeat) { 14 | this.statement = statement; 15 | this.repeat = repeat; 16 | } 17 | 18 | @Override 19 | public void evaluate() throws Throwable { 20 | for (int i = 0; i < repeat; i++) { 21 | statement.evaluate(); 22 | } 23 | } 24 | 25 | } 26 | 27 | @Override 28 | public Statement apply(Statement statement, Description description) { 29 | Statement result = statement; 30 | Repeat repeat = description.getAnnotation(Repeat.class); 31 | if (repeat != null) { 32 | int times = repeat.value(); 33 | result = new RepeatStatement(statement, times); 34 | } 35 | return result; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /modules/benchmark-jmh/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | bcrypt-parent 9 | at.favre.lib 10 | 0.10.2 11 | ../../ 12 | 13 | 14 | benchmark-jmh 15 | jar 16 | 17 | BCrypt JMH Benchmark 18 | 19 | 20 | true 21 | 1.37 22 | 23 | 24 | 25 | 26 | 27 | org.apache.maven.plugins 28 | maven-checkstyle-plugin 29 | 30 | true 31 | 32 | 33 | 34 | org.jacoco 35 | jacoco-maven-plugin 36 | 37 | true 38 | 39 | 40 | 41 | com.github.chrisdchristo 42 | capsule-maven-plugin 43 | 1.5.1 44 | 45 | org.openjdk.jmh.Main 46 | -full 47 | fat 48 | 49 | 50 | 51 | build-fat-jar 52 | package 53 | 54 | build 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | org.openjdk.jmh 65 | jmh-core 66 | ${jmh.version} 67 | 68 | 69 | org.openjdk.jmh 70 | jmh-generator-annprocess 71 | ${jmh.version} 72 | provided 73 | 74 | 75 | at.favre.lib 76 | bcrypt 77 | 0.10.2 78 | 79 | 80 | org.mindrot 81 | jbcrypt 82 | ${project.jbcryptVersion} 83 | 84 | 85 | org.bouncycastle 86 | bcprov-jdk15on 87 | ${project.bcVersion} 88 | 89 | 90 | com.github.fzakaria 91 | ascii85 92 | 1.2 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /modules/benchmark-jmh/src/main/java/at/favre/lib/crypto/bcrypt/benchmark/BcryptBenchmark.java: -------------------------------------------------------------------------------- 1 | package at.favre.lib.crypto.bcrypt.benchmark; 2 | 3 | import at.favre.lib.bytes.BinaryToTextEncoding; 4 | import at.favre.lib.bytes.Bytes; 5 | import at.favre.lib.crypto.bcrypt.BCrypt; 6 | import com.github.fzakaria.ascii85.Ascii85; 7 | import org.openjdk.jmh.annotations.*; 8 | 9 | import java.nio.ByteOrder; 10 | import java.nio.charset.StandardCharsets; 11 | import java.util.concurrent.TimeUnit; 12 | 13 | @SuppressWarnings("CheckStyle") 14 | @State(Scope.Thread) 15 | @Fork(1) 16 | @Warmup(iterations = 2, time = 5) 17 | @Measurement(iterations = 3, time = 10) 18 | @BenchmarkMode(Mode.AverageTime) 19 | @OutputTimeUnit(TimeUnit.MILLISECONDS) 20 | public class BcryptBenchmark { 21 | 22 | private AbstractBcrypt favreBcrypt = new FavreBcrypt(); 23 | private AbstractBcrypt jBcrypt = new JBcrypt(); 24 | private AbstractBcrypt bcBcrypt = new BC(); 25 | 26 | @Param({"5", "6", "8", "10", "12", "14"}) 27 | public int cost; 28 | public byte[] pw = Bytes.random(36).encode(new BinaryToTextEncoding.Encoder() { 29 | @Override 30 | public String encode(byte[] bytes, ByteOrder byteOrder) { 31 | return Ascii85.encode(bytes); 32 | } 33 | }).getBytes(StandardCharsets.US_ASCII); 34 | 35 | @Benchmark 36 | public byte[] benchmarkBcBcrypt() { 37 | return benchmark(bcBcrypt, cost, Bytes.wrap(pw).copy().array()); 38 | } 39 | 40 | @Benchmark 41 | public byte[] benchmarkFavreBcrypt() { 42 | return benchmark(favreBcrypt, cost, Bytes.wrap(pw).copy().array()); 43 | } 44 | 45 | @Benchmark 46 | public byte[] benchmarkJBcrypt() { 47 | return benchmark(jBcrypt, cost, Bytes.wrap(pw).copy().array()); 48 | } 49 | 50 | private byte[] benchmark(AbstractBcrypt bcrypt, int logRounds, byte[] pw) { 51 | return bcrypt.bcrypt(logRounds, pw); 52 | } 53 | 54 | static final class FavreBcrypt implements AbstractBcrypt { 55 | @Override 56 | public byte[] bcrypt(int cost, byte[] password) { 57 | return BCrypt.withDefaults().hash(cost, password); 58 | } 59 | } 60 | 61 | static final class JBcrypt implements AbstractBcrypt { 62 | @Override 63 | public byte[] bcrypt(int cost, byte[] password) { 64 | return org.mindrot.jbcrypt.BCrypt.hashpw(new String(password, StandardCharsets.UTF_8), org.mindrot.jbcrypt.BCrypt.gensalt(cost)).getBytes(StandardCharsets.UTF_8); 65 | } 66 | } 67 | 68 | static final class BC implements AbstractBcrypt { 69 | @Override 70 | public byte[] bcrypt(int cost, byte[] password) { 71 | return org.bouncycastle.crypto.generators.BCrypt.generate(Bytes.from(password).append((byte) 0).array(), Bytes.random(16).array(), cost); 72 | } 73 | } 74 | 75 | interface AbstractBcrypt { 76 | byte[] bcrypt(int cost, byte[] password); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.1.1 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "`uname`" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=`java-config --jre-home` 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && 89 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="`which javac`" 94 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=`which readlink` 97 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 98 | if $darwin ; then 99 | javaHome="`dirname \"$javaExecutable\"`" 100 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 101 | else 102 | javaExecutable="`readlink -f \"$javaExecutable\"`" 103 | fi 104 | javaHome="`dirname \"$javaExecutable\"`" 105 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="`\\unset -f command; \\command -v java`" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=`cd "$wdir/.."; pwd` 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir"; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | echo "$(tr -s '\n' ' ' < "$1")" 164 | fi 165 | } 166 | 167 | BASE_DIR=$(find_maven_basedir "$(dirname $0)") 168 | if [ -z "$BASE_DIR" ]; then 169 | exit 1; 170 | fi 171 | 172 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | echo $MAVEN_PROJECTBASEDIR 175 | fi 176 | 177 | ########################################################################################## 178 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 179 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 180 | ########################################################################################## 181 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 182 | if [ "$MVNW_VERBOSE" = true ]; then 183 | echo "Found .mvn/wrapper/maven-wrapper.jar" 184 | fi 185 | else 186 | if [ "$MVNW_VERBOSE" = true ]; then 187 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 188 | fi 189 | if [ -n "$MVNW_REPOURL" ]; then 190 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 191 | else 192 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 193 | fi 194 | while IFS="=" read key value; do 195 | case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; 196 | esac 197 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 198 | if [ "$MVNW_VERBOSE" = true ]; then 199 | echo "Downloading from: $wrapperUrl" 200 | fi 201 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 202 | if $cygwin; then 203 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 204 | fi 205 | 206 | if command -v wget > /dev/null; then 207 | QUIET="--quiet" 208 | if [ "$MVNW_VERBOSE" = true ]; then 209 | echo "Found wget ... using wget" 210 | QUIET="" 211 | fi 212 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 213 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" 214 | else 215 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" 216 | fi 217 | [ $? -eq 0 ] || rm -f "$wrapperJarPath" 218 | elif command -v curl > /dev/null; then 219 | QUIET="--silent" 220 | if [ "$MVNW_VERBOSE" = true ]; then 221 | echo "Found curl ... using curl" 222 | QUIET="" 223 | fi 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L 228 | fi 229 | [ $? -eq 0 ] || rm -f "$wrapperJarPath" 230 | else 231 | if [ "$MVNW_VERBOSE" = true ]; then 232 | echo "Falling back to using Java to download" 233 | fi 234 | javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 235 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" 236 | # For Cygwin, switch paths to Windows format before running javac 237 | if $cygwin; then 238 | javaSource=`cygpath --path --windows "$javaSource"` 239 | javaClass=`cygpath --path --windows "$javaClass"` 240 | fi 241 | if [ -e "$javaSource" ]; then 242 | if [ ! -e "$javaClass" ]; then 243 | if [ "$MVNW_VERBOSE" = true ]; then 244 | echo " - Compiling MavenWrapperDownloader.java ..." 245 | fi 246 | # Compiling the Java class 247 | ("$JAVA_HOME/bin/javac" "$javaSource") 248 | fi 249 | if [ -e "$javaClass" ]; then 250 | # Running the downloader 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo " - Running MavenWrapperDownloader.java ..." 253 | fi 254 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 255 | fi 256 | fi 257 | fi 258 | fi 259 | ########################################################################################## 260 | # End of extension 261 | ########################################################################################## 262 | 263 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 264 | 265 | # For Cygwin, switch paths to Windows format before running java 266 | if $cygwin; then 267 | [ -n "$JAVA_HOME" ] && 268 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 269 | [ -n "$CLASSPATH" ] && 270 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 271 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 272 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 273 | fi 274 | 275 | # Provide a "standardized" way to retrieve the CLI args that will 276 | # work with both Windows and non-Windows executions. 277 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 278 | export MAVEN_CMD_LINE_ARGS 279 | 280 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 281 | 282 | exec "$JAVACMD" \ 283 | $MAVEN_OPTS \ 284 | $MAVEN_DEBUG_OPTS \ 285 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 286 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 287 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 288 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.1.1 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM Provide a "standardized" way to retrieve the CLI args that will 157 | @REM work with both Windows and non-Windows executions. 158 | set MAVEN_CMD_LINE_ARGS=%* 159 | 160 | %MAVEN_JAVA_EXE% ^ 161 | %JVM_CONFIG_MAVEN_PROPS% ^ 162 | %MAVEN_OPTS% ^ 163 | %MAVEN_DEBUG_OPTS% ^ 164 | -classpath %WRAPPER_JAR% ^ 165 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 166 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 167 | if ERRORLEVEL 1 goto error 168 | goto end 169 | 170 | :error 171 | set ERROR_CODE=1 172 | 173 | :end 174 | @endlocal & set ERROR_CODE=%ERROR_CODE% 175 | 176 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 177 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 178 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 179 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 180 | :skipRcPost 181 | 182 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 183 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 184 | 185 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 186 | 187 | cmd /C exit /B %ERROR_CODE% 188 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | at.favre.lib 9 | common-parent 10 | 20 11 | 12 | 13 | bcrypt-parent 14 | pom 15 | 0.10.2 16 | 17 | https://favr.dev/opensource/bcrypt 18 | 2018 19 | 20 | bcrypt-parent 21 | Parent Maven project for Bcrypt 22 | 23 | 24 | 25 | 26 | allmodules 27 | 28 | true 29 | 30 | 31 | modules/bcrypt 32 | modules/bcrypt-cli 33 | modules/benchmark-jmh 34 | 35 | 36 | 37 | mainmodule 38 | 39 | modules/bcrypt 40 | 41 | 42 | 43 | 44 | 45 | 46 | patrickfav 47 | https://sonarcloud.io 48 | jacoco 49 | reuseReports 50 | **/target/site/jacoco/jacoco.xml 51 | java 52 | 53 | 0.4 54 | 1.70 55 | 56 | true 57 | 58 | 59 | 60 | 61 | 62 | org.apache.maven.plugins 63 | maven-surefire-plugin 64 | 65 | 66 | org.apache.maven.plugins 67 | maven-compiler-plugin 68 | 69 | 70 | org.jacoco 71 | jacoco-maven-plugin 72 | 73 | 74 | 75 | 76 | 77 | scm:git:https://github.com/patrickfav/bcrypt.git 78 | scm:git:https://github.com/patrickfav/bcrypt.git 79 | https://github.com/patrickfav/bcrypt 80 | 81 | 82 | 83 | Github 84 | https://github.com/patrickfav/bcrypt/issues 85 | 86 | 87 | 88 | Github Actions 89 | https://github.com/patrickfav/bcrypt/actions 90 | 91 | 92 | -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -optimizations !code/simplification/arithmetic,!code/simplification/cast,!field/*,!class/merging/* 2 | -dontusemixedcaseclassnames 3 | -dontobfuscate 4 | -verbose 5 | 6 | -keepattributes *Annotation*,EnclosingMethod, InnerClasses, Exceptions, Signature, SourceFile, LineNumberTable, MethodParameters 7 | -renamesourcefileattribute SourceFile 8 | -optimizationpasses 3 9 | -overloadaggressively 10 | 11 | -keepclasseswithmembernames class * { 12 | native ; 13 | } 14 | 15 | -keepclassmembers enum * { 16 | public static **[] values(); 17 | public static ** valueOf(java.lang.String); 18 | } 19 | 20 | ################################################ 21 | 22 | -dontnote com.sun.** 23 | -dontwarn com.sun.** 24 | 25 | -dontnote sun.** 26 | -dontwarn sun.** 27 | 28 | -dontnote java.** 29 | -dontwarn java.** 30 | 31 | -dontnote javax.** 32 | -dontwarn javax.** 33 | 34 | # keep all public classes in main package 35 | -keep public class at.favre.lib.crypto.bcrypt.** { public *; } 36 | -keep interface at.favre.lib.crypto.bcrypt.** { ; } 37 | --------------------------------------------------------------------------------