かつての美貌も今は見る影もなくて(The Pluto Scarab)

The Pluto Scarab「Hash Functions」をPowerShellから再利用

前置き

薦めるに薦められないサイトがあって。

読みやすそうなサイトでしょう? でも上のキャプチャ、クリックして訪れてみた人はわかると思いますが、Wayback Machine ですよ、これ。残念なことに今やこんなことに:

このサイトにはね、お世話になったのです。昔、少人数のユーザしか利用しない「ユーザ登録制アプリケーション」を作ったときに、ユーザIDに利用するのに「32ビットの質の良いハッシュ関数はないか?」と探っていて見つけたの。で、最近気付いたのですけど、Wikipedia の Jenkins Hash ページ:

あ、The Pluto Scarab の検証が再利用されてるのかな? と思って。(リファレンスに言及がないので違うかもしれない。)

C# 環境持ってませんが

さて、本題はここから。

Hash Functionsの検証は、全て C# です、と。でね、アタシ、今の割と買ったばかりの PC には Visual Studio そのものを入れてない。入れる準備だけはしてるけどね、入れてません。こういうの、結構あるのですね。やりたいことそのものズバリの答えが C#、みたいな。その場合にね、「移植してやれ」というのはアリだけど、移植するからには、元のコードで動かないと厳しい。机上だけでの移植は、地獄だ。なんとか PowerShell だけで出来ないかなぁ、と、PowerShell に本気出そうかと思ったその日から考えていた。(そのときに考えてたのがまさにこの The Pluto Scarab。)

でだ。「PowerShell からスクリーンキャプチャ出来ねぇかなぁ?」と思って見つたこれ:

のソースコードを眺めてちょっと驚いてしまった:

Take-ScreenShot.ps1の抜粋
  1 # C# code 
  2 $code = @' 
  3 using System; 
  4 using System.Runtime.InteropServices; 
  5 using System.Drawing; 
  6 using System.Drawing.Imaging; 
  7 namespace ScreenShotDemo 
  8 { 
  9   /// <summary> 
 10   /// Provides functions to capture the entire screen, or a particular window, and save it to a file. 
 11   /// </summary> 
 12   public class ScreenCapture 
 13   { 
 14     /// <summary> 
 15     /// Creates an Image object containing a screen shot the active window 
 16     /// </summary> 
 17     /// <returns></returns> 
 18     public Image CaptureActiveWindow() 
 19     { 
 20       return CaptureWindow( User32.GetForegroundWindow() ); 
 21     } 
 22     /// <summary> 
 23     /// Creates an Image object containing a screen shot of the entire desktop 
 24     /// </summary> 
 25     /// <returns></returns> 
 26     public Image CaptureScreen() 
 27     { 
 28       return CaptureWindow( User32.GetDesktopWindow() ); 
 29     }     
 30     /// <summary> 
 31     /// Creates an Image object containing a screen shot of a specific window 
 32     /// </summary> 
 33     /// <param name="handle">The handle to the window. (In windows forms, this is obtained by the Handle property)</param> 
 34     /// <returns></returns> 
 35     private Image CaptureWindow(IntPtr handle) 
 36     { 
 37       // get te hDC of the target window 
 38       IntPtr hdcSrc = User32.GetWindowDC(handle); 
 39       // get the size 
 40       User32.RECT windowRect = new User32.RECT(); 
 41       User32.GetWindowRect(handle,ref windowRect); 
 42       int width = windowRect.right - windowRect.left; 
 43       int height = windowRect.bottom - windowRect.top; 
 44       // create a device context we can copy to 
 45       IntPtr hdcDest = GDI32.CreateCompatibleDC(hdcSrc); 
 46       // create a bitmap we can copy it to, 
 47       // using GetDeviceCaps to get the width/height 
 48       IntPtr hBitmap = GDI32.CreateCompatibleBitmap(hdcSrc,width,height); 
 49       // select the bitmap object 
 50       IntPtr hOld = GDI32.SelectObject(hdcDest,hBitmap); 
 51       // bitblt over 
 52       GDI32.BitBlt(hdcDest,0,0,width,height,hdcSrc,0,0,GDI32.SRCCOPY); 
 53       // restore selection 
 54       GDI32.SelectObject(hdcDest,hOld); 
 55       // clean up 
 56       GDI32.DeleteDC(hdcDest); 
 57       User32.ReleaseDC(handle,hdcSrc); 
 58       // get a .NET image object for it 
 59       Image img = Image.FromHbitmap(hBitmap); 
 60       // free up the Bitmap object 
 61       GDI32.DeleteObject(hBitmap); 
 62       return img; 
 63     } 
 64     /// <summary> 
 65     /// Captures a screen shot of the active window, and saves it to a file 
 66     /// </summary> 
 67     /// <param name="filename"></param> 
 68     /// <param name="format"></param> 
 69     public void CaptureActiveWindowToFile(string filename, ImageFormat format) 
 70     { 
 71       Image img = CaptureActiveWindow(); 
 72       img.Save(filename,format); 
 73     } 
 74     /// <summary> 
 75     /// Captures a screen shot of the entire desktop, and saves it to a file 
 76     /// </summary> 
 77     /// <param name="filename"></param> 
 78     /// <param name="format"></param> 
 79     public void CaptureScreenToFile(string filename, ImageFormat format) 
 80     { 
 81       Image img = CaptureScreen(); 
 82       img.Save(filename,format); 
 83     }     
 84     
 85     /// <summary> 
 86     /// Helper class containing Gdi32 API functions 
 87     /// </summary> 
 88     private class GDI32 
 89     { 
 90        
 91       public const int SRCCOPY = 0x00CC0020; // BitBlt dwRop parameter 
 92       [DllImport("gdi32.dll")] 
 93       public static extern bool BitBlt(IntPtr hObject,int nXDest,int nYDest, 
 94         int nWidth,int nHeight,IntPtr hObjectSource, 
 95         int nXSrc,int nYSrc,int dwRop); 
 96       [DllImport("gdi32.dll")] 
 97       public static extern IntPtr CreateCompatibleBitmap(IntPtr hDC,int nWidth, 
 98         int nHeight); 
 99       [DllImport("gdi32.dll")] 
100       public static extern IntPtr CreateCompatibleDC(IntPtr hDC); 
101       [DllImport("gdi32.dll")] 
102       public static extern bool DeleteDC(IntPtr hDC); 
103       [DllImport("gdi32.dll")] 
104       public static extern bool DeleteObject(IntPtr hObject); 
105       [DllImport("gdi32.dll")] 
106       public static extern IntPtr SelectObject(IntPtr hDC,IntPtr hObject); 
107     } 
108  
109     /// <summary> 
110     /// Helper class containing User32 API functions 
111     /// </summary> 
112     private class User32 
113     { 
114       [StructLayout(LayoutKind.Sequential)] 
115       public struct RECT 
116       { 
117         public int left; 
118         public int top; 
119         public int right; 
120         public int bottom; 
121       } 
122       [DllImport("user32.dll")] 
123       public static extern IntPtr GetDesktopWindow(); 
124       [DllImport("user32.dll")] 
125       public static extern IntPtr GetWindowDC(IntPtr hWnd); 
126       [DllImport("user32.dll")] 
127       public static extern IntPtr ReleaseDC(IntPtr hWnd,IntPtr hDC); 
128       [DllImport("user32.dll")] 
129       public static extern IntPtr GetWindowRect(IntPtr hWnd,ref RECT rect); 
130       [DllImport("user32.dll")] 
131       public static extern IntPtr GetForegroundWindow();       
132     } 
133   } 
134 } 
135 '@ 
136 #User Add-Type to import the code 
137 add-type $code -ReferencedAssemblies 'System.Windows.Forms','System.Drawing' 
138 #Create the object for the Function 
139 $capture = New-Object ScreenShotDemo.ScreenCapture 

