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

Issue:
Provide Owned Big Integer type
In order to serialize a BigUint it is required to provide a DER minimal encoding as a data source.
However currently BigUint only allows for borrowing the data.

This means that in order to construct a slice to a structure containing BigUint members, one have to add additional side storage to ohole the data referenced by the native asn1::BigUint.

A type that owns the data would make things much simpler for application that need to encode complex asn.1 structures and value simplicity of code over allocation efficiency.


Discussion / hints:

Fix patch:
diff --git a/src/lib.rs b/src/lib.rs
index 390ad5b..2d2a565 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -157,9 +157,10 @@ pub use crate::tag::Tag;
 pub use crate::types::{
     Asn1DefinedByReadable, Asn1DefinedByWritable, Asn1Readable, Asn1Writable, BMPString, BigInt,
     BigUint, Choice1, Choice2, Choice3, DateTime, DefinedByMarker, Enumerated, Explicit,
-    GeneralizedTime, IA5String, Implicit, Null, OctetStringEncoded, PrintableString, Sequence,
-    SequenceOf, SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter, SimpleAsn1Readable,
-    SimpleAsn1Writable, Tlv, UniversalString, UtcTime, Utf8String, VisibleString,
+    GeneralizedTime, IA5String, Implicit, Null, OctetStringEncoded, OwnedBigInt, OwnedBigUint,
+    PrintableString, Sequence, SequenceOf, SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter,
+    SimpleAsn1Readable, SimpleAsn1Writable, Tlv, UniversalString, UtcTime, Utf8String,
+    VisibleString,
 };
 pub use crate::writer::{write, write_single, WriteBuf, WriteError, WriteResult, Writer};
 
diff --git a/src/parser.rs b/src/parser.rs
index ab03696..a4cde29 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -295,8 +295,9 @@ mod tests {
     use crate::{
         BMPString, BigInt, BigUint, BitString, Choice1, Choice2, Choice3, DateTime, Enumerated,
         Explicit, GeneralizedTime, IA5String, Implicit, ObjectIdentifier, OctetStringEncoded,
-        OwnedBitString, ParseError, ParseErrorKind, ParseLocation, ParseResult, PrintableString,
-        Sequence, SequenceOf, SetOf, Tag, Tlv, UniversalString, UtcTime, Utf8String, VisibleString,
+        OwnedBigInt, OwnedBigUint, OwnedBitString, ParseError, ParseErrorKind, ParseLocation,
+        ParseResult, PrintableString, Sequence, SequenceOf, SetOf, Tag, Tlv, UniversalString,
+        UtcTime, Utf8String, VisibleString,
     };
     use alloc::boxed::Box;
     use alloc::{format, vec};
@@ -843,6 +844,39 @@ mod tests {
         ]);
     }
 
+    #[test]
+    fn test_parse_ownedbiguint() {
+        assert_parses::<OwnedBigUint>(&[
+            (
+                Ok(OwnedBigUint::new(b"\x00".to_vec()).unwrap()),
+                b"\x02\x01\x00",
+            ),
+            (
+                Ok(OwnedBigUint::new(b"\x00\xff".to_vec()).unwrap()),
+                b"\x02\x02\x00\xff",
+            ),
+            (
+                Ok(OwnedBigUint::new(
+                    b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec(),
+                )
+                .unwrap()),
+                b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+            ),
+            (
+                Err(ParseError::new(ParseErrorKind::InvalidValue)),
+                b"\x02\x00",
+            ),
+            (
+                Err(ParseError::new(ParseErrorKind::InvalidValue)),
+                b"\x02\x01\x80",
+            ),
+            (
+                Err(ParseError::new(ParseErrorKind::InvalidValue)),
+                b"\x02\x02\xff\x80",
+            ),
+        ]);
+    }
+
     #[test]
     fn test_parse_bigint() {
         assert_parses::<BigInt>(&[
@@ -867,6 +901,39 @@ mod tests {
         ]);
     }
 
+    #[test]
+    fn test_parse_owned_bigint() {
+        assert_parses::<OwnedBigInt>(&[
+            (
+                Ok(OwnedBigInt::new(b"\x80".to_vec()).unwrap()),
+                b"\x02\x01\x80",
+            ),
+            (
+                Ok(OwnedBigInt::new(b"\xff".to_vec()).unwrap()),
+                b"\x02\x01\xff",
+            ),
+            (
+                Ok(OwnedBigInt::new(b"\x00\xff\xff".to_vec()).unwrap()),
+                b"\x02\x03\x00\xff\xff",
+            ),
+            (
+                Err(ParseError::new(ParseErrorKind::InvalidValue)),
+                b"\x02\x02\xff\xff",
+            ),
+            (
+                Ok(
+                    OwnedBigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec())
+                        .unwrap(),
+                ),
+                b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+            ),
+            (
+                Err(ParseError::new(ParseErrorKind::InvalidValue)),
+                b"\x02\x00",
+            ),
+        ]);
+    }
+
     #[test]
     fn test_parse_object_identifier() {
         assert_parses::<ObjectIdentifier>(&[
diff --git a/src/types.rs b/src/types.rs
index 7bb3a7c..12eac48 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,5 +1,6 @@
 use alloc::boxed::Box;
 use alloc::vec;
+use alloc::vec::Vec;
 use core::borrow::Borrow;
 use core::convert::TryInto;
 use core::hash::{Hash, Hasher};
@@ -655,6 +656,44 @@ impl<'a> SimpleAsn1Writable for BigUint<'a> {
     }
 }
 
+/// Arbitrary sized unsigned integer which owns its data. Contents may be
+/// accessed as `&[u8]` of big-endian data. Its contents always match the DER
+/// encoding of a value (i.e. they are minimal)
+#[derive(PartialEq, Clone, Debug, Hash, Eq)]
+pub struct OwnedBigUint {
+    data: Vec<u8>,
+}
+
+impl OwnedBigUint {
+    /// Create a new `OwnedBigUint` from already encoded data. `data` must be
+    /// encoded as required by DER: minimally and if the high bit would be set
+    /// in the first octet, a leading `\x00` should be prepended (to
+    /// disambiguate from negative values).
+    pub fn new(data: Vec<u8>) -> Option<Self> {
+        validate_integer(&data, false).ok()?;
+        Some(OwnedBigUint { data })
+    }
+
+    /// Returns the contents of the integer as big-endian bytes.
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.data
+    }
+}
+
+impl SimpleAsn1Readable<'_> for OwnedBigUint {
+    const TAG: Tag = Tag::primitive(0x02);
+    fn parse_data(data: &[u8]) -> ParseResult<Self> {
+        OwnedBigUint::new(data.to_vec())
+            .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue))
+    }
+}
+impl SimpleAsn1Writable for OwnedBigUint {
+    const TAG: Tag = Tag::primitive(0x02);
+    fn write_data(&self, dest: &mut WriteBuf) -> WriteResult {
+        dest.push_slice(self.as_bytes())
+    }
+}
+
 /// Arbitrary sized signed integer. Contents may be accessed as `&[u8]` of
 /// big-endian data. Its contents always match the DER encoding of a value
 /// (i.e. they are minimal)
@@ -697,6 +736,48 @@ impl<'a> SimpleAsn1Writable for BigInt<'a> {
     }
 }
 
+/// Arbitrary sized signed integer which owns its contents. Contents may be
+/// accessed as `&[u8]` of big-endian data. Its contents always match the DER
+/// encoding of a value (i.e. they are minimal)
+#[derive(PartialEq, Clone, Debug, Hash, Eq)]
+pub struct OwnedBigInt {
+    data: Vec<u8>,
+}
+
+impl OwnedBigInt {
+    /// Create a new `OwnedBigInt` from already encoded data. `data` must be
+    /// encoded as required by DER: minimally and if the high bit would be set
+    /// in the first octet, a leading `\x00` should be prepended (to
+    /// disambiguate from negative values).
+    pub fn new(data: Vec<u8>) -> Option<Self> {
+        validate_integer(&data, true).ok()?;
+        Some(OwnedBigInt { data })
+    }
+
+    /// Returns the contents of the integer as big-endian bytes.
+    pub fn as_bytes(&self) -> &[u8] {
+        &self.data
+    }
+
+    /// Returns a boolean indicating whether the integer is negative.
+    pub fn is_negative(&self) -> bool {
+        self.data[0] & 0x80 == 0x80
+    }
+}
+
+impl SimpleAsn1Readable<'_> for OwnedBigInt {
+    const TAG: Tag = Tag::primitive(0x02);
+    fn parse_data(data: &[u8]) -> ParseResult<Self> {
+        OwnedBigInt::new(data.to_vec()).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue))
+    }
+}
+impl SimpleAsn1Writable for OwnedBigInt {
+    const TAG: Tag = Tag::primitive(0x02);
+    fn write_data(&self, dest: &mut WriteBuf) -> WriteResult {
+        dest.push_slice(self.as_bytes())
+    }
+}
+
 impl<'a> SimpleAsn1Readable<'a> for ObjectIdentifier {
     const TAG: Tag = Tag::primitive(0x06);
     fn parse_data(data: &'a [u8]) -> ParseResult<ObjectIdentifier> {
@@ -1565,9 +1646,9 @@ impl<T> DefinedByMarker<T> {
 mod tests {
     use crate::{
         parse_single, BigInt, BigUint, DateTime, DefinedByMarker, Enumerated, GeneralizedTime,
-        IA5String, ObjectIdentifier, OctetStringEncoded, ParseError, ParseErrorKind,
-        PrintableString, SequenceOf, SequenceOfWriter, SetOf, SetOfWriter, Tag, Tlv, UtcTime,
-        Utf8String, VisibleString,
+        IA5String, ObjectIdentifier, OctetStringEncoded, OwnedBigInt, OwnedBigUint, ParseError,
+        ParseErrorKind, PrintableString, SequenceOf, SequenceOfWriter, SetOf, SetOfWriter, Tag,
+        Tlv, UtcTime, Utf8String, VisibleString,
     };
     use crate::{Explicit, Implicit};
     use alloc::vec;
@@ -1659,11 +1740,19 @@ mod tests {
     #[test]
     fn test_biguint_as_bytes() {
         assert_eq!(BigUint::new(b"\x01").unwrap().as_bytes(), b"\x01");
+        assert_eq!(
+            OwnedBigUint::new(b"\x01".to_vec()).unwrap().as_bytes(),
+            b"\x01"
+        );
     }
 
     #[test]
     fn test_bigint_as_bytes() {
         assert_eq!(BigInt::new(b"\x01").unwrap().as_bytes(), b"\x01");
+        assert_eq!(
+            OwnedBigInt::new(b"\x01".to_vec()).unwrap().as_bytes(),
+            b"\x01"
+        );
     }
 
     #[test]
@@ -1671,6 +1760,10 @@ mod tests {
         assert!(!BigInt::new(b"\x01").unwrap().is_negative()); // 1
         assert!(!BigInt::new(b"\x00").unwrap().is_negative()); // 0
         assert!(BigInt::new(b"\xff").unwrap().is_negative()); // -1
+
+        assert!(!OwnedBigInt::new(b"\x01".to_vec()).unwrap().is_negative()); // 1
+        assert!(!OwnedBigInt::new(b"\x00".to_vec()).unwrap().is_negative()); // 0
+        assert!(OwnedBigInt::new(b"\xff".to_vec()).unwrap().is_negative()); // -1
     }
 
     #[test]
diff --git a/src/writer.rs b/src/writer.rs
index c6faa45..0ac196d 100644
--- a/src/writer.rs
+++ b/src/writer.rs
@@ -214,9 +214,9 @@ mod tests {
     use crate::{
         parse_single, BMPString, BigInt, BigUint, BitString, Choice1, Choice2, Choice3, DateTime,
         Enumerated, Explicit, GeneralizedTime, IA5String, Implicit, ObjectIdentifier,
-        OctetStringEncoded, OwnedBitString, PrintableString, Sequence, SequenceOf,
-        SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter, Tlv, UniversalString, UtcTime,
-        Utf8String, VisibleString, WriteError,
+        OctetStringEncoded, OwnedBigInt, OwnedBigUint, OwnedBitString, PrintableString, Sequence,
+        SequenceOf, SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter, Tlv, UniversalString,
+        UtcTime, Utf8String, VisibleString, WriteError,
     };
     use alloc::vec::Vec;
 
@@ -438,6 +438,21 @@ mod tests {
         ]);
     }
 
+    #[test]
+    fn test_write_ownedbiguint() {
+        assert_writes::<OwnedBigUint>(&[
+            (
+                OwnedBigUint::new(b"\x00\xff".to_vec()).unwrap(),
+                b"\x02\x02\x00\xff",
+            ),
+            (
+                OwnedBigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec())
+                    .unwrap(),
+                b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+            ),
+        ]);
+    }
+
     #[test]
     fn test_write_bigint() {
         assert_writes::<BigInt>(&[
@@ -449,6 +464,18 @@ mod tests {
         ]);
     }
 
+    #[test]
+    fn test_write_ownedbigint() {
+        assert_writes::<OwnedBigInt>(&[
+            (OwnedBigInt::new(b"\xff".to_vec()).unwrap(), b"\x02\x01\xff"),
+            (
+                OwnedBigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec())
+                    .unwrap(),
+                b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff",
+            ),
+        ]);
+    }
+
     #[test]
     fn test_write_object_identifier() {
         assert_writes::<ObjectIdentifier>(&[


Test patch: