8#include <botan/bcrypt.h>
10#include <botan/internal/blowfish.h>
11#include <botan/base64.h>
12#include <botan/internal/parsing.h>
13#include <botan/internal/ct_utils.h>
20uint8_t base64_to_bcrypt_encoding(uint8_t c)
35 ret = is_ab.select(c -
'a' +
'Y', ret);
36 ret = is_cz.select(c - 2, ret);
37 ret = is_CZ.select(c - 2, ret);
38 ret = is_01.select(c -
'0' +
'y', ret);
39 ret = is_29.select(c -
'2' +
'0', ret);
40 ret = is_A.select(
'.', ret);
41 ret = is_B.select(
'/', ret);
42 ret = is_plus.select(
'8', ret);
43 ret = is_slash.select(
'9', ret);
48uint8_t bcrypt_encoding_to_base64(uint8_t c)
63 ret = is_ax.select(c -
'a' +
'c', ret);
64 ret = is_yz.select(c -
'y' +
'0', ret);
65 ret = is_AX.select(c -
'A' +
'C', ret);
66 ret = is_YZ.select(c -
'Y' +
'a', ret);
67 ret = is_07.select(c -
'0' +
'2', ret);
68 ret = is_8.select(
'+', ret);
69 ret = is_9.select(
'/', ret);
70 ret = is_dot.select(
'A', ret);
71 ret = is_slash.select(
'B', ret);
76std::string bcrypt_base64_encode(
const uint8_t input[],
size_t length)
80 while(!b64.empty() && b64[b64.size()-1] ==
'=')
81 b64 = b64.substr(0, b64.size() - 1);
83 for(
size_t i = 0; i != b64.size(); ++i)
84 b64[i] =
static_cast<char>(base64_to_bcrypt_encoding(
static_cast<uint8_t
>(b64[i])));;
89std::vector<uint8_t> bcrypt_base64_decode(std::string input)
91 for(
size_t i = 0; i != input.size(); ++i)
92 input[i] = bcrypt_encoding_to_base64(
static_cast<uint8_t
>(input[i]));
97std::string make_bcrypt(
const std::string& pass,
98 const std::vector<uint8_t>& salt,
109 "Invalid bcrypt work factor");
111 alignas(64)
static const uint8_t BCRYPT_MAGIC[8*3] = {
112 0x4F, 0x72, 0x70, 0x68, 0x65, 0x61, 0x6E, 0x42,
113 0x65, 0x68, 0x6F, 0x6C, 0x64, 0x65, 0x72, 0x53,
114 0x63, 0x72, 0x79, 0x44, 0x6F, 0x75, 0x62, 0x74
126 std::vector<uint8_t> ctext(BCRYPT_MAGIC, BCRYPT_MAGIC + 8*3);
128 for(
size_t i = 0; i != 64; ++i)
129 blowfish.encrypt_n(ctext.data(), ctext.data(), 3);
131 std::string salt_b64 = bcrypt_base64_encode(salt.data(), salt.size());
134 if(work_factor_str.length() == 1)
135 work_factor_str =
"0" + work_factor_str;
137 return "$2" + std::string(1, version) +
"$" + work_factor_str +
138 "$" + salt_b64.substr(0, 22) +
139 bcrypt_base64_encode(ctext.data(), ctext.size() - 1);
146 uint16_t work_factor,
154 if(version !=
'a' && version !=
'b' && version !=
'y')
155 throw Invalid_Argument(
"Unknown bcrypt version '" + std::string(1, version) +
"'");
157 std::vector<uint8_t> salt;
159 return make_bcrypt(pass, salt, work_factor, version);
164 if(
hash.size() != 60 ||
170 const char bcrypt_version =
hash[2];
172 if(bcrypt_version !=
'a' && bcrypt_version !=
'b' && bcrypt_version !=
'y')
179 const std::vector<uint8_t> salt = bcrypt_base64_decode(
hash.substr(7, 22));
180 if(salt.size() != 16)
183 const std::string compare = make_bcrypt(pass, salt, workfactor, bcrypt_version);
185 return same_mem(
hash.data(), compare.data(), compare.size());
#define BOTAN_ARG_CHECK(expr, msg)
static Mask< T > is_equal(T x, T y)
static Mask< T > is_within_range(T v, T l, T u)
secure_vector< uint8_t > random_vec(size_t bytes)
std::string to_string(const BER_Object &obj)
std::string generate_bcrypt(const std::string &pass, RandomNumberGenerator &rng, uint16_t work_factor, char version)
uint16_t to_uint16(const std::string &str)
size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t &input_consumed, bool final_inputs)
size_t base64_decode(uint8_t out[], const char in[], size_t input_length, size_t &input_consumed, bool final_inputs, bool ignore_ws)
std::vector< T > unlock(const secure_vector< T > &in)
bool check_bcrypt(const std::string &pass, const std::string &hash)
bool same_mem(const T *p1, const T *p2, size_t n)
const uint8_t * cast_char_ptr_to_uint8(const char *s)