diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Browser.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Browser.cs index 8365c887ee8b1..dc087adb84736 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Browser.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Browser.cs @@ -65,14 +65,12 @@ private static void FillManaged( Span destination) { using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes( - password.ToArray(), - salt.ToArray(), + password, + salt, iterations, - hashAlgorithmName, - clearPassword: true)) + hashAlgorithmName)) { - byte[] result = deriveBytes.GetBytes(destination.Length); - result.AsSpan().CopyTo(destination); + deriveBytes.GetBytes(destination); } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Managed.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Managed.cs index 6369309d4db5b..f94d9e2fc1f5c 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Managed.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Pbkdf2Implementation.Managed.cs @@ -19,14 +19,12 @@ public static unsafe void Fill( Debug.Assert(hashAlgorithmName.Name is not null); using (Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes( - password.ToArray(), - salt.ToArray(), + password, + salt, iterations, - hashAlgorithmName, - clearPassword: true)) + hashAlgorithmName)) { - byte[] result = deriveBytes.GetBytes(destination.Length); - result.AsSpan().CopyTo(destination); + deriveBytes.GetBytes(destination); } } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs index 595eca3457043..d82c892ded8dd 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/Rfc2898DeriveBytes.cs @@ -16,7 +16,7 @@ public partial class Rfc2898DeriveBytes : DeriveBytes { private byte[] _salt; private uint _iterations; - private HMAC _hmac; + private IncrementalHash _hmac; private readonly int _blockSize; private byte[] _buffer; @@ -84,34 +84,36 @@ public Rfc2898DeriveBytes(string password, int saltSize, int iterations, HashAlg HashAlgorithm = hashAlgorithm; _hmac = OpenHmac(passwordBytes); CryptographicOperations.ZeroMemory(passwordBytes); - // _blockSize is in bytes, HashSize is in bits. - _blockSize = _hmac.HashSize >> 3; + _blockSize = _hmac.HashLengthInBytes; Initialize(); } - internal Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword) + internal Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword) : + this( + new ReadOnlySpan(password ?? throw new NullReferenceException()), // This "should" be ArgumentNullException but for compat, we throw NullReferenceException. + new ReadOnlySpan(salt ?? throw new ArgumentNullException(nameof(salt))), + iterations, + hashAlgorithm) { - ArgumentNullException.ThrowIfNull(salt); + if (clearPassword) + { + CryptographicOperations.ZeroMemory(password); + } + } + internal Rfc2898DeriveBytes(ReadOnlySpan password, ReadOnlySpan salt, int iterations, HashAlgorithmName hashAlgorithm) + { if (iterations <= 0) throw new ArgumentOutOfRangeException(nameof(iterations), SR.ArgumentOutOfRange_NeedPosNum); - if (password is null) - throw new NullReferenceException(); // This "should" be ArgumentNullException but for compat, we throw NullReferenceException. _salt = new byte[salt.Length + sizeof(uint)]; - salt.AsSpan().CopyTo(_salt); + salt.CopyTo(_salt); _iterations = (uint)iterations; HashAlgorithm = hashAlgorithm; _hmac = OpenHmac(password); - if (clearPassword) - { - CryptographicOperations.ZeroMemory(password); - } - - // _blockSize is in bytes, HashSize is in bits. - _blockSize = _hmac.HashSize >> 3; + _blockSize = _hmac.HashLengthInBytes; Initialize(); } @@ -167,27 +169,35 @@ protected override void Dispose(bool disposing) public override byte[] GetBytes(int cb) { - Debug.Assert(_blockSize > 0); - if (cb <= 0) throw new ArgumentOutOfRangeException(nameof(cb), SR.ArgumentOutOfRange_NeedPosNum); - byte[] password = new byte[cb]; + byte[] ret = new byte[cb]; + GetBytes(ret); + return ret; + } + + internal void GetBytes(Span destination) + { + Debug.Assert(_blockSize > 0); + int cb = destination.Length; int offset = 0; int size = _endIndex - _startIndex; + ReadOnlySpan bufferSpan = _buffer; + if (size > 0) { if (cb >= size) { - Buffer.BlockCopy(_buffer, _startIndex, password, 0, size); + bufferSpan.Slice(_startIndex, size).CopyTo(destination); _startIndex = _endIndex = 0; offset += size; } else { - Buffer.BlockCopy(_buffer, _startIndex, password, 0, cb); + bufferSpan.Slice(_startIndex, cb).CopyTo(destination); _startIndex += cb; - return password; + return; } } @@ -199,18 +209,17 @@ public override byte[] GetBytes(int cb) int remainder = cb - offset; if (remainder >= _blockSize) { - Buffer.BlockCopy(_buffer, 0, password, offset, _blockSize); + bufferSpan.Slice(0, _blockSize).CopyTo(destination.Slice(offset)); offset += _blockSize; } else { - Buffer.BlockCopy(_buffer, 0, password, offset, remainder); + bufferSpan.Slice(0, remainder).CopyTo(destination.Slice(offset)); _startIndex = remainder; _endIndex = _buffer.Length; - return password; + return; } } - return password; } [Obsolete(Obsoletions.Rfc2898CryptDeriveKeyMessage, DiagnosticId = Obsoletions.Rfc2898CryptDeriveKeyDiagId, UrlFormat = Obsoletions.SharedUrlFormat)] @@ -230,26 +239,25 @@ public override void Reset() Initialize(); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA5350", Justification = "HMACSHA1 is needed for compat. (https://github.com/dotnet/runtime/issues/17618)")] - private HMAC OpenHmac(byte[] password) + private IncrementalHash OpenHmac(ReadOnlySpan password) { - Debug.Assert(password != null); - HashAlgorithmName hashAlgorithm = HashAlgorithm; if (string.IsNullOrEmpty(hashAlgorithm.Name)) + { throw new CryptographicException(SR.Cryptography_HashAlgorithmNameNullOrEmpty); + } - if (hashAlgorithm == HashAlgorithmName.SHA1) - return new HMACSHA1(password); - if (hashAlgorithm == HashAlgorithmName.SHA256) - return new HMACSHA256(password); - if (hashAlgorithm == HashAlgorithmName.SHA384) - return new HMACSHA384(password); - if (hashAlgorithm == HashAlgorithmName.SHA512) - return new HMACSHA512(password); + // Restrict the HashAlgorithmName to known hashes, particularly excluding MD5. + if (hashAlgorithm != HashAlgorithmName.SHA1 && + hashAlgorithm != HashAlgorithmName.SHA256 && + hashAlgorithm != HashAlgorithmName.SHA384 && + hashAlgorithm != HashAlgorithmName.SHA512) + { + throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name)); + } - throw new CryptographicException(SR.Format(SR.Cryptography_UnknownHashAlgorithm, hashAlgorithm.Name)); + return IncrementalHash.CreateHMAC(hashAlgorithm, password); } [MemberNotNull(nameof(_buffer))] @@ -281,20 +289,17 @@ private void Func() // Span uiSpan = stackalloc byte[64]; uiSpan = uiSpan.Slice(0, _blockSize); - - if (!_hmac.TryComputeHash(_salt, uiSpan, out int bytesWritten) || bytesWritten != _blockSize) - { - throw new CryptographicException(); - } + _hmac.AppendData(_salt); + int bytesWritten = _hmac.GetHashAndReset(uiSpan); + Debug.Assert(bytesWritten == _blockSize); uiSpan.CopyTo(_buffer); for (int i = 2; i <= _iterations; i++) { - if (!_hmac.TryComputeHash(uiSpan, uiSpan, out bytesWritten) || bytesWritten != _blockSize) - { - throw new CryptographicException(); - } + _hmac.AppendData(uiSpan); + bytesWritten = _hmac.GetHashAndReset(uiSpan); + Debug.Assert(bytesWritten == _blockSize); for (int j = _buffer.Length - 1; j >= 0; j--) {