//+-------------------------------------------------------------------------------+ //| Copyright (c) 2003 Liping Dai. All rights reserved. | //| Web: www.lipingshare.com | //| Email: lipingshare@yahoo.com | //| | //| Copyright and Permission Details: | //| ================================= | //| Permission is hereby granted, free of charge, to any person obtaining a copy | //| of this software and associated documentation files (the "Software"), to deal | //| in the Software without restriction, including without limitation the rights | //| to use, copy, modify, merge, publish, distribute, and/or sell copies of the | //| Software, subject to the following conditions: | //| | //| 1. Redistributions of source code must retain the above copyright notice, this| //| list of conditions and the following disclaimer. | //| | //| 2. Redistributions in binary form must reproduce the above copyright notice, | //| this list of conditions and the following disclaimer in the documentation | //| and/or other materials provided with the distribution. | //| | //| THE SOFTWARE PRODUCT IS PROVIDED �AS IS� WITHOUT WARRANTY OF ANY KIND, | //| EITHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | //| WARRANTIES OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR | //| A PARTICULAR PURPOSE. | //+-------------------------------------------------------------------------------+ using System; using System.IO; using System.Collections; using System.Linq; using System.Text; using UnityEngine; namespace LipingShare.LCLib.Asn1Processor { /// /// Asn1Node, implemented IAsn1Node interface. /// internal class Asn1Node : IAsn1Node { // PrivateMembers private byte tag; private long dataOffset; private long dataLength; private long lengthFieldBytes; private byte[] data; private ArrayList childNodeList; private byte unusedBits; private long deepness; private string path = ""; private const int indentStep = 3; private Asn1Node parentNode; private bool requireRecalculatePar = true; private bool isIndefiniteLength = false; private bool parseEncapsulatedData = true; /// /// Default Asn1Node text line length. /// public const int defaultLineLen = 80; /// /// Minium line length. /// public const int minLineLen = 60; const int k_EndOfStream = -1; const int k_InvalidIndeterminateContentLength = -1; const int k_IndefiniteLengthFooterSize = 2; private Asn1Node(Asn1Node parentNode, long dataOffset) { Init(); deepness = parentNode.Deepness + 1; this.parentNode = parentNode; this.dataOffset = dataOffset; } private void Init() { childNodeList = new ArrayList(); data = null; dataLength = 0; lengthFieldBytes = 0; unusedBits = 0; tag = Asn1Tag.SEQUENCE | Asn1TagClasses.CONSTRUCTED; childNodeList.Clear(); deepness = 0; parentNode = null; } private string GetHexPrintingStr(Asn1Node startNode, string baseLine, string lStr, int lineLen) { string nodeStr = ""; string iStr = GetIndentStr(startNode); string dataStr = Asn1Util.ToHexString(data); if (dataStr.Length > 0) { if (baseLine.Length + dataStr.Length < lineLen) { nodeStr += baseLine + "'" + dataStr + "'"; } else { nodeStr += baseLine + FormatLineHexString( lStr, iStr.Length, lineLen, dataStr ); } } else { nodeStr += baseLine; } return nodeStr + "\r\n"; } private string FormatLineString(string lStr, int indent, int lineLen, string msg) { string retval = ""; indent += indentStep; int realLen = lineLen - indent; int sLen = indent; int currentp; for (currentp = 0; currentp < msg.Length; currentp += realLen) { if (currentp + realLen > msg.Length) { retval += "\r\n" + lStr + Asn1Util.GenStr(sLen, ' ') + "'" + msg.Substring(currentp, msg.Length - currentp) + "'"; } else { retval += "\r\n" + lStr + Asn1Util.GenStr(sLen, ' ') + "'" + msg.Substring(currentp, realLen) + "'"; } } return retval; } private string FormatLineHexString(string lStr, int indent, int lineLen, string msg) { string retval = ""; indent += indentStep; int realLen = lineLen - indent; int sLen = indent; int currentp; for (currentp = 0; currentp < msg.Length; currentp += realLen) { if (currentp + realLen > msg.Length) { retval += "\r\n" + lStr + Asn1Util.GenStr(sLen, ' ') + msg.Substring(currentp, msg.Length - currentp); } else { retval += "\r\n" + lStr + Asn1Util.GenStr(sLen, ' ') + msg.Substring(currentp, realLen); } } return retval; } //PublicMembers /// /// Constructor, initialize all the members. /// public Asn1Node() { Init(); dataOffset = 0; } /// /// Get/Set isIndefiniteLength. /// public bool IsIndefiniteLength { get { return isIndefiniteLength; } set { isIndefiniteLength = value; } } /// /// Clone a new Asn1Node by current node. /// /// new node. public Asn1Node Clone() { MemoryStream ms = new MemoryStream(); this.SaveData(ms); ms.Position = 0; Asn1Node node = new Asn1Node(); node.LoadData(ms); return node; } /// /// Get/Set tag value. /// public byte Tag { get { return tag; } set { tag = value; } } public byte MaskedTag { get { return (byte)(tag & Asn1Tag.TAG_MASK); } } /// /// Load data from byte[]. /// /// byte[] /// true:Succeed; false:failed. public bool LoadData(byte[] byteData) { bool retval = true; try { MemoryStream ms = new MemoryStream(byteData); ms.Position = 0; retval = LoadData(ms); ms.Close(); } catch { retval = false; } return retval; } /// /// Retrieve all the node count in the node subtree. /// /// starting node. /// long integer node count in the node subtree. public static long GetDescendantNodeCount(Asn1Node node) { long count = 0; count += node.ChildNodeCount; for (int i = 0; i < node.ChildNodeCount; i++) { count += GetDescendantNodeCount(node.GetChildNode(i)); } return count; } /// /// Load data from Stream. Start from current position. /// This function sets requireRecalculatePar to false then calls InternalLoadData /// to complish the task. /// /// Stream /// true:Succeed; false:failed. public bool LoadData(Stream xdata) { bool retval = false; try { RequireRecalculatePar = false; retval = InternalLoadData(xdata); return retval; } finally { RequireRecalculatePar = true; RecalculateTreePar(); } } /// /// Call SaveData and return byte[] as result instead stream. /// /// public byte[] GetRawData() { MemoryStream ms = new MemoryStream(); SaveData(ms); byte[] retval = new byte[ms.Length]; ms.Position = 0; ms.Read(retval, 0, (int)ms.Length); ms.Close(); return retval; } /// /// Get if data is empty. /// public bool IsEmptyData { get { if (data == null) return true; if (data.Length < 1) return true; else return false; } } /// /// Save node data into Stream. /// /// Stream. /// true:Succeed; false:failed. public bool SaveData(Stream xdata) { bool retval = true; long nodeCount = ChildNodeCount; xdata.WriteByte(tag); Asn1Util.DERLengthEncode(xdata, (ulong)dataLength); if ((tag) == Asn1Tag.BIT_STRING) { xdata.WriteByte(unusedBits); } if (nodeCount == 0) { if (data != null) { xdata.Write(data, 0, data.Length); } } else { Asn1Node tempNode; int i; for (i = 0; i < nodeCount; i++) { tempNode = GetChildNode(i); retval = tempNode.SaveData(xdata); } } return retval; } /// /// Clear data and children list. /// public void ClearAll() { data = null; Asn1Node tempNode; for (int i = 0; i < childNodeList.Count; i++) { tempNode = (Asn1Node)childNodeList[i]; tempNode.ClearAll(); } childNodeList.Clear(); RecalculateTreePar(); } /// /// Add child node at the end of children list. /// /// the node that will be add in. public void AddChild(Asn1Node xdata) { childNodeList.Add(xdata); RecalculateTreePar(); } /// /// Insert a node in the children list before the pointed index. /// /// Asn1Node /// 0 based index. /// New node index. public int InsertChild(Asn1Node xdata, int index) { childNodeList.Insert(index, xdata); RecalculateTreePar(); return index; } /// /// Insert a node in the children list before the pointed node. /// /// Asn1Node that will be instered in the children list. /// Index node. /// New node index. public int InsertChild(Asn1Node xdata, Asn1Node indexNode) { int index = childNodeList.IndexOf(indexNode); childNodeList.Insert(index, xdata); RecalculateTreePar(); return index; } /// /// Insert a node in the children list after the pointed node. /// /// Asn1Node /// Index node. /// New node index. public int InsertChildAfter(Asn1Node xdata, Asn1Node indexNode) { int index = childNodeList.IndexOf(indexNode) + 1; childNodeList.Insert(index, xdata); RecalculateTreePar(); return index; } /// /// Insert a node in the children list after the pointed node. /// /// Asn1Node that will be instered in the children list. /// 0 based index. /// New node index. public int InsertChildAfter(Asn1Node xdata, int index) { int xindex = index + 1; childNodeList.Insert(xindex, xdata); RecalculateTreePar(); return xindex; } /// /// Remove a child from children node list by index. /// /// 0 based index. /// The Asn1Node just removed from the list. public Asn1Node RemoveChild(int index) { Asn1Node retval = null; if (index < (childNodeList.Count - 1)) { retval = (Asn1Node)childNodeList[index + 1]; } childNodeList.RemoveAt(index); if (retval == null) { if (childNodeList.Count > 0) { retval = GetLastChild(); } else { retval = this; } } RecalculateTreePar(); return retval; } Asn1Node GetLastChild() { return (Asn1Node)childNodeList[childNodeList.Count - 1]; } /// /// Remove the child from children node list. /// /// The node needs to be removed. /// public Asn1Node RemoveChild(Asn1Node node) { Asn1Node retval = null; int i = childNodeList.IndexOf(node); retval = RemoveChild(i); return retval; } /// /// Get child node count. /// public long ChildNodeCount { get { return childNodeList.Count; } } /// /// Retrieve child node by index. /// /// 0 based index. /// 0 based index. public Asn1Node GetChildNode(int index) { Asn1Node retval = null; if (index < ChildNodeCount) { retval = (Asn1Node)childNodeList[index]; } return retval; } /// /// Get tag name. /// public string TagName { get { return Asn1Util.GetTagName(tag); } } /// /// Get parent node. /// public Asn1Node ParentNode { get { return parentNode; } } /// /// Get the node and all the descendents text description. /// /// starting node. /// line length. /// public string GetText(Asn1Node startNode, int lineLen) { string nodeStr = ""; string baseLine = ""; string dataStr = ""; const string lStr = " | | | "; string oid, oidName; switch (tag) { case Asn1Tag.BIT_STRING: baseLine = String.Format("{0,6}|{1,6}|{2,7}|{3} {4} UnusedBits:{5} : ", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName, unusedBits ); dataStr = Asn1Util.ToHexString(data); if (baseLine.Length + dataStr.Length < lineLen) { if (dataStr.Length < 1) { nodeStr += baseLine + "\r\n"; } else { nodeStr += baseLine + "'" + dataStr + "'\r\n"; } } else { nodeStr += baseLine + FormatLineHexString( lStr, GetIndentStr(startNode).Length, lineLen, dataStr + "\r\n" ); } break; case Asn1Tag.OBJECT_IDENTIFIER: Oid xoid = new Oid(); oid = xoid.Decode(new MemoryStream(data)); oidName = xoid.GetOidName(oid); nodeStr += String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : {5} [{6}]\r\n", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName, oidName, oid ); break; case Asn1Tag.RELATIVE_OID: RelativeOid xiod = new RelativeOid(); oid = xiod.Decode(new MemoryStream(data)); oidName = ""; nodeStr += String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : {5} [{6}]\r\n", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName, oidName, oid ); break; case Asn1Tag.PRINTABLE_STRING: case Asn1Tag.IA5_STRING: case Asn1Tag.UNIVERSAL_STRING: case Asn1Tag.VISIBLE_STRING: case Asn1Tag.NUMERIC_STRING: case Asn1Tag.UTC_TIME: case Asn1Tag.UTF8_STRING: case Asn1Tag.BMPSTRING: case Asn1Tag.GENERAL_STRING: case Asn1Tag.GENERALIZED_TIME: baseLine = String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : ", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName ); if (tag == Asn1Tag.UTF8_STRING) { UTF8Encoding unicode = new UTF8Encoding(); dataStr = unicode.GetString(data); } else { dataStr = Asn1Util.BytesToString(data); } if (baseLine.Length + dataStr.Length < lineLen) { nodeStr += baseLine + "'" + dataStr + "'\r\n"; } else { nodeStr += baseLine + FormatLineString( lStr, GetIndentStr(startNode).Length, lineLen, dataStr) + "\r\n"; } break; case Asn1Tag.INTEGER: if (data != null && dataLength < 8) { nodeStr += String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : {5}\r\n", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName, Asn1Util.BytesToLong(data).ToString() ); } else { baseLine = String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : ", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName ); nodeStr += GetHexPrintingStr(startNode, baseLine, lStr, lineLen); } break; default: if ((tag & Asn1Tag.TAG_MASK) == 6) // Visible string for certificate { baseLine = String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : ", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName ); dataStr = Asn1Util.BytesToString(data); if (baseLine.Length + dataStr.Length < lineLen) { nodeStr += baseLine + "'" + dataStr + "'\r\n"; } else { nodeStr += baseLine + FormatLineString( lStr, GetIndentStr(startNode).Length, lineLen, dataStr) + "\r\n"; } } else { baseLine = String.Format("{0,6}|{1,6}|{2,7}|{3} {4} : ", dataOffset, dataLength, lengthFieldBytes, GetIndentStr(startNode), TagName ); nodeStr += GetHexPrintingStr(startNode, baseLine, lStr, lineLen); } break; }; if (childNodeList.Count >= 0) { nodeStr += GetListStr(startNode, lineLen); } return nodeStr; } /// /// Get the path string of the node. /// public string Path { get { return path; } } /// /// Retrieve the node description. /// /// true:Return hex string only; /// false:Convert to more readable string depending on the node tag. /// string public string GetDataStr(bool pureHexMode) { const int lineLen = 32; string dataStr = ""; if (pureHexMode) { dataStr = Asn1Util.FormatString(Asn1Util.ToHexString(data), lineLen, 2); } else { switch (tag) { case Asn1Tag.BIT_STRING: dataStr = Asn1Util.FormatString(Asn1Util.ToHexString(data), lineLen, 2); break; case Asn1Tag.OBJECT_IDENTIFIER: Oid xoid = new Oid(); dataStr = xoid.Decode(new MemoryStream(data)); break; case Asn1Tag.RELATIVE_OID: RelativeOid roid = new RelativeOid(); dataStr = roid.Decode(new MemoryStream(data)); break; case Asn1Tag.PRINTABLE_STRING: case Asn1Tag.IA5_STRING: case Asn1Tag.UNIVERSAL_STRING: case Asn1Tag.VISIBLE_STRING: case Asn1Tag.NUMERIC_STRING: case Asn1Tag.UTC_TIME: case Asn1Tag.BMPSTRING: case Asn1Tag.GENERAL_STRING: case Asn1Tag.GENERALIZED_TIME: dataStr = Asn1Util.BytesToString(data); break; case Asn1Tag.UTF8_STRING: UTF8Encoding utf8 = new UTF8Encoding(); dataStr = utf8.GetString(data); break; case Asn1Tag.INTEGER: dataStr = Asn1Util.FormatString(Asn1Util.ToHexString(data), lineLen, 2); break; default: if ((tag & Asn1Tag.TAG_MASK) == 6) // Visible string for certificate { dataStr = Asn1Util.BytesToString(data); } else { dataStr = Asn1Util.FormatString(Asn1Util.ToHexString(data), lineLen, 2); } break; }; } return dataStr; } /// /// Get node label string. /// /// /// /// SHOW_OFFSET /// SHOW_DATA /// USE_HEX_OFFSET /// SHOW_TAG_NUMBER /// SHOW_PATH /// /// string public string GetLabel(uint mask) { string nodeStr = ""; string dataStr = ""; string offsetStr = ""; if ((mask & TagTextMask.USE_HEX_OFFSET) != 0) { if ((mask & TagTextMask.SHOW_TAG_NUMBER) != 0) offsetStr = String.Format("(0x{0:X2},0x{1:X6},0x{2:X4})", tag, dataOffset, dataLength); else offsetStr = String.Format("(0x{0:X6},0x{1:X4})", dataOffset, dataLength); } else { if ((mask & TagTextMask.SHOW_TAG_NUMBER) != 0) offsetStr = String.Format("({0},{1},{2})", tag, dataOffset, dataLength); else offsetStr = String.Format("({0},{1})", dataOffset, dataLength); } string oid, oidName; switch (tag) { case Asn1Tag.BIT_STRING: if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName + " UnusedBits: " + unusedBits.ToString(); if ((mask & TagTextMask.SHOW_DATA) != 0) { dataStr = Asn1Util.ToHexString(data); nodeStr += ((dataStr.Length > 0) ? " : '" + dataStr + "'" : ""); } break; case Asn1Tag.OBJECT_IDENTIFIER: Oid xoid = new Oid(); oid = xoid.Decode(data); oidName = xoid.GetOidName(oid); if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName; nodeStr += " : " + oidName; if ((mask & TagTextMask.SHOW_DATA) != 0) { nodeStr += ((oid.Length > 0) ? " : '" + oid + "'" : ""); } break; case Asn1Tag.RELATIVE_OID: RelativeOid roid = new RelativeOid(); oid = roid.Decode(data); oidName = ""; if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName; nodeStr += " : " + oidName; if ((mask & TagTextMask.SHOW_DATA) != 0) { nodeStr += ((oid.Length > 0) ? " : '" + oid + "'" : ""); } break; case Asn1Tag.PRINTABLE_STRING: case Asn1Tag.IA5_STRING: case Asn1Tag.UNIVERSAL_STRING: case Asn1Tag.VISIBLE_STRING: case Asn1Tag.NUMERIC_STRING: case Asn1Tag.UTC_TIME: case Asn1Tag.UTF8_STRING: case Asn1Tag.BMPSTRING: case Asn1Tag.GENERAL_STRING: case Asn1Tag.GENERALIZED_TIME: if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName; if ((mask & TagTextMask.SHOW_DATA) != 0) { if (tag == Asn1Tag.UTF8_STRING) { UTF8Encoding unicode = new UTF8Encoding(); dataStr = unicode.GetString(data); } else { dataStr = Asn1Util.BytesToString(data); } nodeStr += ((dataStr.Length > 0) ? " : '" + dataStr + "'" : ""); } break; case Asn1Tag.INTEGER: if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName; if ((mask & TagTextMask.SHOW_DATA) != 0) { if (data != null && dataLength < 8) { dataStr = Asn1Util.BytesToLong(data).ToString(); } else { dataStr = Asn1Util.ToHexString(data); } nodeStr += ((dataStr.Length > 0) ? " : '" + dataStr + "'" : ""); } break; default: if ((mask & TagTextMask.SHOW_OFFSET) != 0) { nodeStr += offsetStr; } nodeStr += " " + TagName; if ((mask & TagTextMask.SHOW_DATA) != 0) { if ((tag & Asn1Tag.TAG_MASK) == 6) // Visible string for certificate { dataStr = Asn1Util.BytesToString(data); } else { dataStr = Asn1Util.ToHexString(data); } nodeStr += ((dataStr.Length > 0) ? " : '" + dataStr + "'" : ""); } break; }; if ((mask & TagTextMask.SHOW_PATH) != 0) { nodeStr = "(" + path + ") " + nodeStr; } return nodeStr; } /// /// Get data length. Not included the unused bits byte for BITSTRING. /// public long DataLength { get { return dataLength; } } /// /// Get the length field bytes. /// public long LengthFieldBytes { get { return lengthFieldBytes; } } /// /// Get/Set node data by byte[], the data length field content and all the /// node in the parent chain will be adjusted. ///

