2019-10-26 01:51:40 +02:00
# include "ProtocolHandler.h"
# include "ServerConnection.h"
# include "Socket.h"
# include "../logger.h"
# include <protocol/buffers.h>
# include <thread>
# include <iostream>
# include <tomcrypt.h>
# include <tommath.h>
# include <misc/base64.h>
# include <misc/digest.h>
# include <License.h>
# include <ed25519/ed25519.h>
# include <ed25519/sha512.h>
using namespace std ;
using namespace tc : : connection ;
using namespace ts : : protocol ;
using namespace ts ;
inline void generate_random ( uint8_t * destination , size_t length ) {
while ( length - - > 0 )
* ( destination + + ) = ( uint8_t ) rand ( ) ;
}
std : : string ProtocolHandler : : generate_client_initiv ( ) {
if ( ! this - > crypto . initiv_command . empty ( ) )
return this - > crypto . initiv_command ;
/* setup basic parameters */
if ( ( this - > crypto . alpha [ 0 ] & 0x01 ) = = 0 ) {
generate_random ( this - > crypto . alpha , 10 ) ;
this - > crypto . alpha [ 0 ] | = 0x01 ;
}
Command command ( " clientinitiv " ) ;
command [ " alpha " ] = base64 : : encode ( ( char * ) this - > crypto . alpha , 10 ) ;
command [ " ot " ] = 1 ;
command [ " ip " ] = " unknown " ;
if ( this - > server_type ! = server_type : : TEAMSPEAK ) /* if TEAMSPEAK then TS3 has been enforced */
command . enableParm ( " teaspeak " ) ; /* using "old" encryption system, we expect a teaspeak=1 within the response */
2020-03-28 15:04:55 +01:00
2019-10-26 01:51:40 +02:00
{
size_t buffer_length = 265 ;
char buffer [ 265 ] ;
auto result = ecc_export ( ( unsigned char * ) buffer , ( unsigned long * ) & buffer_length , PK_PUBLIC , & this - > crypto . identity ) ;
if ( result = = CRYPT_OK )
2020-03-28 15:04:55 +01:00
command [ " omega " ] = base64 : : encode ( buffer , ( unsigned long ) buffer_length ) ;
2019-10-26 01:51:40 +02:00
else
log_error ( category : : connection , tr ( " Failed to export identiry ({}) " ) , result ) ;
}
this - > crypto . initiv_command = command . build ( true ) ;
return this - > crypto . initiv_command ;
}
void ProtocolHandler : : handleCommandInitIVExpend ( ts : : Command & cmd ) {
this - > pow . last_buffer = pipes : : buffer { } ;
auto alpha = base64 : : decode ( cmd [ " alpha " ] ) ;
auto beta = base64 : : decode ( cmd [ " beta " ] ) ;
auto omega = base64 : : decode ( cmd [ " omega " ] ) ;
if ( alpha . length ( ) ! = 10 | | memcmp ( alpha . data ( ) , this - > crypto . alpha , 10 ) ! = 0 ) {
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " alpha key missmatch " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
log_error ( category : : connection , tr ( " InitIVExpend contains invalid alpha " ) ) ;
return ;
}
ecc_key server_key { } ;
2020-03-28 15:04:55 +01:00
if ( ecc_import ( ( u_char * ) omega . data ( ) , ( unsigned long ) omega . length ( ) , & server_key ) ! = CRYPT_OK ) {
2019-10-26 01:51:40 +02:00
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to import server key " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
log_error ( category : : connection , tr ( " InitIVExpend contains invalid key " ) ) ;
return ;
}
string error ;
if ( ! this - > crypt_handler . setupSharedSecret ( alpha , beta , & server_key , & this - > crypto . identity , error ) ) {
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to setup encryption " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
log_error ( category : : connection , tr ( " Failed to setup crypto ({}) " ) , error ) ;
return ;
}
2020-02-02 21:54:37 +01:00
this - > crypt_setupped = true ;
2019-10-26 01:51:40 +02:00
if ( this - > server_type = = server_type : : UNKNOWN ) {
if ( cmd [ 0 ] . has ( " teaspeak " ) & & cmd [ " teaspeak " ] . as < bool > ( ) ) {
this - > server_type = server_type : : TEASPEAK ;
} else {
this - > server_type = server_type : : TEAMSPEAK ;
}
}
this - > handle - > call_connect_result . call ( 0 , true ) ;
this - > connection_state = connection_state : : CONNECTING ;
}
int __ed_sha512_init ( sha512_context * ctx ) {
ctx - > context = new hash_state { } ;
return sha512_init ( ( hash_state * ) ctx - > context ) = = CRYPT_OK ;
}
int __ed_sha512_final ( sha512_context * ctx , unsigned char * out ) {
assert ( ctx - > context ) ;
auto result = sha512_done ( ( hash_state * ) ctx - > context , out ) = = CRYPT_OK ;
delete ( hash_state * ) ctx - > context ;
return result ;
}
int __ed_sha512_update ( sha512_context * ctx , const unsigned char * msg , size_t len ) {
assert ( ctx - > context ) ;
2020-03-28 15:04:55 +01:00
return sha512_process ( ( hash_state * ) ctx - > context , msg , ( unsigned long ) len ) = = CRYPT_OK ;
2019-10-26 01:51:40 +02:00
}
static sha512_functions __ed_sha512_functions {
__ed_sha512_init ,
__ed_sha512_final ,
__ed_sha512_update
} ;
void ProtocolHandler : : handleCommandInitIVExpend2 ( ts : : Command & cmd ) {
this - > pow . last_buffer = pipes : : buffer { } ;
/* setup ed functions */
if ( & __ed_sha512_functions ! = & _ed_sha512_functions )
_ed_sha512_functions = __ed_sha512_functions ;
auto beta = base64 : : decode ( cmd [ " beta " ] ) ;
auto omega = base64 : : decode ( cmd [ " omega " ] ) ;
auto proof = base64 : : decode ( cmd [ " proof " ] ) ;
auto crypto_chain_data = base64 : : decode ( cmd [ " l " ] ) ;
auto crypto_root = cmd [ 0 ] . has ( " root " ) ? base64 : : decode ( cmd [ " root " ] ) : string ( ( char * ) license : : teamspeak : : public_root , 32 ) ;
auto crypto_hash = digest : : sha256 ( crypto_chain_data ) ;
/* suspecius, tries the server to hide himself? We dont know */
if ( this - > server_type = = server_type : : UNKNOWN ) {
if ( cmd [ 0 ] . has ( " root " ) )
this - > server_type = server_type : : TEASPEAK ;
else
this - > server_type = server_type : : TEAMSPEAK ;
}
ecc_key server_key { } ;
2020-03-28 15:04:55 +01:00
if ( ecc_import ( ( u_char * ) omega . data ( ) , ( unsigned long ) omega . length ( ) , & server_key ) ! = CRYPT_OK ) {
2019-10-26 01:51:40 +02:00
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to import server key " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
log_error ( category : : connection , tr ( " InitIVExpend contains invalid key " ) ) ;
return ;
}
int result , crypt_result ;
2020-03-28 15:04:55 +01:00
if ( ( crypt_result = ecc_verify_hash ( ( u_char * ) proof . data ( ) , ( unsigned long ) proof . length ( ) , ( u_char * ) crypto_hash . data ( ) , ( unsigned long ) crypto_hash . length ( ) , & result , & server_key ) ) ! = CRYPT_OK | | result ! = 1 ) {
2019-10-26 01:51:40 +02:00
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to verify server integrity " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
return ;
}
string error ;
auto crypto_chain = license : : teamspeak : : LicenseChain : : parse ( crypto_chain_data , error , false ) ;
if ( ! crypto_chain ) {
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to read crypto chain " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
return ;
}
auto server_public_key = crypto_chain - > generatePublicKey ( * ( license : : teamspeak : : LicensePublicKey * ) crypto_root . data ( ) ) ;
crypto_chain - > print ( ) ;
u_char seed [ 32 ] ;
ed25519_create_seed ( seed ) ;
u_char public_key [ 32 ] , private_key [ 64 ] ; /* We need 64 bytes because we're doing some SHA512 actions */
ed25519_create_keypair ( public_key , private_key , seed ) ;
/* send clientek response */
{
size_t sign_buffer_length = 200 ;
char sign_buffer [ 200 ] ;
prng_state prng_state { } ;
memset ( & prng_state , 0 , sizeof ( prng_state ) ) ;
auto proof_data = digest : : sha256 ( string ( ( char * ) public_key , 32 ) + beta ) ;
2020-03-28 15:04:55 +01:00
if ( ecc_sign_hash ( ( uint8_t * ) proof_data . data ( ) , ( unsigned long ) proof_data . length ( ) , ( uint8_t * ) sign_buffer , ( unsigned long * ) & sign_buffer_length , & prng_state , find_prng ( " sprng " ) , & this - > crypto . identity ) ! = CRYPT_OK ) {
2019-10-26 01:51:40 +02:00
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to generate proof of identity " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
return ;
}
Command response ( " clientek " ) ;
response [ " ek " ] = base64 : : encode ( ( char * ) public_key , 32 ) ;
2020-03-28 15:04:55 +01:00
response [ " proof " ] = base64 : : encode ( sign_buffer , ( unsigned long ) sign_buffer_length ) ;
2019-10-26 01:51:40 +02:00
/* no need to send this because we're sending the clientinit as the begin packet along with the POW init */
//this->_packet_id_manager.nextPacketId(PacketTypeInfo::Command); /* skip the first because we've send our first command within the low level handshake packets */
this - > send_command ( response , [ & ] ( bool success ) {
if ( success ) {
/* trigger connected; because the connection has been established on protocol layer */
2020-02-02 21:54:37 +01:00
this - > crypt_setupped = true ;
2019-10-26 01:51:40 +02:00
this - > handle - > call_connect_result . call ( 0 , true ) ;
this - > connection_state = connection_state : : CONNECTING ;
}
} ) ; /* needs to be encrypted at the time! */
}
if ( ! this - > crypt_handler . setupSharedSecretNew ( string ( ( char * ) this - > crypto . alpha , 10 ) , beta , ( char * ) private_key , server_public_key . data ( ) ) ) {
this - > handle - > call_connect_result . call ( this - > handle - > errors . register_error ( tr ( " failed to setup encryption " ) ) , true ) ;
this - > handle - > close_connection ( ) ;
return ;
}
}