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

randombit / botan / 5134090420

31 May 2023 03:12PM UTC coverage: 91.721% (-0.3%) from 91.995%
5134090420

push

github

randombit
Merge GH #3565 Disable noisy/pointless pylint warnings

76048 of 82912 relevant lines covered (91.72%)

11755290.1 hits per line

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

77.45
/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) { return 128 * r * (N + p); }
226✔
23

24
}  // namespace
25

26
std::string Scrypt_Family::name() const { return "Scrypt"; }
1✔
27

28
std::unique_ptr<PasswordHash> Scrypt_Family::default_params() const { return std::make_unique<Scrypt>(32768, 8, 1); }
2✔
29

30
std::unique_ptr<PasswordHash> Scrypt_Family::tune(size_t output_length,
103✔
31
                                                  std::chrono::milliseconds msec,
32
                                                  size_t max_memory_usage_mb,
33
                                                  std::chrono::milliseconds tune_time) const {
34
   BOTAN_UNUSED(output_length);
103✔
35

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

48
   // We include a bit of slop here (the + 512) to handle the fact that scrypt's
49
   // memory consumption is modified by all three parameters, and otherwise we
50
   // stop before hitting the desired target.
51

52
   const size_t max_memory_usage = max_memory_usage_mb * 1024 * 1024 + 512;
103✔
53
   // Starting parameters
54
   size_t N = 8 * 1024;
103✔
55
   size_t r = 1;
103✔
56
   size_t p = 1;
103✔
57

58
   Timer timer("Scrypt");
103✔
59

60
   auto pwdhash = this->from_params(N, r, p);
103✔
61

62
   timer.run_until_elapsed(tune_time, [&]() {
103✔
63
      uint8_t output[32] = {0};
103✔
64
      pwdhash->derive_key(output, sizeof(output), "test", 4, nullptr, 0);
103✔
65
   });
66

67
   // No timer events seems strange, perhaps something is wrong - give
68
   // up on this and just return default params
69
   if(timer.events() == 0) {
103✔
70
      return default_params();
×
71
   }
72

73
   // nsec per eval of scrypt with initial params
74
   const uint64_t measured_time = timer.value() / timer.events();
103✔
75

76
   const uint64_t target_nsec = msec.count() * static_cast<uint64_t>(1000000);
103✔
77

78
   uint64_t est_nsec = measured_time;
103✔
79

80
   // First move increase r by 8x if possible
81

82
   if(max_memory_usage == 0 || scrypt_memory_usage(N, r * 8, p) <= max_memory_usage) {
103✔
83
      if(target_nsec / est_nsec >= 5) {
4✔
84
         r *= 8;
×
85
         est_nsec *= 5;
×
86
      }
87
   }
88

89
   // Now double N as many times as we can
90

91
   while(max_memory_usage == 0 || scrypt_memory_usage(N * 2, r, p) <= max_memory_usage) {
103✔
92
      if(target_nsec / est_nsec >= 2) {
4✔
93
         N *= 2;
×
94
         est_nsec *= 2;
×
95
      } else {
96
         break;
97
      }
98
   }
99

100
   // If we have extra runtime budget, increment p
101

102
   if(target_nsec / est_nsec > 2) {
103✔
103
      p *= std::min<size_t>(1024, static_cast<size_t>(target_nsec / est_nsec));
12✔
104
   }
105

106
   return std::make_unique<Scrypt>(N, r, p);
103✔
107
}
103✔
108

109
std::unique_ptr<PasswordHash> Scrypt_Family::from_params(size_t N, size_t r, size_t p) const {
233✔
110
   return std::make_unique<Scrypt>(N, r, p);
233✔
111
}
112

113
std::unique_ptr<PasswordHash> Scrypt_Family::from_iterations(size_t iter) const {
×
114
   const size_t r = 8;
×
115
   const size_t p = 1;
×
116

117
   size_t N = 8192;
×
118

119
   if(iter > 50000) {
×
120
      N = 16384;
×
121
   }
122
   if(iter > 100000) {
×
123
      N = 32768;
×
124
   }
125
   if(iter > 150000) {
×
126
      N = 65536;
×
127
   }
128

129
   return std::make_unique<Scrypt>(N, r, p);
×
130
}
131

