Skip to content
Draft
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 @@ -644,7 +644,7 @@ public void RsaDecryptAfterExport()
Assert.Equal(TestData.HelloBytes, output);
}

[Fact]
[ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))]
public void LargeKeyCryptRoundtrip()
{
byte[] output;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public static void PaddedExport()
RSATestHelpers.AssertKeyEquals(diminishedDPParameters, exported);
}

[Fact]
[ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))]
public static void LargeKeyImportExport()
{
RSAParameters imported = TestData.RSA16384Params;
Expand Down Expand Up @@ -367,6 +367,11 @@ internal static RSAParameters MakePublic(in RSAParameters rsaParams)

private static bool TestRsa16384()
{
if (PlatformDetection.IsAndroid)
{
return false;
}

try
{
using (RSA rsa = RSAFactory.Create())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1022,7 +1022,7 @@ public void VerifyExpectedSignature_PssSha256_RSA2048()
modulus2048Signature);
}

[Fact]
[ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))]
public void VerifyExpectedSignature_PssSha256_RSA16384()
{
byte[] modulus2048Signature = (
Expand Down Expand Up @@ -1098,7 +1098,7 @@ public void VerifyExpectedSignature_PssSha256_RSA16384()
modulus2048Signature);
}

[Fact]
[ConditionalFact(typeof(ImportExport), nameof(ImportExport.Supports16384))]
public void VerifyExpectedSignature_PssSha384()
{
byte[] bigModulusSignature = (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Test.Cryptography;
using Xunit;

Expand Down Expand Up @@ -35,11 +37,251 @@ public static void EncryptTamperAADDecrypt(int dataLength, int additionalDataLen
additionalData[0] ^= 1;

byte[] decrypted = new byte[dataLength];
Assert.Throws<AuthenticationTagMismatchException>(
() => chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted, additionalData));
try
{
Assert.Throws<AuthenticationTagMismatchException>(
() => chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted, additionalData));
}
catch (Exception ex) when (ShouldLogAndroidAeadDiagnostics(dataLength, additionalDataLength))
{
Assert.Fail(
CreateAndroidAeadDiagnostics(
nameof(EncryptTamperAADDecrypt),
nameof(ChaCha20Poly1305),
dataLength,
additionalDataLength,
tamperAAD: true,
expectAuthenticationFailure: true,
decryptedMatchesPlaintext: false,
ex));
throw;
}
}
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsAndroid))]
public static void AndroidDiagnostic_TamperedAadDecryptMatrix()
{
StringBuilder diagnostics = new();
AppendAndroidAeadEnvironment(diagnostics, nameof(ChaCha20Poly1305));

int failures = 0;
failures += RunAndroidDiagnosticCase(diagnostics, "baseline-valid-empty", dataLength: 0, additionalDataLength: 0, tamperAAD: false, expectAuthenticationFailure: false);

foreach (int additionalDataLength in Enumerable.Range(1, 33))
{
failures += RunAndroidDiagnosticCase(diagnostics, $"empty-ciphertext-aad-{additionalDataLength}", dataLength: 0, additionalDataLength: additionalDataLength, tamperAAD: true, expectAuthenticationFailure: true);
}

failures += RunAndroidDiagnosticCase(diagnostics, "nonempty-ciphertext-aad-1", dataLength: 1, additionalDataLength: 1, tamperAAD: true, expectAuthenticationFailure: true);
failures += RunAndroidDiagnosticPrimedCase(diagnostics, "target-after-aad30-auth-failure", primerDataLength: 0, primerAdditionalDataLength: 30);
failures += RunAndroidDiagnosticPrimedCase(diagnostics, "target-after-nonempty-auth-failure", primerDataLength: 1, primerAdditionalDataLength: 1);
failures += RunAndroidDiagnosticOversizedRsaPrimedCase(diagnostics, "target-aad1-after-oversized-rsa-import", targetAdditionalDataLength: 1);
failures += RunAndroidDiagnosticOversizedRsaPrimedCase(diagnostics, "target-aad2-after-oversized-rsa-import", targetAdditionalDataLength: 2);
failures += RunAndroidDiagnosticOversizedRsaPrimedCase(diagnostics, "target-aad15-after-oversized-rsa-import", targetAdditionalDataLength: 15);
failures += RunAndroidDiagnosticOversizedRsaPrimedCase(diagnostics, "target-aad30-after-oversized-rsa-import", targetAdditionalDataLength: 30);

if (failures != 0)
{
Assert.Fail(diagnostics.ToString());
}
}
Comment thread
vcsjones marked this conversation as resolved.

