3 // Copyright (c) Microsoft Corporation. All rights reserved.
6 // <OWNER>[....]</OWNER>
10 // Rfc2898DeriveBytes.cs
13 // This implementation follows RFC 2898 recommendations. See http://www.ietf.org/rfc/Rfc2898.txt
14 // It uses HMACSHA1 as the underlying pseudorandom function.
16 namespace System.Security.Cryptography {
17 using System.Globalization;
20 using System.Diagnostics.Contracts;
21 using System.Runtime.CompilerServices;
22 using System.Runtime.InteropServices;
23 using System.Runtime.Versioning;
24 using System.Security.Cryptography.X509Certificates;
26 [System.Runtime.InteropServices.ComVisible(true)]
27 public class Rfc2898DeriveBytes : DeriveBytes
29 private byte[] m_buffer;
30 private byte[] m_salt;
31 private HMACSHA1 m_hmacsha1; // The pseudo-random generator function used in PBKDF2
32 private byte[] m_password;
34 private CspParameters m_cspParams = new CspParameters();
37 private uint m_iterations;
39 private int m_startIndex;
40 private int m_endIndex;
42 private const int BlockSize = 20;
45 // public constructors
48 public Rfc2898DeriveBytes(string password, int saltSize) : this(password, saltSize, 1000) {}
50 // This method needs to be safe critical, because in debug builds the C# compiler will include null
51 // initialization of the _safeProvHandle field in the method. Since SafeProvHandle is critical, a
52 // transparent reference triggers an error using PasswordDeriveBytes.
53 [SecuritySafeCritical]
54 public Rfc2898DeriveBytes(string password, int saltSize, int iterations) {
56 throw new ArgumentOutOfRangeException("saltSize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
57 Contract.EndContractBlock();
59 byte[] salt = new byte[saltSize];
60 Utils.StaticRandomNumberGenerator.GetBytes(salt);
63 IterationCount = iterations;
64 m_password = new UTF8Encoding(false).GetBytes(password);
65 m_hmacsha1 = new HMACSHA1(m_password);
69 public Rfc2898DeriveBytes(string password, byte[] salt) : this(password, salt, 1000) {}
71 public Rfc2898DeriveBytes(string password, byte[] salt, int iterations) : this (new UTF8Encoding(false).GetBytes(password), salt, iterations) {}
73 // This method needs to be safe critical, because in debug builds the C# compiler will include null
74 // initialization of the _safeProvHandle field in the method. Since SafeProvHandle is critical, a
75 // transparent reference triggers an error using PasswordDeriveBytes.
76 [SecuritySafeCritical]
77 public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations) {
79 IterationCount = iterations;
80 m_password = password;
81 m_hmacsha1 = new HMACSHA1(password);
89 public int IterationCount {
90 get { return (int) m_iterations; }
93 throw new ArgumentOutOfRangeException("value", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
94 Contract.EndContractBlock();
95 m_iterations = (uint) value;
101 get { return (byte[]) m_salt.Clone(); }
104 throw new ArgumentNullException("value");
105 if (value.Length < 8)
106 throw new ArgumentException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_FewBytesSalt"));
107 Contract.EndContractBlock();
108 m_salt = (byte[]) value.Clone();
117 public override byte[] GetBytes(int cb) {
119 throw new ArgumentOutOfRangeException("cb", Environment.GetResourceString("ArgumentOutOfRange_NeedPosNum"));
120 Contract.EndContractBlock();
121 byte[] password = new byte[cb];
124 int size = m_endIndex - m_startIndex;
127 Buffer.InternalBlockCopy(m_buffer, m_startIndex, password, 0, size);
128 m_startIndex = m_endIndex = 0;
131 Buffer.InternalBlockCopy(m_buffer, m_startIndex, password, 0, cb);
137 Contract.Assert(m_startIndex == 0 && m_endIndex == 0, "Invalid start or end index in the internal buffer." );
140 byte[] T_block = Func();
141 int remainder = cb - offset;
142 if(remainder > BlockSize) {
143 Buffer.InternalBlockCopy(T_block, 0, password, offset, BlockSize);
146 Buffer.InternalBlockCopy(T_block, 0, password, offset, remainder);
148 Buffer.InternalBlockCopy(T_block, remainder, m_buffer, m_startIndex, BlockSize - remainder);
149 m_endIndex += (BlockSize - remainder);
156 public override void Reset() {
160 protected override void Dispose(bool disposing) {
161 base.Dispose(disposing);
164 if (m_hmacsha1 != null) {
165 ((IDisposable)m_hmacsha1).Dispose();
168 if (m_buffer != null) {
169 Array.Clear(m_buffer, 0, m_buffer.Length);
171 if (m_salt != null) {
172 Array.Clear(m_salt, 0, m_salt.Length);
177 private void Initialize() {
178 if (m_buffer != null)
179 Array.Clear(m_buffer, 0, m_buffer.Length);
180 m_buffer = new byte[BlockSize];
182 m_startIndex = m_endIndex = 0;
185 // This function is defined as follow :
186 // Func (S, i) = HMAC(S || i) | HMAC2(S || i) | ... | HMAC(iterations) (S || i)
187 // where i is the block number.
188 private byte[] Func () {
189 byte[] INT_block = Utils.Int(m_block);
191 m_hmacsha1.TransformBlock(m_salt, 0, m_salt.Length, null, 0);
192 m_hmacsha1.TransformBlock(INT_block, 0, INT_block.Length, null, 0);
193 m_hmacsha1.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
194 byte[] temp = m_hmacsha1.HashValue;
195 m_hmacsha1.Initialize();
198 for (int i = 2; i <= m_iterations; i++) {
199 m_hmacsha1.TransformBlock(temp, 0, temp.Length, null, 0);
200 m_hmacsha1.TransformFinalBlock(EmptyArray<Byte>.Value, 0, 0);
201 temp = m_hmacsha1.HashValue;
202 for (int j = 0; j < BlockSize; j++) {
205 m_hmacsha1.Initialize();
208 // increment the block count.
212 [System.Security.SecuritySafeCritical] // auto-generated
213 public byte[] CryptDeriveKey(string algname, string alghashname, int keySize, byte[] rgbIV)
216 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKeySize"));
219 throw new NotSupportedException ("CspParameters are not supported by Mono");
221 int algidhash = X509Utils.NameOrOidToAlgId(alghashname, OidGroup.HashAlgorithm);
223 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidAlgorithm"));
225 int algid = X509Utils.NameOrOidToAlgId(algname, OidGroup.AllGroups);
227 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidAlgorithm"));
229 // Validate the rgbIV array
231 throw new CryptographicException(Environment.GetResourceString("Cryptography_PasswordDerivedBytes_InvalidIV"));
234 DeriveKey(ProvHandle, algid, algidhash,
235 m_password, m_password.Length, keySize << 16, rgbIV, rgbIV.Length,
236 JitHelpers.GetObjectHandleOnStack(ref key));
242 [System.Security.SecurityCritical] // auto-generated
243 private SafeProvHandle _safeProvHandle = null;
244 private SafeProvHandle ProvHandle
246 [System.Security.SecurityCritical] // auto-generated
249 if (_safeProvHandle == null)
253 if (_safeProvHandle == null)
255 SafeProvHandle safeProvHandle = Utils.AcquireProvHandle(m_cspParams);
256 System.Threading.Thread.MemoryBarrier();
257 _safeProvHandle = safeProvHandle;
261 return _safeProvHandle;
265 [System.Security.SecurityCritical] // auto-generated
266 [ResourceExposure(ResourceScope.None)]
267 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode), SuppressUnmanagedCodeSecurity]
268 private static extern void DeriveKey(SafeProvHandle hProv, int algid, int algidHash,
269 byte[] password, int cbPassword, int dwFlags, byte[] IV, int cbIV,
270 ObjectHandleOnStack retKey);