Merge "Upgrade rust/crates/der-parser to 6.0.0"
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index d5c766e..4eaee56 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,5 +1,5 @@
 {
   "git": {
-    "sha1": "c8adf28076c11651618bcf414a20ecd0bd896315"
+    "sha1": "7b9b5b84387d7c4707b1abc7a13faba515b7c08e"
   }
 }
diff --git a/Android.bp b/Android.bp
index bd24618..55296a0 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,8 +1,6 @@
 // This file is generated by cargo2android.py --config cargo2android.json.
 // Do not modify this file as changes will be overridden on upgrade.
 
-
-
 package {
     default_applicable_licenses: ["external_rust_crates_der-parser_license"],
 }
@@ -43,11 +41,14 @@
     name: "libder_parser",
     host_supported: true,
     crate_name: "der_parser",
+    cargo_env_compat: true,
+    cargo_pkg_version: "6.0.0",
     srcs: ["src/lib.rs"],
     edition: "2018",
     features: [
         "bigint",
         "num-bigint",
+        "std",
     ],
     rustlibs: [
         "libnom",
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4bc3295..2fa3705 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,21 +8,39 @@
 
 ### Thanks
 
-## 5.1.2
+## 6.0.0
+
+This release has several major changes:
+- upgrade to nom 7
+- add support for `no_std`
+- remove all macros
+- update MSRV to 1.48
 
 ### Changed/Fixed
 
-- Make `BerError` Copy + Clone
-- Fix clippy warnings (rustc 1.54)
-
-## 5.1.1
-
-### Changed/Fixed
-
+- Do not attempt to parse PRIVATE object contents (closes #48)
 - BER: raise error if using Indefinite length and not constructed
+- Fix `oid!` macro to be independant of `der_parser` crate name and path (#46)
+- Simplify `der-oid-macro`, do not depend on `nom`
 - Fix `INTEGER` signed/unsigned parsing (#49)
-- Fix INTEGER signed/unsigned parsing for bigint/biguint (#49)
-- Doc: clarify documentation for parsing integers (#49)
+- Change `as_bigint()` and `as_uint()` to return a `Result`
+- Remove deprecated functions
+
+### Added
+
+- Added support for `no_std` (#50)
+- Make `BerError` Copy + Clone (#51)
+- Add feature 'bitvec' for `.as_bitslice()` methods
+
+### Removed
+
+- Remove all macros
+
+### Thanks
+
+- @yoguorui for `no_std` support
+- @SergioBenitez for `BerError` traits
+- @lilyball for `INTEGER` parsing
 
 ## 5.1.0
 
diff --git a/Cargo.toml b/Cargo.toml
index 9715d20..55418dc 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -3,17 +3,16 @@
 # When uploading crates to the registry Cargo will automatically
 # "normalize" Cargo.toml files for maximal compatibility
 # with all versions of Cargo and also rewrite `path` dependencies
-# to registry (e.g., crates.io) dependencies
+# to registry (e.g., crates.io) dependencies.
 #
-# If you believe there's an error in this file please file an
-# issue against the rust-lang/cargo repository. If you're
-# editing this file be aware that the upstream Cargo.toml
-# will likely look very different (and much more reasonable)
+# If you are reading this file be aware that the original Cargo.toml
+# will likely look very different (and much more reasonable).
+# See Cargo.toml.orig for the original contents.
 
 [package]
 edition = "2018"
 name = "der-parser"
-version = "5.1.2"
+version = "6.0.0"
 authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
 include = ["LICENSE-*", "CHANGELOG.md", "README.md", "UPGRADING.md", ".gitignore", "Cargo.toml", "bench/*.rs", "src/*.rs", "src/ber/*.rs", "src/der/*.rs", "tests/*.rs", "der-oid-macro/Cargo.toml", "der-oid-macro/src/*.rs"]
 description = "Parser/encoder for ASN.1 BER/DER data"
@@ -26,15 +25,19 @@
 [package.metadata.docs.rs]
 all-features = true
 rustdoc-args = ["--cfg", "docsrs"]
+[dependencies.bitvec]
+version = "0.22"
+optional = true
+
 [dependencies.cookie-factory]
 version = "0.3.0"
 optional = true
 
 [dependencies.der-oid-macro]
-version = "0.4"
+version = "0.5"
 
 [dependencies.nom]
-version = "6.0"
+version = "7.0"
 
 [dependencies.num-bigint]
 version = "0.4"
@@ -44,7 +47,7 @@
 version = "0.2"
 
 [dependencies.rusticata-macros]
-version = "3.0.1"
+version = "4.0"
 [dev-dependencies.hex-literal]
 version = "0.3"
 
@@ -56,6 +59,7 @@
 
 [features]
 bigint = ["num-bigint"]
-default = []
-serialize = ["cookie-factory"]
+default = ["std"]
+serialize = ["std", "cookie-factory"]
+std = []
 unstable = []
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index e2a6c3f..69f5242 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -5,7 +5,7 @@
 homepage = "https://github.com/rusticata/der-parser"
 repository = "https://github.com/rusticata/der-parser.git"
 name = "der-parser"
-version = "5.1.2"
+version = "6.0.0"
 authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
 categories = ["parser-implementations"]
 readme = "README.md"
@@ -32,19 +32,21 @@
 rustdoc-args = ["--cfg", "docsrs"]
 
 [dependencies]
+bitvec = { version="0.22", optional=true }
 cookie-factory = { version="0.3.0", optional=true }
-nom = "6.0"
-rusticata-macros = "3.0.1"
+nom = "7.0"
+rusticata-macros = "4.0"
 num-traits = "0.2"
 num-bigint = { version = "0.4", optional = true }
 
-der-oid-macro = { version = "0.4", path = "./der-oid-macro" }
+der-oid-macro = { version = "0.5", path = "./der-oid-macro" }
 
 [features]
-default = []
+default = ["std"]
 bigint = ["num-bigint"]
-serialize = ["cookie-factory"]
+serialize = ["std", "cookie-factory"]
 unstable = []
+std = []
 
 [dev-dependencies]
 hex-literal = "0.3"
diff --git a/METADATA b/METADATA
index c3a9cdb..c30f813 100644
--- a/METADATA
+++ b/METADATA
@@ -7,13 +7,13 @@
   }
   url {
     type: ARCHIVE
-    value: "https://static.crates.io/crates/der-parser/der-parser-5.1.2.crate"
+    value: "https://static.crates.io/crates/der-parser/der-parser-6.0.0.crate"
   }
-  version: "5.1.2"
+  version: "6.0.0"
   license_type: NOTICE
   last_upgrade_date {
     year: 2021
-    month: 8
-    day: 9
+    month: 9
+    day: 27
   }
 }
diff --git a/README.md b/README.md
index 03f33bc..42f3ee4 100644
--- a/README.md
+++ b/README.md
@@ -5,9 +5,9 @@
 [![docs.rs](https://docs.rs/der-parser/badge.svg)](https://docs.rs/der-parser)
 [![crates.io](https://img.shields.io/crates/v/der-parser.svg)](https://crates.io/crates/der-parser)
 [![Download numbers](https://img.shields.io/crates/d/der-parser.svg)](https://crates.io/crates/der-parser)
-[![dependency status](https://deps.rs/crate/der-parser/5.1.2/status.svg)](https://deps.rs/crate/der-parser/5.1.2)
+[![dependency status](https://deps.rs/crate/der-parser/5.0.0/status.svg)](https://deps.rs/crate/der-parser/5.0.1)
 [![Github CI](https://github.com/rusticata/der-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/der-parser/actions)
-[![Minimum rustc version](https://img.shields.io/badge/rustc-1.44.0+-lightgray.svg)](#rust-version-requirements)
+[![Minimum rustc version](https://img.shields.io/badge/rustc-1.48.0+-lightgray.svg)](#rust-version-requirements)
 
 # BER/DER Parser
 
@@ -174,7 +174,7 @@
 assert_eq!(object.as_u64(), Ok(65537));
 
 #[cfg(feature = "bigint")]
-assert_eq!(object.as_bigint(), Some(65537.into()))
+assert_eq!(object.as_bigint(), Ok(65537.into()))
 ```
 
 Access to the raw value is possible using the `as_slice` method.
@@ -198,7 +198,7 @@
 
 ## Rust version requirements
 
-The 5.0 series of `der-parser` requires **Rustc version 1.44 or greater**, based on nom 6
+The 6.0 series of `der-parser` requires **Rustc version 1.48 or greater**, based on nom 7
 dependencies.
 
 # Serialization
diff --git a/cargo2android.json b/cargo2android.json
index fd3c228..1a78574 100644
--- a/cargo2android.json
+++ b/cargo2android.json
@@ -1,6 +1,6 @@
 {
     "device": true,
-    "features": "bigint",
+    "features": "bigint,std",
     "run": true
 }
 
diff --git a/patches/bitvec_dep.patch b/patches/bitvec_dep.patch
deleted file mode 100644
index 40bad0b..0000000
--- a/patches/bitvec_dep.patch
+++ /dev/null
@@ -1,44 +0,0 @@
-diff --git a/src/ber/ber.rs b/src/ber/ber.rs
-index 6f6741d..e89b6c8 100644
---- a/src/ber/ber.rs
-+++ b/src/ber/ber.rs
-@@ -9,6 +9,7 @@ use core::convert::From;
- use crate::ber::integer::*;
- use crate::error::BerError;
- use crate::oid::Oid;
-+#[cfg(not(any(target_os = "android", target_os = "linux")))]
- use nom::bitvec::{order::Msb0, slice::BitSlice};
- use rusticata_macros::newtype_enum;
- use std::convert::AsRef;
-@@ -479,6 +480,7 @@ impl<'a> BerObject<'a> {
-     }
- 
-     /// Constructs a shared `&BitSlice` reference over the object data, if available as slice.
-+    #[cfg(not(any(target_os = "android", target_os = "linux")))]
-     pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> {
-         self.content.as_bitslice()
-     }
-@@ -678,6 +680,7 @@ impl<'a> BerObjectContent<'a> {
-     }
- 
-     /// Constructs a shared `&BitSlice` reference over the object data, if available as slice.
-+    #[cfg(not(any(target_os = "android", target_os = "linux")))]
-     pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> {
-         self.as_slice()
-             .and_then(|s| BitSlice::<Msb0, _>::from_slice(s).ok_or(BerError::BerValueError))
-@@ -908,6 +911,7 @@ impl<'a> BitStringObject<'a> {
-     }
- 
-     /// Constructs a shared `&BitSlice` reference over the object data.
-+    #[cfg(not(any(target_os = "android", target_os = "linux")))]
-     pub fn as_bitslice(&self) -> Option<&BitSlice<Msb0, u8>> {
-         BitSlice::<Msb0, _>::from_slice(&self.data)
-     }
-@@ -981,6 +985,7 @@ mod tests {
-     }
- 
-     #[test]
-+    #[cfg(not(any(target_os = "android", target_os = "linux")))]
-     fn test_der_bitslice() {
-         let obj = BitStringObject {
-             data: &[0x0f, 0x00, 0x40],
diff --git a/src/ber/ber.rs b/src/ber/ber.rs
index e7bd384..47d5069 100644
--- a/src/ber/ber.rs
+++ b/src/ber/ber.rs
@@ -2,15 +2,17 @@
 use crate::ber::integer::*;
 use crate::error::BerError;
 use crate::oid::Oid;
-#[cfg(not(any(target_os = "android", target_os = "linux")))]
-use nom::bitvec::{order::Msb0, slice::BitSlice};
+use alloc::borrow::ToOwned;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+#[cfg(feature = "bitvec")]
+use bitvec::{order::Msb0, slice::BitSlice};
+use core::convert::AsRef;
+use core::convert::From;
+use core::convert::TryFrom;
+use core::fmt;
+use core::ops::Index;
 use rusticata_macros::newtype_enum;
-use std::convert::AsRef;
-use std::convert::From;
-use std::convert::TryFrom;
-use std::fmt;
-use std::ops::Index;
-use std::vec::Vec;
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq)]
 pub struct BerClassFromIntError(pub(crate) ());
@@ -183,9 +185,11 @@
     Optional(Option<Box<BerObject<'a>>>),
     /// Tagged object (EXPLICIT): class, tag  and content of inner object
     Tagged(BerClass, BerTag, Box<BerObject<'a>>),
+    /// Private
+    Private(BerObjectHeader<'a>, &'a [u8]),
 
     /// Unknown object: object tag (copied from header), and raw content
-    Unknown(BerTag, &'a [u8]),
+    Unknown(BerClass, BerTag, &'a [u8]),
 }
 
 impl fmt::Display for BerClass {
@@ -391,15 +395,6 @@
         BerObject::from_obj(BerObjectContent::Set(l))
     }
 
-    /// Build a BER header from this object content
-    #[deprecated(
-        since = "0.5.0",
-        note = "please use `obj.header` or `obj.header.clone()` instead"
-    )]
-    pub fn to_header(&self) -> BerObjectHeader {
-        self.header.clone()
-    }
-
     /// Attempt to read a signed integer value from DER object.
     ///
     /// This can fail if the object is not an integer, or if it is too large.
@@ -519,7 +514,7 @@
     }
 
     /// Constructs a shared `&BitSlice` reference over the object data, if available as slice.
-    #[cfg(not(any(target_os = "android", target_os = "linux")))]
+    #[cfg(feature = "bitvec")]
     pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> {
         self.content.as_bitslice()
     }
@@ -595,16 +590,6 @@
     }
 }
 
-/// Replacement function for Option.xor (>= 1.37)
-#[inline]
-pub(crate) fn xor_option<T>(opta: Option<T>, optb: Option<T>) -> Option<T> {
-    match (opta, optb) {
-        (Some(a), None) => Some(a),
-        (None, Some(b)) => Some(b),
-        _ => None,
-    }
-}
-
 /// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`)
 impl<'a> PartialEq<BerObjectHeader<'a>> for BerObjectHeader<'a> {
     fn eq(&self, other: &BerObjectHeader) -> bool {
@@ -620,7 +605,7 @@
             }
             && {
                 // it tag is present for both, compare it
-                if xor_option(self.raw_tag, other.raw_tag).is_none() {
+                if self.raw_tag.xor(other.raw_tag).is_none() {
                     self.raw_tag == other.raw_tag
                 } else {
                     true
@@ -735,7 +720,7 @@
             }
             BerObjectContent::BitString(ignored_bits, data) => {
                 bitstring_to_u64(*ignored_bits as usize, data).and_then(|x| {
-                    if x > u64::from(std::u32::MAX) {
+                    if x > u64::from(core::u32::MAX) {
                         Err(BerError::IntegerTooLarge)
                     } else {
                         Ok(x as u32)
@@ -743,7 +728,7 @@
                 })
             }
             BerObjectContent::Enum(i) => {
-                if *i > u64::from(std::u32::MAX) {
+                if *i > u64::from(core::u32::MAX) {
                     Err(BerError::IntegerTooLarge)
                 } else {
                     Ok(*i as u32)
@@ -802,10 +787,10 @@
     }
 
     /// Constructs a shared `&BitSlice` reference over the object data, if available as slice.
-    #[cfg(not(any(target_os = "android", target_os = "linux")))]
+    #[cfg(feature = "bitvec")]
     pub fn as_bitslice(&self) -> Result<&BitSlice<Msb0, u8>, BerError> {
         self.as_slice()
-            .and_then(|s| BitSlice::<Msb0, _>::from_slice(s).ok_or(BerError::BerValueError))
+            .and_then(|s| BitSlice::<Msb0, _>::from_slice(s).map_err(|_| BerError::BerValueError))
     }
 
     pub fn as_sequence(&self) -> Result<&Vec<BerObject<'a>>, BerError> {
@@ -842,7 +827,8 @@
             BerObjectContent::ObjectDescriptor(s) |
             BerObjectContent::GraphicString(s) |
             BerObjectContent::GeneralString(s) |
-            BerObjectContent::Unknown(_,s) => Ok(s),
+            BerObjectContent::Unknown(_, _,s) |
+            BerObjectContent::Private(_,s) => Ok(s),
             _ => Err(BerError::BerTypeError),
         }
     }
@@ -890,7 +876,8 @@
             BerObjectContent::GraphicString(_)     => BerTag::GraphicString,
             BerObjectContent::GeneralString(_)     => BerTag::GeneralString,
             BerObjectContent::Tagged(_,x,_) |
-            BerObjectContent::Unknown(x,_)         => *x,
+            BerObjectContent::Unknown(_, x,_)         => *x,
+            &BerObjectContent::Private(ref hdr, _) => hdr.tag,
             BerObjectContent::Optional(Some(obj))  => obj.content.tag(),
             BerObjectContent::Optional(None)       => BerTag(0x00), // XXX invalid !
         }
@@ -917,12 +904,12 @@
     ///
     /// let (_, object) = parse_ber_integer(data).expect("parsing failed");
     /// # #[cfg(feature = "bigint")]
-    /// assert_eq!(object.as_bigint(), Some(65537.into()))
+    /// assert_eq!(object.as_bigint(), Ok(65537.into()))
     /// ```
-    pub fn as_bigint(&self) -> Option<BigInt> {
+    pub fn as_bigint(&self) -> Result<BigInt, BerError> {
         match self.content {
-            BerObjectContent::Integer(s) => Some(BigInt::from_signed_bytes_be(s)),
-            _ => None,
+            BerObjectContent::Integer(s) => Ok(BigInt::from_signed_bytes_be(s)),
+            _ => Err(BerError::InvalidTag),
         }
     }
 
@@ -939,17 +926,17 @@
     ///
     /// let (_, object) = parse_ber_integer(data).expect("parsing failed");
     /// # #[cfg(feature = "bigint")]
-    /// assert_eq!(object.as_biguint(), Some(65537_u32.into()))
+    /// assert_eq!(object.as_biguint(), Ok(65537_u32.into()))
     /// ```
-    pub fn as_biguint(&self) -> Option<BigUint> {
+    pub fn as_biguint(&self) -> Result<BigUint, BerError> {
         match self.content {
             BerObjectContent::Integer(s) => {
                 if is_highest_bit_set(s) {
-                    return None;
+                    return Err(BerError::IntegerNegative);
                 }
-                Some(BigUint::from_bytes_be(s))
+                Ok(BigUint::from_bytes_be(s))
             }
-            _ => None,
+            _ => Err(BerError::InvalidTag),
         }
     }
 }
@@ -1066,9 +1053,9 @@
     }
 
     /// Constructs a shared `&BitSlice` reference over the object data.
-    #[cfg(not(any(target_os = "android", target_os = "linux")))]
+    #[cfg(feature = "bitvec")]
     pub fn as_bitslice(&self) -> Option<&BitSlice<Msb0, u8>> {
-        BitSlice::<Msb0, _>::from_slice(self.data)
+        BitSlice::<Msb0, _>::from_slice(self.data).ok()
     }
 }
 
@@ -1138,17 +1125,18 @@
         assert!(obj.is_set(17));
     }
 
+    #[cfg(feature = "bitvec")]
     #[test]
-    #[cfg(not(any(target_os = "android", target_os = "linux")))]
     fn test_der_bitslice() {
+        use std::string::String;
         let obj = BitStringObject {
             data: &[0x0f, 0x00, 0x40],
         };
         let slice = obj.as_bitslice().expect("as_bitslice");
-        assert_eq!(slice.get(0), Some(&false));
-        assert_eq!(slice.get(7), Some(&true));
-        assert_eq!(slice.get(9), Some(&false));
-        assert_eq!(slice.get(17), Some(&true));
+        assert_eq!(slice.get(0).as_deref(), Some(&false));
+        assert_eq!(slice.get(7).as_deref(), Some(&true));
+        assert_eq!(slice.get(9).as_deref(), Some(&false));
+        assert_eq!(slice.get(17).as_deref(), Some(&true));
         let s = slice.iter().fold(String::with_capacity(24), |mut acc, b| {
             acc += if *b { "1" } else { "0" };
             acc
@@ -1172,7 +1160,7 @@
         let obj = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01"));
         let expected = ::num_bigint::BigInt::from(0x10001);
 
-        assert_eq!(obj.as_bigint(), Some(expected));
+        assert_eq!(obj.as_bigint(), Ok(expected));
     }
 
     #[cfg(feature = "bigint")]
@@ -1181,6 +1169,6 @@
         let obj = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01"));
         let expected = ::num_bigint::BigUint::from(0x10001_u32);
 
-        assert_eq!(obj.as_biguint(), Some(expected));
+        assert_eq!(obj.as_biguint(), Ok(expected));
     }
 }
diff --git a/src/ber/mod.rs b/src/ber/mod.rs
index f2fa5f9..c674d93 100644
--- a/src/ber/mod.rs
+++ b/src/ber/mod.rs
@@ -62,3 +62,8 @@
 #[cfg(feature = "serialize")]
 pub use crate::ber::serialize::*;
 pub use crate::ber::tagged::*;
+
+use alloc::borrow::Cow;
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use core::convert::{Into, TryFrom};
diff --git a/src/ber/parser.rs b/src/ber/parser.rs
index ace4937..81ccef8 100644
--- a/src/ber/parser.rs
+++ b/src/ber/parser.rs
@@ -7,8 +7,6 @@
 use nom::number::streaming::be_u8;
 use nom::{Err, Needed, Offset};
 use rusticata_macros::{combinator::parse_hex_to_u64, custom_check};
-use std::borrow::Cow;
-use std::convert::{Into, TryFrom};
 
 /// Default maximum recursion limit
 pub const MAX_RECURSION: usize = 50;
@@ -280,7 +278,7 @@
 
 fn ber_read_content_utf8string(i: &[u8], len: usize) -> BerResult<BerObjectContent> {
     let (i, bytes) = take(len)(i)?;
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::UTF8String(s))?;
     Ok((i, s))
@@ -345,7 +343,7 @@
     if !bytes.iter().all(is_numeric) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::NumericString(s))?;
     Ok((i, s))
@@ -361,7 +359,7 @@
     if !bytes.iter().all(is_visible) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::VisibleString(s))?;
     Ok((i, s))
@@ -395,7 +393,7 @@
     if !bytes.iter().all(is_printable) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::PrintableString(s))?;
     Ok((i, s))
@@ -416,7 +414,7 @@
     if !bytes.iter().all(u8::is_ascii) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::IA5String(s))?;
     Ok((i, s))
@@ -432,7 +430,7 @@
     if !bytes.iter().all(is_visible) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::UTCTime(s))?;
     Ok((i, s))
@@ -451,7 +449,7 @@
     if !bytes.iter().all(is_visible) {
         return Err(Err::Error(BerError::StringInvalidCharset));
     }
-    let s = std::str::from_utf8(bytes)
+    let s = core::str::from_utf8(bytes)
         .map_err(|_| Err::Error(BerError::StringInvalidCharset))
         .map(|s| BerObjectContent::GeneralizedTime(s))?;
     Ok((i, s))
@@ -995,22 +993,6 @@
     }))(i)
 }
 
-/// Parse an optional tagged object, applying function to get content
-///
-/// This function is deprecated, use
-/// [parse_ber_explicit_optional](fn.parse_ber_explicit_optional.html) instead.
-#[deprecated(
-    since = "5.0.0",
-    note = "Please use `parse_ber_explicit_optional` instead"
-)]
-#[inline]
-pub fn parse_ber_explicit<F>(i: &[u8], tag: BerTag, f: F) -> BerResult
-where
-    F: Fn(&[u8]) -> BerResult,
-{
-    parse_ber_explicit_optional(i, tag, f)
-}
-
 /// Parse an implicit tagged object, applying function to read content
 ///
 /// Note: unlike explicit tagged functions, the callback must be a *content* parsing function,
@@ -1184,10 +1166,16 @@
         custom_check!(i, l > MAX_OBJECT_SIZE, BerError::InvalidLength)?;
     }
     match hdr.class {
-        BerClass::Universal | BerClass::Private => (),
+        BerClass::Universal => (),
+        BerClass::Private => {
+            let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
+            let content = BerObjectContent::Private(hdr.clone(), content);
+            let obj = BerObject::from_header_and_content(hdr, content);
+            return Ok((rem, obj));
+        }
         _ => {
             let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
-            let content = BerObjectContent::Unknown(hdr.tag, content);
+            let content = BerObjectContent::Unknown(hdr.class, hdr.tag, content);
             let obj = BerObject::from_header_and_content(hdr, content);
             return Ok((rem, obj));
         }
@@ -1196,7 +1184,7 @@
         Ok((rem, content)) => Ok((rem, BerObject::from_header_and_content(hdr, content))),
         Err(Err::Error(BerError::UnknownTag)) => {
             let (rem, content) = ber_get_object_content(rem, &hdr, max_depth)?;
-            let content = BerObjectContent::Unknown(hdr.tag, content);
+            let content = BerObjectContent::Unknown(hdr.class, hdr.tag, content);
             let obj = BerObject::from_header_and_content(hdr, content);
             Ok((rem, obj))
         }
diff --git a/src/ber/print.rs b/src/ber/print.rs
index b3ae221..e80eb5e 100644
--- a/src/ber/print.rs
+++ b/src/ber/print.rs
@@ -1,8 +1,10 @@
 use crate::ber::BitStringObject;
 use crate::ber::{BerObject, BerObjectContent, BerTag};
-use std::fmt;
-use std::iter::FromIterator;
-use std::str;
+use alloc::string::String;
+use alloc::vec::Vec;
+use core::fmt;
+use core::iter::FromIterator;
+use core::str;
 
 use rusticata_macros::debug;
 
@@ -77,7 +79,7 @@
         fn print_utf32_string_with_type(f: &mut fmt::Formatter, s: &[u8], ty: &str) -> fmt::Result {
             let chars: Option<Vec<char>> = s
                 .chunks_exact(4)
-                .map(|a| std::char::from_u32(u32::from_be_bytes([a[0], a[1], a[2], a[3]])))
+                .map(|a| core::char::from_u32(u32::from_be_bytes([a[0], a[1], a[2], a[3]])))
                 .collect();
 
             match chars {
@@ -116,6 +118,9 @@
                     None => writeln!(f, "NONE"),
                 }
             }
+            BerObjectContent::Private(ref hdr, bytes) => {
+                writeln!(f, "Private(c:{} s:{} t:{}): {:?}", hdr.class, hdr.structured, hdr.tag.0, debug::HexSlice(bytes))
+            },
             BerObjectContent::Tagged(class, tag, ref obj) => {
                 writeln!(f, "ContextSpecific [{} {}] {{", class, tag)?;
                 write!(f, "{:?}", self.next_indent(obj))?;
@@ -138,7 +143,7 @@
                 writeln!(f, "]")?;
                 Ok(())
             },
-            BerObjectContent::Unknown(tag,o)         => writeln!(f, "Unknown({:?},{:x?})", tag, o),
+            BerObjectContent::Unknown(class, tag,o)         => writeln!(f, "Unknown({:?},{:?},{:x?})", class, tag, o),
         }
     }
 }
diff --git a/src/ber/serialize.rs b/src/ber/serialize.rs
index 7ffedc2..8c82e29 100644
--- a/src/ber/serialize.rs
+++ b/src/ber/serialize.rs
@@ -1,3 +1,4 @@
+#![cfg(feature = "std")]
 use crate::ber::*;
 use crate::oid::Oid;
 use cookie_factory::bytes::be_u8;
@@ -8,8 +9,6 @@
 use cookie_factory::{GenError, SerializeFn};
 use std::io::Write;
 
-// we do not use .copied() for compatibility with 1.34
-#[allow(clippy::map_clone)]
 fn encode_length<'a, W: Write + 'a, Len: Into<BerSize>>(len: Len) -> impl SerializeFn<W> + 'a {
     let l = len.into();
     move |out| {
@@ -23,7 +22,7 @@
                     let v: Vec<u8> = sz
                         .to_be_bytes()
                         .iter()
-                        .map(|&x| x)
+                        .cloned()
                         .skip_while(|&b| b == 0)
                         .collect();
                     let b0 = 0b1000_0000 | (v.len() as u8);
@@ -108,8 +107,6 @@
     }
 }
 
-// we do not use .copied() for compatibility with 1.34
-#[allow(clippy::map_clone)]
 fn ber_encode_object_content<'a, W: Write + Default + AsRef<[u8]> + 'a>(
     c: &'a BerObjectContent,
 ) -> impl SerializeFn<W> + 'a {
@@ -129,7 +126,7 @@
             let v: Vec<u8> = i
                 .to_be_bytes()
                 .iter()
-                .map(|&x| x)
+                .cloned()
                 .skip_while(|&b| b == 0)
                 .collect();
             slice(v)(out)
@@ -158,12 +155,13 @@
                 None => slice(&[])(out), // XXX encode NOP ?
             }
         }
+        BerObjectContent::Private(_hdr, bytes) => slice(bytes)(out),
         BerObjectContent::Tagged(_class, _tag, inner) => {
             // directly encode inner object
             // XXX wrong, we should wrap it!
             ber_encode_object(inner)(out)
         }
-        BerObjectContent::Unknown(_tag, s) => slice(s)(out),
+        BerObjectContent::Unknown(_, _, s) => slice(s)(out),
     }
 }
 
diff --git a/src/der/mod.rs b/src/der/mod.rs
index aaa64c5..bad3f23 100644
--- a/src/der/mod.rs
+++ b/src/der/mod.rs
@@ -62,6 +62,10 @@
 pub use crate::der::parser::*;
 pub use crate::der::tagged::*;
 
+use alloc::boxed::Box;
+use alloc::vec::Vec;
+use core::convert::{Into, TryFrom};
+
 /// DER Object class of tag (same as `BerClass`)
 pub type DerClass = BerClass;
 
diff --git a/src/der/parser.rs b/src/der/parser.rs
index bb5ea0f..04b4b23 100644
--- a/src/der/parser.rs
+++ b/src/der/parser.rs
@@ -5,7 +5,6 @@
 use nom::number::streaming::be_u8;
 use nom::{Err, Needed};
 use rusticata_macros::custom_check;
-use std::convert::{Into, TryFrom};
 
 /// Parse DER object recursively
 ///
@@ -317,22 +316,6 @@
     parse_ber_explicit_optional(i, tag, f)
 }
 
-/// Parse an optional tagged object, applying function to get content
-///
-/// This function is deprecated, use
-/// [parse_der_explicit_optional](fn.parse_der_explicit_optional.html) instead.
-#[deprecated(
-    since = "4.1.0",
-    note = "Please use `parse_der_explicit_optional` instead"
-)]
-#[inline]
-pub fn parse_der_explicit<F>(i: &[u8], tag: DerTag, f: F) -> BerResult
-where
-    F: Fn(&[u8]) -> BerResult,
-{
-    parse_der_explicit_optional(i, tag, f)
-}
-
 /// Parse an implicit tagged object, applying function to read content
 ///
 /// Note: unlike explicit tagged functions, the callback must be a *content* parsing function,
@@ -575,10 +558,16 @@
     max_depth: usize,
 ) -> DerResult<'a> {
     match hdr.class {
-        DerClass::Universal | DerClass::Private => (),
+        BerClass::Universal => (),
+        BerClass::Private => {
+            let (rem, content) = ber_get_object_content(i, &hdr, max_depth)?;
+            let content = BerObjectContent::Private(hdr.clone(), content);
+            let obj = BerObject::from_header_and_content(hdr, content);
+            return Ok((rem, obj));
+        }
         _ => {
             let (i, content) = ber_get_object_content(i, &hdr, max_depth)?;
-            let content = DerObjectContent::Unknown(hdr.tag, content);
+            let content = DerObjectContent::Unknown(hdr.class, hdr.tag, content);
             let obj = DerObject::from_header_and_content(hdr, content);
             return Ok((i, obj));
         }
@@ -587,7 +576,7 @@
         Ok((rem, content)) => Ok((rem, DerObject::from_header_and_content(hdr, content))),
         Err(Err::Error(BerError::UnknownTag)) => {
             let (rem, content) = ber_get_object_content(i, &hdr, max_depth)?;
-            let content = DerObjectContent::Unknown(hdr.tag, content);
+            let content = DerObjectContent::Unknown(hdr.class, hdr.tag, content);
             let obj = DerObject::from_header_and_content(hdr, content);
             Ok((rem, obj))
         }
diff --git a/src/error.rs b/src/error.rs
index 803bdee..8a496a8 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -2,10 +2,9 @@
 
 use crate::ber::BerObject;
 use crate::der::DerObject;
+use alloc::fmt;
 use nom::error::{ErrorKind, FromExternalError, ParseError};
 use nom::IResult;
-use std::error::Error;
-use std::fmt;
 
 /// Holds the result of parsing functions
 ///
@@ -97,11 +96,14 @@
     }
 }
 
