webpki/
der.rs

1// Copyright 2015 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15use crate::{calendar, time, Error};
16pub use ring::io::{
17    der::{nested, Tag, CONSTRUCTED},
18    Positive,
19};
20
21#[inline(always)]
22pub fn expect_tag_and_get_value<'a>(
23    input: &mut untrusted::Reader<'a>, tag: Tag,
24) -> Result<untrusted::Input<'a>, Error> {
25    ring::io::der::expect_tag_and_get_value(input, tag).map_err(|_| Error::BadDER)
26}
27
28pub struct Value<'a> {
29    tlv: untrusted::Input<'a>,
30    value: untrusted::Input<'a>,
31}
32
33impl<'a> Value<'a> {
34    #[allow(dead_code)] // TODO: remove this.
35    pub fn tlv(&self) -> untrusted::Input<'a> { self.tlv }
36
37    pub fn value(&self) -> untrusted::Input<'a> { self.value }
38}
39
40pub fn expect_tag<'a>(input: &mut untrusted::Reader<'a>, tag: Tag) -> Result<Value<'a>, Error> {
41    let start = input.mark();
42
43    let (actual_tag, value) = read_tag_and_get_value(input)?;
44    if usize::from(tag) != usize::from(actual_tag) {
45        return Err(Error::BadDER);
46    }
47
48    let end = input.mark();
49
50    let tlv = input
51        .get_input_between_marks(start, end)
52        .map_err(|untrusted::EndOfInput| Error::BadDER)?;
53
54    Ok(Value { tlv, value })
55}
56
57#[inline(always)]
58pub fn read_tag_and_get_value<'a>(
59    input: &mut untrusted::Reader<'a>,
60) -> Result<(u8, untrusted::Input<'a>), Error> {
61    ring::io::der::read_tag_and_get_value(input).map_err(|_| Error::BadDER)
62}
63
64// TODO: investigate taking decoder as a reference to reduce generated code
65// size.
66#[inline(always)]
67pub fn nested_mut<'a, F, R, E: Copy>(
68    input: &mut untrusted::Reader<'a>, tag: Tag, error: E, decoder: F,
69) -> Result<R, E>
70where
71    F: FnMut(&mut untrusted::Reader<'a>) -> Result<R, E>,
72{
73    let inner = expect_tag_and_get_value(input, tag).map_err(|_| error)?;
74    inner.read_all(error, decoder).map_err(|_| error)
75}
76
77// TODO: investigate taking decoder as a reference to reduce generated code
78// size.
79pub fn nested_of_mut<'a, F, E: Copy>(
80    input: &mut untrusted::Reader<'a>, outer_tag: Tag, inner_tag: Tag, error: E, mut decoder: F,
81) -> Result<(), E>
82where
83    F: FnMut(&mut untrusted::Reader<'a>) -> Result<(), E>,
84{
85    nested_mut(input, outer_tag, error, |outer| {
86        loop {
87            nested_mut(outer, inner_tag, error, |inner| decoder(inner))?;
88            if outer.at_end() {
89                break;
90            }
91        }
92        Ok(())
93    })
94}
95
96pub fn bit_string_with_no_unused_bits<'a>(
97    input: &mut untrusted::Reader<'a>,
98) -> Result<untrusted::Input<'a>, Error> {
99    nested(input, Tag::BitString, Error::BadDER, |value| {
100        let unused_bits_at_end = value.read_byte().map_err(|_| Error::BadDER)?;
101        if unused_bits_at_end != 0 {
102            return Err(Error::BadDER);
103        }
104        Ok(value.read_bytes_to_end())
105    })
106}
107
108// Like mozilla::pkix, we accept the nonconformant explicit encoding of
109// the default value (false) for compatibility with real-world certificates.
110pub fn optional_boolean(input: &mut untrusted::Reader) -> Result<bool, Error> {
111    if !input.peek(Tag::Boolean as u8) {
112        return Ok(false);
113    }
114    nested(input, Tag::Boolean, Error::BadDER, |input| {
115        match input.read_byte() {
116            Ok(0xff) => Ok(true),
117            Ok(0x00) => Ok(false),
118            _ => Err(Error::BadDER),
119        }
120    })
121}
122
123pub fn positive_integer<'a>(input: &'a mut untrusted::Reader) -> Result<Positive<'a>, Error> {
124    ring::io::der::positive_integer(input).map_err(|_| Error::BadDER)
125}
126
127pub fn small_nonnegative_integer<'a>(input: &'a mut untrusted::Reader) -> Result<u8, Error> {
128    ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDER)
129}
130
131pub fn time_choice<'a>(input: &mut untrusted::Reader<'a>) -> Result<time::Time, Error> {
132    let is_utc_time = input.peek(Tag::UTCTime as u8);
133    let expected_tag = if is_utc_time {
134        Tag::UTCTime
135    } else {
136        Tag::GeneralizedTime
137    };
138
139    fn read_digit(inner: &mut untrusted::Reader) -> Result<u64, Error> {
140        let b = inner.read_byte().map_err(|_| Error::BadDERTime)?;
141        if b < b'0' || b > b'9' {
142            return Err(Error::BadDERTime);
143        }
144        Ok((b - b'0') as u64)
145    }
146
147    fn read_two_digits(inner: &mut untrusted::Reader, min: u64, max: u64) -> Result<u64, Error> {
148        let hi = read_digit(inner)?;
149        let lo = read_digit(inner)?;
150        let value = (hi * 10) + lo;
151        if value < min || value > max {
152            return Err(Error::BadDERTime);
153        }
154        Ok(value)
155    }
156
157    nested(input, expected_tag, Error::BadDER, |value| {
158        let (year_hi, year_lo) = if is_utc_time {
159            let lo = read_two_digits(value, 0, 99)?;
160            let hi = if lo >= 50 { 19 } else { 20 };
161            (hi, lo)
162        } else {
163            let hi = read_two_digits(value, 0, 99)?;
164            let lo = read_two_digits(value, 0, 99)?;
165            (hi, lo)
166        };
167
168        let year = (year_hi * 100) + year_lo;
169        let month = read_two_digits(value, 1, 12)?;
170        let days_in_month = calendar::days_in_month(year, month);
171        let day_of_month = read_two_digits(value, 1, days_in_month)?;
172        let hours = read_two_digits(value, 0, 23)?;
173        let minutes = read_two_digits(value, 0, 59)?;
174        let seconds = read_two_digits(value, 0, 59)?;
175
176        let time_zone = value.read_byte().map_err(|_| Error::BadDERTime)?;
177        if time_zone != b'Z' {
178            return Err(Error::BadDERTime);
179        }
180
181        calendar::time_from_ymdhms_utc(year, month, day_of_month, hours, minutes, seconds)
182    })
183}
184
185macro_rules! oid {
186    ( $first:expr, $second:expr, $( $tail:expr ),* ) =>
187    (
188        [(40 * $first) + $second, $( $tail ),*]
189    )
190}