Fix 2.0 attributeusage
[mono.git] / mcs / class / Mono.Security / Mono.Security / ASN1.cs
1 //
2 // ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator
3 //
4 // Authors:
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //      Jesper Pedersen  <jep@itplus.dk>
7 //
8 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
9 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
10 // (C) 2004 IT+ A/S (http://www.itplus.dk)
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Collections;
34 using System.IO;
35 using System.Text;
36
37 namespace Mono.Security {
38
39         // References:
40         // a.   ITU ASN.1 standards (free download)
41         //      http://www.itu.int/ITU-T/studygroups/com17/languages/
42
43 #if INSIDE_CORLIB
44         internal
45 #else
46         public
47 #endif
48         class ASN1 {
49
50                 private byte m_nTag;
51                 private byte[] m_aValue;
52                 private ArrayList elist;
53
54                 public ASN1 () : this (0x00, null) {}
55
56                 public ASN1 (byte tag) : this (tag, null) {}
57
58                 public ASN1 (byte tag, byte[] data) 
59                 {
60                         m_nTag = tag;
61                         m_aValue = data;
62                 }
63
64                 public ASN1 (byte[] data) 
65                 {
66                         m_nTag = data [0];
67
68                         int nLenLength = 0;
69                         int nLength = data [1];
70
71                         if (nLength > 0x80) {
72                                 // composed length
73                                 nLenLength = nLength - 0x80;
74                                 nLength = 0;
75                                 for (int i = 0; i < nLenLength; i++) {
76                                         nLength *= 256;
77                                         nLength += data [i + 2];
78                                 }
79                         }
80                         else if (nLength == 0x80) {
81                                 // undefined length encoding
82                                 throw new NotSupportedException ("Undefined length encoding.");
83                         }
84
85                         m_aValue = new byte [nLength];
86                         Buffer.BlockCopy (data, (2 + nLenLength), m_aValue, 0, nLength);
87
88                         if ((m_nTag & 0x20) == 0x20) {
89                                 int nStart = (2 + nLenLength);
90                                 Decode (data, ref nStart, data.Length);
91                         }
92                 }
93
94                 public int Count {
95                         get { 
96                                 if (elist == null)
97                                         return 0;
98                                 return elist.Count; 
99                         }
100                 }
101
102                 public byte Tag {
103                         get { return m_nTag; }
104                 }
105
106                 public int Length {
107                         get { 
108                                 if (m_aValue != null)
109                                         return m_aValue.Length; 
110                                 else
111                                         return 0;
112                         }
113                 }
114
115                 public byte[] Value {
116                         get { 
117                                 if (m_aValue == null)
118                                         GetBytes ();
119                                 return (byte[]) m_aValue.Clone (); 
120                         }
121                         set { 
122                                 if (value != null)
123                                         m_aValue = (byte[]) value.Clone (); 
124                         }
125                 }
126
127                 private bool CompareArray (byte[] array1, byte[] array2)
128                 {
129                         bool bResult = (array1.Length == array2.Length);
130                         if (bResult) {
131                                 for (int i = 0; i < array1.Length; i++) {
132                                         if (array1[i] != array2[i])
133                                                 return false;
134                                 }
135                         }
136                         return bResult;
137                 }
138
139                 public bool Equals (byte[] asn1) 
140                 {
141                         return CompareArray (this.GetBytes (), asn1);
142                 }
143
144                 public bool CompareValue (byte[] value) 
145                 {
146                         return CompareArray (m_aValue, value);
147                 }
148
149                 public ASN1 Add (ASN1 asn1) 
150                 {
151                         if (asn1 != null) {
152                                 if (elist == null)
153                                         elist = new ArrayList ();
154                                 elist.Add (asn1);
155                         }
156                         return asn1;
157                 }
158
159                 public virtual byte[] GetBytes () 
160                 {
161                         byte[] val = null;
162                         
163                         if (Count > 0) {
164                                 int esize = 0;
165                                 ArrayList al = new ArrayList ();
166                                 foreach (ASN1 a in elist) {
167                                         byte[] item = a.GetBytes ();
168                                         al.Add (item);
169                                         esize += item.Length;
170                                 }
171                                 val = new byte [esize];
172                                 int pos = 0;
173                                 for (int i=0; i < elist.Count; i++) {
174                                         byte[] item = (byte[]) al[i];
175                                         Buffer.BlockCopy (item, 0, val, pos, item.Length);
176                                         pos += item.Length;
177                                 }
178                         } else if (m_aValue != null) {
179                                 val = m_aValue;
180                         }
181
182                         byte[] der;
183                         int nLengthLen = 0;
184
185                         if (val != null) {
186                                 int nLength = val.Length;
187                                 // special for length > 127
188                                 if (nLength > 127) {
189                                         if (nLength <= Byte.MaxValue) {
190                                                 der = new byte [3 + nLength];
191                                                 Buffer.BlockCopy (val, 0, der, 3, nLength);
192                                                 nLengthLen = 0x81;
193                                                 der[2] = (byte)(nLength);
194                                         }
195                                         else if (nLength <= UInt16.MaxValue) {
196                                                 der = new byte [4 + nLength];
197                                                 Buffer.BlockCopy (val, 0, der, 4, nLength);
198                                                 nLengthLen = 0x82;
199                                                 der[2] = (byte)(nLength >> 8);
200                                                 der[3] = (byte)(nLength);
201                                         }
202                                         else if (nLength <= 0xFFFFFF) {
203                                                 // 24 bits
204                                                 der = new byte [5 + nLength];
205                                                 Buffer.BlockCopy (val, 0, der, 5, nLength);
206                                                 nLengthLen = 0x83;
207                                                 der [2] = (byte)(nLength >> 16);
208                                                 der [3] = (byte)(nLength >> 8);
209                                                 der [4] = (byte)(nLength);
210                                         }
211                                         else {
212                                                 // max (Length is an integer) 32 bits
213                                                 der = new byte [6 + nLength];
214                                                 Buffer.BlockCopy (val, 0, der, 6, nLength);
215                                                 nLengthLen = 0x84;
216                                                 der [2] = (byte)(nLength >> 24);
217                                                 der [3] = (byte)(nLength >> 16);
218                                                 der [4] = (byte)(nLength >> 8);
219                                                 der [5] = (byte)(nLength);
220                                         }
221                                 }
222                                 else {
223                                         // basic case (no encoding)
224                                         der = new byte [2 + nLength];
225                                         Buffer.BlockCopy (val, 0, der, 2, nLength);
226                                         nLengthLen = nLength;
227                                 }
228                                 if (m_aValue == null)
229                                         m_aValue = val;
230                         }
231                         else
232                                 der = new byte[2];
233
234                         der[0] = m_nTag;
235                         der[1] = (byte)nLengthLen;
236
237                         return der;
238                 }
239
240                 // Note: Recursive
241                 protected void Decode (byte[] asn1, ref int anPos, int anLength) 
242                 {
243                         byte nTag;
244                         int nLength;
245                         byte[] aValue;
246
247                         // minimum is 2 bytes (tag + length of 0)
248                         while (anPos < anLength - 1) {
249                                 DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
250                                 // sometimes we get trailing 0
251                                 if (nTag == 0)
252                                         continue;
253
254                                 ASN1 elm = Add (new ASN1 (nTag, aValue));
255
256                                 if ((nTag & 0x20) == 0x20) {
257                                         int nConstructedPos = anPos;
258                                         elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
259                                 }
260                                 anPos += nLength; // value length
261                         }
262                 }
263
264                 // TLV : Tag - Length - Value
265                 protected void DecodeTLV (byte[] asn1, ref int pos, out byte tag, out int length, out byte[] content) 
266                 {
267                         tag = asn1 [pos++];
268                         length = asn1 [pos++];
269
270                         // special case where L contains the Length of the Length + 0x80
271                         if ((length & 0x80) == 0x80) {
272                                 int nLengthLen = length & 0x7F;
273                                 length = 0;
274                                 for (int i = 0; i < nLengthLen; i++)
275                                         length = length * 256 + asn1 [pos++];
276                         }
277
278                         content = new byte [length];
279                         Buffer.BlockCopy (asn1, pos, content, 0, length);
280                 }
281
282                 public ASN1 this [int index] {
283                         get {           
284                                 try {
285                                         if ((elist == null) || (index >= elist.Count))
286                                                 return null;
287                                         return (ASN1) elist [index];
288                                 }
289                                 catch (ArgumentOutOfRangeException) {
290                                         return null;
291                                 }
292                         }
293                 }
294
295                 public ASN1 Element (int index, byte anTag) 
296                 {
297                         try {
298                                 if ((elist == null) || (index >= elist.Count))
299                                         return null;
300
301                                 ASN1 elm = (ASN1) elist [index];
302                                 if (elm.Tag == anTag)
303                                         return elm;
304                                 else
305                                         return null;
306                         }
307                         catch (ArgumentOutOfRangeException) {
308                                 return null;
309                         }
310                 }
311
312                 public override string ToString()
313                 {
314                         StringBuilder hexLine = new StringBuilder ();
315             
316                         // Add tag
317                         hexLine.AppendFormat ("Tag: {0} {1}", m_nTag.ToString ("X2"), Environment.NewLine);
318
319                         // Add length
320                         hexLine.AppendFormat ("Length: {0} {1}", Value.Length, Environment.NewLine);
321
322                         // Add value
323                         hexLine.Append ("Value: ");
324                         hexLine.Append (Environment.NewLine);
325                         for (int i = 0; i < Value.Length; i++) {
326                                 hexLine.AppendFormat ("{0} ", Value [i].ToString ("X2"));
327                                 if ((i+1) % 16 == 0)
328                                         hexLine.AppendFormat (Environment.NewLine);
329                         }
330                         return hexLine.ToString ();
331                 }
332
333                 public void SaveToFile (string filename)
334                 {
335                         if (filename == null)
336                                 throw new ArgumentNullException ("filename");
337
338                         using (FileStream fs = File.Create (filename)) {
339                                 byte[] data = GetBytes ();
340                                 fs.Write (data, 0, data.Length);
341                         }
342                 }
343         }
344 }