Skip to content

Comments

Add PBKDF2 Hashing support#596

Merged
MohamedSabthar merged 27 commits intoballerina-platform:masterfrom
randilt:PBKDF2-support
Apr 16, 2025
Merged

Add PBKDF2 Hashing support#596
MohamedSabthar merged 27 commits intoballerina-platform:masterfrom
randilt:PBKDF2-support

Conversation

@randilt
Copy link
Contributor

@randilt randilt commented Mar 18, 2025

Purpose

Resolves: #43926

This PR introduces two new APIs to the Ballerina crypto module for password hashing and verification using PBKDF2.

Newly Added APIs

  1. crypto:hashPbkdf2() - Generates a PBKDF2 hash of a given password with optional parameters for iterations and hash algorithm.
  2. crypto:verifyPbkdf2() - Verifies whether a given password matches a PBKDF2 hashed password.

Examples

1. Hashing a Password using PBKDF2

import ballerina/crypto;

string password = "mySecurePassword123";
string|crypto:Error hash = crypto:hashPbkdf2(password);

Parameters:

  • password - The password string to be hashed.
  • iterations (optional) - The number of iterations (default: 10,000).
  • algorithm (optional) - HMAC algorithm (SHA1, SHA256, SHA512, default: SHA256).
  • Returns - A PBKDF2 hashed password string or an error if hashing fails.

2. Verifying a PBKDF2 Hashed Password

import ballerina/crypto;

string password = "mySecurePassword123";
string hashedPassword = "$pbkdf2-sha256$i=10000$salt$hash";
boolean|crypto:Error matches = crypto:verifyPbkdf2(password, hashedPassword);

Parameters:

  • password - The password string to verify.
  • hashedPassword - The PBKDF2 hashed password to compare against.
  • Returns - A boolean indicating whether the password matches or an error if verification fails.

Checklist

  • Linked to an issue
  • Updated the changelog
  • Added tests
  • Updated the spec
  • Checked native-image compatibility

@codecov
Copy link

codecov bot commented Mar 18, 2025

Codecov Report

Attention: Patch coverage is 68.70748% with 46 lines in your changes missing coverage. Please review.

Project coverage is 82.22%. Comparing base (59b8e05) to head (3a5bac0).
Report is 28 commits behind head on master.

Files with missing lines Patch % Lines
...o/ballerina/stdlib/crypto/nativeimpl/Password.java 68.80% 37 Missing and 2 partials ⚠️
...java/io/ballerina/stdlib/crypto/PasswordUtils.java 61.11% 6 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##             master     #596      +/-   ##
============================================
- Coverage     83.47%   82.22%   -1.26%     
- Complexity      226      239      +13     
============================================
  Files            30       29       -1     
  Lines          1283     1367      +84     
  Branches        180      190      +10     