ううん??? これ、C# をダイレクトに使ってない?

実はこれを動かすのにまだ成功できていないんだけど、「C# をそのまま動かせる」ことに興味津々になってしまった。

ここに辿り着いた後で改めて調べなおしてみると、結構これについて触れている人がいた:

どうにも「あまり用途なさそう」とか「C# から PowerShell を呼び出すことは良くありますが」とか、ちがくね、とは思うけど、それは最後に触れるとして。今は Hash Functions の検証プログラムを動かすことに専念。

オリジナルの検証プログラムはここにあります。で、public じゃなかったものを public にしたりとか多少 C# コードも触りつつこんなんなりました:

hash.ps1
  1 $cs = @'
  2 using System;
  3 using System.Security.Cryptography;
  4 using System.Drawing;
  5 
  6 public class Rand : System.Random
  7 {
  8     RandomNumberGenerator rng = new RNGCryptoServiceProvider();
  9     byte[] b = new byte[4];
 10 
 11     protected override double Sample()
 12     {
 13         rng.GetBytes(b);
 14         uint u = BitConverter.ToUInt32(b, 0);
 15         return u / (uint.MaxValue + 1.0);
 16     }
 17 }
 18 
 19 public class ConsoleApp
 20 {
 21     static Rand rand = new Rand();
 22 
 23     [STAThread]
 24     public static void Main(string[] args)
 25     {
 26         HashFunction hf = new SBoxHash();
 27         AvalancheTest(hf);
 28         BitCorrelationTest(hf);
 29         BucketTest(hf);
 30         Console.Write("\r\n>");
 31         Console.ReadLine();
 32     }
 33 
 34     private static void GenerateSBox()
 35     {
 36         byte[] b = new byte[4];
 37         for (int i = 0; i < 256; i++)
 38         {
 39             rand.NextBytes(b);
 40             uint s = BitConverter.ToUInt32(b, 0);
 41             Console.Write("0x{0:X8}, ", s);
 42             if (i % 4 == 3)
 43                 Console.WriteLine();
 44         }
 45     }
 46 
 47     static void BitCorrelationTest(HashFunction hf)
 48     {
 49         int[,] H = new int[32, 32];
 50         for (int i=0; i<10000; i++)
 51         {
 52             byte[] key = null;
 53             switch (rand.Next(3))
 54             {
 55                 case 0:
 56                     key = HashFunction.GetUniformKey();
 57                     break;
 58                 case 1:
 59                     key = HashFunction.GetTextKey();
 60                     break;
 61                 case 2:
 62                     key = HashFunction.GetSparseKey();
 63                     break;
 64             }
 65             uint hash = hf.ComputeHash(key);
 66             for (int j=0; j<32; j++)
 67                 for (int k=0; k<32; k++)
 68                 {
 69                     if (((hash & (1U << j)) != 0) == ((hash & (1U << k)) != 0))
 70                         H[j, k]++;
 71                     else
 72                         H[j, k]--;
 73                 }
 74         }
 75 
 76         using (Bitmap bmp = new Bitmap(255, 255))
 77         using (Graphics g = Graphics.FromImage(bmp))
 78         {
 79             g.DrawLine(Pens.Black, 63, 0, 63, 255);
 80             g.DrawLine(Pens.Black, 127, 0, 127, 255);
 81             g.DrawLine(Pens.Black, 191, 0, 191, 255);
 82             g.DrawLine(Pens.Black, 0, 63, 255, 63);
 83             g.DrawLine(Pens.Black, 0, 127, 255, 127);
 84             g.DrawLine(Pens.Black, 0, 191, 255, 191);
 85             for (int j=0; j<32; j++)
 86                 for (int k=0; k<32; k++)
 87                 {
 88                     Brush br;
 89                     if (H[j,k]<-233)
 90                         br = Brushes.Red;
 91                     else if (H[j,k]<-199)
 92                         br = Brushes.Orange;
 93                     else if (H[j,k]<199)
 94                         br = Brushes.LightGray;
 95                     else if (H[j,k]>233)
 96                         br = Brushes.GreenYellow;
 97                     else 
 98                         br = Brushes.YellowGreen;
 99                     g.FillRectangle(br, 8*j + 1, 8*k + 1, 5, 5);
100                 }
101             bmp.Save(hf.ToString() + "-bits.gif");
102         }
103     }
104 
105     static int[,] matrix;
106 
107     static void InitMatrix(int width)
108     {
109         matrix = new int[width, 32];
110     }
111 
112     static void Tally(HashFunction hf, byte[] key)
113     {
114         uint h0 = hf.ComputeHash(key);
115         if (key.Length <= 6)
116         {
117             for (int i=0; i<key.Length; i++)
118                 for (int j=0; j<8; j++)
119                 {
120                     key[i] ^= (byte) (1 << j);
121                     uint h1 = hf.ComputeHash(key) ^ h0;
122                     key[i] ^= (byte) (1 << j);
123                     int x = i*8 + j;
124                     for (int y=0; y<32; y++)
125                     {
126                         if ((h1 & 1) != 0)
127                             matrix[x, y]++;
128                         h1 >>= 1;
129                     }
130                 }
131         }
132         else
133         {
134             for (int j=0; j<8; j++)
135             {
136                 key[0] ^= (byte) (1 << j);
137                 uint h1 = hf.ComputeHash(key) ^ h0;
138                 key[0] ^= (byte) (1 << j);
139                 for (int y=0; y<32; y++)
140                 {
141                     if ((h1 & 1) != 0)
142                         matrix[j, y]++;
143                     h1 >>= 1;
144                 }
145                 key[key.Length - 1] ^= (byte) (1 << j);
146                 h1 = hf.ComputeHash(key) ^ h0;
147                 key[key.Length - 1] ^= (byte) (1 << j);
148                 for (int y=0; y<32; y++)
149                 {
150                     if ((h1 & 1) != 0)
151                         matrix[j + 8, y]++;
152                     h1 >>= 1;
153                 }
154             }
155         }
156     }
157 
158     static void RenderMatrix(Graphics g, uint trials)
159     {
160         int sc = 8;
161         uint o3 = trials / 3;
162         uint t3 = trials * 2 / 3;
163         for (int x=0; x<matrix.GetLength(0); x++)
164             for (int y=0; y<matrix.GetLength(1); y++)
165             {
166                 Brush br;
167                 if (matrix[x, y] == 0 || matrix[x, y] == trials)
168                     br = Brushes.Red;
169                 else if (matrix[x, y] < o3 || matrix[x, y] > t3)
170                     br = Brushes.Orange;
171                 else
172                     br = Brushes.Green;
173                 g.FillRectangle(br, sc * (matrix.GetLength(1) - 1 - y) + 1, sc * x + 1, sc - 3, sc - 3);
174             }
175     }
176 
177     static void AvalancheTest(HashFunction hf)
178     {
179         int sc = 8;
180 
181         Console.WriteLine("\r\n{0}", hf);
182 
183         Console.WriteLine("\r\nTwo-octet keys");
184         using (Bitmap bmp = new Bitmap(32 * sc - 1, 16 * sc - 1))
185         using (Graphics g = Graphics.FromImage(bmp))
186         {
187             g.DrawLine(Pens.Black, 8*sc-1, 0, 8*sc-1, 16*sc-1);
188             g.DrawLine(Pens.Black, 16*sc-1, 0, 16*sc-1, 16*sc-1);
189             g.DrawLine(Pens.Black, 24*sc-1, 0, 24*sc-1, 16*sc-1);
190             InitMatrix(16);
191             byte[] key = new byte[2];
192             for (int i=0; i<65536; i++)
193             {
194                 key[0] = (byte) (i & 0xFF);
195                 key[1] = (byte) (i >> 8);
196                 Tally(hf, key);
197             }
198             RenderMatrix(g, 65536);
199             bmp.Save(hf.ToString() + "-2.gif");
200         }
201 
202         RandomNumberGenerator rng = new RNGCryptoServiceProvider();
203         Console.WriteLine("Four-octet keys");
204         using (Bitmap bmp = new Bitmap(32 * sc - 1, 32 * sc - 1))
205         using (Graphics g = Graphics.FromImage(bmp))
206         {
207             g.DrawLine(Pens.Black, 8 * sc - 1, 0, 8 * sc - 1, 32 * sc - 1);
208             g.DrawLine(Pens.Black, 16 * sc - 1, 0, 16 * sc - 1, 32 * sc - 1);
209             g.DrawLine(Pens.Black, 24 * sc - 1, 0, 24 * sc - 1, 32 * sc - 1);
210             InitMatrix(32);
211             byte[] key = new byte[4];
212             uint trials = 32768;
213             for (int i = 0; i < trials; i++)
214             {
215                 rng.GetBytes(key);
216                 Tally(hf, key);
217             }
218             RenderMatrix(g, trials);
219             bmp.Save(hf.ToString() + "-4.gif");
220         }
221 
222         Console.WriteLine("Five-octet keys");
223         using (Bitmap bmp = new Bitmap(32 * sc - 1, 40 * sc - 1))
224         using (Graphics g = Graphics.FromImage(bmp))
225         {
226             g.DrawLine(Pens.Black, 8 * sc - 1, 0, 8 * sc - 1, 40 * sc - 1);
227             g.DrawLine(Pens.Black, 16 * sc - 1, 0, 16 * sc - 1, 40 * sc - 1);
228             g.DrawLine(Pens.Black, 24 * sc - 1, 0, 24 * sc - 1, 40 * sc - 1);
229             InitMatrix(40);
230             byte[] key = new byte[5];
231             uint trials = 32768;
232             for (int i = 0; i < trials; i++)
233             {
234                 rng.GetBytes(key);
235                 Tally(hf, key);
236             }
237             RenderMatrix(g, trials);
238             bmp.Save(hf.ToString() + "-5.gif");
239         }
240 
241         Console.WriteLine("Three-octet keys");
242         using (Bitmap bmp = new Bitmap(32 * sc - 1, 24 * sc - 1))
243         using (Graphics g = Graphics.FromImage(bmp))
244         {
245             g.DrawLine(Pens.Black, 8 * sc - 1, 0, 8 * sc - 1, 24 * sc - 1);
246             g.DrawLine(Pens.Black, 16 * sc - 1, 0, 16 * sc - 1, 24 * sc - 1);
247             g.DrawLine(Pens.Black, 24 * sc - 1, 0, 24 * sc - 1, 24 * sc - 1);
248             InitMatrix(40);
249             byte[] key = new byte[3];
250             uint trials = 32768;
251             for (int i = 0; i < trials; i++)
252             {
253                 rng.GetBytes(key);
254                 Tally(hf, key);
255             }
256             RenderMatrix(g, trials);
257             bmp.Save(hf.ToString() + "-3.gif");
258         }
259 
260         rng = new RNGCryptoServiceProvider();
261         Console.WriteLine("256-octet keys");
262         using (Bitmap bmp = new Bitmap(32 * sc - 1, 16 * sc - 1))
263         using (Graphics g = Graphics.FromImage(bmp))
264         {
265             g.DrawLine(Pens.Black, 8*sc-1, 0, 8*sc-1, 16*sc-1);
266             g.DrawLine(Pens.Black, 16*sc-1, 0, 16*sc-1, 16*sc-1);
267             g.DrawLine(Pens.Black, 24*sc-1, 0, 24*sc-1, 16*sc-1);
268             InitMatrix(16);
269             byte[] key = new byte[256];
270             uint trials = 32768;
271             for (int i=0; i<trials; i++)
272             {
273                 rng.GetBytes(key);
274                 Tally(hf, key);
275             }
276             RenderMatrix(g, trials);
277             bmp.Save(hf.ToString() + "-256.gif");
278         }
279     }
280 
281     static void BucketTest(HashFunction hf)
282     {
283         GetRandomKey[] keyfunc = {
284             new GetRandomKey(HashFunction.GetUniformKey),
285             new GetRandomKey(HashFunction.GetTextKey),
286             new GetRandomKey(HashFunction.GetSparseKey),
287         };
288 
289         Console.WriteLine("\r\n{0}", hf);
290         Console.WriteLine("\r\n      Uniform keys    Text keys     Sparse keys");
291         Console.WriteLine("\r\nBits  Lower  Upper   Lower  Upper   Lower  Upper");
292         for (int bits=1; bits<=16; bits++)
293         {
294             Console.Write("{0,2} ", bits);
295             foreach (GetRandomKey getKey in keyfunc)
296             {
297                 Console.Write(" ");
298                 uint mask = (1U << bits) - 1U;
299                 int E = 100;
300                 for (int lsb=0; lsb<32; lsb += 32-bits)
301                 {
302                     if (hf is FNV)
303                     {
304                         if (lsb == 0)
305                             hf = new FNV(bits);
306                         else
307                             hf = new FNV();
308                     }
309                     int[] buckets = new int[1 << bits];
310                     int n = E * buckets.Length;
311                     for (int i=0; i<n; i++)
312                     {
313                         byte[] key = getKey();
314                         uint hash = hf.ComputeHash(key);
315                         uint bucket = (hash >> lsb) & mask;
316                         buckets[bucket]++;
317                     }
318                     double X = 0.0;
319                     double p;
320                     for (int i=0; i<buckets.Length; i++)
321                     {
322                         double err = buckets[i] - E;
323                         X += err * err / E;
324                         p = buckets[i] / (double)n;
325                     }
326                     p = ChiSq(X, buckets.Length - 1);
327                     Console.Write("  {0:0.000}", p);
328                 }
329             }
330             Console.WriteLine();
331         }
332     }
333 
334     static double ChiSq(double x, int n)
335     {
336         double q, t, p;
337         int k, a;
338 
339         if (n == 1 && x > 1000) 
340             return 0.0;
341 
342         if (x > 1000 || n > 1000) 
343         {
344             q = ChiSq((x - n) * (x - n) / (2 * n), 1) / 2;
345             if (x > n) 
346                 return q;
347             else
348                 return 1 - q;
349         }
350 
351         p = Math.Exp(-0.5 * x); 
352         if ((n % 2) == 1) 
353         { 
354             p *= Math.Sqrt(2 * x / Math.PI); 
355         }
356 
357         k = n;
358         while (k >= 2) 
359         { 
360             p *= x / k; 
361             k -= 2;
362         }
363 
364         t = p;
365         q = -1;
366         a = n; 
367         while (q != p) 
368         { 
369             a += 2;
370             t *= x /a; 
371             q = p;
372             p += t;
373         }
374         if (p > 1)
375             p = 1;
376 
377         return 1 - p;
378     }
379 }
380 
381 public delegate byte[] GetRandomKey();
382 
383 public abstract class HashFunction
384 {
385     static RandomNumberGenerator rng = new RNGCryptoServiceProvider();
386     static byte[] b = new byte[4];
387 
388     public abstract uint ComputeHash(byte[] data);
389 
390     private static int GetRandomLength()
391     {
392         rng.GetBytes(b);
393         uint s = BitConverter.ToUInt32(b, 0);
394         double x = 1 - s / (uint.MaxValue + 1.0);
395         return (int) Math.Floor(Math.Sqrt(-800.0 * Math.Log(x)));
396     }
397 
398     public static byte[] GetUniformKey()
399     {
400         // with 8 bits/byte we need at least two octets 
401         int length = GetRandomLength() + 2;
402         byte[] key = new byte[length];
403         rng.GetBytes(key);
404         return key;
405     }
406 
407     public static byte[] GetTextKey()
408     {
409         // with 4.34 bits/byte we need at least 4 octets 
410         int length = GetRandomLength() + 4;
411         byte[] key = new byte[length];
412         rng.GetBytes(key);
413         for (int i=0; i<length; i++)
414             key[i] = (byte) (65 + (key[i] * key[i] * 26) / 65026);
415         return key;
416     }
417 
418     public static byte[] GetSparseKey()
419     {
420         // with 3 bits/byte we need at least 6 octets 
421         int length = GetRandomLength() + 6;
422         byte[] key = new byte[length];
423         rng.GetBytes(key);
424         for (int i=0; i<length; i++)
425             key[i] = (byte)(1 << (key[i] & 7));
426         return key;
427     }
428 }
429 
430 public class SimpleHash : HashFunction
431 {
432     public override uint ComputeHash(byte[] data)
433     {
434         uint hash = 1;
435         foreach (byte b in data)
436             hash = (hash + b) * 0x50003;
437         return hash;
438     }
439 }
440 
441 public class FakeHash : HashFunction
442 {
443     RandomNumberGenerator rng = new RNGCryptoServiceProvider();
444     byte[] b = new byte[4];
445 
446     public override uint ComputeHash(byte[] data)
447     {
448         rng.GetBytes(b);
449         return (uint) (b[0] + 256U * b[1] + 65536U * b[2] + 16777216U * b[3]);
450     }
451 }
452 
453 public class FNV : HashFunction
454 {
455     int shift;
456     uint mask;
457 
458     public FNV()
459     {
460         shift = 0;
461         mask = 0xFFFFFFFF;
462     }
463 
464     public FNV(int bits)
465     {
466         shift = 32 - bits;
467         mask = (1U << shift) - 1U;
468     }
469 
470     public override uint ComputeHash(byte[] data)
471     {
472         uint hash = 2166136261;
473         foreach (byte b in data)
474             hash = (hash * 16777619) ^ b;
475         if (shift == 0)
476             return hash;
477         else
478             return (hash ^ (hash >> shift)) & mask;
479     }
480 }
481 
482 public class ModifiedFNV : HashFunction
483 {
484     public override uint ComputeHash(byte[] data)
485     {
486         const uint p = 16777619;
487         uint hash = 2166136261;
488         foreach (byte b in data)
489             hash = (hash ^ b) * p;
490         hash += hash << 13;
491         hash ^= hash >> 7;
492         hash += hash << 3;
493         hash ^= hash >> 17;
494         hash += hash << 5;
495         return hash;
496     }
497 }
498 
499 public class Jenkins96 : HashFunction
500 {
501     uint a, b, c;
502 
503     void Mix()
504     {
505         a -= b; a -= c; a ^= (c>>13); 
506         b -= c; b -= a; b ^= (a<<8); 
507         c -= a; c -= b; c ^= (b>>13); 
508         a -= b; a -= c; a ^= (c>>12);  
509         b -= c; b -= a; b ^= (a<<16); 
510         c -= a; c -= b; c ^= (b>>5); 
511         a -= b; a -= c; a ^= (c>>3);  
512         b -= c; b -= a; b ^= (a<<10); 
513         c -= a; c -= b; c ^= (b>>15); 
514     }
515 
516     public override uint ComputeHash(byte[] data)
517     {
518         int len = data.Length;
519         a = b = 0x9e3779b9;
520         c = 0;
521         int i = 0;
522         while (i + 12 <= len)
523         {
524             a += (uint)data[i++] |
525                 ((uint)data[i++] << 8) |
526                 ((uint)data[i++] << 16) |
527                 ((uint)data[i++] << 24);
528             b += (uint)data[i++] |
529                 ((uint)data[i++] << 8) |
530                 ((uint)data[i++] << 16) |
531                 ((uint)data[i++] << 24);
532             c += (uint)data[i++] |
533                 ((uint)data[i++] << 8) |
534                 ((uint)data[i++] << 16) |
535                 ((uint)data[i++] << 24);
536             Mix();
537         }
538         c += (uint) len;
539         if (i < len)
540             a += data[i++];
541         if (i < len)
542             a += (uint)data[i++] << 8;
543         if (i < len)
544             a += (uint)data[i++] << 16;
545         if (i < len)
546             a += (uint)data[i++] << 24;
547         if (i < len)
548             b += (uint)data[i++];
549         if (i < len)
550             b += (uint)data[i++] << 8;
551         if (i < len)
552             b += (uint)data[i++] << 16;
553         if (i < len)
554             b += (uint)data[i++] << 24;
555         if (i < len)
556             c += (uint)data[i++] << 8;
557         if (i < len)
558             c += (uint)data[i++] << 16;
559         if (i < len)
560             c += (uint)data[i++] << 24;
561         Mix();
562         return c;
563     }
564 }
565 
566 public class Jenkins32 : HashFunction
567 {
568     uint key;
569 
570     void Mix()
571     {
572         key += (key << 12);
573         key ^= (key >> 22);
574         key += (key << 4);
575         key ^= (key >> 9);
576         key += (key << 10);
577         key ^= (key >> 2);
578         key += (key << 7);
579         key ^= (key >> 12);
580     }
581 
582     public override uint ComputeHash(byte[] data)
583     {
584         key = 1;
585         foreach (byte b in data)
586         {
587             key += b;
588             Mix();
589         }
590         return key;
591     }
592 }
593 
594 public class SHA1Hash : HashFunction
595 {
596     SHA1 sha = SHA1.Create();
597 
598     public override uint ComputeHash(byte[] data)
599     {
600         byte[] hash = sha.ComputeHash(data);
601         return BitConverter.ToUInt32(hash, 0);
602     }
603 }
604 
605 
606 public class MD : HashFunction
607 {
608     MD5 md5 = MD5.Create();
609 
610     public override uint ComputeHash(byte[] data)
611     {
612         byte[] hash = md5.ComputeHash(data);
613         return BitConverter.ToUInt32(hash, 0);
614     }
615 }
616 
617 public class Cheater : HashFunction
618 {
619     public override uint ComputeHash(byte[] data)
620     {
621         byte[] b = new byte[4];
622         int i = 0;
623         while (i < 4 && i < data.Length)
624         {
625             b[i] ^= data[i];
626             i++;
627         }
628         while (i < 4)
629         {
630             b[i] ^= data[i % data.Length];
631             i++;
632         }
633         return BitConverter.ToUInt32(b, 0);
634     }
635 }
636 
637 public class JenkinsOneAtATime : HashFunction
638 {
639     public override uint ComputeHash(byte[] data)
640     {
641         uint hash = 0;
642         foreach (byte b in data)
643         {
644             hash += b;
645             hash += (hash << 10); // 10
646             hash ^= (hash >> 6);  // 6
647         }
648         hash += (hash << 3);  // 3
649         hash ^= (hash >> 11); // 11
650         hash += (hash << 15); // 15
651         return hash;
652     }
653 }
654 
655 public class FletcherChecksum : HashFunction
656 {
657     public override uint ComputeHash(byte[] data)
658     {
659         uint hash = 0;
660         uint sum = 0;
661         foreach (byte b in data)
662         {
663             sum += b;
664             hash += sum;
665         }
666         return hash;
667     }
668 }
669 
670 public class CRC32 : HashFunction
671 {
672     uint[] tab;
673 
674     public CRC32()
675     {
676         Init(0x04c11db7);
677     }
678 
679     public CRC32(uint poly)
680     {
681         Init(poly);
682     }
683 
684     void Init(uint poly)
685     {
686         tab = new uint[256];
687         for (uint i=0; i<256; i++)
688         {
689             uint t = i;
690             for (int j=0; j<8; j++)
691                 if ((t & 1) == 0)
692                     t >>= 1;
693                 else
694                     t = (t >> 1) ^ poly;
695             tab[i] = t;
696         }
697     }
698 
699     public override uint ComputeHash(byte[] data)
700     {
701         uint hash = 0xFFFFFFFF;
702         foreach (byte b in data)
703             hash = (hash << 8) ^ tab[b ^ (hash >> 24)];
704         return ~hash;
705     }
706 }
707 
708 public class SBoxHash : HashFunction
709 {
710     uint[] sbox = new uint[] {
711         0xF53E1837, 0x5F14C86B, 0x9EE3964C, 0xFA796D53,
712         0x32223FC3, 0x4D82BC98, 0xA0C7FA62, 0x63E2C982,
713         0x24994A5B, 0x1ECE7BEE, 0x292B38EF, 0xD5CD4E56,
714         0x514F4303, 0x7BE12B83, 0x7192F195, 0x82DC7300,
715         0x084380B4, 0x480B55D3, 0x5F430471, 0x13F75991,
716         0x3F9CF22C, 0x2FE0907A, 0xFD8E1E69, 0x7B1D5DE8,
717         0xD575A85C, 0xAD01C50A, 0x7EE00737, 0x3CE981E8,
718         0x0E447EFA, 0x23089DD6, 0xB59F149F, 0x13600EC7,
719         0xE802C8E6, 0x670921E4, 0x7207EFF0, 0xE74761B0,
720         0x69035234, 0xBFA40F19, 0xF63651A0, 0x29E64C26,
721         0x1F98CCA7, 0xD957007E, 0xE71DDC75, 0x3E729595,
722         0x7580B7CC, 0xD7FAF60B, 0x92484323, 0xA44113EB,
723         0xE4CBDE08, 0x346827C9, 0x3CF32AFA, 0x0B29BCF1,
724         0x6E29F7DF, 0xB01E71CB, 0x3BFBC0D1, 0x62EDC5B8,
725         0xB7DE789A, 0xA4748EC9, 0xE17A4C4F, 0x67E5BD03,
726         0xF3B33D1A, 0x97D8D3E9, 0x09121BC0, 0x347B2D2C,
727         0x79A1913C, 0x504172DE, 0x7F1F8483, 0x13AC3CF6,
728         0x7A2094DB, 0xC778FA12, 0xADF7469F, 0x21786B7B,
729         0x71A445D0, 0xA8896C1B, 0x656F62FB, 0x83A059B3,
730         0x972DFE6E, 0x4122000C, 0x97D9DA19, 0x17D5947B,
731         0xB1AFFD0C, 0x6EF83B97, 0xAF7F780B, 0x4613138A,
732         0x7C3E73A6, 0xCF15E03D, 0x41576322, 0x672DF292,
733         0xB658588D, 0x33EBEFA9, 0x938CBF06, 0x06B67381,
734         0x07F192C6, 0x2BDA5855, 0x348EE0E8, 0x19DBB6E3,
735         0x3222184B, 0xB69D5DBA, 0x7E760B88, 0xAF4D8154,
736         0x007A51AD, 0x35112500, 0xC9CD2D7D, 0x4F4FB761,
737         0x694772E3, 0x694C8351, 0x4A7E3AF5, 0x67D65CE1,
738         0x9287DE92, 0x2518DB3C, 0x8CB4EC06, 0xD154D38F,
739         0xE19A26BB, 0x295EE439, 0xC50A1104, 0x2153C6A7,
740         0x82366656, 0x0713BC2F, 0x6462215A, 0x21D9BFCE,
741         0xBA8EACE6, 0xAE2DF4C1, 0x2A8D5E80, 0x3F7E52D1,
742         0x29359399, 0xFEA1D19C, 0x18879313, 0x455AFA81,
743         0xFADFE838, 0x62609838, 0xD1028839, 0x0736E92F,
744         0x3BCA22A3, 0x1485B08A, 0x2DA7900B, 0x852C156D,
745         0xE8F24803, 0x00078472, 0x13F0D332, 0x2ACFD0CF,
746         0x5F747F5C, 0x87BB1E2F, 0xA7EFCB63, 0x23F432F0,
747         0xE6CE7C5C, 0x1F954EF6, 0xB609C91B, 0x3B4571BF,
748         0xEED17DC0, 0xE556CDA0, 0xA7846A8D, 0xFF105F94,
749         0x52B7CCDE, 0x0E33E801, 0x664455EA, 0xF2C70414,
750         0x73E7B486, 0x8F830661, 0x8B59E826, 0xBB8AEDCA,
751         0xF3D70AB9, 0xD739F2B9, 0x4A04C34A, 0x88D0F089,
752         0xE02191A2, 0xD89D9C78, 0x192C2749, 0xFC43A78F,
753         0x0AAC88CB, 0x9438D42D, 0x9E280F7A, 0x36063802,
754         0x38E8D018, 0x1C42A9CB, 0x92AAFF6C, 0xA24820C5,
755         0x007F077F, 0xCE5BC543, 0x69668D58, 0x10D6FF74,
756         0xBE00F621, 0x21300BBE, 0x2E9E8F46, 0x5ACEA629,
757         0xFA1F86C7, 0x52F206B8, 0x3EDF1A75, 0x6DA8D843,
758         0xCF719928, 0x73E3891F, 0xB4B95DD6, 0xB2A42D27,
759         0xEDA20BBF, 0x1A58DBDF, 0xA449AD03, 0x6DDEF22B,
760         0x900531E6, 0x3D3BFF35, 0x5B24ABA2, 0x472B3E4C,
761         0x387F2D75, 0x4D8DBA36, 0x71CB5641, 0xE3473F3F,
762         0xF6CD4B7F, 0xBF7D1428, 0x344B64D0, 0xC5CDFCB6,
763         0xFE2E0182, 0x2C37A673, 0xDE4EB7A3, 0x63FDC933,
764         0x01DC4063, 0x611F3571, 0xD167BFAF, 0x4496596F,
765         0x3DEE0689, 0xD8704910, 0x7052A114, 0x068C9EC5,
766         0x75D0E766, 0x4D54CC20, 0xB44ECDE2, 0x4ABC653E,
767         0x2C550A21, 0x1A52C0DB, 0xCFED03D0, 0x119BAFE2,
768         0x876A6133, 0xBC232088, 0x435BA1B2, 0xAE99BBFA,
769         0xBB4F08E4, 0xA62B5F49, 0x1DA4B695, 0x336B84DE,
770         0xDC813D31, 0x00C134FB, 0x397A98E6, 0x151F0E64,
771         0xD9EB3E69, 0xD3C7DF60, 0xD2F2C336, 0x2DDD067B,
772         0xBD122835, 0xB0B3BD3A, 0xB0D54E46, 0x8641F1E4,
773         0xA0B38F96, 0x51D39199, 0x37A6AD75, 0xDF84EE41,
774         0x3C034CBA, 0xACDA62FC, 0x11923B8B, 0x45EF170A, 
775     };
776 
777     public override uint ComputeHash(byte[] data)
778     {
779         uint hash = 0;
780         foreach (byte b in data)
781         {
782             hash ^= sbox[b];
783             hash *= 3;
784         }
785         return hash;
786     }
787 }
788 '@
789 
790 $assemblies = (
791     #"System",
792     #"System.Security.Cryptography",
793     "System.Drawing"
794 )
795 Add-Type -ReferencedAssemblies $assemblies -TypeDefinition $cs -Language CSharp
796 [ConsoleApp]::Main("")

