Skip to content

Commit c1d90bf

Browse files
authored
der_derive: add BitString macro (#1720)
* der: add BitString trait for BitString macro der_derive: add BitString macro * fix cargo clippy * docs: BitString macro
1 parent 9813667 commit c1d90bf

File tree

8 files changed

+479
-2
lines changed

8 files changed

+479
-2
lines changed

der/src/asn1.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
mod internal_macros;
66

77
mod any;
8-
mod bit_string;
8+
pub(crate) mod bit_string;
99
#[cfg(feature = "alloc")]
1010
mod bmp_string;
1111
mod boolean;

der/src/asn1/bit_string.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! ASN.1 `BIT STRING` support.
22
3+
pub mod fixed_len_bit_string;
4+
35
use crate::{
46
BytesRef, DecodeValue, DerOrd, EncodeValue, Error, ErrorKind, FixedTag, Header, Length, Reader,
57
Result, Tag, ValueOrd, Writer,
@@ -118,6 +120,17 @@ impl<'a> BitStringRef<'a> {
118120
position: 0,
119121
}
120122
}
123+
124+
/// Returns Some(bit) if index is valid
125+
pub fn get(&self, position: usize) -> Option<bool> {
126+
if position >= self.bit_len() {
127+
return None;
128+
}
129+
130+
let byte = self.raw_bytes().get(position / 8)?;
131+
let bitmask = 1u8 << (7 - (position % 8));
132+
Some(byte & bitmask != 0)
133+
}
121134
}
122135

123136
impl_any_conversions!(BitStringRef<'a>, 'a);
@@ -309,6 +322,11 @@ mod allocating {
309322
pub fn bits(&self) -> BitStringIter<'_> {
310323
BitStringRef::from(self).bits()
311324
}
325+
326+
/// Returns Some(bit) if index is valid
327+
pub fn get(&self, position: usize) -> Option<bool> {
328+
BitStringRef::from(self).get(position)
329+
}
312330
}
313331

314332
impl_any_conversions!(BitString);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use core::ops::RangeInclusive;
2+
3+
use crate::{Error, ErrorKind, Tag};
4+
5+
/// Trait on automatically derived by BitString macro.
6+
/// Used for checking if binary data fits into defined struct.
7+
///
8+
/// ```
9+
/// /// Bit length of 2
10+
/// struct MyBitString {
11+
/// flag1: bool,
12+
/// flag2: bool,
13+
/// }
14+
/// ```
15+
///
16+
/// ```rust,ignore
17+
/// use der::BitString;
18+
///
19+
/// /// Bit length of 3..=4
20+
/// #[derive(BitString)]
21+
/// struct MyBitString {
22+
/// flag1: bool,
23+
/// flag2: bool,
24+
/// flag3: bool,
25+
///
26+
/// #[asn1(optional = "true")]
27+
/// flag4: bool,
28+
/// }
29+
/// ```
30+
pub trait FixedLenBitString {
31+
/// Implementer must specify how many bits are allowed
32+
const ALLOWED_LEN_RANGE: RangeInclusive<u16>;
33+
34+
/// Returns an error if the bitstring is not in expected length range
35+
fn check_bit_len(bit_len: u16) -> Result<(), Error> {
36+
let allowed_len_range = Self::ALLOWED_LEN_RANGE;
37+
38+
// forces allowed range to eg. 3..=4
39+
if !allowed_len_range.contains(&bit_len) {
40+
Err(ErrorKind::Length {
41+
tag: Tag::BitString,
42+
}
43+
.into())
44+
} else {
45+
Ok(())
46+
}
47+
}
48+
}

der/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ mod document;
365365
mod str_owned;
366366

