summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-11 19:13:58 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-11 19:13:58 +0700
commit4c0a0bb815aa796c9765938434f756330663a76f (patch)
tree868a7018f8deb2df1c7773378cce6c0fc387d3e0
parentAdd DLTaggedObject and use from parser (diff)
downloadBouncyCastle.NET-ed25519-4c0a0bb815aa796c9765938434f756330663a76f.tar.xz
Improved parsing of tagged objects
-rw-r--r--crypto/src/asn1/ASN1StreamParser.cs146
-rw-r--r--crypto/src/asn1/ASN1TaggedObjectParser.cs2
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs180
-rw-r--r--crypto/src/asn1/BERTaggedObjectParser.cs60
4 files changed, 196 insertions, 192 deletions
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs
index 55b5bc656..5c6854e1d 100644
--- a/crypto/src/asn1/ASN1StreamParser.cs
+++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -35,91 +35,102 @@ namespace Org.BouncyCastle.Asn1
             this.tmpBuffers = tmpBuffers;
         }
 
-        internal IAsn1Convertible ReadIndef(int tagValue)
+        internal IAsn1Convertible ReadIndef(int tagNo)
 		{
 			// Note: INDEF => CONSTRUCTED
 
 			// TODO There are other tags that may be constructed (e.g. BIT_STRING)
-			switch (tagValue)
+			switch (tagNo)
 			{
-				case Asn1Tags.External:
-					return new DerExternalParser(this);
-				case Asn1Tags.OctetString:
-					return new BerOctetStringParser(this);
-				case Asn1Tags.Sequence:
-					return new BerSequenceParser(this);
-				case Asn1Tags.Set:
-					return new BerSetParser(this);
-				default:
-					throw new Asn1Exception("unknown BER object encountered: 0x" + tagValue.ToString("X"));
+			case Asn1Tags.External:
+				return new DerExternalParser(this);
+			case Asn1Tags.OctetString:
+				return new BerOctetStringParser(this);
+			case Asn1Tags.Sequence:
+				return new BerSequenceParser(this);
+			case Asn1Tags.Set:
+				return new BerSetParser(this);
+			default:
+				throw new Asn1Exception("unknown BER object encountered: 0x" + tagNo.ToString("X"));
 			}
 		}
 
-		internal IAsn1Convertible ReadImplicit(bool constructed, int tag)
+		internal IAsn1Convertible ReadImplicit(bool constructed, int tagNo)
 		{
 			if (_in is IndefiniteLengthInputStream)
 			{
 				if (!constructed)
 					throw new IOException("indefinite-length primitive encoding encountered");
 
-				return ReadIndef(tag);
+				return ReadIndef(tagNo);
 			}
 
 			if (constructed)
 			{
-				switch (tag)
+				switch (tagNo)
 				{
-					case Asn1Tags.Set:
-						return new DerSetParser(this);
-					case Asn1Tags.Sequence:
-						return new DerSequenceParser(this);
-					case Asn1Tags.OctetString:
-						return new BerOctetStringParser(this);
+				case Asn1Tags.Set:
+					return new DerSetParser(this);
+				case Asn1Tags.Sequence:
+					return new DerSequenceParser(this);
+				case Asn1Tags.OctetString:
+					return new BerOctetStringParser(this);
 				}
 			}
 			else
 			{
-				switch (tag)
+				switch (tagNo)
 				{
-					case Asn1Tags.Set:
-						throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
-					case Asn1Tags.Sequence:
-						throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
-					case Asn1Tags.OctetString:
-						return new DerOctetStringParser((DefiniteLengthInputStream)_in);
+				case Asn1Tags.Set:
+					throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+				case Asn1Tags.Sequence:
+					throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+				case Asn1Tags.OctetString:
+					return new DerOctetStringParser((DefiniteLengthInputStream)_in);
 				}
 			}
 
 			throw new Asn1Exception("implicit tagging not implemented");
 		}
 
-		internal Asn1Object ReadTaggedObject(bool constructed, int tag)
+		internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed)
 		{
-			if (!constructed)
+            if (!constructed)
 			{
-				// Note: !CONSTRUCTED => IMPLICIT
-				DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
-				return new DLTaggedObject(false, tag, new DerOctetString(defIn.ToArray()));
+                // Note: !CONSTRUCTED => IMPLICIT
+                DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in;
+                byte[] contentsOctets = defIn.ToArray();
+
+                if (Asn1Tags.Application == tagClass)
+                    return new DerApplicationSpecific(false, tagNo, contentsOctets);
+
+                return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets));
 			}
 
