Botan 3.8.1
Crypto and TLS for C&
pssr.cpp
Go to the documentation of this file.
1/*
2* PSSR
3* (C) 1999-2007,2017,2023 Jack Lloyd
4*
5* Botan is released under the Simplified BSD License (see license.txt)
6*/
7
8#include <botan/internal/pssr.h>
9
10#include <botan/exceptn.h>
11#include <botan/mem_ops.h>
12#include <botan/rng.h>
13#include <botan/internal/bit_ops.h>
14#include <botan/internal/ct_utils.h>
15#include <botan/internal/fmt.h>
16#include <botan/internal/mgf1.h>
17#include <botan/internal/stl_util.h>
18#include <array>
19
20namespace Botan {
21
22namespace {
23
24/*
25* PSSR Encode Operation
26*/
27std::vector<uint8_t> pss_encode(HashFunction& hash,
28 std::span<const uint8_t> msg,
29 std::span<const uint8_t> salt,
30 size_t output_bits) {
31 const size_t HASH_SIZE = hash.output_length();
32
33 if(msg.size() != HASH_SIZE) {
34 throw Encoding_Error("Cannot encode PSS string, input length invalid for hash");
35 }
36 if(output_bits < 8 * HASH_SIZE + 8 * salt.size() + 9) {
37 throw Encoding_Error("Cannot encode PSS string, output length too small");
38 }
39
40 const size_t output_length = ceil_tobytes(output_bits);
41 const uint8_t db0_mask = 0xFF >> (8 * output_length - output_bits);
42
43 std::array<uint8_t, 8> padding = {0};
44 hash.update(padding);
45 hash.update(msg);
46 hash.update(salt);
47 std::vector<uint8_t> H = hash.final_stdvec();
48
49 const size_t db_len = output_length - HASH_SIZE - 1;
50 std::vector<uint8_t> EM(output_length);
51
52 BufferStuffer stuffer(EM);
53 stuffer.append(0x00, stuffer.remaining_capacity() - (1 + salt.size() + H.size() + 1));
54 stuffer.append(0x01);
55 stuffer.append(salt);
56
57 mgf1_mask(hash, H.data(), H.size(), EM.data(), db_len);
58 EM[0] &= db0_mask;
59
60 stuffer.append(H);
61 stuffer.append(0xBC);
62 BOTAN_ASSERT_NOMSG(stuffer.full());
63
64 return EM;
65}
66
67bool pss_verify(HashFunction& hash,
68 std::span<const uint8_t> pss_repr,
69 std::span<const uint8_t> message_hash,
70 size_t key_bits,
71 size_t* out_salt_size) {
72 const size_t HASH_SIZE = hash.output_length();
73 const size_t key_bytes = ceil_tobytes(key_bits);
74
75 if(key_bits < 8 * HASH_SIZE + 9) {
76 return false;
77 }
78
79 if(message_hash.size() != HASH_SIZE) {
80 return false;
81 }
82
83 if(pss_repr.size() > key_bytes || pss_repr.size() <= 1) {
84 return false;
85 }
86
87 if(pss_repr[pss_repr.size() - 1] != 0xBC) {
88 return false;
89 }
90
91 std::vector<uint8_t> coded;
92 if(pss_repr.size() < key_bytes) {
93 coded.resize(key_bytes);
94 BufferStuffer stuffer(coded);
95 stuffer.append(0x00, key_bytes - pss_repr.size());
96 stuffer.append(pss_repr);
97 } else {
98 coded.assign(pss_repr.begin(), pss_repr.end());
99 }
100
101 // We have to check this after potential zero padding above
102 const size_t top_bits = 8 * ((key_bits + 7) / 8) - key_bits;
103 if(top_bits > 8 - high_bit(coded[0])) {
104 return false;
105 }
106
107 uint8_t* DB = coded.data();
108 const size_t DB_size = coded.size() - HASH_SIZE - 1;
109
110 const uint8_t* H = &coded[DB_size];
111 const size_t H_size = HASH_SIZE;
112
113 mgf1_mask(hash, H, H_size, DB, DB_size);
114 DB[0] &= 0xFF >> top_bits;
115
116 size_t salt_offset = 0;
117 for(size_t j = 0; j != DB_size; ++j) {
118 if(DB[j] == 0x01) {
119 salt_offset = j + 1;
120 break;
121 }
122 if(DB[j]) {
123 return false;
124 }
125 }
126 if(salt_offset == 0) {
127 return false;
128 }
129
130 const size_t salt_size = DB_size - salt_offset;
131
132 std::array<uint8_t, 8> padding = {0};
133 hash.update(padding);
134 hash.update(message_hash);
135 hash.update(&DB[salt_offset], salt_size);
136
137 const std::vector<uint8_t> H2 = hash.final_stdvec();
138
139 const bool ok = CT::is_equal(H, H2.data(), HASH_SIZE).as_bool();
140
141 if(out_salt_size && ok) {
142 *out_salt_size = salt_size;
143 }
144
145 return ok;
146}
147
148} // namespace
149
150PSSR::PSSR(std::unique_ptr<HashFunction> hash) :
151 m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {}
152
153PSSR::PSSR(std::unique_ptr<HashFunction> hash, size_t salt_size) :
154 m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {}
155
156/*
157* PSSR Update Operation
158*/
159void PSSR::update(const uint8_t input[], size_t length) {
160 m_hash->update(input, length);
161}
162
163/*
164* Return the raw (unencoded) data
165*/
166std::vector<uint8_t> PSSR::raw_data() {
167 return m_hash->final_stdvec();
168}
169
170std::vector<uint8_t> PSSR::encoding_of(std::span<const uint8_t> msg, size_t output_bits, RandomNumberGenerator& rng) {
171 const auto salt = rng.random_vec<std::vector<uint8_t>>(m_salt_size);
172 return pss_encode(*m_hash, msg, salt, output_bits);
173}
174
175/*
176* PSSR Decode/Verify Operation
177*/
178bool PSSR::verify(std::span<const uint8_t> coded, std::span<const uint8_t> raw, size_t key_bits) {
179 size_t salt_size = 0;
180 const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
181
182 if(m_required_salt_len && salt_size != m_salt_size) {
183 return false;
184 }
185
186 return ok;
187}
188
189std::string PSSR::name() const {
190 return fmt("PSS({},MGF1,{})", m_hash->name(), m_salt_size);
191}
192
193PSSR_Raw::PSSR_Raw(std::unique_ptr<HashFunction> hash) :
194 m_hash(std::move(hash)), m_salt_size(m_hash->output_length()), m_required_salt_len(false) {}
195
196PSSR_Raw::PSSR_Raw(std::unique_ptr<HashFunction> hash, size_t salt_size) :
197 m_hash(std::move(hash)), m_salt_size(salt_size), m_required_salt_len(true) {}
198
199/*
200* PSSR_Raw Update Operation
201*/
202void PSSR_Raw::update(const uint8_t input[], size_t length) {
203 m_msg.insert(m_msg.end(), input, input + length);
204}
205
206/*
207* Return the raw (unencoded) data
208*/
209std::vector<uint8_t> PSSR_Raw::raw_data() {
210 std::vector<uint8_t> ret;
211 std::swap(ret, m_msg);
212
213 if(ret.size() != m_hash->output_length()) {
214 throw Encoding_Error("PSSR_Raw Bad input length, did not match hash");
215 }
216
217 return ret;
218}
219
220std::vector<uint8_t> PSSR_Raw::encoding_of(std::span<const uint8_t> msg,
221 size_t output_bits,
223 const auto salt = rng.random_vec<std::vector<uint8_t>>(m_salt_size);
224 return pss_encode(*m_hash, msg, salt, output_bits);
225}
226
227/*
228* PSSR_Raw Decode/Verify Operation
229*/
230bool PSSR_Raw::verify(std::span<const uint8_t> coded, std::span<const uint8_t> raw, size_t key_bits) {
231 size_t salt_size = 0;
232 const bool ok = pss_verify(*m_hash, coded, raw, key_bits, &salt_size);
233
234 if(m_required_salt_len && salt_size != m_salt_size) {
235 return false;
236 }
237
238 return ok;
239}
240
241std::string PSSR_Raw::name() const {
242 return fmt("PSS_Raw({},MGF1,{})", m_hash->name(), m_salt_size);
243}
244
245} // namespace Botan
#define BOTAN_ASSERT_NOMSG(expr)
Definition assert.h:61
Helper class to ease in-place marshalling of concatenated fixed-length values.
Definition stl_util.h:143
PSSR_Raw(std::unique_ptr< HashFunction > hash)
Definition pssr.cpp:193
std::string name() const override
Definition pssr.cpp:241
std::string name() const override
Definition pssr.cpp:189
PSSR(std::unique_ptr< HashFunction > hash)
Definition pssr.cpp:150
constexpr CT::Mask< T > is_equal(const T x[], const T y[], size_t len)
Definition ct_utils.h:789
void mgf1_mask(HashFunction &hash, const uint8_t in[], size_t in_len, uint8_t out[], size_t out_len)
Definition mgf1.cpp:16
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
constexpr size_t high_bit(T n)
Definition bit_ops.h:61
constexpr T ceil_tobytes(T bits)
Definition bit_ops.h:168