using Ruby; using Ruby.Runtime; using Ruby.Builtins; using Microsoft.Scripting.Ast; using Microsoft.Scripting.Actions; using Microsoft.Scripting.Runtime; using System.Security.Cryptography; [RubyModule("Digest")] public static class Digest { [RubyClass("MD5")] public class MD5: Base { public MD5() { algorithm = System.Security.Cryptography.MD5.Create(); } } [RubyClass("Base")] public class Base: DigestClass, IDuplicable { public HashAlgorithm algorithm; public MutableString buffer = new MutableString(); [RubyMethod("<<")] [RubyMethod("update")] public static object digest_base_update(CodeContext context, Base self, MutableString str) { self.buffer.Append(str); return self; } [RubyMethod("finish", RubyMethodAttributes.PrivateInstance)] public static MutableString finish(CodeContext context, Base self) { byte[] input = self.buffer.ToByteArray(); byte[] hash = self.algorithm.ComputeHash(input); return new MutableString(hash, 0, hash.Length); } [RubyMethod("reset")] public static object reset(CodeContext context, Base self) { self.buffer = new MutableString(); self.algorithm.Initialize(); return self; } //TODO: should this be initialize_copy method? public void InitializeFrom(object copy_from) { buffer = new MutableString(((Base)copy_from).buffer); } } [RubyClass("Class"), Includes(typeof(Instance))] public class DigestClass { [RubyMethod("digest", RubyMethodAttributes.PublicSingleton)] public static MutableString digest(CodeContext context, RubyClass klass, MutableString str) { object obj = RubySites.Allocate(context, klass); return Call_digest(context, obj, str); } [RubyMethod("hexdigest", RubyMethodAttributes.PublicSingleton)] public static MutableString hexdigest(CodeContext context, RubyClass klass, MutableString str) { return hexencode(Call_digest(context, klass, str)); } #region Helpers internal static MutableString Bytes2Hex(byte[] bytes) { return new MutableString(System.BitConverter.ToString(bytes).Replace("-", "").ToLower()); } internal static MutableString hexencode(MutableString str) { return Bytes2Hex(str.ToByteArray()); } #endregion #region DynamicSites private static DynamicSite DigestSharedSite = DynamicSite.Create(RubySites.InstanceCallAction("digest", ArgumentKind.Simple)); private static MutableString Call_digest(CodeContext/*!*/ context, object self, MutableString str) { return DigestSharedSite.Invoke(context, self, str); } #endregion } [RubyModule("Instance")] public class Instance { [RubyMethod("digest")] public static MutableString digest(CodeContext context, object self, MutableString str) { Call_reset(context, self); Call_update(context, self, str); MutableString value = Call_finish(context, self); Call_reset(context, self); return value; } [RubyMethod("hexdigest")] public static MutableString hexdigest(CodeContext context, Base self) { object clone = Kernel.Clone(context, self); MutableString str = Call_finish(context, clone); Call_reset(context, clone); return DigestClass.hexencode(str); } #region DynamicSites private static DynamicSite ResetSharedSite = DynamicSite.Create(RubySites.InstanceCallAction("reset")); private static object Call_reset(CodeContext/*!*/ context, object self) { return ResetSharedSite.Invoke(context, self); } private static DynamicSite UpdateSharedSite = DynamicSite.Create(RubySites.InstanceCallAction("update", ArgumentKind.Simple)); private static object Call_update(CodeContext/*!*/ context, object self, MutableString str) { return UpdateSharedSite.Invoke(context, self, str); } private static DynamicSite FinishSharedSite = DynamicSite.Create(RubySites.InstanceCallAction("finish")); private static MutableString Call_finish(CodeContext/*!*/ context, object self) { return FinishSharedSite.Invoke(context, self); } #endregion } }