Skip to content

Commit

Permalink
Add errors for panics found by fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
Flakebi committed Mar 29, 2019
1 parent 34764ca commit 0641eff
Showing 1 changed file with 88 additions and 29 deletions.
117 changes: 88 additions & 29 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,17 @@ pub enum ASN1DecodeErr {
LengthTooLarge(usize),
UTF8DecodeFailure(Utf8Error),
PrintableStringDecodeFailure,
InvalidDateValue(String)
InvalidDateValue(String),
InvalidBitStringLength(isize),
/// Not a valid ASN.1 class
InvalidClass(u8),
/// Expected more input
///
/// Invalid ASN.1 input can lead to this error.
Incomplete,

#[doc(hidden)]
__Nonexhaustive,
}

impl fmt::Display for ASN1DecodeErr {
Expand All @@ -290,7 +300,15 @@ impl fmt::Display for ASN1DecodeErr {
ASN1DecodeErr::PrintableStringDecodeFailure =>
write!(f, "Printable string failed to properly decode."),
ASN1DecodeErr::InvalidDateValue(x) =>
write!(f, "Invalid date value: {}", x)
write!(f, "Invalid date value: {}", x),
ASN1DecodeErr::InvalidBitStringLength(i) =>
write!(f, "Invalid length of bit string: {}", i),
ASN1DecodeErr::InvalidClass(i) =>
write!(f, "Invalid class value: {}", i),
ASN1DecodeErr::Incomplete =>
write!(f, "Incomplete data or invalid ASN1"),
ASN1DecodeErr::__Nonexhaustive =>
panic!("A non exhaustive error should not be constructed"),
}
}
}
Expand All @@ -309,7 +327,15 @@ impl Error for ASN1DecodeErr {
ASN1DecodeErr::PrintableStringDecodeFailure =>
"Printable string failed to properly decode.",
ASN1DecodeErr::InvalidDateValue(_) =>
"Invalid date value."
"Invalid date value.",
ASN1DecodeErr::InvalidClass(_) =>
"Invalid class value",
ASN1DecodeErr::InvalidBitStringLength(_) =>
"Invalid length of bit string",
ASN1DecodeErr::Incomplete =>
"Incomplete data or invalid ASN1",
ASN1DecodeErr::__Nonexhaustive =>
panic!("A non exhaustive error should not be constructed"),
}
}

Expand Down Expand Up @@ -372,10 +398,11 @@ fn from_der_(i: &[u8], start_offset: usize)

