2004-10-29 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / Mono.Security / ASN1.cs
index 79f9d8464e5dbd2c19963981bde6ea2e94785539..62c3f55fa01c8efeda357e8475ecffcaa622029f 100644 (file)
 //
 // ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator
 //
-// Author:
-//     Sebastien Pouliot (spouliot@motus.com)
+// Authors:
+//     Sebastien Pouliot  <sebastien@ximian.com>
+//     Jesper Pedersen  <jep@itplus.dk>
 //
 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
+// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2004 IT+ A/S (http://www.itplus.dk)
+//
+// 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, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
 using System;
 using System.Collections;
+using System.IO;
+using System.Text;
 
 namespace Mono.Security {
 
-// References:
-// a.  ITU ASN.1 standards (free download)
-//     http://www.itu.int/ITU-T/studygroups/com17/languages/
+       // References:
+       // a.   ITU ASN.1 standards (free download)
+       //      http://www.itu.int/ITU-T/studygroups/com17/languages/
 
 #if INSIDE_CORLIB
        internal
 #else
        public
 #endif
-class ASN1 {
+       class ASN1 {
 
-       protected byte m_nTag;
-       protected byte[] m_aValue;
-       protected ArrayList elist;
+               private byte m_nTag;
+               private byte[] m_aValue;
+               private ArrayList elist;
 
-       public ASN1 () : this (0x00, null) {}
+               public ASN1 () : this (0x00, null) {}
 
-       public ASN1 (byte tag) : this (tag, null) {}
+               public ASN1 (byte tag) : this (tag, null) {}
 
-       public ASN1 (byte tag, byte[] data) 
-       {
-               m_nTag = tag;
-               m_aValue = data;
-       }
+               public ASN1 (byte tag, byte[] data) 
+               {
+                       m_nTag = tag;
+                       m_aValue = data;
+               }
 
-       public ASN1 (byte[] data) 
-       {
-               m_nTag = data [0];
+               public ASN1 (byte[] data) 
+               {
+                       m_nTag = data [0];
 
-               int nLenLength = 0;
-               int nLength = data [1];
+                       int nLenLength = 0;
+                       int nLength = data [1];
 
-               if (nLength > 0x80) {
-                       // composed length
-                       nLenLength = nLength - 0x80;
-                       nLength = 0;
-                       for (int i = 0; i < nLenLength; i++) {
-                               nLength *= 256;
-                               nLength += data [i + 2];
+                       if (nLength > 0x80) {
+                               // composed length
+                               nLenLength = nLength - 0x80;
+                               nLength = 0;
+                               for (int i = 0; i < nLenLength; i++) {
+                                       nLength *= 256;
+                                       nLength += data [i + 2];
+                               }
+                       }
+                       else if (nLength == 0x80) {
+                               // undefined length encoding
+                               throw new NotSupportedException ("Undefined length encoding.");
                        }
-               }
 
-               m_aValue = new byte [nLength];
-               Array.Copy (data, (2 + nLenLength), m_aValue, 0, nLength);
+                       m_aValue = new byte [nLength];
+                       Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength);
 
-               if ((m_nTag & 0x20) == 0x20) {
-                       int nStart = (2 + nLenLength);
-                       Decode (data, ref nStart, data.Length);
+                       if ((m_nTag & 0x20) == 0x20) {
+                               int nStart = (2 + nLenLength);
+                               Decode (data, ref nStart, data.Length);
+                       }
                }
-       }
 
-       public int Count {
-               get { 
-                       if (elist == null)
-                               return 0;
-                       return elist.Count; 
+               public int Count {
+                       get { 
+                               if (elist == null)
+                                       return 0;
+                               return elist.Count; 
+                       }
                }
-       }
-
-       public byte Tag {
-               get { return m_nTag; }
-       }
 
-       public int Length {
-               get { 
-                       if (m_aValue != null)
-                               return m_aValue.Length; 
-                       else
-                               return 0;
+               public byte Tag {
+                       get { return m_nTag; }
                }
-       }
 
-       public byte[] Value {
-               get { 
-                       if (m_aValue == null)
-                               GetBytes ();
-                       return (byte[]) m_aValue.Clone (); 
-               }
-               set { 
-                       if (value != null)
-                               m_aValue = (byte[]) value.Clone (); 
+               public int Length {
+                       get { 
+                               if (m_aValue != null)
+                                       return m_aValue.Length; 
+                               else
+                                       return 0;
+                       }
                }
-       }
 
-       private bool CompareArray (byte[] array1, byte[] array2)
-       {
-               bool bResult = (array1.Length == array2.Length);
-               if (bResult) {
-                       for (int i = 0; i < array1.Length; i++) {
-                               if (array1[i] != array2[i])
-                                       return false;
+               public byte[] Value {
+                       get { 
+                               if (m_aValue == null)
+                                       GetBytes ();
+                               return (byte[]) m_aValue.Clone (); 
+                       }
+                       set { 
+                               if (value != null)
+                                       m_aValue = (byte[]) value.Clone (); 
                        }
                }
-               return bResult;
-       }
 
-       public bool Equals (byte[] asn1) 
-       {
-               return CompareArray (this.GetBytes (), asn1);
-       }
+               private bool CompareArray (byte[] array1, byte[] array2)
+               {
+                       bool bResult = (array1.Length == array2.Length);
+                       if (bResult) {
+                               for (int i = 0; i < array1.Length; i++) {
+                                       if (array1[i] != array2[i])
+                                               return false;
+                               }
+                       }
+                       return bResult;
+               }
 
-       public bool CompareValue (byte[] aValue
-       {
-               return CompareArray (m_aValue, aValue);
-       }
+               public bool Equals (byte[] asn1
+               {
+                       return CompareArray (this.GetBytes (), asn1);
+               }
 
-       public virtual ASN1 Add (ASN1 asn1) 
-       {
-               if (asn1 != null) {
-                       if (elist == null)
-                               elist = new ArrayList ();
-                       elist.Add (asn1);
+               public bool CompareValue (byte[] value) 
+               {
+                       return CompareArray (m_aValue, value);
                }
-               return asn1;
-       }
 
-       public virtual byte[] GetBytes () 
-       {
-               byte[] val = null;
-               if (m_aValue != null) {
-                       val = m_aValue;
+               public ASN1 Add (ASN1 asn1) 
+               {
+                       if (asn1 != null) {
+                               if (elist == null)
+                                       elist = new ArrayList ();
+                               elist.Add (asn1);
+                       }
+                       return asn1;
                }
-               else if (Count > 0) {
-                       int esize = 0;
-                       ArrayList al = new ArrayList ();
-                       foreach (ASN1 a in elist) {
-                               byte[] item = a.GetBytes ();
-                               al.Add (item);
-                               esize += item.Length;
+
+               public virtual byte[] GetBytes () 
+               {
+                       byte[] val = null;
+                       if (m_aValue != null) {
+                               val = m_aValue;
                        }
-                       val = new byte [esize];
-                       int pos = 0;
-                       for (int i=0; i < elist.Count; i++) {
-                               byte[] item = (byte[]) al[i];
-                               Array.Copy (item, 0, val, pos, item.Length);
-                               pos += item.Length;
+                       else if (Count > 0) {
+                               int esize = 0;
+                               ArrayList al = new ArrayList ();
+                               foreach (ASN1 a in elist) {
+                                       byte[] item = a.GetBytes ();
+                                       al.Add (item);
+                                       esize += item.Length;
+                               }
+                               val = new byte [esize];
+                               int pos = 0;
+                               for (int i=0; i < elist.Count; i++) {
+                                       byte[] item = (byte[]) al[i];
+                                       Buffer.BlockCopy (item, 0, val, pos, item.Length);
+                                       pos += item.Length;
+                               }
                        }
-               }
 
-               byte[] der;
-               int nLengthLen = 0;
-
-               if (val != null) {
-                       int nLength = val.Length;
-                       // special for length > 127
-                       if (nLength > 127) {
-                               if (nLength < 256) {
-                                       der = new byte [3 + nLength];
-                                       Array.Copy (val, 0, der, 3, nLength);
-                                       nLengthLen += 0x81;
-                                       der[2] = (byte)(nLength);
+                       byte[] der;
+                       int nLengthLen = 0;
+
+                       if (val != null) {
+                               int nLength = val.Length;
+                               // special for length > 127
+                               if (nLength > 127) {
+                                       if (nLength <= Byte.MaxValue) {
+                                               der = new byte [3 + nLength];
+                                               Buffer.BlockCopy (val, 0, der, 3, nLength);
+                                               nLengthLen = 0x81;
+                                               der[2] = (byte)(nLength);
+                                       }
+                                       else if (nLength <= UInt16.MaxValue) {
+                                               der = new byte [4 + nLength];
+                                               Buffer.BlockCopy (val, 0, der, 4, nLength);
+                                               nLengthLen = 0x82;
+                                               der[2] = (byte)(nLength >> 8);
+                                               der[3] = (byte)(nLength);
+                                       }
+                                       else if (nLength <= 0xFFFFFF) {
+                                               // 24 bits
+                                               der = new byte [5 + nLength];
+                                               Buffer.BlockCopy (val, 0, der, 5, nLength);
+                                               nLengthLen = 0x83;
+                                               der [2] = (byte)(nLength >> 16);
+                                               der [3] = (byte)(nLength >> 8);
+                                               der [4] = (byte)(nLength);
+                                       }
+                                       else {
+                                               // max (Length is an integer) 32 bits
+                                               der = new byte [6 + nLength];
+                                               Buffer.BlockCopy (val, 0, der, 6, nLength);
+                                               nLengthLen = 0x84;
+                                               der [2] = (byte)(nLength >> 24);
+                                               der [3] = (byte)(nLength >> 16);
+                                               der [4] = (byte)(nLength >> 8);
+                                               der [5] = (byte)(nLength);
+                                       }
                                }
                                else {
-                                       der = new byte [4 + nLength];
-                                       Array.Copy (val, 0, der, 4, nLength);
-                                       nLengthLen += 0x82;
-                                       der[2] = (byte)(nLength / 256);
-                                       der[3] = (byte)(nLength % 256);
+                                       // basic case (no encoding)
+                                       der = new byte [2 + nLength];
+                                       Buffer.BlockCopy (val, 0, der, 2, nLength);
+                                       nLengthLen = nLength;
                                }
+                               if (m_aValue == null)
+                                       m_aValue = val;
                        }
-                       else {
-                               der = new byte [2 + nLength];
-                               Array.Copy (val, 0, der, 2, nLength);
-                               nLengthLen = nLength;
-                       }
-                       if (m_aValue == null)
-                               m_aValue = val;
-               }
-               else
-                       der = new byte[2];
+                       else
+                               der = new byte[2];
 
-               der[0] = m_nTag;
-               der[1] = (byte)nLengthLen;
+                       der[0] = m_nTag;
+                       der[1] = (byte)nLengthLen;
 
-               return der;
-       }
+                       return der;
+               }
 
-       // Note: Recursive
-       protected void Decode (byte[] asn1, ref int anPos, int anLength) 
-       {
-               byte nTag;
-               int nLength;
-               byte[] aValue;
+               // Note: Recursive
+               protected void Decode (byte[] asn1, ref int anPos, int anLength) 
+               {
+                       byte nTag;
+                       int nLength;
+                       byte[] aValue;
 
-               // minimum is 2 bytes (tag + length of 0)
-               while (anPos < anLength - 1) {
-                       int nPosOri = anPos;
-                       DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
+                       // minimum is 2 bytes (tag + length of 0)
+                       while (anPos < anLength - 1) {
+                               int nPosOri = anPos;
+                               DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
 
-                       ASN1 elm = Add (new ASN1 (nTag, aValue));
+                               ASN1 elm = Add (new ASN1 (nTag, aValue));
 
-                       if ((nTag & 0x20) == 0x20) {
-                               int nConstructedPos = anPos;
-                               elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
+                               if ((nTag & 0x20) == 0x20) {
+                                       int nConstructedPos = anPos;
+                                       elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
+                               }
+                               anPos += nLength; // value length
                        }
-                       anPos += nLength; // value length
                }
-       }
 
-       // TLV : Tag - Length - Value
-       protected void DecodeTLV (byte[] asn1, ref int anPos, out byte anTag, out int anLength, out byte[] aValue) 
-       {
-               anTag = asn1 [anPos++];
-               anLength = asn1 [anPos++];
-
-               // special case where L contains the Length of the Length + 0x80
-               if ((anLength & 0x80) == 0x80) {
-                       int nLengthLen = anLength & 0x7F;
-                       anLength = 0;
-                       for (int i = 0; i < nLengthLen; i++)
-                               anLength = anLength * 256 + asn1 [anPos++];
+               // TLV : Tag - Length - Value
+               protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content) 
+               {
+                       tag = asn1 [pos++];
+                       length = asn1 [pos++];
+
+                       // special case where L contains the Length of the Length + 0x80
+                       if ((length & 0x80) == 0x80) {
+                               int nLengthLen = length & 0x7F;
+                               length = 0;
+                               for (int i = 0; i < nLengthLen; i++)
+                                       length = length * 256 + asn1 [pos++];
+                       }
+
+                       content = new byte [length];
+                       Buffer.BlockCopy (asn1, pos, content, 0, length);
                }
 
-               aValue = new byte [anLength];
-               Array.Copy (asn1, anPos, aValue, 0, anLength);
-       }
+               public ASN1 this [int index] {
+                       get {           
+                               try {
+                                       if ((elist == null) || (index >= elist.Count))
+                                               return null;
+                                       return (ASN1) elist [index];
+                               }
+                               catch (ArgumentOutOfRangeException) {
+                                       return null;
+                               }
+                       }
+               }
 
-       public ASN1 this [int index] {
-               get {           
+               public ASN1 Element (int index, byte anTag) 
+               {
                        try {
-                               if (index >= elist.Count)
+                               if ((elist == null) || (index >= elist.Count))
+                                       return null;
+
+                               ASN1 elm = (ASN1) elist [index];
+                               if (elm.Tag == anTag)
+                                       return elm;
+                               else
                                        return null;
-                               return (ASN1) elist [index];
                        }
-                       catch {
+                       catch (ArgumentOutOfRangeException) {
                                return null;
                        }
                }
-       }
 
-       public ASN1 Element (int index, byte anTag) 
-       {
-               try {
-                       if (index >= elist.Count)
-                               return null;
-
-                       ASN1 elm = (ASN1) elist [index];
-                       if (elm.Tag == anTag)
-                               return elm;
-                       else
-                               return null;
+               public override string ToString()
+               {
+                       StringBuilder hexLine = new StringBuilder ();
+            
+                       // Add tag
+                       hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine);
+
+                       // Add length
+                       hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine);
+
+                       // Add value
+                       hexLine.Append ("Value: ");
+                       hexLine.Append (Environment.NewLine);
+                       for (int i = 0; i < Value.Length; i++) {
+                               hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2"));
+                               if ((i+1) % 16 == 0)
+                                       hexLine.AppendFormat (Environment.NewLine);
+                       }
+                       return hexLine.ToString ();
                }
-               catch {
-                       return null;
+
+               public void SaveToFile (string filename)
+               {
+                       if (filename == null)
+                               throw new ArgumentNullException ("filename");
+
+                       using (FileStream fs = File.OpenWrite (filename)) {
+                               byte[] data = GetBytes ();
+                               fs.Write (data, 0, data.Length);
+                               fs.Flush ();
+                               fs.Close ();
+                       }
                }
        }
 }
-
-}