using StellaOps.AirGap.Time.Models; using StellaOps.AirGap.Time.Services; namespace StellaOps.AirGap.Time.Tests; /// /// Tests for Rfc3161Verifier with real SignedCms verification. /// Per AIRGAP-TIME-57-001: Trusted time-anchor service. /// public class Rfc3161VerifierTests { private readonly Rfc3161Verifier _verifier = new(); [Fact] public void Verify_ReturnsFailure_WhenTrustRootsEmpty() { var token = new byte[] { 0x01, 0x02, 0x03 }; var result = _verifier.Verify(token, Array.Empty(), out var anchor); Assert.False(result.IsValid); Assert.Equal("rfc3161-trust-roots-required", result.Reason); Assert.Equal(TimeAnchor.Unknown, anchor); } [Fact] public void Verify_ReturnsFailure_WhenTokenEmpty() { var trust = new[] { new TimeTrustRoot("tsa-root", new byte[] { 0x01 }, "rsa") }; var result = _verifier.Verify(ReadOnlySpan.Empty, trust, out var anchor); Assert.False(result.IsValid); Assert.Equal("rfc3161-token-empty", result.Reason); Assert.Equal(TimeAnchor.Unknown, anchor); } [Fact] public void Verify_ReturnsFailure_WhenInvalidAsn1Structure() { var token = new byte[] { 0x01, 0x02, 0x03 }; // Invalid ASN.1 var trust = new[] { new TimeTrustRoot("tsa-root", new byte[] { 0x01 }, "rsa") }; var result = _verifier.Verify(token, trust, out var anchor); Assert.False(result.IsValid); Assert.Contains("rfc3161-", result.Reason); Assert.Equal(TimeAnchor.Unknown, anchor); } [Fact] public void Verify_ProducesTokenDigest() { var token = new byte[] { 0x30, 0x00 }; // Empty SEQUENCE (minimal valid ASN.1) var trust = new[] { new TimeTrustRoot("tsa-root", new byte[] { 0x01 }, "rsa") }; var result = _verifier.Verify(token, trust, out _); // Should fail on CMS decode but attempt was made Assert.False(result.IsValid); Assert.Contains("rfc3161-", result.Reason); } [Fact] public void Verify_HandlesExceptionsGracefully() { // Create bytes that might cause internal exceptions var token = new byte[256]; new Random(42).NextBytes(token); var trust = new[] { new TimeTrustRoot("tsa-root", new byte[] { 0x01 }, "rsa") }; var result = _verifier.Verify(token, trust, out var anchor); // Should not throw, should return failure result Assert.False(result.IsValid); Assert.Contains("rfc3161-", result.Reason); Assert.Equal(TimeAnchor.Unknown, anchor); } [Fact] public void Verify_ReportsDecodeErrorForMalformedCms() { // Create something that looks like CMS but isn't valid var token = new byte[] { 0x30, 0x82, 0x00, 0x10, 0x06, 0x09 }; var trust = new[] { new TimeTrustRoot("tsa-root", new byte[] { 0x01 }, "rsa") }; var result = _verifier.Verify(token, trust, out _); Assert.False(result.IsValid); // Should report either decode or error Assert.True(result.Reason?.Contains("rfc3161-") ?? false); } }