2003-03-04 Sebastien Pouliot <spouliot@videotron.ca>
[mono.git] / mcs / class / corlib / System.Security.Cryptography / CryptoConfig.cs
1 //
2 // CryptoConfig.cs: Handles cryptographic implementations and OIDs.
3 //
4 // Author:
5 //      Sebastien Pouliot (spouliot@motus.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.IO;
13 using System.Reflection;
14 using System.Runtime.CompilerServices;
15 using System.Text;
16
17 namespace System.Security.Cryptography {
18
19 internal class CorlibReader : MiniParser.IReader {
20         private string xml;
21         private int pos;
22
23         public CorlibReader (string filename) 
24         {
25                 try {
26                         StreamReader sr = new StreamReader (filename);
27                         xml = sr.ReadToEnd ();
28                         sr.Close ();
29                 }
30                 catch {
31                         xml = null;
32                 }
33         }
34
35         public int Read () {
36                 try {
37                         return (int) xml [pos++];
38                 }
39                 catch {
40                         return -1;
41                 }
42         }
43 }
44
45 internal class CorlibHandler : MiniParser.IHandler {
46
47         private bool mscorlib;
48         private bool cryptographySettings;
49         private bool cryptoNameMapping;
50         private bool cryptoClasses;
51
52         private Hashtable algo;
53         private Hashtable cryptoClass;
54         private Hashtable nameEntry;
55         private Hashtable oid;
56
57         public CorlibHandler (Hashtable algo, Hashtable oid) 
58         {
59                 this.algo = algo;
60                 this.oid = oid;
61                 cryptoClass = new Hashtable ();
62                 nameEntry = new Hashtable ();
63         }
64
65         public void OnStartParsing (MiniParser parser) {}
66
67         public void OnStartElement (string name, MiniParser.IAttrList attrs) 
68         {
69                 switch (name) {
70                         case "mscorlib":
71                                 mscorlib = true;
72                                 break;
73                         case "cryptographySettings":
74                                 if (mscorlib)
75                                         cryptographySettings = true;
76                                 break;
77                         case "cryptoNameMapping":
78                                 if (cryptographySettings)
79                                         cryptoNameMapping = true;
80                                 break;
81                         case "nameEntry":
82                                 if (cryptoNameMapping) {
83                                         string ename = attrs.Values [0];
84                                         string eclas = attrs.Values [1];
85                                         nameEntry.Add (ename, eclas);
86                                 }
87                                 break;
88                         case "cryptoClasses":
89                                 if (cryptoNameMapping)
90                                         cryptoClasses = true;
91                                 break;
92                         case "cryptoClass":
93                                 if (cryptoClasses)
94                                         cryptoClass.Add (attrs.Names [0], attrs.Values [0]);
95                                 break;
96                         default:
97                                 // unknown tag in parameters
98                                 break;
99                 }
100         }
101
102         public void OnEndElement (string name) 
103         {
104                 switch (name) {
105                         case "mscorlib":
106                                 mscorlib = false;
107                                 break;
108                         case "cryptographySettings":
109                                 cryptographySettings = false;
110                                 break;
111                         case "cryptoNameMapping":
112                                 cryptoNameMapping = false;
113                                 break;
114                         case "cryptoClasses":
115                                 cryptoClasses = false;
116                                 break;
117                         default:
118                                 // unknown tag in parameters
119                                 break;
120                 }
121         }
122
123         public void OnChars (string ch) {}
124
125         public void OnEndParsing (MiniParser parser) 
126         {
127                 foreach (string key in nameEntry.Keys) {
128                         string eclass = (string) nameEntry [key];
129                         
130                         // is it a class or a friendly name ?
131                         object o = cryptoClass [eclass];
132                         if (o != null) {
133                                 // friendly name, so get it's class
134                                 eclass = (string) o;
135                         }
136
137                         if (algo.ContainsKey (key)) 
138                                 algo.Remove (key);
139                         algo.Add (key, eclass);
140                 }
141         }
142 }
143
144
145 public class CryptoConfig {
146
147         static private Hashtable algorithms;
148         static private Hashtable oid;
149
150         private const string defaultNamespace = "System.Security.Cryptography.";
151         private const string defaultSHA1 = defaultNamespace + "SHA1CryptoServiceProvider";
152         private const string defaultMD5 = defaultNamespace + "MD5CryptoServiceProvider";
153         private const string defaultSHA256 = defaultNamespace + "SHA256Managed";
154         private const string defaultSHA384 = defaultNamespace + "SHA384Managed";
155         private const string defaultSHA512 = defaultNamespace + "SHA512Managed";
156         private const string defaultRSA = defaultNamespace + "RSACryptoServiceProvider";
157         private const string defaultDSA = defaultNamespace + "DSACryptoServiceProvider";
158         private const string defaultDES = defaultNamespace + "DESCryptoServiceProvider";
159         private const string default3DES = defaultNamespace + "TripleDESCryptoServiceProvider";
160         private const string defaultRC2 = defaultNamespace + "RC2CryptoServiceProvider";
161         private const string defaultAES = defaultNamespace + "RijndaelManaged";
162         // LAMESPEC: undocumented names in CryptoConfig
163         private const string defaultRNG = defaultNamespace + "RNGCryptoServiceProvider";
164         private const string defaultHMAC = defaultNamespace + "HMACSHA1";
165         private const string defaultMAC3DES = defaultNamespace + "MACTripleDES";
166         // LAMESPEC: undocumented classes (also undocumented in CryptoConfig ;-)
167         private const string defaultDSASigDesc = defaultNamespace + "DSASignatureDescription";
168         private const string defaultRSASigDesc = defaultNamespace + "RSAPKCS1SHA1SignatureDescription";
169         // LAMESPEC: undocumented names in CryptoConfig
170         private const string xmlAssembly = ", System.Security, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null";
171         private const string defaultC14N = defaultNamespace + "Xml.XmlDsigC14NTransform" + xmlAssembly;
172         private const string defaultC14NWithComments = defaultNamespace + "Xml.XmlDsigC14NWithCommentsTransform" + xmlAssembly;
173         private const string defaultBase64 = defaultNamespace + "Xml.XmlDsigBase64Transform" + xmlAssembly;
174         private const string defaultXPath = defaultNamespace + "Xml.XmlDsigXPathTransform" + xmlAssembly;
175         private const string defaultXslt = defaultNamespace + "Xml.XmlDsigXsltTransform" + xmlAssembly;
176         private const string defaultEnveloped = defaultNamespace + "Xml.XmlDsigEnvelopedSignatureTransform" + xmlAssembly;
177         // LAMESPEC: only documentated in ".NET Framework Security" book
178         private const string defaultX509Data = defaultNamespace + "Xml.KeyInfoX509Data" + xmlAssembly;
179         private const string defaultKeyName = defaultNamespace + "Xml.KeyInfoName" + xmlAssembly;
180         private const string defaultKeyValueDSA = defaultNamespace + "Xml.DSAKeyValue" + xmlAssembly;
181         private const string defaultKeyValueRSA = defaultNamespace + "Xml.RSAKeyValue" + xmlAssembly;
182         private const string defaultRetrievalMethod = defaultNamespace + "Xml.KeyInfoRetrievalMethod" + xmlAssembly;
183
184         private const string managedSHA1 = defaultNamespace + "SHA1Managed";
185
186         // Oddly OID seems only available for hash algorithms
187         private const string oidSHA1 = "1.3.14.3.2.26";
188         private const string oidMD5 = "1.2.840.113549.2.5";
189         private const string oidSHA256 = "2.16.840.1.101.3.4.1";
190         private const string oidSHA384 = "2.16.840.1.101.3.4.2";
191         private const string oidSHA512 = "2.16.840.1.101.3.4.3";
192         // LAMESPEC: only documentated in ".NET Framework Security" book
193         private const string oid3DESKeyWrap = "1.2.840.113549.1.9.16.3.6";
194
195         private const string nameSHA1a = "SHA";
196         private const string nameSHA1b = "SHA1";
197         private const string nameSHA1c = "System.Security.Cryptography.SHA1";
198         private const string nameSHA1d = "System.Security.Cryptography.HashAlgorithm";
199         private const string nameMD5a = "MD5";
200         private const string nameMD5b = "System.Security.Cryptography.MD5";
201         private const string nameSHA256a = "SHA256";
202         private const string nameSHA256b = "SHA-256";
203         private const string nameSHA256c = "System.Security.Cryptography.SHA256";
204         private const string nameSHA384a = "SHA384";
205         private const string nameSHA384b = "SHA-384";
206         private const string nameSHA384c = "System.Security.Cryptography.SHA384";
207         private const string nameSHA512a = "SHA512";
208         private const string nameSHA512b = "SHA-512";
209         private const string nameSHA512c = "System.Security.Cryptography.SHA512";
210         private const string nameRSAa = "RSA";
211         private const string nameRSAb = "System.Security.Cryptography.RSA";
212         private const string nameRSAc = "System.Security.Cryptography.AsymmetricAlgorithm";
213         private const string nameDSAa = "DSA";
214         private const string nameDSAb = "System.Security.Cryptography.DSA";
215         private const string nameDESa = "DES";
216         private const string nameDESb = "System.Security.Cryptography.DES";
217         private const string name3DESa = "3DES";
218         private const string name3DESb = "TripleDES";
219         private const string name3DESc = "Triple DES";
220         private const string name3DESd = "System.Security.Cryptography.TripleDES";
221         private const string nameRC2a = "RC2";
222         private const string nameRC2b = "System.Security.Cryptography.RC2";
223         private const string nameAESa = "Rijndael";
224         private const string nameAESb = "System.Security.Cryptography.Rijndael";
225         private const string nameAESc = "System.Security.Cryptography.SymmetricAlgorithm";
226         // LAMESPEC: undocumented names in CryptoConfig
227         private const string nameRNGa = "RandomNumberGenerator";
228         private const string nameRNGb = "System.Security.Cryptography.RandomNumberGenerator";
229         private const string nameKeyHasha = "System.Security.Cryptography.KeyedHashAlgorithm";
230         private const string nameHMACa = "HMACSHA1";
231         private const string nameHMACb = "System.Security.Cryptography.HMACSHA1";
232         private const string nameMAC3DESa = "MACTripleDES";
233         private const string nameMAC3DESb = "System.Security.Cryptography.MACTripleDES";
234         // LAMESPEC: only documentated in ".NET Framework Security" book
235         private const string name3DESKeyWrap = "TripleDESKeyWrap";
236
237         private const string urlXmlDsig = "http://www.w3.org/2000/09/xmldsig#";
238         // LAMESPEC: undocumented URLs in CryptoConfig
239         private const string urlDSASHA1 = urlXmlDsig + "dsa-sha1";                      // no space
240         private const string urlRSASHA1 = urlXmlDsig + "rsa-sha1";                      // no space
241         private const string urlSHA1 = urlXmlDsig + "sha1";                             // no space
242         private const string urlC14N = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; 
243         private const string urlC14NWithComments = "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments";
244         private const string urlBase64 = "http://www.w3.org/2000/09/xmldsig#base64";
245         private const string urlXPath = "http://www.w3.org/TR/1999/REC-xpath-19991116";
246         private const string urlXslt = "http://www.w3.org/TR/1999/REC-xslt-19991116";
247         private const string urlEnveloped = urlXmlDsig + "enveloped-signature";         // no space
248         // LAMESPEC: only documentated in ".NET Framework Security" book
249         private const string urlX509Data = urlXmlDsig + " X509Data";                    // space is required
250         private const string urlKeyName = urlXmlDsig + " KeyName";                      // space is required
251         private const string urlKeyValueDSA = urlXmlDsig + " KeyValue/DSAKeyValue";     // space is required
252         private const string urlKeyValueRSA = urlXmlDsig + " KeyValue/RSAKeyValue";     // space is required
253         private const string urlRetrievalMethod = urlXmlDsig + " RetrievalMethod";      // space is required
254
255         // ??? must we read from the machine.config each time or just at startup ???
256         [MonoTODO ("support OID in machine.config")]
257         static CryptoConfig ()
258         {
259                 algorithms = new Hashtable ();
260                 // see list @ http://msdn.microsoft.com/library/en-us/cpref/html/
261                 // frlrfSystemSecurityCryptographyCryptoConfigClassTopic.asp
262                 algorithms.Add (nameSHA1a, defaultSHA1);
263                 algorithms.Add (nameSHA1b, defaultSHA1);
264                 algorithms.Add (nameSHA1c, defaultSHA1);
265                 algorithms.Add (nameSHA1d, defaultSHA1);
266
267                 algorithms.Add (nameMD5a, defaultMD5);
268                 algorithms.Add (nameMD5b, defaultMD5);
269
270                 algorithms.Add (nameSHA256a, defaultSHA256);
271                 algorithms.Add (nameSHA256b, defaultSHA256);
272                 algorithms.Add (nameSHA256c, defaultSHA256);
273
274                 algorithms.Add (nameSHA384a, defaultSHA384);
275                 algorithms.Add (nameSHA384b, defaultSHA384);
276                 algorithms.Add (nameSHA384c, defaultSHA384);
277
278                 algorithms.Add (nameSHA512a, defaultSHA512);
279                 algorithms.Add (nameSHA512b, defaultSHA512);
280                 algorithms.Add (nameSHA512c, defaultSHA512);
281
282                 algorithms.Add (nameRSAa, defaultRSA);
283                 algorithms.Add (nameRSAb, defaultRSA); 
284                 algorithms.Add (nameRSAc, defaultRSA);
285
286                 algorithms.Add (nameDSAa, defaultDSA);  
287                 algorithms.Add (nameDSAb, defaultDSA);  
288         
289                 algorithms.Add (nameDESa, defaultDES);
290                 algorithms.Add (nameDESb, defaultDES);
291         
292                 algorithms.Add (name3DESa, default3DES);
293                 algorithms.Add (name3DESb, default3DES);
294                 algorithms.Add (name3DESc, default3DES);
295                 algorithms.Add (name3DESd, default3DES);
296         
297                 algorithms.Add (nameRC2a, defaultRC2);
298                 algorithms.Add (nameRC2b, defaultRC2);
299
300                 algorithms.Add (nameAESa, defaultAES);  
301                 algorithms.Add (nameAESb, defaultAES);
302                 // LAMESPEC SymmetricAlgorithm documented as TripleDESCryptoServiceProvider
303                 algorithms.Add (nameAESc, defaultAES);
304
305                 // LAMESPEC These names aren't documented but (hint) the classes also have
306                 // static Create methods. So logically they should (and are) here.
307                 algorithms.Add (nameRNGa, defaultRNG);
308                 algorithms.Add (nameRNGb, defaultRNG);
309                 algorithms.Add (nameKeyHasha, defaultHMAC);
310                 algorithms.Add (nameHMACa, defaultHMAC);
311                 algorithms.Add (nameHMACb, defaultHMAC);
312                 algorithms.Add (nameMAC3DESa, defaultMAC3DES);
313                 algorithms.Add (nameMAC3DESb, defaultMAC3DES);
314
315                 // LAMESPEC These URLs aren't documented but (hint) installing the WSDK
316                 // add some of the XMLDSIG urls into machine.config (and they make a LOT
317                 // of sense for implementing XMLDSIG in System.Security.Cryptography.Xml)
318                 algorithms.Add (urlDSASHA1, defaultDSASigDesc); 
319                 algorithms.Add (urlRSASHA1, defaultRSASigDesc);
320                 algorithms.Add (urlSHA1, defaultSHA1);
321                 algorithms.Add (urlC14N, defaultC14N);
322                 algorithms.Add (urlC14NWithComments, defaultC14NWithComments);
323                 algorithms.Add (urlBase64, defaultBase64);
324                 algorithms.Add (urlXPath, defaultXPath);
325                 algorithms.Add (urlXslt, defaultXslt);
326                 algorithms.Add (urlEnveloped, defaultEnveloped);
327                 // LAMESPEC: only documentated in ".NET Framework Security" book
328                 algorithms.Add (urlX509Data, defaultX509Data);
329                 algorithms.Add (urlKeyName, defaultKeyName);
330                 algorithms.Add (urlKeyValueDSA, defaultKeyValueDSA);
331                 algorithms.Add (urlKeyValueRSA, defaultKeyValueRSA);
332                 algorithms.Add (urlRetrievalMethod, defaultRetrievalMethod);
333
334                 oid = new Hashtable ();
335                 // comments here are to match with MS implementation (but not with doc)
336                 // LAMESPEC: only HashAlgorithm seems to have their OID included
337                 oid.Add (defaultSHA1, oidSHA1);
338                 oid.Add (managedSHA1, oidSHA1);
339                 oid.Add (nameSHA1b, oidSHA1);
340                 oid.Add (nameSHA1c, oidSHA1);
341
342                 oid.Add (defaultMD5, oidMD5);
343                 oid.Add (nameMD5a, oidMD5);
344                 oid.Add (nameMD5b, oidMD5);
345
346                 oid.Add (defaultSHA256, oidSHA256);
347                 oid.Add (nameSHA256a, oidSHA256);
348                 oid.Add (nameSHA256c, oidSHA256);
349
350                 oid.Add (defaultSHA384, oidSHA384);
351                 oid.Add (nameSHA384a, oidSHA384);
352                 oid.Add (nameSHA384c, oidSHA384);
353
354                 oid.Add (defaultSHA512, oidSHA512);
355                 oid.Add (nameSHA512a, oidSHA512);
356                 oid.Add (nameSHA512c, oidSHA512);
357
358                 // surprise! documented in ".NET Framework Security" book
359                 oid.Add (name3DESKeyWrap, oid3DESKeyWrap);
360
361                 // Add/modify the config as specified by machine.config
362                 string config = GetMachineConfigPath ();
363                 // debug @"C:\mono-0.17\install\etc\mono\machine.config";
364                 if (config != null) {
365                         MiniParser parser = new MiniParser ();
366                         CorlibReader reader = new CorlibReader (config);
367                         CorlibHandler handler = new CorlibHandler (algorithms, oid);
368                         parser.Parse (reader, handler);
369                 }
370         }
371
372         // managed version of "get_machine_config_path"
373         private static string GetMachineConfigPath () 
374         {
375                 string env = Environment.GetEnvironmentVariable ("MONO_CONFIG");
376                 if (env != null)
377                         return env;
378                 env = Environment.GetEnvironmentVariable ("MONO_BASEPATH");
379                 if (env == null)
380                         return null;
381
382                 StringBuilder sb = new StringBuilder ();
383                 sb.Append (env);
384                 sb.Append (Path.DirectorySeparatorChar);
385                 sb.Append ("etc");
386                 sb.Append (Path.DirectorySeparatorChar);
387                 sb.Append ("mono");
388                 sb.Append (Path.DirectorySeparatorChar);
389                 sb.Append ("machine.config");
390                 return sb.ToString ();
391         }
392
393         public static object CreateFromName (string name)
394         {
395                 return CreateFromName (name, null);
396         }
397
398         public static object CreateFromName (string name, object[] args)
399         {
400                 if (name == null)
401                         throw new ArgumentNullException ();
402         
403                 try {
404                         Type algoClass = null;
405                         string algo = (string) algorithms [name];
406                         // do we have an entry
407                         if (algo == null)
408                                 algo = name;
409                         algoClass = Type.GetType (algo);
410                         // call the constructor for the type
411                         return Activator.CreateInstance (algoClass, args);
412                 }
413                 catch {
414                         // method deosn't throw any exception
415                         return null;
416                 }
417         }
418
419         // encode (7bits array) number greater than 127
420         private static byte[] EncodeLongNumber (long x)
421         {
422                 // for MS BCL compatibility
423                 // comment next two lines to remove restriction
424                 if ((x > Int32.MaxValue) || (x < Int32.MinValue))
425                         throw new OverflowException("part of OID doesn't fit in Int32");
426
427                 long y = x;
428                 // number of bytes required to encode this number
429                 int n = 1;
430                 while (y > 0x7F) {
431                         y = y >> 7;
432                         n++;
433                 }
434                 byte[] num = new byte [n];
435                 // encode all bytes 
436                 for (int i = 0; i < n; i++) {
437                         y = x >> (7 * i);
438                         y = y & 0x7F;
439                         if (i != 0)
440                                 y += 0x80;
441                         num[n-i-1] = Convert.ToByte (y);
442                 }
443                 return num;
444         }
445
446         public static byte[] EncodeOID (string str)
447         {
448                 char[] delim = { '.' };
449                 string[] parts = str.Split (delim);
450                 // according to X.208 n is always at least 2
451                 if (parts.Length < 2)
452                         throw new CryptographicUnexpectedOperationException ();
453                 // we're sure that the encoded OID is shorter than its string representation
454                 byte[] oid = new byte [str.Length];
455                 // now encoding value
456                 try {
457                         byte part0 = Convert.ToByte (parts [0]);
458                         // OID[0] > 2 is invalid but "supported" in MS BCL
459                         // uncomment next line to trap this error
460                         // if (part0 > 2) throw new CryptographicUnexpectedOperationException ();
461                         byte part1 = Convert.ToByte (parts [1]);
462                         // OID[1] >= 40 is illegal for OID[0] < 2 because of the % 40
463                         // however the syntax is "supported" in MS BCL
464                         // uncomment next 2 lines to trap this error
465                         //if ((part0 < 2) && (part1 >= 40))
466                         //      throw new CryptographicUnexpectedOperationException ();
467                         oid[2] = Convert.ToByte (part0 * 40 + part1);
468                 }
469                 catch {
470                         throw new CryptographicUnexpectedOperationException ();
471                 }
472                 int j = 3;
473                 for (int i = 2; i < parts.Length; i++) {
474                         long x = Convert.ToInt64( parts [i]);
475                         if (x > 0x7F) {
476                                 byte[] num = EncodeLongNumber (x);
477                                 Array.Copy(num, 0, oid, j, num.Length);
478                                 j += num.Length;
479                         }
480                         else
481                                 oid[j++] = Convert.ToByte (x);
482                 }
483
484                 int k = 2;
485                 // copy the exact number of byte required
486                 byte[] oid2 = new byte [j];
487                 oid2[0] = 0x06; // always - this tag means OID
488                 // Length (of value)
489                 if (j > 0x7F) {
490                         // for compatibility with MS BCL
491                         throw new CryptographicUnexpectedOperationException ("OID > 127 bytes");
492                         // comment exception and uncomment next 3 lines to remove restriction
493                         //byte[] num = EncodeLongNumber (j);
494                         //Array.Copy (num, 0, oid, j, num.Length);
495                         //k = num.Length + 1;
496                 }
497                 else
498                         oid2 [1] = Convert.ToByte (j - 2); 
499
500                 System.Array.Copy (oid, k, oid2, k, j - k);
501                 return oid2;
502         }
503
504         public static string MapNameToOID (string name)
505         {
506                 if (name == null)
507                         throw new ArgumentNullException ("name");
508
509                 return (string)oid [name];
510         }
511 }
512
513 }