Coverage Report

Created: 2025-04-11 06:34

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