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