/// It return all the child data for constructed node. ///
public byte[] Data { get { MemoryStream xdata = new MemoryStream(); long nodeCount = ChildNodeCount; if (nodeCount == 0) { if (data != null) { xdata.Write(data, 0, data.Length); } } else { Asn1Node tempNode; for (int i = 0; i < nodeCount; i++) { tempNode = GetChildNode(i); tempNode.SaveData(xdata); } } byte[] tmpData = new byte[xdata.Length]; xdata.Position = 0; xdata.Read(tmpData, 0, (int)xdata.Length); xdata.Close(); return tmpData; } set { SetData(value); } } /// /// Get the deepness of the node. /// public long Deepness { get { return deepness; } } /// /// Get data offset. /// public long DataOffset { get { return dataOffset; } } /// /// Get unused bits for BITSTRING. /// public byte UnusedBits { get { return unusedBits; } set { unusedBits = value; } } /// /// Get descendant node by node path. /// /// relative node path that refer to current node. /// public Asn1Node GetDescendantNodeByPath(string nodePath) { Asn1Node retval = this; if (nodePath == null) return retval; nodePath = nodePath.TrimEnd().TrimStart(); if (nodePath.Length < 1) return retval; string[] route = nodePath.Split('/'); try { for (int i = 1; i < route.Length; i++) { retval = retval.GetChildNode(Convert.ToInt32(route[i])); } } catch { retval = null; } return retval; } /// /// Get node by OID. /// /// OID. /// Starting node. /// Null or Asn1Node. static public Asn1Node GetDecendantNodeByOid(string oid, Asn1Node startNode) { Asn1Node retval = null; Oid xoid = new Oid(); for (int i = 0; i < startNode.ChildNodeCount; i++) { Asn1Node childNode = startNode.GetChildNode(i); int tmpTag = childNode.tag & Asn1Tag.TAG_MASK; if (tmpTag == Asn1Tag.OBJECT_IDENTIFIER) { if (oid == xoid.Decode(childNode.Data)) { retval = childNode; break; } } retval = GetDecendantNodeByOid(oid, childNode); if (retval != null) break; } return retval; } /// /// Constant of tag field length. /// public const int TagLength = 1; /// /// Constant of unused bits field length. /// public const int BitStringUnusedFiledLength = 1; /// /// Tag text generation mask definition. /// public class TagTextMask { /// /// Show offset. /// public const uint SHOW_OFFSET = 0x01; /// /// Show decoded data. /// public const uint SHOW_DATA = 0x02; /// /// Show offset in hex format. /// public const uint USE_HEX_OFFSET = 0x04; /// /// Show tag. /// public const uint SHOW_TAG_NUMBER = 0x08; /// /// Show node path. /// public const uint SHOW_PATH = 0x10; } /// /// Set/Get requireRecalculatePar. RecalculateTreePar function will not do anything /// if it is set to false. /// protected bool RequireRecalculatePar { get { return requireRecalculatePar; } set { requireRecalculatePar = value; } } //ProtectedMembers /// /// Find root node and recalculate entire tree length field, /// path, offset and deepness. /// protected void RecalculateTreePar() { if (!requireRecalculatePar) return; Asn1Node rootNode; for (rootNode = this; rootNode.ParentNode != null;) { rootNode = rootNode.ParentNode; } ResetBranchDataLength(rootNode); rootNode.dataOffset = 0; rootNode.deepness = 0; long subOffset = rootNode.dataOffset + TagLength + rootNode.lengthFieldBytes; ResetChildNodePar(rootNode, subOffset); } /// /// Recursively set all the node data length. /// /// /// node data length. protected static long ResetBranchDataLength(Asn1Node node) { long retval = 0; long childDataLength = 0; if (node.ChildNodeCount < 1) { if (node.data != null) childDataLength += node.data.Length; } else { for (int i = 0; i < node.ChildNodeCount; i++) { childDataLength += ResetBranchDataLength(node.GetChildNode(i)); } } node.dataLength = childDataLength; if (node.tag == Asn1Tag.BIT_STRING) node.dataLength += BitStringUnusedFiledLength; ResetDataLengthFieldWidth(node); retval = node.dataLength + TagLength + node.lengthFieldBytes; return retval; } /// /// Encode the node data length field and set lengthFieldBytes and dataLength. /// /// The node needs to be reset. protected static void ResetDataLengthFieldWidth(Asn1Node node) { MemoryStream tempStream = new MemoryStream(); Asn1Util.DERLengthEncode(tempStream, (ulong)node.dataLength); node.lengthFieldBytes = tempStream.Length; tempStream.Close(); } /// /// Recursively set all the child parameters, except dataLength. /// dataLength is set by ResetBranchDataLength. /// /// Starting node. /// Starting node offset. protected void ResetChildNodePar(Asn1Node xNode, long subOffset) { int i; if (xNode.tag == Asn1Tag.BIT_STRING) { subOffset++; } Asn1Node tempNode; for (i = 0; i < xNode.ChildNodeCount; i++) { tempNode = xNode.GetChildNode(i); tempNode.parentNode = xNode; tempNode.dataOffset = subOffset; tempNode.deepness = xNode.deepness + 1; tempNode.path = xNode.path + '/' + i.ToString(); subOffset += TagLength + tempNode.lengthFieldBytes; ResetChildNodePar(tempNode, subOffset); subOffset += tempNode.dataLength; } } /// /// Generate all the child text from childNodeList. /// /// Starting node. /// Line length. /// Text string. protected string GetListStr(Asn1Node startNode, int lineLen) { string nodeStr = ""; int i; Asn1Node tempNode; for (i = 0; i < childNodeList.Count; i++) { tempNode = (Asn1Node)childNodeList[i]; nodeStr += tempNode.GetText(startNode, lineLen); } return nodeStr; } /// /// Generate the node indent string. /// /// The node. /// Text string. protected string GetIndentStr(Asn1Node startNode) { string retval = ""; long startLen = 0; if (startNode != null) { startLen = startNode.Deepness; } for (long i = 0; i < deepness - startLen; i++) { retval += " "; } return retval; } /// /// Decode ASN.1 encoded node Stream data. /// /// Stream data. /// true:Succeed, false:Failed. protected bool GeneralDecode(Stream xdata) { long nodeMaxLen = xdata.Length - xdata.Position; tag = (byte)xdata.ReadByte(); long start = xdata.Position; dataLength = Asn1Util.DerLengthDecode(xdata, ref isIndefiniteLength); if (AreTagsOk()) { if (isIndefiniteLength) { return GeneralDecodeIndefiniteLength(xdata, nodeMaxLen - k_IndefiniteLengthFooterSize); } else { return GeneralDecodeKnownLengthWithChecks(xdata, start, nodeMaxLen); } } else { return false; } } private bool AreTagsOk() { if (ParentNode == null || ((ParentNode.tag & Asn1TagClasses.CONSTRUCTED) == 0)) { if ((tag & Asn1Tag.TAG_MASK) <= 0 || (tag & Asn1Tag.TAG_MASK) > 0x1E) { return false; } } return true; } private bool GeneralDecodeKnownLengthWithChecks(Stream xdata, long start, long nodeMaxLen) { if (IsGeneralStreamLengthOk(xdata, start, nodeMaxLen)) { return GeneralDecodeKnownLength(xdata); } return false; } private bool IsGeneralStreamLengthOk(Stream xdata, long start, long nodeMaxLen) { if (dataLength >= 0) { lengthFieldBytes = xdata.Position - start; if (nodeMaxLen >= (dataLength + TagLength + lengthFieldBytes)) { return true; } } return false; } private bool GeneralDecodeKnownLength(Stream xdata) { if (tag == Asn1Tag.BIT_STRING) { // First byte of BIT_STRING is unused bits. // BIT_STRING data does not include this byte. // Fixed by Gustaf Bj�rklund. if (dataLength < 1) { return false; } unusedBits = (byte)xdata.ReadByte(); ReadStreamDataDefiniteLength(xdata, (int)(dataLength - 1)); } else { ReadStreamDataDefiniteLength(xdata, (int)(dataLength)); } return true; } private void ReadStreamDataDefiniteLength(Stream xdata, int length) { data = new byte[length]; xdata.Read(data, 0, (int)(length)); } private bool GeneralDecodeIndefiniteLength(Stream xdata, long nodeMaxLen) { if (tag == Asn1Tag.BIT_STRING) { unusedBits = (byte)xdata.ReadByte(); nodeMaxLen--; } return ReadStreamDataIndefiniteLength(xdata, nodeMaxLen); } bool ReadStreamDataIndefiniteLength(Stream xdata, long nodeMaxLen) { var streamPosition = xdata.Position; long contentLength = MeasureContentLength(xdata); if (contentLength != k_InvalidIndeterminateContentLength && contentLength <= nodeMaxLen) { ReadMeasuredLengthDataFromStart(xdata, streamPosition, contentLength); return true; } else { return false; } } long MeasureContentLength(Stream xdata) { long contentLength = 0; bool firstEocByteFound = false; bool foundEoc = false; while (!foundEoc) { var currentByte = xdata.ReadByte(); if (currentByte == k_EndOfStream) { foundEoc = true; contentLength = k_InvalidIndeterminateContentLength; } else if (currentByte == Asn1Tag.TAG_END_OF_CONTENTS) { if (firstEocByteFound) { foundEoc = true; } else { firstEocByteFound = true; } } else { if (firstEocByteFound) { firstEocByteFound = false; contentLength++; } contentLength++; } } return contentLength; } void ReadMeasuredLengthDataFromStart(Stream xdata, long startPosition, long length) { xdata.Seek(startPosition, SeekOrigin.Begin); data = new byte[length]; xdata.Read(data, 0, (int)(length)); } /// /// Decode ASN.1 encoded complex data type Stream data. /// /// Stream data. /// true:Succeed, false:Failed. protected bool ListDecode(Stream xdata) { bool retval = false; long originalPosition = xdata.Position; try { tag = (byte)xdata.ReadByte(); long start = xdata.Position; dataLength = Asn1Util.DerLengthDecode(xdata, ref isIndefiniteLength); long childNodeMaxLen = xdata.Length - xdata.Position; if (isIndefiniteLength) { retval = ListDecodeIndefiniteLength(xdata, start, childNodeMaxLen - k_IndefiniteLengthFooterSize); } else { retval = ListDecodeKnownLengthWithChecks(xdata, start, childNodeMaxLen); } } finally { if (!retval) { xdata.Position = originalPosition; ClearAll(); } } return retval; } private bool ListDecodeKnownLengthWithChecks(Stream xdata, long start, long childNodeMaxLen) { if (IsListStreamLengthOk(xdata, childNodeMaxLen)) { return ListDecodeKnownLength(xdata, start); } return false; } private bool IsListStreamLengthOk(Stream xdata, long childNodeMaxLen) { return (dataLength >= 0 && childNodeMaxLen >= dataLength); } private bool ListDecodeKnownLength(Stream xdata, long start) { long offset = CalculateListEncodeFieldBytesAndOffset(xdata, start); HandleBitStringTag(xdata, ref offset); if (dataLength > 0) { return ListDecodeKnownLengthInternal(xdata, offset); } return false; } private long CalculateListEncodeFieldBytesAndOffset(Stream xdata, long start) { lengthFieldBytes = xdata.Position - start; return dataOffset + TagLength + lengthFieldBytes; } bool HandleBitStringTag(Stream xdata, ref long offset) { if (tag == Asn1Tag.BIT_STRING) { // First byte of BIT_STRING is unused bits. // BIT_STRING data does not include this byte. unusedBits = (byte)xdata.ReadByte(); dataLength--; offset++; return true; } return false; } private bool ListDecodeKnownLengthInternal(Stream xdata, long offset) { Stream secData = CreateAndPrepareListDecodeMemoryStreamKnownLength(xdata); return ListDecodeChildNodesWithKnownLength(secData, offset); } private Stream CreateAndPrepareListDecodeMemoryStreamKnownLength(Stream xdata) { Stream secData = new MemoryStream((int)dataLength); byte[] secByte = new byte[dataLength]; xdata.Read(secByte, 0, (int)(dataLength)); if (tag == Asn1Tag.BIT_STRING) { dataLength++; } secData.Write(secByte, 0, secByte.Length); secData.Position = 0; return secData; } private bool ListDecodeChildNodesWithKnownLength(Stream secData, long offset) { while (secData.Position < secData.Length) { if (!CreateAndAddChildNode(secData, ref offset)) { return false; } } return true; } private bool CreateAndAddChildNode(Stream secData, ref long offset) { var node = new Asn1Node(this, offset); node.parseEncapsulatedData = this.parseEncapsulatedData; long start = secData.Position; if (!node.InternalLoadData(secData)) { return false; } AddChild(node); offset += secData.Position - start; return true; } private bool ListDecodeIndefiniteLength(Stream xdata, long start, long childNodeMaxLen) { long offset = CalculateListEncodeFieldBytesAndOffset(xdata, start); if (HandleBitStringTag(xdata, ref offset)) { childNodeMaxLen--; } return ListDecodeIndefiniteLengthInternal(xdata, offset, childNodeMaxLen); } bool ListDecodeIndefiniteLengthInternal(Stream xdata, long offset, long childNodeMaxLen) { bool doneReading = false; while (!doneReading) { var oldOffset = offset; doneReading = ReadNextChildNodeOrEndFooterOfIndefiniteListClearIfInvalid(xdata, ref offset, childNodeMaxLen); childNodeMaxLen -= (offset - oldOffset); } return ChildNodeCount > 0; } bool ReadNextChildNodeOrEndFooterOfIndefiniteListClearIfInvalid(Stream xdata, ref long offset, long childNodeMaxLen) { bool doneReading; var byteChecks = DetectEndOfIndefiniteListContents(xdata); if (byteChecks == Asn1EndOfIndefiniteLengthNodeType.NotEnd) { doneReading = !ReadNextChildNodeOfIndefiniteListClearIfInvalid(xdata, ref offset, childNodeMaxLen); } else { if (byteChecks == Asn1EndOfIndefiniteLengthNodeType.EndOfStream && ChildNodeCount > 0) { //DEVELOPERS' NOTE: //End of Stream hit parsing an Indeterminate Length List Node. //This Indeterminate Length List Node is a false flag that just happens to have initial bytes matching the pattern of such a node. ClearAll(); } doneReading = true; } return doneReading; } Asn1EndOfIndefiniteLengthNodeType DetectEndOfIndefiniteListContents(Stream xdata) { var tagByte = xdata.ReadByte(); if (tagByte != k_EndOfStream) { var lengthByte = xdata.ReadByte(); if (lengthByte != k_EndOfStream) { if (tagByte == Asn1Tag.TAG_END_OF_CONTENTS && lengthByte == Asn1Tag.TAG_END_OF_CONTENTS) { return Asn1EndOfIndefiniteLengthNodeType.EndOfNodeFooter; } else { return Asn1EndOfIndefiniteLengthNodeType.NotEnd; } } } return Asn1EndOfIndefiniteLengthNodeType.EndOfStream; } bool ReadNextChildNodeOfIndefiniteListClearIfInvalid(Stream xdata, ref long offset, long childNodeMaxLen) { xdata.Position -= k_IndefiniteLengthFooterSize; bool validChildNode = false; if (CreateAndAddChildNode(xdata, ref offset)) { validChildNode = GetLastChild().DataLength < childNodeMaxLen; } if (ChildNodeCount > 0 && !validChildNode) { //DEVELOPERS' NOTE: //Invalid sequential child Asn1Node found in an Indeterminate Length List Node. //This Indeterminate Length List Node is a false flag that just happens to have initial bytes matching the pattern of such a node. ClearAll(); } return validChildNode; } /// /// Set the node data and recalculate the entire tree parameters. /// /// byte[] data. protected void SetData(byte[] xdata) { if (childNodeList.Count > 0) { throw new Exception("Constructed node can't hold simple data."); } else { data = xdata; if (data != null) { dataLength = data.Length; } else { dataLength = 0; } RecalculateTreePar(); } } /// /// Load data from Stream. Start from current position. /// /// Stream /// true:Succeed; false:failed. protected bool InternalLoadData(Stream xdata) { bool retval = true; ClearAll(); byte xtag; long curPosition = xdata.Position; xtag = (byte)xdata.ReadByte(); xdata.Position = curPosition; int maskedTag = xtag & Asn1Tag.TAG_MASK; if (((xtag & Asn1TagClasses.CONSTRUCTED) != 0) || (parseEncapsulatedData && ((maskedTag == Asn1Tag.BIT_STRING) || (maskedTag == Asn1Tag.EXTERNAL) || (maskedTag == Asn1Tag.GENERAL_STRING) || (maskedTag == Asn1Tag.GENERALIZED_TIME) || (maskedTag == Asn1Tag.GRAPHIC_STRING) || (maskedTag == Asn1Tag.IA5_STRING) || (maskedTag == Asn1Tag.OCTET_STRING) || (maskedTag == Asn1Tag.PRINTABLE_STRING) || (maskedTag == Asn1Tag.SEQUENCE) || (maskedTag == Asn1Tag.SET) || (maskedTag == Asn1Tag.T61_STRING) || (maskedTag == Asn1Tag.UNIVERSAL_STRING) || (maskedTag == Asn1Tag.UTF8_STRING) || (maskedTag == Asn1Tag.VIDEOTEXT_STRING) || (maskedTag == Asn1Tag.VISIBLE_STRING))) ) { if (!ListDecode(xdata)) { if (!GeneralDecode(xdata)) { retval = false; } } } else { if (!GeneralDecode(xdata)) retval = false; } return retval; } /// /// Get/Set parseEncapsulatedData. This property will be inherited by the /// child nodes when loading data. /// public bool ParseEncapsulatedData { get { return parseEncapsulatedData; } set { if (parseEncapsulatedData == value) return; byte[] tmpData = Data; parseEncapsulatedData = value; ClearAll(); if ((tag & Asn1TagClasses.CONSTRUCTED) != 0 || parseEncapsulatedData) { MemoryStream ms = new MemoryStream(tmpData); ms.Position = 0; bool isLoaded = true; while (ms.Position < ms.Length) { Asn1Node tempNode = new Asn1Node(); tempNode.ParseEncapsulatedData = parseEncapsulatedData; if (!tempNode.LoadData(ms)) { ClearAll(); isLoaded = false; break; } AddChild(tempNode); } if (!isLoaded) { Data = tmpData; } } else { Data = tmpData; } } } } }