Coverage Report

Created: 2023-02-22 06:14

/src/botan/src/lib/pbkdf/scrypt/scrypt.cpp
Line
Count
Source (jump to first uncovered line)
1
/**
2
* (C) 2018 Jack Lloyd
3
* (C) 2018 Ribose Inc
4
*
5
* Botan is released under the Simplified BSD License (see license.txt)
6
*/
7
8
#include <botan/scrypt.h>
9
#include <botan/pbkdf2.h>
10
#include <botan/exceptn.h>
11
#include <botan/internal/salsa20.h>
12
#include <botan/internal/loadstor.h>
13
#include <botan/internal/bit_ops.h>
14
#include <botan/internal/timer.h>
15
#include <sstream>
16
17
namespace Botan {
18
19
namespace {
20
21
size_t scrypt_memory_usage(size_t N, size_t r, size_t p)
22
0
   {
23
0
   return 128 * r * (N + p);
24
0
   }
25
26
}
27
28
std::string Scrypt_Family::name() const
29
0
   {
30
0
   return "Scrypt";
31
0
   }
32
33
std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const
34
0
   {
35
0
   return std::make_unique<Scrypt>(32768, 8, 1);
36
0
   }
37
38
std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
39
                                                  std::chrono::milliseconds msec,
40
                                                  size_t max_memory_usage_mb) const
41
0
   {
42
0
   BOTAN_UNUSED(output_length);
43
44
   /*
45
   * Some rough relations between scrypt parameters and runtime.
46
   * Denote here by stime(N,r,p) the msec it takes to run scrypt.
47
   *
48
   * Emperically for smaller sizes:
49
   * stime(N,8*r,p) / stime(N,r,p) is ~ 6-7
50
   * stime(N,r,8*p) / stime(N,r,8*p) is ~ 7
51
   * stime(2*N,r,p) / stime(N,r,p) is ~ 2
52
   *
53
   * Compute stime(8192,1,1) as baseline and extrapolate
54
   */
55
56
0
   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024;
57
   // Starting parameters
58
0
   size_t N = 8192;
59
0
   size_t r = 1;
60
0
   size_t p = 1;
61
62
0
   Timer timer("Scrypt");
63
0
   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
64
65
0
   auto pwdhash = this->from_params(N, r, p);
66
67
0
   timer.run_until_elapsed(tune_time, [&]() {
68
0
      uint8_t output[32] = { 0 };
69
0
      pwdhash->derive_key(output, sizeof(output),
70
0
                          "test", 4,
71
0
                          nullptr, 0);
72
0
      });
73
74
   // No timer events seems strange, perhaps something is wrong - give
75
   // up on this and just return default params
76
0
   if(timer.events() == 0)
77
0
      return default_params();
78
79
   // nsec per eval of scrypt with initial params
80
0
   const uint64_t measured_time = timer.value() / timer.events();
81
82
0
   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
83
84
0
   uint64_t est_nsec = measured_time;
85
86
   // First move increase r by 8x if possible
87
88
0
   if(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*8 < max_memory_usage)
89
0
      {
90
0
      if(target_nsec / est_nsec >= 5)
91
0
         {
92
0
         r *= 8;
93
0
         est_nsec *= 5;
94
0
         }
95
0
      }
96
97
   // Now double N as many times as we can
98
99
0
   while(max_memory_usage == 0 || scrypt_memory_usage(N, r, p)*2 < max_memory_usage)
100
0
      {
101
0
      if(target_nsec / est_nsec >= 2)
102
0
         {
103
0
         N *= 2;
104
0
         est_nsec *= 2;
105
0
         }
106
0
      else
107
0
         break;
108
0
      }
109
110
   // If we have extra runtime budget, increment p
111
112
0
   if(target_nsec / est_nsec > 2)
113
0
      p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec));
114
115
0
   return std::make_unique<Scrypt>(N, r, p);
116
0
   }
117
118
std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const
119
1.14k
   {
120
1.14k
   return std::make_unique<Scrypt>(N, r, p);
121
1.14k
   }
122
123
std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const
124
0
   {
125
0
   const size_t r = 8;
126
0
   const size_t p = 1;
127
128
0
   size_t N = 8192;
129
130
0
   if(iter > 50000)
131
0
      N = 16384;
132
0
   if(iter > 100000)
133
0
      N = 32768;
134
0
   if(iter > 150000)
135
0
      N = 65536;
136
137
0
   return std::make_unique<Scrypt>(N, r, p);
138
0
   }
