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