Skip to content

Commit 4b73e99

Browse files
committed
Harden scoped pentest findings
1 parent ceec487 commit 4b73e99

2 files changed

Lines changed: 123 additions & 49 deletions

File tree

src/lib.rs

Lines changed: 121 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,8 +1232,8 @@ pub mod stream {
12321232
pending_len: usize,
12331233
output: OutputQueue<1024>,
12341234
finished: bool,
1235-
finalized: bool,
12361235
failed: bool,
1236+
finalized: bool,
12371237
}
12381238

12391239
impl<W, A, const PAD: bool> Decoder<W, A, PAD>
@@ -1693,6 +1693,13 @@ pub mod stream {
16931693
)
16941694
}
16951695

1696+
fn stream_encoder_failed_error() -> io::Error {
1697+
io::Error::new(
1698+
io::ErrorKind::InvalidInput,
1699+
"base64 stream encoder is failed after internal error",
1700+
)
1701+
}
1702+
16961703
/// A streaming Base64 decoder for `std::io::Read`.
16971704
///
16981705
/// For padded engines, this reader stops at the terminal padded Base64
@@ -2067,6 +2074,7 @@ pub mod stream {
20672074
pending_len: usize,
20682075
output: OutputQueue<1024>,
20692076
finished: bool,
2077+
failed: bool,
20702078
}
20712079

20722080
impl<R, A, const PAD: bool> EncoderReader<R, A, PAD>
@@ -2083,6 +2091,7 @@ pub mod stream {
20832091
pending_len: 0,
20842092
output: OutputQueue::new(),
20852093
finished: false,
2094+
failed: false,
20862095
}
20872096
}
20882097

@@ -2181,11 +2190,18 @@ pub mod stream {
21812190
self.finished && self.output.is_empty()
21822191
}
21832192

2193+
/// Returns whether this adapter has failed closed after an internal
2194+
/// stream error.
2195+
#[must_use]
2196+
pub const fn is_failed(&self) -> bool {
2197+
self.failed
2198+
}
2199+
21842200
/// Returns whether [`Self::try_into_inner`] can recover the wrapped
21852201
/// reader without discarding pending input or buffered encoded output.
21862202
#[must_use]
21872203
pub const fn can_into_inner(&self) -> bool {
2188-
self.is_finished()
2204+
self.is_finished() && !self.failed
21892205
}
21902206

