PageRenderTime 39ms CodeModel.GetById 23ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 0ms

/IronPython_Main/Languages/Ruby/Libraries.LCA_RESTRICTED/OpenSSL/OpenSSL.cs

#
C# | 466 lines | 292 code | 80 blank | 94 comment | 28 complexity | 961d5f6d44ec431172cf431f8022d96b MD5 | raw file
  1/* ****************************************************************************
  2 *
  3 * Copyright (c) Microsoft Corporation. 
  4 *
  5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
  6 * copy of the license can be found in the License.html file at the root of this distribution. If 
  7 * you cannot locate the  Apache License, Version 2.0, please send an email to 
  8 * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
  9 * by the terms of the Apache License, Version 2.0.
 10 *
 11 * You must not remove this notice, or any other, from this software.
 12 *
 13 *
 14 * ***************************************************************************/
 15
 16using System;
 17using System.Runtime.InteropServices;
 18using IronRuby.Builtins;
 19using IronRuby.Runtime;
 20using Microsoft.Scripting.Math;
 21using Microsoft.Scripting.Runtime;
 22using Crypto = System.Security.Cryptography;
 23using System.Text;
 24using System.Security.Cryptography.X509Certificates;
 25using System.Security.Cryptography;
 26using System.Globalization;
 27
 28namespace IronRuby.StandardLibrary.OpenSsl {
 29
 30    [RubyModule("OpenSSL")]
 31    public static class OpenSsl {
 32        // TODO: constants
 33        // Config,HMACError,PKCS12,Random,OPENSSL_VERSION,PKCS7,BN,ConfigError,PKey,Engine,BNError,Netscape,OCSP
 34        // OpenSSLError,CipherError,SSL,VERSION,X509,ASN1,OPENSSL_VERSION_NUMBER,Cipher
 35
 36        [RubyConstant]
 37        public const string OPENSSL_VERSION = "OpenSSL 0.9.8d 28 Sep 2006";
 38
 39        [RubyConstant]
 40        public const double OPENSSL_VERSION_NUMBER = 9470031;
 41
 42        [RubyConstant]
 43        public const string VERSION = "1.0.0";
 44
 45        [RubyModule("Digest")]
 46        public static class DigestFactory {
 47
 48            // TODO: constants:
 49            // SHA224,MDC2,DSS1,SHA512,SHA1,MD5,DSS,SHA384,SHA,MD4,SHA256,DigestError,RIPEMD160,MD2
 50
 51            [RubyClass("Digest")]
 52            public class Digest {
 53                private Crypto.HMAC _algorithm;
 54
 55                public Crypto.HMAC Algorithm {
 56                    get { return _algorithm; }
 57                }
 58
 59                protected Digest() {
 60                }
 61
 62                [RubyConstructor]
 63                public static Digest/*!*/ CreateDigest(RubyClass/*!*/ self, [NotNull]MutableString/*!*/ algorithmName) {
 64                    return Initialize(new Digest(), algorithmName);
 65                }
 66
 67                // Reinitialization. Not called when a factory/non-default ctor is called.
 68                [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)]
 69                public static Digest/*!*/ Initialize(Digest/*!*/ self, [NotNull]MutableString/*!*/ algorithmName) {
 70                    Crypto.HMAC algorithm;
 71
 72#if SILVERLIGHT
 73                    switch (algorithmName.ToString()) {
 74                        case "SHA1": algorithm = new Crypto.HMACSHA1(); break;
 75                        case "SHA256": algorithm = new Crypto.HMACSHA256(); break;
 76                        default: algorithm = null; break;
 77                    }
 78#else
 79                    algorithm = Crypto.HMAC.Create("HMAC" + algorithmName.ConvertToString());
 80#endif
 81
 82                    if (algorithm == null) {
 83                        throw RubyExceptions.CreateRuntimeError("Unsupported digest algorithm ({0}).", algorithmName);
 84                    }
 85
 86                    self._algorithm = algorithm;
 87                    return self;
 88                }
 89
 90                // new(string) -> digest
 91
 92                [RubyMethod("reset")]
 93                public static Digest/*!*/ Reset(Digest/*!*/ self) {
 94                    self._algorithm.Clear();
 95                    return self;
 96                }
 97
 98                // update(string) -> aString
 99                // finish -> aString
100
101                [RubyMethod("name")]
102                public static MutableString/*!*/ Name(Digest/*!*/ self) {
103                    return MutableString.CreateAscii(self._algorithm.HashName);
104                }
105
106                [RubyMethod("digest_size")]
107                public static int Seed(Digest/*!*/ self) {
108                    return self._algorithm.OutputBlockSize;
109                }
110
111                //TODO: Properly disable this with BuildConfig
112                [RubyMethod("digest")]
113                public static MutableString/*!*/ BlankDigest(Digest/*!*/ self) {
114#if !SILVERLIGHT
115                    // TODO: This support only SHA1, It should use self._algorithm but It is not
116                    byte[] blank_data = Encoding.UTF8.GetBytes("");
117                    byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(blank_data);
118                    return MutableString.CreateBinary(hash);
119#else
120            throw new NotSupportedException();
121#endif
122                }
123
124                //TODO: Properly disable with BuildConfig
125                [RubyMethod("hexdigest")]
126                public static MutableString/*!*/ BlankHexDigest(Digest/*!*/ self) {
127#if !SILVERLIGHT
128                    byte[] blank_data = Encoding.UTF8.GetBytes("");
129                    byte[] hash = new SHA1CryptoServiceProvider().ComputeHash(blank_data);
130                    return MutableString.CreateAscii(BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant());
131#else
132            throw new NotSupportedException();
133#endif
134                }
135            }
136        }
137
138        [RubyClass("HMAC")]
139        public class HMAC {
140
141            internal static byte[] Digest(DigestFactory.Digest digest, MutableString key, MutableString data) {
142                // TODO: does MRI really modify the digest object?
143                digest.Algorithm.Key = key.ConvertToBytes();
144                byte[] hash = digest.Algorithm.ComputeHash(data.ConvertToBytes());
145                return hash;
146            }
147
148            [RubyMethod("hexdigest", RubyMethodAttributes.PublicSingleton)]
149            public static MutableString/*!*/ HexDigest(RubyClass/*!*/ self,
150                [NotNull]DigestFactory.Digest/*!*/ digest,
151                [NotNull]MutableString/*!*/ key,
152                [NotNull]MutableString/*!*/ data) {
153
154                byte[] hash = Digest(digest, key, data);
155
156                // TODO (opt):
157                return MutableString.CreateAscii(BitConverter.ToString(hash).Replace("-", "").ToLowerInvariant());
158            }
159
160            [RubyMethod("digest", RubyMethodAttributes.PublicSingleton)]
161            public static MutableString/*!*/ Digest(RubyClass/*!*/ self,
162                [NotNull]DigestFactory.Digest/*!*/ digest,
163                [NotNull]MutableString/*!*/ key,
164                [NotNull]MutableString/*!*/ data) {
165
166                byte[] hash = Digest(digest, key, data);
167
168                return MutableString.CreateBinary(hash);
169            }
170
171            // HMAC.new(key, digest) -> hmac
172            // update(string) -> self
173            // digest -> aString
174            // hexdigest -> aString
175            // reset -> self
176        }
177
178        [RubyModule("Random")]
179        public static class RandomModule {
180
181            // This is a no-op method since our random number generator uses the .NET crypto random number generator
182            // that gets its seed values from the OS
183
184            [RubyMethod("seed", RubyMethodAttributes.PublicSingleton)]
185            public static MutableString/*!*/ Seed(RubyModule/*!*/ self, [DefaultProtocol, NotNull]MutableString/*!*/ seed) {
186                return seed;
187            }
188
189            [RubyMethod("pseudo_bytes", RubyMethodAttributes.PublicSingleton)]
190            [RubyMethod("random_bytes", RubyMethodAttributes.PublicSingleton)]
191            public static MutableString/*!*/ RandomBytes(RubyModule/*!*/ self, [DefaultProtocol]int length) {
192                if (length < 0) {
193                    throw RubyExceptions.CreateArgumentError("negative string size");
194                }
195
196                if (length == 0) {
197                    return MutableString.CreateEmpty();
198                }
199
200                byte[] data = new byte[length];
201                var generator = new Crypto.RNGCryptoServiceProvider();
202                generator.GetBytes(data);
203
204                return MutableString.CreateBinary(data);
205            }
206
207            // add(str, entropy) -> self
208            // load_random_file(filename) -> true
209        }
210
211        [RubyClass("BN")]
212        public class BN {
213
214            // new => aBN
215            // new(bn) => aBN
216            // new(string) => aBN
217            // new(string, 0 | 2 | 10 | 16) => aBN
218
219            [RubyMethod("rand", RubyMethodAttributes.PublicSingleton)]
220            public static BigInteger/*!*/ Rand(RubyClass/*!*/ self, [DefaultProtocol]int bits, [DefaultProtocol, Optional]int someFlag, [Optional]bool otherFlag) { // TODO: figure out someFlag and otherFlag
221                byte[] data = new byte[bits >> 3];
222                var generator = new Crypto.RNGCryptoServiceProvider();
223                generator.GetBytes(data);
224
225                uint[] transformed = new uint[data.Length >> 2];
226                int j = 0;
227                for (int i = 0; i < transformed.Length; ++i) {
228                    transformed[i] = data[j] + (uint)(data[j + 1] << 8) + (uint)(data[j + 2] << 16) + (uint)(data[j + 3] << 24);
229                    j += 4;
230                }
231
232                return new BigInteger(1, transformed);
233            }
234        }
235
236        [RubyModule("X509")]
237        public static class X509 {
238
239            [RubyClass("CertificateError", Extends = typeof(CryptographicException), Inherits = typeof(ExternalException))]
240            public class CryptographicExceptionOps {
241                [RubyConstructor]
242                public static CryptographicException/*!*/ Create(RubyClass/*!*/ self, [DefaultProtocol, DefaultParameterValue(null)]MutableString message) {
243                    CryptographicException result = new CryptographicException(RubyExceptions.MakeMessage(ref message, "Not enought data."));
244                    RubyExceptionData.InitializeException(result, message);
245                    return result;
246                }
247            }
248
249            // TODO: Constants
250
251            [RubyClass("Certificate")]
252            public class Certificate {
253                private X509Certificate/*!*/ _certificate;
254
255                [RubyConstructor]
256                public static Certificate/*!*/ CreateCertificate(RubyClass/*!*/ self) {
257                    return Initialize(new Certificate(), null);
258                }
259
260                [RubyConstructor]
261                public static Certificate/*!*/ CreateCertificate(RubyClass/*!*/ self, MutableString/*!*/ data) {
262                    return Initialize(new Certificate(), data);
263                }
264
265                [RubyMethod("initialize", RubyMethodAttributes.PrivateInstance)]
266                public static Certificate/*!*/ Initialize(Certificate/*!*/ self, MutableString/*!*/ data) {
267                    if (data == null) {
268                        self._certificate = new X509Certificate();
269                    } else {
270                        self._certificate = new X509Certificate(data.ToByteArray());
271                    }
272
273                    return self;
274                }
275
276                // add_extension
277                // check_private_key
278                // extensions
279                // extensions=
280
281                private static string OpenSSLFormat(string x509String) {
282                    string[] pairs = x509String.Split(',');
283                    Array.Sort<string>(pairs);
284
285                    StringBuilder sb = new StringBuilder();
286                    foreach (var val in pairs) {
287                        sb.AppendFormat("/{0}", val.Trim());
288                    }
289
290                    return sb.ToString();
291                }
292
293                // issuer=
294
295                [RubyMethod("issuer")]
296                public static MutableString Issuer(Certificate/*!*/ self) {
297                    if (self._certificate.Handle == IntPtr.Zero) {
298                        return null;
299                    } else {
300                        return MutableString.CreateAscii(OpenSSLFormat(self._certificate.Issuer));
301                    }
302                }
303
304                // not_after => time
305                // not_after=
306                // not_before => time
307                // not_before=
308
309                [RubyMethod("public_key")]
310                public static MutableString PublicKey(Certificate/*!*/ self) {
311                    if (self._certificate.Handle == IntPtr.Zero) {
312                        // TODO: Raise OpenSSL::X509::CertificateError
313                        return MutableString.CreateEmpty();
314                    } else {
315                        return MutableString.CreateAscii(self._certificate.GetPublicKeyString());
316                    }
317                }
318                // public_key=
319
320                private int SerailNumber {
321                    get {
322                        if (_certificate.Handle == IntPtr.Zero) {
323                            return 0;
324                        } else {
325                            return int.Parse(_certificate.GetSerialNumberString(), CultureInfo.InvariantCulture);
326                        }
327                    }
328                }
329
330                [RubyMethod("serial")]
331                public static int Serial(Certificate/*!*/ self) {
332                    return self.SerailNumber;
333                }
334
335                // serial=
336                // sign(key, digest) => self
337                // signature_algorithm
338
339                [RubyMethod("subject")]
340                public static MutableString Subject(Certificate/*!*/ self) {
341                    if (self._certificate.Handle == IntPtr.Zero) {
342                        return null;
343                    } else {
344                        return MutableString.CreateAscii(OpenSSLFormat(self._certificate.Subject));
345                    }
346                }
347
348                // subject=
349                // to_der
350                // to_pem
351
352                [RubyMethod("inspect")]
353                [RubyMethod("to_s")]
354                public static MutableString ToString(RubyContext/*!*/ context, Certificate/*!*/ self) {
355                    using (IDisposable handle = RubyUtils.InfiniteInspectTracker.TrackObject(self)) {
356                        // #<OpenSSL::X509::Certificate subject=, issuer=, serial=0, not_before=nil, not_after=nil>
357                        var result = MutableString.CreateEmpty();
358                        result.Append("#<");
359                        result.Append(context.Inspect(context.GetClassOf(self)));
360
361                        if (handle == null) {
362                            return result.Append(":...>");
363                        }
364                        bool empty = self._certificate.Handle == IntPtr.Zero;
365                        result.AppendFormat(" subject={0}, issuer={1}, serial={2}, not_before=nil, not_after=nil>", 
366                            empty ? "" : OpenSSLFormat(self._certificate.Subject),
367                            empty ? "" : OpenSSLFormat(self._certificate.Issuer),
368                            empty ? 0 : self.SerailNumber
369                        );
370                        return result;
371                    }
372                }
373
374                // to_text
375                // verify
376
377                [RubyMethod("version")]
378                public static int Version(Certificate/*!*/ self) {
379                    if (self._certificate.Handle == IntPtr.Zero) {
380                        return 0;
381                    } else {
382                        return 2;
383                    }
384                }
385
386                // version=
387            }
388
389            [RubyClass("Name")]
390            public class Name {
391                // new => name
392                // new(string) => name
393                // new(dn) => name
394                // new(dn, template) => name
395                // add_entry(oid, value [, type]) => self
396                // to_s => string
397                // to_s(integer) => string
398                // to_a => [[name, data, type], ...]
399                // hash => integer
400                // to_der => string
401                // parse(string) => name
402            }
403        }
404
405        [RubyModule("PKey")]
406        public static class PKey {
407
408            [RubyClass("RSA")]
409            public class RSA {
410                // RSA.new([size | encoded_key] [, pass]) -> rsa
411                // new(2048) -> rsa 
412                // new(File.read("rsa.pem")) -> rsa
413                // new(File.read("rsa.pem"), "mypassword") -> rsa
414                // initialize
415                // generate(size [, exponent]) -> rsa
416                // public? -> true (The return value is always true since every private key is also a public key)
417                // private? -> true | false
418                // to_pem -> aString
419                // to_pem(cipher, pass) -> aString
420                // to_der -> aString
421                // public_encrypt(string [, padding]) -> aString
422                // public_decrypt(string [, padding]) -> aString
423                // private_encrypt(string [, padding]) -> aString
424                // private_decrypt(string [, padding]) -> aString
425                // params -> hash
426                // to_text -> aString
427                // public_key -> aRSA
428                // inspect
429                // to_s
430            }
431        }
432
433        [RubyClass("OpenSSLError"), Serializable]
434        public class OpenSSLError : SystemException {
435            private const string/*!*/ M = "OpenSSL error";
436
437            public OpenSSLError() : this(null, null) { }
438            public OpenSSLError(string message) : this(message, null) { }
439            public OpenSSLError(string message, Exception inner) : base(RubyExceptions.MakeMessage(message, M), inner) { }
440            public OpenSSLError(MutableString message) : base(RubyExceptions.MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); }
441
442#if !SILVERLIGHT
443            protected OpenSSLError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
444                : base(info, context) { }
445#endif
446        }
447
448        [RubyModule("SSL")]
449        public static class SSL {
450            [RubyClass("SSLError"), Serializable]
451            public class SSLError : OpenSSLError {
452                private const string/*!*/ M = "SSL error";
453
454                public SSLError() : this(null, null) { }
455                public SSLError(string message) : this(message, null) { }
456                public SSLError(string message, Exception inner) : base(RubyExceptions.MakeMessage(message, M), inner) { }
457                public SSLError(MutableString message) : base(RubyExceptions.MakeMessage(ref message, M)) { RubyExceptionData.InitializeException(this, message); }
458
459#if !SILVERLIGHT
460                protected SSLError(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
461                    : base(info, context) { }
462#endif
463            }
464        }
465    }
466}