Skip to content

Instantly share code, notes, and snippets.

@marty1885
Created March 15, 2021 00:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save marty1885/6615102db63ba879ee51b89537e332f0 to your computer and use it in GitHub Desktop.
Save marty1885/6615102db63ba879ee51b89537e332f0 to your computer and use it in GitHub Desktop.
Cryptographic (password hashing) library for Drogon
#include "crypto.hpp"
#include <regex>
#include <bsd/stdlib.h>
#include <botan/argon2.h>
#include <botan/system_rng.h>
static Botan::System_RNG rng;
uint32_t secureRandom(uint32_t lower, uint32_t upper)
{
return lower+arc4random_uniform(upper-lower);
}
std::string secureRandomString(size_t length)
{
const std::string_view alphabet = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ";
std::string result;
result.resize(length);
for(auto& ch : result)
ch = alphabet[secureRandom(0, alphabet.size())];
return result;
}
static std::tuple<std::string, std::string, std::string> parse_passwdhash(const std::string& str)
{
auto parts = drogon::utils::splitString(str, ":", true);
if(parts.size() <= 3)
throw std::runtime_error("Bad password hash format");
// Just incase there's a : in the hash
auto hash = parts[2];
for(size_t i=3;i<parts.size();i++)
hash = hash + ":" + parts[i];
return {parts[0], parts[1], hash};
}
std::string passwdhash::argon2(const std::string& passwd, size_t salt_length)
{
auto salt = secureRandomString(salt_length);
auto salted_passwd = salt + passwd;
auto hash = Botan::argon2_generate_pwhash(salted_passwd.c_str(), salted_passwd.size(), rng, 1, 1, 4096);
return "ARGON2:"+salt+":"+salted_passwd;
}
bool passwdhash::argon2_verify(const std::string& passwd, const std::string& passwd_hash)
{
auto [algo, salt, hash] = parse_passwdhash(passwd_hash);
if(algo != "ARGON2")
throw std::runtime_error("Not Argon2");
return Botan::argon2_check_pwhash(passwd.c_str(), passwd.size(), hash);
}
std::string passwdhash::hash(const std::string& passwd, const std::string& algo, size_t salt_length)
{
if(algo == "ARGON2")
return argon2(passwd, salt_length);
throw std::domain_error("Unknown password hash algorithm: " + algo);
}
bool passwdhash::verify(const std::string& passwd, const std::string& passwd_hash)
{
auto [algo, salt, hash] = parse_passwdhash(passwd_hash);
if(algo == "ARGON2")
return argon2_verify(passwd, passwd_hash);
throw std::domain_error("Unknown password hash algorithm: " + algo);
}
#pragma once
#include <string>
#include <cstdint>
#include <limits>
uint32_t secureRandom(uint32_t lower=0, uintmax_t uint32_t=std::numeric_limits<uint32_t>::max());
std::string secureRandomString(size_t length=16);
namespace passwdhash
{
std::string hash(const std::string& passwd, const std::string& algo="ARGON2", size_t salt_length=16);
bool verify(const std::string& passwd, const std::string& passwd_hash);
// NOTE: Expand the selection of algorithms here in the future to comidate new standards
// while maintaining compatibility
std::string argon2(const std::string& passwd, size_t salt_length);
bool argon2_verify(const std::string& passwd, const std::string& passwd_hash);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment