Skip to content

Skip RC2 encrypted PKCS12 files on Android for iteration counting #88787

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.DotNet.XUnitExtensions;
using System.Collections.Generic;
using System.Linq;
using Test.Cryptography;
using Xunit;

namespace System.Security.Cryptography.X509Certificates.Tests
Expand All @@ -20,7 +21,7 @@ private static readonly Dictionary<string, PfxInfo> s_certificatesDictionary

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountNotExceedingDefaultLimit_AndNullOrEmptyPassword_MemberData), MemberType = typeof(PfxIterationCountTests))]
public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationCountNotExceedingDefaultLimit(string name, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationCountNotExceedingDefaultLimit(string name, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = iterationCount;
_ = blob;
Expand All @@ -30,6 +31,11 @@ public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationC
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

RemoteExecutor.Invoke((certName) =>
{
AppContext.SetData("System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit", -2);
Expand All @@ -43,7 +49,7 @@ public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationC

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountExceedingDefaultLimit_MemberData), MemberType = typeof(PfxIterationCountTests))]
public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationCountLimitExceeded_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationCountLimitExceeded_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = password;
_ = iterationCount;
Expand All @@ -54,6 +60,11 @@ public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationC
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

RemoteExecutor.Invoke((certName) =>
{
AppContext.SetData("System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit", -2);
Expand All @@ -67,7 +78,7 @@ public void Import_AppContextDataWithValueMinusTwo_ActsAsDefaultLimit_IterationC

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountNotExceedingDefaultLimit_AndNullOrEmptyPassword_MemberData), MemberType = typeof(PfxIterationCountTests))]
public void Import_AppContextDataWithValueZero_IterationCountNotExceedingDefaultLimit_Throws(string name, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_AppContextDataWithValueZero_IterationCountNotExceedingDefaultLimit_Throws(string name, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = iterationCount;
_ = blob;
Expand All @@ -77,6 +88,11 @@ public void Import_AppContextDataWithValueZero_IterationCountNotExceedingDefault
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

RemoteExecutor.Invoke((certName) =>
{
AppContext.SetData("System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit", 0);
Expand All @@ -90,7 +106,7 @@ public void Import_AppContextDataWithValueZero_IterationCountNotExceedingDefault

[ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))]
[MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountExceedingDefaultLimit_MemberData), MemberType = typeof(PfxIterationCountTests))]
public void Import_AppContextDataWithValueMinusOne_IterationCountExceedingDefaultLimit(string name, string password, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_AppContextDataWithValueMinusOne_IterationCountExceedingDefaultLimit(string name, string password, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = password;
_ = blob;
Expand All @@ -101,6 +117,11 @@ public void Import_AppContextDataWithValueMinusOne_IterationCountExceedingDefaul
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

RemoteExecutor.Invoke((certName) =>
{
AppContext.SetData("System.Security.Cryptography.Pkcs12UnspecifiedPasswordIterationLimit", -1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ public abstract partial class PfxIterationCountTests

[ConditionalTheory]
[MemberData(nameof(GetCertsWith_IterationCountNotExceedingDefaultLimit_AndNullOrEmptyPassword_MemberData))]
public void Import_IterationCounLimitNotExceeded_Succeeds(string name, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_IterationCounLimitNotExceeded_Succeeds(string name, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
if (usesPbes2 && !PfxTests.Pkcs12PBES2Supported)
{
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

if (PfxTests.IsPkcs12IterationCountAllowed(iterationCount, PfxTests.DefaultIterations))
{
X509Certificate cert = Import(blob);
Expand All @@ -40,7 +45,7 @@ public void Import_IterationCounLimitNotExceeded_Succeeds(string name, bool uses

[ConditionalTheory]
[MemberData(nameof(GetCertsWith_IterationCountExceedingDefaultLimit_MemberData))]
public void Import_IterationCountLimitExceeded_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_IterationCountLimitExceeded_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = password;
_ = iterationCount;
Expand All @@ -50,13 +55,18 @@ public void Import_IterationCountLimitExceeded_Throws(string name, string passwo
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

CryptographicException ce = Assert.Throws<CryptographicException>(() => Import(blob));
Assert.Contains(FwlinkId, ce.Message);
}

[ConditionalTheory]
[MemberData(nameof(GetCertsWith_IterationCountExceedingDefaultLimit_MemberData))]
public void ImportWithPasswordOrFileName_IterationCountLimitExceeded(string name, string password, bool usesPbes2, byte[] blob, long iterationCount)
public void ImportWithPasswordOrFileName_IterationCountLimitExceeded(string name, string password, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
_ = iterationCount;

Expand All @@ -65,6 +75,11 @@ public void ImportWithPasswordOrFileName_IterationCountLimitExceeded(string name
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

using (TempFileHolder tempFile = new TempFileHolder(blob))
{
string fileName = tempFile.FilePath;
Expand Down Expand Up @@ -100,13 +115,18 @@ internal static void VerifyThrowsCryptoExButDoesNotThrowPfxWithoutPassword(Actio

[ConditionalTheory]
[MemberData(nameof(GetCertsWith_NonNullOrEmptyPassword_MemberData))]
public void Import_NonNullOrEmptyPasswordExpected_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount)
public void Import_NonNullOrEmptyPasswordExpected_Throws(string name, string password, bool usesPbes2, byte[] blob, long iterationCount, bool usesRC2)
{
if (usesPbes2 && !PfxTests.Pkcs12PBES2Supported)
{
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

CryptographicException ce = Assert.ThrowsAny<CryptographicException>(() => Import(blob));

if (PfxTests.IsPkcs12IterationCountAllowed(iterationCount, PfxTests.DefaultIterations))
Expand All @@ -123,7 +143,7 @@ public void Import_NonNullOrEmptyPasswordExpected_Throws(string name, string pas
}
}

[Fact]
[ConditionalFact(typeof(PlatformSupport), nameof(PlatformSupport.IsRC2Supported))]
public void ExportedPfxWithNullPassword_DecryptReturnsValidPaddingWithEmptyPassword()
{
Assert.NotNull(Import(TestData.MsCertificateExportedToPfx_NullPassword));
Expand Down Expand Up @@ -160,7 +180,7 @@ private static List<PfxInfo> GetCertificates()
certificates.Add(new PfxInfo(
nameof(TestData.Pkcs12WindowsDotnetExportEmptyPassword), "", 6000, false, TestData.Pkcs12WindowsDotnetExportEmptyPassword));
certificates.Add(new PfxInfo(
nameof(TestData.Pkcs12MacosKeychainCreated), null, 4097, false, TestData.Pkcs12MacosKeychainCreated));
nameof(TestData.Pkcs12MacosKeychainCreated), null, 4097, false, TestData.Pkcs12MacosKeychainCreated, usesRC2: true));
certificates.Add(new PfxInfo(
nameof(TestData.Pkcs12BuilderSaltWithMacNullPassword), null, 120000, true, TestData.Pkcs12BuilderSaltWithMacNullPassword));
certificates.Add(new PfxInfo(
Expand All @@ -183,7 +203,7 @@ public static IEnumerable<object[]> GetCertsWith_IterationCountNotExceedingDefau
{
if (p.IterationCount <= DefaultIterationLimit && string.IsNullOrEmpty(p.Password))
{
yield return new object[] { p.Name, p.UsesPbes2, p.Blob, p.IterationCount };
yield return new object[] { p.Name, p.UsesPbes2, p.Blob, p.IterationCount, p.UsesRC2 };
}
}
}
Expand All @@ -194,7 +214,7 @@ public static IEnumerable<object[]> GetCertsWith_IterationCountExceedingDefaultL
{
if (p.IterationCount > DefaultIterationLimit)
{
yield return new object[] { p.Name, p.Password, p.UsesPbes2, p.Blob, p.IterationCount };
yield return new object[] { p.Name, p.Password, p.UsesPbes2, p.Blob, p.IterationCount, p.UsesRC2 };
}
}
}
Expand All @@ -205,7 +225,7 @@ public static IEnumerable<object[]> GetCertsWith_NonNullOrEmptyPassword_MemberDa
{
if (!string.IsNullOrEmpty(p.Password))
{
yield return new object[] { p.Name, p.Password, p.UsesPbes2, p.Blob, p.IterationCount };
yield return new object[] { p.Name, p.Password, p.UsesPbes2, p.Blob, p.IterationCount, p.UsesRC2 };
}
}
}
Expand All @@ -218,14 +238,16 @@ public class PfxInfo
internal long IterationCount { get; set; }
internal bool UsesPbes2 { get; set; }
internal byte[] Blob { get; set; }
internal bool UsesRC2 { get; set; }

internal PfxInfo(string name, string password, long iterationCount, bool usesPbes2, byte[] blob)
internal PfxInfo(string name, string password, long iterationCount, bool usesPbes2, byte[] blob, bool usesRC2 = false)
{
Name = name;
Password = password;
IterationCount = iterationCount;
UsesPbes2 = usesPbes2;
Blob = blob;
UsesRC2 = usesRC2;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ public static void CollectionPerphemeralImport_HasKeyName()

[ConditionalTheory]
[MemberData(memberName: nameof(PfxIterationCountTests.GetCertsWith_IterationCountNotExceedingDefaultLimit_AndNullOrEmptyPassword_MemberData), MemberType = typeof(PfxIterationCountTests))]
public static void TestIterationCounter(string name, bool usesPbes2, byte[] blob, int iterationCount)
public static void TestIterationCounter(string name, bool usesPbes2, byte[] blob, int iterationCount, bool usesRC2)
{
_ = iterationCount;

Expand All @@ -482,6 +482,11 @@ public static void TestIterationCounter(string name, bool usesPbes2, byte[] blob
throw new SkipTestException(name + " uses PBES2, which is not supported on this version.");
}

if (usesRC2 && !PlatformSupport.IsRC2Supported)
{
throw new SkipTestException(name + " uses RC2, which is not supported on this platform.");
}

try
{
long count = (long)target(blob, out int bytesConsumed);
Expand Down