libeufin

Integration and sandbox testing for FinTech APIs and data formats
Log | Files | Refs | Submodules | README | LICENSE

commit 23acdbd445d2a9a8f22eecc2459cc4e962d4a4f1
parent 243a7188d9d67e55ef18216b702a770873287155
Author: Antoine A <>
Date:   Fri,  4 Sep 2026 12:03:27 +0200

bank: fix bcrypt compatibility with kotlin

Diffstat:
Mlibeufin-bank/src/pw.rs | 26++++++++++++++++++++++++--
1 file changed, 24 insertions(+), 2 deletions(-)

diff --git a/libeufin-bank/src/pw.rs b/libeufin-bank/src/pw.rs @@ -61,6 +61,18 @@ pub enum PwCrypto { Sha256, } +fn bcrypt(cost: u32, salt: [u8; 16], pw: &[u8]) -> [u8; 24] { + // The bcrypt spec specifies that passwords should be null terminated + // strings, but if longer than 72 bytes, are truncated at 72 bytes (thereby + // losing the null byte at the end). + let copy_len = pw.len().min(72); + let mut pass = [0u8; 72]; + pass[..copy_len].copy_from_slice(&pw[..copy_len]); + let used = (copy_len + 1).min(72); + let truncated = &pass[..used]; + bcrypt::bcrypt(cost, salt, truncated) +} + impl PwCrypto { /** Hash [pw] using [cfg] hashing method */ pub fn hashpw(&self, pw: &str) -> String { @@ -68,7 +80,7 @@ impl PwCrypto { PwCrypto::Bcrypt { cost } => { let mut salt = [0u8; 16]; SysRng.try_fill_bytes(&mut salt).unwrap(); - let pwh = bcrypt::hash_with_salt_bytes(pw.as_bytes(), *cost, salt).unwrap(); + let pwh = bcrypt(*cost, salt, pw.as_bytes()); format!("bcrypt${cost}${}${}", base64::fmt(salt), base64::fmt(pwh)) } PwCrypto::Sha256 => { @@ -103,7 +115,7 @@ impl PwCrypto { let salt = base64::decode(salt)? .try_into() .map_err(|_| anyhow!("bad password hash format"))?; - let pwh = bcrypt::hash_with_salt_bytes(pw, cost, salt)?; + let pwh = bcrypt(cost, salt, pw.as_bytes()); let pwh = base64::encode(pwh); ( pwh == hash, @@ -190,4 +202,14 @@ fn pwh() { outdated: true } ); + + // Check compatibility with kotlin hashes + let hash = "bcrypt$4$PfNp4JBeMU/t5mS5zaUY3A==$0Tjw0KzNA3ca3y9BVkh/e3TJDlHsdkEA"; + assert_eq!( + crypto.checkpw("password", &hash).unwrap(), + PwCheck { + matches: true, + outdated: false + } + ); }