Coverage Report

Created: 2021-05-04 09:02

/src/botan/src/lib/pbkdf/argon2/argon2pwhash.cpp
Line
Count
Source (jump to first uncovered line)
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
0
   {
20
0
   BOTAN_ARG_CHECK(m_p >= 1 && m_p <= 128, "Invalid Argon2 threads parameter");
21
0
   BOTAN_ARG_CHECK(m_M >= 8*m_p && m_M <= 8192*1024, "Invalid Argon2 M parameter");
22
0
   BOTAN_ARG_CHECK(m_t >= 1, "Invalid Argon2 t parameter");
23
0
   }
24
25
void Argon2::derive_key(uint8_t output[], size_t output_len,
26
                        const char* password, size_t password_len,
27
                        const uint8_t salt[], size_t salt_len) const
28
0
   {
29
0
   argon2(output, output_len,
30
0
          password, password_len,
31
0
          salt, salt_len,
32
0
          nullptr, 0,
33
0
          nullptr, 0);
34
0
   }
35
36
void Argon2::derive_key(uint8_t output[], size_t output_len,
37
                        const char* password, size_t password_len,
38
                        const uint8_t salt[], size_t salt_len,
39
                        const uint8_t ad[], size_t ad_len,
40
                        const uint8_t key[], size_t key_len) const
41
0
   {
42
0
   argon2(output, output_len,
43
0
          password, password_len,
44
0
          salt, salt_len,
45
0
          key, key_len,
46
0
          ad, ad_len);
47
0
   }
48
49
namespace {
50
51
std::string argon2_family_name(uint8_t f)
52
0
   {
53
0
   switch(f)
54
0
      {
55
0
      case 0:
56
0
         return "Argon2d";
57
0
      case 1:
58
0
         return "Argon2i";
59
0
      case 2:
60
0
         return "Argon2id";
61
0
      default:
62
0
         throw Invalid_Argument("Unknown Argon2 parameter");
63
0
      }
64
0
   }
65
66
}
67
68
std::string Argon2::to_string() const
69
0
   {
70
0
   return argon2_family_name(m_family) + "(" +
71
0
      std::to_string(m_M) + "," +
72
0
      std::to_string(m_t) + "," +
73
0
      std::to_string(m_p) + ")";
74
0
  }
75
76
Argon2_Family::Argon2_Family(uint8_t family) : m_family(family)
77
0
   {
78
0
   if(m_family != 0 && m_family != 1 && m_family != 2)
79
0
      throw Invalid_Argument("Unknown Argon2 family identifier");
80
0
   }
81
82
std::string Argon2_Family::name() const
83
0
   {
84
0
   return argon2_family_name(m_family);
85
0
   }
86
87
std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
88
                                                  std::chrono::milliseconds msec,
89
                                                  size_t max_memory) const
90
0
   {
91
0
   const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024;
92
93
   // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
94
   // costs for larger params. Default is 36 MiB, or use 128 for long times.
95
0
   const size_t tune_M = (msec >= std::chrono::milliseconds(500) ? 128 : 36) * 1024;
96
0
   const size_t p = 1;
97
0
   size_t t = 1;
98
99
0
   Timer timer("Argon2");
100
0
   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
101
102
0
   auto pwhash = this->from_params(tune_M, t, p);
103
104
0
   timer.run_until_elapsed(tune_time, [&]() {
105
0
      uint8_t output[64] = { 0 };
106
0
      pwhash->derive_key(output, sizeof(output),
107
0
                         "test", 4,
108
0
                         nullptr, 0);
109
0
      });
110
111
0
   if(timer.events() == 0 || timer.value() == 0)
112
0
      return default_params();
113
114
0
   size_t M = 4*1024;
115
116
0
   const uint64_t measured_time = timer.value() / (timer.events() * (tune_M / M));
117
118
0
   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
119
120
   /*
121
   * Argon2 scaling rules:
122
   * k*M, k*t, k*p all increase cost by about k
123
   *
124
   * Since we don't even take advantage of p > 1, we prefer increasing
125
   * t or M instead.
126
   *
127
   * If possible to increase M, prefer that.
128
   */
129
130
0
   uint64_t est_nsec = measured_time;
131
132
0
   if(est_nsec < target_nsec && M < max_kib)
133
0
      {
134
0
      const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
135
0
      const uint64_t mem_headroom = max_kib / M;
136
137
0
      const uint64_t M_mult = std::min(desired_cost_increase, mem_headroom);
138
0
      M *= static_cast<size_t>(M_mult);
139
0
      est_nsec *= M_mult;
140
0
      }
141
142
0
   if(est_nsec < target_nsec)
143
0
      {
144
0
      const uint64_t desired_cost_increase = (target_nsec + est_nsec - 1) / est_nsec;
145
0
      t *= static_cast<size_t>(desired_cost_increase);
146
0
      }
147
148
0
   return this->from_params(M, t, p);
149
0
   }
150
151
std::unique_ptr<PasswordHash> Argon2_Family::default_params() const
152
0
   {
153
0
   return this->from_params(128*1024, 1, 1);
154
0
   }
155
156
std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const
157
0
   {
158
   /*
159
   These choices are arbitrary, but should not change in future
160
   releases since they will break applications expecting deterministic
161
   mapping from iteration count to params
162
   */
163
0
   const size_t M = iter;
164
0
   const size_t t = 1;
165
0
   const size_t p = 1;
166
0
   return this->from_params(M, t, p);
167
0
   }
168
169
std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const
170
0
   {
171
0
   return std::make_unique<Argon2>(m_family, M, t, p);
172
0
   }
173
174
}