| // 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(crate) use ring::io::der::{CONSTRUCTED, CONTEXT_SPECIFIC}; |
| |
| // Copied (and extended) from ring's src/der.rs |
| #[allow(clippy::upper_case_acronyms)] |
| #[derive(Clone, Copy, Eq, PartialEq)] |
| #[repr(u8)] |
| pub(crate) enum Tag { |
| Boolean = 0x01, |
| Integer = 0x02, |
| BitString = 0x03, |
| OctetString = 0x04, |
| OID = 0x06, |
| Enum = 0x0A, |
| UTF8String = 0x0C, |
| Sequence = CONSTRUCTED | 0x10, // 0x30 |
| Set = CONSTRUCTED | 0x11, // 0x31 |
| UTCTime = 0x17, |
| GeneralizedTime = 0x18, |
| |
| #[allow(clippy::identity_op)] |
| ContextSpecificConstructed0 = CONTEXT_SPECIFIC | CONSTRUCTED | 0, |
| ContextSpecificConstructed1 = CONTEXT_SPECIFIC | CONSTRUCTED | 1, |
| ContextSpecificConstructed3 = CONTEXT_SPECIFIC | CONSTRUCTED | 3, |
| } |
| |
| impl From<Tag> for usize { |
| #[allow(clippy::as_conversions)] |
| fn from(tag: Tag) -> Self { |
| tag as Self |
| } |
| } |
| |
| impl From<Tag> for u8 { |
| #[allow(clippy::as_conversions)] |
| fn from(tag: Tag) -> Self { |
| tag as Self |
| } // XXX: narrowing conversion. |
| } |
| |
| #[inline(always)] |
| pub(crate) fn expect_tag_and_get_value<'a>( |
| input: &mut untrusted::Reader<'a>, |
| tag: Tag, |
| ) -> Result<untrusted::Input<'a>, Error> { |
| let (actual_tag, inner) = read_tag_and_get_value(input)?; |
| if usize::from(tag) != usize::from(actual_tag) { |
| return Err(Error::BadDer); |
| } |
| Ok(inner) |
| } |
| |
| #[inline(always)] |
| pub(crate) fn expect_tag_and_get_value_limited<'a>( |
| input: &mut untrusted::Reader<'a>, |
| tag: Tag, |
| size_limit: usize, |
| ) -> Result<untrusted::Input<'a>, Error> { |
| let (actual_tag, inner) = read_tag_and_get_value_limited(input, size_limit)?; |
| if usize::from(tag) != usize::from(actual_tag) { |
| return Err(Error::BadDer); |
| } |
| Ok(inner) |
| } |
| |
| pub(crate) fn nested_limited<'a, R, E: Copy>( |
| input: &mut untrusted::Reader<'a>, |
| tag: Tag, |
| error: E, |
| decoder: impl FnOnce(&mut untrusted::Reader<'a>) -> Result<R, E>, |
| size_limit: usize, |
| ) -> Result<R, E> { |
| expect_tag_and_get_value_limited(input, tag, size_limit) |
| .map_err(|_| error)? |
| .read_all(error, decoder) |
| } |
| |
| // TODO: investigate taking decoder as a reference to reduce generated code |
| // size. |
| pub(crate) fn nested<'a, R, E: Copy>( |
| input: &mut untrusted::Reader<'a>, |
| tag: Tag, |
| error: E, |
| decoder: impl FnOnce(&mut untrusted::Reader<'a>) -> Result<R, E>, |
| ) -> Result<R, E> { |
| nested_limited(input, tag, error, decoder, TWO_BYTE_DER_SIZE) |
| } |
| |
| pub(crate) struct Value<'a> { |
| value: untrusted::Input<'a>, |
| } |
| |
| impl<'a> Value<'a> { |
| pub(crate) fn value(&self) -> untrusted::Input<'a> { |
| self.value |
| } |
| } |
| |
| pub(crate) 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(crate) fn read_tag_and_get_value<'a>( |
| input: &mut untrusted::Reader<'a>, |
| ) -> Result<(u8, untrusted::Input<'a>), Error> { |
| read_tag_and_get_value_limited(input, TWO_BYTE_DER_SIZE) |
| } |
| |
| #[inline(always)] |
| pub(crate) fn read_tag_and_get_value_limited<'a>( |
| input: &mut untrusted::Reader<'a>, |
| size_limit: usize, |
| ) -> Result<(u8, untrusted::Input<'a>), Error> { |
| let tag = input.read_byte()?; |
| if (tag & HIGH_TAG_RANGE_START) == HIGH_TAG_RANGE_START { |
| 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 input.read_byte()? { |
| n if (n & SHORT_FORM_LEN_MAX) == 0 => usize::from(n), |
| LONG_FORM_LEN_ONE_BYTE => { |
| let length_byte = input.read_byte()?; |
| if length_byte < SHORT_FORM_LEN_MAX { |
| return Err(Error::BadDer); // Not the canonical encoding. |
| } |
| usize::from(length_byte) |
| } |
| LONG_FORM_LEN_TWO_BYTES => { |
| let length_byte_one = usize::from(input.read_byte()?); |
| let length_byte_two = usize::from(input.read_byte()?); |
| let combined = (length_byte_one << 8) | length_byte_two; |
| if combined <= LONG_FORM_LEN_ONE_BYTE_MAX { |
| return Err(Error::BadDer); // Not the canonical encoding. |
| } |
| combined |
| } |
| LONG_FORM_LEN_THREE_BYTES => { |
| let length_byte_one = usize::from(input.read_byte()?); |
| let length_byte_two = usize::from(input.read_byte()?); |
| let length_byte_three = usize::from(input.read_byte()?); |
| let combined = (length_byte_one << 16) | (length_byte_two << 8) | length_byte_three; |
| if combined <= LONG_FORM_LEN_TWO_BYTES_MAX { |
| return Err(Error::BadDer); // Not the canonical encoding. |
| } |
| combined |
| } |
| LONG_FORM_LEN_FOUR_BYTES => { |
| let length_byte_one = usize::from(input.read_byte()?); |
| let length_byte_two = usize::from(input.read_byte()?); |
| let length_byte_three = usize::from(input.read_byte()?); |
| let length_byte_four = usize::from(input.read_byte()?); |
| let combined = (length_byte_one << 24) |
| | (length_byte_two << 16) |
| | (length_byte_three << 8) |
| | length_byte_four; |
| if combined <= LONG_FORM_LEN_THREE_BYTES_MAX { |
| return Err(Error::BadDer); // Not the canonical encoding. |
| } |
| combined |
| } |
| _ => { |
| return Err(Error::BadDer); // We don't support longer lengths. |
| } |
| }; |
| |
| if length >= size_limit { |
| return Err(Error::BadDer); // The length is larger than the caller accepts. |
| } |
| |
| let inner = input.read_bytes(length)?; |
| Ok((tag, inner)) |
| } |
| |
| // Long-form DER encoded lengths of two bytes can express lengths up to the following limit. |
| // |
| // The upstream ring::io::der::read_tag_and_get_value() function limits itself to up to two byte |
| // long-form DER lengths, and so this limit represents the maximum length that was possible to |
| // read before the introduction of the read_tag_and_get_value_limited function. |
| pub(crate) const TWO_BYTE_DER_SIZE: usize = LONG_FORM_LEN_TWO_BYTES_MAX; |
| |
| // The maximum size of a DER value that Webpki can support reading. |
| // |
| // Webpki limits itself to four byte long-form DER lengths, and so this limit represents |
| // the maximum size tagged DER value that can be read for any purpose. |
| pub(crate) const MAX_DER_SIZE: usize = LONG_FORM_LEN_FOUR_BYTES_MAX; |
| |
| // DER Tag identifiers have two forms: |
| // * Low tag number form (for tags values in the range [0..30] |
| // * High tag number form (for tag values in the range [31..] |
| // We only support low tag number form. |
| const HIGH_TAG_RANGE_START: u8 = 31; |
| |
| // DER length octets have two forms: |
| // * Short form: 1 octet supporting lengths between 0 and 127. |
| // * Long definite form: 2 to 127 octets, number of octets encoded into first octet. |
| const SHORT_FORM_LEN_MAX: u8 = 128; |
| |
| // Leading octet for long definite form DER length expressed in second byte. |
| const LONG_FORM_LEN_ONE_BYTE: u8 = 0x81; |
| |
| // Maximum size that can be expressed in a one byte long form len. |
| const LONG_FORM_LEN_ONE_BYTE_MAX: usize = 0xff; |
| |
| // Leading octet for long definite form DER length expressed in subsequent two bytes. |
| const LONG_FORM_LEN_TWO_BYTES: u8 = 0x82; |
| |
| // Maximum size that can be expressed in a two byte long form len. |
| const LONG_FORM_LEN_TWO_BYTES_MAX: usize = 0xff_ff; |
| |
| // Leading octet for long definite form DER length expressed in subsequent three bytes. |
| const LONG_FORM_LEN_THREE_BYTES: u8 = 0x83; |
| |
| // Maximum size that can be expressed in a three byte long form len. |
| const LONG_FORM_LEN_THREE_BYTES_MAX: usize = 0xff_ff_ff; |
| |
| // Leading octet for long definite form DER length expressed in subsequent four bytes. |
| const LONG_FORM_LEN_FOUR_BYTES: u8 = 0x84; |
| |
| // Maximum size that can be expressed in a four byte long form der len. |
| const LONG_FORM_LEN_FOUR_BYTES_MAX: usize = 0xff_ff_ff_ff; |
| |
| // TODO: investigate taking decoder as a reference to reduce generated code |
| // size. |
| pub(crate) 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(crate) 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()) |
| }) |
| } |
| |
| pub(crate) struct BitStringFlags<'a> { |
| raw_bits: &'a [u8], |
| } |
| |
| impl<'a> BitStringFlags<'a> { |
| pub(crate) fn bit_set(&self, bit: usize) -> bool { |
| let byte_index = bit / 8; |
| let bit_shift = 7 - (bit % 8); |
| |
| if self.raw_bits.len() < (byte_index + 1) { |
| false |
| } else { |
| ((self.raw_bits[byte_index] >> bit_shift) & 1) != 0 |
| } |
| } |
| } |
| |
| // ASN.1 BIT STRING fields for sets of flags are encoded in DER with some peculiar details related |
| // to padding. Notably this means we expect a Tag::BitString, a length, an indicator of the number |
| // of bits of padding, and then the actual bit values. See this Stack Overflow discussion[0], and |
| // ITU X690-0207[1] Section 8.6 and Section 11.2 for more information. |
| // |
| // [0]: https://security.stackexchange.com/a/10396 |
| // [1]: https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf |
| pub(crate) fn bit_string_flags<'a>( |
| input: &mut untrusted::Reader<'a>, |
| ) -> Result<BitStringFlags<'a>, Error> { |
| expect_tag_and_get_value(input, Tag::BitString)?.read_all(Error::BadDer, |bit_string| { |
| // ITU X690-0207 11.2: |
| // "The initial octet shall encode, as an unsigned binary integer with bit 1 as the least |
| // significant bit, the number of unused bits in the final subsequent octet. |
| // The number shall be in the range zero to seven" |
| let padding_bits = bit_string.read_byte().map_err(|_| Error::BadDer)?; |
| let raw_bits = bit_string.read_bytes_to_end().as_slice_less_safe(); |
| |
| // It's illegal to have more than 7 bits of padding. Similarly, if the raw bitflags |
| // are empty there should be no padding. |
| if padding_bits > 7 || (raw_bits.is_empty() && padding_bits != 0) { |
| return Err(Error::BadDer); |
| } |
| |
| // If there are padding bits then the last bit of the last raw byte must be 0 or the |
| // distinguished encoding rules are not being followed. |
| let last_byte = raw_bits[raw_bits.len() - 1]; |
| let padding_mask = (1 << padding_bits) - 1; |
| |
| match padding_bits > 0 && (last_byte & padding_mask) != 0 { |
| true => Err(Error::BadDer), |
| false => Ok(BitStringFlags { raw_bits }), |
| } |
| }) |
| } |
| |
| // Like mozilla::pkix, we accept the nonconformant explicit encoding of |
| // the default value (false) for compatibility with real-world certificates. |
| pub(crate) 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(crate) fn small_nonnegative_integer(input: &mut untrusted::Reader) -> Result<u8, Error> { |
| ring::io::der::small_nonnegative_integer(input).map_err(|_| Error::BadDer) |
| } |
| |
| pub(crate) 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 ),*] |
| ) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| #[test] |
| fn test_optional_boolean() { |
| use super::{optional_boolean, Error}; |
| |
| // Empty input results in false |
| assert!(!optional_boolean(&mut bytes_reader(&[])).unwrap()); |
| |
| // Optional, so another data type results in false |
| assert!(!optional_boolean(&mut bytes_reader(&[0x05, 0x00])).unwrap()); |
| |
| // Only 0x00 and 0xff are accepted values |
| assert_eq!( |
| Err(Error::BadDer), |
| optional_boolean(&mut bytes_reader(&[0x01, 0x01, 0x42])) |
| ); |
| |
| // True |
| assert!(optional_boolean(&mut bytes_reader(&[0x01, 0x01, 0xff])).unwrap()); |
| |
| // False |
| assert!(!optional_boolean(&mut bytes_reader(&[0x01, 0x01, 0x00])).unwrap()); |
| } |
| |
| #[test] |
| fn test_bit_string_with_no_unused_bits() { |
| use super::{bit_string_with_no_unused_bits, Error}; |
| |
| // Unexpected type |
| assert_eq!( |
| Err(Error::BadDer), |
| bit_string_with_no_unused_bits(&mut bytes_reader(&[0x01, 0x01, 0xff])) |
| ); |
| |
| // Unexpected nonexistent type |
| assert_eq!( |
| Err(Error::BadDer), |
| bit_string_with_no_unused_bits(&mut bytes_reader(&[0x42, 0xff, 0xff])) |
| ); |
| |
| // Unexpected empty input |
| assert_eq!( |
| Err(Error::BadDer), |
| bit_string_with_no_unused_bits(&mut bytes_reader(&[])) |
| ); |
| |
| // Valid input with non-zero unused bits |
| assert_eq!( |
| Err(Error::BadDer), |
| bit_string_with_no_unused_bits(&mut bytes_reader(&[0x03, 0x03, 0x04, 0x12, 0x34])) |
| ); |
| |
| // Valid input |
| assert_eq!( |
| untrusted::Input::from(&[0x12, 0x34]), |
| bit_string_with_no_unused_bits(&mut bytes_reader(&[0x03, 0x03, 0x00, 0x12, 0x34])) |
| .unwrap() |
| ); |
| } |
| |
| fn bytes_reader(bytes: &[u8]) -> untrusted::Reader { |
| return untrusted::Reader::new(untrusted::Input::from(bytes)); |
| } |
| |
| #[test] |
| fn read_tag_and_get_value_default_limit() { |
| use super::{read_tag_and_get_value, Error}; |
| |
| let inputs = &[ |
| // DER with short-form length encoded as three bytes. |
| &[EXAMPLE_TAG, 0x83, 0xFF, 0xFF, 0xFF].as_slice(), |
| // DER with short-form length encoded as four bytes. |
| &[EXAMPLE_TAG, 0x84, 0xFF, 0xFF, 0xFF, 0xFF].as_slice(), |
| ]; |
| |
| for input in inputs { |
| let mut bytes = untrusted::Reader::new(untrusted::Input::from(input)); |
| // read_tag_and_get_value should reject DER with encoded lengths larger than two |
| // bytes as BadDer. |
| assert!(matches!( |
| read_tag_and_get_value(&mut bytes), |
| Err(Error::BadDer) |
| )); |
| } |
| } |
| |
| #[test] |
| fn read_tag_and_get_value_limited_high_form() { |
| use super::{read_tag_and_get_value_limited, Error, LONG_FORM_LEN_TWO_BYTES_MAX}; |
| |
| let mut bytes = untrusted::Reader::new(untrusted::Input::from(&[0xFF])); |
| // read_tag_and_get_value_limited_high_form should reject DER with "high tag number form" tags. |
| assert!(matches!( |
| read_tag_and_get_value_limited(&mut bytes, LONG_FORM_LEN_TWO_BYTES_MAX), |
| Err(Error::BadDer) |
| )); |
| } |
| |
| #[test] |
| fn read_tag_and_get_value_limited_non_canonical() { |
| use super::{read_tag_and_get_value_limited, Error, LONG_FORM_LEN_TWO_BYTES_MAX}; |
| |
| let inputs = &[ |
| // Two byte length, with expressed length < 128. |
| &[EXAMPLE_TAG, 0x81, 0x01].as_slice(), |
| // Three byte length, with expressed length < 256. |
| &[EXAMPLE_TAG, 0x82, 0x00, 0x01].as_slice(), |
| // Four byte length, with expressed length, < 65536. |
| &[EXAMPLE_TAG, 0x83, 0x00, 0x00, 0x01].as_slice(), |
| // Five byte length, with expressed length < 16777216. |
| &[EXAMPLE_TAG, 0x84, 0x00, 0x00, 0x00, 0x01].as_slice(), |
| ]; |
| |
| for input in inputs { |
| let mut bytes = untrusted::Reader::new(untrusted::Input::from(input)); |
| // read_tag_and_get_value_limited should reject DER with non-canonical lengths. |
| assert!(matches!( |
| read_tag_and_get_value_limited(&mut bytes, LONG_FORM_LEN_TWO_BYTES_MAX), |
| Err(Error::BadDer) |
| )); |
| } |
| } |
| |
| #[test] |
| #[cfg(feature = "alloc")] |
| fn read_tag_and_get_value_limited_limits() { |
| use super::{read_tag_and_get_value_limited, Error}; |
| |
| let short_input = &[0xFF]; |
| let short_input_encoded = &[ |
| &[EXAMPLE_TAG], |
| der_encode_length(short_input.len()).as_slice(), |
| short_input, |
| ] |
| .concat(); |
| |
| let long_input = &[1_u8; 65537]; |
| let long_input_encoded = &[ |
| &[EXAMPLE_TAG], |
| der_encode_length(long_input.len()).as_slice(), |
| long_input, |
| ] |
| .concat(); |
| |
| struct Testcase<'a> { |
| input: &'a [u8], |
| limit: usize, |
| err: Option<Error>, |
| } |
| |
| let testcases = &[ |
| Testcase { |
| input: short_input_encoded, |
| limit: 1, |
| err: Some(Error::BadDer), |
| }, |
| Testcase { |
| input: short_input_encoded, |
| limit: short_input_encoded.len() + 1, |
| err: None, |
| }, |
| Testcase { |
| input: long_input_encoded, |
| limit: long_input.len(), |
| err: Some(Error::BadDer), |
| }, |
| Testcase { |
| input: long_input_encoded, |
| limit: long_input.len() + 1, |
| err: None, |
| }, |
| ]; |
| |
| for tc in testcases { |
| let mut bytes = untrusted::Reader::new(untrusted::Input::from(tc.input)); |
| |
| let res = read_tag_and_get_value_limited(&mut bytes, tc.limit); |
| match tc.err { |
| None => assert!(res.is_ok()), |
| Some(e) => { |
| let actual = res.unwrap_err(); |
| assert_eq!(actual, e) |
| } |
| } |
| } |
| } |
| |
| #[allow(clippy::as_conversions)] // infallible. |
| const EXAMPLE_TAG: u8 = super::Tag::Sequence as u8; |
| |
| #[cfg(feature = "alloc")] |
| #[allow(clippy::as_conversions)] // test code. |
| fn der_encode_length(length: usize) -> Vec<u8> { |
| if length < 128 { |
| vec![length as u8] |
| } else { |
| let mut encoded: Vec<u8> = Vec::new(); |
| let mut remaining_length = length; |
| |
| while remaining_length > 0 { |
| let byte = (remaining_length & 0xFF) as u8; |
| encoded.insert(0, byte); |
| remaining_length >>= 8; |
| } |
| |
| let length_octet = encoded.len() as u8 | 0x80; |
| encoded.insert(0, length_octet); |
| |
| encoded |
| } |
| } |
| |
| #[allow(clippy::as_conversions)] // infallible. |
| const BITSTRING_TAG: u8 = super::Tag::BitString as u8; |
| |
| #[test] |
| fn misencoded_bit_string_flags() { |
| use super::{bit_string_flags, Error}; |
| |
| let mut bad_padding_example = untrusted::Reader::new(untrusted::Input::from(&[ |
| BITSTRING_TAG, // BitString |
| 0x2, // 2 bytes of content. |
| 0x08, // 8 bit of padding (illegal!). |
| 0x06, // 1 byte of bit flags asserting bits 5 and 6. |
| ])); |
| assert!(matches!( |
| bit_string_flags(&mut bad_padding_example), |
| Err(Error::BadDer) |
| )); |
| |
| let mut bad_padding_example = untrusted::Reader::new(untrusted::Input::from(&[ |
| BITSTRING_TAG, // BitString |
| 0x2, // 2 bytes of content. |
| 0x01, // 1 bit of padding. |
| // No flags value (illegal with padding!). |
| ])); |
| assert!(matches!( |
| bit_string_flags(&mut bad_padding_example), |
| Err(Error::BadDer) |
| )); |
| |
| let mut trailing_zeroes = untrusted::Reader::new(untrusted::Input::from(&[ |
| BITSTRING_TAG, // BitString |
| 0x2, // 2 bytes of content. |
| 0x01, // 1 bit of padding. |
| 0xFF, // Flag data with |
| 0x00, // trailing zeros. |
| ])); |
| assert!(matches!( |
| bit_string_flags(&mut trailing_zeroes), |
| Err(Error::BadDer) |
| )) |
| } |
| |
| #[test] |
| fn valid_bit_string_flags() { |
| use super::bit_string_flags; |
| |
| let mut example_key_usage = untrusted::Reader::new(untrusted::Input::from(&[ |
| BITSTRING_TAG, // BitString |
| 0x2, // 2 bytes of content. |
| 0x01, // 1 bit of padding. |
| 0x06, // 1 byte of bit flags asserting bits 5 and 6. |
| ])); |
| let res = bit_string_flags(&mut example_key_usage).unwrap(); |
| |
| assert!(!res.bit_set(0)); |
| assert!(!res.bit_set(1)); |
| assert!(!res.bit_set(2)); |
| assert!(!res.bit_set(3)); |
| assert!(!res.bit_set(4)); |
| // NB: Bits 5 and 6 should be set. |
| assert!(res.bit_set(5)); |
| assert!(res.bit_set(6)); |
| assert!(!res.bit_set(7)); |
| assert!(!res.bit_set(8)); |
| // Bits outside the range of values shouldn't be considered set. |
| assert!(!res.bit_set(256)); |
| } |
| } |