Esempio n. 1
0
        /// <summary>
        /// Read value of given node. Underlying stream in BER reader has to be seekable to do that.
        /// </summary>
        /// <param name="node">Node  whose value shall be read.</param>
        /// <param name="outputBuffer">Reader will read content to this parameter.
        /// The parameter need to be initialized beforehand with length of actual read node.</param>
        /// <remarks>
        /// Basically it can be used only on nodes that have definite length and are primitive, i.e. Integer,BitString,OctetString etc.
        /// Sequence and Set are construct types so they are excluded.
        /// </remarks>
        /// <returns>Content of the given node.</returns>
        public void ReadContentAsBuffer(InternalNode node, byte[] outputBuffer)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }
            if (outputBuffer == null)
            {
                throw new ArgumentNullException("outputBuffer");
            }
            if (outputBuffer.Length != node.Length)
            {
                throw new ArgumentException("Parameter outputBuffer needs to be initialized to length of actual read node.");
            }

            if (node.NodeType != Asn1NodeType.Primitive && node.Identifier.Class != Asn1Class.ContextSpecific)
            {
                throw new InvalidOperationException("Content cannot be read at this position");
            }

            if ((node.IsConstructed && node.Identifier.Class != Asn1Class.ContextSpecific) || node.HasIndefiniteLength)
            {
                throw new InvalidOperationException("Cannot read value from constructed type");
            }

            if (node.Length > int.MaxValue)
            {
                throw new InvalidOperationException("Cannot read values larger than 2GB into buffer");
            }

            _innerStream.Seek(node.DataOffsetToStream, SeekOrigin.Begin);
            _innerStream.Read(outputBuffer, 0, node.Length);
        }
Esempio n. 2
0
        /// <summary>
        /// Reads identifier octet from ASN.1 object.
        /// </summary>
        /// <returns></returns>
        private Identifier ReadIdentifierOctet()
        {
            // Identifier octets
            // read one byte from stream. This should represent Identifier Asn.1 tag (class and number)
            int identifierOctet = _innerStream.ReadByte();

            if (identifierOctet == -1)
            {
                // reached end of stream
                _current = new InternalNode()
                {
                    NodeType = Asn1NodeType.DocumentEnd, Identifier = new Identifier()
                };
                return(_current.Identifier);
            }

            //  |               |   (   63  ------------------------------->    |
            //  |               |   32  |   (   31  ----------------------->    |
            //  -----------------------------------------------------------------
            //  |   8   |   7   |   6   |   5   |   4   |   3   |   2   |   1   |
            //  |   class       |   P/C | Tag Number                            |
            //  -----------------------------------------------------------------
            var tagStart = (byte)identifierOctet;

            //parse Tag
            // bit 6 contains P/C (Primitive/Constructed) flag. 6bit => value 0x20 (32 decimal)
            bool isConstructed = (tagStart & 0x20) != 0;

            // Tag number allows values from 0 to 31. 31 states long-form
            bool moreBytes = (tagStart & (int)Asn1Type.LongForm) == (int)Asn1Type.LongForm;

            while (moreBytes)
            {
                byte b = ReadByteOrThrow(_innerStream);

                // TODO parse multi byte tag
                throw new NotSupportedException();

                moreBytes = (b & 0x80) != 0;
            }

            var classTag  = (Asn1Class)(tagStart & ~63);
            var tagNumber = (Asn1Type)(tagStart & 31);

            var identifier = new Identifier
            {
                Class       = classTag,
                Constructed = isConstructed,
                Tag         = tagNumber
            };

            return(identifier);
        }
Esempio n. 3
0
        /// <summary>
        /// Read value of given node. Underlying stream in BER reader has to be seekable to do that.
        /// </summary>
        /// <param name="node">Node  whose value shall be read.</param>
        /// <returns>Content of the given node.</returns>
        public byte[] ReadContentAsBuffer(InternalNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            var result = new byte[node.Length];

            ReadContentAsBuffer(node, result);
            return(result);
        }
Esempio n. 4
0
        /// <summary>
        /// Get raw ASN.1 node data (whole TLV => Identifier, Length and Value octets).
        /// </summary>
        /// <param name="node">Node that shall be read.</param>
        /// <returns>Raw data of the given ASN.1 node including Identifier and Length octets.</returns>
        public byte[] ExtractAsn1NodeAsRawData(InternalNode node)
        {
            if (node == null)
            {
                throw new ArgumentNullException("node");
            }

            byte[] outputBuffer = new byte[node.EndPosition - node.StartPosition];

            if (node.Length > int.MaxValue)
            {
                throw new InvalidOperationException("Cannot read values larger than 2GB into buffer");
            }

            _innerStream.Seek(node.StartPosition, SeekOrigin.Begin);
            _innerStream.Read(outputBuffer, 0, Convert.ToInt32(node.EndPosition - node.StartPosition));

            return(outputBuffer);
        }
Esempio n. 5
0
        /// <summary>
        /// Parse ASN.1 node and sets the result to <see cref="InternalNode"/> class.
        /// <param name="readContent">Flag indicating that value of each node should be read as well.</param>
        /// </summary>
        private void InternalReadElement(bool readContent = false)
        {
            // This is the end of Asn1 constructed element.
            if (_nodeStack.Count > 0 && _innerStream.Position >= _nodeStack.Peek().EndPosition)
            {
                _current = new InternalNode()
                {
                    Identifier = new Identifier
                    {
                        Class       = _nodeStack.Peek().Identifier.Class,
                        Constructed = _nodeStack.Peek().Identifier.Constructed,
                        Tag         = _nodeStack.Peek().Identifier.Tag
                    },
                    EndPosition        = _innerStream.Position,
                    Length             = 0,
                    DataOffsetToStream = _innerStream.Position,
                    NodeType           = Asn1NodeType.ConstructedEnd
                };

                return;
            }

            var nodeStartPosition = _innerStream.Position;

            // Identifier
            var identifier = ReadIdentifierOctet();

            if (identifier == null)
            {
                // if no identifier was returned then it is the end of stream
                return;
            }

            // ReadIdentifierOctet(); may change _current to DocumentEnd type
            if (_current.NodeType == Asn1NodeType.DocumentEnd)
            {
                return;
            }

            var length = ReadLength();

            if (length >= _innerStream.Length)
            {
                throw new Exception("Length of ASN.1 node exceeds length of current stream.");
            }

            var hasIndefiniteLengthForm = false;

            if (length == -1)
            {
                hasIndefiniteLengthForm = true;
                length = DetermineLengthOfIndefiniteLengthNode();
            }

            // set what we parsed so far in internal node class
            _current = new InternalNode()
            {
                Identifier          = identifier,
                StartPosition       = nodeStartPosition,
                HasIndefiniteLength = hasIndefiniteLengthForm,
                EndPosition         = _innerStream.Position + ((hasIndefiniteLengthForm) ? length + 2: length),
                Length             = length,
                DataOffsetToStream = _innerStream.Position,
                NodeType           = (identifier.Constructed) ? Asn1NodeType.ConstructedStart : Asn1NodeType.Primitive
            };

            // if content should be read then be it but only if the node is Primitive. Context Specific value should be read manually when appropriate
            if (readContent)
            {
                _current.RawValue = (_current.NodeType == Asn1NodeType.Primitive)
                    ? ReadContentAsBuffer(_current)
                    : null;
            }
        }