Coverage Report

Created: 2024-11-21 07:00

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