while index < len {
let soff = start_offset + index;
let (tag, constructed, class) = decode_tag(i, &mut index);
let (tag, constructed, class) = decode_tag(i, &mut index)?;
let len = decode_length(i, &mut index)?;
if i.len() < index + len {
return Err(ASN1DecodeErr::LengthTooLarge(index + len));
let checklen = index.checked_add(len).ok_or(ASN1DecodeErr::LengthTooLarge(len))?;
if checklen > i.len() {
return Err(ASN1DecodeErr::Incomplete);
}
let body = &i[index .. (index + len)];

Expand Down Expand Up @@ -419,7 +446,14 @@ fn from_der_(i: &[u8], start_offset: usize)
}
Some(0x03) => {
let bits = (&body[1..]).to_vec();
let nbits = (bits.len() * 8) - (body[0] as usize);
let bitcount = bits.len() * 8;
let rest = body[0] as usize;
if bitcount < rest {
return Err(ASN1DecodeErr::InvalidBitStringLength(
bitcount as isize - rest as isize));
}

let nbits = bitcount - (body[0] as usize);
result.push(ASN1Block::BitString(soff, nbits, bits))
}
// OCTET STRING
Expand All @@ -433,6 +467,9 @@ fn from_der_(i: &[u8], start_offset: usize)
// OBJECT IDENTIFIER
Some(0x06) => {
let mut value1 = BigUint::zero();
if body.len() == 0 {
return Err(ASN1DecodeErr::Incomplete) ;
}
let mut value2 = BigUint::from_u8(body[0]).unwrap();
let mut oidres = Vec::new();
let mut bindex = 1;
Expand All @@ -450,7 +487,7 @@ fn from_der_(i: &[u8], start_offset: usize)
oidres.push(value1);
oidres.push(value2);
while bindex < body.len() {
oidres.push(decode_base127(body, &mut bindex));
oidres.push(decode_base127(body, &mut bindex)?);
}
let res = OID(oidres);

Expand Down Expand Up @@ -533,10 +570,17 @@ fn from_der_(i: &[u8], start_offset: usize)
return Err(ASN1DecodeErr::InvalidDateValue(format!("{}",body.len())));
}

let mut v = String::from_iter(body.iter().map(|x| *x as char));
let mut v: String = String::from_utf8(body.to_vec())
.map_err(|e| ASN1DecodeErr::UTF8DecodeFailure(e.utf8_error()))?;
// Make sure the string is ascii, otherwise we cannot insert
// chars at specific bytes.
if !v.is_ascii() {
return Err(ASN1DecodeErr::InvalidDateValue(v));
}

// We need to add padding back to the string if it's not there.
if v.find('.').is_none() {
v.insert(15, '.')
if !v.contains('.') {
v.insert(14, '.')
}
while v.len() < 25 {
let idx = v.len() - 1;
Expand Down Expand Up @@ -584,46 +628,57 @@ fn from_der_(i: &[u8], start_offset: usize)
}

/// Returns the tag, if the type is constructed and the class.
fn decode_tag(i: &[u8], index: &mut usize) -> (BigUint, bool, ASN1Class) {
fn decode_tag(i: &[u8], index: &mut usize) -> Result<(BigUint, bool, ASN1Class),ASN1DecodeErr> {
if *index >= i.len() {
return Err(ASN1DecodeErr::Incomplete) ;
}
let tagbyte = i[*index];
let constructed = (tagbyte & 0b0010_0000) != 0;
let class = decode_class(tagbyte);
let class = decode_class(tagbyte)?;
let basetag = tagbyte & 0b1_1111;

*index += 1;

if basetag == 0b1_1111 {
let res = decode_base127(i, index);
(res, constructed, class)
let res = decode_base127(i, index)?;
Ok((res, constructed, class))
} else {
(BigUint::from(basetag), constructed, class)
Ok((BigUint::from(basetag), constructed, class))
}
}

fn decode_base127(i: &[u8], index: &mut usize) -> BigUint {
fn decode_base127(i: &[u8], index: &mut usize) -> Result<BigUint,ASN1DecodeErr> {
let mut res = BigUint::zero();

loop {
if *index >= i.len() {
return Err(ASN1DecodeErr::Incomplete) ;
}

let nextbyte = i[*index];

*index += 1;
res = (res << 7) + BigUint::from(nextbyte & 0x7f);
if (nextbyte & 0x80) == 0 {
return res;
return Ok(res);
}
}
}

fn decode_class(i: u8) -> ASN1Class {
fn decode_class(i: u8) -> Result<ASN1Class,ASN1DecodeErr> {
match i >> 6 {
0b00 => ASN1Class::Universal,
0b01 => ASN1Class::Application,
0b10 => ASN1Class::ContextSpecific,
0b11 => ASN1Class::Private,
_ => panic!("The universe is broken.")
0b00 => Ok(ASN1Class::Universal),
0b01 => Ok(ASN1Class::Application),
0b10 => Ok(ASN1Class::ContextSpecific),
0b11 => Ok(ASN1Class::Private),
_ => Err(ASN1DecodeErr::InvalidClass(i)),
}
}

fn decode_length(i: &[u8], index: &mut usize) -> Result<usize,ASN1DecodeErr> {
if *index >= i.len() {
return Err(ASN1DecodeErr::Incomplete) ;
}
let startbyte = i[*index];

// NOTE: Technically, this size can be much larger than a usize.
Expand All @@ -640,6 +695,10 @@ fn decode_length(i: &[u8], index: &mut usize) -> Result<usize,ASN1DecodeErr> {
}

while lenlen > 0 {
if *index >= i.len() {
return Err(ASN1DecodeErr::Incomplete) ;
}

res = (res << 8) + (i[*index] as usize);

*index += 1;
Expand Down Expand Up @@ -1071,11 +1130,11 @@ mod tests {

quickcheck! {
fn class_encdec_roundtrips(c: ASN1Class) -> bool {
c == decode_class(encode_class(c.clone()))
c == decode_class(encode_class(c.clone())).unwrap()
}

fn class_decenc_roundtrips(v: u8) -> bool {
(v & 0b11000000) == encode_class(decode_class(v))
(v & 0b11000000) == encode_class(decode_class(v).unwrap())
}
}

Expand All @@ -1095,7 +1154,7 @@ mod tests {
fn tags_encdec_roundtrips(c: ASN1Class, con: bool, t: RandomUint) -> bool {
let bytes = encode_tag(c, con, &t.x);
let mut zero = 0;
let (t2, con2, c2) = decode_tag(&bytes[..], &mut zero);
let (t2, con2, c2) = decode_tag(&bytes[..], &mut zero).unwrap();
(c == c2) && (con == con2) && (t.x == t2)
}

Expand Down Expand Up @@ -1417,7 +1476,7 @@ mod tests {

#[test]
fn x509_tests() {
assert!(can_parse("test/server.bin").is_ok());
assert!(can_parse("test/key.bin").is_ok());
can_parse("test/server.bin").unwrap();
can_parse("test/key.bin").unwrap();
}
}

0 comments on commit 0641eff

Please sign in to comment.