perf: sync protocol and core hardening updates
This commit is contained in:
parent
152fed3b87
commit
4390ebf5a9
283 changed files with 29231 additions and 2295 deletions
315
internal/rpc/message_idempotency_test.go
Normal file
315
internal/rpc/message_idempotency_test.go
Normal file
|
|
@ -0,0 +1,315 @@
|
|||
package rpc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/gotd/td/clock"
|
||||
"github.com/gotd/td/tg"
|
||||
"github.com/gotd/td/tgerr"
|
||||
"go.uber.org/zap/zaptest"
|
||||
|
||||
"telesrv/internal/domain"
|
||||
)
|
||||
|
||||
func TestRPCRequestFingerprintStableAndPayloadSensitive(t *testing.T) {
|
||||
req := &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: 1002, AccessHash: 22},
|
||||
Message: "hello",
|
||||
RandomID: 991,
|
||||
Entities: []tg.MessageEntityClass{&tg.MessageEntityBold{Offset: 0, Length: 5}},
|
||||
}
|
||||
|
||||
first, err := rpcRequestFingerprint(req)
|
||||
if err != nil {
|
||||
t.Fatalf("first fingerprint: %v", err)
|
||||
}
|
||||
second, err := rpcRequestFingerprint(req)
|
||||
if err != nil {
|
||||
t.Fatalf("second fingerprint: %v", err)
|
||||
}
|
||||
if len(first) != 32 || !bytes.Equal(first, second) {
|
||||
t.Fatalf("fingerprints = %x / %x, want stable SHA-256", first, second)
|
||||
}
|
||||
|
||||
req.Message = "changed"
|
||||
changed, err := rpcRequestFingerprint(req)
|
||||
if err != nil {
|
||||
t.Fatalf("changed fingerprint: %v", err)
|
||||
}
|
||||
if bytes.Equal(first, changed) {
|
||||
t.Fatalf("changed payload fingerprint = %x, want different from %x", changed, first)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMessageFingerprintIgnoresRetryOnlyHints(t *testing.T) {
|
||||
first := &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: 1002, AccessHash: 22},
|
||||
Message: "hello",
|
||||
RandomID: 991,
|
||||
ClearDraft: true,
|
||||
Background: true,
|
||||
UpdateStickersetsOrder: true,
|
||||
}
|
||||
retry := *first
|
||||
retry.Flags.Set(31) // stale decoded flags must not leak into the canonical intent.
|
||||
retry.ClearDraft = false
|
||||
retry.Background = false
|
||||
retry.UpdateStickersetsOrder = false
|
||||
|
||||
a, err := sendMessageIdempotencyFingerprint(first)
|
||||
if err != nil {
|
||||
t.Fatalf("first fingerprint: %v", err)
|
||||
}
|
||||
b, err := sendMessageIdempotencyFingerprint(&retry)
|
||||
if err != nil {
|
||||
t.Fatalf("retry fingerprint: %v", err)
|
||||
}
|
||||
if !bytes.Equal(a, b) {
|
||||
t.Fatalf("retry-only flags changed fingerprint: %x != %x", a, b)
|
||||
}
|
||||
|
||||
retry.Message = "different"
|
||||
c, err := sendMessageIdempotencyFingerprint(&retry)
|
||||
if err != nil {
|
||||
t.Fatalf("changed fingerprint: %v", err)
|
||||
}
|
||||
if bytes.Equal(a, c) {
|
||||
t.Fatal("durable message change did not change fingerprint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendMultiMediaFingerprintIsPerItemAndSubsetStable(t *testing.T) {
|
||||
item1 := tg.InputSingleMedia{Media: &tg.InputMediaEmpty{}, RandomID: 101, Message: "one"}
|
||||
item2 := tg.InputSingleMedia{Media: &tg.InputMediaEmpty{}, RandomID: 102, Message: "two"}
|
||||
full := &tg.MessagesSendMultiMediaRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: 1002, AccessHash: 22},
|
||||
ClearDraft: true,
|
||||
Background: true,
|
||||
MultiMedia: []tg.InputSingleMedia{item1, item2},
|
||||
}
|
||||
subset := &tg.MessagesSendMultiMediaRequest{
|
||||
Peer: full.Peer,
|
||||
MultiMedia: []tg.InputSingleMedia{item2},
|
||||
}
|
||||
|
||||
fromFull, err := sendMultiMediaItemIdempotencyFingerprint(full, item2)
|
||||
if err != nil {
|
||||
t.Fatalf("full item fingerprint: %v", err)
|
||||
}
|
||||
fromSubset, err := sendMultiMediaItemIdempotencyFingerprint(subset, item2)
|
||||
if err != nil {
|
||||
t.Fatalf("subset item fingerprint: %v", err)
|
||||
}
|
||||
if !bytes.Equal(fromFull, fromSubset) {
|
||||
t.Fatalf("subset retry fingerprint = %x, want %x", fromSubset, fromFull)
|
||||
}
|
||||
|
||||
changed := item2
|
||||
changed.Message = "changed"
|
||||
different, err := sendMultiMediaItemIdempotencyFingerprint(subset, changed)
|
||||
if err != nil {
|
||||
t.Fatalf("changed item fingerprint: %v", err)
|
||||
}
|
||||
if bytes.Equal(fromFull, different) {
|
||||
t.Fatal("changed album item reused the original fingerprint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestForwardFingerprintIsPerItemAndSubsetStable(t *testing.T) {
|
||||
full := &tg.MessagesForwardMessagesRequest{
|
||||
FromPeer: &tg.InputPeerUser{UserID: 1002, AccessHash: 22},
|
||||
ID: []int{41, 42},
|
||||
RandomID: []int64{201, 202},
|
||||
ToPeer: &tg.InputPeerUser{UserID: 1003, AccessHash: 33},
|
||||
Background: true,
|
||||
}
|
||||
subset := *full
|
||||
subset.Flags = 0
|
||||
subset.Background = false
|
||||
subset.ID = []int{42}
|
||||
subset.RandomID = []int64{202}
|
||||
|
||||
fromFull, err := forwardMessagesItemIdempotencyFingerprint(full, 42, 202)
|
||||
if err != nil {
|
||||
t.Fatalf("full item fingerprint: %v", err)
|
||||
}
|
||||
fromSubset, err := forwardMessagesItemIdempotencyFingerprint(&subset, 42, 202)
|
||||
if err != nil {
|
||||
t.Fatalf("subset item fingerprint: %v", err)
|
||||
}
|
||||
if !bytes.Equal(fromFull, fromSubset) {
|
||||
t.Fatalf("subset retry fingerprint = %x, want %x", fromSubset, fromFull)
|
||||
}
|
||||
|
||||
different, err := forwardMessagesItemIdempotencyFingerprint(&subset, 41, 202)
|
||||
if err != nil {
|
||||
t.Fatalf("changed source fingerprint: %v", err)
|
||||
}
|
||||
if bytes.Equal(fromFull, different) {
|
||||
t.Fatal("different source message reused the original fingerprint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageSendErrMapsRandomIDConflict(t *testing.T) {
|
||||
err := messageSendErr(fmt.Errorf("wrapped store error: %w", domain.ErrMessageRandomIDDuplicate))
|
||||
if !tgerr.Is(err, "RANDOM_ID_DUPLICATE") || !tgerr.IsCode(err, 500) {
|
||||
t.Fatalf("messageSendErr = %v, want 500 RANDOM_ID_DUPLICATE", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessageForwardErrMapsRandomIDConflict(t *testing.T) {
|
||||
err := messageForwardErr(fmt.Errorf("wrapped store error: %w", domain.ErrMessageRandomIDDuplicate))
|
||||
if !tgerr.Is(err, "RANDOM_ID_DUPLICATE") || !tgerr.IsCode(err, 500) {
|
||||
t.Fatalf("messageForwardErr = %v, want 500 RANDOM_ID_DUPLICATE", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateSendDuplicateResponseIncludesAndroidConfirmationSnapshot(t *testing.T) {
|
||||
res := domain.SendPrivateTextResult{
|
||||
Duplicate: true,
|
||||
SenderMessage: domain.Message{
|
||||
ID: 41, UID: 51, RandomID: 9911, OwnerUserID: 1001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1001},
|
||||
Date: 1700000000, Out: true, Body: "confirmed", Pts: 7,
|
||||
},
|
||||
SenderEvent: domain.UpdateEvent{Pts: 7, PtsCount: 1, Date: 1700000000},
|
||||
}
|
||||
updates := tgPrivateSendResultUpdates(res, 9911, true, nil, nil)
|
||||
if len(updates.Updates) != 2 {
|
||||
t.Fatalf("duplicate updates = %#v, want mapping + new message", updates.Updates)
|
||||
}
|
||||
mapping, ok := updates.Updates[0].(*tg.UpdateMessageID)
|
||||
if !ok || mapping.ID != 41 || mapping.RandomID != 9911 {
|
||||
t.Fatalf("duplicate mapping = %#v, want id/random_id 41/9911", updates.Updates[0])
|
||||
}
|
||||
confirmed, ok := updates.Updates[1].(*tg.UpdateNewMessage)
|
||||
if !ok || confirmed.Pts != 7 || confirmed.PtsCount != 1 {
|
||||
t.Fatalf("duplicate confirmation = %#v, want UpdateNewMessage pts 7/1", updates.Updates[1])
|
||||
}
|
||||
msg, ok := confirmed.Message.(*tg.Message)
|
||||
if !ok || msg.ID != 41 || msg.Message != "confirmed" {
|
||||
t.Fatalf("duplicate confirmation message = %#v, want sender snapshot", confirmed.Message)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateSendDeletedDuplicateConfirmsThenConvergesWithDurableDelete(t *testing.T) {
|
||||
deleteEvent := domain.UpdateEvent{UserID: 1001, Type: domain.UpdateEventDeleteMessages, Pts: 9, PtsCount: 1, Date: 1700000002, MessageIDs: []int{41}}
|
||||
res := domain.SendPrivateTextResult{
|
||||
Duplicate: true,
|
||||
SenderMessage: domain.Message{
|
||||
ID: 41, UID: 51, RandomID: 9911, OwnerUserID: 1001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1001},
|
||||
Date: 1700000000, Out: true, Body: "original", Pts: 7,
|
||||
},
|
||||
SenderEvent: domain.UpdateEvent{Pts: 7, PtsCount: 1, Date: 1700000000},
|
||||
ReplayDeleteEvent: &deleteEvent,
|
||||
}
|
||||
updates := tgPrivateSendResultUpdates(res, 9911, true, nil, nil)
|
||||
if len(updates.Updates) != 3 {
|
||||
t.Fatalf("deleted duplicate updates = %#v, want mapping + new + delete", updates.Updates)
|
||||
}
|
||||
if _, ok := updates.Updates[1].(*tg.UpdateNewMessage); !ok {
|
||||
t.Fatalf("deleted duplicate confirmation = %#v, want UpdateNewMessage", updates.Updates[1])
|
||||
}
|
||||
deleted, ok := updates.Updates[2].(*tg.UpdateDeleteMessages)
|
||||
if !ok || deleted.Pts != 9 || deleted.PtsCount != 1 || len(deleted.Messages) != 1 || deleted.Messages[0] != 41 {
|
||||
t.Fatalf("deleted duplicate convergence = %#v, want real delete event", updates.Updates[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestForwardDeletedDuplicateIncludesMessageAndDelete(t *testing.T) {
|
||||
deleteEvent := &domain.UpdateEvent{UserID: 1001, Type: domain.UpdateEventDeleteMessages, Pts: 12, PtsCount: 1, Date: 1700000012, MessageIDs: []int{61}}
|
||||
updates := tgForwardMessagesUpdates(domain.ForwardPrivateMessagesResult{
|
||||
SenderMessages: []domain.Message{{
|
||||
ID: 61, UID: 71, RandomID: 8811, OwnerUserID: 1001,
|
||||
Peer: domain.Peer{Type: domain.PeerTypeUser, ID: 1002},
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1001},
|
||||
Date: 1700000010, Out: true, Body: "forwarded", Pts: 10,
|
||||
}},
|
||||
SenderEvents: []domain.UpdateEvent{{Pts: 10, PtsCount: 1, Date: 1700000010}},
|
||||
Duplicates: []bool{true},
|
||||
ReplayDeleteEvents: []*domain.UpdateEvent{deleteEvent},
|
||||
}, []int64{8811}, nil, nil)
|
||||
if len(updates.Updates) != 3 {
|
||||
t.Fatalf("forward duplicate updates = %#v, want mapping + new + delete", updates.Updates)
|
||||
}
|
||||
if _, ok := updates.Updates[1].(*tg.UpdateNewMessage); !ok {
|
||||
t.Fatalf("forward duplicate confirmation = %#v, want UpdateNewMessage", updates.Updates[1])
|
||||
}
|
||||
if deleted, ok := updates.Updates[2].(*tg.UpdateDeleteMessages); !ok || len(deleted.Messages) != 1 || deleted.Messages[0] != 61 || deleted.Pts != 12 {
|
||||
t.Fatalf("forward duplicate delete = %#v, want durable delete", updates.Updates[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestChannelDeletedDuplicateEchoIncludesMessageAndDelete(t *testing.T) {
|
||||
router := New(Config{}, Deps{}, zaptest.NewLogger(t), clock.System)
|
||||
deleteEvent := &domain.ChannelUpdateEvent{ChannelID: 2001, Type: domain.ChannelUpdateDeleteMessages, Pts: 12, PtsCount: 1, Date: 1700000012, MessageIDs: []int{61}}
|
||||
msg := domain.ChannelMessage{
|
||||
ChannelID: 2001, ID: 61, RandomID: 8811, SenderUserID: 1001,
|
||||
From: domain.Peer{Type: domain.PeerTypeUser, ID: 1001},
|
||||
Date: 1700000010, Body: "channel", Pts: 10,
|
||||
}
|
||||
updates := router.channelMessagesUpdatesWithPeerCache(context.Background(), 1001, []domain.SendChannelMessageResult{{
|
||||
Channel: domain.Channel{ID: 2001, AccessHash: 22, Title: "group", Megagroup: true, Date: 1700000000},
|
||||
Message: msg,
|
||||
Event: domain.ChannelUpdateEvent{
|
||||
ChannelID: 2001, Type: domain.ChannelUpdateNewMessage, Pts: 10, PtsCount: 1, Date: 1700000010, Message: msg,
|
||||
},
|
||||
Duplicate: true,
|
||||
ReplayDeleteEvent: deleteEvent,
|
||||
}}, []int64{8811}, true, nil, newViewerPeerCache(router))
|
||||
if len(updates.Updates) != 3 {
|
||||
t.Fatalf("channel duplicate updates = %#v, want mapping + new + delete", updates.Updates)
|
||||
}
|
||||
if _, ok := updates.Updates[1].(*tg.UpdateNewChannelMessage); !ok {
|
||||
t.Fatalf("channel duplicate confirmation = %#v, want UpdateNewChannelMessage", updates.Updates[1])
|
||||
}
|
||||
if deleted, ok := updates.Updates[2].(*tg.UpdateDeleteChannelMessages); !ok || deleted.ChannelID != 2001 || len(deleted.Messages) != 1 || deleted.Messages[0] != 61 || deleted.Pts != 12 {
|
||||
t.Fatalf("channel duplicate delete = %#v, want durable channel delete", updates.Updates[2])
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrivateSendRecordsRawOriginAuthKey(t *testing.T) {
|
||||
const (
|
||||
senderID = int64(1001)
|
||||
recipientID = int64(1002)
|
||||
)
|
||||
raw := [8]byte{1, 2, 3, 4, 5, 6, 7, 8}
|
||||
business := [8]byte{8, 7, 6, 5, 4, 3, 2, 1}
|
||||
messages := &captureMessages{}
|
||||
router := New(Config{}, Deps{
|
||||
Messages: messages,
|
||||
Users: mapUsersService{users: map[int64]domain.User{
|
||||
senderID: {ID: senderID, FirstName: "Sender"},
|
||||
recipientID: {ID: recipientID, FirstName: "Recipient"},
|
||||
}},
|
||||
}, zaptest.NewLogger(t), clock.System)
|
||||
ctx := WithSessionID(
|
||||
WithRawAuthKeyID(
|
||||
WithAuthKeyID(
|
||||
WithUserID(context.Background(), senderID),
|
||||
business,
|
||||
),
|
||||
raw,
|
||||
),
|
||||
77,
|
||||
)
|
||||
if _, err := router.onMessagesSendMessage(ctx, &tg.MessagesSendMessageRequest{
|
||||
Peer: &tg.InputPeerUser{UserID: recipientID},
|
||||
Message: "raw origin",
|
||||
RandomID: 992,
|
||||
}); err != nil {
|
||||
t.Fatalf("send message: %v", err)
|
||||
}
|
||||
if messages.sendReq.OriginAuthKeyID != raw || messages.sendReq.OriginAuthKeyID == business {
|
||||
t.Fatalf("origin auth key = %x, want raw %x (business %x)", messages.sendReq.OriginAuthKeyID, raw, business)
|
||||
}
|
||||
if messages.sendReq.OriginSessionID != 77 {
|
||||
t.Fatalf("origin session = %d, want 77", messages.sendReq.OriginSessionID)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue