Botan 3.3.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
10#include <botan/base64.h>
11#include <botan/pbkdf2.h>
12#include <botan/rng.h>
13#include <botan/internal/ct_utils.h>
14#include <botan/internal/loadstor.h>
15
16namespace Botan {
17
18namespace {
19
20const std::string MAGIC_PREFIX = "$9$";
21
22const size_t WORKFACTOR_BYTES = 2;
23const size_t ALGID_BYTES = 1;
24const size_t SALT_BYTES = 12; // 96 bits of salt
25const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
26
27const size_t WORK_FACTOR_SCALE = 10000;
28
29std::unique_ptr<MessageAuthenticationCode> get_pbkdf_prf(uint8_t alg_id) {
30 if(alg_id == 0) {
31 return MessageAuthenticationCode::create("HMAC(SHA-1)");
32 } else if(alg_id == 1) {
33 return MessageAuthenticationCode::create("HMAC(SHA-256)");
34 } else if(alg_id == 2) {
35 return MessageAuthenticationCode::create("CMAC(Blowfish)");
36 } else if(alg_id == 3) {
37 return MessageAuthenticationCode::create("HMAC(SHA-384)");
38 } else if(alg_id == 4) {
39 return MessageAuthenticationCode::create("HMAC(SHA-512)");
40 }
41 return nullptr;
42}
43
44} // namespace
45
46std::string generate_passhash9(std::string_view pass,
48 uint16_t work_factor,
49 uint8_t alg_id) {
50 BOTAN_ARG_CHECK(work_factor > 0 && work_factor < 512, "Invalid Passhash9 work factor");
51
52 auto prf = get_pbkdf_prf(alg_id);
53
54 if(!prf) {
55 throw Invalid_Argument("Passhash9: Algorithm id " + std::to_string(alg_id) + " is not defined");
56 }
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, pass, salt.data(), salt.size(), kdf_iterations).bits_of();
71
72 return MAGIC_PREFIX + base64_encode(blob);
73}
74
75bool check_passhash9(std::string_view pass, std::string_view hash) {
76 const size_t BINARY_LENGTH = ALGID_BYTES + WORKFACTOR_BYTES + PASSHASH9_PBKDF_OUTPUT_LEN + SALT_BYTES;
77
78 const size_t BASE64_LENGTH = MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
79
80 if(hash.size() != BASE64_LENGTH) {
81 return false;
82 }
83
84 for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i) {
85 if(hash[i] != MAGIC_PREFIX[i]) {
86 return false;
87 }
88 }
89
90 secure_vector<uint8_t> bin = base64_decode(hash.data() + MAGIC_PREFIX.size());
91
92 if(bin.size() != BINARY_LENGTH) {
93 return false;
94 }
95
96 uint8_t alg_id = bin[0];
97
98 const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
99
100 // Bug in the format, bad states shouldn't be representable, but are...
101 if(work_factor == 0) {
102 return false;
103 }
104
105 if(work_factor > 512) {
106 throw Invalid_Argument("Requested passhash9 work factor " + std::to_string(work_factor) + " is too large");
107 }
108
109 const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
110
111 auto pbkdf_prf = get_pbkdf_prf(alg_id);
112
113 if(!pbkdf_prf) {
114 return false; // unknown algorithm, reject
115 }
116
117 PKCS5_PBKDF2 kdf(std::move(pbkdf_prf));
118
120 kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN, pass, &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES, kdf_iterations)
121 .bits_of();
122
123 const uint8_t* hashbytes = &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES];
124
125 return CT::is_equal(cmp.data(), hashbytes, PASSHASH9_PBKDF_OUTPUT_LEN).as_bool();
126}
127
128bool is_passhash9_alg_supported(uint8_t alg_id) {
129 if(get_pbkdf_prf(alg_id)) {
130 return true;
131 }
132 return false;
133}
134
135} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:29
static std::unique_ptr< MessageAuthenticationCode > create(std::string_view algo_spec, std::string_view provider="")
Definition mac.cpp:51
secure_vector< uint8_t > bits_of() const
Definition symkey.h:36
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:173
void randomize(std::span< uint8_t > output)
Definition rng.h:52
CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:339
std::string generate_passhash9(std::string_view pass, RandomNumberGenerator &rng, uint16_t work_factor, uint8_t alg_id)
Definition passhash9.cpp:46
bool check_passhash9(std::string_view pass, std::string_view hash)
Definition passhash9.cpp:75
size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t &input_consumed, bool final_inputs)
Definition base64.cpp:146
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:154
bool is_passhash9_alg_supported(uint8_t alg_id)
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:61