• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

randombit / botan / 11571892341

29 Oct 2024 10:24AM UTC coverage: 91.076% (-0.001%) from 91.077%
11571892341

Pull #4418

github

web-flow
Merge b97f217ca into dc326edf1
Pull Request #4418: Refactor and fixes for URI type

90392 of 99249 relevant lines covered (91.08%)

9373126.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.25
/src/lib/pbkdf/scrypt/scrypt.cpp
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

10
#include <botan/exceptn.h>
11
#include <botan/pbkdf2.h>
12
#include <botan/internal/bit_ops.h>
13
#include <botan/internal/fmt.h>
14
#include <botan/internal/loadstor.h>
15
#include <botan/internal/salsa20.h>
16
#include <botan/internal/timer.h>
17

18
namespace Botan {
19

20
namespace {
21

22
size_t scrypt_memory_usage(size_t N, size_t r, size_t p) {
24✔
23
   return 128 * r * (N + p);
24✔
24
}
25

26
}  // namespace
27

28
std::string Scrypt_Family::name() const {
1✔
29
   return "Scrypt";
1✔
30
}
31

32
std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const {
2✔
33
   return std::make_unique<Scrypt>(32768, 8, 1);
2✔
34
}
35

36
std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
237✔
37
                                                  std::chrono::milliseconds msec,
38
                                                  size_t max_memory_usage_mb,
39
                                                  std::chrono::milliseconds tune_time) const {
40
   BOTAN_UNUSED(output_length);
237✔
41

42
   /*
43
   * Some rough relations between scrypt parameters and runtime.
44
   * Denote here by stime(N,r,p) the msec it takes to run scrypt.
45
   *
46
   * Emperically for smaller sizes:
47
   * stime(N,8*r,p) / stime(N,r,p) is ~ 6-7
48
   * stime(N,r,8*p) / stime(N,r,8*p) is ~ 7
49
   * stime(2*N,r,p) / stime(N,r,p) is ~ 2
50
   *
51
   * Compute stime(8192,1,1) as baseline and extrapolate
52
   */
53

54
   // This is zero if max_memory_usage_mb == 0 (unbounded)
55
   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024;
237✔
56

57
   // Starting parameters
58
   size_t N = 8 * 1024;
237✔
59
   size_t r = 1;
237✔
60
   size_t p = 1;
237✔
61

62
   Timer timer("Scrypt");
237✔
63

64
   auto pwdhash = this->from_params(N, r, p);
237✔
65

66
   timer.run_until_elapsed(tune_time, [&]() {
237✔
67
      uint8_t output[32] = {0};
237✔
68
      pwdhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
237✔
69
   });
70

71
   // No timer events seems strange, perhaps something is wrong - give
72
   // up on this and just return default params
73
   if(timer.events() == 0) {
237✔
74
      return default_params();
×
75
   }
76

77
   // nsec per eval of scrypt with initial params
78
   const uint64_t measured_time = timer.value() / timer.events();
237✔
79

80
   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
237✔
81

82
   uint64_t est_nsec = measured_time;
237✔
83

84
   // In below code we invoke scrypt_memory_usage with p == 0 as p contributes
85
   // (very slightly) to memory consumption, but N is the driving factor.
86
   // Including p leads to using an N half as large as what the user would expect.
87

88
   // First increase r by 8x if possible
89
   if(max_memory_usage == 0 || scrypt_memory_usage(N, r * 8, 0) <= max_memory_usage) {
237✔
90
      if(target_nsec / est_nsec >= 5) {
237✔
91
         r *= 8;
×
92
         est_nsec *= 5;
×
93
      }
94
   }
95

96
   // Now double N as many times as we can
97
   while(max_memory_usage == 0 || scrypt_memory_usage(N * 2, r, 0) <= max_memory_usage) {
248✔
98
      if(target_nsec / est_nsec >= 2) {
248✔
99
         N *= 2;
11✔
100
         est_nsec *= 2;
11✔
101
      } else {
102
         break;
103
      }
104
   }
105

106
   // If we have extra runtime budget, increment p
107
   if(target_nsec / est_nsec >= 2) {
237✔
108
      p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec));
×
109
   }
110

111
   return std::make_unique<Scrypt>(N, r, p);
237✔
112
}
237✔
113

114
std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const {
501✔
115
   return std::make_unique<Scrypt>(N, r, p);
501✔
116
}
117

118
std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const {
×
119
   const size_t r = 8;
×
120
   const size_t p = 1;
×
121

122
   size_t N = 8192;
×
123

124
   if(iter > 50000) {
×
125
      N = 16384;
×
126
   }
127
   if(iter > 100000) {
×
128
      N = 32768;
×
129
   }
130
   if(iter > 150000) {
×
131
      N = 65536;
×
132
   }
133

134
   return std::make_unique<Scrypt>(N, r, p);
×
135
}
136

