using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text.Json;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
using Entropy = XFESystem.NativeEntropy.NativeEntropy;
if (args.Length != 2 || args[0] is not ("production" or "failure" or "missing-export")) throw new ArgumentException("AdapterChecks <production|failure|missing-export> <native dll>");
string mode = args[0], libraryPath = Path.GetFullPath(args[1]);
nint library = NativeLibrary.Load(libraryPath);
NativeLibrary.SetDllImportResolver(typeof(Entropy).Assembly, (name, _, _) => name == "xfe_pal" ? library : 0);
var checks = new List<object>(); bool passed = false; string? failure = null;
void Check(string name, bool condition)
{ checks.Add(new { Name = name, Passed = condition }); if (!condition) throw new InvalidOperationException(name); }
void Throws<T>(string name, Action action) where T : Exception
{
bool thrown = false;
try { action(); } catch (T) { thrown = true; }
Check(name, thrown);
}
bool HasCryptoFailure(Exception error)
{
for (Exception? current = error; current is not null; current = current.InnerException)
if (current is CryptographicException && current.Message.Contains("native hardware entropy source failed", StringComparison.Ordinal)) return true;
return false;
}
void BouncyCastleFailsClosed()
{
bool rejected = false;
try { new SecureRandom().NextBytes(new byte[32]); }
catch (Exception error) { rejected = HasCryptoFailure(error); }
Check("PatchedBouncyCastleDefaultSeedingRejectsUnavailableHardware", rejected);
}
try
{
Check("AdapterHasNoBouncyCastleDependency", !typeof(Entropy).Assembly.GetReferencedAssemblies().Any(name => name.Name!.Contains("BouncyCastle", StringComparison.Ordinal)));
Check("PrivateBouncyCastleReferencesNativeAdapter", typeof(SecureRandom).Assembly.GetReferencedAssemblies().Any(name => name.Name == "XFESystem.NativeEntropy"));
Entropy.Fill(Span<byte>.Empty); Check("EmptySpanDoesNotRequireHardware", true);
byte[] tooLarge = Enumerable.Repeat((byte)0x55, Entropy.MaximumRequestBytes + 1).ToArray();
Throws<ArgumentOutOfRangeException>("OneMiBBoundRejectedBeforeNativeAccess", () => Entropy.Fill(tooLarge));
Check("OversizedBufferRemainsUntouched", tooLarge.All(value => value == 0x55));
using var generator = Entropy.Create();
Check("CreateUsesOnlyTheExplicitHardwareSubclass", generator.GetType().Name.Contains("HardwareRandomNumberGenerator", StringComparison.Ordinal));
Throws<ArgumentNullException>("NullGeneratorRejected", () => Entropy.GetBytes(null!, new byte[4]));
Throws<ArgumentNullException>("NullArrayRejected", () => Entropy.GetBytes(generator, (byte[])null!));
Throws<ArgumentOutOfRangeException>("InvalidSliceRejected", () => Entropy.GetBytes(generator, new byte[4], 3, 2));
using var foreign = new ForeignRandom();
Throws<CryptographicException>("ExplicitForeignProviderRejected", () => Entropy.GetBytes(foreign, new byte[32]));
Check("ForeignProviderNeverRan", foreign.Calls == 0);
Entropy.Dispose(generator); Entropy.Dispose(generator);
Throws<ObjectDisposedException>("DisposedStaticArrayRejected", () => Entropy.GetBytes(generator, new byte[4]));
Throws<ObjectDisposedException>("DisposedStaticSliceRejected", () => Entropy.GetBytes(generator, new byte[8], 2, 4));
Throws<ObjectDisposedException>("DisposedStaticSpanRejected", () => Entropy.GetBytes(generator, new byte[8].AsSpan(2, 4)));
Throws<ObjectDisposedException>("DisposedInstanceRejected", () => generator.GetBytes(new byte[4]));
if (mode == "production")
{
byte[] first = new byte[32], second = new byte[32]; Entropy.Fill(first); Entropy.Fill(second);
Check("ActualProductionCpuBytesAreNonconstant", first.Distinct().Count() > 1 && second.Distinct().Count() > 1);
Check("SeparateActualHardwareCallsDiffer", !first.SequenceEqual(second));
using var actual = Entropy.Create(); byte[] data = Enumerable.Repeat((byte)0x55, 80).ToArray();
Entropy.GetBytes(actual, data, 16, 32);
Check("OffsetPrefixAndSuffixUntouched", data.Take(16).All(value => value == 0x55) && data.Skip(48).All(value => value == 0x55));
Check("OffsetSliceUsesActualHardware", data.Skip(16).Take(32).Distinct().Count() > 1);
Entropy.GetBytes(actual, data.AsSpan(8, 24)); Check("StaticSpanUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
actual.GetBytes(data); Check("InstanceArrayUsesActualHardware", data.Distinct().Count() > 1);
actual.GetBytes(data.AsSpan(8, 24)); Check("InstanceSpanUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
actual.GetBytes(data, 8, 24); Check("InstanceSliceUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
var random = new SecureRandom(); random.NextBytes(first);
Check("PatchedBouncyCastleDefaultSecureRandomSeeding", first.Distinct().Count() > 1);
var api = new CryptoApiRandomGenerator(); api.NextBytes(first); api.Dispose();
Check("PatchedBouncyCastleCryptoApiRandomGenerator", first.Distinct().Count() > 1);
byte[] seed = new CryptoApiEntropySourceProvider().Get(256).GetEntropy();
Check("PatchedBouncyCastleCryptoApiEntropySourceProvider", seed.Length == 32 && seed.Distinct().Count() > 1);
SecureRandom.GetInstance("SHA256PRNG", true).NextBytes(second);
Check("PatchedBouncyCastleAutoSeededSha256Prng", second.Distinct().Count() > 1);
}
else
{
byte[] data = Enumerable.Repeat((byte)0x55, 64).ToArray();
if (mode == "missing-export")
Throws<EntryPointNotFoundException>("MissingNativeEntryIsRefused", () => Entropy.Fill(data.AsSpan(8, 32)));
else
Throws<CryptographicException>("EveryNonSuccessNativeResultIsRefused", () => Entropy.Fill(data.AsSpan(8, 32)));
Check("FailureClearsWholeDestinationSpan", data.Skip(8).Take(32).All(value => value == 0));
Check("FailureDoesNotTouchOutsideDestinationSpan", data.Take(8).Concat(data.Skip(40)).All(value => value == 0x55));
if (mode == "failure") BouncyCastleFailsClosed();
}
passed = true;
}
catch (Exception error) { failure = error.ToString(); }
Console.WriteLine(JsonSerializer.Serialize(new { Kind = "NativeEntropyAdapterHostChecks", Passed = passed, Mode = mode,
HardwareEntropyProof = mode == "production", FailureOnlyTestProvider = mode == "failure", GuestExecutionVerified = false,
GuestTlsVerified = false, EntropyQualityCertified = false, NativeLibrary = libraryPath, Checks = checks, Failure = failure }));
return passed ? 0 : 1;
sealed class ForeignRandom : RandomNumberGenerator
{
public int Calls { get; private set; }
public override void GetBytes(byte[] data) { Calls++; throw new InvalidOperationException("A foreign source must never run."); }
}
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Text.Json;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Security;
using Entropy = XFESystem.NativeEntropy.NativeEntropy;
if (args.Length != 2 || args[0] is not ("production" or "failure" or "missing-export")) throw new ArgumentException("AdapterChecks <production|failure|missing-export> <native dll>");
string mode = args[0], libraryPath = Path.GetFullPath(args[1]);
nint library = NativeLibrary.Load(libraryPath);
NativeLibrary.SetDllImportResolver(typeof(Entropy).Assembly, (name, _, _) => name == "xfe_pal" ? library : 0);
var checks = new List<object>(); bool passed = false; string? failure = null;
void Check(string name, bool condition)
{ checks.Add(new { Name = name, Passed = condition }); if (!condition) throw new InvalidOperationException(name); }
void Throws<T>(string name, Action action) where T : Exception
{
bool thrown = false;
try { action(); } catch (T) { thrown = true; }
Check(name, thrown);
}
bool HasCryptoFailure(Exception error)
{
for (Exception? current = error; current is not null; current = current.InnerException)
if (current is CryptographicException && current.Message.Contains("native hardware entropy source failed", StringComparison.Ordinal)) return true;
return false;
}
void BouncyCastleFailsClosed()
{
bool rejected = false;
try { new SecureRandom().NextBytes(new byte[32]); }
catch (Exception error) { rejected = HasCryptoFailure(error); }
Check("PatchedBouncyCastleDefaultSeedingRejectsUnavailableHardware", rejected);
}
try
{
Check("AdapterHasNoBouncyCastleDependency", !typeof(Entropy).Assembly.GetReferencedAssemblies().Any(name => name.Name!.Contains("BouncyCastle", StringComparison.Ordinal)));
Check("PrivateBouncyCastleReferencesNativeAdapter", typeof(SecureRandom).Assembly.GetReferencedAssemblies().Any(name => name.Name == "XFESystem.NativeEntropy"));
Entropy.Fill(Span<byte>.Empty); Check("EmptySpanDoesNotRequireHardware", true);
byte[] tooLarge = Enumerable.Repeat((byte)0x55, Entropy.MaximumRequestBytes + 1).ToArray();
Throws<ArgumentOutOfRangeException>("OneMiBBoundRejectedBeforeNativeAccess", () => Entropy.Fill(tooLarge));
Check("OversizedBufferRemainsUntouched", tooLarge.All(value => value == 0x55));
using var generator = Entropy.Create();
Check("CreateUsesOnlyTheExplicitHardwareSubclass", generator.GetType().Name.Contains("HardwareRandomNumberGenerator", StringComparison.Ordinal));
Throws<ArgumentNullException>("NullGeneratorRejected", () => Entropy.GetBytes(null!, new byte[4]));
Throws<ArgumentNullException>("NullArrayRejected", () => Entropy.GetBytes(generator, (byte[])null!));
Throws<ArgumentOutOfRangeException>("InvalidSliceRejected", () => Entropy.GetBytes(generator, new byte[4], 3, 2));
using var foreign = new ForeignRandom();
Throws<CryptographicException>("ExplicitForeignProviderRejected", () => Entropy.GetBytes(foreign, new byte[32]));
Check("ForeignProviderNeverRan", foreign.Calls == 0);
Entropy.Dispose(generator); Entropy.Dispose(generator);
Throws<ObjectDisposedException>("DisposedStaticArrayRejected", () => Entropy.GetBytes(generator, new byte[4]));
Throws<ObjectDisposedException>("DisposedStaticSliceRejected", () => Entropy.GetBytes(generator, new byte[8], 2, 4));
Throws<ObjectDisposedException>("DisposedStaticSpanRejected", () => Entropy.GetBytes(generator, new byte[8].AsSpan(2, 4)));
Throws<ObjectDisposedException>("DisposedInstanceRejected", () => generator.GetBytes(new byte[4]));
if (mode == "production")
{
byte[] first = new byte[32], second = new byte[32]; Entropy.Fill(first); Entropy.Fill(second);
Check("ActualProductionCpuBytesAreNonconstant", first.Distinct().Count() > 1 && second.Distinct().Count() > 1);
Check("SeparateActualHardwareCallsDiffer", !first.SequenceEqual(second));
using var actual = Entropy.Create(); byte[] data = Enumerable.Repeat((byte)0x55, 80).ToArray();
Entropy.GetBytes(actual, data, 16, 32);
Check("OffsetPrefixAndSuffixUntouched", data.Take(16).All(value => value == 0x55) && data.Skip(48).All(value => value == 0x55));
Check("OffsetSliceUsesActualHardware", data.Skip(16).Take(32).Distinct().Count() > 1);
Entropy.GetBytes(actual, data.AsSpan(8, 24)); Check("StaticSpanUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
actual.GetBytes(data); Check("InstanceArrayUsesActualHardware", data.Distinct().Count() > 1);
actual.GetBytes(data.AsSpan(8, 24)); Check("InstanceSpanUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
actual.GetBytes(data, 8, 24); Check("InstanceSliceUsesActualHardware", data.Skip(8).Take(24).Distinct().Count() > 1);
var random = new SecureRandom(); random.NextBytes(first);
Check("PatchedBouncyCastleDefaultSecureRandomSeeding", first.Distinct().Count() > 1);
var api = new CryptoApiRandomGenerator(); api.NextBytes(first); api.Dispose();
Check("PatchedBouncyCastleCryptoApiRandomGenerator", first.Distinct().Count() > 1);
byte[] seed = new CryptoApiEntropySourceProvider().Get(256).GetEntropy();
Check("PatchedBouncyCastleCryptoApiEntropySourceProvider", seed.Length == 32 && seed.Distinct().Count() > 1);
SecureRandom.GetInstance("SHA256PRNG", true).NextBytes(second);
Check("PatchedBouncyCastleAutoSeededSha256Prng", second.Distinct().Count() > 1);
}
else
{
byte[] data = Enumerable.Repeat((byte)0x55, 64).ToArray();
if (mode == "missing-export")
Throws<EntryPointNotFoundException>("MissingNativeEntryIsRefused", () => Entropy.Fill(data.AsSpan(8, 32)));
else
Throws<CryptographicException>("EveryNonSuccessNativeResultIsRefused", () => Entropy.Fill(data.AsSpan(8, 32)));
Check("FailureClearsWholeDestinationSpan", data.Skip(8).Take(32).All(value => value == 0));
Check("FailureDoesNotTouchOutsideDestinationSpan", data.Take(8).Concat(data.Skip(40)).All(value => value == 0x55));
if (mode == "failure") BouncyCastleFailsClosed();
}
passed = true;
}
catch (Exception error) { failure = error.ToString(); }
Console.WriteLine(JsonSerializer.Serialize(new { Kind = "NativeEntropyAdapterHostChecks", Passed = passed, Mode = mode,
HardwareEntropyProof = mode == "production", FailureOnlyTestProvider = mode == "failure", GuestExecutionVerified = false,
GuestTlsVerified = false, EntropyQualityCertified = false, NativeLibrary = libraryPath, Checks = checks, Failure = failure }));
return passed ? 0 : 1;
sealed class ForeignRandom : RandomNumberGenerator
{
public int Calls { get; private set; }
public override void GetBytes(byte[] data) { Calls++; throw new InvalidOperationException("A foreign source must never run."); }
}