#endif
}
-std::string hashPassword(const std::string& password)
+std::string hashPassword(const std::string& password, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize)
{
#ifdef HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT
std::string result;
result.append(pwhash_prefix);
result.append("ln=");
- result.append(std::to_string(static_cast<uint64_t>(std::log2(CredentialsHolder::s_defaultWorkFactor))));
+ result.append(std::to_string(static_cast<uint64_t>(std::log2(workFactor))));
result.append(",p=");
- result.append(std::to_string(CredentialsHolder::s_defaultParallelFactor));
+ result.append(std::to_string(parallelFactor));
result.append(",r=");
- result.append(std::to_string(CredentialsHolder::s_defaultBlockSize));
+ result.append(std::to_string(blockSize));
result.append("$");
auto salt = generateRandomSalt();
result.append(Base64Encode(salt));
result.append("$");
- auto out = hashPasswordInternal(password, salt, CredentialsHolder::s_defaultWorkFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize);
+ auto out = hashPasswordInternal(password, salt, workFactor, parallelFactor, blockSize);
result.append(Base64Encode(out));
#endif
}
+std::string hashPassword(const std::string& password)
+{
+#ifdef HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT
+ return hashPassword(password, CredentialsHolder::s_defaultWorkFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize);
+#else
+ throw std::runtime_error("Hashing a password requires scrypt support in OpenSSL, and it is not available");
+#endif
+}
+
bool verifyPassword(const std::string& binaryHash, const std::string& salt, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize, const std::string& binaryPassword)
{
#ifdef HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT
#ifdef HAVE_EVP_PKEY_CTX_SET1_SCRYPT_SALT
auto parametersEnd = hash.find('$', pwhash_prefix.size());
if (parametersEnd == std::string::npos || parametersEnd == hash.size()) {
- cerr<<hash<<endl;
throw std::runtime_error("Invalid hashed password format, no parameters");
}
return false;
}
+ size_t parametersSize = parametersEnd - pwhash_prefix.size();
+ /* ln=X,p=Y,r=Z */
+ if (parametersSize < 12) {
+ return false;
+ }
+
auto saltEnd = password.find('$', parametersEnd + 1);
if (saltEnd == std::string::npos || saltEnd == password.size()) {
return false;
};
std::string hashPassword(const std::string& password);
+std::string hashPassword(const std::string& password, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize);
bool verifyPassword(const std::string& hash, const std::string& password);
bool verifyPassword(const std::string& binaryHash, const std::string& salt, uint64_t workFactor, uint64_t parallelFactor, uint64_t blockSize, const std::string& binaryPassword);
bool isPasswordHashed(const std::string& password);
{ "getTLSFrontend", true, "n", "returns the TLS frontend with index n" },
{ "getTLSFrontendCount", true, "", "returns the number of DoT listeners" },
{ "grepq", true, "Netmask|DNS Name|100ms|{\"::1\", \"powerdns.com\", \"100ms\"} [, n]", "shows the last n queries and responses matching the specified client address or range (Netmask), or the specified DNS Name, or slower than 100ms" },
- { "hashPassword", true, "password", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"},
+ { "hashPassword", true, "password [, workFactor]", "Returns a hashed and salted version of the supplied password, usable with 'setWebserverConfig()'"},
{ "HTTPHeaderRule", true, "name, regex", "matches DoH queries with a HTTP header 'name' whose content matches the regular expression 'regex'"},
{ "HTTPPathRegexRule", true, "regex", "matches DoH queries whose HTTP path matches 'regex'"},
{ "HTTPPathRule", true, "path", "matches DoH queries whose HTTP path is an exact match to 'path'"},
}
});
- luaCtx.writeFunction("hashPassword", [](const std::string& password) {
+ luaCtx.writeFunction("hashPassword", [](const std::string& password, boost::optional<uint64_t> workFactor) {
+ if (workFactor) {
+ return hashPassword(password, *workFactor, CredentialsHolder::s_defaultParallelFactor, CredentialsHolder::s_defaultBlockSize);
+ }
return hashPassword(password);
});
Webserver configuration
~~~~~~~~~~~~~~~~~~~~~~~
-.. function:: hashPassword(password)
+.. function:: hashPassword(password [, workFactor])
.. versionadded:: 1.7.0
Hash the supplied password using a random salt, and returns a string that can be used with :func:`setWebserverConfig`.
:param string - password: The password to hash
+ :param int - workFactor: The work factor to use for the hash function (currently scrypt), as a power of two. Default is 1024.
.. function:: webserver(listen_address [, password[, apikey[, custom_headers[, acl]]]])
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_NO_MAIN
+#include <boost/algorithm/string.hpp>
#include <boost/test/unit_test.hpp>
#include "config.h"
BOOST_CHECK(isPasswordHashed(hashed));
BOOST_CHECK(isPasswordHashed(sampleHash));
BOOST_CHECK(!isPasswordHashed(plaintext));
+
+ {
+ // hash password with custom parameters
+ auto customParams = hashPassword(plaintext, 512, 2, 16);
+ // check that the output is OK
+ BOOST_CHECK(boost::starts_with(customParams, "$scrypt$ln=9,p=2,r=16$"));
+ // check that we can verify the password
+ BOOST_CHECK(verifyPassword(customParams, plaintext));
+ }
+
+ // empty
+ BOOST_CHECK(!isPasswordHashed(""));
+ // missing leading $
+ BOOST_CHECK(!isPasswordHashed("scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // unknown algo
+ BOOST_CHECK(!isPasswordHashed("$tcrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // missing parameters
+ BOOST_CHECK(!isPasswordHashed("$scrypt$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // empty parameters
+ BOOST_CHECK(!isPasswordHashed("$scrypt$$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // missing r
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // salt is too short
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$dGVzdA==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // hash is too short
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$c2hvcnQ="));
+ // missing salt
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // missing $ between the salt and hash
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI="));
+ // no hash
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$"));
+ // hash is too long
+ BOOST_CHECK(!isPasswordHashed("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$dGhpcyBpcyBhIHZlcnkgbG9uZyBoYXNoLCBtdWNoIG11Y2ggbG9uZ2VyIHRoYW4gdGhlIG9uZXMgd2UgYXJlIGdlbmVyYXRpbmc="));
+
+ // empty r
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=1,r=$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // too many parameters
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=1,r=8,t=1$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // invalid ln
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=A,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // invalid p
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=p,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // work factor is too large
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=16,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // salt is too long
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=1,r=8$dGhpcyBpcyBhIHZlcnkgbG9uZyBzYWx0$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // invalid b64 salt
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJdrPhI=", plaintext), std::runtime_error);
+ // invalid b64 hash
+ BOOST_CHECK_THROW(verifyPassword("$scrypt$ln=10,p=1,r=8$1GZ10YdmSGtTmKK9jTH85Q==$JHeICW1mUCnTC+nnULDr7QFQ3kRrZ7u12djruJd", plaintext), std::runtime_error);
}
#endif