137
Scrypt::Scrypt(size_t N, size_t r, size_t p) : m_N(N), m_r(r), m_p(p) {
740✔
138
   if(!is_power_of_2(N)) {
740✔
139
      throw Invalid_Argument("Scrypt N parameter must be a power of 2");
×
140
   }
141

142
   if(p == 0 || p > 1024) {
740✔
143
      throw Invalid_Argument("Invalid or unsupported scrypt p");
×
144
   }
145
   if(r == 0 || r > 256) {
740✔
146
      throw Invalid_Argument("Invalid or unsupported scrypt r");
×
147
   }
148
   if(N < 1 || N > 4194304) {
740✔
149
      throw Invalid_Argument("Invalid or unsupported scrypt N");
×
150
   }
151
}
740✔
152

153
std::string Scrypt::to_string() const {
4✔
154
   return fmt("Scrypt({},{},{})", m_N, m_r, m_p);
4✔
155
}
156

157
size_t Scrypt::total_memory_usage() const {
20✔
158
   const size_t N = memory_param();
20✔
159
   const size_t p = parallelism();
20✔
160
   const size_t r = iterations();
20✔
161

162
   return scrypt_memory_usage(N, r, p);
20✔
163
}
164

165
namespace {
166

167
void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y) {
17,303,000✔
168
   uint32_t B32[16];
17,303,000✔
169
   secure_vector<uint8_t> X(64);
17,303,000✔
170
   copy_mem(X.data(), &B[(2 * r - 1) * 64], 64);
17,303,000✔
171

172
   for(size_t i = 0; i != 2 * r; i++) {
126,293,672✔
173
      xor_buf(X.data(), &B[64 * i], 64);
108,990,672✔
174
      load_le<uint32_t>(B32, X.data(), 16);
108,990,672✔
175
      Salsa20::salsa_core(X.data(), B32, 8);
108,990,672✔
176
      copy_mem(&Y[64 * i], X.data(), 64);
108,990,672✔
177
   }
178

179
   for(size_t i = 0; i < r; ++i) {
71,798,336✔
180
      copy_mem(&B[i * 64], &Y[(i * 2) * 64], 64);
54,495,336✔
181
   }
182

183
   for(size_t i = 0; i < r; ++i) {
71,798,336✔
184
      copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
54,495,336✔
185
   }
186
}
17,303,000✔
187

188
void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V) {
1,103✔
189
   const size_t S = 128 * r;
1,103✔
190

191
   for(size_t i = 0; i != N; ++i) {
8,652,603✔
192
      copy_mem(&V[S * i], B, S);
8,651,500✔
193
      scryptBlockMix(r, B, &V[N * S]);
8,651,500✔
194
   }
195

196
   for(size_t i = 0; i != N; ++i) {
8,652,603✔
197
      // compiler doesn't know here that N is power of 2
198
      const size_t j = load_le<uint32_t>(&B[(2 * r - 1) * 64], 0) & (N - 1);
8,651,500✔
199
      xor_buf(B, &V[j * S], S);
8,651,500✔
200
      scryptBlockMix(r, B, &V[N * S]);
8,651,500✔
201
   }
202
}
1,103✔
203

204
}  // namespace
205

206
void Scrypt::derive_key(uint8_t output[],
740✔
207
                        size_t output_len,
208
                        const char* password,
209
                        size_t password_len,
210
                        const uint8_t salt[],
211
                        size_t salt_len) const {
212
   const size_t N = memory_param();
740✔
213
   const size_t p = parallelism();
740✔
214
   const size_t r = iterations();
740✔
215

216
   const size_t S = 128 * r;
740✔
217
   secure_vector<uint8_t> B(p * S);
740✔
218
   // temp space
219
   secure_vector<uint8_t> V((N + 1) * S);
740✔
220

221
   auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
740✔
222

223
   try {
740✔
224
      hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len);
740✔
225
   } catch(Invalid_Key_Length&) {
×
226
      throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length");
×
227
   }
×
228

229
   pbkdf2(*hmac_sha256, B.data(), B.size(), salt, salt_len, 1);
740✔
230

231
   // these can be parallel
232
   for(size_t i = 0; i != p; ++i) {
1,843✔
233
      scryptROMmix(r, N, &B[128 * r * i], V);
1,103✔
234
   }
235

236
   pbkdf2(*hmac_sha256, output, output_len, B.data(), B.size(), 1);
740✔
237
}
2,220✔
238

239
}  // namespace Botan
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc