Skip to content

Commit 6f708c4

Browse files
authored
Fix panic in the JER OctetString decoder if the string isn't even length (#382)
1 parent 8b42b54 commit 6f708c4

File tree

2 files changed

+320
-17
lines changed

2 files changed

+320
-17
lines changed

src/jer/de.rs

+124-16
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ impl crate::Decoder for Decoder {
7070
self.codec(),
7171
)
7272
})?;
73-
(value, *size as u64)
73+
(value, *size)
7474
} else {
7575
let last = self.stack.pop().ok_or_else(JerDecodeErrorKind::eoi)?;
7676
let value_map = last
@@ -86,20 +86,26 @@ impl crate::Decoder for Decoder {
8686
value_map
8787
.get("length")
8888
.and_then(|l| l.as_number())
89-
.and_then(|i| i.as_u64()),
89+
.and_then(|i| i.as_u64())
90+
.map(|i| i as usize),
9091
)
9192
.ok_or_else(|| JerDecodeErrorKind::TypeMismatch {
9293
needed: "JSON object containing 'value' and 'length' properties.",
9394
found: alloc::format!("{value_map:#?}"),
9495
})?;
95-
(
96-
(0..value.len())
97-
.step_by(2)
98-
.map(|i| u8::from_str_radix(&value[i..=i + 1], 16))
99-
.collect::<Result<BitString, _>>()
100-
.map_err(|e| JerDecodeErrorKind::InvalidJerBitstring { parse_int_err: e })?,
101-
length,
102-
)
96+
97+
let value = bytes_from_hexstring(value).ok_or(DecodeError::custom(
98+
alloc::format!("Failed to create BitString from bytes: {value:02x?}"),
99+
self.codec(),
100+
))?;
101+
let value = BitString::try_from_vec(value).map_err(|e| {
102+
DecodeError::custom(
103+
alloc::format!("Failed to create BitString from bytes: {e:02x?}"),
104+
self.codec(),
105+
)
106+
})?;
107+
108+
(value, length)
103109
};
104110
let padding_length = if bitstring_length % 8 == 0 {
105111
0
@@ -109,7 +115,15 @@ impl crate::Decoder for Decoder {
109115
for _ in 0..padding_length {
110116
padded.pop();
111117
}
112-
Ok(padded)
118+
119+
if bitstring_length != padded.len() {
120+
Err(DecodeError::custom(
121+
alloc::format!("Failed to create BitString from bytes: invalid value length (was: {}, expected: {})", padded.len(), bitstring_length),
122+
self.codec(),
123+
))
124+
} else {
125+
Ok(padded)
126+
}
113127
}
114128

115129
fn decode_bool(&mut self, _t: Tag) -> Result<bool, Self::Error> {
@@ -605,11 +619,8 @@ impl Decoder {
605619
needed: "hex string",
606620
found: alloc::format!("{value}"),
607621
})?;
608-
Ok((0..octet_string.len())
609-
.step_by(2)
610-
.map(|i| u8::from_str_radix(&octet_string[i..=i + 1], 16))
611-
.collect::<Result<alloc::vec::Vec<u8>, _>>()
612-
.map_err(|_| JerDecodeErrorKind::InvalidJerOctetString {})?)
622+
bytes_from_hexstring(octet_string)
623+
.ok_or(JerDecodeErrorKind::InvalidJerOctetString {}.into())
613624
}
614625

615626
fn utc_time_from_value(value: Value) -> Result<chrono::DateTime<chrono::Utc>, DecodeError> {
@@ -647,3 +658,100 @@ impl Decoder {
647658
})?)
648659
}
649660
}
661+
662+
/// Parses a hex string into bytes.
663+
fn bytes_from_hexstring(hex_string: &str) -> Option<alloc::vec::Vec<u8>> {
664+
if hex_string.len() % 2 != 0 {
665+
return None;
666+
}
667+
let mut bytes = alloc::vec::Vec::<u8>::with_capacity(hex_string.len() / 2);
668+
for (i, c) in hex_string.char_indices() {
669+
let n = nibble_from_hexdigit(c)?;
670+
if i % 2 == 0 {
671+
bytes.push(n << 4);
672+
} else {
673+
bytes[i / 2] |= n;
674+
}
675+
}
676+
Some(bytes)
677+
}
678+
679+
/// Parses a hexdigit character into a nibble (four bits).
680+
fn nibble_from_hexdigit(c: char) -> Option<u8> {
681+
match c {
682+
'0'..='9' => Some(c as u8 - b'0'),
683+
'a'..='f' => Some(c as u8 - b'a' + 0xA),
684+
'A'..='F' => Some(c as u8 - b'A' + 0xA),
685+
_ => None,
686+
}
687+
}
688+
689+
#[cfg(test)]
690+
mod tests {
691+
use super::*;
692+
693+
#[test]
694+
fn test_bytes_from_hexstring() {
695+
assert_eq!(bytes_from_hexstring(""), Some(vec![]));
696+
assert_eq!(bytes_from_hexstring("00"), Some(vec![0]));
697+
assert_eq!(bytes_from_hexstring("FF"), Some(vec![0xFF]));
698+
assert_eq!(bytes_from_hexstring("0000"), Some(vec![0, 0]));
699+
assert_eq!(bytes_from_hexstring("FFFF"), Some(vec![0xFF, 0xFF]));
700+
701+
assert_eq!(bytes_from_hexstring(" "), None);
702+
assert_eq!(bytes_from_hexstring("!"), None);
703+
assert_eq!(bytes_from_hexstring("0"), None);
704+
assert_eq!(bytes_from_hexstring(" 0"), None);
705+
assert_eq!(bytes_from_hexstring("0 "), None);
706+
assert_eq!(bytes_from_hexstring("0!"), None);
707+
assert_eq!(bytes_from_hexstring(" "), None);
708+
assert_eq!(bytes_from_hexstring("00 "), None);
709+
assert_eq!(bytes_from_hexstring(" 00"), None);
710+
assert_eq!(bytes_from_hexstring("000"), None);
711+
assert_eq!(bytes_from_hexstring("Œ"), None);
712+
assert_eq!(bytes_from_hexstring("ŒŒ"), None);
713+
assert_eq!(bytes_from_hexstring("ŒŒŒ"), None);
714+
assert_eq!(bytes_from_hexstring("ABCDEFG"), None);
715+
assert_eq!(bytes_from_hexstring(" ABCDEF"), None);
716+
assert_eq!(bytes_from_hexstring("\u{0000}"), None);
717+
assert_eq!(bytes_from_hexstring("\u{FFFF}"), None);
718+
assert_eq!(bytes_from_hexstring("\u{0123}"), None);
719+
assert_eq!(bytes_from_hexstring("\u{30}"), None);
720+
assert_eq!(bytes_from_hexstring("\\u0030"), None);
721+
assert_eq!(bytes_from_hexstring("\\u202E\\u0030\\u0030"), None);
722+
assert_eq!(bytes_from_hexstring("⣐⡄"), None);
723+
assert_eq!(bytes_from_hexstring("😎"), None);
724+
assert_eq!(bytes_from_hexstring("🙈🙉🙊"), None);
725+
}
726+
727+
#[test]
728+
fn test_nibble_from_hexdigit() {
729+
for c in '\u{0}'..'\u{1024}' {
730+
match c {
731+
'0' => assert_eq!(Some(0x00), nibble_from_hexdigit(c)),
732+
'1' => assert_eq!(Some(0x01), nibble_from_hexdigit(c)),
733+
'2' => assert_eq!(Some(0x02), nibble_from_hexdigit(c)),
734+
'3' => assert_eq!(Some(0x03), nibble_from_hexdigit(c)),
735+
'4' => assert_eq!(Some(0x04), nibble_from_hexdigit(c)),
736+
'5' => assert_eq!(Some(0x05), nibble_from_hexdigit(c)),
737+
'6' => assert_eq!(Some(0x06), nibble_from_hexdigit(c)),
738+
'7' => assert_eq!(Some(0x07), nibble_from_hexdigit(c)),
739+
'8' => assert_eq!(Some(0x08), nibble_from_hexdigit(c)),
740+
'9' => assert_eq!(Some(0x09), nibble_from_hexdigit(c)),
741+
'A' => assert_eq!(Some(0x0A), nibble_from_hexdigit(c)),
742+
'B' => assert_eq!(Some(0x0B), nibble_from_hexdigit(c)),
743+
'C' => assert_eq!(Some(0x0C), nibble_from_hexdigit(c)),
744+
'D' => assert_eq!(Some(0x0D), nibble_from_hexdigit(c)),
745+
'E' => assert_eq!(Some(0x0E), nibble_from_hexdigit(c)),
746+
'F' => assert_eq!(Some(0x0F), nibble_from_hexdigit(c)),
747+
'a' => assert_eq!(Some(0x0A), nibble_from_hexdigit(c)),
748+
'b' => assert_eq!(Some(0x0B), nibble_from_hexdigit(c)),
749+
'c' => assert_eq!(Some(0x0C), nibble_from_hexdigit(c)),
750+
'd' => assert_eq!(Some(0x0D), nibble_from_hexdigit(c)),
751+
'e' => assert_eq!(Some(0x0E), nibble_from_hexdigit(c)),
752+
'f' => assert_eq!(Some(0x0F), nibble_from_hexdigit(c)),
753+
_ => assert_eq!(None, nibble_from_hexdigit(c)),
754+
}
755+
}
756+
}
757+
}

tests/strings.rs

+196-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// Test whether constrained OctetString and FixedOctetString are equal
21
use bitvec::prelude::*;
32
use rasn::prelude::*;
43
use rasn::{ber, jer, oer, uper};
@@ -89,6 +88,7 @@ fn build_fixed_octet() -> ConstrainedHashes {
8988
}
9089
}
9190

91+
// Test whether constrained OctetString and FixedOctetString are equal
9292
macro_rules! test_decode_eq {
9393
($fn_name:ident, $codec:ident) => {
9494
#[test]
@@ -132,3 +132,198 @@ test_decode_eq!(test_uper_octet_eq, uper);
132132
test_decode_eq!(test_oer_octet_eq, oer);
133133
test_decode_eq!(test_ber_octet_eq, ber);
134134
test_decode_eq!(test_jer_octet_eq, jer);
135+
136+
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
137+
#[rasn(automatic_tags)]
138+
pub struct ABitString {
139+
#[rasn(size("0..=255"))]
140+
pub the_string: BitString,
141+
}
142+
143+
/// Tests that valid strings are parsed and invalid strings are rejected.
144+
#[test]
145+
fn test_jer_bitstring_dec() {
146+
use bitvec::prelude::*;
147+
148+
let good_cases: Vec<(&str, usize, BitVec<u8, bitvec::order::Msb0>)> = vec![
149+
("", 0, bitvec::bits![u8, Msb0;].into()),
150+
("00", 1, bitvec::bits![u8, Msb0; 0].into()),
151+
("00", 3, bitvec::bits![u8, Msb0; 0,0,0].into()),
152+
("0F", 3, bitvec::bits![u8, Msb0; 0,0,0].into()),
153+
("F0", 3, bitvec::bits![u8, Msb0; 1,1,1].into()),
154+
("00", 7, bitvec::bits![u8, Msb0; 0,0,0,0,0,0,0].into()),
155+
("00", 8, bitvec::bits![u8, Msb0; 0,0,0,0,0,0,0,0].into()),
156+
("0F", 8, bitvec::bits![u8, Msb0; 0,0,0,0,1,1,1,1].into()),
157+
(
158+
"\\u0030\\u0030",
159+
8,
160+
bitvec::bits![u8, Msb0; 0,0,0,0,0,0,0,0].into(),
161+
),
162+
(
163+
"\\u0046\\u0046",
164+
8,
165+
bitvec::bits![u8, Msb0; 1,1,1,1,1,1,1,1].into(),
166+
),
167+
];
168+
169+
let bad_cases: Vec<(&str, usize)> = vec![
170+
(" ", 0),
171+
("!", 0),
172+
("0", 0),
173+
(" 0", 0),
174+
("0 ", 0),
175+
("0!", 0),
176+
(" ", 0),
177+
("00 ", 0),
178+
(" 00", 0),
179+
("000", 0),
180+
("Œ", 0),
181+
("ŒŒ", 0),
182+
("ŒŒŒ", 0),
183+
("ABCDEFG", 0),
184+
(" ABCDEF", 0),
185+
("\u{0000}", 0),
186+
("\u{FFFF}", 0),
187+
("\u{0123}", 0),
188+
("\u{30}", 0),
189+
("\\u0030", 0),
190+
("\\u202E\\u0030\\u0030", 0),
191+
("⣐⡄", 0),
192+
("😎", 0),
193+
("🙈🙉🙊", 0),
194+
("", 1),
195+
("", 8),
196+
("00", 0),
197+
("00", 10),
198+
("00", 16),
199+
("00", 16384),
200+
("0000", 0),
201+
("0000", 8),
202+
("0000", 17),
203+
];
204+
205+
for (case, length, bits) in good_cases {
206+
let json = format!("{{\"the_string\":{{\"length\":{length},\"value\":\"{case}\"}}}}");
207+
let expected = ABitString { the_string: bits };
208+
let decoded = jer::decode::<ABitString>(&json);
209+
if let Err(e) = decoded {
210+
panic!("should have decoded case \"{case}\" fine: {e:?}");
211+
}
212+
assert_eq!(decoded.unwrap(), expected);
213+
}
214+
215+
for (case, length) in bad_cases {
216+
let json = format!("{{\"the_string\":{{\"length\":{length},\"value\":\"{case}\"}}}}");
217+
let decoded = jer::decode::<ABitString>(&json);
218+
if let Ok(decoded) = decoded {
219+
panic!(
220+
"should have rejected case \"{case}\", but decoded: {decoded:?} (length {})",
221+
decoded.the_string.len()
222+
);
223+
}
224+
}
225+
}
226+
227+
#[derive(AsnType, Decode, Encode, Debug, Clone, PartialEq)]
228+
#[rasn(automatic_tags)]
229+
pub struct AnOctetString {
230+
#[rasn(size("0..=255"))]
231+
pub the_string: OctetString,
232+
}
233+
234+
/// Tests that valid strings are parsed and invalid strings are rejected.
235+
#[test]
236+
fn test_jer_octetstring_dec() {
237+
let good_cases: Vec<&[u8]> = vec![
238+
&[],
239+
&[0x00; 1],
240+
&[0x00; 2],
241+
&[0x0F; 1],
242+
&[0xFF; 1],
243+
&[0xFF; 2],
244+
&[0x00; 10],
245+
&[0x00; 100],
246+
&[0x00; 200],
247+
&[0x01, 0x23],
248+
&[0xAB, 0xCD],
249+
&[0xAB, 0xCD, 0xEF],
250+
&[0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF],
251+
&[0x00; 255],
252+
&[0x0F; 255],
253+
&[0xFF; 255],
254+
&[0x00; 256],
255+
&[0x0F; 256],
256+
&[0xFF; 256],
257+
&[0x00; 16384],
258+
&[0x0F; 16384],
259+
&[0xFF; 16384],
260+
];
261+
262+
let special_cases: Vec<(&str, &[u8])> =
263+
vec![("\\u0030\\u0030", &[0x00]), ("\\u0046\\u0046", &[0xFF])];
264+
265+
let bad_cases = vec![
266+
" ",
267+
"!",
268+
"0",
269+
" 0",
270+
"0 ",
271+
"0!",
272+
" ",
273+
"000",
274+
"Œ",
275+
"ŒŒ",
276+
"ŒŒŒ",
277+
"ABCDEFG",
278+
" ABCDEF",
279+
"\u{0000}",
280+
"\u{FFFF}",
281+
"\u{0123}",
282+
"\u{30}",
283+
"\\u0030",
284+
"\\u202E\\u0030\\u0030",
285+
"⣐⡄",
286+
"😎",
287+
"🙈🙉🙊",
288+
];
289+
290+
for case in good_cases {
291+
let upper_hex = case
292+
.iter()
293+
.map(|b| format!("{b:02X}"))
294+
.collect::<Vec<String>>()
295+
.join("");
296+
let json = format!("{{\"the_string\":\"{upper_hex}\"}}");
297+
let expected = AnOctetString {
298+
the_string: case.into(),
299+
};
300+
let decoded = jer::decode::<AnOctetString>(&json);
301+
if let Err(e) = decoded {
302+
panic!("should have decoded case \"{upper_hex}\" fine: {e:?}");
303+
}
304+
assert_eq!(decoded.unwrap(), expected);
305+
}
306+
307+
for (case, expected) in special_cases {
308+
let json = format!("{{\"the_string\":\"{case}\"}}");
309+
let expected = AnOctetString {
310+
the_string: expected.into(),
311+
};
312+
let decoded = jer::decode::<AnOctetString>(&json);
313+
if let Err(e) = decoded {
314+
panic!("should have decoded case \"{case}\" fine: {e:?}");
315+
}
316+
assert_eq!(decoded.unwrap(), expected);
317+
}
318+
319+
for case in bad_cases {
320+
let json = format!("{{\"the_string\":\"{case}\"}}");
321+
let decoded = jer::decode::<AnOctetString>(&json);
322+
if let Ok(decoded) = decoded {
323+
panic!(
324+
"should have rejected case \"{case}\", but decoded: {decoded:?} (length {})",
325+
decoded.the_string.len()
326+
);
327+
}
328+
}
329+
}

0 commit comments

Comments
 (0)