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