using System.Text.Json; using FinalFactory.Rendezvous.Client; using FinalFactory.Rendezvous.Contracts; namespace FinalFactory.Rendezvous.Tests.Contracts; public sealed class TraversalTokenCodecTests { [Fact] public void IntroductionTokenBindsAttemptAndRedactsTheFixedTicket() { JoinAttemptId attemptId = new(Guid.Parse("00000000-0000-0000-0000-000000000301")); string authenticator = Credential('T'); string encoded = NatIntroductionTokenCodec.Encode(attemptId, authenticator); Assert.Equal(NatIntroductionTokenCodec.EncodedLength, encoded.Length); Assert.True(encoded.Length <= ContractLimits.LiteNetLibNatTokenMaxCharacters); Assert.True(NatIntroductionTokenCodec.TryDecode(encoded, out NatIntroductionToken? decoded)); Assert.NotNull(decoded); Assert.Equal(attemptId, decoded.AttemptId); Assert.Equal(encoded, decoded.ConnectionTicket); Assert.DoesNotContain(encoded, decoded.ToString(), StringComparison.Ordinal); } [Fact] public void ConnectionTicketMatchesTheVersionedV1Vector() { using JsonDocument vector = JsonDocument.Parse(ContractTestFiles.Read("connection-ticket.json")); JsonElement root = vector.RootElement; JoinAttemptId attemptId = new(Guid.Parse(root.GetProperty("attemptId").GetString()!)); string authenticator = root.GetProperty("derivedAuthenticator").GetString()!; string ticket = NatIntroductionTokenCodec.Encode(attemptId, authenticator); Assert.Equal(root.GetProperty("connectionTicket").GetString(), ticket); Assert.Equal(root.GetProperty("digest").GetString(), NatIntroductionTokenCodec.ComputeDigest(ticket)); } [Fact] public void IntroductionTokenRejectsNonCanonicalOrAlteredFields() { string valid = NatIntroductionTokenCodec.Encode( new JoinAttemptId(Guid.Parse("abcdef00-0000-0000-0000-000000000302")), Credential('T')); Assert.False(NatIntroductionTokenCodec.TryDecode(null, out _)); Assert.False(NatIntroductionTokenCodec.TryDecode(valid[..^1], out _)); Assert.False(NatIntroductionTokenCodec.TryDecode(valid[..^1] + "!", out _)); Assert.False(NatIntroductionTokenCodec.TryDecode(new string('A', 43), out _)); } [Fact] public void DirectConnectionRequestRoundTripsFixedBoundedPayloadAndRedactsTicket() { JoinAttemptId attemptId = new(Guid.Parse("00000000-0000-0000-0000-000000000303")); string ticket = Credential('D'); byte[] encoded = DirectConnectionRequestCodec.Encode(attemptId, ticket); Assert.Equal(DirectConnectionRequestCodec.EncodedLength, encoded.Length); Assert.True(DirectConnectionRequestCodec.TryDecode(encoded, out DirectConnectionRequest? decoded)); Assert.NotNull(decoded); Assert.Equal(attemptId, decoded.AttemptId); Assert.Equal(ticket, decoded.ConnectionTicket); Assert.DoesNotContain(ticket, decoded.ToString(), StringComparison.Ordinal); encoded[0] ^= 0xff; Assert.False(DirectConnectionRequestCodec.TryDecode(encoded, out _)); Assert.False(DirectConnectionRequestCodec.TryDecode(encoded.AsSpan(1), out _)); } private static string Credential(char value) => new(value, ContractLimits.DerivedCredentialCharacters); }