Botan 3.0.0
Crypto and TLS for C&
pgp_s2k.cpp
Go to the documentation of this file.
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 <botan/internal/fmt.h>
13#include <algorithm>
14
15namespace Botan {
16
17namespace {
18
19void pgp_s2k(HashFunction& hash,
20 uint8_t output_buf[], size_t output_len,
21 const char* password, const size_t password_size,
22 const uint8_t salt[], size_t salt_len,
23 size_t iterations)
24 {
25 if(iterations > 1 && salt_len == 0)
26 throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode");
27
28 secure_vector<uint8_t> input_buf(salt_len + password_size);
29 if(salt_len > 0)
30 {
31 copy_mem(&input_buf[0], salt, salt_len);
32 }
33 if(password_size > 0)
34 {
35 copy_mem(&input_buf[salt_len],
36 cast_char_ptr_to_uint8(password),
37 password_size);
38 }
39
40 secure_vector<uint8_t> hash_buf(hash.output_length());
41
42 size_t pass = 0;
43 size_t generated = 0;
44
45 while(generated != output_len)
46 {
47 const size_t output_this_pass =
48 std::min(hash_buf.size(), output_len - generated);
49
50 // Preload some number of zero bytes (empty first iteration)
51 std::vector<uint8_t> zero_padding(pass);
52 hash.update(zero_padding);
53
54 // The input is always fully processed even if iterations is very small
55 if(input_buf.empty() == false)
56 {
57 size_t left = std::max(iterations, input_buf.size());
58 while(left > 0)
59 {
60 const size_t input_to_take = std::min(left, input_buf.size());
61 hash.update(input_buf.data(), input_to_take);
62 left -= input_to_take;
63 }
64 }
65
66 hash.final(hash_buf.data());
67 copy_mem(output_buf + generated, hash_buf.data(), output_this_pass);
68 generated += output_this_pass;
69 ++pass;
70 }
71 }
72
73}
74
75size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[], size_t output_len,
76 std::string_view password,
77 const uint8_t salt[], size_t salt_len,
78 size_t iterations,
79 std::chrono::milliseconds msec) const
80 {
81 std::unique_ptr<PasswordHash> pwdhash;
82
83 if(iterations == 0)
84 {
85 RFC4880_S2K_Family s2k_params(m_hash->new_object());
86 iterations = s2k_params.tune(output_len, msec, 0, std::chrono::milliseconds(10))->iterations();
87 }
88
89 pgp_s2k(*m_hash, output_buf, output_len,
90 password.data(), password.size(),
91 salt, salt_len,
92 iterations);
93
94 return iterations;
95 }
96
97std::string RFC4880_S2K_Family::name() const
98 {
99 return fmt("OpenPGP-S2K({})", m_hash->name());
100 }
101
102std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune(
103 size_t output_len,
104 std::chrono::milliseconds msec,
105 size_t /*max_memory_usage_mb*/,
106 std::chrono::milliseconds tune_time) const
107 {
108 const size_t buf_size = 1024;
109 std::vector<uint8_t> buffer(buf_size);
110
111 Timer timer("RFC4880_S2K", buf_size);
112 timer.run_until_elapsed(tune_time, [&]() {
113 m_hash->update(buffer);
114 });
115
116 const double hash_bytes_per_second = timer.bytes_per_second();
117 const uint64_t desired_nsec = msec.count() * 1000000;
118
119 const size_t hash_size = m_hash->output_length();
120 const size_t blocks_required = (output_len <= hash_size ? 1 : (output_len + hash_size - 1) / hash_size);
121
122 const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required;
123 const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed));
124
125 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iterations);
126 }
127
128std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iter, size_t /*i2*/, size_t /*i3*/) const
129 {
130 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iter);
131 }
132
133std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const
134 {
135 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), 50331648);
136 }
137
138std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iter) const
139 {
140 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iter);
141 }
142
143RFC4880_S2K::RFC4880_S2K(std::unique_ptr<HashFunction> hash, size_t iterations) :
144 m_hash(std::move(hash)),
145 m_iterations(iterations)
146 {
147 }
148
149std::string RFC4880_S2K::to_string() const
150 {
151 return fmt("OpenPGP-S2K({},{})", m_hash->name(), m_iterations);
152 }
153
154void RFC4880_S2K::derive_key(uint8_t out[], size_t out_len,
155 const char* password, const size_t password_len,
156 const uint8_t salt[], size_t salt_len) const
157 {
158 pgp_s2k(*m_hash, out, out_len,
159 password, password_len,
160 salt, salt_len,
161 m_iterations);
162 }
163
164}
size_t pbkdf(uint8_t output_buf[], size_t output_len, std::string_view passphrase, const uint8_t salt[], size_t salt_len, size_t iterations, std::chrono::milliseconds msec) const override
Definition: pgp_s2k.cpp:75
std::unique_ptr< PasswordHash > default_params() const override
Definition: pgp_s2k.cpp:133
std::unique_ptr< PasswordHash > tune(size_t output_len, std::chrono::milliseconds msec, size_t max_mem, std::chrono::milliseconds tune_msec) const override
Definition: pgp_s2k.cpp:102
std::string name() const override
Definition: pgp_s2k.cpp:97
std::unique_ptr< PasswordHash > from_params(size_t iter, size_t, size_t) const override
Definition: pgp_s2k.cpp:128
std::unique_ptr< PasswordHash > from_iterations(size_t iter) const override
Definition: pgp_s2k.cpp:138
std::string to_string() const override
Definition: pgp_s2k.cpp:149
void derive_key(uint8_t out[], size_t out_len, const char *password, size_t password_len, const uint8_t salt[], size_t salt_len) const override
Definition: pgp_s2k.cpp:154
RFC4880_S2K(std::unique_ptr< HashFunction > hash, size_t iterations)
Definition: pgp_s2k.cpp:143
void run_until_elapsed(std::chrono::milliseconds msec, F f)
Definition: timer.h:76
double bytes_per_second() const
Definition: timer.h:133
Definition: alg_id.cpp:12
std::string fmt(std::string_view format, const T &... args)
Definition: fmt.h:60
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition: mem_ops.h:126
size_t RFC4880_round_iterations(size_t iterations)
Definition: rfc4880.h:32
const uint8_t * cast_char_ptr_to_uint8(const char *s)
Definition: mem_ops.h:183
Definition: bigint.h:1092