package exchange

import (
	
	

	
	

	
	
	
	
)

// ServerExchangeError is returned when exchange fails due to
// some security or validation checks.
type ServerExchangeError struct {
	Code int32
	Err  error
}

// Error implements error.
func ( *ServerExchangeError) () string {
	return .Err.Error()
}

// Unwrap implements error wrapper interface.
func ( *ServerExchangeError) () error {
	return .Err
}

func ( int32,  error) error {
	return &ServerExchangeError{
		Code: ,
		Err:  ,
	}
}

// req_pq#60469778 or req_pq_multi#be7e8ef1
type reqPQ struct {
	Type  uint32
	Nonce bin.Int128
}

func ( *reqPQ) ( *bin.Buffer) error {
	var (
		 mt.ReqPqRequest
		  mt.ReqPqMultiRequest
	)
	,  := .PeekID()
	if  != nil {
		return 
	}
	.Type = 
	switch  {
	case .TypeID():
		if  := .Decode();  != nil {
			return 
		}
		.Nonce = .Nonce
		return nil
	case .TypeID():
		if  := .Decode();  != nil {
			return 
		}
		.Nonce = .Nonce
		return nil
	default:
		return bin.NewUnexpectedID()
	}
}

type reqOrDH struct {
	Type uint32
	DH   mt.ReqDHParamsRequest
	Req  reqPQ
}

func ( *reqOrDH) ( *bin.Buffer) error {
	,  := .PeekID()
	if  != nil {
		return 
	}
	.Type = 
	switch  {
	case .DH.TypeID():
		return .DH.Decode()
	default:
		return .Req.Decode()
	}
}

// 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 ( ServerExchange) ( context.Context) (ServerExchangeResult, error) {
	 := func( error) error {
		return serverError(codec.CodeAuthKeyNotFound, )
	}

	// 1. Client sends query to server
	// req_pq#60469778 nonce:int128 = ResPQ; // legacy
	// req_pq_multi#be7e8ef1 nonce:int128 = ResPQ;
	var  reqPQ
	 := new(bin.Buffer)
	if  := .readUnencrypted(, , &);  != nil {
		return ServerExchangeResult{}, 
	}
	.log.Debug("Received client ReqPqMultiRequest")

	,  := crypto.RandInt128(.rand)
	if  != nil {
		return ServerExchangeResult{}, errors.Wrap(, "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;
	,  := .rng.PQ()
	if  != nil {
		return ServerExchangeResult{}, errors.Wrap(, "generate pq")
	}

:
	.log.Debug("Sending ResPQ", zap.String("pq", .String()))
	if  := .writeUnencrypted(, , &mt.ResPQ{
		Pq:          .Bytes(),
		Nonce:       .Nonce,
		ServerNonce: ,
		ServerPublicKeyFingerprints: []int64{
			.key.Fingerprint(),
		},
	});  != nil {
		return ServerExchangeResult{}, 
	}

	// 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  reqOrDH
	if  := .readUnencrypted(, , &);  != nil {
		return ServerExchangeResult{}, 
	}
	switch .Type {
	case mt.ReqPqRequestTypeID, mt.ReqPqMultiRequestTypeID:
		// Client can send fake req_pq on start. Ignore it.
		//
		// Next one should be not fake.
		.log.Debug("Received ReqPQ again")
		 = .Req
		goto 
	default:
		.log.Debug("Received client ReqDHParamsRequest")
	}

	var  mt.PQInnerData
	{
		,  := crypto.DecodeRSAPad(.DH.EncryptedData, .key.RSA)
		if  != nil {
			return ServerExchangeResult{}, ()
		}
		.ResetTo()

		,  := mt.DecodePQInnerData()
		if  != nil {
			return ServerExchangeResult{}, 
		}

		if ,  := .(*mt.PQInnerDataDC);  && .DC != .dc {
			 := errors.Errorf(
				"wrong DC ID, want %d, got %d",
				.dc, .DC,
			)
			return ServerExchangeResult{}, serverError(codec.CodeWrongDC, )
		}

		 = mt.PQInnerData{
			Pq:          .GetPq(),
			P:           .GetP(),
			Q:           .GetQ(),
			Nonce:       .GetNonce(),
			ServerNonce: .GetServerNonce(),
			NewNonce:    .GetNewNonce(),
		}
	}

	,  := .rng.DhPrime()
	if  != nil {
		return ServerExchangeResult{}, errors.Wrap(, "generate dh_prime")
	}

	 := 3
	, ,  := .rng.GA(, )
	if  != nil {
		return ServerExchangeResult{}, errors.Wrap(, "generate g_a")
	}

	 := mt.ServerDHInnerData{
		Nonce:       .Nonce,
		ServerNonce: ,
		G:           ,
		GA:          .Bytes(),
		DhPrime:     .Bytes(),
		ServerTime:  int(.clock.Now().Unix()),
	}

	.Reset()
	if  := .Encode();  != nil {
		return ServerExchangeResult{}, 
	}

	,  := crypto.TempAESKeys(.NewNonce.BigInt(), .BigInt())
	,  := crypto.EncryptExchangeAnswer(.rand, .Raw(), , )
	if  != nil {
		return ServerExchangeResult{}, 
	}

	.log.Debug("Sending ServerDHParamsOk", zap.Int("g", ))
	// 5. Server responds with Server_DH_Params.
	if  := .writeUnencrypted(, , &mt.ServerDHParamsOk{
		Nonce:           .Nonce,
		ServerNonce:     ,
		EncryptedAnswer: ,
	});  != nil {
		return ServerExchangeResult{}, 
	}

	var  mt.SetClientDHParamsRequest
	if  := .readUnencrypted(, , &);  != nil {
		return ServerExchangeResult{}, 
	}
	.log.Debug("Received client SetClientDHParamsRequest")

	,  := crypto.DecryptExchangeAnswer(.EncryptedData, , )
	if  != nil {
		 = errors.Wrap(, "decrypt exchange answer")
		return ServerExchangeResult{}, ()
	}
	.ResetTo()

	var  mt.ClientDHInnerData
	if  := .Decode();  != nil {
		return ServerExchangeResult{}, ()
	}

	 := big.NewInt(0).SetBytes(.GB)
	var  crypto.Key
	if !crypto.FillBytes(big.NewInt(0).Exp(, , ), [:]) {
		 := errors.New("auth_key is too big")
		return ServerExchangeResult{}, ()
	}

	// 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;
	.log.Debug("Sending DhGenOk")
	if  := .writeUnencrypted(, , &mt.DhGenOk{
		Nonce:         .Nonce,
		ServerNonce:   ,
		NewNonceHash1: crypto.NonceHash1(.NewNonce, ),
	});  != nil {
		return ServerExchangeResult{}, 
	}

	 := crypto.ServerSalt(.NewNonce, )
	return ServerExchangeResult{
		Key:        .WithID(),
		ServerSalt: ,
	}, nil
}