367367
pub use crate::{
368+
asn1::bit_string::fixed_len_bit_string::FixedLenBitString,
368369
asn1::{AnyRef, Choice, Sequence},
369370
datetime::DateTime,
370371
decode::{Decode, DecodeOwned, DecodeValue},
@@ -384,7 +385,7 @@ pub use crate::{
384385
pub use crate::{asn1::Any, document::Document};
385386

386387
#[cfg(feature = "derive")]
387-
pub use der_derive::{Choice, Enumerated, Sequence, ValueOrd};
388+
pub use der_derive::{BitString, Choice, Enumerated, Sequence, ValueOrd};
388389

389390
#[cfg(feature = "flagset")]
390391
pub use flagset;

der/tests/derive.rs

+158
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,164 @@ mod sequence {
627627
}
628628
}
629629

630+
/// Custom derive test cases for the `BitString` macro.
631+
#[cfg(feature = "std")]
632+
mod bitstring {
633+
use der::BitString;
634+
use der::Decode;
635+
use der::Encode;
636+
use hex_literal::hex;
637+
638+
const BITSTRING_EXAMPLE: &[u8] = &hex!("03 03 06 03 80");
639+
640+
// this BitString allows only 10..=10 bits
641+
#[derive(BitString)]
642+
pub struct MyBitStringTest {
643+
pub first_bit: bool,
644+
pub second_bit: bool,
645+
pub third_bit: bool,
646+
pub fourth_bit: bool,
647+
pub a: bool,
648+
pub b: bool,
649+
pub almost_least_significant: bool,
650+
pub least_significant_bit: bool,
651+
652+
// second byte
653+
pub second_byte_bit: bool,
654+
pub second_byte_bit2: bool,
655+
}
656+
657+
#[test]
658+
fn decode_bitstring() {
659+
let test_flags = MyBitStringTest::from_der(BITSTRING_EXAMPLE).unwrap();
660+
661+
assert!(!test_flags.first_bit);
662+
663+
assert!(test_flags.almost_least_significant);
664+
assert!(test_flags.least_significant_bit);
665+
assert!(test_flags.second_byte_bit);
666+
assert!(!test_flags.second_byte_bit2);
667+
668+
let reencoded = test_flags.to_der().unwrap();
669+
670+
assert_eq!(reencoded, BITSTRING_EXAMPLE);
671+
}
672+
673+
/// this BitString will allow only 3..=4 bits
674+
#[derive(BitString)]
675+
pub struct MyBitString3or4 {
676+
pub bit_0: bool,
677+
pub bit_1: bool,
678+
pub bit_2: bool,
679+
680+
#[asn1(optional = "true")]
681+
pub bit_3: bool,
682+
}
683+
684+
#[test]
685+
fn decode_bitstring_3_used_first_lit() {
686+
// 5 unused bits, so 3 used
687+
let bits_3 = MyBitString3or4::from_der(&hex!("03 02 05 80")).unwrap();
688+
689+
assert!(bits_3.bit_0);
690+
assert!(!bits_3.bit_1);
691+
assert!(!bits_3.bit_2);
692+
assert!(!bits_3.bit_3);
693+
}
694+
#[test]
695+
fn decode_bitstring_3_used_all_lit() {
696+
// 5 unused bits, so 3 used
697+
let bits_3 = MyBitString3or4::from_der(&hex!("03 02 05 FF")).unwrap();
698+
699+
assert!(bits_3.bit_0);
700+
assert!(bits_3.bit_1);
701+
assert!(bits_3.bit_2);
702+
assert!(!bits_3.bit_3);
703+
}
704+
705+
#[test]
706+
fn decode_bitstring_4_used_all_lit() {
707+
// 4 unused bits, so 4 used
708+
let bits_3 = MyBitString3or4::from_der(&hex!("03 02 04 FF")).unwrap();
709+
710+
assert!(bits_3.bit_0);
711+
assert!(bits_3.bit_1);
712+
assert!(bits_3.bit_2);
713+
assert!(bits_3.bit_3);
714+
}
715+
716+
#[test]
717+
fn decode_invalid_bitstring_5_used() {
718+
// 3 unused bits, so 5 used
719+
assert!(MyBitString3or4::from_der(&hex!("03 02 03 FF")).is_err());
720+
}
721+
722+
#[test]
723+
fn decode_invalid_bitstring_2_used() {
724+
// 6 unused bits, so 2 used
725+
assert!(MyBitString3or4::from_der(&hex!("03 02 06 FF")).is_err());
726+
}
727+
728+
#[test]
729+
fn encode_3_zero_bits() {
730+
let encoded_3_zeros = MyBitString3or4 {
731+
bit_0: false,
732+
bit_1: false,
733+
bit_2: false,
734+
bit_3: false,
735+
}
736+
.to_der()
737+
.unwrap();
738+
739+
// 3 bits used, 5 unused
740+
assert_eq!(encoded_3_zeros, hex!("03 02 05 00"));
741+
}
742+
743+
#[test]
744+
fn encode_3_one_bits() {
745+
let encoded_3_zeros = MyBitString3or4 {
746+
bit_0: true,
747+
bit_1: true,
748+
bit_2: true,
749+
bit_3: false,
750+
}
751+
.to_der()
752+
.unwrap();
753+
754+
// 3 bits used, 5 unused
755+
assert_eq!(encoded_3_zeros, hex!("03 02 05 E0"));
756+
}
757+
758+
#[test]
759+
fn encode_4_one_bits() {
760+
let encoded_4_zeros = MyBitString3or4 {
761+
bit_0: true,
762+
bit_1: true,
763+
bit_2: true,
764+
bit_3: true,
765+
}
766+
.to_der()
767+
.unwrap();
768+
769+
// 4 bits used, 4 unused
770+
assert_eq!(encoded_4_zeros, hex!("03 02 04 F0"));
771+
}
772+
773+
#[test]
774+
fn encode_optional_one_4_used() {
775+
let encoded_4_zeros = MyBitString3or4 {
776+
bit_0: false,
777+
bit_1: false,
778+
bit_2: false,
779+
bit_3: true,
780+
}
781+
.to_der()
782+
.unwrap();
783+
784+
// 4 bits used, 4 unused
785+
assert_eq!(encoded_4_zeros, hex!("03 02 04 10"));
786+
}
787+
}
630788
mod infer_default {
631789
//! When another crate might define a PartialEq for another type, the use of
632790
//! `default="Default::default"` in the der derivation will not provide enough

0 commit comments

Comments
 (0)