{"id":21513236,"url":"https://github.com/patrickfav/bkdf","last_synced_at":"2025-04-09T18:52:51.256Z","repository":{"id":57714961,"uuid":"160938307","full_name":"patrickfav/bkdf","owner":"patrickfav","description":"BCrypt based key derivation function to improve BCrypt as a cryptographic primitive for password hashing and key derivation","archived":false,"fork":false,"pushed_at":"2023-11-13T04:54:06.000Z","size":399,"stargazers_count":5,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-23T20:51:15.590Z","etag":null,"topics":["bcrypt","cryptography","hkdf","java","kdf","password","security"],"latest_commit_sha":null,"homepage":"https://favr.dev/opensource/bkdf","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/patrickfav.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-08T12:43:16.000Z","updated_at":"2024-12-09T19:53:52.000Z","dependencies_parsed_at":"2022-08-30T19:22:40.951Z","dependency_job_id":null,"html_url":"https://github.com/patrickfav/bkdf","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Fbkdf","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Fbkdf/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Fbkdf/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/patrickfav%2Fbkdf/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/patrickfav","download_url":"https://codeload.github.com/patrickfav/bkdf/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248093217,"owners_count":21046645,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bcrypt","cryptography","hkdf","java","kdf","password","security"],"created_at":"2024-11-23T22:55:06.276Z","updated_at":"2025-04-09T18:52:51.232Z","avatar_url":"https://github.com/patrickfav.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# BCrypt based Key Derivation Function (BKDF)\n\n[![Maven Central](https://img.shields.io/maven-central/v/at.favre.lib/bkdf)](https://mvnrepository.com/artifact/at.favre.lib/bkdf)\n[![Github Actions](https://github.com/patrickfav/bkdf/actions/workflows/build_deploy.yml/badge.svg)](https://github.com/patrickfav/bkdf/actions)\n[![Javadocs](https://www.javadoc.io/badge/at.favre.lib/bkdf.svg)](https://www.javadoc.io/doc/at.favre.lib/bkdf)\n[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bkdf\u0026metric=coverage)](https://sonarcloud.io/summary/new_code?id=patrickfav_bkdf)\n[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bkdf\u0026metric=security_rating)](https://sonarcloud.io/summary/new_code?id=patrickfav_bkdf)\n[![Technical Debt](https://sonarcloud.io/api/project_badges/measure?project=patrickfav_bkdf\u0026metric=sqale_index)](https://sonarcloud.io/summary/new_code?id=patrickfav_bkdf)\n\nThe aim of this project is to improve on the cryptographic primitive [BCrypt](https://en.wikipedia.org/wiki/Bcrypt) with\nproviding well-defined modes of operation which includes:\n\n* Improved password hashing function\n* Protocol to upgrade password hashes offline\n* Fully functional key derivation function\n\nAll this is achieved by only adding [HKDF](https://en.wikipedia.org/wiki/HKDF) as additional building block.\n\nThe 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.\n\n_Note, that this project is ongoing research and may not be ready for prime-time yet as it requires more feedback from the cryptographic community._\n\n## Quickstart\n\nAdd dependency to your `pom.xml` ([check latest release](https://github.com/patrickfav/bkdf/releases)):\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eat.favre.lib\u003c/groupId\u003e\n    \u003cartifactId\u003ebkdf\u003c/artifactId\u003e\n    \u003cversion\u003e{latest-version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\nA very simple example using the password hasher:\n\n```java\nPasswordHasher hasher = BKDF.createPasswordHasher();\n\nchar[] pw = \"secret\".toCharArray();\nint costFactor = 6; // same as with bcrypt 4-31 doubling the iterations every increase\n\n//returns base64 url-safe encoded string\nString hash = hasher.hash(pw, costFactor);\n\nPasswordHashVerifier verifier = BKDF.createPasswordHashVerifier();\nboolean verified = verifier.verify(pw, hash);\n```\n\n### Full Example\n\nThe BKDF protocol supports 3 use-cases:\n\n* password hash with key stretching feature for storage\n* upgrade of previously generated password hashes offline without the user password\n* key derivation function with key strechting feature to generate high quality keying material (for e.g. secret keys)\n\n#### Password Hash\n\nA password hash is used to generate a hash from a user-password which can't easily be used to calculate the used password without brute-forcing. An important feature of password hashes are, that they are slow, so it makes it harder (or infeasible) for an attacker to brute force. This property is also called \"[key-stretching](https://en.wikipedia.org/wiki/Key_stretching)\". Well known password hashes are [PBKDF2](https://en.wikipedia.org/wiki/PBKDF2), [scrypt](https://en.wikipedia.org/wiki/Scrypt) and [Argon2](https://en.wikipedia.org/wiki/Argon2).\n\n```java\n// provide different version of hash config and provide own impl of secure random for salt gen\nPasswordHasher hasher = BKDF.createPasswordHasher(Version.HKDF_HMAC512, new SecureRandom());\nchar[] pw = \"secret\".toCharArray();\nHashData hashData = hasher.hashRaw(\"secret\".toCharArray(), 4);\n\n// get the raw, non-encoded hash message\nbyte[] hashMsgAsBlob = hashData.getAsBlobMessageFormat();\n\n// get the base64 url-safe encoded string\nString hashAsBase64 = hashData.getAsEncodedMessageFormat();\n\nPasswordHashVerifier verifier = BKDF.createPasswordHashVerifier();\nboolean verified = verifier.verify(pw, hashData);\n```\n\n#### Password Hash Upgrade\n\nBCrypt does not support upgrading the strength of the password hash without the user password. Having legacy password hashes in a DB, the need may arise to improve them, because CPU performance increased of the last couple of years. With this feature a password can be upgraded offline by basically chaining multiple hashes together.\n\nThis mode will chain a specific new hash with given cost factor:\n\n```java\nchar[] pw = \"secret\".toCharArray();\n\n// hash with cost factor 5\nString hash = BKDF.createPasswordHasher().hash(pw, 5);\n        PasswordHashUpgrader upgrader=new PasswordHashUpgrader.Default(new SecureRandom());\n\n// upgrade hash with an additional cost factor (i.e. now needs to calculate 5 + 6 = 32 + 64 = 96 iterations\n        CompoundHashData compoundHashData=upgrader.upgradePasswordHashWith(6,hash);\n\n// create base64 url-safe encoded msg and verify\nboolean verified = BKDF.createPasswordHashVerifier().verify(pw, compoundHashData.getAsEncodedMessageFormat());\n```\n\nAnother mode will take a target cost factor and calculate the required hashes to achieve it\n\n```java\nchar[] pw = \"secret\".toCharArray();\n\n// hash with cost factor 5\nString hash = BKDF.createPasswordHasher().hash(pw, 5);\nPasswordHashUpgrader upgrader = new PasswordHashUpgrader.Default(new SecureRandom());\n\n// upgrade to have exactly cost factor 8 (aka 2^8 = 256 iterations)\nCompoundHashData compoundHashData = upgrader.upgradePasswordHashTo(8, hash);\n\n// create base64 url-safe encoded msg and verify\nboolean verified = BKDF.createPasswordHashVerifier().verify(pw, compoundHashData.getAsEncodedMessageFormat());\n```\n\n#### Key Derivation Function\nIt might be useful to have a primitive that generates high-quality key material for e.g. symmetric encryption and not password hashes.\n\nThis example creates an AES key from a user password: \n\n```java\nchar[] pw = \"secret\".toCharArray();\nbyte[] salt = Bytes.random(16).array();\nint costFactor = 5;\n\nKeyDerivationFunction kdf = new KeyDerivationFunction.Default(Version.HKDF_HMAC512);\nbyte[] aesKey = kdf.derive(salt, pw, costFactor, Bytes.from(\"aes-key\").array(), 16);\n\nSecretKey aesSecretKey = new SecretKeySpec(aesKey, \"AES\");\n```\n\nTo generate multiple keys, use the following example, so you are not required to generate the internal bcrypt hash for every key: \n\n```java\n// an entropy source used in your current protocol\nbyte[]ikm=Bytes.random(12).array();\nbyte[] salt = Bytes.random(16).array();\nint costFactor = 5;\n\nKeyDerivationFunction kdf = new KeyDerivationFunction.Default(Version.HKDF_HMAC512);\nList\u003cKeyDerivationFunction.KdfConfig\u003e config = Arrays.asList(\n        new KeyDerivationFunction.KdfConfig(Bytes.from(\"aes-key\").array(), 16),\n        new KeyDerivationFunction.KdfConfig(Bytes.from(\"mac-key\").array(), 32)\n);\nList\u003cbyte[]\u003e keys = kdf.deriveMulti(salt, ikm, costFactor, config);\n\nSecretKey aesSecretKey = new SecretKeySpec(keys.get(0), \"AES\");\nSecretKey macSecretKey = new SecretKeySpec(keys.get(1), \"HmacSHA512\");\n```\n\n## Description\n\nIn the following the details of each of the protocols are discussed.\n\nIn the example the following functions are used:\n\n    bcrypt(cost_factor {4-31}, user_pw, [16-byte-salt])\n    hkdf_extract(salt, input_key_material)\n    hkdf_expand(output_key_material, info_param, out_length_byte)\n\nThe [HMAC](https://en.wikipedia.org/wiki/HMAC) used by [HKDF](https://tools.ietf.org/html/rfc5869) is defined by the used hash version, currently only HMAC-SHA512 is supported.\n\n### Password Hash Protocol\n\n#### Step 1: Extract User Password\n\nFirst create uniformly distributed entropy byte string with through HKDF \"extract\" from user password. Convert the user password to a byte array using UTF-8 encoding. Use an empty byte array as salt with the length of the underyling hash output length (aka HMAC-SHA512 == 64 byte)\n\n    utf8PwBytes = user_password.getUtf8Bytes()\n    extractedPw = hkdf_extract(empty_byte_array, utf8PwBytes)\n   \n#### Step 2: Stretch with BCrypt\n\ntbd.\n\n### Password Upgrade Protocol\n\ntbd.\n\n### KDF Protocol\n\ntbd.\n\n## Download\n\nThe artifacts are deployed to [Maven Central](https://search.maven.org/).\n\n### Maven\n\nAdd dependency to your `pom.xml`:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eat.favre.lib\u003c/groupId\u003e\n    \u003cartifactId\u003ebkdf\u003c/artifactId\u003e\n    \u003cversion\u003e{latest-version}\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\nAdd to your `build.gradle` module dependencies:\n\n    compile group: 'at.favre.lib', name: 'bkdf', version: '{latest-version}'\n\n### Local Jar\n\n[Grab jar from the latest release.](https://github.com/patrickfav/bkdf/releases/latest)\n\n## Security Relevant Information\n\n### OWASP Dependency Check\n\nThis 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).\nThe build will fail if any issue is found.\n\n### Digital Signatures\n\n#### Signed Jar\n\nThe provided JARs in the GitHub release page are signed with my private key:\n\n    CN=Patrick Favre-Bulle, OU=Private, O=PF Github Open Source, L=Vienna, ST=Vienna, C=AT\n    Validity: Thu Sep 07 16:40:57 SGT 2017 to: Fri Feb 10 16:40:57 SGT 2034\n    SHA1: 06:DE:F2:C5:F7:BC:0C:11:ED:35:E2:0F:B1:9F:78:99:0F:BE:43:C4\n    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\n\nUse the jarsigner tool (found in your `$JAVA_HOME/bin` folder) folder to verify.\n\n#### Signed Commits\n\nAll tags and commits by me are signed with git with my private key:\n\n    GPG key ID: 4FDF85343912A3AB\n    Fingerprint: 2FB392FB05158589B767960C4FDF85343912A3AB\n\n## Build\n\n### Jar Sign\n\nIf you want to jar sign you need to provide a file `keystore.jks` in the\nroot folder with the correct credentials set in environment variables (\n`OPENSOURCE_PROJECTS_KS_PW` and `OPENSOURCE_PROJECTS_KEY_PW`); alias is\nset as `pfopensource`.\n\nIf you want to skip jar signing just change the skip configuration in the\n`pom.xml` jar sign plugin to true:\n\n    \u003cskip\u003etrue\u003c/skip\u003e\n\n### Build with Maven\n\nUse the Maven wrapper to create a jar including all dependencies\n\n    mvnw clean install\n\n### Checkstyle Config File\n\nThis project uses my [`common-parent`](https://github.com/patrickfav/mvn-common-parent) which centralized a lot of\nthe 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\n`target/checkstyle-checker.xml`. So if you use a plugin for your IDE, use this file as your local configuration.\n\n## Tech-Stack\n\n* Java 7 (+ [errorprone](https://github.com/google/error-prone) static analyzer)\n* Maven\n\n## Related Libraries\n\n* [BCyrpt Password Hash Function (Java)](https://github.com/patrickfav/bcrypt)\n* [HKDF [RFC5869] Two-Step KDF (Java)](https://github.com/patrickfav/hkdf)\n* [Single Step KDF [NIST SP 800-56C] (Java)](https://github.com/patrickfav/singlestep-kdf)\n\n# License\n\nCopyright 2018 Patrick Favre-Bulle\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickfav%2Fbkdf","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpatrickfav%2Fbkdf","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpatrickfav%2Fbkdf/lists"}