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