-impl Error for BerError {}
+#[cfg(feature = "std")]
+impl std::error::Error for BerError {}
 
-#[cfg(test)]
+#[cfg(all(test, feature = "std"))]
 mod tests {
     use super::*;
+    use std::boxed::Box;
+    use std::error::Error;
 
     #[test]
     fn test_unwrap_bererror() {
diff --git a/src/lib.rs b/src/lib.rs
index 926a036..a5f6e2e 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,9 +3,9 @@
 //! [![docs.rs](https://docs.rs/der-parser/badge.svg)](https://docs.rs/der-parser)
 //! [![crates.io](https://img.shields.io/crates/v/der-parser.svg)](https://crates.io/crates/der-parser)
 //! [![Download numbers](https://img.shields.io/crates/d/der-parser.svg)](https://crates.io/crates/der-parser)
-//! [![dependency status](https://deps.rs/crate/der-parser/5.1.2/status.svg)](https://deps.rs/crate/der-parser/5.1.2)
+//! [![dependency status](https://deps.rs/crate/der-parser/5.0.0/status.svg)](https://deps.rs/crate/der-parser/5.0.1)
 //! [![Github CI](https://github.com/rusticata/der-parser/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/der-parser/actions)
-//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.44.0+-lightgray.svg)](#rust-version-requirements)
+//! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.48.0+-lightgray.svg)](#rust-version-requirements)
 //!
 //! # BER/DER Parser
 //!
@@ -172,7 +172,7 @@
 //! assert_eq!(object.as_u64(), Ok(65537));
 //!
 //! #[cfg(feature = "bigint")]
-//! assert_eq!(object.as_bigint(), Some(65537.into()))
+//! assert_eq!(object.as_bigint(), Ok(65537.into()))
 //! ```
 //!
 //! Access to the raw value is possible using the `as_slice` method.
@@ -196,7 +196,7 @@
 //!
 //! ## Rust version requirements
 //!
-//! The 5.0 series of `der-parser` requires **Rustc version 1.44 or greater**, based on nom 6
+//! The 6.0 series of `der-parser` requires **Rustc version 1.48 or greater**, based on nom 7
 //! dependencies.
 //!
 //! # Serialization
@@ -237,9 +237,13 @@
     no_crate_inject,
     attr(deny(warnings/*, rust_2018_idioms*/), allow(dead_code, unused_variables))
 ))]
+#![no_std]
 
+#[cfg(any(test, feature = "std"))]
 #[macro_use]
-mod macros;
+extern crate std;
+
+extern crate alloc;
 
 #[allow(clippy::module_inception)]
 pub mod ber;
@@ -259,7 +263,32 @@
 // re-exports nom macros, so this crate's macros can be used without importing nom
 pub use nom::IResult;
 #[doc(hidden)]
-pub use rusticata_macros::{custom_check, flat_take};
+pub use rusticata_macros::custom_check;
+
+#[doc(hidden)]
+pub mod exports {
+    pub use alloc::borrow;
+    pub use der_oid_macro;
+}
 
 /// Procedural macro to get encoded oids, see the [oid module](oid/index.html).
-pub use der_oid_macro::oid;
+#[macro_export]
+macro_rules! oid {
+    (raw $($args:tt)*) => {{
+        $crate::exports::der_oid_macro::encode_oid!($($args)*)
+    }};
+    (rel $($args:tt)*) => {{
+        $crate::oid::Oid::new_relative(
+            $crate::exports::borrow::Cow::Borrowed(&
+                $crate::exports::der_oid_macro::encode_oid!(rel $($args)*)
+            )
+        )
+    }};
+    ($($args:tt)*) => {{
+        $crate::oid::Oid::new(
+            $crate::exports::borrow::Cow::Borrowed(&
+                $crate::exports::der_oid_macro::encode_oid!($($args)*)
+            )
+        )
+    }};
+}
diff --git a/src/macros.rs b/src/macros.rs
deleted file mode 100644
index 24ad59b..0000000
--- a/src/macros.rs
+++ /dev/null
@@ -1,668 +0,0 @@
-/// Internal parser, do not use directly
-#[doc(hidden)]
-#[macro_export]
-macro_rules! fold_der_defined_m(
-    (__impl $i:expr, $acc:ident, $f:ident) => ( {
-        match $f($i) {
-            Ok((rem,res)) => { $acc.push(res); Ok((rem,$acc)) },
-            Err(e)        => Err(e)
-        }
-    });
-    (__impl $i:expr, $acc:ident, $submac:ident!( $($args:tt)* ) ) => ( {
-        match $submac!($i, $($args)*) {
-            Ok((rem,res)) => { $acc.push(res); Ok((rem,$acc)) },
-            Err(e)        => Err(e)
-        }
-    });
-    (__impl $i:expr, $acc:ident, $f:ident >> $($rest:tt)*) => (
-        {
-            match $f($i) {
-                Ok((rem,res)) => {
-                    $acc.push(res);
-                    fold_der_defined_m!(__impl rem, $acc, $($rest)* )
-                },
-                Err(e)        => Err(e)
-            }
-        }
-    );
-    (__impl $i:expr, $acc:ident, $submac:ident!( $($args:tt)* ) >> $($rest:tt)*) => (
-        {
-            match $submac!($i, $($args)*) {
-                Ok((rem,res)) => {
-                    $acc.push(res);
-                    fold_der_defined_m!(__impl rem, $acc, $($rest)* )
-                },
-                Err(e)        => Err(e)
-            }
-        }
-    );
-
-    ($i:expr, $($rest:tt)* ) => (
-        {
-            let mut v = Vec::new();
-            fold_der_defined_m!(__impl $i, v, $($rest)*)
-        }
-    );
-);
-
-/// Internal parser, do not use directly
-#[doc(hidden)]
-#[macro_export]
-macro_rules! parse_ber_defined_m(
-    ($i:expr, $tag:expr, $($args:tt)*) => (
-        {
-            use $crate::ber::parse_ber_container;
-            use $crate::error::BerError;
-            parse_ber_container(|content, hdr| {
-                if !hdr.is_constructed() {
-                    return Err(nom::Err::Error(BerError::ConstructExpected.into()));
-                }
-                if hdr.tag != $tag {
-                    return Err(nom::Err::Error(BerError::InvalidTag.into()));
-                }
-                let (rem, content) = fold_der_defined_m!(content, $($args)* )?;
-                Ok((rem, (hdr, content)))
-            })($i)
-        }
-    );
-);
-
-/// Parse a defined sequence of DER elements (deprecated)
-///
-/// Given a list of expected parsers, apply them to build a DER sequence.
-///
-/// Deprecated, use [`parse_der_sequence_defined`](macro.parse_der_sequence_defined.html) instead.
-#[macro_export]
-#[deprecated(since = "3.0.0", note = "Use parse_der_sequence_defined")]
-macro_rules! parse_der_sequence_defined_m(
-    ($i:expr, $($args:tt)*) => ({
-        parse_der_sequence_defined!($i, $($args)*)
-    });
-);
-
-/// Parse a defined set of DER elements (deprecated)
-///
-/// Given a list of expected parsers, apply them to build a DER set.
-///
-/// Deprecated, use [`parse_der_set_defined`](macro.parse_der_set_defined.html) instead.
-#[macro_export]
-#[deprecated(since = "3.0.0", note = "Use parse_der_set_defined")]
-macro_rules! parse_der_set_defined_m(
-    ($i:expr, $($args:tt)*) => ({
-        parse_der_set_defined!($i, $($args)*)
-    });
-);
-
-/// Internal parser, do not use directly
-#[doc(hidden)]
-#[macro_export]
-macro_rules! fold_parsers(
-    ($i:expr, $($args:tt)*) => (
-        {
-            let parsers = [ $($args)* ];
-            parsers.iter().fold(
-                (Ok(($i,vec![]))),
-                |r, f| {
-                    match r {
-                        Ok((rem,mut v)) => {
-                            map!(rem, f, |x| { v.push(x); v })
-                        }
-                        Err(e)          => Err(e)
-                    }
-                }
-                )
-        }
-    );
-);
-
-/// Internal parser, do not use directly
-#[doc(hidden)]
-#[macro_export]
-macro_rules! parse_der_defined(
-    ($i:expr, $tag:expr, $($args:tt)*) => (
-        {
-            use $crate::ber::ber_read_element_header;
-            let res =
-            do_parse!(
-                $i,
-                hdr:     ber_read_element_header >>
-                         custom_check!(hdr.class != $crate::ber::BerClass::Universal, $crate::error::BerError::InvalidClass) >>
-                         custom_check!(hdr.structured != 0b1, $crate::error::BerError::ConstructExpected) >>
-                         custom_check!(hdr.tag != $tag, $crate::error::BerError::InvalidTag) >>
-                content: take!(hdr.len) >>
-                (hdr,content)
-            );
-            match res {
-                Ok((_rem,o)) => {
-                    match fold_parsers!(o.1, $($args)* ) {
-                        Ok((rem,v)) => {
-                            if rem.len() != 0 { Err(::nom::Err::Error($crate::error::BerError::ObjectTooShort)) }
-                            else { Ok((_rem,(o.0,v))) }
-                        },
-                        Err(e)      => Err(e)
-                    }
-                },
-                Err(e)       => Err(e)
-            }
-        }
-    );
-);
-
-/// Parse a defined sequence of DER elements
-///
-/// Given a list of expected parsers, apply them to build a DER sequence.
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{parse_ber_integer, BerObject};
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// fn localparse_seq(i:&[u8]) -> BerResult {
-///     parse_der_sequence_defined!(i,
-///         parse_ber_integer >>
-///         parse_ber_integer
-///     )
-/// }
-///
-/// let empty = &b""[..];
-/// let bytes = [ 0x30, 0x0a,
-///               0x02, 0x03, 0x01, 0x00, 0x01,
-///               0x02, 0x03, 0x01, 0x00, 0x00,
-/// ];
-/// let expected  = BerObject::from_seq(vec![
-///     BerObject::from_int_slice(b"\x01\x00\x01"),
-///     BerObject::from_int_slice(b"\x01\x00\x00"),
-/// ]);
-/// assert_eq!(localparse_seq(&bytes), Ok((empty, expected)));
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_sequence_defined(
-    ($i:expr, $($args:tt)*) => ({
-        use nom::map;
-        map!(
-            $i,
-            parse_ber_defined_m!($crate::ber::BerTag::Sequence, $($args)*),
-            |(hdr,o)| $crate::ber::BerObject::from_header_and_content(hdr,$crate::ber::BerObjectContent::Sequence(o))
-        )
-    });
-);
-
-/// Parse a defined set of DER elements
-///
-/// Given a list of expected parsers, apply them to build a DER set.
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{parse_ber_integer, BerObject};
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// fn localparse_set(i:&[u8]) -> BerResult {
-///     parse_der_set_defined!(i,
-///         parse_ber_integer >>
-///         parse_ber_integer
-///     )
-/// }
-///
-/// let empty = &b""[..];
-/// let bytes = [ 0x31, 0x0a,
-///               0x02, 0x03, 0x01, 0x00, 0x01,
-///               0x02, 0x03, 0x01, 0x00, 0x00,
-/// ];
-///  let expected = BerObject::from_set(vec![
-///      BerObject::from_int_slice(b"\x01\x00\x01").set_raw_tag(Some(&[0x2])),
-///      BerObject::from_int_slice(b"\x01\x00\x00").set_raw_tag(Some(&[0x2])),
-///  ]).set_raw_tag(Some(&[0x31]));
-/// assert_eq!(localparse_set(&bytes), Ok((empty, expected)));
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_set_defined(
-    ($i:expr, $($args:tt)*) => ({
-        use nom::map;
-        map!(
-            $i,
-            parse_ber_defined_m!($crate::ber::BerTag::Set, $($args)*),
-            |(hdr,o)| $crate::ber::BerObject::from_header_and_content(hdr,$crate::ber::BerObjectContent::Set(o))
-        )
-    });
-);
-
-/// Parse a sequence of identical DER elements
-///
-/// Given a subparser for a DER type, parse a sequence of identical objects.
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{parse_ber_integer, BerObject};
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// fn parser(i:&[u8]) -> BerResult {
-///     parse_der_sequence_of!(i, parse_ber_integer)
-/// }
-///
-/// let empty = &b""[..];
-/// let bytes = [ 0x30, 0x0a,
-///               0x02, 0x03, 0x01, 0x00, 0x01,
-///               0x02, 0x03, 0x01, 0x00, 0x00,
-/// ];
-/// let expected  = BerObject::from_seq(vec![
-///     BerObject::from_int_slice(b"\x01\x00\x01"),
-///     BerObject::from_int_slice(b"\x01\x00\x00"),
-/// ]);
-/// assert_eq!(parser(&bytes), Ok((empty, expected)));
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_sequence_of(
-    ($i:expr, $f:ident) => ({
-        use $crate::ber::parse_ber_sequence_of;
-        parse_ber_sequence_of($f)($i)
-    })
-);
-
-/// Parse a set of identical DER elements
-///
-/// Given a subparser for a DER type, parse a set of identical objects.
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{parse_ber_integer, BerObject};
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// fn parser(i:&[u8]) -> BerResult {
-///     parse_der_set_of!(i, parse_ber_integer)
-/// }
-///
-/// let empty = &b""[..];
-/// let bytes = [ 0x31, 0x0a,
-///               0x02, 0x03, 0x01, 0x00, 0x01,
-///               0x02, 0x03, 0x01, 0x00, 0x00,
-/// ];
-/// let expected  = BerObject::from_set(vec![
-///     BerObject::from_int_slice(b"\x01\x00\x01"),
-///     BerObject::from_int_slice(b"\x01\x00\x00"),
-/// ]);
-/// assert_eq!(parser(&bytes), Ok((empty, expected)));
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_set_of(
-    ($i:expr, $f:ident) => ({
-        use $crate::ber::parse_ber_set_of;
-        parse_ber_set_of($f)($i)
-    })
-);
-
-/// Parse an optional DER element
-///
-/// Try to parse an optional DER element, and return it as a `ContextSpecific` item with tag 0.
-/// If the parsing failed, the `ContextSpecific` object has value `None`.
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::*;
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// let empty = &b""[..];
-/// let bytes1 = [ 0x30, 0x0a,
-///                0x0a, 0x03, 0x00, 0x00, 0x01,
-///                0x02, 0x03, 0x01, 0x00, 0x01];
-/// let bytes2 = [ 0x30, 0x05,
-///                0x02, 0x03, 0x01, 0x00, 0x01];
-/// let expected1 = BerObject::from_seq(vec![
-///     BerObject::from_obj(BerObjectContent::Optional(Some(Box::new(
-///         BerObject::from_obj(BerObjectContent::Enum(1)),
-///     )))),
-///     BerObject::from_int_slice(b"\x01\x00\x01"),
-/// ]);
-/// let expected2  = BerObject::from_seq(vec![
-///     BerObject::from_obj(
-///         BerObjectContent::Optional(None),
-///     ),
-///     BerObject::from_int_slice(b"\x01\x00\x01"),
-/// ]);
-///
-/// fn parse_optional_enum(i:&[u8]) -> BerResult {
-///     parse_der_optional!(i, parse_ber_enum)
-/// }
-/// fn parser(i:&[u8]) -> BerResult {
-///     parse_der_sequence_defined!(i,
-///         parse_optional_enum >>
-///         parse_ber_integer
-///     )
-/// }
-///
-/// assert_eq!(parser(&bytes1), Ok((empty, expected1)));
-/// assert_eq!(parser(&bytes2), Ok((empty, expected2)));
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_optional(
-    ($i:expr, $f:ident) => (
-        match $f($i) {
-            Ok((rem, obj)) => {
-                let opt =
-                    $crate::ber::BerObject::from_header_and_content(
-                        obj.header.clone(),
-                        $crate::ber::BerObjectContent::Optional(Some(Box::new(obj)))
-                    );
-                Ok((rem, opt))
-            }
-            Err(_) => {
-                let opt = $crate::ber::BerObject::from_obj(
-                    $crate::ber::BerObjectContent::Optional(None)
-                );
-                Ok(($i, opt))
-            }
-        }
-        // alt!(
-        //     $i,
-        //     complete!(do_parse!(
-        //         content: call!($f) >>
-        //         (
-        //             $crate::ber::BerObject::from_obj(
-        //                 $crate::ber::BerObjectContent::ContextSpecific($crate::ber::BerTag(0) /* XXX */,Some(Box::new(content)))
-        //             )
-        //         )
-        //     )) |
-        //     complete!(call!($crate::ber::parse_ber_explicit_failed,$crate::ber::BerTag(0) /* XXX */))
-        // )
-    )
-);
-
-/// Parse a constructed DER element
-///
-/// Read a constructed DER element (sequence or set, typically) using the provided functions.
-/// This is generally used to build a struct from a DER sequence.
-///
-/// The returned object is a tuple containing a [`BerObjectHeader`](struct.BerObjectHeader.html)
-/// and the object returned by the subparser.
-///
-/// To ensure the subparser consumes all bytes from the constructed object, add the `eof!()`
-/// subparser as the last parsing item.
-///
-/// To verify the tag of the constructed element, use the `TAG` version, for ex
-/// `parse_der_struct!(i, TAG DerTag::Sequence, parse_der_integer)`
-///
-/// Similar to [`parse_der_sequence_defined`](macro.parse_der_sequence_defined.html), but using the
-/// `do_parse` macro from nom.
-/// This allows declaring variables, and running code at the end.
-///
-/// # Examples
-///
-/// Basic struct parsing (ignoring tag):
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::*;
-/// # use der_parser::error::BerResult;
-/// # use nom::eof;
-/// #
-/// # fn main() {
-/// #[derive(Debug, PartialEq)]
-/// struct MyStruct<'a>{
-///     a: BerObject<'a>,
-///     b: BerObject<'a>,
-/// }
-///
-/// fn parse_struct01(i: &[u8]) -> BerResult<MyStruct> {
-///     parse_der_struct!(
-///         i,
-///         a: parse_ber_integer >>
-///         b: parse_ber_integer >>
-///            eof!() >>
-///         ( MyStruct{ a: a, b: b } )
-///     )
-/// }
-///
-/// let bytes = [ 0x30, 0x0a,
-///               0x02, 0x03, 0x01, 0x00, 0x01,
-///               0x02, 0x03, 0x01, 0x00, 0x00,
-/// ];
-/// let empty = &b""[..];
-/// let expected = MyStruct {
-///     a: BerObject::from_int_slice(b"\x01\x00\x01"),
-///     b: BerObject::from_int_slice(b"\x01\x00\x00"),
-/// };
-/// let res = parse_struct01(&bytes);
-/// assert_eq!(res, Ok((empty, expected)));
-/// # }
-/// ```
-///
-/// To check the expected tag, use the `TAG <tagname>` variant:
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::*;
-/// # use der_parser::error::BerResult;
-/// # use nom::eof;
-/// #
-/// # fn main() {
-/// struct MyStruct<'a>{
-///     a: BerObject<'a>,
-///     b: BerObject<'a>,
-/// }
-///
-/// fn parse_struct_with_tag(i: &[u8]) -> BerResult<MyStruct> {
-///     parse_der_struct!(
-///         i,
-///         TAG BerTag::Sequence,
-///         a: parse_ber_integer >>
-///         b: parse_ber_integer >>
-///            eof!() >>
-///         ( MyStruct{ a, b } )
-///     )
-/// }
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_struct(
-    ($i:expr, TAG $tag:expr, $($rest:tt)*) => ({
-        use $crate::ber::parse_ber_container;
-        use $crate::error::BerError;
-        use nom::do_parse;
-        parse_ber_container(
-            |i, hdr| {
-                if !hdr.is_constructed() {
-                    return Err(nom::Err::Error(BerError::ConstructExpected.into()));
-                }
-                if hdr.tag != $tag {
-                    return Err(nom::Err::Error(BerError::InvalidTag.into()));
-                }
-                do_parse!(i, $($rest)*)
-            }
-        )($i)
-    });
-    ($i:expr, $($rest:tt)*) => ({
-        use $crate::ber::parse_ber_container;
-        use $crate::error::BerError;
-        use nom::do_parse;
-        parse_ber_container(
-            |i, hdr| {
-                if !hdr.is_constructed() {
-                    return Err(nom::Err::Error(BerError::ConstructExpected.into()));
-                }
-                do_parse!(i, $($rest)*)
-            }
-        )($i)
-    });
-);
-
-/// Parse a tagged DER element
-///
-/// Read a tagged DER element using the provided function.
-///
-/// The returned object is either the object returned by the subparser, or a nom error.
-/// Unlike [`parse_der_explicit`](fn.parse_der_explicit.html) or
-/// [`parse_der_implicit`](fn.parse_der_implicit.html), the returned values are *not* encapsulated
-/// in a `BerObject` (they are directly returned, without the tag).
-///
-/// To specify the kind of tag, use the EXPLICIT or IMPLICIT keyword. If no keyword is specified,
-/// the parsing is EXPLICIT by default.
-///
-/// When parsing IMPLICIT values, the third argument is a [`DerTag`](enum.DerTag.html) defining the
-/// subtype of the object.
-///
-/// # Examples
-///
-/// The following parses `[2] INTEGER`:
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{parse_ber_integer, BerObject};
-/// # use nom::map_res;
-/// # use der_parser::error::BerResult;
-/// #
-/// # fn main() {
-/// fn parse_int_explicit(i:&[u8]) -> BerResult<u32> {
-///     map_res!(
-///         i,
-///         parse_der_tagged!(EXPLICIT 2, parse_ber_integer),
-///         |x: BerObject| x.as_tagged()?.2.as_u32()
-///     )
-/// }
-///
-/// let bytes = &[0xa2, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01];
-/// let res = parse_int_explicit(bytes);
-/// match res {
-///     Ok((rem,val)) => {
-///         assert!(rem.is_empty());
-///         assert_eq!(val, 0x10001);
-///     },
-///     Err(e) => panic!("parse_int_explicit failed: {:?}", e),
-/// }
-/// # }
-/// ```
-///
-/// The following parses `[2] IMPLICIT INTEGER`:
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::{BerObject, BerTag};
-/// # use der_parser::error::BerResult;
-/// # use nom::map_res;
-/// #
-/// # fn main() {
-/// fn parse_int_implicit(i:&[u8]) -> BerResult<u32> {
-///     map_res!(
-///         i,
-///         parse_der_tagged!(IMPLICIT 2, BerTag::Integer),
-///         |x: BerObject| x.as_u32()
-///     )
-/// }
-///
-/// let bytes = &[0x82, 0x03, 0x01, 0x00, 0x01];
-/// let res = parse_int_implicit(bytes);
-/// match res {
-///     Ok((rem,val)) => {
-///         assert!(rem.is_empty());
-///         assert_eq!(val, 0x10001);
-///     },
-///     _ => assert!(false)
-/// }
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_tagged(
-    ($i:expr, EXPLICIT $tag:expr, $f:ident) => ({
-        use $crate::ber::parse_ber_tagged_explicit;
-        parse_ber_tagged_explicit(
-            $tag,
-            $f
-        )($i)
-    });
-    ($i:expr, EXPLICIT $tag:expr, $submac:ident!( $($args:tt)*)) => ({
-        use $crate::ber::parse_ber_tagged_explicit;
-        parse_ber_tagged_explicit(
-            $tag,
-            |content| {
-                $submac!(content, $($args)*)
-            }
-        )($i)
-    });
-    ($i:expr, IMPLICIT $tag:expr, $type:expr) => ({
-        use $crate::ber::{ber_read_element_content_as, parse_ber_tagged_implicit};
-        parse_ber_tagged_implicit(
-            $tag,
-            |content, hdr, max_size| {
-                ber_read_element_content_as(content, $type, hdr.len, hdr.is_constructed(), max_size)
-            }
-        )($i)
-    });
-    ($i:expr, $tag:expr, $f:ident) => ( parse_der_tagged!($i, EXPLICIT $tag, $f) );
-);
-
-/// Parse an application DER element
-///
-/// Read an application DER element using the provided functions.
-/// This is generally used to build a struct from a DER sequence.
-///
-/// The returned object is a tuple containing a [`BerObjectHeader`](struct.BerObjectHeader.html)
-/// and the object returned by the subparser.
-///
-/// To ensure the subparser consumes all bytes from the constructed object, add the `eof!()`
-/// subparser as the last parsing item.
-///
-/// # Examples
-///
-/// The following parses `[APPLICATION 2] INTEGER`:
-///
-/// ```rust
-/// # #[macro_use] extern crate der_parser;
-/// # use der_parser::ber::*;
-/// # use der_parser::error::BerResult;
-/// # use nom::{eof, map_res};
-/// #
-/// # fn main() {
-/// #[derive(Debug, PartialEq)]
-/// struct SimpleStruct {
-///     a: u32,
-/// }
-///
-/// fn parse_app01(i:&[u8]) -> BerResult<SimpleStruct> {
-///     parse_der_application!(
-///         i,
-///         APPLICATION 2,
-///         a: map_res!(parse_ber_integer,|x: BerObject| x.as_u32()) >>
-///            eof!() >>
-///         ( SimpleStruct{ a:a } )
-///     )
-/// }
-///
-/// let bytes = &[0x62, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01];
-/// let res = parse_app01(bytes);
-/// match res {
-///     Ok((rem, app)) => {
-///         assert!(rem.is_empty());
-///         assert_eq!(app, SimpleStruct{ a:0x10001 });
-///     },
-///     _ => assert!(false)
-/// }
-/// # }
-/// ```
-#[macro_export]
-macro_rules! parse_der_application(
-    ($i:expr, APPLICATION $tag:expr, $($rest:tt)*) => ({
-        use $crate::ber::{parse_ber_container, BerClass, BerTag};
-        use $crate::error::BerError;
-        use nom::do_parse;
-        parse_ber_container(
-            |content, hdr| {
-                if hdr.class != BerClass::Application {
-                    return Err(nom::Err::Error(BerError::InvalidClass.into()));
-                }
-                if hdr.tag != BerTag($tag) {
-                    return Err(nom::Err::Error(BerError::BerTypeError.into()));
-                }
-                do_parse!(content, $($rest)* )
-            }
-        )($i)
-    });
-    ($i:expr, $tag:expr, $($rest:tt)*) => ( parse_der_application!($i, APPLICATION $tag, $($rest)*) );
-);
diff --git a/src/oid.rs b/src/oid.rs
index fdb17b3..618988d 100644
--- a/src/oid.rs
+++ b/src/oid.rs
@@ -47,17 +47,22 @@
 //! ```
 //! *Attention*, be aware that the latter version might not handle the case of a relative oid correctly. An
 //! extra check might be necessary.
-use std::borrow::Cow;
-use std::convert::From;
-use std::fmt;
-use std::iter::{ExactSizeIterator, FusedIterator, Iterator};
-use std::ops::Shl;
-use std::str::FromStr;
+use alloc::borrow::Cow;
+use alloc::fmt;
+use alloc::str::FromStr;
+use alloc::string::{String, ToString};
+use alloc::vec::Vec;
+use core::convert::From;
+use core::iter::{ExactSizeIterator, FusedIterator, Iterator};
+use core::ops::Shl;
 
 #[cfg(feature = "bigint")]
 use num_bigint::BigUint;
 use num_traits::Num;
 
+#[cfg(not(feature = "std"))]
+use alloc::format;
+
 #[derive(Debug)]
 pub enum ParseError {
     TooShort,
@@ -212,7 +217,7 @@
             oid: self,
             pos: 0,
             first: false,
-            n: std::marker::PhantomData,
+            n: core::marker::PhantomData,
         }
     }
 
@@ -248,7 +253,7 @@
             oid: self,
             pos: 0,
             first: false,
-            n: std::marker::PhantomData,
+            n: core::marker::PhantomData,
         })
     }
 }
@@ -260,7 +265,7 @@
     oid: &'a Oid<'a>,
     pos: usize,
     first: bool,
-    n: std::marker::PhantomData<&'a N>,
+    n: core::marker::PhantomData<&'a N>,
 }
 
 impl<'a, N: Repr> Iterator for SubIdentifierIterator<'a, N> {
@@ -358,6 +363,7 @@
 mod tests {
     use crate::oid::Oid;
     use std::borrow::Cow;
+    use std::borrow::ToOwned;
     use std::str::FromStr;
 
     #[test]
@@ -437,6 +443,7 @@
         {
             use num_bigint::BigUint;
             use num_traits::FromPrimitive;
+            use std::vec::Vec;
 
             let oid_raw = Oid::new(Cow::Borrowed(&[0]));
             let ids: Vec<BigUint> = oid_raw.iter_bigint().collect();
@@ -444,6 +451,7 @@
             assert_eq!(oid_raw.iter_bigint().len(), 1);
         }
         {
+            use std::vec::Vec;
             let oid_raw = Oid::new(Cow::Borrowed(&[0]));
             let ids: Vec<u64> = oid_raw.iter().unwrap().collect();
             assert_eq!(vec![0], ids);
diff --git a/tests/ber_parser.rs b/tests/ber_parser.rs
index 488a0c1..6f020e3 100644
--- a/tests/ber_parser.rs
+++ b/tests/ber_parser.rs
@@ -220,22 +220,19 @@
 }
 
 #[cfg(feature = "bigint")]
-#[test_case(&hex!("02 01 01"), Ok(Some(BigUint::from(1_u8))) ; "biguint-1")]
-#[test_case(&hex!("02 02 00 ff"), Ok(Some(BigUint::from(255_u8))) ; "biguint-255")]
-#[test_case(&hex!("02 01 ff"), Ok(None) ; "biguint-neg1")]
-#[test_case(&hex!("02 01 80"), Ok(None) ; "biguint-neg128")]
-#[test_case(&hex!("02 02 ff 7f"), Ok(None) ; "biguint-neg129")]
-#[test_case(&hex!("02 09 00 ff ff ff ff ff ff ff ff"), Ok(Some(BigUint::from(0xffff_ffff_ffff_ffff_u64))) ; "biguint-long2-ok")]
-#[test_case(&hex!("02 09 01 23 45 67 01 23 45 67 ab"), Ok(Some(BigUint::from_bytes_be(&hex!("01 23 45 67 01 23 45 67 ab")))) ; "biguint-longer1")]
+#[test_case(&hex!("02 01 01"), Ok(BigUint::from(1_u8)) ; "biguint-1")]
+#[test_case(&hex!("02 02 00 ff"), Ok(BigUint::from(255_u8)) ; "biguint-255")]
+#[test_case(&hex!("02 01 ff"), Err(BerError::IntegerNegative) ; "biguint-neg1")]
+#[test_case(&hex!("02 01 80"), Err(BerError::IntegerNegative) ; "biguint-neg128")]
+#[test_case(&hex!("02 02 ff 7f"), Err(BerError::IntegerNegative) ; "biguint-neg129")]
+#[test_case(&hex!("02 09 00 ff ff ff ff ff ff ff ff"), Ok(BigUint::from(0xffff_ffff_ffff_ffff_u64)) ; "biguint-long2-ok")]
+#[test_case(&hex!("02 09 01 23 45 67 01 23 45 67 ab"), Ok(BigUint::from_bytes_be(&hex!("01 23 45 67 01 23 45 67 ab"))) ; "biguint-longer1")]
 #[test_case(&hex!("03 03 01 00 01"), Err(BerError::InvalidTag) ; "invalid tag")]
-fn tc_ber_biguint(i: &[u8], out: Result<Option<BigUint>, BerError>) {
-    let res = parse_ber_integer(i);
+fn tc_ber_biguint(i: &[u8], out: Result<BigUint, BerError>) {
+    let res = parse_ber_integer(i).and_then(|(rem, ber)| Ok((rem, ber.as_biguint()?)));
     match out {
         Ok(expected) => {
-            let (rem, ber) = res.expect("parsing failed");
-            assert!(rem.is_empty());
-            let uint = ber.as_biguint();
-            pretty_assertions::assert_eq!(uint, expected);
+            pretty_assertions::assert_eq!(res, Ok((&b""[..], expected)));
         }
         Err(e) => {
             pretty_assertions::assert_eq!(res, Err(Err::Error(e)));
@@ -543,3 +540,11 @@
     assert_eq!(tag, BerTag::Integer);
     assert_eq!(content.as_u32(), Ok(0x10001));
 }
+
+#[test]
+fn parse_ber_private() {
+    let bytes = &hex!("c0 03 01 00 01");
+    let (rem, res) = parse_ber(bytes).expect("parsing failed");
+    assert!(rem.is_empty());
+    assert!(matches!(res.content, BerObjectContent::Private(_, _)));
+}
diff --git a/tests/constructed.rs b/tests/constructed.rs
index 49bc0c6..20a83a0 100644
--- a/tests/constructed.rs
+++ b/tests/constructed.rs
@@ -1,9 +1,12 @@
 use der_parser::ber::*;
+use der_parser::der::*;
 use der_parser::error::*;
 use der_parser::*;
 use hex_literal::hex;
-use nom::combinator::map;
+use nom::branch::alt;
+use nom::combinator::{complete, eof, map, map_res};
 use nom::error::ErrorKind;
+use nom::multi::many0;
 use nom::sequence::tuple;
 use nom::*;
 use oid::Oid;
@@ -17,44 +20,33 @@
 }
 
 fn parse_struct01(i: &[u8]) -> BerResult<MyStruct> {
-    parse_der_struct!(
-        i,
-        a: parse_ber_integer >> b: parse_ber_integer >> (MyStruct { a, b })
-    )
+    parse_der_sequence_defined_g(|i: &[u8], _| {
+        let (i, a) = parse_ber_integer(i)?;
+        let (i, b) = parse_ber_integer(i)?;
+        Ok((i, MyStruct { a, b }))
+    })(i)
 }
 
 fn parse_struct01_complete(i: &[u8]) -> BerResult<MyStruct> {
-    parse_der_struct!(
-        i,
-        a: parse_ber_integer >> b: parse_ber_integer >> eof!() >> (MyStruct { a, b })
-    )
-}
-
-// calling user function
-#[allow(dead_code)]
-fn parse_struct02(i: &[u8]) -> BerResult<()> {
-    parse_der_struct!(i, _a: parse_ber_integer >> _b: parse_struct01 >> (()))
-}
-
-// embedded DER structs
-#[allow(dead_code)]
-fn parse_struct03(i: &[u8]) -> BerResult<()> {
-    parse_der_struct!(
-        i,
-        _a: parse_ber_integer >> _b: parse_der_struct!(parse_ber_integer >> (())) >> (())
-    )
+    parse_der_sequence_defined_g(|i: &[u8], _| {
+        let (i, a) = parse_ber_integer(i)?;
+        let (i, b) = parse_ber_integer(i)?;
+        eof(i)?;
+        Ok((i, MyStruct { a, b }))
+    })(i)
 }
 
 // verifying tag
 fn parse_struct04(i: &[u8], tag: BerTag) -> BerResult<MyStruct> {
-    parse_der_struct!(
-        i,
-        TAG tag,
-        a: parse_ber_integer >>
-        b: parse_ber_integer >>
-           eof!() >>
-        ( MyStruct{ a, b } )
-    )
+    parse_der_container(|i: &[u8], hdr| {
+        if hdr.tag != tag {
+            return Err(Err::Error(BerError::InvalidTag));
+        }
+        let (i, a) = parse_ber_integer(i)?;
+        let (i, b) = parse_ber_integer(i)?;
+        eof(i)?;
+        Ok((i, MyStruct { a, b }))
+    })(i)
 }
 
 #[test_case(&hex!("30 00"), Ok(&[]) ; "empty seq")]
@@ -247,27 +239,33 @@
         ],
     };
     fn parse_directory_string(i: &[u8]) -> BerResult {
-        alt!(
-            i,
-            parse_ber_utf8string | parse_ber_printablestring | parse_ber_ia5string
-        )
+        alt((
+            parse_ber_utf8string,
+            parse_ber_printablestring,
+            parse_ber_ia5string,
+        ))(i)
     }
     fn parse_attr_type_and_value(i: &[u8]) -> BerResult<Attr> {
         fn clone_oid(x: BerObject) -> Result<Oid, BerError> {
             x.as_oid().map(|o| o.clone())
         }
-        parse_der_struct!(
-            i,
-            o: map_res!(parse_ber_oid, clone_oid)
-                >> s: parse_directory_string
-                >> (Attr { oid: o, val: s })
-        )
+        parse_der_sequence_defined_g(|i: &[u8], _| {
+            let (i, o) = map_res(parse_ber_oid, clone_oid)(i)?;
+            let (i, s) = parse_directory_string(i)?;
+            Ok((i, Attr { oid: o, val: s }))
+        })(i)
     }
     fn parse_rdn(i: &[u8]) -> BerResult<Rdn> {
-        parse_der_struct!(i, a: parse_attr_type_and_value >> (Rdn { a }))
+        parse_der_set_defined_g(|i: &[u8], _| {
+            let (i, a) = parse_attr_type_and_value(i)?;
+            Ok((i, Rdn { a }))
+        })(i)
     }
     fn parse_name(i: &[u8]) -> BerResult<Name> {
-        parse_der_struct!(i, l: many0!(complete!(parse_rdn)) >> (Name { l }))
+        parse_der_sequence_defined_g(|i: &[u8], _| {
+            let (i, l) = many0(complete(parse_rdn))(i)?;
+            Ok((i, Name { l }))
+        })(i)
     }
     let parsed = parse_name(&bytes).unwrap();
     assert_eq!(parsed, (empty, expected));
@@ -337,36 +335,24 @@
 #[test]
 fn tagged_explicit() {
     fn parse_int_explicit(i: &[u8]) -> BerResult<u32> {
-        map_res!(
-            i,
-            parse_der_tagged!(EXPLICIT 2, parse_ber_integer),
-            |x: BerObject| x.as_tagged()?.2.as_u32()
-        )
-    }
-    fn parse_int_noexplicit(i: &[u8]) -> BerResult<u32> {
-        map_res!(
-            i,
-            parse_der_tagged!(2, parse_ber_integer),
-            |x: BerObject| x.as_tagged()?.2.as_u32()
-        )
+        map_res(
+            parse_der_tagged_explicit(2, parse_der_integer),
+            |x: BerObject| x.as_tagged()?.2.as_u32(),
+        )(i)
     }
     let bytes = &[0xa2, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01];
     // EXPLICIT tagged value parsing
     let (rem, val) = parse_int_explicit(bytes).expect("Could not parse explicit int");
     assert!(rem.is_empty());
     assert_eq!(val, 0x10001);
-    // omitting EXPLICIT keyword
-    let a = parse_int_explicit(bytes);
-    let b = parse_int_noexplicit(bytes);
-    assert_eq!(a, b);
     // wrong tag
     assert_eq!(
-        parse_der_tagged!(bytes as &[u8], 3, parse_ber_integer),
+        parse_der_tagged_explicit(3, parse_der_integer)(bytes as &[u8]),
         Err(Err::Error(BerError::InvalidTag))
     );
     // wrong type
     assert_eq!(
-        parse_der_tagged!(bytes as &[u8], 2, parse_ber_bool),
+        parse_der_tagged_explicit(2, parse_der_bool)(bytes as &[u8]),
         Err(Err::Error(BerError::InvalidTag))
     );
 }
@@ -395,11 +381,10 @@
 #[test]
 fn tagged_implicit() {
     fn parse_int_implicit(i: &[u8]) -> BerResult<u32> {
-        map_res!(
-            i,
-            parse_der_tagged!(IMPLICIT 2, BerTag::Integer),
-            |x: BerObject| x.as_u32()
-        )
+        map_res(
+            parse_der_tagged_implicit(2, parse_der_content(DerTag::Integer)),
+            |x: BerObject| x.as_u32(),
+        )(i)
     }
     let bytes = &[0x82, 0x03, 0x01, 0x00, 0x01];
     // IMPLICIT tagged value parsing
@@ -408,7 +393,7 @@
     assert_eq!(val, 0x10001);
     // wrong tag
     assert_eq!(
-        parse_der_tagged!(bytes as &[u8], IMPLICIT 3, BerTag::Integer),
+        parse_der_tagged_implicit(3, parse_der_content(DerTag::Integer))(bytes as &[u8]),
         Err(Err::Error(BerError::InvalidTag))
     );
 }
@@ -420,12 +405,16 @@
         a: u32,
     }
     fn parse_app01(i: &[u8]) -> BerResult<SimpleStruct> {
-        parse_der_application!(
-            i,
-            APPLICATION 2,
-            a: map_res!(parse_ber_integer,|x: BerObject| x.as_u32()) >>
-            ( SimpleStruct{ a } )
-        )
+        parse_der_container(|i, hdr| {
+            if hdr.class != BerClass::Application {
+                return Err(Err::Error(BerError::InvalidClass));
+            }
+            if hdr.tag != BerTag(2) {
+                return Err(Err::Error(BerError::InvalidTag));
+            }
+            let (i, a) = map_res(parse_ber_integer, |x: BerObject| x.as_u32())(i)?;
+            Ok((i, SimpleStruct { a }))
+        })(i)
     }
     let bytes = &[0x62, 0x05, 0x02, 0x03, 0x01, 0x00, 0x01];
     let (rem, app) = parse_app01(bytes).expect("could not parse application");
diff --git a/tests/der_parser.rs b/tests/der_parser.rs
index 9a31e5c..33665f3 100644
--- a/tests/der_parser.rs
+++ b/tests/der_parser.rs
@@ -6,8 +6,11 @@
 use der_parser::oid::*;
 use der_parser::*;
 use hex_literal::hex;
+use nom::branch::alt;
+use nom::combinator::map;
 use nom::error::ErrorKind;
-use nom::{alt, call, Err};
+use nom::sequence::tuple;
+use nom::Err;
 use pretty_assertions::assert_eq;
 use test_case::test_case;
 
@@ -175,7 +178,13 @@
         DerObject::from_int_slice(b"\x01\x00\x00"),
     ]);
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_sequence_defined!(i, parse_der_integer >> parse_der_integer)
+        parse_der_sequence_defined(
+            // the nom `tuple` combinator returns a tuple, so we have to map it
+            // to a list
+            map(tuple((parse_der_integer, parse_der_integer)), |(a, b)| {
+                vec![a, b]
+            }),
+        )(i)
     }
     assert_eq!(parser(&bytes), Ok((empty, expected)));
 }
