mirror of https://github.com/briansmith/webpki
174 lines
5.5 KiB
Rust
174 lines
5.5 KiB
Rust
// 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 crate::{calendar, time, Error};
|
|
pub use ring::io::{
|
|
der::{nested, Tag},
|
|
Positive,
|
|
};
|
|
|
|
#[inline(always)]
|
|
pub fn expect_tag_and_get_value<'a>(
|
|
input: &mut untrusted::Reader<'a>,
|
|
tag: Tag,
|
|
) -> Result<untrusted::Input<'a>, Error> {
|
|
ring::io::der::expect_tag_and_get_value(input, tag).map_err(|_| Error::BadDer)
|
|
}
|
|
|
|
pub struct Value<'a> {
|
|
value: untrusted::Input<'a>,
|
|
}
|
|
|
|
impl<'a> Value<'a> {
|
|
pub fn value(&self) -> untrusted::Input<'a> {
|
|
self.value
|
|
}
|
|
}
|
|
|
|
pub fn expect_tag<'a>(input: &mut untrusted::Reader<'a>, tag: Tag) -> Result<Value<'a>, Error> {
|
|
let (actual_tag, value) = read_tag_and_get_value(input)?;
|
|
if usize::from(tag) != usize::from(actual_tag) {
|
|
return Err(Error::BadDer);
|
|
}
|
|
|
|
Ok(Value { value })
|
|
}
|
|
|
|
#[inline(always)]
|
|
pub fn read_tag_and_get_value<'a>(
|
|
input: &mut untrusted::Reader<'a>,
|
|
) -> Result<(u8, untrusted::Input<'a>), Error> {
|
|
ring::io::der::read_tag_and_get_value(input).map_err(|_| Error::BadDer)
|
|
}
|
|
|
|
// TODO: investigate taking decoder as a reference to reduce generated code
|
|
// size.
|
|
pub fn nested_of_mut<'a, E>(
|
|
input: &mut untrusted::Reader<'a>,
|
|
outer_tag: Tag,
|
|
inner_tag: Tag,
|
|
error: E,
|
|
mut decoder: impl FnMut(&mut untrusted::Reader<'a>) -> Result<(), E>,
|
|
) -> Result<(), E>
|
|
where
|
|
E: Copy,
|
|
{
|
|
nested(input, outer_tag, error, |outer| {
|
|
loop {
|
|
nested(outer, inner_tag, error, |inner| decoder(inner))?;
|
|
if outer.at_end() {
|
|
break;
|
|
}
|
|
}
|
|
Ok(())
|
|
})
|
|
}
|
|
|
|
pub fn bit_string_with_no_unused_bits<'a>(
|
|
input: &mut untrusted::Reader<'a>,
|
|
) -> Result<untrusted::Input<'a>, Error> {
|
|
nested(input, Tag::BitString, Error::BadDer, |value| {
|
|
let unused_bits_at_end = value.read_byte().map_err(|_| Error::BadDer)?;
|
|
if unused_bits_at_end != 0 {
|
|
return Err(Error::BadDer);
|
|
}
|
|
Ok(value.read_bytes_to_end())
|
|
})
|
|
}
|
|
|
|
// Like mozilla::pkix, we accept the nonconformant explicit encoding of
|
|
// the default value (false) for compatibility with real-world certificates.
|
|
pub fn optional_boolean(input: &mut untrusted::Reader) -> Result<bool, Error> {
|
|
if !input.peek(Tag::Boolean.into()) {
|
|
return Ok(false);
|
|
}
|
|
nested(input, Tag::Boolean, Error::BadDer, |input| {
|
|
match input.read_byte() {
|
|
Ok(0xff) => Ok(true),
|
|
Ok(0x00) => Ok(false),
|
|
_ => Err(Error::BadDer),
|
|
}
|
|
})
|
|
}
|
|
|
|
pub fn positive_integer<'a>(input: &'a mut untrusted::Reader) -> Result<Positive<'a>, Error> {
|
|
ring::io::der::positive_integer(input).map_err(|_| Error::BadDer)
|
|
}
|
|
|
|
pub fn small_nonnegative_integer(input: &mut untrusted::Reader) -> Result<u8, Error> {
|
|
ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDer)
|
|
}
|
|
|
|
pub fn time_choice(input: &mut untrusted::Reader) -> Result<time::Time, Error> {
|
|
let is_utc_time = input.peek(Tag::UTCTime.into());
|
|
let expected_tag = if is_utc_time {
|
|
Tag::UTCTime
|
|
} else {
|
|
Tag::GeneralizedTime
|
|
};
|
|
|
|
fn read_digit(inner: &mut untrusted::Reader) -> Result<u64, Error> {
|
|
const DIGIT: core::ops::RangeInclusive<u8> = b'0'..=b'9';
|
|
let b = inner.read_byte().map_err(|_| Error::BadDerTime)?;
|
|
if DIGIT.contains(&b) {
|
|
return Ok(u64::from(b - DIGIT.start()));
|
|
}
|
|
Err(Error::BadDerTime)
|
|
}
|
|
|
|
fn read_two_digits(inner: &mut untrusted::Reader, min: u64, max: u64) -> Result<u64, Error> {
|
|
let hi = read_digit(inner)?;
|
|
let lo = read_digit(inner)?;
|
|
let value = (hi * 10) + lo;
|
|
if value < min || value > max {
|
|
return Err(Error::BadDerTime);
|
|
}
|
|
Ok(value)
|
|
}
|
|
|
|
nested(input, expected_tag, Error::BadDer, |value| {
|
|
let (year_hi, year_lo) = if is_utc_time {
|
|
let lo = read_two_digits(value, 0, 99)?;
|
|
let hi = if lo >= 50 { 19 } else { 20 };
|
|
(hi, lo)
|
|
} else {
|
|
let hi = read_two_digits(value, 0, 99)?;
|
|
let lo = read_two_digits(value, 0, 99)?;
|
|
(hi, lo)
|
|
};
|
|
|
|
let year = (year_hi * 100) + year_lo;
|
|
let month = read_two_digits(value, 1, 12)?;
|
|
let days_in_month = calendar::days_in_month(year, month);
|
|
let day_of_month = read_two_digits(value, 1, days_in_month)?;
|
|
let hours = read_two_digits(value, 0, 23)?;
|
|
let minutes = read_two_digits(value, 0, 59)?;
|
|
let seconds = read_two_digits(value, 0, 59)?;
|
|
|
|
let time_zone = value.read_byte().map_err(|_| Error::BadDerTime)?;
|
|
if time_zone != b'Z' {
|
|
return Err(Error::BadDerTime);
|
|
}
|
|
|
|
calendar::time_from_ymdhms_utc(year, month, day_of_month, hours, minutes, seconds)
|
|
})
|
|
}
|
|
|
|
macro_rules! oid {
|
|
( $first:expr, $second:expr, $( $tail:expr ),* ) =>
|
|
(
|
|
[(40 * $first) + $second, $( $tail ),*]
|
|
)
|
|
}
|