Skip to content

Commit 69db155

Browse files
authored
der: add UnusedBits newtype for BitString (#1956)
1 parent 20fad86 commit 69db155

File tree

1 file changed

+93
-40
lines changed

1 file changed

+93
-40
lines changed

der/src/asn1/bit_string.rs

Lines changed: 93 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::{
77
Result, Tag, ValueOrd, Writer,
88
};
99
use core::{cmp::Ordering, iter::FusedIterator};
10+
use unused_bits::UnusedBits;
1011

1112
#[cfg(feature = "flagset")]
1213
use core::mem::size_of_val;
@@ -20,40 +21,32 @@ use core::mem::size_of_val;
2021
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
2122
pub struct BitStringRef<'a> {
2223
/// Number of unused bits in the final octet.
23-
unused_bits: u8,
24-
25-
/// Length of this `BIT STRING` in bits.
26-
bit_length: usize,
24+
unused_bits: UnusedBits,
2725

2826
/// Bitstring represented as a slice of bytes.
2927
inner: &'a BytesRef,
3028
}
3129

3230
impl<'a> BitStringRef<'a> {
33-
/// Maximum number of unused bits allowed.
34-
pub const MAX_UNUSED_BITS: u8 = 7;
35-
3631
/// Create a new ASN.1 `BIT STRING` from a byte slice.
3732
///
3833
/// Accepts an optional number of "unused bits" (0-7) which are omitted
3934
/// from the final octet. This number is 0 if the value is octet-aligned.
4035
pub fn new(unused_bits: u8, bytes: &'a [u8]) -> Result<Self> {
41-
if (unused_bits > Self::MAX_UNUSED_BITS) || (unused_bits != 0 && bytes.is_empty()) {
42-
return Err(Self::TAG.value_error().into());
43-
}
44-
36+
let unused_bits = UnusedBits::new(unused_bits, bytes)?;
4537
let inner = BytesRef::new(bytes).map_err(|_| Self::TAG.length_error())?;
38+
let value = Self::new_unchecked(unused_bits, inner);
39+
value
40+
.bit_len_checked()
41+
.ok_or_else(|| Error::from(ErrorKind::Overflow))?;
42+
Ok(value)
43+
}
4644

47-
let bit_length = usize::try_from(inner.len())?
48-
.checked_mul(8)
49-
.and_then(|n| n.checked_sub(usize::from(unused_bits)))
50-
.ok_or(ErrorKind::Overflow)?;
51-
52-
Ok(Self {
53-
unused_bits,
54-
bit_length,
55-
inner,
56-
})
45+
/// Internal function. Assumptions:
46+
/// - [`UnusedBits`] was checked for given [`BytesRef`],
47+
/// - [`BitStringRef::bit_len_checked`] was called and returned `Ok`.
48+
pub(crate) fn new_unchecked(unused_bits: UnusedBits, inner: &'a BytesRef) -> Self {
49+
Self { unused_bits, inner }
5750
}
5851

5952
/// Create a new ASN.1 `BIT STRING` from the given bytes.
@@ -65,17 +58,31 @@ impl<'a> BitStringRef<'a> {
6558

6659
/// Get the number of unused bits in this byte slice.
6760
pub fn unused_bits(&self) -> u8 {
68-
self.unused_bits
61+
*self.unused_bits
6962
}
7063

7164
/// Is the number of unused bits a value other than 0?
7265
pub fn has_unused_bits(&self) -> bool {
73-
self.unused_bits != 0
66+
*self.unused_bits != 0
67+
}
68+
69+
/// Get the length of this `BIT STRING` in bits, or `None` if the value overflows.
70+
///
71+
/// Ensured to be valid in the constructor.
72+
fn bit_len_checked(&self) -> Option<usize> {
73+
usize::try_from(self.inner.len())
74+
.ok()
75+
.and_then(|n| n.checked_mul(8))
76+
.and_then(|n| n.checked_sub(usize::from(*self.unused_bits)))
7477
}
7578

7679
/// Get the length of this `BIT STRING` in bits.
7780
pub fn bit_len(&self) -> usize {
78-
self.bit_length
81+
let bit_len = self.bit_len_checked();
82+
debug_assert!(bit_len.is_some());
83+
84+
// Ensured to be valid in the constructor.
85+
bit_len.unwrap_or(0)
7986
}
8087

8188
/// Get the number of bytes/octets needed to represent this `BIT STRING`
@@ -153,7 +160,7 @@ impl EncodeValue for BitStringRef<'_> {
153160
}
154161

155162
fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
156-
writer.write_byte(self.unused_bits)?;
163+
writer.write_byte(*self.unused_bits)?;
157164
writer.write(self.raw_bytes())
158165
}
159166
}
@@ -223,13 +230,48 @@ impl FixedTag for BitStringRef<'_> {
223230
const TAG: Tag = Tag::BitString;
224231
}
225232

