84 lines
2.6 KiB
C#
84 lines
2.6 KiB
C#
using System.Text;
|
|
using FinalFactory.Rendezvous.Contracts;
|
|
|
|
namespace FinalFactory.Rendezvous.Client;
|
|
|
|
public sealed class DirectConnectionRequest
|
|
{
|
|
public JoinAttemptId AttemptId { get; set; }
|
|
public string ConnectionTicket { get; set; } = string.Empty;
|
|
|
|
public override string ToString() =>
|
|
$"[DirectConnectionRequest {AttemptId}; ticket redacted]";
|
|
}
|
|
|
|
public static class DirectConnectionRequestCodec
|
|
{
|
|
public const int EncodedLength = 63;
|
|
|
|
private const int MagicLength = 4;
|
|
private const int AttemptIdLength = 16;
|
|
private const int TicketLength = ContractLimits.DerivedCredentialCharacters;
|
|
private static readonly byte[] Magic = [(byte)'R', (byte)'V', (byte)'D', (byte)'1'];
|
|
|
|
public static bool IsRendezvousRequest(ReadOnlySpan<byte> encoded) =>
|
|
encoded.Length >= MagicLength && encoded[..MagicLength].SequenceEqual(Magic);
|
|
|
|
public static byte[] Encode(JoinAttemptId attemptId, string connectionTicket)
|
|
{
|
|
if (attemptId.Value == Guid.Empty
|
|
|| connectionTicket is null
|
|
|| connectionTicket.Length != TicketLength
|
|
|| !ContractValidation.IsConnectionTicketValid(connectionTicket))
|
|
{
|
|
throw new ArgumentException("The direct connection request fields are invalid.");
|
|
}
|
|
|
|
byte[] encoded = new byte[EncodedLength];
|
|
Magic.CopyTo(encoded, 0);
|
|
if (!attemptId.Value.TryWriteBytes(encoded.AsSpan(MagicLength, AttemptIdLength)))
|
|
{
|
|
throw new InvalidOperationException("The join attempt identifier could not be encoded.");
|
|
}
|
|
|
|
Encoding.ASCII.GetBytes(
|
|
connectionTicket,
|
|
0,
|
|
connectionTicket.Length,
|
|
encoded,
|
|
MagicLength + AttemptIdLength);
|
|
return encoded;
|
|
}
|
|
|
|
public static bool TryDecode(
|
|
ReadOnlySpan<byte> encoded,
|
|
out DirectConnectionRequest? request)
|
|
{
|
|
request = null;
|
|
if (encoded.Length != EncodedLength
|
|
|| !encoded[..MagicLength].SequenceEqual(Magic))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Guid attemptId = new(encoded.Slice(MagicLength, AttemptIdLength));
|
|
if (attemptId == Guid.Empty)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
string ticket = Encoding.ASCII.GetString(encoded[(MagicLength + AttemptIdLength)..]);
|
|
if (!ContractValidation.IsConnectionTicketValid(ticket))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
request = new DirectConnectionRequest
|
|
{
|
|
AttemptId = new JoinAttemptId(attemptId),
|
|
ConnectionTicket = ticket,
|
|
};
|
|
return true;
|
|
}
|
|
}
|