Add ASN.1 DER parsing framework.

This commit is contained in:
Brian Smith 2015-08-13 11:35:09 -04:00
parent 62a20c6df2
commit 6b60cbd719
3 changed files with 108 additions and 0 deletions

89
src/der.rs Normal file
View File

@ -0,0 +1,89 @@
// Copyright 2015 Brian Smith.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
use super::Error;
use super::input::*;
const CONSTRUCTED : u8 = 1 << 5;
#[derive(PartialEq)]
#[repr(u8)]
pub enum Tag {
Boolean = 0x01,
Integer = 0x02,
BitString = 0x03,
OctetString = 0x04,
Null = 0x05,
OID = 0x06,
Sequence = CONSTRUCTED | 0x10, // 0x30
UTCTime = 0x17,
GeneralizedTime = 0x18,
}
pub fn expect_tag_and_get_input<'a>(input: &mut Reader<'a>, tag: Tag)
-> Result<Input<'a>, Error> {
let (actual_tag, inner) = try!(read_tag_and_get_input(input));
if (tag as usize) != (actual_tag as usize) {
return Err(Error::BadDER);
}
Ok(inner)
}
fn read_tag_and_get_input<'a>(input: &mut Reader<'a>)
-> Result<(u8, Input<'a>), Error> {
let tag = try!(input.read_byte().ok_or(Error::BadDER));
if (tag & 0x1F) == 0x1F {
return Err(Error::BadDER) // High tag number form is not allowed.
}
// If the high order bit of the first byte is set to zero then the length
// is encoded in the seven remaining bits of that byte. Otherwise, those
// seven bits represent the number of bytes used to encode the length.
let length = match try!(input.read_byte().ok_or(Error::BadDER)) {
n if (n & 0x80) == 0 => n as usize,
0x81 => {
let second_byte = try!(input.read_byte().ok_or(Error::BadDER));
if second_byte < 128 {
return Err(Error::BadDER) // Not the canonical encoding.
}
second_byte as usize
},
0x82 => {
let second_byte = try!(input.read_byte().ok_or(Error::BadDER))
as usize;
let third_byte = try!(input.read_byte().ok_or(Error::BadDER))
as usize;
let combined = (second_byte << 8) | third_byte;
if combined < 256 {
return Err(Error::BadDER); // Not the canonical encoding.
}
combined
},
_ => {
return Err(Error::BadDER); // We don't support longer lengths.
}
};
let inner = try!(input.skip_and_get_input(length).ok_or(Error::BadDER));
Ok((tag, inner))
}
// TODO: investigate taking decoder as a reference to reduce generated code
// size.
pub fn nested<'a, F, R>(input: &mut Reader<'a>, tag: Tag, decoder: F)
-> Result<R, Error>
where F : FnOnce(&mut Reader<'a>) -> Result<R, Error> {
let inner = try!(expect_tag_and_get_input(input, tag));
read_all(inner, Error::BadDER, decoder)
}

View File

@ -90,4 +90,17 @@ impl<'a> Reader<'a> {
None => None
}
}
pub fn skip_and_get_input(&mut self, num_bytes: usize)
-> Option<Input<'a>> {
match self.i.checked_add(num_bytes) {
Some(new_i) if new_i <= self.input.bytes.len() => {
let ret =
Some(Input { bytes: &self.input.bytes[self.i..new_i] });
self.i = new_i;
ret
},
_ => None
}
}
}

View File

@ -12,4 +12,10 @@
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
mod der;
mod input;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Error {
BadDER,
}