Botan 3.13.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
11#include <botan/exceptn.h>
12#include <botan/mem_ops.h>
13#include <botan/internal/fmt.h>
14#include <botan/internal/int_utils.h>
15#include <botan/internal/mem_utils.h>
16#include <botan/internal/time_utils.h>
17#include <algorithm>
18
19namespace Botan {
20
21namespace {
22
23void pgp_s2k(HashFunction& hash,
24 uint8_t output_buf[],
25 size_t output_len,
26 const char* password,
27 const size_t password_size,
28 const uint8_t salt[],
29 size_t salt_len,
30 size_t iterations) {
31 if(iterations > 1 && salt_len == 0) {
32 throw Invalid_Argument("OpenPGP S2K requires a salt in iterated mode");
33 }
34
35 const size_t input_len = add_or_throw(salt_len, password_size, "OpenPGP S2K salt and password are too large");
36 secure_vector<uint8_t> input_buf(input_len);
37 if(salt_len > 0) {
38 copy_mem(input_buf.data(), salt, salt_len);
39 }
40 if(password_size > 0) {
41 copy_mem(std::span(input_buf).subspan(salt_len), as_span_of_bytes(password, password_size));
42 }
43
44 secure_vector<uint8_t> hash_buf(hash.output_length());
45
46 size_t pass = 0;
47 size_t generated = 0;
48
49 while(generated != output_len) {
50 const size_t output_this_pass = std::min(hash_buf.size(), output_len - generated);
51
52 // Preload some number of zero bytes (empty first iteration)
53 std::vector<uint8_t> zero_padding(pass);
54 hash.update(zero_padding);
55
56 // The input is always fully processed even if iterations is very small
57 if(!input_buf.empty()) {
58 size_t left = std::max(iterations, input_buf.size());
59 while(left > 0) {
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} // namespace
74
75size_t OpenPGP_S2K::pbkdf(uint8_t output_buf[],
76 size_t output_len,
77 std::string_view password,
78 const uint8_t salt[],
79 size_t salt_len,
80 size_t iterations,
81 std::chrono::milliseconds desired_msec) const {
82 if(iterations == 0) {
83 const RFC4880_S2K_Family s2k_params(m_hash->new_object());
84 iterations = s2k_params.tune_params(output_len, desired_msec.count(), {}, 10)->iterations();
85 }
86
87 pgp_s2k(*m_hash, output_buf, output_len, password.data(), password.size(), salt, salt_len, iterations);
88
89 return iterations;
90}
91
92std::string RFC4880_S2K_Family::name() const {
93 return fmt("OpenPGP-S2K({})", m_hash->name());
94}
95
96std::unique_ptr<PasswordHash> RFC4880_S2K_Family::tune_params(size_t output_len,
97 uint64_t desired_msec,
98 std::optional<size_t> /*max_memory*/,
99 uint64_t tuning_msec) const {
100 constexpr size_t buf_size = 1024;
101 std::vector<uint8_t> buffer(buf_size);
102
103 const uint64_t measured_nsec = measure_cost(tuning_msec, [&]() { m_hash->update(buffer); });
104
105 const double hash_bytes_per_second = (buf_size * 1000000000.0) / measured_nsec;
106 const uint64_t desired_nsec = desired_msec * 1000000;
107
108 const size_t hash_size = m_hash->output_length();
109 const size_t blocks_required = std::max<size_t>(1, (output_len / hash_size) + (output_len % hash_size != 0 ? 1 : 0));
110
111 const double bytes_to_be_hashed = (hash_bytes_per_second * (desired_nsec / 1000000000.0)) / blocks_required;
112 const size_t iterations = RFC4880_round_iterations(static_cast<size_t>(bytes_to_be_hashed));
113
114 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iterations);
115}
116
117std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_params(size_t iterations,
118 size_t /*unused*/,
119 size_t /*unused*/) const {
120 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iterations);
121}
122
123std::unique_ptr<PasswordHash> RFC4880_S2K_Family::default_params() const {
124 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), 50331648);
125}
126
127std::unique_ptr<PasswordHash> RFC4880_S2K_Family::from_iterations(size_t iterations) const {
128 return std::make_unique<RFC4880_S2K>(m_hash->new_object(), iterations);
129}
130
131RFC4880_S2K::RFC4880_S2K(std::unique_ptr<HashFunction> hash, size_t iterations) :
132 m_hash(std::move(hash)), m_iterations(iterations) {}
133
134std::string RFC4880_S2K::to_string() const {
135 return fmt("OpenPGP-S2K({},{})", m_hash->name(), m_iterations);
136}
137
138void RFC4880_S2K::derive_key(uint8_t out[],
139 size_t out_len,
140 const char* password,
141 const size_t password_len,
142 const uint8_t salt[],
143 size_t salt_len) const {
144 pgp_s2k(*m_hash, out, out_len, password, password_len, salt, salt_len, m_iterations);
145}
146
147} // namespace Botan
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
void hash(std::span< uint8_t > out, std::string_view password, std::span< const uint8_t > salt) const
Definition pwdhash.h:95
std::unique_ptr< PasswordHash > from_iterations(size_t iterations) const override
Definition pgp_s2k.cpp:127
std::unique_ptr< PasswordHash > default_params() const override
Definition pgp_s2k.cpp:123
std::unique_ptr< PasswordHash > from_params(size_t iterations, size_t, size_t) const override
Definition pgp_s2k.cpp:117
std::string name() const override
Definition pgp_s2k.cpp:92
std::unique_ptr< PasswordHash > tune_params(size_t output_len, uint64_t desired_runtime_msec, std::optional< size_t > max_memory, uint64_t tune_msec) const override
Definition pgp_s2k.cpp:96
std::string to_string() const override
Definition pgp_s2k.cpp:134
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:138
size_t iterations() const override
Definition pgp_s2k.h:90
RFC4880_S2K(std::unique_ptr< HashFunction > hash, size_t iterations)
Definition pgp_s2k.cpp:131
constexpr T add_or_throw(T a, T b, std::string_view msg)
Definition int_utils.h:66
std::span< const uint8_t > as_span_of_bytes(const char *s, size_t len)
Definition mem_utils.h:59
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr void copy_mem(T *out, const T *in, size_t n)
Definition mem_ops.h:144
std::vector< T, secure_allocator< T > > secure_vector
Definition secmem.h:128
uint64_t measure_cost(uint64_t trial_msec, F func)
Definition time_utils.h:19
size_t RFC4880_round_iterations(size_t iterations)
Definition rfc4880.h:32