From e9a51455eb03b7d06abe51aff4e7459496276654 Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 8 Apr 2013 21:25:32 +0000 Subject: [PATCH] added crypto functions and msg sending/receiving for private distant chat git-svn-id: http://svn.code.sf.net/p/retroshare/code/branches/v0.5-GenericTunneling@6299 b45a01b8-16f6-495d-af2f-9b41ad6348cc --- libretroshare/src/rsserver/rsinit.cc | 2 + libretroshare/src/services/p3chatservice.cc | 215 +++++++++++++++++--- libretroshare/src/services/p3chatservice.h | 54 ++++- libretroshare/src/tests/util/aes_test.cc | 4 +- libretroshare/src/util/rsaes.cc | 9 +- libretroshare/src/util/rsaes.h | 8 +- 6 files changed, 254 insertions(+), 38 deletions(-) diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 45cf2069f..f0188e1e9 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -2233,7 +2233,9 @@ int RsServer::StartupRetroShare() p3turtle *tr = new p3turtle(mLinkMgr,ftserver) ; rsTurtle = tr ; pqih -> addService(tr); + ftserver->connectToTurtleRouter(tr) ; + chatSrv->connectToTurtleRouter(tr) ; pqih -> addService(ad); pqih -> addService(msgSrv); diff --git a/libretroshare/src/services/p3chatservice.cc b/libretroshare/src/services/p3chatservice.cc index c4e3b4a43..93e6df4d6 100644 --- a/libretroshare/src/services/p3chatservice.cc +++ b/libretroshare/src/services/p3chatservice.cc @@ -25,8 +25,10 @@ #include #include "util/rsdir.h" +#include "util/rsaes.h" #include "util/rsrandom.h" #include "util/rsstring.h" +#include "turtle/p3turtle.h" #include "retroshare/rsiface.h" #include "retroshare/rspeers.h" #include "pqi/pqibin.h" @@ -55,15 +57,23 @@ static const time_t MIN_DELAY_BETWEEN_PUBLIC_LOBBY_REQ = 20 ; // don't ask fo p3ChatService::p3ChatService(p3LinkMgr *lm, p3HistoryMgr *historyMgr) :p3Service(RS_SERVICE_TYPE_CHAT), p3Config(CONFIG_TYPE_CHAT), mChatMtx("p3ChatService"), mLinkMgr(lm) , mHistoryMgr(historyMgr) { - addSerialType(new RsChatSerialiser()); - + _serializer = new RsChatSerialiser() ; _own_avatar = NULL ; _custom_status_string = "" ; _time_shift_average = 0.0f ; _default_nick_name = rsPeers->getPeerName(rsPeers->getOwnId()); _should_reset_lobby_counts = false ; + mTurtle = NULL ; last_visible_lobby_info_request_time = 0 ; + + addSerialType(_serializer) ; +} + +void p3ChatService::connectToTurtleRouter(p3turtle *tr) +{ + mTurtle = tr ; + tr->registerTunnelService(this) ; } int p3ChatService::tick() @@ -608,38 +618,41 @@ void p3ChatService::receiveChatQueue() RsItem *item ; while(NULL != (item=recvItem())) - { + handleIncomingItem(item) ; +} + +void p3ChatService::handleIncomingItem(RsItem *item) +{ #ifdef CHAT_DEBUG - std::cerr << "p3ChatService::receiveChatQueue() Item:" << (void*)item << std::endl ; + std::cerr << "p3ChatService::receiveChatQueue() Item:" << (void*)item << std::endl ; #endif - // RsChatMsgItems needs dynamic_cast, since they have derived siblings. - // - RsChatMsgItem *ci = dynamic_cast(item) ; - if(ci != NULL) - { - if(! handleRecvChatMsgItem(ci)) - delete ci ; + // RsChatMsgItems needs dynamic_cast, since they have derived siblings. + // + RsChatMsgItem *ci = dynamic_cast(item) ; + if(ci != NULL) + { + if(! handleRecvChatMsgItem(ci)) + delete ci ; - continue ; // don't delete! It's handled by handleRecvChatMsgItem in some specific cases only. - } - - switch(item->PacketSubType()) - { - case RS_PKT_SUBTYPE_CHAT_STATUS: handleRecvChatStatusItem (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_AVATAR: handleRecvChatAvatarItem (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_INVITE: handleRecvLobbyInvite (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_CHALLENGE: handleConnectionChallenge (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_EVENT: handleRecvChatLobbyEventItem (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_UNSUBSCRIBE: handleFriendUnsubscribeLobby (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_REQUEST: handleRecvChatLobbyListRequest(dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST: handleRecvChatLobbyList (dynamic_cast(item)) ; break ; - case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_deprecated: handleRecvChatLobbyList (dynamic_cast(item)) ; break ; - - default: - std::cerr << "Unhandled item subtype " << item->PacketSubType() << " in p3ChatService: " << std::endl; - } - delete item ; + return ; // don't delete! It's handled by handleRecvChatMsgItem in some specific cases only. } + + switch(item->PacketSubType()) + { + case RS_PKT_SUBTYPE_CHAT_STATUS: handleRecvChatStatusItem (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_AVATAR: handleRecvChatAvatarItem (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_INVITE: handleRecvLobbyInvite (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_CHALLENGE: handleConnectionChallenge (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_EVENT: handleRecvChatLobbyEventItem (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_UNSUBSCRIBE: handleFriendUnsubscribeLobby (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_REQUEST: handleRecvChatLobbyListRequest(dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST: handleRecvChatLobbyList (dynamic_cast(item)) ; break ; + case RS_PKT_SUBTYPE_CHAT_LOBBY_LIST_deprecated: handleRecvChatLobbyList (dynamic_cast(item)) ; break ; + + default: + std::cerr << "Unhandled item subtype " << item->PacketSubType() << " in p3ChatService: " << std::endl; + } + delete item ; } void p3ChatService::handleRecvChatLobbyListRequest(RsChatLobbyListRequestItem *clr) @@ -2737,4 +2750,146 @@ void p3ChatService::cleanLobbyCaches() sendConnectionChallenge(*it) ; } +bool p3ChatService::handleTunnelRequest(const std::string& hash,const std::string& peer_id,std::string& description_info_string) +{ + std::map::const_iterator it = _distant_chat_invites.find(hash) ; + + std::cerr << "p3ChatService::handleTunnelRequest: received tunnel request for hash " << hash << std::endl; + + if(it == _distant_chat_invites.end()) + return false ; + + std::cerr << "Responding ok." << std::endl; + return true ; +} + +void p3ChatService::receiveTurtleData( RsTurtleGenericTunnelItem *gitem,const std::string& hash, + const std::string& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) +{ + std::cerr << "p3ChatService::receiveTurtleData(): Received turtle data. " << std::endl; + std::cerr << " hash = " << hash << std::endl; + std::cerr << " vpid = " << virtual_peer_id << std::endl; + std::cerr << " dir = " << virtual_peer_id << std::endl; + + RsTurtleGenericDataItem *item = dynamic_cast(gitem) ; + + if(item == NULL) + { + std::cerr << "(EE) item is not a data item. That is an error." << std::endl; + return ; + } + std::cerr << " size = " << item->data_size << std::endl; + std::cerr << " data = " << (void*)item->data_bytes << std::endl; + + uint8_t aes_key[16] ; + + { + RsStackMutex stack(mChatMtx); /********** STACK LOCKED MTX ******/ + std::map::iterator it = _distant_chat_peers.find(virtual_peer_id) ; + + if(it == _distant_chat_peers.end()) + { + std::cerr << "(EE) item is not coming out of a registered tunnel. Weird. hash=" << hash << ", peer id = " << virtual_peer_id << std::endl; + return ; + } + it->second.last_contact = time(NULL) ; + memcpy(aes_key,it->second.aes_key,8) ; + } + + // Call the AES crypto module + // - the IV is the first 8 bytes of item->data_bytes + + if(item->data_size < 8) + { + std::cerr << "(EE) item encrypted data stream is too small: size = " << item->data_size << std::endl; + return ; + } + uint32_t decrypted_size ; + uint8_t *decrypted_data = new uint8_t[RsAES::get_buffer_size(item->data_size-8)]; + + if(!RsAES::aes_decrypt_8_16((uint8_t*)item->data_bytes+8,item->data_size-8,aes_key,(uint8_t*)item->data_bytes,decrypted_data,decrypted_size)) + { + std::cerr << "(EE) packet decryption failed." << std::endl; + delete[] decrypted_data ; + return ; + } + + std::cerr << "(II) Decrypted data: size=" << decrypted_size << std::endl; + + // Now try deserialise the decrypted data to make an RsItem out of it. + // + RsItem *citem = _serializer->deserialise(decrypted_data,&decrypted_size) ; + delete[] decrypted_data ; + + if(citem == NULL) + { + std::cerr << "(EE) item could not be de-serialized. That is an error." << std::endl; + return ; + } + + // Setup the virtual peer to be the origin, and pass it on. + // + citem->PeerId(virtual_peer_id) ; + handleIncomingItem(citem) ; // Treats the item, and deletes it +} + +void p3ChatService::sendTurtleData(RsChatItem *item, const std::string& virtual_peer_id) +{ + uint32_t rssize = item->serial_size(); + uint8_t *buff = new uint8_t[rssize] ; + + if(!item->serialise(buff,rssize)) + { + std::cerr << "(EE) p3ChatService::sendTurtleData(): Could not serialise item!" << std::endl; + delete[] buff ; + return ; + } + + uint8_t aes_key[16] ; + + { + RsStackMutex stack(mChatMtx); /********** STACK LOCKED MTX ******/ + std::map::iterator it = _distant_chat_peers.find(virtual_peer_id) ; + + if(it == _distant_chat_peers.end()) + { + std::cerr << "(EE) item is not coming out of a registered tunnel. Weird. peer id = " << virtual_peer_id << std::endl; + delete[] buff ; + return ; + } + it->second.last_contact = time(NULL) ; + memcpy(aes_key,it->second.aes_key,8) ; + } + // Now encrypt this data using AES. + // + uint32_t encrypted_size ; + uint8_t *encrypted_data = new uint8_t[RsAES::get_buffer_size(rssize)]; + + uint64_t IV = RSRandom::random_u64() ; // make a random 8 bytes IV + + if(!RsAES::aes_crypt_8_16(buff,rssize,aes_key,(uint8_t*)&IV,encrypted_data,encrypted_size)) + { + std::cerr << "(EE) packet encryption failed." << std::endl; + delete[] encrypted_data ; + delete[] buff ; + return ; + } + delete[] buff ; + + // make a TurtleGenericData item out of it: + // + RsTurtleGenericDataItem *gitem = new RsTurtleGenericDataItem ; + + gitem->data_size = encrypted_size + 8 ; + gitem->data_bytes = malloc(gitem->data_size) ; + + memcpy(gitem->data_bytes ,&IV,8) ; + memcpy(gitem->data_bytes+8,encrypted_data,encrypted_size) ; + + delete[] encrypted_data ; + delete item ; + + mTurtle->sendTurtleData(virtual_peer_id,gitem) ; +} + diff --git a/libretroshare/src/services/p3chatservice.h b/libretroshare/src/services/p3chatservice.h index 9337f97dc..d7ce23cf4 100644 --- a/libretroshare/src/services/p3chatservice.h +++ b/libretroshare/src/services/p3chatservice.h @@ -33,10 +33,13 @@ #include "serialiser/rsmsgitems.h" #include "services/p3service.h" +#include "pgp/pgphandler.h" +#include "turtle/turtleclientservice.h" #include "retroshare/rsmsgs.h" class p3LinkMgr; class p3HistoryMgr; +class p3turtle ; //!The basic Chat service. /** @@ -45,7 +48,7 @@ class p3HistoryMgr; * This service uses rsnotify (callbacks librs clients (e.g. rs-gui)) * @see NotifyBase */ -class p3ChatService: public p3Service, public p3Config, public pqiMonitor +class p3ChatService: public p3Service, public p3Config, public pqiMonitor, public RsTurtleClientService { public: p3ChatService(p3LinkMgr *cm, p3HistoryMgr *historyMgr); @@ -191,6 +194,7 @@ class p3ChatService: public p3Service, public p3Config, public pqiMonitor // Receive chat queue void receiveChatQueue(); + void handleIncomingItem(RsItem *); // called by the former, and turtle handler for incoming encrypted items void initRsChatInfo(RsChatMsgItem *c, ChatInfo &i); @@ -289,9 +293,55 @@ class p3ChatService: public p3Service, public p3Config, public pqiMonitor std::map _lobby_ids ; std::string _default_nick_name ; float _time_shift_average ; - time_t last_lobby_challenge_time ; // prevents bruteforce attack + time_t last_lobby_challenge_time ; // prevents bruteforce attack time_t last_visible_lobby_info_request_time ; // allows to ask for updates bool _should_reset_lobby_counts ; + + RsChatSerialiser *_serializer ; + + // ===========================================================// + // Members related to anonymous distant chat. // + // ===========================================================// + + struct DistantChatInvite + { + unsigned char aes_key[16] ; + time_t validity_time_stamp ; + std::string virtual_peer_id ; + }; + struct DistantChatPeerInfo + { + time_t last_contact ; // used to send keep alive packets + unsigned char aes_key[16] ; // key to encrypt packets + }; + + // This map contains the ongoing invites. This is the list where to look to + // handle tunnel requests. + // + std::map _distant_chat_invites ; + + // This maps contains the current peers to talk to with distant chat. + // + std::map _distant_chat_peers ; + + // Creates the invite if the public key of the distant peer is available. + // Om success, stores the invite in the map above, so that we can respond to tunnel requests. + // + bool createDistantChatInvite(PGPIdType pgp_id,time_t time_of_validity,TurtleFileHash& hash) ; + + // Overloaded from RsTurtleClientService + + virtual bool handleTunnelRequest(const std::string& hash,const std::string& peer_id,std::string& description_info_string) ; + virtual void receiveTurtleData(RsTurtleGenericTunnelItem *item,const std::string& hash,const std::string& virtual_peer_id,RsTurtleGenericTunnelItem::Direction direction) ; + + // Utility functions + + void cleanDistantChatInvites() ; + void sendTurtleData(RsChatItem *,const std::string& virtual_peer_id) ; + + p3turtle *mTurtle ; + public: + void connectToTurtleRouter(p3turtle *) ; }; class p3ChatService::StateStringInfo diff --git a/libretroshare/src/tests/util/aes_test.cc b/libretroshare/src/tests/util/aes_test.cc index a31bba4ab..7123eff7d 100644 --- a/libretroshare/src/tests/util/aes_test.cc +++ b/libretroshare/src/tests/util/aes_test.cc @@ -84,7 +84,7 @@ int main(int argc,char *argv[]) unsigned char output_data[source_string.size() + 16] ; uint32_t output_data_length = source_string.size() + 16 ; - CHECK(RsAes::aes_crypt_8_16( (const uint8_t*)source_string.c_str(),source_string.length(),key_data,salt,output_data,output_data_length)) ; + CHECK(RsAES::aes_crypt_8_16( (const uint8_t*)source_string.c_str(),source_string.length(),key_data,salt,output_data,output_data_length)) ; std::cerr << "Round " << i << " salt=" ; printHex(salt,8) ; @@ -95,7 +95,7 @@ int main(int argc,char *argv[]) unsigned char output_data2[output_data_length + 16] ; uint32_t output_data_length2 = output_data_length + 16 ; - CHECK(RsAes::aes_decrypt_8_16(output_data,output_data_length,key_data,salt,output_data2,output_data_length2)) ; + CHECK(RsAES::aes_decrypt_8_16(output_data,output_data_length,key_data,salt,output_data2,output_data_length2)) ; std::cerr << " output_length = " << output_data_length2 << ", decrypted string = " ; printHex(output_data2,output_data_length2) ; diff --git a/libretroshare/src/util/rsaes.cc b/libretroshare/src/util/rsaes.cc index 5dc7b20c6..34a59a68f 100644 --- a/libretroshare/src/util/rsaes.cc +++ b/libretroshare/src/util/rsaes.cc @@ -28,7 +28,12 @@ #include "rsaes.h" -bool RsAes::aes_crypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key_data[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) +uint32_t RsAES::get_buffer_size(uint32_t n) +{ + return n + AES_BLOCK_SIZE ; +} + +bool RsAES::aes_crypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key_data[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) { int nrounds = 5; uint8_t key[32], iv[32]; @@ -70,7 +75,7 @@ bool RsAes::aes_crypt_8_16(const uint8_t *input_data,uint32_t input_data_length, return true; } -bool RsAes::aes_decrypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key_data[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) +bool RsAES::aes_decrypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key_data[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) { int nrounds = 5; uint8_t key[32], iv[32]; diff --git a/libretroshare/src/util/rsaes.h b/libretroshare/src/util/rsaes.h index 8acf082ce..21a8c701c 100644 --- a/libretroshare/src/util/rsaes.h +++ b/libretroshare/src/util/rsaes.h @@ -25,12 +25,12 @@ #include -class RsAes +class RsAES { public: // Crypt/decrypt data using a 16 bytes key and a 8 bytes salt. // - // output_data allocation is left to the client. The size should be at least input_data_length+16 bytes. + // output_data allocation is left to the client. The size should be at least RsAES::get_buffer_size(input_data_length) // // Return value: // true: encryption/decryption ok @@ -39,5 +39,9 @@ class RsAes // static bool aes_crypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) ; static bool aes_decrypt_8_16(const uint8_t *input_data,uint32_t input_data_length,uint8_t key[16],uint8_t salt[8],uint8_t *output_data,uint32_t& output_data_length) ; + + // computes the safe buffer size to store encrypted/decrypted data for the given input stream size + // + static uint32_t get_buffer_size(uint32_t size) ; };