You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

208 lines
5.6 KiB

package exchange
import (
"context"
"math/big"
"github.com/go-faster/errors"
"go.uber.org/zap"
"github.com/gotd/td/bin"
"github.com/gotd/td/internal/crypto"
"github.com/gotd/td/internal/mt"
"github.com/gotd/td/internal/proto/codec"
)
// ServerExchangeError is returned when exchange fails due to
// some security or validation checks.
type ServerExchangeError struct {
Code int32
Err error
}
// Error implements error.
func (s *ServerExchangeError) Error() string {
return s.Err.Error()
}
// Unwrap implements error wrapper interface.
func (s *ServerExchangeError) Unwrap() error {
return s.Err
}
func serverError(code int32, err error) error {
return &ServerExchangeError{
Code: code,
Err: err,
}
}
// Run runs server-side flow.
// If b parameter is not nil, it will be used as first read message.
// Otherwise, it will be read from connection.
func (s ServerExchange) Run(ctx context.Context) (ServerExchangeResult, error) {
wrapKeyNotFound := func(err error) error {
return serverError(codec.CodeAuthKeyNotFound, err)
}
// 1. Client sends query to server
//
// req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
var pqReq mt.ReqPqMultiRequest
b := new(bin.Buffer)
if err := s.readUnencrypted(ctx, b, &pqReq); err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Received client ReqPqMultiRequest")
serverNonce, err := crypto.RandInt128(s.rand)
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate server nonce")
}
// 2. Server sends response of the form
//
// resPQ#05162463 nonce:int128 server_nonce:int128 pq:string server_public_key_fingerprints:Vector long = ResPQ;
pq, err := s.rng.PQ()
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate pq")
}
s.log.Debug("Sending ResPQ", zap.String("pq", pq.String()))
if err := s.writeUnencrypted(ctx, b, &mt.ResPQ{
Pq: pq.Bytes(),
Nonce: pqReq.Nonce,
ServerNonce: serverNonce,
ServerPublicKeyFingerprints: []int64{
s.key.Fingerprint(),
},
}); err != nil {
return ServerExchangeResult{}, err
}
// 4. Client sends query to server
//
// req_DH_params#d712e4be nonce:int128 server_nonce:int128 p:string
// q:string public_key_fingerprint:long encrypted_data:string = Server_DH_Params
var dhParams mt.ReqDHParamsRequest
if err := s.readUnencrypted(ctx, b, &dhParams); err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Received client ReqDHParamsRequest")
var innerData mt.PQInnerData
{
r, err := crypto.DecodeRSAPad(dhParams.EncryptedData, s.key.RSA)
if err != nil {
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
b.ResetTo(r)
d, err := mt.DecodePQInnerData(b)
if err != nil {
return ServerExchangeResult{}, err
}
if innerDataDC, ok := d.(*mt.PQInnerDataDC); ok && innerDataDC.DC != s.dc {
err := errors.Errorf(
"wrong DC ID, want %d, got %d",
s.dc, innerDataDC.DC,
)
return ServerExchangeResult{}, serverError(codec.CodeWrongDC, err)
}
innerData = mt.PQInnerData{
Pq: d.GetPq(),
P: d.GetP(),
Q: d.GetQ(),
Nonce: d.GetNonce(),
ServerNonce: d.GetServerNonce(),
NewNonce: d.GetNewNonce(),
}
}
dhPrime, err := s.rng.DhPrime()
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate dh_prime")
}
g := 3
a, ga, err := s.rng.GA(g, dhPrime)
if err != nil {
return ServerExchangeResult{}, errors.Wrap(err, "generate g_a")
}
data := mt.ServerDHInnerData{
Nonce: pqReq.Nonce,
ServerNonce: serverNonce,
G: g,
GA: ga.Bytes(),
DhPrime: dhPrime.Bytes(),
ServerTime: int(s.clock.Now().Unix()),
}
b.Reset()
if err := data.Encode(b); err != nil {
return ServerExchangeResult{}, err
}
key, iv := crypto.TempAESKeys(innerData.NewNonce.BigInt(), serverNonce.BigInt())
answer, err := crypto.EncryptExchangeAnswer(s.rand, b.Raw(), key, iv)
if err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Sending ServerDHParamsOk", zap.Int("g", g))
// 5. Server responds with Server_DH_Params.
if err := s.writeUnencrypted(ctx, b, &mt.ServerDHParamsOk{
Nonce: pqReq.Nonce,
ServerNonce: serverNonce,
EncryptedAnswer: answer,
}); err != nil {
return ServerExchangeResult{}, err
}
var clientDhParams mt.SetClientDHParamsRequest
if err := s.readUnencrypted(ctx, b, &clientDhParams); err != nil {
return ServerExchangeResult{}, err
}
s.log.Debug("Received client SetClientDHParamsRequest")
decrypted, err := crypto.DecryptExchangeAnswer(clientDhParams.EncryptedData, key, iv)
if err != nil {
err = errors.Wrap(err, "decrypt exchange answer")
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
b.ResetTo(decrypted)
var clientInnerData mt.ClientDHInnerData
if err := clientInnerData.Decode(b); err != nil {
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
gB := big.NewInt(0).SetBytes(clientInnerData.GB)
var authKey crypto.Key
if !crypto.FillBytes(big.NewInt(0).Exp(gB, a, dhPrime), authKey[:]) {
err := errors.New("auth_key is too big")
return ServerExchangeResult{}, wrapKeyNotFound(err)
}
// DH key exchange complete
// 8. Server responds in one of three ways:
// dh_gen_ok#3bcbf734 nonce:int128 server_nonce:int128
// new_nonce_hash1:int128 = Set_client_DH_params_answer;
s.log.Debug("Sending DhGenOk")
if err := s.writeUnencrypted(ctx, b, &mt.DhGenOk{
Nonce: pqReq.Nonce,
ServerNonce: serverNonce,
NewNonceHash1: crypto.NonceHash1(innerData.NewNonce, authKey),
}); err != nil {
return ServerExchangeResult{}, err
}
serverSalt := crypto.ServerSalt(innerData.NewNonce, serverNonce)
return ServerExchangeResult{
Key: authKey.WithID(),
ServerSalt: serverSalt,
}, nil
}