@@ -191,7 +200,13 @@
         DerObject::from_int_slice(b"\x01\x00\x00"),
     ]);
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_set_defined!(i, parse_der_integer >> parse_der_integer)
+        parse_der_set_defined(
+            // the nom `tuple` combinator returns a tuple, so we have to map it
+            // to a list
+            map(tuple((parse_der_integer, parse_der_integer)), |(a, b)| {
+                vec![a, b]
+            }),
+        )(i)
     }
     assert_eq!(parser(&bytes), Ok((empty, expected)));
 }
@@ -207,7 +222,7 @@
         DerObject::from_int_slice(b"\x01\x00\x00"),
     ]);
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_sequence_of!(i, parse_der_integer)
+        parse_der_sequence_of(parse_der_integer)(i)
     }
     assert_eq!(parser(&bytes), Ok((empty, expected.clone())));
     //
@@ -222,7 +237,7 @@
 fn test_der_seq_of_incomplete() {
     let bytes = [0x30, 0x07, 0x02, 0x03, 0x01, 0x00, 0x01, 0x00, 0x00];
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_sequence_of!(i, parse_der_integer)
+        parse_der_sequence_of(parse_der_integer)(i)
     }
     assert_eq!(parser(&bytes), Err(Err::Failure(BerError::InvalidTag)));
     //
@@ -250,7 +265,7 @@
         DerObject::from_int_slice(b"\x01\x00\x00"),
     ]);
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_set_of!(i, parse_der_integer)
+        parse_der_set_of(parse_der_integer)(i)
     }
     assert_eq!(parser(&bytes), Ok((empty, expected)));
 }
@@ -293,13 +308,13 @@
     let expected = DerObject {
         header: BerObjectHeader::new(BerClass::ContextSpecific, 1, BerTag(0), 3)
             .with_raw_tag(Some(&[0xa0])),
-        content: BerObjectContent::Unknown(BerTag(0), &bytes[2..]),
+        content: BerObjectContent::Unknown(BerClass::ContextSpecific, BerTag(0), &bytes[2..]),
     };
     assert_eq!(parse_der(&bytes), Ok((empty, expected)));
 }
 
 #[test]
-fn test_der_explicit() {
+fn test_der_explicit_optional() {
     let empty = &b""[..];
     let bytes = [0xa0, 0x03, 0x02, 0x01, 0x02];
     let header = BerObjectHeader::new(BerClass::ContextSpecific, 1, BerTag(0), 3)
@@ -316,12 +331,12 @@
         )))),
     };
     assert_eq!(
-        parse_der_explicit(&bytes, BerTag(0), parse_der_integer),
+        parse_der_explicit_optional(&bytes, BerTag(0), parse_der_integer),
         Ok((empty, expected))
     );
     let expected2 = DerObject::from_obj(BerObjectContent::Optional(None));
     assert_eq!(
-        parse_der_explicit(&bytes, BerTag(1), parse_der_integer),
+        parse_der_explicit_optional(&bytes, BerTag(1), parse_der_integer),
         Ok((&bytes[..], expected2))
     );
 }