233+
/// Sealed, so that `UnusedBits` newtype can't be created directly
234+
mod unused_bits {
235+
use core::ops::Deref;
236+
237+
use crate::{Result, Tag};
238+
239+
/// Value in range `0..=7`
240+
///
241+
/// Must be zero for empty `BIT STRING`.
242+
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
243+
pub(crate) struct UnusedBits(u8);
244+
245+
impl UnusedBits {
246+
/// Maximum number of unused bits allowed.
247+
pub const MAX_UNUSED_BITS: u8 = 7;
248+
249+
/// Represents number of "unused bits" (0-7) in `BIT STRING` which are omitted
250+
/// from the final octet. This number is 0 if the value is octet-aligned.
251+
pub fn new(unused_bits: u8, bytes: &[u8]) -> Result<Self> {
252+
if (unused_bits > Self::MAX_UNUSED_BITS) || (unused_bits != 0 && bytes.is_empty()) {
253+
Err(Tag::BitString.value_error().into())
254+
} else {
255+
Ok(Self(unused_bits))
256+
}
257+
}
258+
}
259+
impl Deref for UnusedBits {
260+
type Target = u8;
261+
262+
fn deref(&self) -> &Self::Target {
263+
&self.0
264+
}
265+
}
266+
}
267+
226268
// Implement by hand because the derive would create invalid values.
227269
// Use the constructor to create a valid value.
228270
#[cfg(feature = "arbitrary")]
229271
impl<'a> arbitrary::Arbitrary<'a> for BitStringRef<'a> {
230272
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
231273
Self::new(
232-
u.int_in_range(0..=Self::MAX_UNUSED_BITS)?,
274+
u.int_in_range(0..=UnusedBits::MAX_UNUSED_BITS)?,
233275
<&'a BytesRef>::arbitrary(u)?.as_slice(),
234276
)
235277
.map_err(|_| arbitrary::Error::IncorrectFormat)
@@ -256,10 +298,7 @@ mod allocating {
256298
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
257299
pub struct BitString {
258300
/// Number of unused bits in the final octet.
259-
unused_bits: u8,
260-
261-
/// Length of this `BIT STRING` in bits.
262-
bit_length: usize,
301+
unused_bits: UnusedBits,
263302

264303
/// Bitstring represented as a slice of bytes.
265304
inner: Vec<u8>,
@@ -277,11 +316,10 @@ mod allocating {
277316
let inner = bytes.into();
278317

279318
// Ensure parameters parse successfully as a `BitStringRef`.
280-
let bit_length = BitStringRef::new(unused_bits, &inner)?.bit_length;
319+
let ref_value = BitStringRef::new(unused_bits, &inner)?;
281320

282321
Ok(BitString {
283-
unused_bits,
284-
bit_length,
322+
unused_bits: ref_value.unused_bits,
285323
inner,
286324
})
287325
}
@@ -296,17 +334,34 @@ mod allocating {
296334
/// Get the number of unused bits in the octet serialization of this
297335
/// `BIT STRING`.
298336
pub fn unused_bits(&self) -> u8 {
299-
self.unused_bits
337+
*self.unused_bits
300338
}
301339

302340
/// Is the number of unused bits a value other than 0?
303341
pub fn has_unused_bits(&self) -> bool {
304-
self.unused_bits != 0
342+
*self.unused_bits != 0
343+
}
344+
345+
/// Returns inner [`BytesRef`] slice.
346+
pub(crate) fn bytes_ref(&self) -> &BytesRef {
347+
// Ensured to parse successfully in constructor
348+
BytesRef::new_unchecked(&self.inner)
349+
}
350+
351+
/// Get the length of this `BIT STRING` in bits, or `None` if the value overflows.
352+
///
353+
/// Ensured to be valid in the constructor.
354+
fn bit_len_checked(&self) -> Option<usize> {
355+
BitStringRef::new_unchecked(self.unused_bits, self.bytes_ref()).bit_len_checked()
305356
}
306357

307358
/// Get the length of this `BIT STRING` in bits.
308359
pub fn bit_len(&self) -> usize {
309-
self.bit_length
360+
let bit_len = self.bit_len_checked();
361+
debug_assert!(bit_len.is_some());
362+
363+
// Ensured to be valid in the constructor.
364+
bit_len.unwrap_or(0)
310365
}
311366

312367
/// Is the inner byte slice empty?
@@ -364,7 +419,7 @@ mod allocating {
364419
}
365420

366421
fn encode_value(&self, writer: &mut impl Writer) -> Result<()> {
367-
writer.write_byte(self.unused_bits)?;
422+
writer.write_byte(*self.unused_bits)?;
368423
writer.write(&self.inner)
369424
}
370425
}
@@ -376,8 +431,7 @@ mod allocating {
376431
impl<'a> From<&'a BitString> for BitStringRef<'a> {
377432
fn from(bit_string: &'a BitString) -> BitStringRef<'a> {
378433
// Ensured to parse successfully in constructor
379-
BitStringRef::new(bit_string.unused_bits, &bit_string.inner)
380-
.expect("invalid BIT STRING")
434+
BitStringRef::new_unchecked(bit_string.unused_bits, bit_string.bytes_ref())
381435
}
382436
}
383437

@@ -433,7 +487,6 @@ mod allocating {
433487
fn ref_to_owned(&self) -> Self::Owned {
434488
BitString {
435489
unused_bits: self.unused_bits,
436-
bit_length: self.bit_length,
437490
inner: Vec::from(self.inner.as_slice()),
438491
}
439492
}

0 commit comments

Comments
 (0)