-			Asn1EncodableVector v = ReadVector();
+			Asn1EncodableVector contentsElements = ReadVector();
 
 			if (_in is IndefiniteLengthInputStream)
 			{
-				return v.Count == 1
-					?   new BerTaggedObject(true, tag, v[0])
-					:   new BerTaggedObject(false, tag, BerSequence.FromVector(v));
+                if (Asn1Tags.Application == tagClass)
+                    return new BerApplicationSpecific(tagNo, contentsElements);
+
+                return contentsElements.Count == 1
+					?   new BerTaggedObject(true, tagNo, contentsElements[0])
+					:   new BerTaggedObject(false, tagNo, BerSequence.FromVector(contentsElements));
 			}
 
-			return v.Count == 1
-				?   new DLTaggedObject(true, tag, v[0])
-				:   new DLTaggedObject(false, tag, DLSequence.FromVector(v));
+            if (Asn1Tags.Application == tagClass)
+                return new DerApplicationSpecific(tagNo, contentsElements);
+
+            return contentsElements.Count == 1
+				?   new DLTaggedObject(true, tagNo, contentsElements[0])
+				:   new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements));
 		}
 
 		public virtual IAsn1Convertible ReadObject()
 		{
-			int tag = _in.ReadByte();
-			if (tag == -1)
+			int tagHdr = _in.ReadByte();
+			if (tagHdr == -1)
 				return null;
 
 			// turn off looking for "00" while we resolve the tag
@@ -128,15 +139,16 @@ namespace Org.BouncyCastle.Asn1
 			//
 			// calculate tag number
 			//
-			int tagNo = Asn1InputStream.ReadTagNumber(_in, tag);
+			int tagNo = Asn1InputStream.ReadTagNumber(_in, tagHdr);
 
-			bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
+			bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
 
 			//
 			// calculate length
 			//
 			int length = Asn1InputStream.ReadLength(_in, _limit,
-                tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set || tagNo == Asn1Tags.External);
+                tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set ||
+                tagNo == Asn1Tags.External);
 
 			if (length < 0) // indefinite-length method
 			{
@@ -146,13 +158,13 @@ namespace Org.BouncyCastle.Asn1
                 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
                 Asn1StreamParser sp = new Asn1StreamParser(indIn, _limit, tmpBuffers);
 
-                int tagClass = tag & Asn1Tags.Private;
+                int tagClass = tagHdr & Asn1Tags.Private;
                 if (0 != tagClass)
                 {
-                    if ((tag & Asn1Tags.Application) != 0)
+                    if (Asn1Tags.Application == tagClass)
                         return new BerApplicationSpecificParser(tagNo, sp);
 
-                    return new BerTaggedObjectParser(true, tagNo, sp);
+                    return new BerTaggedObjectParser(tagClass, tagNo, true, sp);
                 }
 
                 return sp.ReadIndef(tagNo);
@@ -161,14 +173,15 @@ namespace Org.BouncyCastle.Asn1
 			{
 				DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
-                int tagClass = tag & Asn1Tags.Private;
+                int tagClass = tagHdr & Asn1Tags.Private;
                 if (0 != tagClass)
                 {
-                    if ((tag & Asn1Tags.Application) != 0)
-                        return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray());
+                    Asn1StreamParser sub = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers);
+
+                    if (Asn1Tags.Application == tagClass)
+                        return (DerApplicationSpecific)sub.ReadTaggedObject(tagClass, tagNo, isConstructed);
 
-                    return new BerTaggedObjectParser(isConstructed, tagNo,
-                        new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers));
+                    return new BerTaggedObjectParser(tagClass, tagNo, isConstructed, sub);
                 }
 
                 if (!isConstructed)
@@ -176,8 +189,8 @@ namespace Org.BouncyCastle.Asn1
                     // Some primitive encodings can be handled by parsers too...
                     switch (tagNo)
                     {
-                        case Asn1Tags.OctetString:
-                            return new DerOctetStringParser(defIn);
+                    case Asn1Tags.OctetString:
+                        return new DerOctetStringParser(defIn);
                     }
 
                     try
@@ -195,19 +208,16 @@ namespace Org.BouncyCastle.Asn1
                 // TODO There are other tags that may be constructed (e.g. BitString)
                 switch (tagNo)
 				{
-					case Asn1Tags.OctetString:
-						//
-						// yes, people actually do this...
-						//
-						return new BerOctetStringParser(sp);
-					case Asn1Tags.Sequence:
-						return new DerSequenceParser(sp);
-					case Asn1Tags.Set:
-						return new DerSetParser(sp);
-					case Asn1Tags.External:
-						return new DerExternalParser(sp);
-					default:
-                        throw new IOException("unknown tag " + tagNo + " encountered");
+				case Asn1Tags.OctetString:
+					return new BerOctetStringParser(sp);
+				case Asn1Tags.Sequence:
+					return new DerSequenceParser(sp);
+				case Asn1Tags.Set:
+					return new DerSetParser(sp);
+				case Asn1Tags.External:
+					return new DerExternalParser(sp);
+				default:
+                    throw new IOException("unknown tag " + tagNo + " encountered");
                 }
 			}
 		}
diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs
index 32327a269..dd031ad07 100644
--- a/crypto/src/asn1/ASN1TaggedObjectParser.cs
+++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs
@@ -3,6 +3,8 @@ namespace Org.BouncyCastle.Asn1
 	public interface Asn1TaggedObjectParser
 		: IAsn1Convertible
 	{
+        int TagClass { get; }
+
 		int TagNo { get; }
 
 		IAsn1Convertible GetObjectParser(int tag, bool isExplicit);
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 27c583338..6e097e86d 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -1,5 +1,4 @@
 using System;
-using System.Diagnostics;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -74,23 +73,15 @@ namespace Org.BouncyCastle.Asn1
         /**
         * build an object given its tag and the number of bytes to construct it from.
         */
-        private Asn1Object BuildObject(
-            int	tag,
-            int	tagNo,
-            int	length)
+        private Asn1Object BuildObject(int tagHdr, int tagNo, int length)
         {
-            bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
+            bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
 
             DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit);
 
-            int tagClass = tag & Asn1Tags.Private;
+            int tagClass = tagHdr & Asn1Tags.Private;
             if (0 != tagClass)
-            {
-                if ((tag & Asn1Tags.Application) != 0)
-                    return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray());
-
-                return new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers).ReadTaggedObject(isConstructed, tagNo);
-            }
+                return ReadTaggedObject(tagClass, tagNo, isConstructed, defIn);
 
             if (!isConstructed)
                 return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
@@ -98,16 +89,9 @@ namespace Org.BouncyCastle.Asn1
             switch (tagNo)
             {
             case Asn1Tags.BitString:
-            {
                 return BuildConstructedBitString(ReadVector(defIn));
-            }
             case Asn1Tags.OctetString:
-            {
-                //
-                // yes, people actually do this...
-                //
                 return BuildConstructedOctetString(ReadVector(defIn));
-            }
             case Asn1Tags.Sequence:
                 return CreateDLSequence(defIn);
             case Asn1Tags.Set:
@@ -119,6 +103,29 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+        internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn)
+        {
+            if (!constructed)
+            {
+                // Note: !CONSTRUCTED => IMPLICIT
+                byte[] contentsOctets = defIn.ToArray();
+
+                if (Asn1Tags.Application == tagClass)
+                    return new DerApplicationSpecific(false, tagNo, contentsOctets);
+
+                return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets));
+            }
+
+            Asn1EncodableVector contentsElements = ReadVector(defIn);
+
+            if (Asn1Tags.Application == tagClass)
+                return new DerApplicationSpecific(tagNo, contentsElements);
+
+            return contentsElements.Count == 1
+                ? new DLTaggedObject(true, tagNo, contentsElements[0])
+                : new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements));
+        }
+
         internal virtual Asn1EncodableVector ReadVector()
         {
             Asn1Object o = ReadObject();
@@ -155,16 +162,16 @@ namespace Org.BouncyCastle.Asn1
 
         public Asn1Object ReadObject()
         {
-            int tag = ReadByte();
-            if (tag <= 0)
+            int tagHdr = ReadByte();
+            if (tagHdr <= 0)
             {
-                if (tag == 0)
+                if (tagHdr == 0)
                     throw new IOException("unexpected end-of-contents marker");
 
                 return null;
             }
 
-            int tagNo = ReadTagNumber(this, tag);
+            int tagNo = ReadTagNumber(this, tagHdr);
             int length = ReadLength(this, limit, false);
 
             if (length >= 0)
@@ -172,7 +179,7 @@ namespace Org.BouncyCastle.Asn1
                 // definite-length
                 try
                 {
-                    return BuildObject(tag, tagNo, length);
+                    return BuildObject(tagHdr, tagNo, length);
                 }
                 catch (ArgumentException e)
                 {
@@ -182,20 +189,15 @@ namespace Org.BouncyCastle.Asn1
 
             // indefinite-length
 
-            if (0 == (tag & Asn1Tags.Constructed))
+            if (0 == (tagHdr & Asn1Tags.Constructed))
                 throw new IOException("indefinite-length primitive encoding encountered");
 
             IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this, limit);
             Asn1StreamParser sp = new Asn1StreamParser(indIn, limit, tmpBuffers);
 
-            int tagClass = tag & Asn1Tags.Private;
+            int tagClass = tagHdr & Asn1Tags.Private;
             if (0 != tagClass)
-            {
-                if ((tag & Asn1Tags.Application) != 0)
-                    return new BerApplicationSpecificParser(tagNo, sp).ToAsn1Object();
-
-                return new BerTaggedObjectParser(true, tagNo, sp).ToAsn1Object();
-            }
+                return sp.ReadTaggedObject(tagClass, tagNo, true);
 
             // TODO There are other tags that may be constructed (e.g. BitString)
             switch (tagNo)
@@ -254,9 +256,9 @@ namespace Org.BouncyCastle.Asn1
             get { return limit; }
         }
 
-        internal static int ReadTagNumber(Stream s, int tag)
+        internal static int ReadTagNumber(Stream s, int tagHdr)
         {
-            int tagNo = tag & 0x1f;
+            int tagNo = tagHdr & 0x1f;
 
             //
             // with tagged object tag number is bottom 5 bits, or stored at the start of the content
@@ -403,71 +405,69 @@ namespace Org.BouncyCastle.Asn1
             return str;
         }
 
-        internal static Asn1Object CreatePrimitiveDerObject(
-            int                         tagNo,
-            DefiniteLengthInputStream   defIn,
-            byte[][]                    tmpBuffers)
+        internal static Asn1Object CreatePrimitiveDerObject(int tagNo, DefiniteLengthInputStream defIn,
+            byte[][] tmpBuffers)
         {
             switch (tagNo)
             {
-                case Asn1Tags.BmpString:
-                    return new DerBmpString(GetBmpCharBuffer(defIn));
-                case Asn1Tags.Boolean:
-                    return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
-                case Asn1Tags.Enumerated:
-                    return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
-                case Asn1Tags.ObjectIdentifier:
-                    // TODO Ideally only clone if we used a buffer
-                    return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true);
+            case Asn1Tags.BmpString:
+                return new DerBmpString(GetBmpCharBuffer(defIn));
+            case Asn1Tags.Boolean:
+                return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
+            case Asn1Tags.Enumerated:
+                return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
+            case Asn1Tags.ObjectIdentifier:
+                // TODO Ideally only clone if we used a buffer
+                return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true);
             }
 
             byte[] bytes = defIn.ToArray();
 
             switch (tagNo)
             {
-                case Asn1Tags.BitString:
-                    return DerBitString.CreatePrimitive(bytes);
-                case Asn1Tags.GeneralizedTime:
-                    return new DerGeneralizedTime(bytes);
-                case Asn1Tags.GeneralString:
-                    return new DerGeneralString(bytes);
-                case Asn1Tags.GraphicString:
-                    return DerGraphicString.CreatePrimitive(bytes);
-                case Asn1Tags.IA5String:
-                    return new DerIA5String(bytes);
-                case Asn1Tags.Integer:
-                    return new DerInteger(bytes, false);
-                case Asn1Tags.Null:
-                {
-                    if (0 != bytes.Length)
-                        throw new InvalidOperationException("malformed NULL encoding encountered");
+            case Asn1Tags.BitString:
+                return DerBitString.CreatePrimitive(bytes);
+            case Asn1Tags.GeneralizedTime:
+                return new DerGeneralizedTime(bytes);
+            case Asn1Tags.GeneralString:
+                return new DerGeneralString(bytes);
+            case Asn1Tags.GraphicString:
+                return DerGraphicString.CreatePrimitive(bytes);
+            case Asn1Tags.IA5String:
+                return new DerIA5String(bytes);
+            case Asn1Tags.Integer:
+                return new DerInteger(bytes, false);
+            case Asn1Tags.Null:
+            {
+                if (0 != bytes.Length)
+                    throw new InvalidOperationException("malformed NULL encoding encountered");
 
-                    return DerNull.Instance;
-                }
-                case Asn1Tags.NumericString:
-                    return new DerNumericString(bytes);
-                case Asn1Tags.ObjectDescriptor:
-                    return Asn1ObjectDescriptor.CreatePrimitive(bytes);
-                case Asn1Tags.OctetString:
-                    return new DerOctetString(bytes);
-                case Asn1Tags.PrintableString:
-                    return new DerPrintableString(bytes);
-                case Asn1Tags.RelativeOid:
-                    return Asn1RelativeOid.CreatePrimitive(bytes, false);
-                case Asn1Tags.T61String:
-                    return new DerT61String(bytes);
-                case Asn1Tags.UniversalString:
-                    return new DerUniversalString(bytes);
-                case Asn1Tags.UtcTime:
-                    return new DerUtcTime(bytes);
-                case Asn1Tags.Utf8String:
-                    return new DerUtf8String(bytes);
-                case Asn1Tags.VideotexString:
-                    return new DerVideotexString(bytes);
-                case Asn1Tags.VisibleString:
-                    return new DerVisibleString(bytes);
-                default:
-                    throw new IOException("unknown tag " + tagNo + " encountered");
+                return DerNull.Instance;
+            }
+            case Asn1Tags.NumericString:
+                return new DerNumericString(bytes);
+            case Asn1Tags.ObjectDescriptor:
+                return Asn1ObjectDescriptor.CreatePrimitive(bytes);
+            case Asn1Tags.OctetString:
+                return new DerOctetString(bytes);
+            case Asn1Tags.PrintableString:
+                return new DerPrintableString(bytes);
+            case Asn1Tags.RelativeOid:
+                return Asn1RelativeOid.CreatePrimitive(bytes, false);
+            case Asn1Tags.T61String:
+                return new DerT61String(bytes);
+            case Asn1Tags.UniversalString:
+                return new DerUniversalString(bytes);
+            case Asn1Tags.UtcTime:
+                return new DerUtcTime(bytes);
+            case Asn1Tags.Utf8String:
+                return new DerUtf8String(bytes);
+            case Asn1Tags.VideotexString:
+                return new DerVideotexString(bytes);
+            case Asn1Tags.VisibleString:
+                return new DerVisibleString(bytes);
+            default:
+                throw new IOException("unknown tag " + tagNo + " encountered");
             }
         }
     }
diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs
index 354437a6a..55ad36d46 100644
--- a/crypto/src/asn1/BERTaggedObjectParser.cs
+++ b/crypto/src/asn1/BERTaggedObjectParser.cs
@@ -1,66 +1,58 @@
 using System;
 using System.IO;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1
 {
 	public class BerTaggedObjectParser
 		: Asn1TaggedObjectParser
 	{
-		private bool				_constructed;
-		private int					_tagNumber;
-		private Asn1StreamParser	_parser;
-
-		[Obsolete]
-		internal BerTaggedObjectParser(
-			int		baseTag,
-			int		tagNumber,
-			Stream	contentStream)
-			: this((baseTag & Asn1Tags.Constructed) != 0, tagNumber, new Asn1StreamParser(contentStream))
-		{
-		}
+        private readonly int m_tagClass;
+        private readonly int m_tagNo;
+        private readonly bool m_constructed;
+        private readonly Asn1StreamParser m_parser;
 
-		internal BerTaggedObjectParser(
-			bool				constructed,
-			int					tagNumber,
-			Asn1StreamParser	parser)
+		internal BerTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser)
 		{
-			_constructed = constructed;
-			_tagNumber = tagNumber;
-			_parser = parser;
+            this.m_tagClass = tagClass;
+            this.m_tagNo = tagNo;
+            this.m_constructed = constructed;
+            this.m_parser = parser;
 		}
 
 		public bool IsConstructed
 		{
-			get { return _constructed; }
+			get { return m_constructed; }
 		}
 
+        public int TagClass
+        {
+            get { return m_tagClass; }
+        }
+
 		public int TagNo
 		{
-			get { return _tagNumber; }
+			get { return m_tagNo; }
 		}
 
-		public IAsn1Convertible GetObjectParser(
-			int		tag,
-			bool	isExplicit)
+		public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit)
 		{
-			if (isExplicit)
-			{
-				if (!_constructed)
-					throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+            if (Asn1Tags.ContextSpecific != TagClass)
+                throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags");
 
-				return _parser.ReadObject();
-			}
+            if (!declaredExplicit)
+                return m_parser.ReadImplicit(m_constructed, baseTagNo);
+
+			if (!m_constructed)
+				throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
 
-			return _parser.ReadImplicit(_constructed, tag);
+	        return m_parser.ReadObject();
 		}
 
 		public Asn1Object ToAsn1Object()
 		{
 			try
 			{
-				return _parser.ReadTaggedObject(_constructed, _tagNumber);
+				return m_parser.ReadTaggedObject(TagClass, TagNo, IsConstructed);
 			}
 			catch (IOException e)
 			{