This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / class / corlib / Test / System.Security.Cryptography / PasswordDeriveBytesTest.cs
1 //
2 // PasswordDeriveTest.cs - NUnit Test Cases for PasswordDerive
3 //
4 // Author:
5 //      Sebastien Pouliot (sebastien@ximian.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // (C) 2004 Novell (http://novell.com)
9 //
10
11 using NUnit.Framework;
12 using System;
13 using System.Security.Cryptography;
14
15 namespace MonoTests.System.Security.Cryptography {
16
17 // References:
18 // a.   PKCS#5: Password-Based Cryptography Standard 
19 //      http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html
20
21 [TestFixture]
22 public class PasswordDeriveBytesTest : Assertion {
23
24         public void AssertEquals (string msg, byte[] array1, byte[] array2)
25         {
26                 AllTests.AssertEquals (msg, array1, array2);
27         }
28
29         static byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
30
31         static int ToInt32LE(byte [] bytes, int offset)
32         {
33                 return (bytes[offset + 3] << 24) | (bytes[offset + 2] << 16) | (bytes[offset + 1] << 8) | bytes[offset];
34         }
35
36         // generate the key up to HashSize and reset between operations
37         public void ShortRun(string msg, PasswordDeriveBytes pd, byte[] finalKey)
38         {
39                 for (int i=0; i < finalKey.Length; i++) {
40                         int j = 0;
41                         bool compare = true;
42                         byte[] key = pd.GetBytes (i+1);
43                         for (; j < i; j++) {
44                                 if (finalKey [j] != key[j]) {
45                                         compare = false;
46                                         break;
47                                 }
48                         }
49                         Assert (msg + " #" + j, compare);
50                         pd.Reset ();
51                 }
52         }
53
54         // generate a key at least 1000 bytes and don't reset between operations
55         public void LongRun(string msg, PasswordDeriveBytes pd, byte[] finalKey)
56         {
57                 int bloc = finalKey.Length;
58                 int iter = (int) ((1000 + bloc - 1) / bloc);
59                 byte[] pass = null;
60                 for (int i=0; i < iter; i++) {
61                         pass = pd.GetBytes (bloc);
62                 }
63                 AssertEquals (msg, pass, finalKey);
64         }
65
66         public void Run (string password, byte[] salt, string hashname, int iterations, int getbytes, int lastFourBytes) 
67         {
68                 PasswordDeriveBytes pd = new PasswordDeriveBytes (password, salt, hashname, iterations);
69                 byte[] key = pd.GetBytes (getbytes);
70                 string msg = "[pwd=" + password;
71                 msg += ", salt=" + ((salt == null) ? "null" : salt.Length.ToString ());
72                 msg += ", hash=" + hashname;
73                 msg += ", iter=" + iterations;
74                 msg += ", get=" + getbytes + "]";
75                 AssertEquals (msg, lastFourBytes, ToInt32LE (key, key.Length - 4));
76         }
77
78         [Test]
79         [ExpectedException (typeof (IndexOutOfRangeException))]
80         public void TooShort () 
81         {
82                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", null, "SHA1", 1);
83                 byte[] key = pd.GetBytes (0);
84         }
85
86         public void TooLong (string hashName, int size, int lastFourBytes) 
87         {
88                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("toolong", null, hashName, 1);
89                 
90                 // this should work (we check the last four devired bytes to be sure)
91                 byte[] key = pd.GetBytes (size);
92                 AssertEquals ("Last 4 bytes", lastFourBytes, ToInt32LE (key, size - 4));
93
94                 // but we can't get another byte from it!
95                 try {
96                         key = pd.GetBytes (1);
97                         Fail ("Expected CryptographicException but got none");
98                 }
99                 catch (CryptographicException) {
100                         // LAMESPEC: no limit is documented
101                 }
102                 catch (Exception e) {
103                         Fail ("Expected CryptographicException but got " + e.ToString ());
104                 }
105         }
106
107         [Test]
108         public void TooLong () 
109         {
110                 // 1000 times hash length is the maximum
111                 TooLong ("MD5",    16000, 1135777886);
112                 TooLong ("SHA1",   20000, -1167918035);
113                 TooLong ("SHA256", 32000, -358766048);
114                 TooLong ("SHA384", 48000, 1426370534);
115                 TooLong ("SHA512", 64000, -1763233543);
116         }
117
118         [Test]
119         public void OneIteration () 
120         {
121                 // (1) size of hash, (2) size of 2 hash
122                 Run ("password", salt, "MD5",    1, 16, 986357363);
123                 Run ("monomono", null, "MD5",    1, 32, -1092059875);
124                 Run ("password", salt, "SHA1",   1, 20, -1251929751);
125                 Run ("monomono", null, "SHA1",   1, 40, -1148594972);
126                 Run ("password", salt, "SHA256", 1, 32, -1106908309);
127                 Run ("monomono", null, "SHA256", 1, 64, 1243724695);
128                 Run ("password", salt, "SHA384", 1, 48, 1338639872);
129                 Run ("monomono", null, "SHA384", 1, 96, -1974067932);
130                 Run ("password", salt, "SHA512", 1, 64, 998927776);
131                 Run ("monomono", null, "SHA512", 1, 128, -1082987985);
132         }
133
134         [Test]
135         public void Salt () 
136         {
137                 Run ("password", salt, "MD5",  10, 10, -1174247292);
138                 Run ("monomono", salt, "SHA1", 20, 20, 622814236);
139                 Run ("password", salt, "MD5",  30, 30, 1491759020);
140                 Run ("monomono", salt, "SHA1", 40, 40, 1186751819);
141                 Run ("password", salt, "MD5",  50, 50, -1416348895);
142                 Run ("monomono", salt, "SHA1", 60, 60, -1167799882);
143                 Run ("password", salt, "MD5",  70, 70, -695745351);
144                 Run ("monomono", salt, "SHA1", 80, 80, 598766793);
145                 Run ("password", salt, "MD5",  90, 90, -906351079);
146                 Run ("monomono", salt, "SHA1", 100, 100, 1247157997);
147         }
148
149         [Test]
150         public void NoSalt () 
151         {
152                 Run ("password", null, "MD5",  10, 10, -385488886);
153                 Run ("password", null, "SHA1", 20, 20, -385953596);
154                 Run ("password", null, "MD5",  30, 30, -669295228);
155                 Run ("password", null, "SHA1", 40, 40, -1921654064);
156                 Run ("password", null, "MD5",  50, 50, -1664099354);
157                 Run ("monomono", null, "SHA1", 60, 60, -1988511363);
158                 Run ("monomono", null, "MD5",  70, 70, -1326415479);
159                 Run ("monomono", null, "SHA1", 80, 80, 158880373);
160                 Run ("monomono", null, "MD5",  90, 90,  532527918);
161                 Run ("monomono", null, "SHA1", 100, 100, 769250758);
162         }
163
164         [Test]
165         public void MD5 () 
166         {
167                 const string hashName = "MD5";
168                 // getbytes less than hash size
169                 Run ("password", null, hashName, 10, 10, -385488886);
170                 // getbytes equal to hash size
171                 Run ("password", salt, hashName, 20, 16, -470982134);
172                 // getbytes more than hash size
173                 Run ("password", null, hashName, 30, 30, -669295228);
174                 Run ("password", salt, hashName, 40, 40, 892279589);
175                 Run ("password", null, hashName, 50, 50, -1664099354);
176                 Run ("monomono", salt, hashName, 60, 60, -2050574033);
177                 Run ("monomono", null, hashName, 70, 70, -1326415479);
178                 Run ("monomono", salt, hashName, 80, 80, 2047895994);
179                 Run ("monomono", null, hashName, 90, 90, 532527918);
180                 Run ("monomono", salt, hashName, 100, 100, 1522243696);
181         }
182
183         [Test]
184         public void SHA1 () 
185         {
186                 const string hashName = "SHA1";
187                 // getbytes less than hash size
188                 Run ("password", null, hashName, 10, 10, -852142057);
189                 // getbytes equal to hash size
190                 Run ("password", salt, hashName, 20, 20, -1096621819);
191                 // getbytes more than hash size
192                 Run ("password", null, hashName, 30, 30, 1748347042);
193                 Run ("password", salt, hashName, 40, 40, 900690664);
194                 Run ("password", null, hashName, 50, 50, 2125027038);
195                 Run ("monomono", salt, hashName, 60, 60, -1167799882);
196                 Run ("monomono", null, hashName, 70, 70, -1967623713);
197                 Run ("monomono", salt, hashName, 80, 80, 598766793);
198                 Run ("monomono", null, hashName, 90, 90, -1754629926);
199                 Run ("monomono", salt, hashName, 100, 100, 1247157997);
200         }
201
202         [Test]
203         public void SHA256 () 
204         {
205                 const string hashName = "SHA256";
206                 // getbytes less than hash size
207                 Run ("password", null, hashName, 10, 10, -1636557322);
208                 Run ("password", salt, hashName, 20, 20, -1403130075);
209                 // getbytes equal to hash size
210                 Run ("password", null, hashName, 30, 32, -1013167039);
211                 // getbytes more than hash size
212                 Run ("password", salt, hashName, 40, 40, 379553148);
213                 Run ("password", null, hashName, 50, 50, 1031928292);
214                 Run ("monomono", salt, hashName, 60, 60, 1933836953);
215                 Run ("monomono", null, hashName, 70, 70, -956782587);
216                 Run ("monomono", salt, hashName, 80, 80, 1239391711);
217                 Run ("monomono", null, hashName, 90, 90, -872090432);
218                 Run ("monomono", salt, hashName, 100, 100, -591569127);
219         }
220
221         [Test]
222         public void SHA384 () 
223         {
224                 const string hashName = "SHA384";
225                 // getbytes less than hash size
226                 Run ("password", null, hashName, 10, 10, 323393534);
227                 Run ("password", salt, hashName, 20, 20, -2034683704);
228                 Run ("password", null, hashName, 30, 32, 167978389);
229                 Run ("password", salt, hashName, 40, 40, 2123410525);
230                 // getbytes equal to hash size
231                 Run ("password", null, hashName, 50, 48, -47538843);
232                 // getbytes more than hash size
233                 Run ("monomono", salt, hashName, 60, 60, -118610774);
234                 Run ("monomono", null, hashName, 70, 70, 772360425);
235                 Run ("monomono", salt, hashName, 80, 80, -1018881215);
236                 Run ("monomono", null, hashName, 90, 90, -1585583772);
237                 Run ("monomono", salt, hashName, 100, 100, -821501990);
238         }
239
240         [Test]
241         public void SHA512 () 
242         {
243                 const string hashName = "SHA512";
244                 // getbytes less than hash size
245                 Run ("password", null, hashName, 10, 10, 708870265);
246                 Run ("password", salt, hashName, 20, 20, 23889227);
247                 Run ("password", null, hashName, 30, 32, 1718904507);
248                 Run ("password", salt, hashName, 40, 40, 979228711);
249                 Run ("password", null, hashName, 50, 48, 1554003653);
250                 // getbytes equal to hash size
251                 Run ("monomono", salt, hashName, 60, 64, 1251099126);
252                 // getbytes more than hash size
253                 Run ("monomono", null, hashName, 70, 70, 1021441810);
254                 Run ("monomono", salt, hashName, 80, 80, 640059310);
255                 Run ("monomono", null, hashName, 90, 90, 1178147201);
256                 Run ("monomono", salt, hashName, 100, 100, 206423887);
257         }
258
259         // get one block after the other
260         [Test]
261         public void OneByOne () 
262         {
263                 byte[] key = { 0x91, 0xDA, 0xF9, 0x9D, 0x7C, 0xA9, 0xB4, 0x42, 0xB8, 0xD9, 0x45, 0xAB, 0x69, 0xEE, 0x12, 0xBC, 0x48, 0xDD, 0x38, 0x74 };
264                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", salt, "SHA1", 1);
265                 string msg = "PKCS#5-Long password salt SHA1 (1)";
266
267                 int bloc = key.Length;
268                 int iter = (int) ((1000 + bloc - 1) / bloc);
269                 byte[] pass = null;
270                 for (int i=0; i < iter; i++) {
271                         pass = pd.GetBytes (bloc);
272                 }
273                 AssertEquals (msg, pass, key);
274         }
275
276         [Test]
277         public void SHA1SaltShortRun ()
278         {
279                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
280                 byte[] key = { 0x0B, 0x61, 0x93, 0x96, 0x3A, 0xFF, 0x0D, 0xFC, 0xF6, 0x3D, 0xA3, 0xDB, 0x34, 0xC2, 0x99, 0x71, 0x69, 0x11, 0x61, 0xB5 };
281                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", salt, "SHA1", 1);
282                 string msg = "PKCS#5 password salt SHA1 (1)";
283                 ShortRun (msg, pd, key);
284         }
285
286         [Test]
287         public void SHA1SaltLongRun () 
288         {
289                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
290                 byte[] key = { 0x91, 0xDA, 0xF9, 0x9D, 0x7C, 0xA9, 0xB4, 0x42, 0xB8, 0xD9, 0x45, 0xAB, 0x69, 0xEE, 0x12, 0xBC, 0x48, 0xDD, 0x38, 0x74 };
291                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", salt, "SHA1", 1);
292                 string msg = "PKCS#5-Long password salt SHA1 (1)";
293                 LongRun (msg, pd, key);
294         }
295
296         [Test]
297         public void SHA1NoSaltShortRun ()
298         {
299                 byte[] key = { 0x74, 0x61, 0x03, 0x6C, 0xA1, 0xFE, 0x85, 0x3E, 0xD9, 0x3F, 0x03, 0x06, 0x58, 0x45, 0xDE, 0x36, 0x52, 0xEF, 0x4B, 0x68 };
300                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("mono", null, "SHA1", 10);
301                 string msg = "PKCS#5 mono null SHA1 (10)";
302                 ShortRun (msg, pd, key);
303         }
304
305         [Test]
306         public void SHA1NoSaltLongRun () 
307         {
308                 byte[] key = { 0x3A, 0xF8, 0x33, 0x88, 0x39, 0x61, 0x29, 0x75, 0x5C, 0x17, 0xD2, 0x9E, 0x8A, 0x78, 0xEB, 0xBD, 0x89, 0x1E, 0x4C, 0x67 };
309                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("mono", null, "SHA1", 10);
310                 string msg = "PKCS#5-Long mono null SHA1 (10)";
311                 LongRun (msg, pd, key);
312         }
313
314         [Test]
315         public void MD5SaltShortRun ()
316         {
317                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
318                 byte[] key = { 0xA5, 0x4D, 0x4E, 0xDD, 0x3A, 0x59, 0xAC, 0x98, 0x08, 0xDA, 0xE7, 0xF2, 0x85, 0x2F, 0x7F, 0xF2 };
319                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("mono", salt, "MD5", 100);
320                 string msg = "PKCS#5 mono salt MD5 (100)";
321                 ShortRun (msg, pd, key);
322         }
323
324         [Test]
325         public void MD5SaltLongRun () 
326         {
327                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
328                 byte[] key = { 0x92, 0x51, 0x4D, 0x10, 0xE1, 0x5F, 0xA8, 0x44, 0xEF, 0xFC, 0x0F, 0x1F, 0x6F, 0x3E, 0x40, 0x36 };
329                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("mono", salt, "MD5", 100);
330                 string msg = "PKCS#5-Long mono salt MD5 (100)";
331                 LongRun (msg, pd, key);
332         }
333
334         [Test]
335         public void MD5NoSaltShortRun ()
336         {
337                 byte[] key = { 0x39, 0xEB, 0x82, 0x84, 0xCF, 0x1A, 0x3B, 0x3C, 0xA1, 0xF2, 0x68, 0xAF, 0xBF, 0xAC, 0x41, 0xA6 };
338                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", null, "MD5", 1000);
339                 string msg = "PKCS#5 password null MD5 (1000)";
340                 ShortRun (msg, pd, key);
341         }
342
343         [Test]
344         public void MD5NoSaltLongRun () 
345         {
346                 byte[] key = { 0x49, 0x3C, 0x00, 0x69, 0xB4, 0x55, 0x21, 0xA4, 0xC9, 0x69, 0x2E, 0xFF, 0xAA, 0xED, 0x4C, 0x72 };
347                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", null, "MD5", 1000);
348                 string msg = "PKCS#5-Long password null MD5 (1000)";
349                 LongRun (msg, pd, key);
350         }
351
352         [Test]
353         public void Properties () 
354         {
355                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
356                 // create object...
357                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", null, "MD5", 1000);
358                 AssertEquals ("HashName-MD5", "MD5", pd.HashName);
359                 AssertEquals ("IterationCount-1000", 1000, pd.IterationCount);
360                 // ...then change all its properties...
361                 pd.HashName = "SHA1";
362                 AssertEquals ("HashName-SHA1", "SHA1", pd.HashName);
363                 pd.Salt = salt;
364                 AssertEquals ("Salt", "DE-AD-C0-DE", BitConverter.ToString (pd.Salt));
365                 pd.IterationCount = 1;
366                 AssertEquals ("IterationCount-1", 1, pd.IterationCount);
367                 byte[] expectedKey = { 0x0b, 0x61, 0x93, 0x96 };
368                 // ... before using it
369                 AssertEquals ("PKCS#5 test properties", expectedKey, pd.GetBytes (4));
370                 // it should work but if we try to set any properties after GetBytes
371                 // they should all throw an exception
372                 try {
373                         pd.HashName = "SHA256";
374                         Fail ("PKCS#5 can't set HashName after GetBytes - expected CryptographicException but got none");
375                 }
376                 catch (CryptographicException) {
377                         // do nothing, this is what we expect
378                 }
379                 catch (Exception e) {
380                         Fail ("PKCS#5 can't set HashName after GetBytes - expected CryptographicException but got " + e.ToString ());
381                 }
382                 try {
383                         pd.Salt = expectedKey;
384                         Fail ("PKCS#5 can't set Salt after GetBytes - expected CryptographicException but got none");
385                 }
386                 catch (CryptographicException) {
387                         // do nothing, this is what we expect
388                 }
389                 catch (Exception e) {
390                         Fail ("PKCS#5 can't set Salt after GetBytes - expected CryptographicException but got " + e.ToString ());
391                 }
392                 try {
393                         pd.IterationCount = 10;
394                         Fail ("PKCS#5 can't set IterationCount after GetBytes - expected CryptographicException but got none");
395                 }
396                 catch (CryptographicException) {
397                         // do nothing, this is what we expect
398                 }
399                 catch (Exception e) {
400                         Fail ("PKCS#5 can't set IterationCount after GetBytes - expected CryptographicException but got " + e.ToString ());
401                 }
402                 // same thing after Reset
403                 pd.Reset ();
404                 try {
405                         pd.HashName = "SHA256";
406                         Fail ("PKCS#5 can't set HashName after Reset - expected CryptographicException but got none");
407                 }
408                 catch (CryptographicException) {
409                         // do nothing, this is what we expect
410                 }
411                 catch (Exception e) {
412                         Fail ("PKCS#5 can't set HashName after Reset - expected CryptographicException but got " + e.ToString ());
413                 }
414                 try {
415                         pd.Salt = expectedKey;
416                         Fail ("PKCS#5 can't set Salt after Reset - expected CryptographicException but got none");
417                 }
418                 catch (CryptographicException) {
419                         // do nothing, this is what we expect
420                 }
421                 catch (Exception e) {
422                         Fail ("PKCS#5 can't set Salt after Reset - expected CryptographicException but got " + e.ToString ());
423                 }
424                 try {
425                         pd.IterationCount = 10;
426                         Fail ("PKCS#5 can't set IterationCount after Reset - expected CryptographicException but got none");
427                 }
428                 catch (CryptographicException) {
429                         // do nothing, this is what we expect
430                 }
431                 catch (Exception e) {
432                         Fail ("PKCS#5 can't set IterationCount after Reset - expected CryptographicException but got " + e.ToString ());
433                 }
434         }
435
436         // FIXME: should we treat this as a bug or as a feature ?
437         [Test]
438         [ExpectedException (typeof (NullReferenceException))]
439         public void StrangeBehaviour ()
440         {
441                 byte[] salt = { 0xDE, 0xAD, 0xC0, 0xDE };
442                 // create object with a salt...
443                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", salt, "MD5", 1000);
444                 // ...then change the salt to null
445                 pd.Salt = null;
446         }
447
448         [Test]
449         [ExpectedException (typeof (CryptographicException))]
450         public void CryptDeriveKey_TooLongKey () 
451         {
452                 PasswordDeriveBytes pd = new PasswordDeriveBytes ("password", null, "MD5", 1000);
453                 pd.CryptDeriveKey ("AlgName", "MD5", 256, new byte [8]);
454         }
455 }
456
457 }