132
Scrypt::Scrypt(size_t N, size_t r, size_t p) : m_N(N), m_r(r), m_p(p) {
338✔
133
   if(!is_power_of_2(N)) {
338✔
134
      throw Invalid_Argument("Scrypt N parameter must be a power of 2");
×
135
   }
136

137
   if(p == 0 || p > 1024) {
338✔
138
      throw Invalid_Argument("Invalid or unsupported scrypt p");
×
139
   }
140
   if(r == 0 || r > 256) {
338✔
141
      throw Invalid_Argument("Invalid or unsupported scrypt r");
×
142
   }
143
   if(N < 1 || N > 4194304) {
338✔
144
      throw Invalid_Argument("Invalid or unsupported scrypt N");
×
145
   }
146
}
338✔
147

148
std::string Scrypt::to_string() const { return fmt("Scrypt({},{},{})", m_N, m_r, m_p); }
4✔
149

150
size_t Scrypt::total_memory_usage() const {
20✔
151
   const size_t N = memory_param();
20✔
152
   const size_t p = parallelism();
20✔
153
   const size_t r = iterations();
20✔
154

155
   return scrypt_memory_usage(N, r, p);
20✔
156
}
157

158
namespace {
159

160
void scryptBlockMix(size_t r, uint8_t* B, uint8_t* Y) {
10,880,472✔
161
   uint32_t B32[16];
10,880,472✔
162
   secure_vector<uint8_t> X(64);
10,880,472✔
163
   copy_mem(X.data(), &B[(2 * r - 1) * 64], 64);
10,880,472✔
164

165
   for(size_t i = 0; i != 2 * r; i++) {
107,026,088✔
166
      xor_buf(X.data(), &B[64 * i], 64);
96,145,616✔
167
      load_le<uint32_t>(B32, X.data(), 16);
96,145,616✔
168
      Salsa20::salsa_core(X.data(), B32, 8);
96,145,616✔
169
      copy_mem(&Y[64 * i], X.data(), 64);
96,145,616✔
170
   }
171

172
   for(size_t i = 0; i < r; ++i) {
58,953,280✔
173
      copy_mem(&B[i * 64], &Y[(i * 2) * 64], 64);
48,072,808✔
174
   }
175

176
   for(size_t i = 0; i < r; ++i) {
58,953,280✔
177
      copy_mem(&B[(i + r) * 64], &Y[(i * 2 + 1) * 64], 64);
48,072,808✔
178
   }
179
}
10,880,472✔
180

181
void scryptROMmix(size_t r, size_t N, uint8_t* B, secure_vector<uint8_t>& V) {
739✔
182
   const size_t S = 128 * r;
739✔
183

184
   for(size_t i = 0; i != N; ++i) {
5,440,975✔
185
      copy_mem(&V[S * i], B, S);
5,440,236✔
186
      scryptBlockMix(r, B, &V[N * S]);
5,440,236✔
187
   }
188

189
   for(size_t i = 0; i != N; ++i) {
5,440,975✔
190
      // compiler doesn't know here that N is power of 2
191
      const size_t j = load_le<uint32_t>(&B[(2 * r - 1) * 64], 0) & (N - 1);
5,440,236✔
192
      xor_buf(B, &V[j * S], S);
5,440,236✔
193
      scryptBlockMix(r, B, &V[N * S]);
5,440,236✔
194
   }
195
}
739✔
196

197
}  // namespace
198

199
void Scrypt::derive_key(uint8_t output[],
338✔
200
                        size_t output_len,
201
                        const char* password,
202
                        size_t password_len,
203
                        const uint8_t salt[],
204
                        size_t salt_len) const {
205
   const size_t N = memory_param();
338✔
206
   const size_t p = parallelism();
338✔
207
   const size_t r = iterations();
338✔
208

209
   const size_t S = 128 * r;
338✔
210
   secure_vector<uint8_t> B(p * S);
338✔
211
   // temp space
212
   secure_vector<uint8_t> V((N + 1) * S);
338✔
213

214
   auto hmac_sha256 = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
338✔
215

216
   try {
338✔
217
      hmac_sha256->set_key(cast_char_ptr_to_uint8(password), password_len);
338✔
218
   } catch(Invalid_Key_Length&) {
×
219
      throw Invalid_Argument("Scrypt cannot accept passphrases of the provided length");
×
220
   }
×
221

222
   pbkdf2(*hmac_sha256, B.data(), B.size(), salt, salt_len, 1);
338✔
223

224
   // these can be parallel
225
   for(size_t i = 0; i != p; ++i) {
1,077✔
226
      scryptROMmix(r, N, &B[128 * r * i], V);
739✔
227
   }
228

229
   pbkdf2(*hmac_sha256, output, output_len, B.data(), B.size(), 1);
338✔
230
}
1,014✔
231

232
}  // 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