Merge pull request #697 from linquize/atom-bug
[mono.git] / mcs / class / corlib / System.Security.Cryptography / PasswordDeriveBytes.cs
1 //
2 // PasswordDeriveBytes.cs: Handles PKCS#5 key derivation using password
3 //
4 // Author:
5 //      Sebastien Pouliot (sebastien@ximian.com)
6 //
7 // (C) 2002, 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004-2007 Novell, Inc (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29
30 using System.Globalization;
31 using System.Runtime.InteropServices;
32 using System.Text;
33
34 namespace System.Security.Cryptography {
35
36 // References:
37 // a.   PKCS #5 - Password-Based Cryptography Standard 
38 //      http://www.rsasecurity.com/rsalabs/pkcs/pkcs-5/index.html
39 // b.   IETF RFC2898: PKCS #5: Password-Based Cryptography Specification Version 2.0
40 //      http://www.rfc-editor.org/rfc/rfc2898.txt
41
42 [ComVisible (true)]
43 public class PasswordDeriveBytes : DeriveBytes {
44
45         private string HashNameValue;
46         private byte[] SaltValue;
47         private int IterationsValue;
48
49         private HashAlgorithm hash;
50         private int state;
51         private byte[] password;
52         private byte[] initial;
53         private byte[] output;
54         private int position;
55         private int hashnumber;
56
57         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt) 
58         {
59                 Prepare (strPassword, rgbSalt, "SHA1", 100);
60         }
61
62         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams) 
63         {
64                 Prepare (strPassword, rgbSalt, "SHA1", 100);
65                 if (cspParams != null) {
66                         throw new NotSupportedException (
67                                 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
68                 }
69         }
70
71         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations) 
72         {
73                 Prepare (strPassword, rgbSalt, strHashName, iterations);
74         }
75
76         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams) 
77         {
78                 Prepare (strPassword, rgbSalt, strHashName, iterations);
79                 if (cspParams != null) {
80                         throw new NotSupportedException (
81                                 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
82                 }
83         }
84
85         public PasswordDeriveBytes (byte[] password, byte[] salt) 
86         {
87                 Prepare (password, salt, "SHA1", 100);
88         }
89
90         public PasswordDeriveBytes (byte[] password, byte[] salt, CspParameters cspParams) 
91         {
92                 Prepare (password, salt, "SHA1", 100);
93                 if (cspParams != null) {
94                         throw new NotSupportedException (
95                                 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
96                 }
97         }
98
99         public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations) 
100         {
101                 Prepare (password, salt, hashName, iterations);
102         }
103
104         public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations, CspParameters cspParams) 
105         {
106                 Prepare (password, salt, hashName, iterations);
107                 if (cspParams != null) {
108                         throw new NotSupportedException (
109                                 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
110                 }
111         }
112
113 #if NET_4_0
114         protected override void Dispose (bool disposing)
115         {
116                 // zeroize buffer
117                 if (initial != null) {
118                         Array.Clear (initial, 0, initial.Length);
119                         initial = null;
120                 }
121                 // zeroize temporary password storage
122                 if (password != null) {
123                         Array.Clear (password, 0, password.Length);
124                         password = null;
125                 }
126                 base.Dispose (disposing);
127         }
128 #endif
129
130         private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations) 
131         {
132                 if (strPassword == null)
133                         throw new ArgumentNullException ("strPassword");
134
135                 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
136                 Prepare (pwd, rgbSalt, strHashName, iterations);
137                 Array.Clear (pwd, 0, pwd.Length);
138         }
139
140         private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
141         {
142                 if (password == null)
143                         throw new ArgumentNullException ("password");
144
145                 this.password = (byte[]) password.Clone ();
146
147                 Salt = rgbSalt;
148
149                 HashName = strHashName;
150                 IterationCount = iterations;
151                 state = 0;
152         }
153         public string HashName {
154                 get { return HashNameValue; } 
155                 set {
156                         if (value == null)
157                                 throw new ArgumentNullException ("HashName");
158                         if (state != 0) {
159                                 throw new CryptographicException (
160                                         Locale.GetText ("Can't change this property at this stage"));
161                         }
162                         HashNameValue = value;
163                 }
164         }
165
166         public int IterationCount {
167                 get { return IterationsValue; }
168                 set {
169                         if (value < 1)
170                                 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
171                         if (state != 0) {
172                                 throw new CryptographicException (
173                                         Locale.GetText ("Can't change this property at this stage"));
174                         }
175                         IterationsValue = value;
176                 }
177         }
178
179         public byte[] Salt {
180                 get { 
181                         if (SaltValue == null)
182                                 return null;
183                         return (byte[]) SaltValue.Clone ();
184                 }
185                 set {
186                         if (state != 0) {
187                                 throw new CryptographicException (
188                                         Locale.GetText ("Can't change this property at this stage"));
189                         }
190                         if (value != null)
191                                 SaltValue = (byte[]) value.Clone ();
192                         else
193                                 SaltValue = null;
194                 }
195         }
196
197         public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV) 
198         {
199                 if (keySize > 128) {
200                         throw new CryptographicException (
201                                 Locale.GetText ("Key Size can't be greater than 128 bits"));
202                 }
203                 throw new NotSupportedException (
204                         Locale.GetText ("CspParameters not supported by Mono"));
205         }
206
207         // note: Key is returned - we can't zeroize it ourselve :-(
208         [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
209  #pragma warning disable 809
210         public override byte[] GetBytes (int cb) 
211         {
212  #pragma warning restore 809
213
214                 if (cb < 1)
215                         throw new IndexOutOfRangeException ("cb");
216
217                 if (state == 0) {
218                         // it's now impossible to change the HashName, Salt
219                         // and IterationCount
220                         Reset ();
221                         state = 1;
222                 }
223
224                 byte[] result = new byte [cb];
225                 int cpos = 0;
226                 // the initial hash (in reset) + at least one iteration
227                 int iter = Math.Max (1, IterationsValue - 1);
228
229                 // start with the PKCS5 key
230                 if (output == null) {
231                         // calculate the PKCS5 key
232                         output = initial;
233
234                         // generate new key material
235                         for (int i = 0; i < iter - 1; i++)
236                                 output = hash.ComputeHash (output);
237                 }
238
239                 while (cpos < cb) {
240                         byte[] output2 = null;
241                         if (hashnumber == 0) {
242                                 // last iteration on output
243                                 output2 = hash.ComputeHash (output);
244                         }
245                         else if (hashnumber < 1000) {
246                                 string n = Convert.ToString (hashnumber);
247                                 output2 = new byte [output.Length + n.Length];
248                                 for (int j=0; j < n.Length; j++)
249                                         output2 [j] = (byte)(n [j]);
250                                 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
251                                 // don't update output
252                                 output2 = hash.ComputeHash (output2);
253                         }
254                         else {
255                                 throw new CryptographicException (
256                                         Locale.GetText ("too long"));
257                         }
258
259                         int rem = output2.Length - position;
260                         int l = Math.Min (cb - cpos, rem);
261                         Buffer.BlockCopy (output2, position, result, cpos, l);
262                         cpos += l;
263                         position += l;
264                         while (position >= output2.Length) {
265                                 position -= output2.Length;
266                                 hashnumber++;
267                         }
268                 }
269                 return result;
270         }
271
272         public override void Reset () 
273         {
274                 state = 0;
275                 position = 0;
276                 hashnumber = 0;
277
278                 hash = HashAlgorithm.Create (HashNameValue);
279                 if (SaltValue != null) {
280                         hash.TransformBlock (password, 0, password.Length, password, 0);
281                         hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
282                         initial = hash.Hash;
283                 }
284                 else
285                         initial = hash.ComputeHash (password);
286         }
287
288         
289