diff --git a/src/server/common_services/auth_server.js b/src/server/common_services/auth_server.js index 76cfa58012..2aa1ca5beb 100644 --- a/src/server/common_services/auth_server.js +++ b/src/server/common_services/auth_server.js @@ -3,6 +3,7 @@ const _ = require('lodash'); const bcrypt = require('bcrypt'); +const node_hash = require('../../util/account_password_hash'); const ip_module = require('ip'); const P = require('../../util/promise'); @@ -21,6 +22,14 @@ const config = require('../../../config'); const s3_bucket_policy_utils = require('../../endpoint/s3/s3_bucket_policy_utils'); + +function compare_password_hash(password, target_account) { + if (bcrypt.compare(password.unwrap(), target_account.password.unwrap())) { + return true; + } + return (node_hash.verify_node_password_hash(password.unwrap(), target_account.password.unwrap())); +} + /** * * CREATE_AUTH @@ -66,7 +75,7 @@ function create_auth(req) { if (!password) return; return P.resolve() - .then(() => bcrypt.compare(password.unwrap(), target_account.password.unwrap())) + .then(() => compare_password_hash(password, target_account)) .then(match => { if (!match) { dbg.log0('password mismatch', email, system_name); diff --git a/src/server/system_services/account_server.js b/src/server/system_services/account_server.js index 98a5d6be4d..d25730a562 100644 --- a/src/server/system_services/account_server.js +++ b/src/server/system_services/account_server.js @@ -6,7 +6,8 @@ const _ = require('lodash'); const net = require('net'); const chance = require('chance')(); const GoogleStorage = require('../../util/google_storage_wrap'); -const bcrypt = require('bcrypt'); +const node_hash = require('../../util/account_password_hash'); + const server_rpc = require('../server_rpc'); const config = require('../../../config'); @@ -79,7 +80,7 @@ async function create_account(req) { if (req.rpc_params.has_login) { account.password = req.rpc_params.password; - const password_hash = await bcrypt_password(account.password.unwrap()); + const password_hash = await crypto_password(account.password.unwrap()); account.password = password_hash; } @@ -574,7 +575,7 @@ async function reset_password(req) { const params = req.rpc_params; - const password = await bcrypt_password(params.password.unwrap()); + const password = await crypto_password(params.password.unwrap()); const changes = { password: new SensitiveString(password), @@ -1334,7 +1335,7 @@ function ensure_support_account() { } console.log('CREATING SUPPORT ACCOUNT...'); - return bcrypt_password(system_store.get_server_secret()) + return crypto_password(system_store.get_server_secret()) .then(password => { const support_account = { _id: system_store.new_system_store_id(), @@ -1358,9 +1359,9 @@ function ensure_support_account() { }); } -function bcrypt_password(password) { - return P.resolve() - .then(() => password && bcrypt.hash(password, 10)); +function crypto_password(password) { + return P.resolve() + .then(() => password && node_hash.create_node_password_hash(password)); } function is_support_or_admin_or_me(system, account, target_account) { @@ -1454,7 +1455,7 @@ async function verify_authorized_account(req) { if (req.role === 'operator') { return true; } - return bcrypt.compare(req.rpc_params.verification_password.unwrap(), req.account.password.unwrap()); + return node_hash.verify_node_password_hash(req.rpc_params.verification_password.unwrap(), req.account.password.unwrap()); } function _list_connection_usage(account, credentials) { diff --git a/src/util/account_password_hash.js b/src/util/account_password_hash.js new file mode 100644 index 0000000000..8d33b5b9e7 --- /dev/null +++ b/src/util/account_password_hash.js @@ -0,0 +1,36 @@ +/* Copyright (C) 2024 NooBaa */ +'use strict'; + +const CRYPTO_SALT_BUFFER_SIZE = 8; +const CRYPTO_SALT_KEY_LENGTH = 32; +const CRYPTO_PBKDF2_ITERATIONS = 100; + +const crypto = require('crypto'); + +function create_node_password_hash(password) { + return new Promise((resolve, reject) => { + const salt = crypto.randomBytes(CRYPTO_SALT_BUFFER_SIZE).toString('hex'); + crypto.pbkdf2(password, salt, CRYPTO_PBKDF2_ITERATIONS, CRYPTO_SALT_KEY_LENGTH, 'sha512', (err, derivedKey) => { + if (err) { + reject(err); + } + resolve(salt + ':' + derivedKey.toString('hex')); + }); + }); +} + +function verify_node_password_hash(password, hashedPassword) { + return new Promise((resolve, reject) => { + const [salt, key] = hashedPassword.split(':'); + crypto.pbkdf2(password, salt, CRYPTO_PBKDF2_ITERATIONS, CRYPTO_SALT_KEY_LENGTH, 'sha512', (err, derivedKey) => { + if (err) { + reject(err); + } + resolve(key === derivedKey.toString('hex')); + }); + }); +} + + +exports.create_node_password_hash = create_node_password_hash; +exports.verify_node_password_hash = verify_node_password_hash;