Summary Update.
This commit is contained in:
@@ -2,71 +2,159 @@ namespace RelayShared.Services;
|
||||
|
||||
//TODO: review name of file, potentially rename for Encryption services rather than sockets
|
||||
|
||||
/// <summary>
|
||||
/// The "data plane" wire types for the WebSocket protocol.
|
||||
///
|
||||
/// Every type here carries a SignalType discriminator so a generic JsonDocument peek
|
||||
/// can identify the variant. The server dispatches on SignalType in ChatSocketBehavior.OnMessage;
|
||||
/// the client dispatches on it in RelaySocketClient.OnMessage.
|
||||
///
|
||||
/// Encrypted payloads share a uniform 4-tuple shape: (CipherText, Nonce, Tag, EncryptedKey).
|
||||
/// That tuple is hybrid RSA+AES-GCM: EncryptedKey is the per-message AES key wrapped with the
|
||||
/// recipient's RSA public key; CipherText/Nonce/Tag are the AES-GCM ciphertext, nonce, and
|
||||
/// authentication tag for the actual JSON-serialised ChatMessageContent.
|
||||
/// </summary>
|
||||
public sealed class SocketRtcSignalMessage
|
||||
{
|
||||
/// <summary>Always SignalType.EncryptedSignal in flight.</summary>
|
||||
public SignalType Type { get; set; }
|
||||
|
||||
/// <summary>Username of the user generating the SDP/ICE signal.</summary>
|
||||
public string SenderUsername { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The voice channel this signal belongs to.</summary>
|
||||
public string ChannelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM ciphertext of the JSON-serialised RtcSignalMessage.</summary>
|
||||
public string CipherText { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM 96-bit nonce.</summary>
|
||||
public string Nonce { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM 128-bit authentication tag.</summary>
|
||||
public string Tag { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 RSA-OAEP-encrypted AES key (encrypted with recipient's public key).</summary>
|
||||
public string EncryptedKey { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The workhorse envelope for chat messages and message lifecycle events.
|
||||
/// Used for both directions and for new sends / edits / delete tombstones.
|
||||
/// </summary>
|
||||
public sealed class SocketEncryptedMessage
|
||||
{
|
||||
/// <summary>
|
||||
/// EncryptedChat (server→client), ClientEncryptedChat (client→server new message),
|
||||
/// ClientEditMessage / ClientDeleteMessage (client→server lifecycle), MessageEdited (server→client).
|
||||
/// </summary>
|
||||
public SignalType Type { get; set; } = SignalType.EncryptedChat;
|
||||
|
||||
/// <summary>Surreal record id (e.g. "channel_messages:abc"). Populated by the server on outbound delivery.</summary>
|
||||
public string MessageId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Who wrote the message.</summary>
|
||||
public string SenderUsername { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Who this specific delivery is encrypted for. Different per recipient on the same logical message.</summary>
|
||||
public string RecipientUsername { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>The channel the message belongs to.</summary>
|
||||
public string ChannelId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM ciphertext of the JSON-serialised ChatMessageContent. Empty on tombstone deliveries.</summary>
|
||||
public string CipherText { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM 96-bit nonce.</summary>
|
||||
public string Nonce { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 AES-GCM 128-bit authentication tag.</summary>
|
||||
public string Tag { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 RSA-OAEP-encrypted AES key (encrypted with recipient's public key on outbound, server's on inbound).</summary>
|
||||
public string EncryptedKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>True when this message has been edited at least once. Drives the (edited) footer in the bubble.</summary>
|
||||
public bool IsEdited { get; set; }
|
||||
|
||||
/// <summary>True for tombstone deliveries (history only). Client renders a placeholder; no decryption is attempted.</summary>
|
||||
public bool IsDeleted { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-broadcast tombstone fired the moment a message is deleted. Carries no content —
|
||||
/// recipients use MessageId to find the existing bubble and swap it to a "deleted" placeholder.
|
||||
/// </summary>
|
||||
public sealed class SocketMessageDeletedEvent
|
||||
{
|
||||
public SignalType Type { get; set; } = SignalType.MessageDeleted;
|
||||
|
||||
/// <summary>The message being tombstoned.</summary>
|
||||
public string MessageId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Channel scope — clients that aren't viewing this channel can defer the bubble update.</summary>
|
||||
public string ChannelId { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// "{Username} is typing…" hint. Server forwards to every connected member except the sender.
|
||||
/// Client auto-clears the indicator 3 seconds after the last such event.
|
||||
/// </summary>
|
||||
public sealed class SocketTypingEvent
|
||||
{
|
||||
public SignalType Type { get; set; } = SignalType.TypingIndicator;
|
||||
|
||||
/// <summary>Who is typing.</summary>
|
||||
public string Username { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Which channel they're typing in. Clients ignore events for channels they're not viewing.</summary>
|
||||
public string ChannelId { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>One historical version of an edited message, re-encrypted for the requester.</summary>
|
||||
public sealed class SocketEditHistoryEntry
|
||||
{
|
||||
/// <summary>Base64 AES-GCM ciphertext of the JSON-serialised previous ChatMessageContent.</summary>
|
||||
public string CipherText { get; set; } = string.Empty;
|
||||
|
||||
public string Nonce { get; set; } = string.Empty;
|
||||
public string Tag { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Base64 RSA-OAEP-encrypted AES key (encrypted with requester's public key).</summary>
|
||||
public string EncryptedKey { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>When this version was the current text (i.e. when it was replaced).</summary>
|
||||
public DateTime EditedAt { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Server reply to a GetEditHistory request. Entries are ordered oldest→newest.</summary>
|
||||
public sealed class SocketEditHistoryResponse
|
||||
{
|
||||
public SignalType Type { get; set; } = SignalType.EditHistory;
|
||||
|
||||
/// <summary>Which message this history is for.</summary>
|
||||
public string MessageId { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>Every previous version of the message. Empty if the message has never been edited.</summary>
|
||||
public List<SocketEditHistoryEntry> Entries { get; set; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server-to-client delivery of the server's public RSA key. Sent once per session in
|
||||
/// response to WsAction.GetServerKey. Clients cache this for all outbound encryption.
|
||||
/// </summary>
|
||||
public sealed class ServerPublicKeyMessage
|
||||
{
|
||||
public SignalType Type { get; set; } = SignalType.ServerPublicKey;
|
||||
|
||||
/// <summary>Base64 SubjectPublicKeyInfo (DER) of the server's RSA public key.</summary>
|
||||
public string PublicKey { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>The wire discriminator for every data-plane Socket*Message.</summary>
|
||||
public enum SignalType
|
||||
{
|
||||
// RTC SDP/ICE wire types (used by the WebView RTC engine, not handled directly here)
|
||||
Offer,
|
||||
Answer,
|
||||
Candidate,
|
||||
@@ -74,15 +162,37 @@ public enum SignalType
|
||||
AnswerUpdated,
|
||||
CandidateAdded,
|
||||
CallLeft,
|
||||
|
||||
/// <summary>Server→client: paginated channel list (SocketChannelList).</summary>
|
||||
ChannelList,
|
||||
|
||||
/// <summary>Server→client: ServerPublicKeyMessage delivery.</summary>
|
||||
ServerPublicKey,
|
||||
|
||||
/// <summary>Bidirectional: encrypted RTC SDP/ICE signal (SocketRtcSignalMessage).</summary>
|
||||
EncryptedSignal,
|
||||
|
||||
/// <summary>Server→client: delivered chat message (SocketEncryptedMessage).</summary>
|
||||
EncryptedChat,
|
||||
|
||||
/// <summary>Client→server: new chat message send (SocketEncryptedMessage).</summary>
|
||||
ClientEncryptedChat,
|
||||
|
||||
/// <summary>Client→server: request to edit own message (SocketEncryptedMessage with new content).</summary>
|
||||
ClientEditMessage,
|
||||
|
||||
/// <summary>Client→server: request to delete own message (SocketEncryptedMessage with only MessageId).</summary>
|
||||
ClientDeleteMessage,
|
||||
|
||||
/// <summary>Server→clients: edit broadcast carrying re-encrypted new content (SocketEncryptedMessage).</summary>
|
||||
MessageEdited,
|
||||
|
||||
/// <summary>Server→clients: deletion tombstone (SocketMessageDeletedEvent).</summary>
|
||||
MessageDeleted,
|
||||
|
||||
/// <summary>Server→peers: typing indicator (SocketTypingEvent).</summary>
|
||||
TypingIndicator,
|
||||
|
||||
/// <summary>Server→requester: edit-history response (SocketEditHistoryResponse).</summary>
|
||||
EditHistory
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user