@@ -396,10 +411,16 @@
         DerObject::from_int_slice(b"\x01\x00\x01"),
     ]);
     fn parse_optional_enum(i: &[u8]) -> DerResult {
-        parse_der_optional!(i, parse_der_enum)
+        parse_ber_optional(parse_der_enum)(i)
     }
     fn parser(i: &[u8]) -> DerResult {
-        parse_der_sequence_defined!(i, parse_optional_enum >> parse_der_integer)
+        parse_der_sequence_defined(
+            // the nom `tuple` combinator returns a tuple, so we have to map it
+            // to a list
+            map(tuple((parse_optional_enum, parse_der_integer)), |(a, b)| {
+                vec![a, b]
+            }),
+        )(i)
     }
     assert_eq!(parser(&bytes1), Ok((empty, expected1)));
     assert_eq!(parser(&bytes2), Ok((empty, expected2)));
@@ -458,66 +479,33 @@
     ]);
     #[inline]
     fn parse_directory_string(i: &[u8]) -> DerResult {
-        alt!(
-            i,
-            parse_der_utf8string | parse_der_printablestring | parse_der_ia5string
-        )
+        alt((
+            parse_der_utf8string,
+            parse_der_printablestring,
+            parse_der_ia5string,
+        ))(i)
     }
     #[inline]
     fn parse_attr_type_and_value(i: &[u8]) -> DerResult {
-        parse_der_sequence_defined!(i, parse_der_oid >> parse_directory_string)
+        parse_der_sequence_defined(
+            // the nom `tuple` combinator returns a tuple, so we have to map it
+            // to a list
+            map(tuple((parse_der_oid, parse_directory_string)), |(a, b)| {
+                vec![a, b]
+            }),
+        )(i)
     }
     #[inline]
     fn parse_rdn(i: &[u8]) -> DerResult {
-        parse_der_set_defined!(i, parse_attr_type_and_value)
+        parse_der_set_of(parse_attr_type_and_value)(i)
     }
     #[inline]
     fn parse_name(i: &[u8]) -> DerResult {
-        parse_der_sequence_defined!(i, parse_rdn >> parse_rdn >> parse_rdn)
+        parse_der_sequence_of(parse_rdn)(i)
     }
     assert_eq!(parse_name(&bytes), Ok((empty, expected)));
 }
 
-#[test]
-fn test_der_defined_seq_macros() {
-    fn localparse_seq(i: &[u8]) -> DerResult {
-        parse_der_sequence_defined_m! {
-            i,
-            parse_der_integer >>
-            call!(parse_der_integer)
-        }
-    }
-    let empty = &b""[..];
-    let bytes = [
-        0x30, 0x0a, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00,
-    ];
-    let expected = DerObject::from_seq(vec![
-        DerObject::from_int_slice(b"\x01\x00\x01"),
-        DerObject::from_int_slice(b"\x01\x00\x00"),
-    ]);
-    assert_eq!(localparse_seq(&bytes), Ok((empty, expected)));
-}
-
-#[test]
-fn test_der_defined_set_macros() {
-    fn localparse_set(i: &[u8]) -> DerResult {
-        parse_der_set_defined_m! {
-            i,
-            parse_der_integer >>
-            call!(parse_der_integer)
-        }
-    }
-    let empty = &b""[..];
-    let bytes = [
-        0x31, 0x0a, 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00,
-    ];
-    let expected = DerObject::from_set(vec![
-        DerObject::from_int_slice(b"\x01\x00\x01"),
-        DerObject::from_int_slice(b"\x01\x00\x00"),
-    ]);
-    assert_eq!(localparse_set(&bytes), Ok((empty, expected)));
-}
-
 #[test_case(&hex!("02 01 01"), Ok(1) ; "u32-1")]
 #[test_case(&hex!("02 01 ff"), Err(BerError::IntegerNegative) ; "negative integer")]
 #[test_case(&hex!("02 02 00 ff"), Ok(255) ; "u32-255")]
diff --git a/tests/macros.rs b/tests/macros.rs
deleted file mode 100644
index 361fee9..0000000
--- a/tests/macros.rs
+++ /dev/null
@@ -1,129 +0,0 @@
-#![allow(deprecated)]
-
-use der_parser::ber::{parse_ber_integer, BerObject};
-use der_parser::der::{parse_der_enum, parse_der_integer};
-use der_parser::error::{BerResult, DerResult};
-use der_parser::*;
-use nom::map_res;
-
-// Do not import nom, to check types and re-exports
-
-// all following functions are declared to check if macros from
-// der-parser can be used without importing nom or rusticata_macros
-
-#[derive(Debug, PartialEq)]
-struct MyStruct<'a> {
-    a: BerObject<'a>,
-    b: BerObject<'a>,
-}
-
-#[allow(dead_code)]
-fn parse_seq_m(i: &[u8]) -> DerResult {
-    parse_der_sequence_defined_m! {
-        i,
-        parse_der_integer >>
-        parse_der_integer
-    }
-}
-
-#[allow(dead_code)]
-fn parse_set_m(i: &[u8]) -> DerResult {
-    parse_der_set_defined_m! {
-        i,
-        parse_der_integer >>
-        parse_der_integer
-    }
-}
-
-#[allow(dead_code)]
-fn parse_seq(i: &[u8]) -> DerResult {
-    parse_der_sequence_defined! {
-        i,
-        parse_der_integer >>
-        parse_der_integer
-    }
-}
-
-#[allow(dead_code)]
-fn parse_set(i: &[u8]) -> DerResult {
-    parse_der_set_defined! {
-        i,
-        parse_der_integer >>
-        parse_der_integer
-    }
-}
-
-#[allow(dead_code)]
-fn parse_seq_of_int(i: &[u8]) -> DerResult {
-    parse_der_sequence_of!(i, parse_der_integer)
-}
-
-#[allow(dead_code)]
-fn parse_set_of_int(i: &[u8]) -> DerResult {
-    parse_der_set_of!(i, parse_der_integer)
-}
-
-#[allow(dead_code)]
-fn parse_optional_enum(i: &[u8]) -> BerResult {
-    parse_der_optional!(i, parse_der_enum)
-}
-
-#[allow(dead_code)]
-fn parse_struct01(i: &[u8]) -> BerResult<MyStruct> {
-    parse_der_struct!(
-        i,
-        a: parse_ber_integer >> b: parse_ber_integer >> (MyStruct { a, b })
-    )
-}
-
-#[allow(dead_code)]
-fn parse_tagged_int(i: &[u8]) -> BerResult {
-    parse_der_tagged!(i, EXPLICIT 2, parse_ber_integer)
-}
-
-#[derive(Debug, PartialEq)]
-struct SimpleStruct {
-    a: u32,
-}
-
-#[allow(dead_code)]
-fn parse_app_int(i: &[u8]) -> BerResult<SimpleStruct> {
-    parse_der_application!(
-        i,
-        APPLICATION 2,
-        a: map_res!(parse_ber_integer,|x: BerObject| x.as_u32()) >>
-        ( SimpleStruct{ a } )
-    )
-}
-
-#[rustfmt::skip::macros(oid)]
-#[test]
-fn oid_macro() {
-    let abs = oid!(1.2.44.233.0.124_982_9_348248912829838230928);
-    assert!(!abs.relative);
-    if cfg!(feature = "bigint") {
-        assert_eq!(abs.to_string(), "1.2.44.233.0.1249829348248912829838230928");
-    } else {
-        assert_eq!(
-            abs.to_string(),
-            "2a 2c 81 69 00 c0 ce d6 d1 ba fd 8e c7 e5 dc ee 8b 10"
-        );
-    }
-
-    let rel = oid!(rel 1.2.44.233);
-    assert!(rel.relative);
-    assert_eq!(rel.to_id_string(), "1.2.44.233");
-    assert_eq!(format!("{:?}", &rel), "OID(rel. 1.2.44.233)")
-}
-
-#[test]
-fn oid_macro_edge_cases() {
-    let undef = oid!(0);
-    assert_eq!(undef.bytes(), [0].as_ref());
-
-    let two = oid!(1.2);
-    assert_eq!(two.bytes(), [40 + 2].as_ref());
-
-    let spacing = oid!(5.2);
-    assert_eq!(spacing.bytes(), [5 * 40 + 2].as_ref());
-}
diff --git a/tests/oid.rs b/tests/oid.rs
index 7f04b53..5cffad4 100644
--- a/tests/oid.rs
+++ b/tests/oid.rs
@@ -1,5 +1,6 @@
 //! Test the API provided to compare OIDs
 
+extern crate alloc;
 use der_parser::oid;
 use der_parser::oid::Oid;
 
diff --git a/tests/primitive.rs b/tests/primitive.rs
index ae40317..4c07b0a 100644
--- a/tests/primitive.rs
+++ b/tests/primitive.rs
@@ -1,3 +1,4 @@
+extern crate alloc;
 use der_parser::ber::*;
 use der_parser::der::*;
 use der_parser::error::*;
@@ -88,13 +89,21 @@
     assert!(res.0.is_empty());
     assert_eq!(
         res.1,
-        BerObject::from_obj(BerObjectContent::Unknown(BerTag(0x1d), &bytes[2..]))
+        BerObject::from_obj(BerObjectContent::Unknown(
+            BerClass::Universal,
+            BerTag(0x1d),
+            &bytes[2..]
+        ))
     );
     let res = parse_der(&bytes).expect("parsing failed");
     assert!(res.0.is_empty());
     assert_eq!(
         res.1,
-        BerObject::from_obj(BerObjectContent::Unknown(BerTag(0x1d), &bytes[2..]))
+        BerObject::from_obj(BerObjectContent::Unknown(
+            BerClass::Universal,
+            BerTag(0x1d),
+            &bytes[2..]
+        ))
     );
 }
 
@@ -108,7 +117,7 @@
         BerObject {
             header: BerObjectHeader::new(BerClass::ContextSpecific, 0, BerTag(0), 1)
                 .with_raw_tag(Some(&[0x80])),
-            content: BerObjectContent::Unknown(BerTag(0x0), &bytes[2..]),
+            content: BerObjectContent::Unknown(BerClass::ContextSpecific, BerTag(0x0), &bytes[2..]),
         }
     );
 }
@@ -123,7 +132,11 @@
         BerObject {
             header: BerObjectHeader::new(BerClass::ContextSpecific, 0, BerTag(0x22), 1)
                 .with_raw_tag(Some(&[0x9f, 0x22])),
-            content: BerObjectContent::Unknown(BerTag(0x22), &bytes[3..]),
+            content: BerObjectContent::Unknown(
+                BerClass::ContextSpecific,
+                BerTag(0x22),
+                &bytes[3..]
+            ),
         }
     );
 }
@@ -138,7 +151,11 @@
         BerObject {
             header: BerObjectHeader::new(BerClass::ContextSpecific, 0, BerTag(0x1122), 1)
                 .with_raw_tag(Some(&[0x9f, 0xa2, 0x22])),
-            content: BerObjectContent::Unknown(BerTag(0x1122), &bytes[4..]),
+            content: BerObjectContent::Unknown(
+                BerClass::ContextSpecific,
+                BerTag(0x1122),
+                &bytes[4..]
+            ),
         }
     );
 }