Botan 3.0.0
Crypto and TLS for C&
passhash9.cpp
Go to the documentation of this file.
1/*
2* Passhash9 Password Hashing
3* (C) 2010 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/passhash9.h>
9#include <botan/rng.h>
10#include <botan/internal/loadstor.h>
11#include <botan/pbkdf2.h>
12#include <botan/base64.h>
13
14namespace Botan {
15
16namespace {
17
18const std::string MAGIC_PREFIX = "$9$";
19
20const size_t WORKFACTOR_BYTES = 2;
21const size_t ALGID_BYTES = 1;
22const size_t SALT_BYTES = 12; // 96 bits of salt
23const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
24
25const size_t WORK_FACTOR_SCALE = 10000;
26
27std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id)
28 {
29 if(alg_id == 0)
30 return MessageAuthenticationCode::create("HMAC(SHA-1)");
31 else if(alg_id == 1)
32 return MessageAuthenticationCode::create("HMAC(SHA-256)");
33 else if(alg_id == 2)
34 return MessageAuthenticationCode::create("CMAC(Blowfish)");
35 else if(alg_id == 3)
36 return MessageAuthenticationCode::create("HMAC(SHA-384)");
37 else if(alg_id == 4)
38 return MessageAuthenticationCode::create("HMAC(SHA-512)");
39 return nullptr;
40 }
41
42}
43
44std::string generate_passhash9(std::string_view pass,
46 uint16_t work_factor,
47 uint8_t alg_id)
48 {
49 BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512, "Invalid Passhash9 work factor");
50
51 auto prf = get_pbkdf_prf(alg_id);
52
53 if(!prf)
54 throw Invalid_Argument("Passhash9: Algorithm id " +
55 std::to_string(alg_id) +
56 " is not defined");
57
58 PKCS5_PBKDF2 kdf(std::move(prf));
59
60 secure_vector<uint8_t> salt(SALT_BYTES);
61 rng.randomize(salt.data(), salt.size());
62
63 const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
64
66 blob.push_back(alg_id);
67 blob.push_back(get_byte<0>(work_factor));
68 blob.push_back(get_byte<1>(work_factor));
69 blob += salt;
70 blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN,
71 pass,
72 salt.data(), salt.size(),
73 kdf_iterations).bits_of();
74
75 return MAGIC_PREFIX + base64_encode(blob);
76 }
77
78bool check_passhash9(std::string_view pass, std::string_view hash)
79 {
80 const size_t BINARY_LENGTH =
81 ALGID_BYTES +
82 WORKFACTOR_BYTES +
83 PASSHASH9_PBKDF_OUTPUT_LEN +
84 SALT_BYTES;
85
86 const size_t BASE64_LENGTH =
87 MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
88
89 if(hash.size() != BASE64_LENGTH)
90 return false;
91
92 for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
93 if(hash[i] != MAGIC_PREFIX[i])
94 return false;
95
96 secure_vector<uint8_t> bin = base64_decode(hash.data() + MAGIC_PREFIX.size());
97
98 if(bin.size() != BINARY_LENGTH)
99 return false;
100
101 uint8_t alg_id = bin[0];
102
103 const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
104
105 // Bug in the format, bad states shouldn't be representable, but are...
106 if(work_factor == 0)
107 return false;
108
109 if(work_factor > 512)
110 throw Invalid_Argument("Requested passhash9 work factor " +
111 std::to_string(work_factor) + " is too large");
112
113 const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
114
115 auto pbkdf_prf = get_pbkdf_prf(alg_id);
116
117 if(!pbkdf_prf)
118 return false; // unknown algorithm, reject
119
120 PKCS5_PBKDF2 kdf(std::move(pbkdf_prf));
121
123 PASSHASH9_PBKDF_OUTPUT_LEN,
124 pass,
125 &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
126 kdf_iterations).bits_of();
127
128 return constant_time_compare(cmp.data(),
129 &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
130 PASSHASH9_PBKDF_OUTPUT_LEN);
131 }
132
133bool is_passhash9_alg_supported(uint8_t alg_id)
134 {
135 if (get_pbkdf_prf(alg_id))
136 {
137 return true;
138 }
139 return false;
140 }
141
142}
#define BOTAN_ARG_CHECK(expr, msg)
Definition: assert.h:36
static std::unique_ptr< MessageAuthenticationCode > create(std::string_view algo_spec, std::string_view provider="")
Definition: mac.cpp:46
secure_vector< uint8_t > bits_of() const
Definition: symkey.h:35
OctetString derive_key(size_t out_len, std::string_view passphrase, const uint8_t salt[], size_t salt_len, size_t iterations) const
Definition: pbkdf.h:175
void randomize(std::span< uint8_t > output)
Definition: rng.h:53
Definition: alg_id.cpp:12
std::string generate_passhash9(std::string_view pass, RandomNumberGenerator &rng, uint16_t work_factor, uint8_t alg_id)
Definition: passhash9.cpp:44
bool check_passhash9(std::string_view pass, std::string_view hash)
Definition: passhash9.cpp:78
size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t &input_consumed, bool final_inputs)
Definition: base64.cpp:178
size_t base64_decode(uint8_t out[], const char in[], size_t input_length, size_t &input_consumed, bool final_inputs, bool ignore_ws)
Definition: base64.cpp:193
bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.h:82
bool is_passhash9_alg_supported(uint8_t alg_id)
Definition: passhash9.cpp:133
constexpr uint16_t load_be< uint16_t >(const uint8_t in[], size_t off)
Definition: loadstor.h:150
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:64