長いけれど、「スクリプト」たる部分は最後の8行くらいだけなのね。残りは全部 C# そのもの。

動かしてみた:

 1 me@host: ~$ /C/WINDOWS/SysWOW64/WindowsPowerShell/v1.0/powershell ./h.ps1
 2 
 3 SBoxHash
 4 
 5 Two-octet keys
 6 Four-octet keys
 7 Five-octet keys
 8 Three-octet keys
 9 256-octet keys
10 
11 SBoxHash
12 
13       Uniform keys    Text keys     Sparse keys
14 
15 Bits  Lower  Upper   Lower  Upper   Lower  Upper
16  1    0.258  0.034   0.777  0.322   0.480  0.888
17  2    0.457  0.936   0.272  0.628   0.167  0.701
18  3    0.496  0.136   0.157  0.233   0.619  0.889
19  4    0.927  0.365   0.826  0.194   0.046  0.879
20  5    0.611  0.185   0.119  0.873   0.360  0.900
21  6    0.195  0.639   0.647  0.008   0.914  0.328
22  7    0.421  0.775   0.844  0.036   0.685  0.595
23  8    0.309  0.270   0.298  0.900   0.589  0.541
24  9    0.350  0.507   0.200  0.579   0.687  0.159
25 10    0.457  0.601   0.447  0.870   0.758  0.596
26 11    0.221  0.516   0.755  0.629   0.501  0.526
27 12    0.042  0.787   0.197  0.025   0.718  0.363
28 13    0.180  0.031   0.277  0.296   0.666  0.680
29 14    0.950  0.482   0.857  0.672   0.161  0.285
30 15    0.194  0.972   0.566  0.377   0.149  0.051
31 16    0.077  0.559   0.797  0.806   0.504  0.858

