aboutsummaryrefslogtreecommitdiffstats
path: root/src/lib/pbkdf/argon2/argon2pwhash.cpp
blob: c31c9087874f0e4a872da3fa41fb54c6073c41e0 (plain)
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
/**
* (C) 2019 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include <botan/argon2.h>
#include <botan/exceptn.h>
#include <botan/internal/timer.h>

namespace Botan {

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)
   {}

void Argon2::derive_key(uint8_t output[], size_t output_len,
                        const char* password, const size_t password_len,
                        const uint8_t salt[], size_t salt_len) const
   {
   argon2(output, output_len,
          password, password_len,
          salt, salt_len,
          nullptr, 0,
          nullptr, 0,
          m_family, m_p, m_M, m_t);
   }

namespace {

std::string argon2_family_name(uint8_t f)
   {
   switch(f)
      {
      case 0:
         return "Argon2d";
      case 1:
         return "Argon2i";
      case 2:
         return "Argon2id";
      default:
         throw Invalid_Argument("Unknown Argon2 parameter");
      }
   }

}

std::string Argon2::to_string() const
   {
   return argon2_family_name(m_family) + "(" +
      std::to_string(m_M) + "," +
      std::to_string(m_t) + "," +
      std::to_string(m_p) + ")";
  }

Argon2_Family::Argon2_Family(uint8_t family) : m_family(family)
   {
   if(m_family != 0 && m_family != 1 && m_family != 2)
      throw Invalid_Argument("Unknown Argon2 family identifier");
   }

std::string Argon2_Family::name() const
   {
   return argon2_family_name(m_family);
   }

std::unique_ptr<PasswordHash> Argon2_Family::tune(size_t /*output_length*/,
                                                  std::chrono::milliseconds msec,
                                                  size_t max_memory) const
   {
   const size_t max_kib = (max_memory == 0) ? 256*1024 : max_memory*1024;

   // Tune with a large memory otherwise we measure cache vs RAM speeds and underestimate
   // costs for larger params
   size_t M = 32*1024; // in KiB
   size_t p = 1;
   size_t t = 1;

   Timer timer("Argon2");
   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;

   timer.run_until_elapsed(tune_time, [&]() {
      uint8_t output[32] = { 0 };
      argon2(output, sizeof(output), "test", 4, nullptr, 0, nullptr, 0, nullptr, 0, m_family, p, M, t);
      });

   if(timer.events() == 0 || timer.value() == 0)
      return default_params();

   const uint64_t measured_time = timer.value() / timer.events();

   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);

   /*
   * Argon2 scaling rules:
   * k*M, k*t, k*p all increase cost by about k
   *
   * Since we don't even take advantage of p > 1, we prefer increasing
   * t or M instead.
   *
   * If possible to increase M, prefer that.
   */

   uint64_t est_nsec = measured_time;

   if(est_nsec < target_nsec && M < max_kib)
      {
      const size_t desired_cost_increase = ((target_nsec + est_nsec - 1) / est_nsec);
      const size_t mem_headroom = max_kib / M;

      const size_t M_mult = std::min(desired_cost_increase, mem_headroom);
      M *= M_mult;
      est_nsec *= M_mult;
      }

   if(est_nsec < target_nsec)
      {
      const size_t desired_cost_increase = ((target_nsec + est_nsec - 1) / est_nsec);
      t *= desired_cost_increase;
      }

   return this->from_params(M, t, p);
   }

std::unique_ptr<PasswordHash> Argon2_Family::default_params() const
   {
   return this->from_params(64*1024*1024, 3, 4);
   }

std::unique_ptr<PasswordHash> Argon2_Family::from_iterations(size_t iter) const
   {
   /*
   These choices are arbitrary, but should not change in future
   releases since they will break applications expecting deterministic
   mapping from iteration count to params
   */
   const size_t M = iter;
   const size_t t = 3;
   const size_t p = 1;
   return this->from_params(M, t, p);
   }

std::unique_ptr<PasswordHash> Argon2_Family::from_params(size_t M, size_t t, size_t p) const
   {
   return std::unique_ptr<PasswordHash>(new Argon2(m_family, M, t, p));
   }

}