139
140
Scrypt::Scrypt(size_t N, size_t r, size_t p) :
141
   m_N(N), m_r(r), m_p(p)
142
1.14k
   {
143
1.14k
   if(!is_power_of_2(N))
144
343
      throw Invalid_Argument("Scrypt N parameter must be a power of 2");
145
146
805
   if(p == 0 || p > 1024)
147
192
      throw Invalid_Argument("Invalid or unsupported scrypt p");
148
613
   if(r == 0 || r > 256)
149
121
      throw Invalid_Argument("Invalid or unsupported scrypt r");
150
492
   if(N < 1 || N > 4194304)
151
0
      throw Invalid_Argument("Invalid or unsupported scrypt N");
152
492
   }
153
154
std::string Scrypt::to_string() const
155
0
   {
156
0
   std::ostringstream out;
157
0
   out << "Scrypt("
158
0
       << std::to_string(m_N) << ","
159
0
       << std::to_string(m_r) << ","
160
0
       << std::to_string(m_p) << ")";
161
0
   return out.str();
162
0
   }
163
164
size_t Scrypt::total_memory_usage() const
165
0
   {
166
0
   const size_t N = memory_param();
167
0
   const size_t p = parallelism();
168
0
   const size_t r = iterations();
169
170
0
   return scrypt_memory_usage(N, r, p);
171
0
   }
172
173
namespace {
174
175
void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y)
176
9.44k
   {
177
9.44k
   uint32_t B32[16];
178
9.44k
   secure_vector<uint8_t> X(64);
179
9.44k
   copy_mem(X.data(), &B[(2*r-1)*64], 64);
180
181
97.5k
   for(size_t i = 0; i != 2*r; i++)
182
88.1k
      {
183
88.1k
      xor_buf(X.data(), &B[64*i], 64);
184
88.1k
      load_le<uint32_t>(B32, X.data(), 16);
185
88.1k
      Salsa20::salsa_core(X.data(), B32, 8);
186
88.1k
      copy_mem(&Y[64*i], X.data(), 64);
187
88.1k
      }
188
189
53.5k
   for(size_t i = 0; i < r; ++i)
190
44.0k
      {
191
44.0k
      copy_mem(&B[i*64], &Y[(i * 2) * 64], 64);
192
44.0k
      }
193
194
53.5k
   for(size_t i = 0; i < r; ++i)
195
44.0k
      {
196
44.0k
      copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
197
44.0k
      }
198
9.44k
   }
199
200
void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V)
201
1.27k
   {
202
1.27k
   const size_t S = 128 * r;
203
204
5.99k
   for(size_t i = 0; i != N; ++i)
205
4.72k
      {
206
4.72k
      copy_mem(&V[S*i], B, S);
207
4.72k
      scryptBlockMix(r, B, &V[N*S]);
208
4.72k
      }
209
210
5.99k
   for(size_t i = 0; i != N; ++i)
211
4.72k
      {
212
      // compiler doesn't know here that N is power of 2
213
4.72k
      const size_t j = load_le<uint32_t>(&B[(2*r-1)*64], 0) & (N - 1);
214
4.72k
      xor_buf(B, &V[j*S], S);
215
4.72k
      scryptBlockMix(r, B, &V[N*S]);
216
4.72k
      }
217
1.27k
   }
218
219
}
220
221
void Scrypt::derive_key(uint8_t output[], size_t output_len,
222
                        const char* password, size_t password_len,
223
                        const uint8_t salt[], size_t salt_len) const
224
492
   {
225
492
   const size_t N = memory_param();
226
492
   const size_t p = parallelism();
227
492
   const size_t r = iterations();
228
229
492
   const size_t S = 128 * r;
230
492
   secure_vector<uint8_t> B(p * S);
231
   // temp space
232
492
   secure_vector<uint8_t> V((N+1) * S);
233
234
492
   auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
235
236
492
   try
237
492
      {
238
492
      hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len);
239
492
      }
240
492
   catch(Invalid_Key_Length&)
241
492
      {
242
63
      throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length");
243
63
      }
244
245
429
   pbkdf2(*hmac_sha256,
246
429
          B.data(), B.size(),
247
429
          salt, salt_len,
248
429
          1);
249
250
   // these can be parallel
251
1.70k
   for(size_t i = 0; i != p; ++i)
252
1.27k
      {
253
1.27k
      scryptROMmix(r, N, &B[128*r*i], V);
254
1.27k
      }
255
256
429
   pbkdf2(*hmac_sha256,
257
429
          output, output_len,
258
429
          B.data(), B.size(),
259
429
          1);
260
429
   }
261
262
}