8#include <botan/passhash9.h>
10#include <botan/base64.h>
11#include <botan/pbkdf2.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/loadstor.h>
20const std::string MAGIC_PREFIX =
"$9$";
22const size_t WORKFACTOR_BYTES = 2;
23const size_t ALGID_BYTES = 1;
24const size_t SALT_BYTES = 12;
25const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24;
27const size_t WORK_FACTOR_SCALE = 10000;
29std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id) {
32 }
else if(alg_id == 1) {
34 }
else if(alg_id == 2) {
36 }
else if(alg_id == 3) {
38 }
else if(alg_id == 4) {
50 BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512,
"Invalid Passhash9 work factor");
52 auto prf = get_pbkdf_prf(alg_id);
55 throw Invalid_Argument(
"Passhash9: Algorithm id " + std::to_string(alg_id) +
" is not defined");
63 const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
66 blob.push_back(alg_id);
70 blob += kdf.
derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, salt.data(), salt.size(), kdf_iterations).
bits_of();
76 const size_t BINARY_LENGTH = ALGID_BYTES + WORKFACTOR_BYTES + PASSHASH9_PBKDF_OUTPUT_LEN + SALT_BYTES;
78 const size_t BASE64_LENGTH = MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
80 if(hash.size() != BASE64_LENGTH) {
84 for(
size_t i = 0; i != MAGIC_PREFIX.size(); ++i) {
85 if(hash[i] != MAGIC_PREFIX[i]) {
92 if(bin.size() != BINARY_LENGTH) {
96 uint8_t alg_id = bin[0];
101 if(work_factor == 0) {
105 if(work_factor > 512) {
106 throw Invalid_Argument(
"Requested passhash9 work factor " + std::to_string(work_factor) +
" is too large");
109 const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
111 auto pbkdf_prf = get_pbkdf_prf(alg_id);
120 kdf.
derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations)
123 const uint8_t* hashbytes = &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES];
125 return CT::is_equal(cmp.data(), hashbytes, PASSHASH9_PBKDF_OUTPUT_LEN).as_bool();
129 if(get_pbkdf_prf(alg_id)) {
#define BOTAN_ARG_CHECK(expr, msg)
static std::unique_ptr< MessageAuthenticationCode > create(std::string_view algo_spec, std::string_view provider="")
secure_vector< uint8_t > bits_of() const
OctetString derive_key(size_t out_len, std::string_view passphrase, const uint8_t salt[], size_t salt_len, size_t iterations) const
void randomize(std::span< uint8_t > output)
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
constexpr uint8_t get_byte(T input)
std::string generate_passhash9(std::string_view pass, RandomNumberGenerator &rng, uint16_t work_factor, uint8_t alg_id)
bool check_passhash9(std::string_view pass, std::string_view hash)
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)
bool is_passhash9_alg_supported(uint8_t alg_id)
std::vector< T, secure_allocator< T > > secure_vector
constexpr auto load_be(ParamTs &&... params)