SBoxHashだけが動くような Main になってるので SBoxHash のだけが動きます。んで、Wikipedia で見えてるような「Avalanche behavior」の絵が、gif で出来上がります:
SBoxHash-4

SBoxHash-5

SBoxHash-256

SBoxHash-bits

SBoxHash-2

SBoxHash-3

うんうん、これはいい。

用途ないことなんかあるもんか

その壱。どんだけ C# 資産があると思ってる? 面白いもん、いっぱいあんぞ。それを、C# 環境がないから諦めるなんか勿体無い。

その弐。C# 環境持っていても。Python や Ruby で開発しやすいのはなぜか? それは、「対話的に即結果を確認出来るから」であろう? それを C# で出来る、とは考えない? (多分これ、VB も出来ると思う。)

その参。C# から PowerShell を「気軽に」呼ぶなよ。C# に限らないが、「スクリプトじゃない言語」で書くからには、性能などを考えてそうしてるんではないの。開発コスト削減で、かつ、実用に問題が出ないならいいけどさ、よくいるんだ、だから Python は遅くてダメだ! と、プロセスの exec だらけのコードを書いといて dis る子たちが。(実際に見たことがあります。馬鹿じゃねーの、と思ったけれど、「馬鹿じゃねーの」ってツッコミが案の定入ってました。) Unix 系の fork/exec は比較的安価ですけどそれでもオーバヘッドは高くて、Windows のそれは悲劇的なほどにオーバヘッド高いです(Unix系の10倍くらい)。だから出来るだけ同じ言語内で閉じるようにして、どうしてもという場合だけ他プロセスを呼び出す、とするのがセオリー。

エッセンシャル WPF:Windows Presentation Foundation (Programmer’s SLECTION―Microsoft .net Development Series)

エッセンシャルWCF:Windows Communication Foundation (Programmer’s Selection―Microsoft .net Development Series)

プログラミングC# 第6版