Repository: alex/rust-asn1 (language: rust)

Issue:
Add BIT STRING



Discussion / hints:

Fix patch:
diff --git a/src/deserializer.rs b/src/deserializer.rs
index 5f8f631..c870050 100644
--- a/src/deserializer.rs
+++ b/src/deserializer.rs
@@ -3,7 +3,7 @@ use std::io::{BufRead, Cursor};
 
 use byteorder::{self, ReadBytesExt};
 
-use utils::{Integer, ObjectIdentifier};
+use utils::{BitString, Integer, ObjectIdentifier};
 
 
 #[derive(Debug, PartialEq, Eq)]
@@ -139,6 +139,24 @@ impl<'a> Deserializer<'a> {
         });
     }
 
+    pub fn read_bit_string(&mut self) -> DeserializationResult<BitString> {
+        return self._read_with_tag(3, |data| {
+            let padding_bits = match data.get(0) {
+                Some(&bits) => bits,
+                None => return Err(DeserializationError::InvalidValue),
+            };
+
+            if padding_bits > 7 || (data.len() == 1 && padding_bits > 0) {
+                return Err(DeserializationError::InvalidValue);
+            }
+
+            return BitString::new(
+                data[1..].to_vec(),
+                (data.len() - 1) * 8 - (padding_bits as usize),
+            ).ok_or(DeserializationError::InvalidValue);
+        });
+    }
+
     pub fn read_object_identifier(&mut self) -> DeserializationResult<ObjectIdentifier> {
         return self._read_with_tag(6, |data| {
             if data.is_empty() {
@@ -187,7 +205,7 @@ mod tests {
 
     use num::{BigInt, FromPrimitive, One};
 
-    use utils::{ObjectIdentifier};
+    use utils::{BitString, ObjectIdentifier};
     use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};
 
     fn assert_deserializes<T, F>(values: Vec<(DeserializationResult<T>, &[u8])>, f: F)
@@ -325,6 +343,22 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_read_bit_string() {
+        assert_deserializes(vec![
+            (Ok(BitString::new(b"".to_vec(), 0).unwrap()), b"\x03\x01\x00"),
+            (Ok(BitString::new(b"\x00".to_vec(), 1).unwrap()), b"\x03\x02\x07\x00"),
+            (Ok(BitString::new(b"\x80".to_vec(), 1).unwrap()), b"\x03\x02\x07\x80"),
+            (Ok(BitString::new(b"\x81\xf0".to_vec(), 12).unwrap()), b"\x03\x03\x04\x81\xf0"),
+            (Err(DeserializationError::InvalidValue), b"\x03\x00"),
+            (Err(DeserializationError::InvalidValue), b"\x03\x02\x07\x01"),
+            (Err(DeserializationError::InvalidValue), b"\x03\x02\x07\x40"),
+            (Err(DeserializationError::InvalidValue), b"\x03\x02\x08\x00"),
+        ], |deserializer| {
+            return deserializer.read_bit_string();
+        })
+    }
+
     #[test]
     fn test_read_object_identifier() {
         assert_deserializes(vec![
diff --git a/src/lib.rs b/src/lib.rs
index a19753f..039a190 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -8,4 +8,4 @@ mod utils;
 
 pub use deserializer::{Deserializer, DeserializationError, DeserializationResult, from_vec};
 pub use serializer::{Serializer, to_vec};
-pub use utils::{ObjectIdentifier};
+pub use utils::{BitString, ObjectIdentifier};
diff --git a/src/serializer.rs b/src/serializer.rs
index 6bae038..41aa404 100644
--- a/src/serializer.rs
+++ b/src/serializer.rs
@@ -5,7 +5,7 @@ use byteorder::{WriteBytesExt};
 
 use chrono::{DateTime, UTC};
 
-use utils::{Integer, ObjectIdentifier};
+use utils::{BitString, Integer, ObjectIdentifier};
 
 
 fn _write_base128_int(data: &mut Vec<u8>, n: u32) {
@@ -109,6 +109,16 @@ impl<'a> Serializer<'a> {
         });
     }
 
+    pub fn write_bit_string(&mut self, v: BitString) {
+        return self._write_with_tag(3, || {
+            let data = v.as_bytes();
+            let mut result = Vec::with_capacity(1 + data.len());
+            result.push(((8 - (v.len() % 8)) % 8) as u8);
+            result.extend(data);
+            return result;
+        })
+    }
+
     pub fn write_object_identifier(&mut self, v: ObjectIdentifier) {
         return self._write_with_tag(6, || {
             let mut data = Vec::new();
@@ -151,7 +161,7 @@ mod tests {
 
     use num::{BigInt, FromPrimitive, One};
 
-    use utils::{ObjectIdentifier};
+    use utils::{BitString, ObjectIdentifier};
     use super::{Serializer, to_vec};
 
     fn assert_serializes<T, F>(values: Vec<(T, Vec<u8>)>, f: F)
@@ -261,6 +271,17 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_write_bit_string() {
+        assert_serializes(vec![
+            (BitString::new(b"".to_vec(), 0).unwrap(), b"\x03\x01\x00".to_vec()),
+            (BitString::new(b"\x80".to_vec(), 1).unwrap(), b"\x03\x02\x07\x80".to_vec()),
+            (BitString::new(b"\x81\xf0".to_vec(), 12).unwrap(), b"\x03\x03\x04\x81\xf0".to_vec()),
+        ], |serializer, v| {
+            serializer.write_bit_string(v);
+        })
+    }
+
     #[test]
     fn test_write_object_identifier() {
         assert_serializes(vec![
diff --git a/src/utils.rs b/src/utils.rs
index 7c8ba84..a6268f3 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -23,6 +23,46 @@ impl ObjectIdentifier {
     }
 }
 
+#[derive(Clone, Debug, PartialEq, Eq)]
+pub struct BitString {
+    data: Vec<u8>,
+    bit_length: usize,
+}
+
+impl BitString {
+    pub fn from_bytes(data: Vec<u8>) -> Option<BitString> {
+        let length = data.len() * 8;
+        return BitString::new(data, length);
+    }
+
+    pub fn new(data: Vec<u8>, bit_length: usize) -> Option<BitString> {
+        match (data.len(), bit_length) {
+            (0, 0) => (),
+            (_, 0) | (0, _) => return None,
+            (i, j) if (i * 8 < j) || (i - 1) * 8 > j => return None,
+            _ => (),
+        }
+
+        let padding_bits = data.len() * 8 - bit_length;
+        if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 {
+            return None;
+        }
+
+        return Some(BitString {
+            data: data,
+            bit_length: bit_length,
+        });
+    }
+
+    pub fn len(&self) -> usize {
+        return self.bit_length;
+    }
+
+    pub fn as_bytes(&self) -> &Vec<u8> {
+        return &self.data;
+    }
+}
+
 fn _int_length(v: i64) -> usize {
     let mut num_bytes = 1;
     let mut i = v;
@@ -121,7 +161,7 @@ impl Integer for BigInt {
 
 #[cfg(test)]
 mod tests {
-    use super::{ObjectIdentifier};
+    use super::{BitString, ObjectIdentifier};
 
     #[test]
     fn test_object_identifier_new() {
@@ -129,4 +169,18 @@ mod tests {
         assert!(ObjectIdentifier::new(vec![3, 10]).is_none());
         assert!(ObjectIdentifier::new(vec![1, 50]).is_none());
     }
+
+    #[test]
+    fn test_bit_string_new() {
+        assert!(BitString::new(b"".to_vec(), 1).is_none());
+        assert!(BitString::new(b"\x00".to_vec(), 0).is_none());
+        assert!(BitString::new(b"\x00".to_vec(), 9).is_none());
+        assert!(BitString::new(b"\xff".to_vec(), 3).is_none());
+    }
+
+    #[test]
+    fn test_bit_string_from_bytes() {
+        assert_eq!(BitString::from_bytes(b"".to_vec()), BitString::new(b"".to_vec(), 0));
+        assert_eq!(BitString::from_bytes(b"abc".to_vec()), BitString::new(b"abc".to_vec(), 24));
+    }
 }


Test patch: