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

Issue:
write_int should take arbitrarily sized rust ints or some bignum
Plan: new Integer interface here that we implement serialization code for.



Discussion / hints:
I thought I would have enough time to look into this today, but I didn't get around to it. Might as well mention though that there is a Rust library offering bignum support:
- https://doc.rust-lang.org/num/num/bigint/struct.BigInt.html
- https://doc.rust-lang.org/num/num/bigint/struct.BigUint.html
- https://github.com/rust-lang-deprecated/num ([more info here about 'deprecated'](https://internals.rust-lang.org/t/psa-movement-of-rust-lang-crates/2671))


Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 2ae6daa..fcfa30f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,3 +10,4 @@ description = "ASN.1 (DER) serializer for Rust."
 [dependencies]
 byteorder = "*"
 chrono = "*"
+num = "*"
diff --git a/src/deserializer.rs b/src/deserializer.rs
index 12a7373..b918034 100644
--- a/src/deserializer.rs
+++ b/src/deserializer.rs
@@ -156,6 +156,8 @@ mod tests {
     use std;
     use std::{fmt};
 
+    use num::{BigInt, FromPrimitive, One};
+
     use utils::{ObjectIdentifier};
     use super::{Deserializer, DeserializationError, DeserializationResult, from_vec};
 
@@ -247,6 +249,30 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_read_int_bigint() {
+        assert_deserializes(vec![
+            (Ok(BigInt::from_i64(0).unwrap()), b"\x02\x01\x00".to_vec()),
+            (Ok(BigInt::from_i64(127).unwrap()), b"\x02\x01\x7f".to_vec()),
+            (Ok(BigInt::from_i64(128).unwrap()), b"\x02\x02\x00\x80".to_vec()),
+            (Ok(BigInt::from_i64(256).unwrap()), b"\x02\x02\x01\x00".to_vec()),
+            (Ok(BigInt::from_i64(-128).unwrap()), b"\x02\x01\x80".to_vec()),
+            (Ok(BigInt::from_i64(-129).unwrap()), b"\x02\x02\xff\x7f".to_vec()),
+            (Ok(BigInt::from_i64(-256).unwrap()), b"\x02\x02\xff\x00".to_vec()),
+            (
+                Ok(BigInt::from_i64(std::i64::MAX).unwrap()),
+                b"\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff".to_vec()
+            ),
+            (
+                Ok(BigInt::from_i64(std::i64::MAX).unwrap() + BigInt::one()),
+                b"\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00".to_vec()
+            ),
+            (Err(DeserializationError::InvalidValue), b"\x02\x00".to_vec()),
+        ], |deserializer| {
+            return deserializer.read_int();
+        });
+    }
+
     #[test]
     fn test_read_octet_string() {
         assert_deserializes(vec![
diff --git a/src/lib.rs b/src/lib.rs
index 0f5acaf..a19753f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 extern crate byteorder;
 extern crate chrono;
+extern crate num;
 
 mod deserializer;
 mod serializer;
diff --git a/src/serializer.rs b/src/serializer.rs
index f579757..6bae038 100644
--- a/src/serializer.rs
+++ b/src/serializer.rs
@@ -145,8 +145,12 @@ pub fn to_vec<F>(f: F) -> Vec<u8> where F: Fn(&mut Serializer) {
 
 #[cfg(test)]
 mod tests {
+    use std;
+
     use chrono::{TimeZone, UTC};
 
+    use num::{BigInt, FromPrimitive, One};
+
     use utils::{ObjectIdentifier};
     use super::{Serializer, to_vec};
 
@@ -212,6 +216,26 @@ mod tests {
         });
     }
 
+    #[test]
+    fn test_write_int_bigint() {
+        assert_serializes(vec![
+            (BigInt::from_i64(0).unwrap(), b"\x02\x01\x00".to_vec()),
+            (BigInt::from_i64(127).unwrap(), b"\x02\x01\x7f".to_vec()),
+            (BigInt::from_i64(128).unwrap(), b"\x02\x02\x00\x80".to_vec()),
+            (BigInt::from_i64(255).unwrap(), b"\x02\x02\x00\xff".to_vec()),
+            (BigInt::from_i64(256).unwrap(), b"\x02\x02\x01\x00".to_vec()),
+            (BigInt::from_i64(-1).unwrap(), b"\x02\x01\xff".to_vec()),
+            (BigInt::from_i64(-128).unwrap(), b"\x02\x01\x80".to_vec()),
+            (BigInt::from_i64(-129).unwrap(), b"\x02\x02\xff\x7f".to_vec()),
+            (
+                BigInt::from_i64(std::i64::MAX).unwrap() + BigInt::one(),
+                b"\x02\x09\x00\x80\x00\x00\x00\x00\x00\x00\x00".to_vec()
+            ),
+        ], |serializer, v| {
+            serializer.write_int(v);
+        });
+    }
+
     #[test]
     fn test_write_octet_string() {
         assert_serializes(vec![
diff --git a/src/utils.rs b/src/utils.rs
index 9ce1ace..a7866b5 100644
--- a/src/utils.rs
+++ b/src/utils.rs
@@ -1,5 +1,8 @@
 use std::{mem};
 
+use num::{BigInt, One, Signed};
+use num::bigint::{Sign};
+
 use deserializer::{DeserializationError, DeserializationResult};
 
 
@@ -74,6 +77,48 @@ primitive_integer!(i8);
 primitive_integer!(i32);
 primitive_integer!(i64);
 
+impl Integer for BigInt {
+    fn encode(&self) -> Vec<u8> {
+        if self.is_positive() {
+            let (_, mut bytes) = self.to_bytes_be();
+            if bytes[0] & 0x80 == 0x80 {
+                // If the data has a leading 0x80, pad with a zero-byte.
+                bytes.insert(0, 0);
+            }
+            return bytes;
+        } else if self.is_negative() {
+            // Convert negative numbers to two's-complement by subtracting one and inverting.
+            let n_minus_1 = -self - BigInt::one();
+            let (_, mut bytes) = n_minus_1.to_bytes_be();
+
+            for i in 0..bytes.len() {
+                bytes[i] ^= 0xff;
+            }
+
+            if bytes[0] & 0x80 == 0 {
+                bytes.insert(0, 0xff);
+            }
+            return bytes;
+        } else {
+            return b"\x00".to_vec();
+        }
+    }
+
+    fn decode(data: Vec<u8>) -> DeserializationResult<BigInt> {
+        if data.is_empty() {
+            return Err(DeserializationError::InvalidValue);
+        }
+
+        if data[0] & 0x80 == 0x80 {
+            let inverse_bytes = data.iter().map(|b| !b).collect::<Vec<u8>>();
+            let n_minus_1 = BigInt::from_bytes_be(Sign::Plus, &inverse_bytes[..]);
+            return Ok(-(n_minus_1 + BigInt::one()));
+        } else {
+            return Ok(BigInt::from_bytes_be(Sign::Plus, &data[..]));
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::{ObjectIdentifier};


Test patch: