BCrypt Password Hashing
This page explains how to securely hash and verify passwords using bcrypt.
Code snippets assume using namespace boost::capy; is in effect.
|
What is BCrypt?
BCrypt is a password-hashing function designed by Niels Provos and David Mazières. It incorporates:
-
A salt to protect against rainbow table attacks
-
An adaptive cost factor that can be increased as hardware improves
-
Built-in work factor that makes brute-force attacks expensive
BCrypt is the recommended algorithm for password storage.
Quick Start
#include <boost/capy/bcrypt.hpp>
// Hash a password
bcrypt::result hash = bcrypt::hash("my_password", 12);
// Store hash.str() in database...
// Later, verify the password
system::error_code ec;
bool valid = bcrypt::compare("my_password", stored_hash, ec);
if (ec)
// Hash was malformed
else if (valid)
// Password matches
else
// Password does not match
Hashing Passwords
The hash function generates a salted hash:
// Default cost factor (10)
bcrypt::result r1 = bcrypt::hash("password");
// Custom cost factor
bcrypt::result r2 = bcrypt::hash("password", 12);
// Custom cost factor and version
bcrypt::result r3 = bcrypt::hash("password", 12, bcrypt::version::v2b);
Cost Factor
The cost factor (rounds) determines how expensive hashing is. Each increment doubles the work:
| Cost | Approximate Time (modern CPU) |
|---|---|
10 |
~100ms |
12 |
~400ms |
14 |
~1.6s |
16 |
~6.4s |
Guidelines:
-
Minimum: 10 for new applications
-
Recommended: 12 for most applications
-
Maximum: 31 (impractically slow)
-
Adjust based on your hardware and latency requirements
Verifying Passwords
The compare function extracts the salt from a stored hash, re-hashes
the input password, and compares:
system::error_code ec;
bool valid = bcrypt::compare(user_input, stored_hash, ec);
if (ec == bcrypt::error::invalid_hash)
{
// Hash string is malformed - data corruption or tampering
log_security_event("invalid hash format");
return false;
}
if (valid)
grant_access();
else
reject_login();
| Always check the error code. A false return value alone does not distinguish between "wrong password" and "malformed hash". |
Working with Salts
You can generate and use salts separately:
// Generate a salt
bcrypt::result salt = bcrypt::gen_salt(12);
// Hash with explicit salt
system::error_code ec;
bcrypt::result hash = bcrypt::hash("password", salt.str(), ec);
This is rarely needed since hash() generates a salt automatically.
The result Type
bcrypt::result is a fixed-size buffer (no heap allocation):
bcrypt::result r = bcrypt::hash("password", 12);
// Access the hash string
core::string_view sv = r.str(); // Or just use r (implicit conversion)
char const* cstr = r.c_str(); // Null-terminated
// Check for valid result
if (r)
store(r.str());
Hash String Format
A bcrypt hash string has this format:
$2b$12$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
│ │ │ │
│ │ │ └─ hash (31 chars)
│ │ └─ salt (22 chars)
│ └─ cost factor
└─ version
Total length: 60 characters.
Extracting the Cost Factor
To check the cost factor of an existing hash:
system::error_code ec;
unsigned rounds = bcrypt::get_rounds(stored_hash, ec);
if (ec)
// Invalid hash format
else if (rounds < 12)
// Consider re-hashing with higher cost
Upgrading Cost Factor
When a user logs in successfully, you can check if their hash needs upgrading:
system::error_code ec;
bool valid = bcrypt::compare(password, stored_hash, ec);
if (valid && !ec)
{
unsigned current_cost = bcrypt::get_rounds(stored_hash, ec);
if (!ec && current_cost < 12)
{
// Re-hash with higher cost
bcrypt::result new_hash = bcrypt::hash(password, 12);
update_stored_hash(user_id, new_hash.str());
}
}
Error Handling
BCrypt defines two error codes:
| Error | Meaning |
|---|---|
|
Salt string is malformed |
|
Hash string is malformed |
These errors indicate either data corruption or malicious input. Log them as security events.
Version Selection
BCrypt has multiple version prefixes:
| Version | Description |
|---|---|
|
Original specification |
|
Fixed handling of passwords > 255 chars (recommended) |
Use v2b for new hashes. All versions produce compatible hashes that can
be verified by any version.
Security Considerations
Do:
-
Use cost factor 12 or higher
-
Store the complete hash string (includes salt and cost)
-
Compare in constant time (handled by
compare) -
Log invalid hash errors as security events
Do Not:
-
Store salts separately (they are embedded in the hash)
-
Use bcrypt for general-purpose hashing (use SHA-256)
-
Compare hashes with
==(timing attacks)
Summary
| Function | Purpose |
|---|---|
|
Hash a password with auto-generated salt |
|
Hash with explicit salt |
|
Verify a password against a hash |
|
Generate a random salt |
|
Extract cost factor from hash |
Next Steps
-
Containers — Type-erased storage
-
API Reference — Full reference documentation