namespace DataTransfer.Client.Services; /// /// Plaintext messages of the commit-then-reveal key exchange (see ). They are relayed /// like frames, but before the channel is encrypted. Layout: version (byte) | type (byte) | payload. /// internal static class HandshakeCodec { public const int CommitmentBytes = 32; // A P-256 SPKI key has 91 bytes; anything much larger is not a key. private const int MaxPublicKeyBytes = 512; private const byte Version = 1; private enum MessageType : byte { Commitment = 1, PublicKey = 2 } public static byte[] EncodeCommitment(byte[] commitment) => Encode(MessageType.Commitment, commitment); public static byte[] EncodePublicKey(byte[] publicKey) => Encode(MessageType.PublicKey, publicKey); /// The message is not a commitment. public static byte[] DecodeCommitment(byte[] message) => Decode(message, MessageType.Commitment, CommitmentBytes, CommitmentBytes); /// The message is not a public key. public static byte[] DecodePublicKey(byte[] message) => Decode(message, MessageType.PublicKey, 1, MaxPublicKeyBytes); private static byte[] Encode(MessageType type, byte[] payload) { var message = new byte[payload.Length + 2]; message[0] = Version; message[1] = (byte)type; payload.CopyTo(message, 2); return message; } private static byte[] Decode(byte[] message, MessageType type, int minPayloadBytes, int maxPayloadBytes) { ArgumentNullException.ThrowIfNull(message); var payloadBytes = message.Length - 2; if (payloadBytes < minPayloadBytes || payloadBytes > maxPayloadBytes || message[0] != Version || message[1] != (byte)type) { throw new InvalidDataException($"Expected a valid key-exchange message of type {type}."); } return message[2..]; } }