[UnsupportedOSPlatform("browser")]
public partial class Rfc2898DeriveBytes : DeriveBytes
{
- private const int MinimumSaltSize = 8;
-
private byte[] _salt;
private uint _iterations;
private HMAC _hmac;
}
public Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm)
- :this(password, salt, iterations, hashAlgorithm, clearPassword: false, requireMinimumSaltLength: true)
+ :this(password, salt, iterations, hashAlgorithm, clearPassword: false)
{
}
}
public Rfc2898DeriveBytes(string password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm)
- : this(Encoding.UTF8.GetBytes(password), salt, iterations, hashAlgorithm, clearPassword: true, requireMinimumSaltLength: true)
+ : this(Encoding.UTF8.GetBytes(password), salt, iterations, hashAlgorithm, clearPassword: true)
{
}
{
if (saltSize < 0)
throw new ArgumentOutOfRangeException(nameof(saltSize), SR.ArgumentOutOfRange_NeedNonNegNum);
- if (saltSize < MinimumSaltSize)
- throw new ArgumentException(SR.Cryptography_PasswordDerivedBytes_FewBytesSalt, nameof(saltSize));
if (iterations <= 0)
throw new ArgumentOutOfRangeException(nameof(iterations), SR.ArgumentOutOfRange_NeedPosNum);
Initialize();
}
- internal Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword, bool requireMinimumSaltLength)
+ internal Rfc2898DeriveBytes(byte[] password, byte[] salt, int iterations, HashAlgorithmName hashAlgorithm, bool clearPassword)
{
if (salt is null)
throw new ArgumentNullException(nameof(salt));
- if (requireMinimumSaltLength && salt.Length < MinimumSaltSize)
- throw new ArgumentException(SR.Cryptography_PasswordDerivedBytes_FewBytesSalt, nameof(salt));
if (iterations <= 0)
throw new ArgumentOutOfRangeException(nameof(iterations), SR.ArgumentOutOfRange_NeedPosNum);
if (password is null)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
- if (value.Length < MinimumSaltSize)
- throw new ArgumentException(SR.Cryptography_PasswordDerivedBytes_FewBytesSalt);
_salt = new byte[value.Length + sizeof(uint)];
value.AsSpan().CopyTo(_salt);
[SkipOnPlatform(TestPlatforms.Browser, "Not supported on Browser")]
public class Rfc2898Tests
{
- // 8 bytes is the minimum accepted value, by using it we've already assured that the minimum is acceptable.
private static readonly byte[] s_testSalt = new byte[] { 9, 5, 5, 5, 1, 2, 1, 2 };
private static readonly byte[] s_testSaltB = new byte[] { 0, 4, 0, 4, 1, 9, 7, 5 };
private const string TestPassword = "PasswordGoesHere";
}
[Fact]
- public static void Ctor_EmptySalt()
- {
- AssertExtensions.Throws<ArgumentException>("salt", null, () => new Rfc2898DeriveBytes(TestPassword, Array.Empty<byte>(), DefaultIterationCount));
- }
-
- [Fact]
- public static void Ctor_DiminishedSalt()
- {
- AssertExtensions.Throws<ArgumentException>("salt", null, () => new Rfc2898DeriveBytes(TestPassword, new byte[7], DefaultIterationCount));
- }
-
- [Fact]
- public static void Ctor_GenerateZeroSalt()
- {
- AssertExtensions.Throws<ArgumentException>("saltSize", null, () => new Rfc2898DeriveBytes(TestPassword, 0));
- }
-
- [Fact]
public static void Ctor_GenerateNegativeSalt()
{
Assert.Throws<ArgumentOutOfRangeException>(() => new Rfc2898DeriveBytes(TestPassword, -1));
}
[Fact]
- public static void Ctor_GenerateDiminishedSalt()
- {
- AssertExtensions.Throws<ArgumentException>("saltSize", null, () => new Rfc2898DeriveBytes(TestPassword, 7));
- }
-
- [Fact]
public static void Ctor_TooFewIterations()
{
Assert.Throws<ArgumentOutOfRangeException>(() => new Rfc2898DeriveBytes(TestPassword, s_testSalt, 0));
Assert.Throws<ArgumentOutOfRangeException>(() => new Rfc2898DeriveBytes(TestPassword, s_testSalt, int.MinValue / 2));
}
-#if NETCOREAPP
[Fact]
public static void Ctor_EmptyAlgorithm()
{
Assert.Throws<CryptographicException>(
() => new Rfc2898DeriveBytes(TestPassword, s_testSalt, DefaultIterationCount, new HashAlgorithmName("PotatoLemming")));
}
-#endif
[Fact]
public static void Ctor_SaltCopied()
}
[Fact]
+ public static void Ctor_GenerateEmptySalt()
+ {
+ using (var deriveBytes = new Rfc2898DeriveBytes(TestPassword, 0, 1))
+ {
+ Assert.Empty(deriveBytes.Salt);
+ }
+ }
+
+ [Fact]
public static void Ctor_IterationsRespected()
{
using (var deriveBytes = new Rfc2898DeriveBytes(TestPassword, s_testSalt, 1))
{
byte[] output;
- using (var deriveBytes = new Rfc2898DeriveBytes("", new byte[8], 1))
+ using (var deriveBytes = new Rfc2898DeriveBytes("", Array.Empty<byte>(), 1))
{
output = deriveBytes.GetBytes(1);
+ Assert.Empty(deriveBytes.Salt);
}
Assert.Equal(1, output.Length);
- Assert.Equal(0xA6, output[0]);
+ Assert.Equal(0x1E, output[0]);
}
[Fact]
});
}
-#if NETCOREAPP
[Theory]
[MemberData(nameof(KnownValuesTestCases))]
public static void GetBytes_KnownValues_WithAlgorithm(KnownValuesTestCase testCase)
Assert.Equal(hashAlgorithm, pbkdf2.HashAlgorithm);
}
}
-#endif
[Fact]
public static void CryptDeriveKey_NotSupported()
yield return new KnownValuesTestCase
{
+ CaseName = "RFC 6070 Case 1",
+ HashAlgorithmName = "SHA1",
+ Password = "password",
+ Salt = ascii.GetBytes("salt"),
+ IterationCount = 1,
+ AnswerHex="0C60C80F961F0E71F3A9B524AF6012062FE037A6",
+ };
+
+ yield return new KnownValuesTestCase
+ {
CaseName = "RFC 6070 Case 5",
HashAlgorithmName = "SHA1",
Password = "passwordPASSWORDpassword",
"078100F813C1F8388EC233C1397D5E18C6509B5483141EF836C15A34D6DC67" +
"A3C46A45798A2839CFD239749219E9F2EDAD3249EC8221AFB17C0028A4A0A5"),
};
+
+ // Taken from the OneShot tests, which is implemented using native platform APIs.
+ yield return new KnownValuesTestCase
+ {
+ CaseName = "SHA1 empty",
+ HashAlgorithmName = "SHA1",
+ Password = "",
+ Salt = Array.Empty<byte>(),
+ IterationCount = 1,
+ AnswerHex = "1E437A1C79D75BE61E91141DAE20",
+ };
}
public class KnownValuesTestCase