/src/botan/src/lib/modes/mode_pad/mode_pad.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * CBC Padding Methods |
3 | | * (C) 1999-2007,2013,2018,2020 Jack Lloyd |
4 | | * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity |
5 | | * |
6 | | * Botan is released under the Simplified BSD License (see license.txt) |
7 | | */ |
8 | | |
9 | | #include <botan/internal/mode_pad.h> |
10 | | |
11 | | #include <botan/exceptn.h> |
12 | | #include <botan/internal/ct_utils.h> |
13 | | |
14 | | namespace Botan { |
15 | | |
16 | | /** |
17 | | * Get a block cipher padding method by name |
18 | | */ |
19 | 0 | std::unique_ptr<BlockCipherModePaddingMethod> BlockCipherModePaddingMethod::create(std::string_view algo_spec) { |
20 | 0 | if(algo_spec == "NoPadding") { |
21 | 0 | return std::make_unique<Null_Padding>(); |
22 | 0 | } |
23 | | |
24 | 0 | if(algo_spec == "PKCS7") { |
25 | 0 | return std::make_unique<PKCS7_Padding>(); |
26 | 0 | } |
27 | | |
28 | 0 | if(algo_spec == "OneAndZeros") { |
29 | 0 | return std::make_unique<OneAndZeros_Padding>(); |
30 | 0 | } |
31 | | |
32 | 0 | if(algo_spec == "X9.23") { |
33 | 0 | return std::make_unique<ANSI_X923_Padding>(); |
34 | 0 | } |
35 | | |
36 | 0 | if(algo_spec == "ESP") { |
37 | 0 | return std::make_unique<ESP_Padding>(); |
38 | 0 | } |
39 | | |
40 | 0 | return nullptr; |
41 | 0 | } |
42 | | |
43 | | /* |
44 | | * Pad with PKCS #7 Method |
45 | | */ |
46 | 0 | void PKCS7_Padding::add_padding(secure_vector<uint8_t>& buffer, size_t last_byte_pos, size_t BS) const { |
47 | | /* |
48 | | Padding format is |
49 | | 01 |
50 | | 0202 |
51 | | 030303 |
52 | | ... |
53 | | */ |
54 | 0 | BOTAN_DEBUG_ASSERT(last_byte_pos < BS); |
55 | |
|
56 | 0 | const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); |
57 | |
|
58 | 0 | buffer.resize(buffer.size() + padding_len); |
59 | |
|
60 | 0 | CT::poison(&last_byte_pos, 1); |
61 | 0 | CT::poison(buffer.data(), buffer.size()); |
62 | |
|
63 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); |
64 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() >= BS); |
65 | |
|
66 | 0 | const size_t start_of_last_block = buffer.size() - BS; |
67 | 0 | const size_t end_of_last_block = buffer.size(); |
68 | 0 | const size_t start_of_padding = buffer.size() - padding_len; |
69 | |
|
70 | 0 | for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { |
71 | 0 | auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); |
72 | 0 | buffer[i] = needs_padding.select(padding_len, buffer[i]); |
73 | 0 | } |
74 | |
|
75 | 0 | CT::unpoison(buffer.data(), buffer.size()); |
76 | 0 | CT::unpoison(last_byte_pos); |
77 | 0 | } |
78 | | |
79 | | /* |
80 | | * Unpad with PKCS #7 Method |
81 | | */ |
82 | 138 | size_t PKCS7_Padding::unpad(const uint8_t input[], size_t input_length) const { |
83 | 138 | if(!valid_blocksize(input_length)) { |
84 | 0 | return input_length; |
85 | 0 | } |
86 | | |
87 | 138 | CT::poison(input, input_length); |
88 | | |
89 | 138 | const uint8_t last_byte = input[input_length - 1]; |
90 | | |
91 | | /* |
92 | | The input should == the block size so if the last byte exceeds |
93 | | that then the padding is certainly invalid |
94 | | */ |
95 | 138 | auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); |
96 | | |
97 | 138 | const size_t pad_pos = input_length - last_byte; |
98 | | |
99 | 5.20k | for(size_t i = 0; i != input_length - 1; ++i) { |
100 | | // Does this byte equal the expected pad byte? |
101 | 5.07k | const auto pad_eq = CT::Mask<size_t>::is_equal(input[i], last_byte); |
102 | | |
103 | | // Ignore values that are not part of the padding |
104 | 5.07k | const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); |
105 | 5.07k | bad_input |= in_range & (~pad_eq); |
106 | 5.07k | } |
107 | | |
108 | 138 | CT::unpoison(input, input_length); |
109 | | |
110 | 138 | return bad_input.select_and_unpoison(input_length, pad_pos); |
111 | 138 | } |
112 | | |
113 | | /* |
114 | | * Pad with ANSI X9.23 Method |
115 | | */ |
116 | 0 | void ANSI_X923_Padding::add_padding(secure_vector<uint8_t>& buffer, size_t last_byte_pos, size_t BS) const { |
117 | | /* |
118 | | Padding format is |
119 | | 01 |
120 | | 0002 |
121 | | 000003 |
122 | | ... |
123 | | */ |
124 | 0 | BOTAN_DEBUG_ASSERT(last_byte_pos < BS); |
125 | |
|
126 | 0 | const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); |
127 | |
|
128 | 0 | buffer.resize(buffer.size() + padding_len); |
129 | |
|
130 | 0 | CT::poison(&last_byte_pos, 1); |
131 | 0 | CT::poison(buffer.data(), buffer.size()); |
132 | |
|
133 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); |
134 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() >= BS); |
135 | |
|
136 | 0 | const size_t start_of_last_block = buffer.size() - BS; |
137 | 0 | const size_t end_of_zero_padding = buffer.size() - 1; |
138 | 0 | const size_t start_of_padding = buffer.size() - padding_len; |
139 | |
|
140 | 0 | for(size_t i = start_of_last_block; i != end_of_zero_padding; ++i) { |
141 | 0 | auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); |
142 | 0 | buffer[i] = needs_padding.select(0, buffer[i]); |
143 | 0 | } |
144 | |
|
145 | 0 | buffer[buffer.size() - 1] = padding_len; |
146 | 0 | CT::unpoison(buffer.data(), buffer.size()); |
147 | 0 | CT::unpoison(last_byte_pos); |
148 | 0 | } |
149 | | |
150 | | /* |
151 | | * Unpad with ANSI X9.23 Method |
152 | | */ |
153 | 138 | size_t ANSI_X923_Padding::unpad(const uint8_t input[], size_t input_length) const { |
154 | 138 | if(!valid_blocksize(input_length)) { |
155 | 0 | return input_length; |
156 | 0 | } |
157 | | |
158 | 138 | CT::poison(input, input_length); |
159 | | |
160 | 138 | const size_t last_byte = input[input_length - 1]; |
161 | | |
162 | 138 | auto bad_input = CT::Mask<size_t>::is_gt(last_byte, input_length); |
163 | | |
164 | 138 | const size_t pad_pos = input_length - last_byte; |
165 | | |
166 | 5.20k | for(size_t i = 0; i != input_length - 1; ++i) { |
167 | | // Ignore values that are not part of the padding |
168 | 5.07k | const auto in_range = CT::Mask<size_t>::is_gte(i, pad_pos); |
169 | 5.07k | const auto pad_is_nonzero = CT::Mask<size_t>::expand(input[i]); |
170 | 5.07k | bad_input |= pad_is_nonzero & in_range; |
171 | 5.07k | } |
172 | | |
173 | 138 | CT::unpoison(input, input_length); |
174 | | |
175 | 138 | return bad_input.select_and_unpoison(input_length, pad_pos); |
176 | 138 | } |
177 | | |
178 | | /* |
179 | | * Pad with One and Zeros Method |
180 | | */ |
181 | 0 | void OneAndZeros_Padding::add_padding(secure_vector<uint8_t>& buffer, size_t last_byte_pos, size_t BS) const { |
182 | | /* |
183 | | Padding format is |
184 | | 80 |
185 | | 8000 |
186 | | 800000 |
187 | | ... |
188 | | */ |
189 | |
|
190 | 0 | BOTAN_DEBUG_ASSERT(last_byte_pos < BS); |
191 | |
|
192 | 0 | const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); |
193 | |
|
194 | 0 | buffer.resize(buffer.size() + padding_len); |
195 | |
|
196 | 0 | CT::poison(&last_byte_pos, 1); |
197 | 0 | CT::poison(buffer.data(), buffer.size()); |
198 | |
|
199 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); |
200 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() >= BS); |
201 | |
|
202 | 0 | const size_t start_of_last_block = buffer.size() - BS; |
203 | 0 | const size_t end_of_last_block = buffer.size(); |
204 | 0 | const size_t start_of_padding = buffer.size() - padding_len; |
205 | |
|
206 | 0 | for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { |
207 | 0 | auto needs_80 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_equal(i, start_of_padding)); |
208 | 0 | auto needs_00 = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gt(i, start_of_padding)); |
209 | 0 | buffer[i] = needs_00.select(0x00, needs_80.select(0x80, buffer[i])); |
210 | 0 | } |
211 | |
|
212 | 0 | CT::unpoison(buffer.data(), buffer.size()); |
213 | 0 | CT::unpoison(last_byte_pos); |
214 | 0 | } |
215 | | |
216 | | /* |
217 | | * Unpad with One and Zeros Method |
218 | | */ |
219 | 175 | size_t OneAndZeros_Padding::unpad(const uint8_t input[], size_t input_length) const { |
220 | 175 | if(!valid_blocksize(input_length)) { |
221 | 0 | return input_length; |
222 | 0 | } |
223 | | |
224 | 175 | CT::poison(input, input_length); |
225 | | |
226 | 175 | auto bad_input = CT::Mask<uint8_t>::cleared(); |
227 | 175 | auto seen_0x80 = CT::Mask<uint8_t>::cleared(); |
228 | | |
229 | 175 | size_t pad_pos = input_length - 1; |
230 | 175 | size_t i = input_length; |
231 | | |
232 | 164k | while(i) { |
233 | 164k | const auto is_0x80 = CT::Mask<uint8_t>::is_equal(input[i - 1], 0x80); |
234 | 164k | const auto is_zero = CT::Mask<uint8_t>::is_zero(input[i - 1]); |
235 | | |
236 | 164k | seen_0x80 |= is_0x80; |
237 | 164k | pad_pos -= seen_0x80.if_not_set_return(1); |
238 | 164k | bad_input |= ~seen_0x80 & ~is_zero; |
239 | 164k | i--; |
240 | 164k | } |
241 | 175 | bad_input |= ~seen_0x80; |
242 | | |
243 | 175 | CT::unpoison(input, input_length); |
244 | | |
245 | 175 | return CT::Mask<size_t>::expand(bad_input).select_and_unpoison(input_length, pad_pos); |
246 | 175 | } |
247 | | |
248 | | /* |
249 | | * Pad with ESP Padding Method |
250 | | */ |
251 | 0 | void ESP_Padding::add_padding(secure_vector<uint8_t>& buffer, size_t last_byte_pos, size_t BS) const { |
252 | | /* |
253 | | Padding format is |
254 | | 01 |
255 | | 0102 |
256 | | 010203 |
257 | | ... |
258 | | */ |
259 | 0 | BOTAN_DEBUG_ASSERT(last_byte_pos < BS); |
260 | |
|
261 | 0 | const uint8_t padding_len = static_cast<uint8_t>(BS - last_byte_pos); |
262 | |
|
263 | 0 | buffer.resize(buffer.size() + padding_len); |
264 | |
|
265 | 0 | CT::poison(&last_byte_pos, 1); |
266 | 0 | CT::poison(buffer.data(), buffer.size()); |
267 | |
|
268 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() % BS == 0); |
269 | 0 | BOTAN_DEBUG_ASSERT(buffer.size() >= BS); |
270 | |
|
271 | 0 | const size_t start_of_last_block = buffer.size() - BS; |
272 | 0 | const size_t end_of_last_block = buffer.size(); |
273 | 0 | const size_t start_of_padding = buffer.size() - padding_len; |
274 | |
|
275 | 0 | uint8_t pad_ctr = 0x01; |
276 | |
|
277 | 0 | for(size_t i = start_of_last_block; i != end_of_last_block; ++i) { |
278 | 0 | auto needs_padding = CT::Mask<uint8_t>(CT::Mask<size_t>::is_gte(i, start_of_padding)); |
279 | 0 | buffer[i] = needs_padding.select(pad_ctr, buffer[i]); |
280 | 0 | pad_ctr = needs_padding.select(pad_ctr + 1, pad_ctr); |
281 | 0 | } |
282 | |
|
283 | 0 | CT::unpoison(buffer.data(), buffer.size()); |
284 | 0 | CT::unpoison(last_byte_pos); |
285 | 0 | } |
286 | | |
287 | | /* |
288 | | * Unpad with ESP Padding Method |
289 | | */ |
290 | 138 | size_t ESP_Padding::unpad(const uint8_t input[], size_t input_length) const { |
291 | 138 | if(!valid_blocksize(input_length)) { |
292 | 0 | return input_length; |
293 | 0 | } |
294 | | |
295 | 138 | CT::poison(input, input_length); |
296 | | |
297 | 138 | const uint8_t input_length_8 = static_cast<uint8_t>(input_length); |
298 | 138 | const uint8_t last_byte = input[input_length - 1]; |
299 | | |
300 | 138 | auto bad_input = CT::Mask<uint8_t>::is_zero(last_byte) | CT::Mask<uint8_t>::is_gt(last_byte, input_length_8); |
301 | | |
302 | 138 | const uint8_t pad_pos = input_length_8 - last_byte; |
303 | 138 | size_t i = input_length_8 - 1; |
304 | 5.20k | while(i) { |
305 | 5.07k | const auto in_range = CT::Mask<size_t>::is_gt(i, pad_pos); |
306 | 5.07k | const auto incrementing = CT::Mask<uint8_t>::is_equal(input[i - 1], input[i] - 1); |
307 | | |
308 | 5.07k | bad_input |= CT::Mask<uint8_t>(in_range) & ~incrementing; |
309 | 5.07k | --i; |
310 | 5.07k | } |
311 | | |
312 | 138 | CT::unpoison(input, input_length); |
313 | 138 | return bad_input.select_and_unpoison(input_length_8, pad_pos); |
314 | 138 | } |
315 | | |
316 | | } // namespace Botan |