1 | /// Decode base64 `input`, writing the result into `output`. |
2 | /// |
3 | /// `input` is treated as secret, so efforts are made to avoid |
4 | /// leaking its value via side channels, such as timing, |
5 | /// memory accesses, and execution trace. |
6 | /// |
7 | /// The following is deemed non-secret information: |
8 | /// |
9 | /// - Appearance of whitespace in `input` |
10 | /// - Erroneous characters in `input` (indeed, the first illegal |
11 | /// character is quoted in the error type) |
12 | /// - The length of `input` |
13 | /// - The length of `output` |
14 | /// |
15 | /// Returns the prefix of `output` that was written to. |
16 | pub(crate) fn decode_secret<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { |
17 | decode(input, output, decode_byte:CodePoint::decode_secret) |
18 | } |
19 | |
20 | /// Decode base64 `input`, writing the result into `output`. |
21 | /// |
22 | /// `input` is treated as public information, so its value may |
23 | /// be leaked via side channels. |
24 | /// |
25 | /// Returns the prefix of `output` that was written to. |
26 | pub(crate) fn decode_public<'a>(input: &[u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { |
27 | decode(input, output, decode_byte:CodePoint::decode_public) |
28 | } |
29 | |
30 | /// Provide an upper limit on how much space could be required |
31 | /// to decode a base64 encoding of len `base64_len`. |
32 | pub(crate) const fn decoded_length(base64_len: usize) -> usize { |
33 | ((base64_len + 3) / 4) * 3 |
34 | } |
35 | |
36 | fn decode<'a>( |
37 | input: &[u8], |
38 | output: &'a mut [u8], |
39 | decode_byte: impl Fn(u8) -> CodePoint, |
40 | ) -> Result<&'a [u8], Error> { |
41 | let mut buffer = 0u64; |
42 | let mut used = 0; |
43 | let mut shift = SHIFT_INITIAL; |
44 | let mut pad_mask = 0; |
45 | |
46 | let mut output_offset = 0; |
47 | |
48 | const SHIFT_INITIAL: i32 = (8 - 1) * 6; |
49 | |
50 | for byte in input.iter().copied() { |
51 | let (item, pad) = match decode_byte(byte) { |
52 | CodePoint::WHITESPACE => continue, |
53 | CodePoint::INVALID => return Err(Error::InvalidCharacter(byte)), |
54 | CodePoint::PAD => (0, 1), |
55 | CodePoint(n) => (n, 0), |
56 | }; |
57 | |
58 | // we collect 8 code points (therefore: 6 output bytes) into |
59 | // `buffer`. this keeps this loop as tight as possible. |
60 | if used == 8 { |
61 | if pad_mask != 0b0000_0000 { |
62 | return Err(Error::PrematurePadding); |
63 | } |
64 | |
65 | let chunk = output |
66 | .get_mut(output_offset..output_offset + 6) |
67 | .ok_or(Error::InsufficientOutputSpace)?; |
68 | |
69 | chunk[0] = (buffer >> 40) as u8; |
70 | chunk[1] = (buffer >> 32) as u8; |
71 | chunk[2] = (buffer >> 24) as u8; |
72 | chunk[3] = (buffer >> 16) as u8; |
73 | chunk[4] = (buffer >> 8) as u8; |
74 | chunk[5] = buffer as u8; |
75 | |
76 | output_offset += 6; |
77 | buffer = 0; |
78 | used = 0; |
79 | pad_mask = 0; |
80 | shift = SHIFT_INITIAL; |
81 | } |
82 | |
83 | buffer |= (item as u64) << shift; |
84 | shift -= 6; |
85 | pad_mask |= pad << used; |
86 | used += 1; |
87 | } |
88 | |
89 | // reduce to final block |
90 | if used > 4 { |
91 | if pad_mask & 0b0000_1111 != 0 { |
92 | return Err(Error::PrematurePadding); |
93 | } |
94 | let chunk = output |
95 | .get_mut(output_offset..output_offset + 3) |
96 | .ok_or(Error::InsufficientOutputSpace)?; |
97 | chunk[0] = (buffer >> 40) as u8; |
98 | chunk[1] = (buffer >> 32) as u8; |
99 | chunk[2] = (buffer >> 24) as u8; |
100 | |
101 | buffer <<= 24; |
102 | pad_mask >>= 4; |
103 | used -= 4; |
104 | output_offset += 3; |
105 | } |
106 | |
107 | match (used, pad_mask) { |
108 | // no trailing bytes |
109 | (0, 0b0000) => {} |
110 | |
111 | // 4 trailing bytes, no padding |
112 | (4, 0b0000) => { |
113 | let chunk = output |
114 | .get_mut(output_offset..output_offset + 3) |
115 | .ok_or(Error::InsufficientOutputSpace)?; |
116 | chunk[0] = (buffer >> 40) as u8; |
117 | chunk[1] = (buffer >> 32) as u8; |
118 | chunk[2] = (buffer >> 24) as u8; |
119 | output_offset += 3; |
120 | } |
121 | |
122 | // 4 trailing bytes with one padding char, or 3 trailing bytes |
123 | (4, 0b1000) | (3, 0b0000) => { |
124 | let chunk = output |
125 | .get_mut(output_offset..output_offset + 2) |
126 | .ok_or(Error::InsufficientOutputSpace)?; |
127 | |
128 | chunk[0] = (buffer >> 40) as u8; |
129 | chunk[1] = (buffer >> 32) as u8; |
130 | output_offset += 2; |
131 | } |
132 | |
133 | // 4 trailing bytes with two padding char, or 2 trailing bytes |
134 | (4, 0b1100) | (2, 0b0000) => { |
135 | let chunk = output |
136 | .get_mut(output_offset..output_offset + 1) |
137 | .ok_or(Error::InsufficientOutputSpace)?; |
138 | chunk[0] = (buffer >> 40) as u8; |
139 | output_offset += 1; |
140 | } |
141 | |
142 | // everything else is illegal |
143 | _ => return Err(Error::InvalidTrailingPadding), |
144 | } |
145 | |
146 | Ok(&output[..output_offset]) |
147 | } |
148 | |
149 | #[derive (Debug, PartialEq)] |
150 | pub(crate) enum Error { |
151 | /// Given character is not valid in base64 alphabet. |
152 | InvalidCharacter(u8), |
153 | |
154 | /// A padding character (`=`) appeared outside the final |
155 | /// block of 4 characters. |
156 | PrematurePadding, |
157 | |
158 | /// The padding characters at the end of the input were invalid. |
159 | InvalidTrailingPadding, |
160 | |
161 | /// Not enough space in output buffer. |
162 | /// |
163 | /// Use `decoded_length` to get an upper bound. |
164 | InsufficientOutputSpace, |
165 | } |
166 | |
167 | #[derive (Copy, Clone, Debug, Eq, PartialEq)] |
168 | struct CodePoint(u8); |
169 | |
170 | impl CodePoint { |
171 | const WHITESPACE: Self = Self(0xf0); |
172 | const PAD: Self = Self(0xf1); |
173 | const INVALID: Self = Self(0xf2); |
174 | } |
175 | |
176 | impl CodePoint { |
177 | /// Side-channel rules: |
178 | /// |
179 | /// - code paths that produce `CodePoint(n)` must not make |
180 | /// `n` observable via a side channel. |
181 | /// - other code paths -- whitespace, padding or invalid -- need not, |
182 | /// these are not considered secret conditions. |
183 | fn decode_secret(b: u8) -> Self { |
184 | let is_upper = u8_in_range(b, b'A' , b'Z' ); |
185 | let is_lower = u8_in_range(b, b'a' , b'z' ); |
186 | let is_digit = u8_in_range(b, b'0' , b'9' ); |
187 | let is_plus = u8_equals(b, b'+' ); |
188 | let is_slash = u8_equals(b, b'/' ); |
189 | let is_pad = u8_equals(b, b'=' ); |
190 | let is_space = u8_in_range(b, b' \t' , b' \r' ) | u8_equals(b, b' ' ); |
191 | |
192 | let is_invalid = !(is_lower | is_upper | is_digit | is_plus | is_slash | is_pad | is_space); |
193 | |
194 | Self( |
195 | (is_upper & b.wrapping_sub(b'A' )) |
196 | | (is_lower & (b.wrapping_sub(b'a' ).wrapping_add(26))) |
197 | | (is_digit & (b.wrapping_sub(b'0' ).wrapping_add(52))) |
198 | | (is_plus & 62) |
199 | | (is_slash & 63) |
200 | | (is_space & Self::WHITESPACE.0) |
201 | | (is_pad & Self::PAD.0) |
202 | | (is_invalid & Self::INVALID.0), |
203 | ) |
204 | } |
205 | |
206 | fn decode_public(a: u8) -> Self { |
207 | const TABLE: [CodePoint; 256] = [ |
208 | // 0x00..0x0f |
209 | CodePoint::INVALID, |
210 | CodePoint::INVALID, |
211 | CodePoint::INVALID, |
212 | CodePoint::INVALID, |
213 | CodePoint::INVALID, |
214 | CodePoint::INVALID, |
215 | CodePoint::INVALID, |
216 | CodePoint::INVALID, |
217 | CodePoint::INVALID, |
218 | CodePoint::WHITESPACE, |
219 | CodePoint::WHITESPACE, |
220 | CodePoint::WHITESPACE, |
221 | CodePoint::WHITESPACE, |
222 | CodePoint::WHITESPACE, |
223 | CodePoint::INVALID, |
224 | CodePoint::INVALID, |
225 | // 0x10..0x1f |
226 | CodePoint::INVALID, |
227 | CodePoint::INVALID, |
228 | CodePoint::INVALID, |
229 | CodePoint::INVALID, |
230 | CodePoint::INVALID, |
231 | CodePoint::INVALID, |
232 | CodePoint::INVALID, |
233 | CodePoint::INVALID, |
234 | CodePoint::INVALID, |
235 | CodePoint::INVALID, |
236 | CodePoint::INVALID, |
237 | CodePoint::INVALID, |
238 | CodePoint::INVALID, |
239 | CodePoint::INVALID, |
240 | CodePoint::INVALID, |
241 | CodePoint::INVALID, |
242 | // 0x20..0x2f |
243 | CodePoint::WHITESPACE, |
244 | CodePoint::INVALID, |
245 | CodePoint::INVALID, |
246 | CodePoint::INVALID, |
247 | CodePoint::INVALID, |
248 | CodePoint::INVALID, |
249 | CodePoint::INVALID, |
250 | CodePoint::INVALID, |
251 | CodePoint::INVALID, |
252 | CodePoint::INVALID, |
253 | CodePoint::INVALID, |
254 | CodePoint(62), |
255 | CodePoint::INVALID, |
256 | CodePoint::INVALID, |
257 | CodePoint::INVALID, |
258 | CodePoint(63), |
259 | // 0x30..0x3f |
260 | CodePoint(52), |
261 | CodePoint(53), |
262 | CodePoint(54), |
263 | CodePoint(55), |
264 | CodePoint(56), |
265 | CodePoint(57), |
266 | CodePoint(58), |
267 | CodePoint(59), |
268 | CodePoint(60), |
269 | CodePoint(61), |
270 | CodePoint::INVALID, |
271 | CodePoint::INVALID, |
272 | CodePoint::INVALID, |
273 | CodePoint::PAD, |
274 | CodePoint::INVALID, |
275 | CodePoint::INVALID, |
276 | // 0x40..0x4f |
277 | CodePoint::INVALID, |
278 | CodePoint(0), |
279 | CodePoint(1), |
280 | CodePoint(2), |
281 | CodePoint(3), |
282 | CodePoint(4), |
283 | CodePoint(5), |
284 | CodePoint(6), |
285 | CodePoint(7), |
286 | CodePoint(8), |
287 | CodePoint(9), |
288 | CodePoint(10), |
289 | CodePoint(11), |
290 | CodePoint(12), |
291 | CodePoint(13), |
292 | CodePoint(14), |
293 | // 0x50..0x5f |
294 | CodePoint(15), |
295 | CodePoint(16), |
296 | CodePoint(17), |
297 | CodePoint(18), |
298 | CodePoint(19), |
299 | CodePoint(20), |
300 | CodePoint(21), |
301 | CodePoint(22), |
302 | CodePoint(23), |
303 | CodePoint(24), |
304 | CodePoint(25), |
305 | CodePoint::INVALID, |
306 | CodePoint::INVALID, |
307 | CodePoint::INVALID, |
308 | CodePoint::INVALID, |
309 | CodePoint::INVALID, |
310 | // 0x60..0x6f |
311 | CodePoint::INVALID, |
312 | CodePoint(26), |
313 | CodePoint(27), |
314 | CodePoint(28), |
315 | CodePoint(29), |
316 | CodePoint(30), |
317 | CodePoint(31), |
318 | CodePoint(32), |
319 | CodePoint(33), |
320 | CodePoint(34), |
321 | CodePoint(35), |
322 | CodePoint(36), |
323 | CodePoint(37), |
324 | CodePoint(38), |
325 | CodePoint(39), |
326 | CodePoint(40), |
327 | // 0x70..0x7f |
328 | CodePoint(41), |
329 | CodePoint(42), |
330 | CodePoint(43), |
331 | CodePoint(44), |
332 | CodePoint(45), |
333 | CodePoint(46), |
334 | CodePoint(47), |
335 | CodePoint(48), |
336 | CodePoint(49), |
337 | CodePoint(50), |
338 | CodePoint(51), |
339 | CodePoint::INVALID, |
340 | CodePoint::INVALID, |
341 | CodePoint::INVALID, |
342 | CodePoint::INVALID, |
343 | CodePoint::INVALID, |
344 | // 0x80..0x8f |
345 | CodePoint::INVALID, |
346 | CodePoint::INVALID, |
347 | CodePoint::INVALID, |
348 | CodePoint::INVALID, |
349 | CodePoint::INVALID, |
350 | CodePoint::INVALID, |
351 | CodePoint::INVALID, |
352 | CodePoint::INVALID, |
353 | CodePoint::INVALID, |
354 | CodePoint::INVALID, |
355 | CodePoint::INVALID, |
356 | CodePoint::INVALID, |
357 | CodePoint::INVALID, |
358 | CodePoint::INVALID, |
359 | CodePoint::INVALID, |
360 | CodePoint::INVALID, |
361 | // 0x90..0x9f |
362 | CodePoint::INVALID, |
363 | CodePoint::INVALID, |
364 | CodePoint::INVALID, |
365 | CodePoint::INVALID, |
366 | CodePoint::INVALID, |
367 | CodePoint::INVALID, |
368 | CodePoint::INVALID, |
369 | CodePoint::INVALID, |
370 | CodePoint::INVALID, |
371 | CodePoint::INVALID, |
372 | CodePoint::INVALID, |
373 | CodePoint::INVALID, |
374 | CodePoint::INVALID, |
375 | CodePoint::INVALID, |
376 | CodePoint::INVALID, |
377 | CodePoint::INVALID, |
378 | // 0xa0..0xaf |
379 | CodePoint::INVALID, |
380 | CodePoint::INVALID, |
381 | CodePoint::INVALID, |
382 | CodePoint::INVALID, |
383 | CodePoint::INVALID, |
384 | CodePoint::INVALID, |
385 | CodePoint::INVALID, |
386 | CodePoint::INVALID, |
387 | CodePoint::INVALID, |
388 | CodePoint::INVALID, |
389 | CodePoint::INVALID, |
390 | CodePoint::INVALID, |
391 | CodePoint::INVALID, |
392 | CodePoint::INVALID, |
393 | CodePoint::INVALID, |
394 | CodePoint::INVALID, |
395 | // 0xb0..0xbf |
396 | CodePoint::INVALID, |
397 | CodePoint::INVALID, |
398 | CodePoint::INVALID, |
399 | CodePoint::INVALID, |
400 | CodePoint::INVALID, |
401 | CodePoint::INVALID, |
402 | CodePoint::INVALID, |
403 | CodePoint::INVALID, |
404 | CodePoint::INVALID, |
405 | CodePoint::INVALID, |
406 | CodePoint::INVALID, |
407 | CodePoint::INVALID, |
408 | CodePoint::INVALID, |
409 | CodePoint::INVALID, |
410 | CodePoint::INVALID, |
411 | CodePoint::INVALID, |
412 | // 0xc0..0xcf |
413 | CodePoint::INVALID, |
414 | CodePoint::INVALID, |
415 | CodePoint::INVALID, |
416 | CodePoint::INVALID, |
417 | CodePoint::INVALID, |
418 | CodePoint::INVALID, |
419 | CodePoint::INVALID, |
420 | CodePoint::INVALID, |
421 | CodePoint::INVALID, |
422 | CodePoint::INVALID, |
423 | CodePoint::INVALID, |
424 | CodePoint::INVALID, |
425 | CodePoint::INVALID, |
426 | CodePoint::INVALID, |
427 | CodePoint::INVALID, |
428 | CodePoint::INVALID, |
429 | // 0xd0..0xdf |
430 | CodePoint::INVALID, |
431 | CodePoint::INVALID, |
432 | CodePoint::INVALID, |
433 | CodePoint::INVALID, |
434 | CodePoint::INVALID, |
435 | CodePoint::INVALID, |
436 | CodePoint::INVALID, |
437 | CodePoint::INVALID, |
438 | CodePoint::INVALID, |
439 | CodePoint::INVALID, |
440 | CodePoint::INVALID, |
441 | CodePoint::INVALID, |
442 | CodePoint::INVALID, |
443 | CodePoint::INVALID, |
444 | CodePoint::INVALID, |
445 | CodePoint::INVALID, |
446 | // 0xe0..0xef |
447 | CodePoint::INVALID, |
448 | CodePoint::INVALID, |
449 | CodePoint::INVALID, |
450 | CodePoint::INVALID, |
451 | CodePoint::INVALID, |
452 | CodePoint::INVALID, |
453 | CodePoint::INVALID, |
454 | CodePoint::INVALID, |
455 | CodePoint::INVALID, |
456 | CodePoint::INVALID, |
457 | CodePoint::INVALID, |
458 | CodePoint::INVALID, |
459 | CodePoint::INVALID, |
460 | CodePoint::INVALID, |
461 | CodePoint::INVALID, |
462 | CodePoint::INVALID, |
463 | // 0xf0..0xff |
464 | CodePoint::INVALID, |
465 | CodePoint::INVALID, |
466 | CodePoint::INVALID, |
467 | CodePoint::INVALID, |
468 | CodePoint::INVALID, |
469 | CodePoint::INVALID, |
470 | CodePoint::INVALID, |
471 | CodePoint::INVALID, |
472 | CodePoint::INVALID, |
473 | CodePoint::INVALID, |
474 | CodePoint::INVALID, |
475 | CodePoint::INVALID, |
476 | CodePoint::INVALID, |
477 | CodePoint::INVALID, |
478 | CodePoint::INVALID, |
479 | CodePoint::INVALID, |
480 | ]; |
481 | |
482 | TABLE[a as usize] |
483 | } |
484 | } |
485 | |
486 | /// Returns 0xff if `a` in `lo..=hi`. |
487 | /// |
488 | /// lo..=hi must not be 0..=255. Callers in this file have constant |
489 | /// `lo` and `hi`, and this function is private to this file. |
490 | fn u8_in_range(a: u8, lo: u8, hi: u8) -> u8 { |
491 | debug_assert!(lo <= hi); |
492 | debug_assert!(hi - lo != 255); |
493 | let a: u8 = a.wrapping_sub(lo); |
494 | u8_less_than(a, (hi - lo).wrapping_add(1)) |
495 | } |
496 | |
497 | /// Returns 0xff if a < b, 0 otherwise. |
498 | fn u8_less_than(a: u8, b: u8) -> u8 { |
499 | let a: u16 = u16::from(a); |
500 | let b: u16 = u16::from(b); |
501 | u8_broadcast16(a.wrapping_sub(b)) |
502 | } |
503 | |
504 | /// Returns 0xff if a == b, 0 otherwise. |
505 | fn u8_equals(a: u8, b: u8) -> u8 { |
506 | let diff: u8 = a ^ b; |
507 | u8_nonzero(diff) |
508 | } |
509 | |
510 | /// Returns 0xff if a != 0, 0 otherwise. |
511 | fn u8_nonzero(x: u8) -> u8 { |
512 | u8_broadcast8(!x & x.wrapping_sub(1)) |
513 | } |
514 | |
515 | /// Broadcasts the top bit of `x` |
516 | /// |
517 | /// In other words, if the top bit of `x` is set, |
518 | /// returns 0xff else 0x00. |
519 | fn u8_broadcast8(x: u8) -> u8 { |
520 | let msb: u8 = x >> 7; |
521 | 0u8.wrapping_sub(msb) |
522 | } |
523 | |
524 | /// Broadcasts the top bit of `x` |
525 | /// |
526 | /// In other words, if the top bit of `x` is set, |
527 | /// returns 0xff else 0x00. |
528 | fn u8_broadcast16(x: u16) -> u8 { |
529 | let msb: u16 = x >> 15; |
530 | 0u8.wrapping_sub(msb as u8) |
531 | } |
532 | |
533 | #[cfg (all(test, feature = "alloc" ))] |
534 | mod tests { |
535 | use super::*; |
536 | |
537 | #[test ] |
538 | fn decode_test() { |
539 | assert_eq!( |
540 | decode(b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ), |
541 | b" \x00\x10\x83\x10\x51\x87\x20\x92\x8b\x30\xd3\x8f\x41\x14\x93\x51\x55\x97\ |
542 | \x61\x96\x9b\x71\xd7\x9f\x82\x18\xa3\x92\x59\xa7\xa2\x9a\xab\xb2\xdb\xaf\ |
543 | \xc3\x1c\xb3\xd3\x5d\xb7\xe3\x9e\xbb\xf3\xdf\xbf" |
544 | ); |
545 | assert_eq!(decode(b"aGVsbG8=" ), b"hello" ); |
546 | assert_eq!(decode(b"aGVsbG8gd29ybGQ=" ), b"hello world" ); |
547 | assert_eq!(decode(b"aGVsbG8gd29ybGQh" ), b"hello world!" ); |
548 | assert_eq!(decode(b"////" ), b" \xff\xff\xff" ); |
549 | assert_eq!(decode(b"++++" ), b" \xfb\xef\xbe" ); |
550 | assert_eq!(decode(b"AAAA" ), b" \x00\x00\x00" ); |
551 | assert_eq!(decode(b"AAA=" ), b" \x00\x00" ); |
552 | assert_eq!(decode(b"AA==" ), b" \x00" ); |
553 | |
554 | // like our previous use of rust-base64, we don't require padding |
555 | // if the encoding is otherwise valid given the length |
556 | assert_eq!(decode(b"AAA" ), b" \x00\x00" ); |
557 | assert_eq!(decode(b"AA" ), b" \x00" ); |
558 | |
559 | assert_eq!(decode(b"" ), b"" ); |
560 | } |
561 | |
562 | #[test ] |
563 | fn decode_errors() { |
564 | let mut buf = [0u8; 6]; |
565 | |
566 | // illegal trailing padding |
567 | assert_eq!( |
568 | decode_both(b"A===" , &mut buf), |
569 | Err(Error::InvalidTrailingPadding) |
570 | ); |
571 | assert_eq!( |
572 | decode_both(b"====" , &mut buf), |
573 | Err(Error::InvalidTrailingPadding) |
574 | ); |
575 | assert_eq!( |
576 | decode_both(b"A==" , &mut buf), |
577 | Err(Error::InvalidTrailingPadding) |
578 | ); |
579 | assert_eq!( |
580 | decode_both(b"AA=" , &mut buf), |
581 | Err(Error::InvalidTrailingPadding) |
582 | ); |
583 | assert_eq!( |
584 | decode_both(b"A" , &mut buf), |
585 | Err(Error::InvalidTrailingPadding) |
586 | ); |
587 | |
588 | // padding before final block |
589 | assert_eq!( |
590 | decode_both(b"=AAAAA==" , &mut buf), |
591 | Err(Error::PrematurePadding) |
592 | ); |
593 | assert_eq!( |
594 | decode_both(b"A=AAAA==" , &mut buf), |
595 | Err(Error::PrematurePadding) |
596 | ); |
597 | assert_eq!( |
598 | decode_both(b"AA=AAA==" , &mut buf), |
599 | Err(Error::PrematurePadding) |
600 | ); |
601 | assert_eq!( |
602 | decode_both(b"AAA=AA==" , &mut buf), |
603 | Err(Error::PrematurePadding) |
604 | ); |
605 | |
606 | // illegal inputs |
607 | assert_eq!( |
608 | decode_both(b"%AAA" , &mut buf), |
609 | Err(Error::InvalidCharacter(b'%' )) |
610 | ); |
611 | assert_eq!( |
612 | decode_both(b"A%AA" , &mut buf), |
613 | Err(Error::InvalidCharacter(b'%' )) |
614 | ); |
615 | assert_eq!( |
616 | decode_both(b"AA%A" , &mut buf), |
617 | Err(Error::InvalidCharacter(b'%' )) |
618 | ); |
619 | assert_eq!( |
620 | decode_both(b"AAA%" , &mut buf), |
621 | Err(Error::InvalidCharacter(b'%' )) |
622 | ); |
623 | |
624 | // output sizing |
625 | assert_eq!(decode_both(b"am9lIGJw" , &mut [0u8; 7]), Ok(&b"joe bp" [..])); |
626 | assert_eq!(decode_both(b"am9lIGJw" , &mut [0u8; 6]), Ok(&b"joe bp" [..])); |
627 | assert_eq!( |
628 | decode_both(b"am9lIGJw" , &mut [0u8; 5]), |
629 | Err(Error::InsufficientOutputSpace) |
630 | ); |
631 | assert_eq!( |
632 | decode_both(b"am9lIGJw" , &mut [0u8; 4]), |
633 | Err(Error::InsufficientOutputSpace) |
634 | ); |
635 | assert_eq!( |
636 | decode_both(b"am9lIGJw" , &mut [0u8; 3]), |
637 | Err(Error::InsufficientOutputSpace) |
638 | ); |
639 | |
640 | // output sizing is not pessimistic when padding is valid |
641 | assert_eq!(decode_both(b"am9=" , &mut [0u8; 2]), Ok(&b"jo" [..])); |
642 | assert_eq!(decode_both(b"am==" , &mut [0u8; 1]), Ok(&b"j" [..])); |
643 | assert_eq!(decode_both(b"am9" , &mut [0u8; 2]), Ok(&b"jo" [..])); |
644 | assert_eq!(decode_both(b"am" , &mut [0u8; 1]), Ok(&b"j" [..])); |
645 | } |
646 | |
647 | #[test ] |
648 | fn check_models() { |
649 | fn u8_broadcast8_model(x: u8) -> u8 { |
650 | match x & 0x80 { |
651 | 0x80 => 0xff, |
652 | _ => 0x00, |
653 | } |
654 | } |
655 | |
656 | fn u8_broadcast16_model(x: u16) -> u8 { |
657 | match x & 0x8000 { |
658 | 0x8000 => 0xff, |
659 | _ => 0x00, |
660 | } |
661 | } |
662 | |
663 | fn u8_nonzero_model(x: u8) -> u8 { |
664 | match x { |
665 | 0 => 0xff, |
666 | _ => 0x00, |
667 | } |
668 | } |
669 | |
670 | fn u8_equals_model(x: u8, y: u8) -> u8 { |
671 | match x == y { |
672 | true => 0xff, |
673 | false => 0x00, |
674 | } |
675 | } |
676 | |
677 | fn u8_in_range_model(x: u8, y: u8, z: u8) -> u8 { |
678 | match (y..=z).contains(&x) { |
679 | true => 0xff, |
680 | false => 0x00, |
681 | } |
682 | } |
683 | |
684 | for x in u8::MIN..=u8::MAX { |
685 | assert_eq!(u8_broadcast8(x), u8_broadcast8_model(x)); |
686 | assert_eq!(u8_nonzero(x), u8_nonzero_model(x)); |
687 | assert_eq!(CodePoint::decode_secret(x), CodePoint::decode_public(x)); |
688 | |
689 | for y in u8::MIN..=u8::MAX { |
690 | assert_eq!(u8_equals(x, y), u8_equals_model(x, y)); |
691 | |
692 | let v = (x as u16) | ((y as u16) << 8); |
693 | assert_eq!(u8_broadcast16(v), u8_broadcast16_model(v)); |
694 | |
695 | for z in y..=u8::MAX { |
696 | if z - y == 255 { |
697 | continue; |
698 | } |
699 | assert_eq!(u8_in_range(x, y, z), u8_in_range_model(x, y, z)); |
700 | } |
701 | } |
702 | } |
703 | } |
704 | |
705 | #[cfg (all(feature = "std" , target_os = "linux" , target_arch = "x86_64" ))] |
706 | #[test ] |
707 | fn codepoint_decode_secret_does_not_branch_or_index_on_secret_input() { |
708 | // this is using the same theory as <https://github.com/agl/ctgrind> |
709 | use crabgrind as cg; |
710 | |
711 | if matches!(cg::run_mode(), cg::RunMode::Native) { |
712 | std::println!("SKIPPED: must be run under valgrind" ); |
713 | return; |
714 | } |
715 | |
716 | let input = [b'a' ]; |
717 | cg::monitor_command(format!( |
718 | "make_memory undefined {:p} {}" , |
719 | input.as_ptr(), |
720 | input.len() |
721 | )) |
722 | .unwrap(); |
723 | |
724 | core::hint::black_box(CodePoint::decode_secret(input[0])); |
725 | } |
726 | |
727 | #[track_caller ] |
728 | fn decode(input: &[u8]) -> alloc::vec::Vec<u8> { |
729 | let length = decoded_length(input.len()); |
730 | |
731 | let mut v = alloc::vec![0u8; length]; |
732 | let used = decode_both(input, &mut v).unwrap().len(); |
733 | v.truncate(used); |
734 | |
735 | v |
736 | } |
737 | |
738 | fn decode_both<'a>(input: &'_ [u8], output: &'a mut [u8]) -> Result<&'a [u8], Error> { |
739 | let mut output_copy = output.to_vec(); |
740 | let r_pub = decode_public(input, &mut output_copy); |
741 | |
742 | let r_sec = decode_secret(input, output); |
743 | |
744 | assert_eq!(r_pub, r_sec); |
745 | |
746 | r_sec |
747 | } |
748 | } |
749 | |