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