**** Merged from MCS ****
[mono-project.git] / mcs / class / corlib / System.Security.Cryptography / PasswordDeriveBytes.cs
blobca4b001edf2560170fc483991807fd30221420db
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 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;
31 using System.Globalization;
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 public class PasswordDeriveBytes : DeriveBytes {
44 private string HashNameValue;
45 private byte[] SaltValue;
46 private int IterationsValue;
48 private HashAlgorithm hash;
49 private int state;
50 private byte[] password;
51 private byte[] initial;
52 private byte[] output;
53 private int position;
54 private int hashnumber;
56 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt)
58 Prepare (strPassword, rgbSalt, "SHA1", 100);
61 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams)
63 Prepare (strPassword, rgbSalt, "SHA1", 100);
64 if (cspParams != null) {
65 throw new NotSupportedException (
66 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
70 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
72 Prepare (strPassword, rgbSalt, strHashName, iterations);
75 public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations, CspParameters cspParams)
77 Prepare (strPassword, rgbSalt, strHashName, iterations);
78 if (cspParams != null) {
79 throw new NotSupportedException (
80 Locale.GetText ("CspParameters not supported by Mono for PasswordDeriveBytes."));
84 ~PasswordDeriveBytes ()
86 // zeroize buffer
87 if (initial != null) {
88 Array.Clear (initial, 0, initial.Length);
89 initial = null;
91 // zeroize temporary password storage
92 Array.Clear (password, 0, password.Length);
95 private void Prepare (string strPassword, byte[] rgbSalt, string strHashName, int iterations)
97 #if NET_2_0
98 if (strPassword == null)
99 throw new ArgumentNullException ("strPassword");
100 password = Encoding.UTF8.GetBytes (strPassword);
102 Salt = rgbSalt;
103 #else
104 if (strPassword == null)
105 password = null;
106 else
107 password = Encoding.UTF8.GetBytes (strPassword);
109 if (rgbSalt == null)
110 SaltValue = null;
111 else
112 SaltValue = (byte[]) rgbSalt.Clone ();
113 #endif
115 HashName = strHashName;
116 IterationCount = iterations;
117 state = 0;
120 public string HashName {
121 get { return HashNameValue; }
122 set {
123 if (value == null)
124 throw new ArgumentNullException ("HashName");
125 if (state != 0) {
126 throw new CryptographicException (
127 Locale.GetText ("Can't change this property at this stage"));
129 HashNameValue = value;
133 public int IterationCount {
134 get { return IterationsValue; }
135 set {
136 if (value < 1)
137 throw new ArgumentOutOfRangeException ("> 0", "IterationCount");
138 if (state != 0) {
139 throw new CryptographicException (
140 Locale.GetText ("Can't change this property at this stage"));
142 IterationsValue = value;
146 public byte[] Salt {
147 get {
148 if (SaltValue == null)
149 return null;
150 return (byte[]) SaltValue.Clone ();
152 set {
153 if (state != 0) {
154 throw new CryptographicException (
155 Locale.GetText ("Can't change this property at this stage"));
157 #if NET_2_0
158 if (value != null)
159 SaltValue = (byte[]) value.Clone ();
160 else
161 SaltValue = null;
162 #else
163 // this will cause a NullReferenceException if set to null (like 1.0/1.1)
164 SaltValue = (byte[]) value.Clone ();
165 #endif
169 public byte[] CryptDeriveKey (string algname, string alghashname, int keySize, byte[] rgbIV)
171 if (keySize > 128) {
172 throw new CryptographicException (
173 Locale.GetText ("Key Size can't be greater than 128 bits"));
175 throw new NotSupportedException (
176 Locale.GetText ("CspParameters not supported by Mono"));
179 // note: Key is returned - we can't zeroize it ourselve :-(
180 public override byte[] GetBytes (int cb)
182 #if ! NET_2_0
183 // 1.0/1.1 was a little late at throwing the argument exception ;-)
184 if (password == null)
185 throw new ArgumentNullException ("Password");
186 #endif
187 if (cb < 1)
188 throw new IndexOutOfRangeException ("cb");
190 if (state == 0) {
191 state = 1;
192 // it's now impossible to change the HashName, Salt
193 // and IterationCount
194 Reset ();
197 byte[] result = new byte [cb];
198 int cpos = 0;
199 // the initial hash (in reset) + at least one iteration
200 int iter = Math.Max (1, IterationsValue - 1);
202 // start with the PKCS5 key
203 if (output == null) {
204 // calculate the PKCS5 key
205 output = initial;
207 // generate new key material
208 for (int i = 0; i < iter - 1; i++)
209 output = hash.ComputeHash (output);
212 while (cpos < cb) {
213 byte[] output2 = null;
214 if (hashnumber == 0) {
215 // last iteration on output
216 output2 = hash.ComputeHash (output);
218 else if (hashnumber < 1000) {
219 string n = Convert.ToString (hashnumber);
220 output2 = new byte [output.Length + n.Length];
221 for (int j=0; j < n.Length; j++)
222 output2 [j] = (byte)(n [j]);
223 Buffer.BlockCopy (output, 0, output2, n.Length, output.Length);
224 // don't update output
225 output2 = hash.ComputeHash (output2);
227 else {
228 throw new CryptographicException (
229 Locale.GetText ("too long"));
232 int rem = output2.Length - position;
233 int l = Math.Min (cb - cpos, rem);
234 Buffer.BlockCopy (output2, position, result, cpos, l);
235 cpos += l;
236 position += l;
237 while (position >= output2.Length) {
238 position -= output2.Length;
239 hashnumber++;
242 return result;
245 public override void Reset ()
247 // note: Reset doesn't change state
248 position = 0;
249 hashnumber = 0;
251 hash = HashAlgorithm.Create (HashNameValue);
252 if (SaltValue != null) {
253 hash.TransformBlock (password, 0, password.Length, password, 0);
254 hash.TransformFinalBlock (SaltValue, 0, SaltValue.Length);
255 initial = hash.Hash;
257 else
258 initial = hash.ComputeHash (password);