Skip to content

Commit

Permalink
[Android] Resolve Android-specific active issues in System.Net.Securi…
Browse files Browse the repository at this point in the history
…ty and System.Security.Cryptography (#104352)

* Enable ServerAsyncAuthenticate_MismatchProtocols_Fails

* Enable subset of CertificateSelectionCallback_DelayedCertificate_OK

* Enable SslStream_StreamToStream_Alpn_NonMatchingProtocols_Fail and do not assume android backend supports ALPN

* Enable ConnectWithRevocation_WithCallback and pass full chain to ServerCertificateContext

* Enable or permanently disable tests in SslStreamNetworkStreamTest

* Adjust host name data for Android in SslStreamSniTest

* Fix expected outcome of TransportContext_ConnectToServerWithSsl_GetExpectedChannelBindings for Android

* Fix ChainTests active issue on Android

* Fix failing test

* TMP: Print full exception stacktrace

* Adjust expected exception for server authentication protocol mismatch

* Revert "TMP: Print full exception stacktrace"

This reverts commit 1be0a16.

* Adjust the expected exceptions for arm and x86/x64

* Fix assert

* Address review comments
  • Loading branch information
simonrozsival authored Jul 19, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent 5ee8f4a commit 110b5f2
Showing 10 changed files with 66 additions and 23 deletions.
Original file line number Diff line number Diff line change
@@ -735,7 +735,7 @@ private static bool DetermineBinaryFormatterSupport()
{
return false;
}

Assembly assembly = typeof(System.Runtime.Serialization.Formatters.Binary.BinaryFormatter).Assembly;
AssemblyName name = assembly.GetName();
Version assemblyVersion = name.Version;
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@

using Xunit;
using Xunit.Abstractions;
using Microsoft.DotNet.XUnitExtensions;

namespace System.Net.Security.Tests
{
@@ -40,9 +41,13 @@ public void Dispose()
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(false, false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task CertificateSelectionCallback_DelayedCertificate_OK(bool delayCertificate, bool sendClientCertificate)
{
if (delayCertificate && OperatingSystem.IsAndroid())
{
throw new SkipTestException("Android does not support delayed certificate selection.");
}

X509Certificate? remoteCertificate = null;

(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
Original file line number Diff line number Diff line change
@@ -96,7 +96,6 @@ public async Task DefaultConnect_EndToEnd_Ok(string host)
[Theory]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public Task ConnectWithRevocation_WithCallback(bool checkRevocation)
{
X509RevocationMode mode = checkRevocation ? X509RevocationMode.Online : X509RevocationMode.NoCheck;
@@ -266,9 +265,13 @@ private async Task ConnectWithRevocation_WithCallback_Core(

if (offlineContext.HasValue)
{
// on android we need to include the root certificate in the certifiate context
X509Certificate2[] additionalCertificates = OperatingSystem.IsAndroid()
? [issuerCert, rootCert]
: [issuerCert];
serverOpts.ServerCertificateContext = SslStreamCertificateContext.Create(
serverCert,
new X509Certificate2Collection(issuerCert),
new X509Certificate2Collection(additionalCertificates),
offlineContext.GetValueOrDefault());

if (revocationMode == X509RevocationMode.Offline)
Original file line number Diff line number Diff line change
@@ -45,7 +45,6 @@ public async Task ServerAsyncAuthenticate_EachSupportedProtocol_Success(SslProto

[Theory]
[MemberData(nameof(ProtocolMismatchData))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails(
SslProtocols clientProtocol,
SslProtocols serverProtocol)
@@ -60,7 +59,16 @@ public async Task ServerAsyncAuthenticate_MismatchProtocols_Fails(
});

Assert.NotNull(e);
Assert.IsType<AuthenticationException>(e);

if (OperatingSystem.IsAndroid())
{
// On Android running on x64 or x86 the server side sometimes throws IOException instead of AuthenticationException
Assert.True(e is IOException || e is AuthenticationException, $"Unexpected exception type: {e.GetType()}");
}
else
{
Assert.IsType<AuthenticationException>(e);
}
}

[Theory]
Original file line number Diff line number Diff line change
@@ -133,7 +133,6 @@ public async Task SslStream_StreamToStream_Alpn_Success(SslProtocols protocol, L
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task SslStream_StreamToStream_Alpn_NonMatchingProtocols_Fail()
{
(SslStream clientStream, SslStream serverStream) = TestHelper.GetConnectedSslStreams();
@@ -155,7 +154,8 @@ public async Task SslStream_StreamToStream_Alpn_NonMatchingProtocols_Fail()
};

// Test ALPN failure only on platforms that supports ALPN.
if (BackendSupportsAlpn)
// On Android, protocol mismatch won't cause an exception, even though it supports ALPN.
if (BackendSupportsAlpn && !OperatingSystem.IsAndroid())
{
Task t1 = Assert.ThrowsAsync<AuthenticationException>(() => clientStream.AuthenticateAsClientAsync(TestAuthenticateAsync, clientOptions));
await Assert.ThrowsAsync<AuthenticationException>(() => serverStream.AuthenticateAsServerAsync(TestAuthenticateAsync, serverOptions).WaitAsync(TestConfiguration.PassingTestTimeout));
Original file line number Diff line number Diff line change
@@ -695,10 +695,9 @@ public async Task SslStream_NestedAuth_Throws()
[InlineData(false, true)]
[InlineData(false, false)]
[InlineData(true, true)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task SslStream_TargetHostName_Succeeds(bool useEmptyName, bool useCallback)
{
string targetName = useEmptyName ? string.Empty : Guid.NewGuid().ToString("N");
string targetName = useEmptyName ? string.Empty : $"{Guid.NewGuid().ToString("N")}.dot.net";
int count = 0;

(Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
@@ -751,12 +750,16 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
}
}

[Theory]
[ConditionalTheory]
[InlineData(true)]
[InlineData(false)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task SslStream_ServerUntrustedCaWithCustomTrust_OK(bool usePartialChain)
{
if (usePartialChain && OperatingSystem.IsAndroid())
{
throw new SkipTestException("Android does not support partial chain validation.");
}

int split = Random.Shared.Next(0, _certificates.serverChain.Count - 1);

var clientOptions = new SslClientAuthenticationOptions() { TargetHost = "localhost" };
@@ -854,8 +857,8 @@ private async Task SslStream_ClientSendsChain_Core(SslClientAuthenticationOption
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
[ActiveIssue("https://github.com/dotnet/runtime/issues/73862", TestPlatforms.OSX)]
[SkipOnPlatform(TestPlatforms.Android, "It is not possible to add the intermediate certificates to the trust store on Android at runtime.")]
public async Task SslStream_ClientCertificate_SendsChain()
{
// macOS ignores CertificateAuthority
Original file line number Diff line number Diff line change
@@ -21,7 +21,6 @@ public class SslStreamSniTest
{
[Theory]
[MemberData(nameof(HostNameData))]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task SslStream_ClientSendsSNIServerReceives_Ok(string hostName)
{
using X509Certificate serverCert = Configuration.Certificates.GetSelfSignedServerCertificate();
@@ -237,7 +236,6 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task UnencodedHostName_ValidatesCertificate()
{
string rawHostname = "räksmörgås.josefsson.org";
@@ -284,7 +282,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout(
[InlineData("www-.volal.cz")]
[InlineData("www-.colorhexa.com")]
[InlineData("xn--www-7m0a.thegratuit.com")]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
[SkipOnPlatform(TestPlatforms.Android, "Safe invalid IDN hostnames are not supported on Android")]
public async Task SslStream_SafeInvalidIdn_Success(string name)
{
(SslStream client, SslStream server) = TestHelper.GetConnectedSslStreams();
@@ -369,6 +367,16 @@ private async Task WithVirtualConnection(Func<SslStream, SslStream, Task> server

public static IEnumerable<object[]> HostNameData()
{
if (OperatingSystem.IsAndroid())
{
yield return new object[] { "localhost" };
yield return new object[] { "dot.net" };
// max allowed hostname length is 63
yield return new object[] { $"{new string('a', 59)}.net" };
yield return new object[] { "\u017C\u00F3\u0142\u0107g\u0119\u015Bl\u0105ja\u017A\u0144.\u7EA2\u70E7.\u7167\u308A\u713C\u304D" };
yield break;
}

yield return new object[] { "a" };
yield return new object[] { "test" };
// max allowed hostname length is 63
Original file line number Diff line number Diff line change
@@ -14,7 +14,6 @@ namespace System.Net.Security.Tests
public class TransportContextTest
{
[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/68206", TestPlatforms.Android)]
public async Task TransportContext_ConnectToServerWithSsl_GetExpectedChannelBindings()
{
(Stream clientStream, Stream serverStream) = TestHelper.GetConnectedStreams();
@@ -46,9 +45,10 @@ private static void CheckTransportContext(TransportContext context)

Assert.True(cbt1 != null, "ChannelBindingKind.Endpoint token data should be returned.");

if (OperatingSystem.IsMacOS())
if (OperatingSystem.IsMacOS() || OperatingSystem.IsAndroid())
{
Assert.True(cbt2 == null, "ChannelBindingKind.Unique token data is not expected on OSX platform.");
var platform = OperatingSystem.IsMacOS() ? "macOS" : "Android";
Assert.True(cbt2 == null, $"ChannelBindingKind.Unique token data is not expected on {platform}.");
}
else
{
Original file line number Diff line number Diff line change
@@ -1270,7 +1270,6 @@ public static void BuildChainForSelfSignedSha3Certificate()
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/100224", typeof(PlatformDetection), nameof(PlatformDetection.IsAndroid), nameof(PlatformDetection.IsArmOrArm64Process))]
public static void BuildChainForSelfSignedCertificate_WithSha256RsaSignature()
{
using (ChainHolder chainHolder = new ChainHolder())
@@ -1284,12 +1283,22 @@ public static void BuildChainForSelfSignedCertificate_WithSha256RsaSignature()
// minimum be marked UntrustedRoot.

Assert.False(chain.Build(cert));
AssertExtensions.HasFlag(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags());

if (PlatformDetection.IsAndroid)
{
// Android always validates trust as part of building a path,
// so violations comes back as PartialChain with no elements
Assert.Equal(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags());
Assert.Equal(0, chain.ChainElements.Count);
}
else
{
AssertExtensions.HasFlag(X509ChainStatusFlags.UntrustedRoot, chain.AllStatusFlags());
}
}
}

[Fact]
[ActiveIssue("https://github.com/dotnet/runtime/issues/100224", typeof(PlatformDetection), nameof(PlatformDetection.IsAndroid), nameof(PlatformDetection.IsArmOrArm64Process))]
public static void BuildChainForSelfSignedCertificate_WithUnknownOidSignature()
{
using (ChainHolder chainHolder = new ChainHolder())
@@ -1311,6 +1320,12 @@ public static void BuildChainForSelfSignedCertificate_WithUnknownOidSignature()
Assert.False(chain.Build(cert));
AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags());
}
else if (PlatformDetection.IsAndroid)
{
Assert.False(chain.Build(cert));
AssertExtensions.HasFlag(X509ChainStatusFlags.PartialChain, chain.AllStatusFlags());
Assert.Equal(0, chain.ChainElements.Count);
}
else if (PlatformDetection.IsOpenSslSupported)
{
Assert.False(chain.Build(cert));
3 changes: 2 additions & 1 deletion src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -8,7 +8,8 @@
<uses-permission a:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission a:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application a:label="%PackageName%"
a:largeHeap="true">
a:largeHeap="true"
a:usesCleartextTraffic="true">
<activity a:name="net.dot.MainActivity" a:exported="true">
<intent-filter>
<category a:name="android.intent.category.LAUNCHER"/>

0 comments on commit 110b5f2

Please sign in to comment.