// 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, CONSTRUCTED}, Positive, }; #[inline(always)] pub fn expect_tag_and_get_value<'a>( input: &mut untrusted::Reader<'a>, tag: Tag, ) -> Result, 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, 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, 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 { 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, Error> { ring::io::der::positive_integer(input).map_err(|_| Error::BadDer) } pub fn small_nonnegative_integer(input: &mut untrusted::Reader) -> Result { ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDer) } pub fn time_choice(input: &mut untrusted::Reader) -> Result { 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 { const DIGIT: core::ops::RangeInclusive = 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 { 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 ),*] ) }