2003-02-13 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / tools / ASN1.cs
1 //
2 // ASN1.cs: Abstract Syntax Notation 1 - micro-parser and generator
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.Security.Cryptography;
13
14 namespace Mono.Security.ASN1 {
15
16 // References:
17 // a.   ITU ASN.1 standards (free download)
18 //      http://www.itu.int/ITU-T/studygroups/com17/languages/
19
20 internal class ASN1 {
21         protected byte m_nTag;
22         protected byte[] m_aValue;
23         protected ArrayList elist;
24
25         public ASN1 () : this (0x00, null) {}
26
27         public ASN1 (byte tag) : this (tag, null) {}
28
29         public ASN1 (byte tag, byte[] data) 
30         {
31                 m_nTag = tag;
32                 m_aValue = data;
33                 elist = new ArrayList ();
34         }
35
36         public ASN1 (byte[] data) 
37         {
38                 elist = new ArrayList ();
39                 m_nTag = data [0];
40
41                 int nLenLength = 0;
42                 int nLength = data [1];
43
44                 if (nLength > 0x80) {
45                         // composed length
46                         nLenLength = nLength - 0x80;
47                         nLength = 0;
48                         for (int i = 0; i < nLenLength; i++) {
49                                 nLength *= 256;
50                                 nLength += data [i + 2];
51                         }
52                 }
53
54                 m_aValue = new byte [nLength];
55                 Array.Copy (data, (2 + nLenLength), m_aValue, 0, nLength);
56
57                 int nStart = 0;
58                 Decode (data, ref nStart, data.Length);
59         }
60
61         public int Count {
62                 get { return elist.Count; }
63         }
64
65         public byte Tag {
66                 get { return m_nTag; }
67         }
68
69         public int Length {
70                 get { 
71                         if (m_aValue != null)
72                                 return m_aValue.Length; 
73                         else
74                                 return 0;
75                 }
76         }
77
78         public byte[] Value {
79                 get { return (byte[]) m_aValue.Clone (); }
80                 set { 
81                         if (value != null)
82                                 m_aValue = (byte[]) value.Clone (); 
83                 }
84         }
85
86         public bool CompareValue (byte[] aValue) 
87         {
88                 bool bResult = (m_aValue.Length == aValue.Length);
89                 if (bResult) {
90                         for (int i = 0; i < m_aValue.Length; i++) {
91                                 if (m_aValue[i] != aValue[i])
92                                         return false;
93                         }
94                 }
95                 return bResult;
96         }
97
98         public virtual void Add (ASN1 asn1) 
99         {
100                 if (asn1 != null)
101                         elist.Add (asn1);
102         }
103
104         public virtual byte[] GetBytes () 
105         {
106                 byte[] val = null;
107                 if (m_aValue != null) {
108                         val = m_aValue;
109                 }
110                 else if (elist.Count > 0) {
111                         int esize = 0;
112                         ArrayList al = new ArrayList ();
113                         foreach (ASN1 a in elist) {
114                                 byte[] item = a.GetBytes ();
115                                 al.Add (item);
116                                 esize += item.Length;
117                         }
118                         val = new byte [esize];
119                         int pos = 0;
120                         for (int i=0; i < elist.Count; i++) {
121                                 byte[] item = (byte[]) al[i];
122                                 Array.Copy (item, 0, val, pos, item.Length);
123                                 pos += item.Length;
124                         }
125                 }
126
127                 byte[] der;
128                 int nLengthLen = 0;
129
130                 if (val != null) {
131                         int nLength = val.Length;
132                         // special for length > 127
133                         if (nLength > 127) {
134                                 if (nLength < 256) {
135                                         der = new byte [3 + nLength];
136                                         Array.Copy (val, 0, der, 3, nLength);
137                                         nLengthLen += 0x81;
138                                         der[2] = (byte)(nLength);
139                                 }
140                                 else {
141                                         der = new byte [4 + nLength];
142                                         Array.Copy (val, 0, der, 4, nLength);
143                                         nLengthLen += 0x82;
144                                         der[2] = (byte)(nLength / 256);
145                                         der[3] = (byte)(nLength % 256);
146                                 }
147                         }
148                         else {
149                                 der = new byte [2 + nLength];
150                                 Array.Copy (val, 0, der, 2, nLength);
151                                 nLengthLen = nLength;
152                         }
153                 }
154                 else
155                         der = new byte[2];
156
157                 der[0] = m_nTag;
158                 der[1] = (byte)nLengthLen;
159
160                 return der;
161         }
162
163         // Note: Recursive
164         protected void Decode (byte[] asn1, ref int anPos, int anLength) 
165         {
166                 byte nTag;
167                 int nLength;
168                 byte[] aValue;
169
170                 // minimum is 2 bytes (tag + length of 0)
171                 while (anPos < anLength - 1) {
172                         int nPosOri = anPos;
173                         DecodeTLV (asn1, ref anPos, out nTag, out nLength, out aValue);
174
175                         ASN1 elm = new ASN1 (nTag, aValue);
176                         elist.Add (elm);
177
178                         if ((nTag & 0x20) == 0x20) {
179                                 int nConstructedPos = anPos;
180                                 elm.Decode (asn1, ref nConstructedPos, nConstructedPos + nLength);
181                         }
182                         anPos += nLength; // value length
183                 }
184         }
185
186         // TLV : Tag - Length - Value
187         protected void DecodeTLV (byte[] asn1, ref int anPos, out byte anTag, out int anLength, out byte[] aValue) 
188         {
189                 anTag = asn1 [anPos++];
190                 anLength = asn1 [anPos++];
191
192                 // special case where L contains the Length of the Length + 0x80
193                 if ((anLength & 0x80) == 0x80) {
194                         int nLengthLen = anLength & 0x7F;
195                         anLength = 0;
196                         for (int i = 0; i < nLengthLen; i++) {
197                                 anLength = anLength * 256 + asn1 [anPos++];
198                         }
199                 }
200
201                 aValue = new byte [anLength];
202                 Array.Copy (asn1, anPos, aValue, 0, anLength);
203         }
204
205         public ASN1 Element (int index) 
206         {
207                 try {
208                         return (ASN1) elist [index];
209                 }
210                 catch {
211                         return null;
212                 }
213         }
214
215         public ASN1 Element (int anIndex, byte anTag) 
216         {
217                 try {
218                         ASN1 elm = (ASN1) elist [anIndex];
219                         if (elm.Tag == anTag)
220                                 return elm;
221                         else
222                                 return null;
223                 }
224                 catch {
225                         return null;
226                 }
227         }
228 }
229
230 internal class OID : ASN1 {
231         public OID (string oid) : base (CryptoConfig.EncodeOID (oid)) {}
232 }
233
234 }