1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
|
/*
* Passhash9 Password Hashing
* (C) 2010 Jack Lloyd
*
* Distributed under the terms of the Botan license
*/
#include <botan/passhash9.h>
#include <botan/loadstor.h>
#include <botan/libstate.h>
#include <botan/pbkdf2.h>
#include <botan/base64.h>
#include <botan/pipe.h>
namespace Botan {
namespace {
const std::string MAGIC_PREFIX = "$9$";
const u32bit WORKFACTOR_BYTES = 2;
const u32bit ALGID_BYTES = 1;
const u32bit SALT_BYTES = 12; // 96 bits of salt
const u32bit PBKDF_OUTPUT_LEN = 24; // 192 bits output
const u32bit WORK_FACTOR_SCALE = 10000;
MessageAuthenticationCode* get_pbkdf_prf(byte alg_id)
{
Algorithm_Factory& af = global_state().algorithm_factory();
try
{
if(alg_id == 0)
return af.make_mac("HMAC(SHA-1)");
}
catch(Algorithm_Not_Found) {}
return 0;
}
std::pair<byte, MessageAuthenticationCode*> choose_pbkdf_prf()
{
for(byte alg_id = 0; alg_id != 255; ++alg_id)
{
MessageAuthenticationCode* prf = get_pbkdf_prf(alg_id);
if(prf)
return std::make_pair(alg_id, prf);
}
throw Internal_Error("Passhash9: No PRF available");
}
}
std::string generate_passhash9(const std::string& pass,
RandomNumberGenerator& rng,
u16bit work_factor)
{
std::pair<byte, MessageAuthenticationCode*> prf = choose_pbkdf_prf();
byte alg_id = prf.first;
PKCS5_PBKDF2 kdf(prf.second); // takes ownership of pointer
SecureVector<byte> salt(SALT_BYTES);
rng.randomize(&salt[0], salt.size());
u32bit kdf_iterations = WORK_FACTOR_SCALE * work_factor;
SecureVector<byte> pbkdf2_output =
kdf.derive_key(PBKDF_OUTPUT_LEN, pass,
&salt[0], salt.size(),
kdf_iterations).bits_of();
Pipe pipe(new Base64_Encoder);
pipe.start_msg();
pipe.write(alg_id);
pipe.write(get_byte(0, work_factor));
pipe.write(get_byte(1, work_factor));
pipe.write(salt);
pipe.write(pbkdf2_output);
pipe.end_msg();
return MAGIC_PREFIX + pipe.read_all_as_string();
}
bool check_passhash9(const std::string& pass, const std::string& hash)
{
const u32bit BINARY_LENGTH =
(ALGID_BYTES + WORKFACTOR_BYTES + PBKDF_OUTPUT_LEN + SALT_BYTES);
const u32bit BASE64_LENGTH =
MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
if(hash.size() != BASE64_LENGTH)
return false;
for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
if(hash[i] != MAGIC_PREFIX[i])
return false;
Pipe pipe(new Base64_Decoder);
pipe.start_msg();
pipe.write(hash.c_str() + MAGIC_PREFIX.size());
pipe.end_msg();
SecureVector<byte> bin = pipe.read_all();
if(bin.size() != BINARY_LENGTH)
return false;
byte alg_id = bin[0];
u32bit kdf_iterations =
WORK_FACTOR_SCALE * load_be<u16bit>(bin + ALGID_BYTES, 0);
if(kdf_iterations == 0)
return false;
MessageAuthenticationCode* pbkdf_prf = get_pbkdf_prf(alg_id);
if(pbkdf_prf == 0)
return false; // unknown algorithm, reject
PKCS5_PBKDF2 kdf(pbkdf_prf); // takes ownership of pointer
SecureVector<byte> cmp = kdf.derive_key(
PBKDF_OUTPUT_LEN, pass,
&bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
kdf_iterations).bits_of();
return same_mem(cmp.begin(),
bin.begin() + ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES,
PBKDF_OUTPUT_LEN);
}
}
|