private static int RunAndroidDiagnosticCase(
StringBuilder diagnostics,
string caseName,
int dataLength,
int additionalDataLength,
bool tamperAAD,
bool expectAuthenticationFailure)
{
byte[] additionalData = new byte[additionalDataLength];
RandomNumberGenerator.Fill(additionalData);

byte[] plaintext = Enumerable.Range(1, dataLength).Select((x) => (byte)x).ToArray();
byte[] ciphertext = new byte[dataLength];
byte[] key = RandomNumberGenerator.GetBytes(KeySizeInBytes);
byte[] nonce = RandomNumberGenerator.GetBytes(NonceSizeInBytes);
byte[] tag = new byte[TagSizeInBytes];

using var chaChaPoly = new ChaCha20Poly1305(key);
chaChaPoly.Encrypt(nonce, plaintext, ciphertext, tag, additionalData);

if (tamperAAD)
{
additionalData[0] ^= 1;
}

byte[] decrypted = new byte[dataLength];
Exception ex = Record.Exception(() => chaChaPoly.Decrypt(nonce, ciphertext, tag, decrypted, additionalData));
string nativeDiagnostic = GetLastAndroidCipherNativeDiagnostic();
bool decryptedMatchesPlaintext = plaintext.SequenceEqual(decrypted);

AppendAndroidAeadDiagnostics(
diagnostics,
nameof(AndroidDiagnostic_TamperedAadDecryptMatrix),
caseName,
nameof(ChaCha20Poly1305),
dataLength,
additionalDataLength,
tamperAAD,
expectAuthenticationFailure,
decryptedMatchesPlaintext,
ex,
nativeDiagnostic);

if (expectAuthenticationFailure)
{
return ex is AuthenticationTagMismatchException ? 0 : 1;
}

return ex is null && plaintext.SequenceEqual(decrypted) ? 0 : 1;
}
Comment thread
vcsjones marked this conversation as resolved.

private static int RunAndroidDiagnosticPrimedCase(
StringBuilder diagnostics,
string caseName,
int primerDataLength,
int primerAdditionalDataLength)
{
int failures = 0;
failures += RunAndroidDiagnosticCase(
diagnostics,
$"{caseName}-primer",
primerDataLength,
primerAdditionalDataLength,
tamperAAD: true,
expectAuthenticationFailure: true);

failures += RunAndroidDiagnosticCase(
diagnostics,
$"{caseName}-target",
dataLength: 0,
additionalDataLength: 1,
tamperAAD: true,
expectAuthenticationFailure: true);

return failures;
}

private static int RunAndroidDiagnosticOversizedRsaPrimedCase(
StringBuilder diagnostics,
string caseName,
int targetAdditionalDataLength)
{
Exception rsaException = Record.Exception(() =>
{
using RSA rsa = RSA.Create();
byte[] modulus = new byte[2048];
modulus[0] = 0x80;
modulus[^1] = 0x01;

rsa.ImportParameters(new RSAParameters
{
Modulus = modulus,
Exponent = new byte[] { 0x01, 0x00, 0x01 },
});
});

diagnostics.AppendLine(
$"Android AEAD primer diagnostics: case={caseName}, operation=RSA.ImportParameters, " +
$"modulusBytes=2048, exceptionType={rsaException?.GetType().FullName ?? "<none>"}, " +
$"exceptionMessage={rsaException?.Message ?? "<none>"}");

return RunAndroidDiagnosticCase(
diagnostics,
$"{caseName}-target",
dataLength: 0,
additionalDataLength: targetAdditionalDataLength,
tamperAAD: true,
expectAuthenticationFailure: true);
}

private static bool ShouldLogAndroidAeadDiagnostics(int dataLength, int additionalDataLength)
{
return PlatformDetection.IsAndroid && dataLength == 0 && additionalDataLength == 1;
}

private static string CreateAndroidAeadDiagnostics(
string testName,
string algorithm,
int dataLength,
int additionalDataLength,
bool tamperAAD,
bool expectAuthenticationFailure,
bool decryptedMatchesPlaintext,
Exception ex)
{
StringBuilder diagnostics = new();
AppendAndroidAeadEnvironment(diagnostics, algorithm);
AppendAndroidAeadDiagnostics(
diagnostics,
testName,
caseName: "theory",
algorithm,
dataLength,
additionalDataLength,
tamperAAD,
expectAuthenticationFailure,
decryptedMatchesPlaintext,
ex,
GetLastAndroidCipherNativeDiagnostic());

return diagnostics.ToString();
}

private static void AppendAndroidAeadEnvironment(StringBuilder diagnostics, string algorithm)
{
diagnostics.AppendLine($"Android AEAD diagnostics for {algorithm}:");
diagnostics.AppendLine($"RuntimeIdentifier={RuntimeInformation.RuntimeIdentifier}");
diagnostics.AppendLine($"ProcessArchitecture={RuntimeInformation.ProcessArchitecture}");
diagnostics.AppendLine($"OSArchitecture={RuntimeInformation.OSArchitecture}");
diagnostics.AppendLine($"IntPtr.Size={IntPtr.Size}");
diagnostics.AppendLine($"Framework={RuntimeInformation.FrameworkDescription}");
}

private static void AppendAndroidAeadDiagnostics(
StringBuilder diagnostics,
string testName,
string caseName,
string algorithm,
int dataLength,
int additionalDataLength,
bool tamperAAD,
bool expectAuthenticationFailure,
bool decryptedMatchesPlaintext,
Exception ex,
string nativeDiagnostic)
{
diagnostics.AppendLine(
$"Android AEAD diagnostics: test={testName}, case={caseName}, algorithm={algorithm}, " +
$"dataLength={dataLength}, additionalDataLength={additionalDataLength}, " +
$"tamperAAD={tamperAAD}, expectAuthenticationFailure={expectAuthenticationFailure}, " +
$"decryptedMatchesPlaintext={decryptedMatchesPlaintext}, " +
$"exceptionType={ex?.GetType().FullName ?? "<none>"}, " +
$"exceptionMessage={ex?.Message ?? "<none>"}, " +
$"nativeDiagnostic={nativeDiagnostic}");
}

private static string GetLastAndroidCipherNativeDiagnostic()
{
if (!PlatformDetection.IsAndroid)
{
return "<not-android>";
}

byte[] buffer = new byte[2048];
int length = AndroidCryptoNative_CipherGetLastDiagnostic(buffer, buffer.Length);
int terminator = System.Array.IndexOf(buffer, (byte)0);
int bytesToDecode = terminator >= 0 ? terminator : buffer.Length;
string value = Encoding.UTF8.GetString(buffer, 0, bytesToDecode);

return value.Length == 0 ? $"<empty; nativeLength={length}>" : value;
}

[DllImport("libSystem.Security.Cryptography.Native.Android", EntryPoint = "AndroidCryptoNative_CipherGetLastDiagnostic")]
private static extern int AndroidCryptoNative_CipherGetLastDiagnostic(byte[] buffer, int bufferLength);

[Theory]
[InlineData(0)]
[InlineData(1)]
Expand Down
Loading
Loading