owpengram-server/internal/mtprotoedge/auth_key_switch_test.go

56 lines
1.8 KiB
Go

package mtprotoedge
import (
"testing"
"time"
"github.com/iamxvbaba/td/mt"
"github.com/iamxvbaba/td/proto"
)
func TestEncryptedConnectionSwitchesAuthKeyEvenWhenSessionIDIsReused(t *testing.T) {
const dc = 2
addr, pub, srv := startTestServer(t, Options{DC: dc})
connA, authA, cipherA := dialHandshake(t, addr, dc, pub)
_, authB, cipherB := dialHandshake(t, addr, dc, pub)
msgID := proto.NewMessageIDGen(time.Now)
sendEncryptedWithSeq(t, connA, cipherA, authA, msgID.New(proto.MessageFromClient), 1, &mt.PingRequest{PingID: 1})
for range 3 { // new_session_created + pong + msgs_ack; leave no A-key frame on the socket.
readServerMessage(t, connA, cipherA, authA.AuthKey)
}
// Reuse A's session id on the same physical TCP socket, but encrypt with the independently
// established key B and B's salt. Session identity is (raw auth_key_id, session_id): comparing
// session_id alone would keep A's cached key/user identity and encrypt the reply with A.
body := encodeClientMessageBodyForTest(t, &mt.PingRequest{PingID: 2})
sendEncryptedWithSessionSaltAndSeq(
t,
connA,
cipherB,
authB,
authA.SessionID,
authB.ServerSalt,
msgID.New(proto.MessageFromClient),
1,
body,
)
seenPong := false
for range 3 {
_, typeID, _ := readServerMessage(t, connA, cipherB, authB.AuthKey)
seenPong = seenPong || typeID == mt.PongTypeID
}
if !seenPong {
t.Fatal("new auth key did not receive pong")
}
oldKey := sessionKey{authKeyID: authA.AuthKey.ID, sessionID: authA.SessionID}
newKey := sessionKey{authKeyID: authB.AuthKey.ID, sessionID: authA.SessionID}
srv.conns.mu.RLock()
_, oldAlive := srv.conns.bySession[oldKey]
current := srv.conns.bySession[newKey]
srv.conns.mu.RUnlock()
if oldAlive || current == nil || current.authKeyID != authB.AuthKey.ID {
t.Fatalf("registry after key switch: old_alive=%v current=%v", oldAlive, current != nil)
}
}