merged from gramsrv upstream

This commit is contained in:
onysd 2026-09-01 12:06:31 +03:00
parent 79c64ee916
commit 21a0856587
651 changed files with 54774 additions and 4590 deletions

View file

@ -2,13 +2,17 @@ package rpc
import (
"context"
"fmt"
"github.com/iamxvbaba/td/proto"
"github.com/iamxvbaba/td/tg"
"go.uber.org/zap"
"telesrv/internal/domain"
)
const encryptedDifferencePageSize = 1000
// 私聊密聊 qts 消息收发 RPC handler(P1)。服务端是盲中继:sendEncrypted* 的 bytes 是
// 客户端加密的 DecryptedMessage,服务端盲存进【接收方设备】的 qts 队列、原样转发,
// 永不解密。在线推 updateNewEncryptedMessage(设备定向,离线靠 getDifference 补回)。
@ -34,11 +38,16 @@ func (r *Router) pushEncryptedNewMessage(ctx context.Context, msg domain.SecretC
_, _ = targeted.PushToUserAuthKey(ctx, msg.ReceiverUserID, deviceAuthKeyBytes(msg.ReceiverAuthKeyID), proto.MessageFromServer, upd)
return
}
// 回退(测试替身/未装配定向能力):账号级推送。
r.pushUserMessage(ctx, msg.ReceiverUserID, "secret chat message", upd)
// 设备隔离是安全边界;缺少定向能力时只保留 durable qts,离线 difference 补回。
r.log.Error("secret chat targeted session binder unavailable",
zap.Int64("target_user_id", msg.ReceiverUserID),
zap.Int64("target_auth_key_id", msg.ReceiverAuthKeyID))
}
func (r *Router) sendEncryptedCommon(ctx context.Context, peer tg.InputEncryptedChat, randomID int64, data []byte, isService bool) (tg.MessagesSentEncryptedMessageClass, error) {
if len(data) > domain.MaxSecretMessageDataBytes {
return nil, dataTooLongErr()
}
if r.deps.SecretChats == nil {
return nil, notImplementedErr()
}
@ -46,7 +55,11 @@ func (r *Router) sendEncryptedCommon(ctx context.Context, peer tg.InputEncrypted
if err != nil {
return nil, err
}
_, stored, err := r.deps.SecretChats.SendEncrypted(ctx, peer.ChatID, userID, peer.AccessHash, domain.SecretMessageDelivery{
deviceAuthKeyID, ok := businessAuthKeyIDFrom(ctx)
if !ok {
return nil, internalErr()
}
_, stored, err := r.deps.SecretChats.SendEncrypted(ctx, peer.ChatID, userID, deviceAuthKeyID, peer.AccessHash, domain.SecretMessageDelivery{
RandomID: randomID,
Bytes: data,
IsService: isService,
@ -130,27 +143,38 @@ func (r *Router) deviceEncryptedQts(ctx context.Context) int {
return qts
}
// encryptedDifference 返回当前设备 qts > sinceQts 的加密消息(TL 投影)与推进后的 qts
// (getDifference 注入用)。无新消息时返回 (nil, sinceQts)。
func (r *Router) encryptedDifference(ctx context.Context, sinceQts int) ([]tg.EncryptedMessageClass, int) {
// encryptedDifference 返回当前设备 qts > sinceQts 的连续前缀、推进后的 qts 与是否还有
// 下一页。存储错误或 qts gap 必须 fail-fast,禁止越过缺口推进客户端水位。
func (r *Router) encryptedDifference(ctx context.Context, sinceQts int) ([]tg.EncryptedMessageClass, int, bool, error) {
if r.deps.SecretChats == nil {
return nil, sinceQts
return nil, sinceQts, false, nil
}
deviceKey, ok := businessAuthKeyIDFrom(ctx)
if !ok {
return nil, sinceQts
return nil, sinceQts, false, nil
}
msgs, err := r.deps.SecretChats.ListNewMessages(ctx, deviceKey, sinceQts, 0)
if err != nil || len(msgs) == 0 {
return nil, sinceQts
msgs, err := r.deps.SecretChats.ListNewMessages(ctx, deviceKey, sinceQts, encryptedDifferencePageSize+1)
if err != nil {
return nil, sinceQts, false, err
}
if len(msgs) == 0 {
return nil, sinceQts, false, nil
}
partial := len(msgs) > encryptedDifferencePageSize
if partial {
msgs = msgs[:encryptedDifferencePageSize]
}
out := make([]tg.EncryptedMessageClass, 0, len(msgs))
newQts := sinceQts
for _, m := range msgs {
for i, m := range msgs {
expected := newQts + 1
if m.Qts != expected {
return nil, sinceQts, false, fmt.Errorf("secret chat qts gap at index %d: got %d want %d", i, m.Qts, expected)
}
out = append(out, tgEncryptedMessage(m))
newQts = m.Qts
}
return out, newQts
return out, newQts, partial, nil
}
// injectEncryptedMessages 把加密消息与推进后的 qts 注入差分响应(按类型分别写 State /
@ -169,19 +193,28 @@ func injectEncryptedMessages(diff tg.UpdatesDifferenceClass, encMsgs []tg.Encryp
// encryptedStateUpdates 返回当前设备未投递的握手/已读状态事件重建出的 update(进
// OtherUpdates)、涉及的 peer user id(补 Users)、以及要登记已投递的事件 id。
// encryption 事件按 secret_chats 权威态重建(不固化快照)。
func (r *Router) encryptedStateUpdates(ctx context.Context, userID int64) (updates []tg.UpdateClass, peerUserIDs []int64, eventIDs []int64) {
// encryption 事件按 secret_chats 权威态重建(不固化密钥材料快照)。账号级邀请在
// accept 后只对未绑定设备投影为 discarded;获胜设备消费并跳过,绝不能收到 normal 泄漏。
func (r *Router) encryptedStateUpdates(ctx context.Context, userID int64) (updates []tg.UpdateClass, peerUserIDs []int64, eventIDs []int64, partial bool, err error) {
if r.deps.SecretChats == nil {
return nil, nil, nil
return nil, nil, nil, false, nil
}
deviceKey, ok := businessAuthKeyIDFrom(ctx)
if !ok {
return nil, nil, nil
return nil, nil, nil, false, nil
}
events, err := r.deps.SecretChats.ListStateEvents(ctx, userID, deviceKey, 0)
if err != nil || len(events) == 0 {
return nil, nil, nil
events, err := r.deps.SecretChats.ListStateEvents(ctx, userID, deviceKey, encryptedDifferencePageSize+1)
if err != nil {
return nil, nil, nil, false, err
}
if len(events) == 0 {
return nil, nil, nil, false, nil
}
partial = len(events) > encryptedDifferencePageSize
if partial {
events = events[:encryptedDifferencePageSize]
}
seenEncryption := make(map[int]struct{})
for _, ev := range events {
switch ev.Type {
case domain.EncryptedStateEventEncryption:
@ -189,12 +222,23 @@ func (r *Router) encryptedStateUpdates(ctx context.Context, userID int64) (updat
if gerr != nil || !found {
continue
}
updates = append(updates, &tg.UpdateEncryption{
Chat: tgEncryptedChatForViewer(chat, userID),
Date: ev.Date,
})
peerUserIDs = append(peerUserIDs, chat.AdminUserID, chat.ParticipantUserID)
eventIDs = append(eventIDs, ev.ID)
if _, duplicate := seenEncryption[chat.ID]; duplicate {
continue
}
seenEncryption[chat.ID] = struct{}{}
chatView := tgEncryptedChatForViewer(chat, userID)
if ev.TargetAuthKeyID == 0 && chat.State == domain.SecretChatStateNormal {
// 账号级事件只承载 accept 前邀请。accept 后获胜设备已有同步响应;其它设备
// 必须收敛为 discarded,不能用当前 normal 权威态泄漏 access_hash/g_a。
if chat.AuthKeyOf(userID) == deviceKey {
continue
}
chatView = &tg.EncryptedChatDiscarded{ID: chat.ID, HistoryDeleted: true}
}
updates = append(updates, &tg.UpdateEncryption{Chat: chatView, Date: ev.Date})
peerUserIDs = append(peerUserIDs, chat.AdminUserID, chat.ParticipantUserID)
case domain.EncryptedStateEventRead:
updates = append(updates, &tg.UpdateEncryptedMessagesRead{
ChatID: ev.ChatID,
@ -204,7 +248,7 @@ func (r *Router) encryptedStateUpdates(ctx context.Context, userID int64) (updat
eventIDs = append(eventIDs, ev.ID)
}
}
return updates, peerUserIDs, eventIDs
return updates, peerUserIDs, eventIDs, partial, nil
}
// injectEncryptedOtherUpdates 把握手/已读 update 追加进差分的 OtherUpdates、把 peer
@ -214,6 +258,24 @@ func (r *Router) injectEncryptedOtherUpdates(ctx context.Context, viewerUserID i
return diff
}
users := r.tgUsersForIDs(ctx, viewerUserID, peerUserIDs)
return appendEncryptedOtherUpdates(diff, updates, users)
}
func (r *Router) injectEncryptedOtherUpdatesStrict(ctx context.Context, viewerUserID int64, diff tg.UpdatesDifferenceClass, updates []tg.UpdateClass, peerUserIDs []int64, cache *viewerPeerCache) (tg.UpdatesDifferenceClass, error) {
if len(updates) == 0 {
return diff, nil
}
if cache == nil {
cache = newViewerPeerCache(r)
}
users, err := cache.usersForIDsStrict(ctx, viewerUserID, peerUserIDs)
if err != nil {
return nil, err
}
return appendEncryptedOtherUpdates(diff, updates, r.tgUsersForViewer(viewerUserID, users)), nil
}
func appendEncryptedOtherUpdates(diff tg.UpdatesDifferenceClass, updates []tg.UpdateClass, users []tg.UserClass) tg.UpdatesDifferenceClass {
switch v := diff.(type) {
case *tg.UpdatesDifference:
v.OtherUpdates = append(v.OtherUpdates, updates...)