Botan  2.11.0
Crypto and TLS for C++11
argon2pwhash.cpp
Go to the documentation of this file.
1 /**
2 * (C) 2019 Jack Lloyd
3 *
4 * Botan is released under the Simplified BSD License (see license.txt)
5 */
6 
7 #include <botan/argon2.h>
8 #include <botan/exceptn.h>
9 #include <botan/internal/timer.h>
10 #include <algorithm>
11 
12 namespace Botan {
13 
14 Argon2::Argon2(uint8_t family, size_t M, size_t t, size_t p) :
15  m_family(family),
16  m_M(M),
17  m_t(t),
18  m_p(p)
19  {}
20 
21 void Argon2::derive_key(uint8_t output[], size_t output_len,
22  const char* password, size_t password_len,
23  const uint8_t salt[], size_t salt_len) const
24  {
25  argon2(output, output_len,
26  password, password_len,
27  salt, salt_len,
28  nullptr, 0,
29  nullptr, 0,
30  m_family, m_p, m_M, m_t);
31  }
32 
33 namespace {
34 
35 std::string argon2_family_name(uint8_t f)
36  {
37  switch(f)
38  {
39  case 0:
40  return "Argon2d";
41  case 1:
42  return "Argon2i";
43  case 2:
44  return "Argon2id";
45  default:
46  throw Invalid_Argument("Unknown Argon2 parameter");
47  }
48  }
49 
50 }
51 
52 std::string Argon2::to_string() const
53  {
54  return argon2_family_name(m_family) + "(" +
55  std::to_string(m_M) + "," +
56  std::to_string(m_t) + "," +
57  std::to_string(m_p) + ")";
58  }
59 
60 Argon2_Family::Argon2_Family(uint8_t family) : m_family(family)
61  {
62  if(m_family != 0 && m_family != 1 && m_family != 2)
63  throw Invalid_Argument("Unknown Argon2 family identifier");
64  }
65 
66 std::string Argon2_Family::name() const
67  {
68  return argon2_family_name(m_family);
69  }
70 
71 std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
72  std::chrono::milliseconds msec,
73  size_t max_memory) const
74  {
75  const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024;
76 
77  // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
78  // costs for larger params
79  const size_t tune_M = 256*1024; // in KiB
80  const size_t p = 1;
81  size_t t = 1;
82 
83  Timer timer("Argon2");
84  const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
85 
86  timer.run_until_elapsed(tune_time, [&]() {
87  uint8_t output[32] = { 0 };
88  argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, tune_M, t);
89  });
90 
91  if(timer.events() == 0 || timer.value() == 0)
92  return default_params();
93 
94  size_t M = 4*1024;
95 
96  const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M));
97 
98  const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
99 
100  /*
101  * Argon2 scaling rules:
102  * k*M, k*t, k*p all increase cost by about k
103  *
104  * Since we don't even take advantage of p > 1, we prefer increasing
105  * t or M instead.
106  *
107  * If possible to increase M, prefer that.
108  */
109 
110  uint64_t est_nsec = measured_time;
111 
112  if(est_nsec < target_nsec && M < max_kib)
113  {
114  const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
115  const uint64_t mem_headroom = max_kib / M;
116 
117  const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom);
118  M *= static_cast<size_t>(M_mult);
119  est_nsec *= M_mult;
120  }
121 
122  if(est_nsec < target_nsec)
123  {
124  const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
125  t *= static_cast<size_t>(desired_cost_increase);
126  }
127 
128  return this->from_params(M, t, p);
129  }
130 
131 std::unique_ptr<PasswordHash> Argon2_Family::default_params() const
132  {
133  return this->from_params(128*1024, 1, 1);
134  }
135 
136 std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const
137  {
138  /*
139  These choices are arbitrary, but should not change in future
140  releases since they will break applications expecting deterministic
141  mapping from iteration count to params
142  */
143  const size_t M = iter;
144  const size_t t = 1;
145  const size_t p = 1;
146  return this->from_params(M, t, p);
147  }
148 
149 std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const
150  {
151  return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p));
152  }
153 
154 }
std::unique_ptr< PasswordHash > tune(size_t output_length, std::chrono::milliseconds msec, size_t max_memory) const override
std::unique_ptr< PasswordHash > from_iterations(size_t iter) const override
uint64_t events() const
Definition: timer.h:119
std::string name() const override
std::string to_string(const BER_Object &obj)
Definition: asn1_obj.cpp:213
std::string to_string() const override
std::unique_ptr< PasswordHash > default_params() const override
Argon2_Family(uint8_t family)
size_t salt_len
Definition: x509_obj.cpp:26
Definition: alg_id.cpp:13
std::unique_ptr< PasswordHash > from_params(size_t M, size_t t, size_t p) const override
Argon2(uint8_t family, size_t M, size_t t, size_t p)
void argon2(uint8_t output[], size_t output_len, const char *password, size_t password_len, const uint8_t salt[], size_t salt_len, const uint8_t key[], size_t key_len, const uint8_t ad[], size_t ad_len, uint8_t mode, size_t threads, size_t M, size_t t)
Definition: argon2.cpp:381
uint64_t value() const
Definition: timer.h:90
void derive_key(uint8_t out[], size_t out_len, const char *password, size_t password_len, const uint8_t salt[], size_t salt_len) const override
void run_until_elapsed(std::chrono::milliseconds msec, F f)
Definition: timer.h:82