[runtime] Overwrite stacktrace for exception on re-throw. Fixes #1856.
[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         protected override void Dispose (bool disposing)
114         {
115                 // zeroize buffer
116                 if (initial != null) {
117                         Array.Clear (initial, 0, initial.Length);
118                         initial = null;
119                 }
120                 // zeroize temporary password storage
121                 if (password != null) {
122                         Array.Clear (password, 0, password.Length);
123                         password = null;
124                 }
125                 base.Dispose (disposing);
126         }
127
128         private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations) 
129         {
130                 if (strPassword == null)
131                         throw new ArgumentNullException ("strPassword");
132
133                 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
134                 Prepare (pwd, rgbSalt, strHashName, iterations);
135                 Array.Clear (pwd, 0, pwd.Length);
136         }
137
138         private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
139         {
140                 if (password == null)
141                         throw new ArgumentNullException ("password");
142
143                 this.password = (byte[]) password.Clone ();
144
145                 Salt = rgbSalt;
146
147                 HashName = strHashName;
148                 IterationCount = iterations;
149                 state = 0;
150         }
151         public string HashName {
152                 get { return HashNameValue; } 
153                 set {
154                         if (value == null)
155                                 throw new ArgumentNullException ("HashName");
156                         if (state != 0) {
157                                 throw new CryptographicException (
158                                         Locale.GetText ("Can't change this property at this stage"));
159                         }
160                         HashNameValue = value;
161                 }
162         }
163
164         public int IterationCount {
165                 get { return IterationsValue; }
166                 set {
167                         if (value < 1)
168                                 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
169                         if (state != 0) {
170                                 throw new CryptographicException (
171                                         Locale.GetText ("Can't change this property at this stage"));
172                         }
173                         IterationsValue = value;
174                 }
175         }
176
177         public byte[] Salt {
178                 get { 
179                         if (SaltValue == null)
180                                 return null;
181                         return (byte[]) SaltValue.Clone ();
182                 }
183                 set {
184                         if (state != 0) {
185                                 throw new CryptographicException (
186                                         Locale.GetText ("Can't change this property at this stage"));
187                         }
188                         if (value != null)
189                                 SaltValue = (byte[]) value.Clone ();
190                         else
191                                 SaltValue = null;
192                 }
193         }
194
195         public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV) 
196         {
197                 if (keySize > 128) {
198                         throw new CryptographicException (
199                                 Locale.GetText ("Key Size can't be greater than 128 bits"));
200                 }
201                 throw new NotSupportedException (
202                         Locale.GetText ("CspParameters not supported by Mono"));
203         }
204
205         // note: Key is returned - we can't zeroize it ourselve :-(
206         [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
207  #pragma warning disable 809
208         public override byte[] GetBytes (int cb) 
209         {
210  #pragma warning restore 809
211
212                 if (cb < 1)
213                         throw new IndexOutOfRangeException ("cb");
214
215                 if (state == 0) {
216                         // it's now impossible to change the HashName, Salt
217                         // and IterationCount
218                         Reset ();
219                         state = 1;
220                 }
221
222                 byte[] result = new byte [cb];
223                 int cpos = 0;
224                 // the initial hash (in reset) + at least one iteration
225                 int iter = Math.Max (1, IterationsValue - 1);
226
227                 // start with the PKCS5 key
228                 if (output == null) {
229                         // calculate the PKCS5 key
230                         output = initial;
231
232                         // generate new key material
233                         for (int i = 0; i < iter - 1; i++)
234                                 output = hash.ComputeHash (output);
235                 }
236
237                 while (cpos < cb) {
238                         byte[] output2 = null;
239                         if (hashnumber == 0) {
240                                 // last iteration on output
241                                 output2 = hash.ComputeHash (output);
242                         }
243                         else if (hashnumber < 1000) {
244                                 string n = Convert.ToString (hashnumber);
245                                 output2 = new byte [output.Length + n.Length];
246                                 for (int j=0; j < n.Length; j++)
247                                         output2 [j] = (byte)(n [j]);
248                                 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
249                                 // don't update output
250                                 output2 = hash.ComputeHash (output2);
251                         }
252                         else {
253                                 throw new CryptographicException (
254                                         Locale.GetText ("too long"));
255                         }
256
257                         int rem = output2.Length - position;
258                         int l = Math.Min (cb - cpos, rem);
259                         Buffer.BlockCopy (output2, position, result, cpos, l);
260                         cpos += l;
261                         position += l;
262                         while (position >= output2.Length) {
263                                 position -= output2.Length;
264                                 hashnumber++;
265                         }
266                 }
267                 return result;
268         }
269
270         public override void Reset () 
271         {
272                 state = 0;
273                 position = 0;
274                 hashnumber = 0;
275
276                 hash = HashAlgorithm.Create (HashNameValue);
277                 if (SaltValue != null) {
278                         hash.TransformBlock (password, 0, password.Length, password, 0);
279                         hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
280                         initial = hash.Hash;
281                 }
282                 else
283                         initial = hash.ComputeHash (password);
284         }
285
286         
287