Coverage Report

Created: 2022-01-14 08:07

/src/botan/src/lib/pbkdf/pgp_s2k/pgp_s2k.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
* OpenPGP S2K
3
* (C) 1999-2007,2017 Jack Lloyd
4
* (C) 2018 Ribose Inc
5
*
6
* Botan is released under the Simplified BSD License (see license.txt)
7
*/
8
9
#include <botan/pgp_s2k.h>
10
#include <botan/exceptn.h>
11
#include <botan/internal/timer.h>
12
#include <algorithm>
13
14
namespace Botan {
15
16
namespace {
17
18
void pgp_s2k(HashFunction& hash,
19
             uint8_t output_buf[], size_t output_len,
20
             const char* password, const size_t password_size,
21
             const uint8_t salt[], size_t salt_len,
22
             size_t iterations)
23
0
   {
24
0
   if(iterations > 1 && salt_len == 0)
25
0
      throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode");
26
27
0
   secure_vector<uint8_t> input_buf(salt_len + password_size);
28
0
   if(salt_len > 0)
29
0
      {
30
0
      copy_mem(&input_buf[0], salt, salt_len);
31
0
      }
32
0
   if(password_size > 0)
33
0
      {
34
0
      copy_mem(&input_buf[salt_len],
35
0
               cast_char_ptr_to_uint8(password),
36
0
               password_size);
37
0
      }
38
39
0
   secure_vector<uint8_t> hash_buf(hash.output_length());
40
41
0
   size_t pass = 0;
42
0
   size_t generated = 0;
43
44
0
   while(generated != output_len)
45
0
      {
46
0
      const size_t output_this_pass =
47
0
         std::min(hash_buf.size(), output_len - generated);
48
49
      // Preload some number of zero bytes (empty first iteration)
50
0
      std::vector<uint8_t> zero_padding(pass);
51
0
      hash.update(zero_padding);
52
53
      // The input is always fully processed even if iterations is very small
54
0
      if(input_buf.empty() == false)
55
0
         {
56
0
         size_t left = std::max(iterations, input_buf.size());
57
0
         while(left > 0)
58
0
            {
59
0
            const size_t input_to_take = std::min(left, input_buf.size());
60
0
            hash.update(input_buf.data(), input_to_take);
61
0
            left -= input_to_take;
62
0
            }
63
0
         }
64
65
0
      hash.final(hash_buf.data());
66
0
      copy_mem(output_buf + generated, hash_buf.data(), output_this_pass);
67
0
      generated += output_this_pass;
68
0
      ++pass;
69
0
      }
70
0
   }
71
72
}
73
74
size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len,
75
                          const std::string& password,
76
                          const uint8_t salt[], size_t salt_len,
77
                          size_t iterations,
78
                          std::chrono::milliseconds msec) const
79
0
   {
80
0
   std::unique_ptr<PasswordHash> pwdhash;
81
82
0
   if(iterations == 0)
83
0
      {
84
0
      RFC4880_S2K_Family s2k_params(m_hash->clone());
85
0
      iterations = s2k_params.tune(output_len, msec, 0)->iterations();
86
0
      }
87
88
0
   pgp_s2k(*m_hash, output_buf, output_len,
89
0
           password.c_str(), password.size(),
90
0
           salt, salt_len,
91
0
           iterations);
92
93
0
   return iterations;
94
0
   }
95
96
std::string RFC4880_S2K_Family::name() const
97
0
   {
98
0
   return "OpenPGP-S2K(" + m_hash->name() + ")";
99
0
   }
100
101
std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(size_t output_len, std::chrono::milliseconds msec, size_t) const
102
0
   {
103
0
   const auto tune_time = BOTAN_PBKDF_TUNING_TIME;
104
105
0
   const size_t buf_size = 1024;
106
0
   std::vector<uint8_t> buffer(buf_size);
107
108
0
   Timer timer("RFC4880_S2K", buf_size);
109
0
   timer.run_until_elapsed(tune_time, [&]() {
110
0
      m_hash->update(buffer);
111
0
      });
112
113
0
   const double hash_bytes_per_second = timer.bytes_per_second();
114
0
   const uint64_t desired_nsec = msec.count() * 1000000;
115
116
0
   const size_t hash_size = m_hash->output_length();
117
0
   const size_t blocks_required = (output_len <= hash_size ? 1 : (output_len + hash_size - 1) / hash_size);
118
119
0
   const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required;
120
0
   const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed));
121
122
0
   return std::make_unique<RFC4880_S2K>(m_hash->clone(), iterations);
123
0
   }
124
125
std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iter, size_t, size_t) const
126
0
   {
127
0
   return std::make_unique<RFC4880_S2K>(m_hash->clone(), iter);
128
0
   }
129
130
std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const
131
0
   {
132
0
   return std::make_unique<RFC4880_S2K>(m_hash->clone(), 50331648);
133
0
   }
134
135
std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iter) const
136
0
   {
137
0
   return std::make_unique<RFC4880_S2K>(m_hash->clone(), iter);
138
0
   }
139
140
RFC4880_S2K::RFC4880_S2K(HashFunction* hash, size_t iterations) :
141
   m_hash(hash),
142
   m_iterations(iterations)
143
0
   {
144
0
   }
145
146
std::string RFC4880_S2K::to_string() const
147
0
   {
148
0
   return "OpenPGP-S2K(" + m_hash->name() + "," + std::to_string(m_iterations) + ")";
149
0
   }
150
151
void RFC4880_S2K::derive_key(uint8_t out[], size_t out_len,
152
                             const char* password, const size_t password_len,
153
                             const uint8_t salt[], size_t salt_len) const
154
0
   {
155
0
   pgp_s2k(*m_hash, out, out_len,
156
0
           password, password_len,
157
0
           salt, salt_len,
158
0
           m_iterations);
159
0
   }
160
161
}