Add end-to-end encryption for chat messages (X25519 / NaCl box)
Messages between matched users are now encrypted client-side with each user's own key pair before they ever reach the server; the backend only ever stores/relays opaque ciphertext + nonce and cannot read message content, including for the last-message preview on the matches list. - User gains a PublicKey field, registered/rotated via PUT /profile/public-key - Message gains CipherText/Nonce; legacy plaintext Text is kept as-is for messages sent before this change - MatchView exposes the partner's public key and the last message's ciphertext/nonce so the client can decrypt previews itself - Removed the now-dead plaintext SanitizeMessageText utility — the server can no longer sanitize message content since it's opaque ciphertext Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,30 +22,45 @@ type SwipeResponse struct {
|
||||
Match *MatchView `json:"match,omitempty"`
|
||||
}
|
||||
|
||||
// MatchView خلاصهی یک مچ برای نمایش در لیست مچها/گفتگوها
|
||||
// MatchView خلاصهی یک مچ برای نمایش در لیست مچها/گفتگوها. برای پیشنمایش
|
||||
// آخرین پیام، سرور رمزگشایی نمیکنه (نمیتونه) — CipherText/Nonce خودِ آخرین
|
||||
// پیام رو برمیگردونه تا کلاینت خودش رمزگشایی کنه؛ LastMessage فقط برای
|
||||
// پیامهای قدیمیِ قبل از E2EE پر میشه.
|
||||
type MatchView struct {
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"user_id"`
|
||||
FirstName string `json:"first_name"`
|
||||
Photos []string `json:"photos"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
LastMessage *string `json:"last_message,omitempty"`
|
||||
LastAt *string `json:"last_at,omitempty"`
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"user_id"`
|
||||
FirstName string `json:"first_name"`
|
||||
Photos []string `json:"photos"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
LastMessage *string `json:"last_message,omitempty"`
|
||||
LastMessageCipherText *string `json:"last_message_cipher_text,omitempty"`
|
||||
LastMessageNonce *string `json:"last_message_nonce,omitempty"`
|
||||
LastAt *string `json:"last_at,omitempty"`
|
||||
// PartnerPublicKey کلید عمومی X25519 طرف مقابل (base64) برای رمزنگاری
|
||||
// سرتاسری؛ اگه طرف مقابل هنوز کلیدی ثبت نکرده باشه خالیه.
|
||||
PartnerPublicKey *string `json:"partner_public_key,omitempty"`
|
||||
}
|
||||
|
||||
// MessageView یک پیام در یک گفتگو
|
||||
// MessageView یک پیام در یک گفتگو. یا Text (پیام قدیمیِ قبل از E2EE) پر
|
||||
// میشه، یا CipherText+Nonce (پیام رمزنگاریشده) — سرور خودش نمیدونه محتوای
|
||||
// رمزنگاریشده چیه، فقط بین دو طرف مچ ردوبدلش میکنه.
|
||||
type MessageView struct {
|
||||
ID uint `json:"id"`
|
||||
MatchID uint `json:"match_id"`
|
||||
SenderID uint `json:"sender_id"`
|
||||
Text string `json:"text"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
IsMine bool `json:"is_mine"`
|
||||
ID uint `json:"id"`
|
||||
MatchID uint `json:"match_id"`
|
||||
SenderID uint `json:"sender_id"`
|
||||
Text string `json:"text,omitempty"`
|
||||
CipherText *string `json:"cipher_text,omitempty"`
|
||||
Nonce *string `json:"nonce,omitempty"`
|
||||
CreatedAt string `json:"created_at"`
|
||||
IsMine bool `json:"is_mine"`
|
||||
}
|
||||
|
||||
// SendMessageRequest بدنهی درخواست ارسال پیام
|
||||
// SendMessageRequest بدنهی درخواست ارسال پیام رمزنگاریشده. متن خام پیام
|
||||
// هیچوقت به سرور نمیرسه؛ کلاینت قبل از ارسال با کلید عمومی طرف مقابل
|
||||
// رمزنگاریش میکنه.
|
||||
type SendMessageRequest struct {
|
||||
Text string `json:"text" binding:"required,min=1,max=2000"`
|
||||
CipherText string `json:"cipher_text" binding:"required,min=1,max=12000"`
|
||||
Nonce string `json:"nonce" binding:"required,len=32"`
|
||||
}
|
||||
|
||||
// CandidateAnswer پاسخ یک سوال پروفایل، برای نمایش عمومی
|
||||
|
||||
@@ -23,3 +23,10 @@ type UpdateNotificationSettingsRequest struct {
|
||||
NotifyMessages bool `json:"notify_messages"`
|
||||
NotifyLikes bool `json:"notify_likes"`
|
||||
}
|
||||
|
||||
// UpdatePublicKeyRequest ثبت/بهروزرسانی کلید عمومی X25519 کاربر برای
|
||||
// رمزنگاری سرتاسری پیامها. کلید باید دقیقاً ۳۲ بایت خام باشه که بهصورت
|
||||
// base64 استاندارد (۴۴ کاراکتر، شامل padding) کدگذاری شده.
|
||||
type UpdatePublicKeyRequest struct {
|
||||
PublicKey string `json:"public_key" binding:"required,len=44"`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user