Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / passwordderivebytes.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // PasswordDerivedBytes.cs
11 //
12
13 namespace System.Security.Cryptography {
14     using System.IO;
15     using System.Runtime.CompilerServices;
16     using System.Runtime.InteropServices;
17     using System.Runtime.Versioning;
18     using System.Security.Cryptography.X509Certificates;
19     using System.Text;
20     using System.Globalization;
21     using System.Diagnostics.Contracts;
22
23     [System.Runtime.InteropServices.ComVisible(true)]
24     public class PasswordDeriveBytes : DeriveBytes {
25         private int             _extraCount;
26         private int             _prefix;
27         private int             _iterations;
28         private byte[]          _baseValue;
29         private byte[]          _extra;
30         private byte[]          _salt;
31         private string          _hashName;
32         private byte[]          _password;
33         private HashAlgorithm   _hash;
34 #if !MONO        
35         private CspParameters   _cspParams;
36
37         [System.Security.SecurityCritical] // auto-generated
38         private SafeProvHandle _safeProvHandle = null;
39         private SafeProvHandle ProvHandle {
40             [System.Security.SecurityCritical]  // auto-generated
41             get {
42                 if (_safeProvHandle == null) {
43                     lock (this) {
44                         if (_safeProvHandle == null) {
45                             SafeProvHandle safeProvHandle = Utils.AcquireProvHandle(_cspParams);
46                             System.Threading.Thread.MemoryBarrier();
47                             _safeProvHandle = safeProvHandle;
48                         }
49                     }
50                 }
51                 return _safeProvHandle;
52             }
53         }
54 #endif
55         //
56         // public constructors
57         //
58
59         public PasswordDeriveBytes (String strPassword, byte[] rgbSalt) : this (strPassword, rgbSalt, new CspParameters()) {}
60
61         public PasswordDeriveBytes (byte[] password, byte[] salt) : this (password, salt, new CspParameters()) {}
62
63         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, string strHashName, int iterations) : 
64             this (strPassword, rgbSalt, strHashName, iterations, new CspParameters()) {}
65
66         public PasswordDeriveBytes (byte[] password, byte[] salt, string hashName, int iterations) : 
67             this (password, salt, hashName, iterations, new CspParameters()) {}
68
69         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, CspParameters cspParams) :
70             this (strPassword, rgbSalt, "SHA1", 100, cspParams) {}
71
72         public PasswordDeriveBytes (byte[] password, byte[] salt, CspParameters cspParams) : 
73             this (password, salt, "SHA1", 100, cspParams) {}
74
75         public PasswordDeriveBytes (string strPassword, byte[] rgbSalt, String strHashName, int iterations, CspParameters cspParams) :
76             this ((new UTF8Encoding(false)).GetBytes(strPassword), rgbSalt, strHashName, iterations, cspParams) {}
77
78         // This method needs to be safe critical, because in debug builds the C# compiler will include null
79         // initialization of the _safeProvHandle field in the method.  Since SafeProvHandle is critical, a
80         // transparent reference triggers an error using PasswordDeriveBytes.
81         [SecuritySafeCritical]
82         public PasswordDeriveBytes (byte[] password, byte[] salt, String hashName, int iterations, CspParameters cspParams) {
83             this.IterationCount = iterations;
84             this.Salt = salt;
85             this.HashName = hashName;
86             _password = password;
87 #if !MONO
88             _cspParams = cspParams;
89 #endif
90         }
91
92         //
93         // public properties
94         //
95
96         public String HashName {
97             get { return _hashName; }
98             set { 
99                 if (_baseValue != null)
100                     throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_ValuesFixed", "HashName"));
101                 _hashName = value;
102                 _hash = (HashAlgorithm) CryptoConfig.CreateFromName(_hashName);
103             }
104         }
105
106         public int IterationCount {
107             get { return _iterations; }
108             set { 
109                 if (value <= 0)
110                     throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
111                 Contract.EndContractBlock();
112                 if (_baseValue != null)
113                     throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_ValuesFixed", "IterationCount"));
114                 _iterations = value;
115             }
116         }
117
118         public byte[] Salt {
119             get {
120                 if (_salt == null) 
121                     return null;
122                 return (byte[]) _salt.Clone();
123             }
124             set {
125                 if (_baseValue != null)
126                     throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_ValuesFixed", "Salt"));
127                 if (value == null)
128                     _salt = null;
129                 else
130                     _salt = (byte[]) value.Clone();
131             }
132         }
133
134         //
135         // public methods
136         //
137
138         [System.Security.SecuritySafeCritical]  // auto-generated
139         [Obsolete("Rfc2898DeriveBytes replaces PasswordDeriveBytes for deriving key material from a password and is preferred in new applications.")]
140 // disable csharp compiler warning #0809: obsolete member overrides non-obsolete member
141 #pragma warning disable 0809
142         public override byte[] GetBytes(int cb) {
143 #if MONO
144             if (cb < 1)
145                 throw new IndexOutOfRangeException ("cb");
146 #endif
147
148             int         ib = 0;
149             byte[]      rgb;
150             byte[]      rgbOut = new byte[cb];
151
152             if (_baseValue == null) {
153                 ComputeBaseValue();
154             }
155             else if (_extra != null) {
156                 ib = _extra.Length - _extraCount;
157                 if (ib >= cb) {
158                     Buffer.InternalBlockCopy(_extra, _extraCount, rgbOut, 0, cb);
159                     if (ib > cb)
160                         _extraCount += cb;
161                     else
162                         _extra = null;
163
164                     return rgbOut;
165                 }
166                 else {
167                     //
168                     // Note: The second parameter should really be _extraCount instead
169                     // However, changing this would constitute a breaking change compared
170                     // to what has shipped in V1.x.
171                     //
172
173                     Buffer.InternalBlockCopy(_extra, ib, rgbOut, 0, ib);
174                     _extra = null;
175                 }
176             }
177
178             rgb = ComputeBytes(cb-ib);
179             Buffer.InternalBlockCopy(rgb, 0, rgbOut, ib, cb-ib);
180             if (rgb.Length + ib > cb) {
181                 _extra = rgb;
182                 _extraCount = cb-ib;
183             }
184             return rgbOut;
185         }
186 #pragma warning restore 0809
187
188         public override void Reset() {
189             _prefix = 0;
190             _extra = null;
191             _baseValue = null;
192         }
193
194         protected override void Dispose(bool disposing) {
195             base.Dispose(disposing);
196
197             if (disposing) {
198                 if (_hash != null) {
199                     _hash.Dispose();
200                 }
201
202                 if (_baseValue != null) {
203                     Array.Clear(_baseValue, 0, _baseValue.Length);
204                 }
205                 if (_extra != null) {
206                     Array.Clear(_extra, 0, _extra.Length);
207                 }
208                 if (_password != null) {
209                     Array.Clear(_password, 0, _password.Length);
210                 }
211                 if (_salt != null) {
212                     Array.Clear(_salt, 0, _salt.Length);
213                 }
214             }
215         }
216
217         [System.Security.SecuritySafeCritical]  // auto-generated
218         public byte[] CryptDeriveKey(string algname, string alghashname, int keySize, byte[] rgbIV)
219         {
220             if (keySize < 0)
221                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKeySize"));
222 #if MONO
223             throw new NotSupportedException ("CspParameters are not supported by Mono");
224 #else
225             int algidhash = X509Utils.NameOrOidToAlgId(alghashname, OidGroup.HashAlgorithm);
226             if (algidhash == 0) 
227                 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidAlgorithm"));
228             int algid = X509Utils.NameOrOidToAlgId(algname, OidGroup.AllGroups);
229             if (algid == 0) 
230                 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidAlgorithm"));
231
232             // Validate the rgbIV array
233             if (rgbIV == null) 
234                 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidIV"));
235
236             byte[] key = null;
237             DeriveKey(ProvHandle, algid, algidhash, 
238                       _password, _password.Length, keySize << 16, rgbIV, rgbIV.Length, 
239                       JitHelpers.GetObjectHandleOnStack(ref key));
240             return key;
241 #endif
242         }
243
244         //
245         // private methods
246         //
247 #if !MONO
248         [System.Security.SecurityCritical]  // auto-generated
249         [ResourceExposure(ResourceScope.None)]
250         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity]
251         private static extern void DeriveKey(SafeProvHandle hProv, int algid, int algidHash, 
252                                              byte[] password, int cbPassword, int dwFlags, byte[] IV, int cbIV, 
253                                              ObjectHandleOnStack retKey);
254 #endif
255         private byte[] ComputeBaseValue() {
256             _hash.Initialize();
257             _hash.TransformBlock(_password, 0, _password.Length, _password, 0);
258             if (_salt != null)
259                 _hash.TransformBlock(_salt, 0, _salt.Length, _salt, 0);
260             _hash.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
261             _baseValue = _hash.Hash;
262             _hash.Initialize();
263
264             for (int i=1; i<(_iterations-1); i++) {
265                 _hash.ComputeHash(_baseValue);
266                 _baseValue = _hash.Hash;
267             }
268             return _baseValue;
269         }
270
271         [System.Security.SecurityCritical]  // auto-generated
272         private byte[] ComputeBytes(int cb) {
273             int                 cbHash;
274             int                 ib = 0;
275             byte[]              rgb;
276
277             _hash.Initialize();
278             cbHash = _hash.HashSize / 8;
279             rgb = new byte[((cb+cbHash-1)/cbHash)*cbHash];
280
281             using (CryptoStream cs = new CryptoStream(Stream.Null, _hash, CryptoStreamMode.Write)) {
282                 HashPrefix(cs);
283                 cs.Write(_baseValue, 0, _baseValue.Length);
284                 cs.Close();
285             }
286
287             Buffer.InternalBlockCopy(_hash.Hash, 0, rgb, ib, cbHash);
288             ib += cbHash;
289
290             while (cb > ib) {
291                 _hash.Initialize();
292                 using (CryptoStream cs = new CryptoStream(Stream.Null, _hash, CryptoStreamMode.Write)) {
293                     HashPrefix(cs);
294                     cs.Write(_baseValue, 0, _baseValue.Length);
295                     cs.Close();
296                 }
297
298                 Buffer.InternalBlockCopy(_hash.Hash, 0, rgb, ib, cbHash);
299                 ib += cbHash;
300             }
301
302             return rgb;
303         }
304
305         void HashPrefix(CryptoStream cs) {
306             int    cb = 0;
307             byte[] rgb = {(byte)'0', (byte)'0', (byte)'0'};
308
309             if (_prefix > 999)
310                     throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_TooManyBytes"));
311
312             if (_prefix >= 100) {
313                 rgb[0] += (byte) (_prefix /100);
314                 cb += 1;
315             }
316             if (_prefix >= 10) {
317                 rgb[cb] += (byte) ((_prefix % 100) / 10);
318                 cb += 1;
319             }
320             if (_prefix > 0) {
321                 rgb[cb] += (byte) (_prefix % 10);
322                 cb += 1;
323                 cs.Write(rgb, 0, cb);
324             }
325             _prefix += 1;
326         }
327     }
328 }