21912207
/// Consumes the encoder reader and returns the wrapped reader.
@@ -2265,6 +2281,7 @@ pub mod stream {
22652281
)
22662282
.field("can_into_inner", &self.can_into_inner())
22672283
.field("finished", &self.finished)
2284+
.field("failed", &self.failed)
22682285
.finish()
22692286
}
22702287
}
@@ -2275,6 +2292,10 @@ pub mod stream {
22752292
A: Alphabet,
22762293
{
22772294
fn read(&mut self, output: &mut [u8]) -> io::Result<usize> {
2295+
if self.failed {
2296+
return Err(stream_encoder_failed_error());
2297+
}
2298+
22782299
if output.is_empty() {
22792300
return Ok(0);
22802301
}
@@ -2304,7 +2325,10 @@ pub mod stream {
23042325
if read == 0 {
23052326
crate::wipe_bytes(&mut input);
23062327
self.finished = true;
2307-
self.push_final_pending()?;
2328+
if let Err(err) = self.push_final_pending() {
2329+
self.failed = true;
2330+
return Err(err);
2331+
}
23082332
return Ok(());
23092333
}
23102334

@@ -2326,6 +2350,7 @@ pub mod stream {
23262350
crate::wipe_bytes(&mut chunk);
23272351
if let Err(err) = result {
23282352
crate::wipe_bytes(&mut input);
2353+
self.failed = true;
23292354
return Err(err);
23302355
}
23312356
self.clear_pending();
@@ -2345,6 +2370,7 @@ pub mod stream {
23452370
crate::wipe_bytes(&mut input);
23462371
if let Err(err) = result {
23472372
crate::wipe_bytes(&mut tail);
2373+
self.failed = true;
23482374
return Err(err);
23492375
}
23502376
self.pending[..tail_len].copy_from_slice(&tail[..tail_len]);
@@ -7720,7 +7746,8 @@ fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77207746
let mut read = 0;
77217747

77227748
while read + 4 < input.len() {
7723-
let [b0, b1, b2, b3] = read_quad(input, read)?;
7749+
let [b0, b1, b2, b3] =
7750+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
77247751
let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
77257752
let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
77267753
let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -7735,7 +7762,8 @@ fn ct_validate_padded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77357762
read += 4;
77367763
}
77377764

7738-
let final_chunk = read_quad(input, read)?;
7765+
let final_chunk =
7766+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
77397767
let (_, final_invalid_byte, final_invalid_padding, _) =
77407768
ct_padded_final_quantum::<A>(final_chunk, padding);
77417769
invalid_byte |= final_invalid_byte;
@@ -7754,7 +7782,8 @@ fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77547782
let mut read = 0;
77557783

77567784
while read + 4 <= input.len() {
7757-
let [b0, b1, b2, b3] = read_quad(input, read)?;
7785+
let [b0, b1, b2, b3] =
7786+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
77587787
let (_, valid0) = ct_decode_alphabet_byte::<A>(b0);
77597788
let (_, valid1) = ct_decode_alphabet_byte::<A>(b1);
77607789
let (_, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -7772,9 +7801,9 @@ fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77727801
read += 4;
77737802
}
77747803

7775-
match input.get(read..) {
7776-
Some([]) => {}
7777-
Some([b0, b1]) => {
7804+
match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
7805+
[] => {}
7806+
[b0, b1] => {
77787807
let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
77797808
let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
77807809
invalid_byte |= !valid0;
@@ -7783,7 +7812,7 @@ fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77837812
invalid_padding |= ct_mask_eq_u8(*b1, b'=');
77847813
invalid_padding |= ct_mask_nonzero_u8(v1 & 0b0000_1111);
77857814
}
7786-
Some([b0, b1, b2]) => {
7815+
[b0, b1, b2] => {
77877816
let (_, valid0) = ct_decode_alphabet_byte::<A>(*b0);
77887817
let (_, valid1) = ct_decode_alphabet_byte::<A>(*b1);
77897818
let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
@@ -7795,7 +7824,10 @@ fn ct_validate_unpadded<A: Alphabet>(input: &[u8]) -> Result<(), DecodeError> {
77957824
invalid_padding |= ct_mask_eq_u8(*b2, b'=');
77967825
invalid_padding |= ct_mask_nonzero_u8(v2 & 0b0000_0011);
77977826
}
7798-
_ => return Err(DecodeError::InvalidLength),
7827+
_ => {
7828+
invalid_byte = 0xff;
7829+
invalid_padding = 0xff;
7830+
}
77997831
}
78007832

78017833
report_ct_error(invalid_byte, invalid_padding)
@@ -7815,7 +7847,7 @@ fn ct_padded_final_quantum<A: Alphabet>(
78157847
0 => 0,
78167848
1 => 1,
78177849
2 => 2,
7818-
_ => 3,
7850+
_ => return ([0; 3], 0xff, 0xff, 0),
78197851
};
78207852
let no_padding = ct_mask_eq_u8(padding_byte, 0);
78217853
let one_padding = ct_mask_eq_u8(padding_byte, 1);
@@ -7856,7 +7888,8 @@ fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usiz
78567888
let mut read = 0;
78577889

78587890
while read + 4 < input.len() {
7859-
let [b0, b1, b2, b3] = read_quad(input, read)?;
7891+
let [b0, b1, b2, b3] =
7892+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
78607893
let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
78617894
let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
78627895
let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -7875,7 +7908,8 @@ fn ct_decode_padded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<usiz
78757908
read += 4;
78767909
}
78777910

7878-
let final_chunk = read_quad(input, read)?;
7911+
let final_chunk =
7912+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
78797913
let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
78807914
ct_padded_final_quantum::<A>(final_chunk, padding);
78817915
invalid_byte |= final_invalid_byte;
@@ -7905,13 +7939,8 @@ fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, De
79057939
let mut read = 0;
79067940

79077941
while read + 4 < buffer.len() {
7908-
let [b0, b1, b2, b3] = match read_quad(buffer, read) {
7909-
Ok(quad) => quad,
7910-
Err(err) => {
7911-
wipe_bytes(buffer);
7912-
return Err(err);
7913-
}
7914-
};
7942+
let [b0, b1, b2, b3] =
7943+
read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
79157944
let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
79167945
let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
79177946
let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -7930,13 +7959,8 @@ fn ct_decode_padded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize, De
79307959
read += 4;
79317960
}
79327961

7933-
let final_chunk = match read_quad(buffer, read) {
7934-
Ok(quad) => quad,
7935-
Err(err) => {
7936-
wipe_bytes(buffer);
7937-
return Err(err);
7938-
}
7939-
};
7962+
let final_chunk =
7963+
read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
79407964
let (final_bytes, final_invalid_byte, final_invalid_padding, final_written) =
79417965
ct_padded_final_quantum::<A>(final_chunk, padding);
79427966
invalid_byte |= final_invalid_byte;
@@ -7972,7 +7996,8 @@ fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<us
79727996
let mut read = 0;
79737997

79747998
while read + 4 <= input.len() {
7975-
let [b0, b1, b2, b3] = read_quad(input, read)?;
7999+
let [b0, b1, b2, b3] =
8000+
read_quad_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding);
79768001
let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
79778002
let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
79788003
let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -7994,9 +8019,9 @@ fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<us
79948019
write += 3;
79958020
}
79968021

7997-
match input.get(read..) {
7998-
Some([]) => {}
7999-
Some([b0, b1]) => {
8022+
match read_tail_or_mark_invalid(input, read, &mut invalid_byte, &mut invalid_padding) {
8023+
[] => {}
8024+
[b0, b1] => {
80008025
let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
80018026
let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
80028027
invalid_byte |= !valid0;
@@ -8007,7 +8032,7 @@ fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<us
80078032
output[write] = (v0 << 2) | (v1 >> 4);
80088033
write += 1;
80098034
}
8010-
Some([b0, b1, b2]) => {
8035+
[b0, b1, b2] => {
80118036
let (v0, valid0) = ct_decode_alphabet_byte::<A>(*b0);
80128037
let (v1, valid1) = ct_decode_alphabet_byte::<A>(*b1);
80138038
let (v2, valid2) = ct_decode_alphabet_byte::<A>(*b2);
@@ -8022,7 +8047,10 @@ fn ct_decode_unpadded<A: Alphabet>(input: &[u8], output: &mut [u8]) -> Result<us
80228047
output[write + 1] = (v1 << 4) | (v2 >> 2);
80238048
write += 2;
80248049
}
8025-
_ => return Err(DecodeError::InvalidLength),
8050+
_ => {
8051+
invalid_byte = 0xff;
8052+
invalid_padding = 0xff;
8053+
}
80268054
}
80278055

80288056
report_ct_error(invalid_byte, invalid_padding)?;
@@ -8046,13 +8074,8 @@ fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize,
80468074
let mut read = 0;
80478075

80488076
while read + 4 <= buffer.len() {
8049-
let [b0, b1, b2, b3] = match read_quad(buffer, read) {
8050-
Ok(quad) => quad,
8051-
Err(err) => {
8052-
wipe_bytes(buffer);
8053-
return Err(err);
8054-
}
8055-
};
8077+
let [b0, b1, b2, b3] =
8078+
read_quad_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
80568079
let (v0, valid0) = ct_decode_alphabet_byte::<A>(b0);
80578080
let (v1, valid1) = ct_decode_alphabet_byte::<A>(b1);
80588081
let (v2, valid2) = ct_decode_alphabet_byte::<A>(b2);
@@ -8074,13 +8097,7 @@ fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize,
80748097
write += 3;
80758098
}
80768099

8077-
let tail = match read_tail(buffer, read) {
8078-
Ok(tail) => tail,
8079-
Err(err) => {
8080-
wipe_bytes(buffer);
8081-
return Err(err);
8082-
}
8083-
};
8100+
let tail = read_tail_or_mark_invalid(buffer, read, &mut invalid_byte, &mut invalid_padding);
80848101
match tail {
80858102
[] => {}
80868103
[b0, b1] => {
@@ -8109,7 +8126,10 @@ fn ct_decode_unpadded_in_place<A: Alphabet>(buffer: &mut [u8]) -> Result<usize,
81098126
buffer[write + 1] = (v1 << 4) | (v2 >> 2);
81108127
write += 2;
81118128
}
8112-
_ => return Err(DecodeError::InvalidLength),
8129+
_ => {
8130+
invalid_byte = 0xff;
8131+
invalid_padding = 0xff;
8132+
}
81138133
}
81148134

81158135
if write != required {
@@ -8125,6 +8145,44 @@ fn read_tail(input: &[u8], offset: usize) -> Result<&[u8], DecodeError> {
81258145
input.get(offset..).ok_or(DecodeError::InvalidLength)
81268146
}
81278147

8148+
fn read_quad_or_mark_invalid(
8149+
input: &[u8],
8150+
offset: usize,
8151+
invalid_byte: &mut u8,
8152+
invalid_padding: &mut u8,
8153+
) -> [u8; 4] {
8154+
if let Ok(quad) = read_quad(input, offset) {
8155+
quad
8156+
} else {
8157+
debug_assert!(
8158+
false,
8159+
"read_quad failed inside length-validated constant-time decode loop"
8160+
);
8161+
*invalid_byte = 0xff;
8162+
*invalid_padding = 0xff;
8163+
[0; 4]
8164+
}
8165+
}
8166+
8167+
fn read_tail_or_mark_invalid<'a>(
8168+
input: &'a [u8],
8169+
offset: usize,
8170+
invalid_byte: &mut u8,
8171+
invalid_padding: &mut u8,
8172+
) -> &'a [u8] {
8173+
if let Ok(tail) = read_tail(input, offset) {
8174+
tail
8175+
} else {
8176+
debug_assert!(
8177+
false,
8178+
"read_tail failed inside length-validated constant-time decode loop"
8179+
);
8180+
*invalid_byte = 0xff;
8181+
*invalid_padding = 0xff;
8182+
&[]
8183+
}
8184+
}
8185+
81288186
#[inline(never)]
81298187
fn ct_decode_alphabet_byte<A: Alphabet>(byte: u8) -> (u8, u8) {
81308188
let mut decoded = 0u8;
@@ -8549,6 +8607,20 @@ mod tests {
85498607
}
85508608
}
85518609

8610+
#[test]
8611+
fn ct_padded_final_quantum_fails_closed_for_invalid_padding_count() {
8612+
let (_, invalid_byte, invalid_padding, written) =
8613+
ct_padded_final_quantum::<Standard>(*b"ABCD", 3);
8614+
8615+
assert_ne!(invalid_byte, 0);
8616+
assert_ne!(invalid_padding, 0);
8617+
assert_eq!(written, 0);
8618+
assert_eq!(
8619+
report_ct_error(invalid_byte, invalid_padding),
8620+
Err(DecodeError::InvalidInput)
8621+
);
8622+
}
8623+
85528624
#[cfg(feature = "simd")]
85538625
#[test]
85548626
fn simd_dispatch_scaffold_keeps_scalar_active() {

0 commit comments

Comments
 (0)