2010-06-21 Atsushi Enomoto <atsushi@ximian.com>
[mcs.git] / class / corlib / System.Security.Cryptography / PasswordDeriveBytes.cs
blob641c149de12ef2618c2399ccfd7c5b1e9307be33
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.
30 using System.Globalization;
31 using System.Runtime.InteropServices;
32 using System.Text;
34 namespace System.Security.Cryptography {
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
42 [ComVisible (true)]
43 public class PasswordDeriveBytes : DeriveBytes {
45 private string HashNameValue;
46 private byte[] SaltValue;
47 private int IterationsValue;
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;
57 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt)
59 Prepare (strPassword, rgbSalt, "SHA1", 100);
62 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams)
64 Prepare (strPassword, rgbSalt, "SHA1", 100);
65 if (cspParams != null) {
66 throw new NotSupportedException (
67 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
71 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
73 Prepare (strPassword, rgbSalt, strHashName, iterations);
76 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams)
78 Prepare (strPassword, rgbSalt, strHashName, iterations);
79 if (cspParams != null) {
80 throw new NotSupportedException (
81 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
85 public PasswordDeriveBytes (byte[] password, byte[] salt)
87 Prepare (password, salt, "SHA1", 100);
90 public PasswordDeriveBytes (byte[] password, byte[] salt, CspParameters cspParams)
92 Prepare (password, salt, "SHA1", 100);
93 if (cspParams != null) {
94 throw new NotSupportedException (
95 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
99 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations)
101 Prepare (password, salt, hashName, iterations);
104 public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations, CspParameters cspParams)
106 Prepare (password, salt, hashName, iterations);
107 if (cspParams != null) {
108 throw new NotSupportedException (
109 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
113 ~PasswordDeriveBytes ()
115 // zeroize buffer
116 if (initial != null) {
117 Array.Clear (initial, 0, initial.Length);
118 initial = null;
120 // zeroize temporary password storage
121 Array.Clear (password, 0, password.Length);
124 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
126 if (strPassword == null)
127 throw new ArgumentNullException ("strPassword");
129 byte[] pwd = Encoding.UTF8.GetBytes (strPassword);
130 Prepare (pwd, rgbSalt, strHashName, iterations);
131 Array.Clear (pwd, 0, pwd.Length);
134 private void Prepare (byte[] password, byte[] rgbSalt, string strHashName, int iterations)
136 if (password == null)
137 throw new ArgumentNullException ("password");
139 this.password = (byte[]) password.Clone ();
141 Salt = rgbSalt;
143 HashName = strHashName;
144 IterationCount = iterations;
145 state = 0;
147 public string HashName {
148 get { return HashNameValue; }
149 set {
150 if (value == null)
151 throw new ArgumentNullException ("HashName");
152 if (state != 0) {
153 throw new CryptographicException (
154 Locale.GetText ("Can't change this property at this stage"));
156 HashNameValue = value;
160 public int IterationCount {
161 get { return IterationsValue; }
162 set {
163 if (value < 1)
164 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
165 if (state != 0) {
166 throw new CryptographicException (
167 Locale.GetText ("Can't change this property at this stage"));
169 IterationsValue = value;
173 public byte[] Salt {
174 get {
175 if (SaltValue == null)
176 return null;
177 return (byte[]) SaltValue.Clone ();
179 set {
180 if (state != 0) {
181 throw new CryptographicException (
182 Locale.GetText ("Can't change this property at this stage"));
184 if (value != null)
185 SaltValue = (byte[]) value.Clone ();
186 else
187 SaltValue = null;
191 public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
193 if (keySize > 128) {
194 throw new CryptographicException (
195 Locale.GetText ("Key Size can't be greater than 128 bits"));
197 throw new NotSupportedException (
198 Locale.GetText ("CspParameters not supported by Mono"));
201 // note: Key is returned - we can't zeroize it ourselve :-(
202 [Obsolete ("see Rfc2898DeriveBytes for PKCS#5 v2 support")]
203 #pragma warning disable 809
204 public override byte[] GetBytes (int cb)
206 #pragma warning restore 809
208 if (cb < 1)
209 throw new IndexOutOfRangeException ("cb");
211 if (state == 0) {
212 // it's now impossible to change the HashName, Salt
213 // and IterationCount
214 Reset ();
215 state = 1;
218 byte[] result = new byte [cb];
219 int cpos = 0;
220 // the initial hash (in reset) + at least one iteration
221 int iter = Math.Max (1, IterationsValue - 1);
223 // start with the PKCS5 key
224 if (output == null) {
225 // calculate the PKCS5 key
226 output = initial;
228 // generate new key material
229 for (int i = 0; i < iter - 1; i++)
230 output = hash.ComputeHash (output);
233 while (cpos < cb) {
234 byte[] output2 = null;
235 if (hashnumber == 0) {
236 // last iteration on output
237 output2 = hash.ComputeHash (output);
239 else if (hashnumber < 1000) {
240 string n = Convert.ToString (hashnumber);
241 output2 = new byte [output.Length + n.Length];
242 for (int j=0; j < n.Length; j++)
243 output2 [j] = (byte)(n [j]);
244 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
245 // don't update output
246 output2 = hash.ComputeHash (output2);
248 else {
249 throw new CryptographicException (
250 Locale.GetText ("too long"));
253 int rem = output2.Length - position;
254 int l = Math.Min (cb - cpos, rem);
255 Buffer.BlockCopy (output2, position, result, cpos, l);
256 cpos += l;
257 position += l;
258 while (position >= output2.Length) {
259 position -= output2.Length;
260 hashnumber++;
263 return result;
266 public override void Reset ()
268 state = 0;
269 position = 0;
270 hashnumber = 0;
272 hash = HashAlgorithm.Create (HashNameValue);
273 if (SaltValue != null) {
274 hash.TransformBlock (password, 0, password.Length, password, 0);
275 hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
276 initial = hash.Hash;
278 else
279 initial = hash.ComputeHash (password);