Botan  2.4.0
Crypto and TLS for C++11
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/loadstor.h>
11 #include <botan/pbkdf2.h>
12 #include <botan/base64.h>
13 
14 namespace Botan {
15 
16 namespace {
17 
18 const std::string MAGIC_PREFIX = "$9$";
19 
20 const size_t WORKFACTOR_BYTES = 2;
21 const size_t ALGID_BYTES = 1;
22 const size_t SALT_BYTES = 12; // 96 bits of salt
23 const size_t PASSHASH9_PBKDF_OUTPUT_LEN = 24; // 192 bits output
24 
25 const size_t WORK_FACTOR_SCALE = 10000;
26 
27 std::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 
44 std::string generate_passhash9(const std::string& pass,
46  uint16_t work_factor,
47  uint8_t alg_id)
48  {
49  std::unique_ptr<MessageAuthenticationCode> prf = get_pbkdf_prf(alg_id);
50 
51  if(!prf)
52  throw Invalid_Argument("Passhash9: Algorithm id " +
53  std::to_string(alg_id) +
54  " is not defined");
55 
56  PKCS5_PBKDF2 kdf(prf.release()); // takes ownership of pointer
57 
58  secure_vector<uint8_t> salt(SALT_BYTES);
59  rng.randomize(salt.data(), salt.size());
60 
61  const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
62 
64  blob.push_back(alg_id);
65  blob.push_back(get_byte(0, work_factor));
66  blob.push_back(get_byte(1, work_factor));
67  blob += salt;
68  blob += kdf.derive_key(PASSHASH9_PBKDF_OUTPUT_LEN,
69  pass,
70  salt.data(), salt.size(),
71  kdf_iterations).bits_of();
72 
73  return MAGIC_PREFIX + base64_encode(blob);
74  }
75 
76 bool check_passhash9(const std::string& pass, const std::string& hash)
77  {
78  const size_t BINARY_LENGTH =
79  ALGID_BYTES +
80  WORKFACTOR_BYTES +
81  PASSHASH9_PBKDF_OUTPUT_LEN +
82  SALT_BYTES;
83 
84  const size_t BASE64_LENGTH =
85  MAGIC_PREFIX.size() + (BINARY_LENGTH * 8) / 6;
86 
87  if(hash.size() != BASE64_LENGTH)
88  return false;
89 
90  for(size_t i = 0; i != MAGIC_PREFIX.size(); ++i)
91  if(hash[i] != MAGIC_PREFIX[i])
92  return false;
93 
94  secure_vector<uint8_t> bin = base64_decode(hash.c_str() + MAGIC_PREFIX.size());
95 
96  if(bin.size() != BINARY_LENGTH)
97  return false;
98 
99  uint8_t alg_id = bin[0];
100 
101  const size_t work_factor = load_be<uint16_t>(&bin[ALGID_BYTES], 0);
102 
103  // Bug in the format, bad states shouldn't be representable, but are...
104  if(work_factor == 0)
105  return false;
106 
107  if(work_factor > 512)
108  throw Invalid_Argument("Requested passhash9 work factor " +
109  std::to_string(work_factor) + " is too large");
110 
111  const size_t kdf_iterations = WORK_FACTOR_SCALE * work_factor;
112 
113  std::unique_ptr<MessageAuthenticationCode> pbkdf_prf = get_pbkdf_prf(alg_id);
114 
115  if(!pbkdf_prf)
116  return false; // unknown algorithm, reject
117 
118  PKCS5_PBKDF2 kdf(pbkdf_prf.release()); // takes ownership of pointer
119 
121  PASSHASH9_PBKDF_OUTPUT_LEN,
122  pass,
123  &bin[ALGID_BYTES + WORKFACTOR_BYTES], SALT_BYTES,
124  kdf_iterations).bits_of();
125 
126  return constant_time_compare(cmp.data(),
127  &bin[ALGID_BYTES + WORKFACTOR_BYTES + SALT_BYTES],
128  PASSHASH9_PBKDF_OUTPUT_LEN);
129  }
130 
131 bool is_passhash9_alg_supported(uint8_t alg_id)
132  {
133  if (get_pbkdf_prf(alg_id))
134  {
135  return true;
136  }
137  return false;
138  }
139 
140 }
OctetString derive_key(size_t out_len, const std::string &passphrase, const uint8_t salt[], size_t salt_len, size_t iterations) const
Definition: pbkdf.h:158
static std::unique_ptr< MessageAuthenticationCode > create(const std::string &algo_spec, const std::string &provider="")
Definition: mac.cpp:45
virtual void randomize(uint8_t output[], size_t length)=0
uint16_t load_be< uint16_t >(const uint8_t in[], size_t off)
Definition: loadstor.h:137
bool constant_time_compare(const uint8_t x[], const uint8_t y[], size_t len)
Definition: mem_ops.cpp:44
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:108
size_t base64_encode(char out[], const uint8_t in[], size_t input_length, size_t &input_consumed, bool final_inputs)
Definition: base64.cpp:35
std::string generate_passhash9(const std::string &pass, RandomNumberGenerator &rng, uint16_t work_factor, uint8_t alg_id)
Definition: passhash9.cpp:44
size_t base64_decode(uint8_t output[], const char input[], size_t input_length, size_t &input_consumed, bool final_inputs, bool ignore_ws)
Definition: base64.cpp:100
bool check_passhash9(const std::string &pass, const std::string &hash)
Definition: passhash9.cpp:76
Definition: alg_id.cpp:13
uint8_t get_byte(size_t byte_num, T input)
Definition: loadstor.h:39
bool is_passhash9_alg_supported(uint8_t alg_id)
Definition: passhash9.cpp:131
std::vector< T, secure_allocator< T > > secure_vector
Definition: secmem.h:88
secure_vector< uint8_t > bits_of() const
Definition: symkey.h:31
MechanismType hash