============================================
+ Hits           1071     1124      +53     
- Misses          169      197      +28     
- Partials         43       46       +3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Co-authored-by: DimuthuMadushan <35717653+DimuthuMadushan@users.noreply.github.com>
@randilt randilt requested a review from DimuthuMadushan March 20, 2025 04:43
* @return formatted PBKDF2 salt string
*/
public static String formatPBKDF2Salt(String algorithm, long iterations, String saltBase64) {
return String.format(Locale.ROOT, "$pbkdf2-%s$i=%d$%s",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we make $pbkdf2-%s$i=%d$%s a constant?

public static Object verifyPasswordPBKDF2(BString password, BString hashedPassword) {
try {
String hash = hashedPassword.getValue();
Pattern pattern = Pattern.compile("\\$pbkdf2-(\\w+)\\$i=(\\d+)\\$([A-Za-z0-9+/=]+)\\$([A-Za-z0-9+/=]+)");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we make this a constant?

@daneshk
Copy link
Member

daneshk commented Mar 31, 2025

@MohamedSabthar, can you review this please?

* @param algorithm the HMAC algorithm to validate
* @return null if valid, error if invalid
*/
public static Object validatePBKDF2Algorithm(String algorithm) {
Copy link
Member

@MohamedSabthar MohamedSabthar Apr 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public static Object validatePBKDF2Algorithm(String algorithm) {
public static Object validatePbkdf2Algorithm(String algorithm) {

Rename validatePBKDF2Algorithm to validatePbkdf2Algorithm to align with camel case conventions, making it more readable and consistent with standard Java naming practices we follow.

Let's update other places.

/**
* Default number of iterations for PBKDF2.
*/
public static final int DEFAULT_PBKDF2_ITERATIONS = 10000;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to maintain constants for default values here? Ideally, these values are already defined as defaultable parameters in the Ballerina code. Instead of maintaining them separately in both Java and Ballerina, we should consider reading and using the values directly from Ballerina to avoid duplication.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but I think keeping the default values on the Java side would also be good. in the future, if these functions are used internally within the library for testing or any other purposes, they would still work with those default values. Additionally, if for some reason the values don’t get passed from the Ballerina side, the default values would act as a safety net. Let me know if you think it's better to remove them @MohamedSabthar

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification @randilt! I understand your point, but I still think it's better to avoid duplicating default values in both places. Having a single source of truth helps reduce the risk of inconsistencies and makes maintenance easier in the long run. If there's ever a case where defaults are needed in Java (e.g., for internal tests), we can consider injecting or reading them from the Ballerina side rather than hardcoding. So I'd still prefer removing them from Java for now.

@randilt randilt requested a review from MohamedSabthar April 3, 2025 16:48
@randilt randilt requested a review from daneshk April 3, 2025 17:13
@randilt
Copy link
Contributor Author

randilt commented Apr 4, 2025

The suggested changes have been made, could you please check @daneshk @MohamedSabthar

Implements the PBKDF2 (Password-Based Key Derivation Function 2) algorithm for password hashing.

```ballerina
public isolated function hashPbkdf2(string password, int iterations = 10000,
Copy link
Member

@MohamedSabthar MohamedSabthar Apr 4, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's update the spec and proposal with the enum change

Co-authored-by: MohamedSabthar <sabtharugc0@gmail.com>
@MohamedSabthar
Copy link
Member

The suggested changes have been made, could you please check @daneshk @MohamedSabthar

Added few comments

@randilt
Copy link
Contributor Author

randilt commented Apr 4, 2025

Did the necessary changes please check @MohamedSabthar

@randilt randilt requested a review from MohamedSabthar April 4, 2025 16:58
* 2.5. [SHA512](#25-sha512)
* 2.6. [CRC32B](#26-crc32b)
* 2.7. [KECCAK256](#27-keccak256)
- 2.1. [MD5](#21-md5)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@randilt shall we update the code with previous formatting?
replace them with '*' instead of '-'.
We follow this same convention across all library modules.

@MohamedSabthar
Copy link
Member

Did the necessary changes please check @MohamedSabthar

Hi @randilt, the PR looks good overall 🙌. I’ve added a minor comment.
Also, let’s address this one as well: #596 (comment)

Once those are fixed, we can go ahead and merge the PR.

@sonarqubecloud
Copy link

@randilt
Copy link
Contributor Author

randilt commented Apr 11, 2025

@MohamedSabthar I applied the changes you suggested, please check and let me know if anything else needs to be changed or added.

@randilt randilt requested a review from MohamedSabthar April 11, 2025 14:33
Copy link
Member

@MohamedSabthar MohamedSabthar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MohamedSabthar MohamedSabthar merged commit 6346d39 into ballerina-platform:master Apr 16, 2025
7 of 8 checks passed
@MohamedSabthar
Copy link
Member

Thanks for your valuable contribution, @randilt!
If you're interested, you can find more 'good first issues' to contribute to here.
Looking forward to seeing more awesome contributions from you! ✨🧑‍💻🚀

@randilt
Copy link
Contributor Author

randilt commented Apr 16, 2025

Thanks for your valuable contribution, @randilt! If you're interested, you can find more 'good first issues' to contribute to here. Looking forward to seeing more awesome contributions from you! ✨🧑‍💻🚀

Appreciate it! Will definitely check them out and keep contributing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Improvement]: Add PBKDF2 Support to Ballerina Crypto Module

4 participants