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
1 parent 5ee8f4a commit 110b5f2
Show file tree
Hide file tree
Showing 10 changed files with 66 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

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

namespace System.Net.Security.Tests
{
Expand Down Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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" };
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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())
Expand All @@ -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));
Expand Down
3 changes: 2 additions & 1 deletion src/tasks/AndroidAppBuilder/Templates/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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"/>
Expand Down

0 comments on commit 110b5f2

Please sign in to comment.