From 54a0f873691d4749203e9b5137e2f8564392cfbb Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Mon, 30 Jan 2017 15:18:12 +0100 Subject: [PATCH] GxsMail implemented RSA encrypted mails sending Add notice about param not being checked in GxsSecurity Use proper types for RsGenExchange::getMsg* params Implement bitwise or and not operators for t_RsGenericIdType GxsMail unsubscribe old unused groups GxsMail initial work on supporting client services Implemented p3IdService::encryptData(...) for multiple recipients --- libretroshare/src/gxs/gxssecurity.h | 5 +- libretroshare/src/gxs/rsgenexchange.cc | 5 +- libretroshare/src/gxs/rsgenexchange.h | 8 +- libretroshare/src/libretroshare.pro | 2 +- libretroshare/src/retroshare/rsids.h | 16 + libretroshare/src/retroshare/rstokenservice.h | 8 +- libretroshare/src/rsserver/rsinit.cc | 2 +- .../src/serialiser/rsgxsmailitems.cc | 96 ++++++ libretroshare/src/serialiser/rsgxsmailitems.h | 196 ++++++------- libretroshare/src/services/p3gxsmails.cpp | 277 ++++++++++++++---- libretroshare/src/services/p3gxsmails.h | 106 +++++-- libretroshare/src/services/p3idservice.cc | 93 +++++- libretroshare/src/services/p3idservice.h | 38 ++- 13 files changed, 655 insertions(+), 197 deletions(-) create mode 100644 libretroshare/src/serialiser/rsgxsmailitems.cc diff --git a/libretroshare/src/gxs/gxssecurity.h b/libretroshare/src/gxs/gxssecurity.h index b19f81302..129d07147 100644 --- a/libretroshare/src/gxs/gxssecurity.h +++ b/libretroshare/src/gxs/gxssecurity.h @@ -35,9 +35,10 @@ /*! - * This contains functionality for performing security - * operations needed to validate data received in RsGenExchange + * This contains functionality for performing basic security operations needed + * in RsGenExchange operations. * Also has routine for creating security objects around msgs and groups + * TODO: Those functions doesn't do param checking! */ class GxsSecurity { diff --git a/libretroshare/src/gxs/rsgenexchange.cc b/libretroshare/src/gxs/rsgenexchange.cc index aa098a1dc..1b2cb3f8f 100644 --- a/libretroshare/src/gxs/rsgenexchange.cc +++ b/libretroshare/src/gxs/rsgenexchange.cc @@ -1332,7 +1332,7 @@ bool RsGenExchange::getGroupData(const uint32_t &token, std::vector - bool getMsgDataT(const uint32_t &token, std::map >& msgItems) + bool getMsgDataT( uint32_t token, std::map >& msgItems) { GxsMsgDataMap msgData; bool ok = getMsgData(token, msgData); @@ -365,7 +365,7 @@ public: * @param token token to be redeemed for message item retrieval * @param msgItems */ - bool getMsgRelatedData(const uint32_t &token, GxsMsgRelatedDataMap& msgItems); + bool getMsgRelatedData(uint32_t token, GxsMsgRelatedDataMap& msgItems); protected: diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index e1617391a..d94d7005b 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -855,7 +855,7 @@ gxsphotoshare { rs_gxs_mail { HEADERS += serialiser/rsgxsmailitems.h services/p3gxsmails.h - SOURCES += services/p3gxsmails.cpp + SOURCES += serialiser/rsgxsmailitems.cc services/p3gxsmails.cpp } diff --git a/libretroshare/src/retroshare/rsids.h b/libretroshare/src/retroshare/rsids.h index db842ab21..22528043b 100644 --- a/libretroshare/src/retroshare/rsids.h +++ b/libretroshare/src/retroshare/rsids.h @@ -94,6 +94,22 @@ template c inline bool operator==(const t_RsGenericIdType& fp) const { return !memcmp(bytes,fp.bytes,ID_SIZE_IN_BYTES) ; } inline bool operator!=(const t_RsGenericIdType& fp) const { return !!memcmp(bytes,fp.bytes,ID_SIZE_IN_BYTES); } inline bool operator< (const t_RsGenericIdType& fp) const { return (memcmp(bytes,fp.bytes,ID_SIZE_IN_BYTES) < 0) ; } + inline t_RsGenericIdType + operator~ () const + { + t_RsGenericIdType ret; + for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) + ret.bytes[i] = ~bytes[i]; + return ret; + } + inline t_RsGenericIdType + operator| (const t_RsGenericIdType& fp) const + { + t_RsGenericIdType ret; + for(uint32_t i=0; i < ID_SIZE_IN_BYTES; ++i) + ret.bytes[i] = bytes[i] | fp.bytes[i]; + return ret; + } inline bool isNull() const { diff --git a/libretroshare/src/retroshare/rstokenservice.h b/libretroshare/src/retroshare/rstokenservice.h index c55213965..79097cc5c 100644 --- a/libretroshare/src/retroshare/rstokenservice.h +++ b/libretroshare/src/retroshare/rstokenservice.h @@ -137,7 +137,7 @@ public: /*! * Use this to request group related information - * @param token The token returned for the request, store this value to pool for request completion + * @param token The token returned for the request, store this value to poll for request completion * @param ansType The type of result (e.g. group data, meta, ids) * @param opts Additional option that affect outcome of request. Please see specific services, for valid values * @param groupIds group id to request info for @@ -147,7 +147,7 @@ public: /*! * Use this to request all group related info - * @param token The token returned for the request, store this value to pool for request completion + * @param token The token returned for the request, store this value to poll for request completion * @param ansType The type of result (e.g. group data, meta, ids) * @param opts Additional option that affect outcome of request. Please see specific services, for valid values * @return @@ -155,7 +155,7 @@ public: virtual bool requestGroupInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts) = 0; /*! - * Use this to get msg related information, store this value to pole for request completion + * Use this to get msg related information, store this value to poll for request completion * @param token The token returned for the request * @param ansType The type of result wanted * @param opts Additional option that affect outcome of request. Please see specific services, for valid values @@ -165,7 +165,7 @@ public: virtual bool requestMsgInfo(uint32_t &token, uint32_t ansType, const RsTokReqOptions &opts, const GxsMsgReq& msgIds) = 0; /*! - * Use this to get msg related information, store this value to pole for request completion + * Use this to get msg related information, store this value to poll for request completion * @param token The token returned for the request * @param ansType The type of result wanted * @param opts Additional option that affect outcome of request. Please see specific services, for valid values diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index c8a514043..3eb9ba146 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -1491,7 +1491,7 @@ int RsServer::StartupRetroShare() RsGeneralDataService* gxsmail_ds = new RsDataService( currGxsDir + "/", "gxsmails_db", RS_SERVICE_TYPE_GXS_MAIL, NULL, rsInitConfig->gxs_passwd ); - p3GxsMails* mGxsMails = new p3GxsMails(gxsmail_ds, NULL, mGxsIdService); + p3GxsMails* mGxsMails = new p3GxsMails(gxsmail_ds, NULL, *mGxsIdService); RsGxsNetService* gxsmails_ns = new RsGxsNetService( RS_SERVICE_TYPE_GXS_MAIL, gxsmail_ds, nxsMgr, mGxsMails, mGxsMails->getServiceInfo(), mReputations, mGxsCircles, diff --git a/libretroshare/src/serialiser/rsgxsmailitems.cc b/libretroshare/src/serialiser/rsgxsmailitems.cc new file mode 100644 index 000000000..a3d624b44 --- /dev/null +++ b/libretroshare/src/serialiser/rsgxsmailitems.cc @@ -0,0 +1,96 @@ +/* + * GXS Mailing Service + * Copyright (C) 2016-2017 Gioacchino Mazzurco + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +#include "serialiser/rsgxsmailitems.h" + +const RsGxsId RsGxsMailBaseItem::allRecipientsHint("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"); + + +bool RsGxsMailBaseItem::serialize(uint8_t* data, uint32_t size, + uint32_t& offset) const +{ + bool ok = setRsItemHeader(data, size, PacketId(), size); + ok = ok && (offset += 8); // Take in account the header + ok = ok && setRawUInt8(data, size, &offset, cryptoType); + ok = ok && recipientsHint.serialise(data, size, offset); + return ok; +} + +bool RsGxsMailBaseItem::deserialize(const uint8_t* data, uint32_t& size, + uint32_t& offset) +{ + void* dataPtr = reinterpret_cast(const_cast(data)); + uint32_t rssize = getRsItemSize(dataPtr); + uint32_t roffset = offset + 8; + bool ok = rssize <= size; + uint8_t crType; + ok = ok && getRawUInt8(dataPtr, rssize, &offset, &crType); + cryptoType = static_cast(crType); + ok = ok && recipientsHint.deserialise(dataPtr, rssize, roffset); + if(ok) { size = rssize; offset = roffset; } + else size = 0; + return ok; +} + +std::ostream&RsGxsMailBaseItem::print(std::ostream& out, uint16_t) +{ return out; } + +bool RsGxsMailSerializer::serialise(RsItem* item, void* data, uint32_t* size) +{ + uint32_t itemSize = RsGxsMailSerializer::size(item); + if(*size < itemSize) + { + std::cout << "RsGxsMailSerializer::serialise(...) failed due to wrong size: " + << size << " < " << itemSize << std::endl; + return false; + } + + uint8_t* dataPtr = reinterpret_cast(data); + bool ok = true; + switch(item->PacketSubType()) + { + case GXS_MAIL_SUBTYPE_MAIL: + { + uint32_t offset = 0; + RsGxsMailItem* i = dynamic_cast(item); + ok = ok && i->serialize(dataPtr, itemSize, offset); + break; + } + case GXS_MAIL_SUBTYPE_ACK: + { + RsGxsMailAckItem* i = dynamic_cast(item); + ok = ok && setRsItemHeader(data, itemSize, item->PacketId(), itemSize); + uint32_t offset = 8; + ok = ok && i->recipient.serialise(data, itemSize, offset); + break; + } + case GXS_MAIL_SUBTYPE_GROUP: + ok = ok && setRsItemHeader(data, itemSize, item->PacketId(), itemSize); + break; + default: ok = false; break; + } + + if(ok) + { + *size = itemSize; + return true; + } + + std::cout << "RsGxsMailSerializer::serialise(...) failed!" << std::endl; + return false; +} diff --git a/libretroshare/src/serialiser/rsgxsmailitems.h b/libretroshare/src/serialiser/rsgxsmailitems.h index 2be002020..f57439f43 100644 --- a/libretroshare/src/serialiser/rsgxsmailitems.h +++ b/libretroshare/src/serialiser/rsgxsmailitems.h @@ -24,28 +24,36 @@ #include "serialiser/rstlvidset.h" #include "retroshare/rsgxsflags.h" #include "retroshare/rsgxscircles.h" // For: GXS_CIRCLE_TYPE_PUBLIC +#include "services/p3idservice.h" - -enum GxsMailSubtypes +/// Values must fit into uint8_t +enum GxsMailItemsSubtypes { - GXS_MAIL_SUBTYPE_MAIL = 1, - GXS_MAIL_SUBTYPE_ACK, - GXS_MAIL_SUBTYPE_GROUP + GXS_MAIL_SUBTYPE_MAIL = 1, + GXS_MAIL_SUBTYPE_ACK = 2, + GXS_MAIL_SUBTYPE_GROUP = 3 }; struct RsGxsMailBaseItem : RsGxsMsgItem { - RsGxsMailBaseItem(GxsMailSubtypes subtype) : - RsGxsMsgItem(RS_SERVICE_TYPE_GXS_MAIL, (uint8_t)subtype), flags(0) {} + RsGxsMailBaseItem(GxsMailItemsSubtypes subtype) : + RsGxsMsgItem( RS_SERVICE_TYPE_GXS_MAIL, + static_cast(subtype) ) {} - enum RsGxsMailFlags { READ = 0x1 }; - uint8_t flags; + /// Values must fit into uint8_t + enum EncryptionMode + { + CLEAR_TEXT = 1, + RSA = 2, + UNDEFINED_ENCRYPTION = 250 + }; + EncryptionMode cryptoType; /** - * @brief recipient_hint used instead of plain recipient id, so sender can + * @brief recipientsHint used instead of plain recipient id, so sender can * decide the equilibrium between exposing the recipient and the cost of * completely anonymize it. So a bunch of luky non recipient can conclude - * rapidly that they are not recipiend without trying to decrypt the + * rapidly that they are not the recipient without trying to decrypt the * message. * * To be able to decide how much metadata we disclose sending a message we @@ -54,10 +62,10 @@ struct RsGxsMailBaseItem : RsGxsMsgItem * obscure like 0xFF...FF so potentially everyone could be the recipient, or * may expose the complete recipient id or be a middle ground. * To calculate arbitrary precise hint one do a bitwise OR of the recipients - * keys and an arbytrary salting mask, the more recipients has the mail and - * the more 1 bits has the mask the less accurate is the hint. + * keys and an arbitrary salt, the more recipients has the mail and the more + * 1 bits has the salt the less accurate is the hint. * This way the sender is able to adjust the metadata privacy needed for the - * message, in the more private case (recipient_hint == 0xFF...FF) no one + * message, in the more private case (recipientsHint == 0xFFF...FFF) no one * has a clue about who is the actual recipient, while this imply the cost * that every potencial recipient has to try to decrypt it to know if it is * for herself. This way a bunch of non recipients can rapidly discover that @@ -76,42 +84,75 @@ struct RsGxsMailBaseItem : RsGxsMsgItem * mail is directed to the actual recipient as the "apparently" * corresponding hint may be fruit of a "luky" salting of another id. */ - uint32_t recipient_hint; + RsGxsId recipientsHint; + + void inline saltRecipientHint(const RsGxsId& salt) + { saltRecipientHint(recipientsHint, salt); } + + void static inline saltRecipientHint(RsGxsId& hint, const RsGxsId& salt) + { hint = hint | salt; } /** - * @brief maybe_recipient given an id and an hint check if they match - * @see recipient_hint + * @brief maybeRecipient given an id and an hint check if they match + * @see recipientHint * @note this is not the final implementation as id and hint are not 32bit * integers it is just to not forget how to verify the hint/id matching * fastly with boolean ops * @return true if the id may be recipient of the hint, false otherwise */ - static bool maybe_recipient(uint32_t id, uint32_t hint) - { return (~id|hint) == 0xFFFFFFFF; } + bool static inline maybeRecipient(const RsGxsId& hint, const RsGxsId& id) + { return (~id|hint) == allRecipientsHint; } + + const static RsGxsId allRecipientsHint; + + void inline clear() + { + cryptoType = UNDEFINED_ENCRYPTION; + recipientsHint.clear(); + meta = RsMsgMetaData(); + } + + static uint32_t inline size() + { + return 8 + // Header + 1 + // cryptoType + RsGxsId::serial_size(); // recipientsHint + } + bool serialize(uint8_t* data, uint32_t size, uint32_t& offset) const; + bool deserialize(const uint8_t* data, uint32_t& size, uint32_t& offset); + std::ostream &print(std::ostream &out, uint16_t /*indent = 0*/); }; struct RsGxsMailItem : RsGxsMailBaseItem { - RsGxsMailItem() : RsGxsMailBaseItem(GXS_MAIL_SUBTYPE_MAIL) {} + RsGxsMailItem(GxsMailItemsSubtypes subtype) : + RsGxsMailBaseItem(subtype) {} + RsGxsMailItem() : + RsGxsMailBaseItem(GXS_MAIL_SUBTYPE_MAIL) {} - RsTlvGxsIdSet recipients; + /** This should travel encrypted, unless EncryptionMode::CLEAR_TEXT + * is specified */ + std::vector payload; - /** - * @brief body of the email - * Should we ue MIME for compatibility with fido RS-email gateway? - * https://github.com/zeroreserve/fido - * https://github.com/RetroShare/fido - * https://en.wikipedia.org/wiki/MIME - */ - std::string body; - - void clear() + uint32_t size() const { return RsGxsMailBaseItem::size() + payload.size(); } + bool serialize(uint8_t* data, uint32_t size, uint32_t& offset) const { - recipients.TlvClear(); - body.clear(); + return size < MAX_SIZE + && RsGxsMailBaseItem::serialize(data, size, offset) + && memcpy(data+offset, &payload[0], payload.size()); } - std::ostream &print(std::ostream &out, uint16_t indent = 0) - { return recipients.print(out, indent) << body; } + bool deserialize(const uint8_t* data, uint32_t& size, uint32_t& offset) + { + uint32_t bsz = RsGxsMailBaseItem::size(); + uint32_t psz = size - bsz; + return size < MAX_SIZE && size >= bsz + && RsGxsMailBaseItem::deserialize(data, size, offset) + && (payload.resize(psz), memcpy(&payload[0], data+offset, psz)); + } + void clear() { RsGxsMailBaseItem::clear(); payload.clear(); } + + /// Maximum mail size in bytes 10 MiB is more than anything sane can need + const static uint32_t MAX_SIZE = 10*8*1024*1024; }; struct RsGxsMailAckItem : RsGxsMailBaseItem @@ -147,19 +188,13 @@ struct RsGxsMailSerializer : RsSerialType uint32_t size(RsItem* item) { - uint32_t s = 8; // Header - + uint32_t sz = 0; switch(item->PacketSubType()) { case GXS_MAIL_SUBTYPE_MAIL: { RsGxsMailItem* i = dynamic_cast(item); - if(i) - { - s += 4; // RsGxsMailBaseItem::recipient_hint - s += i->recipients.TlvSize(); - s += getRawStringSize(i->body); - } + if(i) sz = i->size(); break; } case GXS_MAIL_SUBTYPE_ACK: @@ -167,74 +202,36 @@ struct RsGxsMailSerializer : RsSerialType RsGxsMailAckItem* i = dynamic_cast(item); if(i) { - s += 4; // RsGxsMailBaseItem::recipient_hint - s += 1; // RsGxsMailAckItem::read - s += i->recipient.serial_size(); + sz = 8; // Header + sz += 4; // RsGxsMailBaseItem::recipient_hint + sz += 1; // RsGxsMailAckItem::read + sz += i->recipient.serial_size(); } break; } - case GXS_MAIL_SUBTYPE_GROUP: break; - default: return 0; + case GXS_MAIL_SUBTYPE_GROUP: sz = 8; break; + default: break; } - return s; + return sz; } - bool serialise(RsItem* item, void* data, uint32_t* size) - { - uint32_t tlvsize = RsGxsMailSerializer::size(item); - uint32_t offset = 0; - - if(*size < tlvsize) return false; - - *size = tlvsize; - - bool ok = true; - ok &= setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize); - - /* skip the header */ - offset += 8; - - switch(item->PacketSubType()) - { - case GXS_MAIL_SUBTYPE_MAIL: - { - RsGxsMailItem* i = dynamic_cast(item); - if(i) - { - ok &= i->recipients.SetTlv(data, tlvsize, &offset); - ok &= setRawString(data, tlvsize, &offset, i->body); - } - break; - } - case GXS_MAIL_SUBTYPE_ACK: - { - RsGxsMailAckItem* i = dynamic_cast(item); - if(i) - { - ok &= i->recipient.serialise(data, tlvsize, offset); - ok &= setRawUInt8(data, tlvsize, &offset, i->flags); - } - break; - } - case GXS_MAIL_SUBTYPE_GROUP: break; - default: ok = false; break; - } - - return ok; - } + bool serialise(RsItem* item, void* data, uint32_t* size); RsItem* deserialise(void* data, uint32_t* size) { uint32_t rstype = getRsItemId(data); uint32_t rssize = getRsItemSize(data); - uint32_t offset = 8; + uint8_t pktv = getRsItemVersion(rstype); + uint16_t srvc = getRsItemService(rstype); - - if ( (RS_PKT_VERSION_SERVICE != getRsItemVersion(rstype)) || - (RS_SERVICE_TYPE_GXS_MAIL != getRsItemService(rstype)) || + if ( (RS_PKT_VERSION_SERVICE != pktv) || // 0x02 + (RS_SERVICE_TYPE_GXS_MAIL != srvc) || // 0x0230 = 560 (*size < rssize) ) + { + print_stacktrace(); return NULL; + } *size = rssize; bool ok = true; @@ -245,15 +242,16 @@ struct RsGxsMailSerializer : RsSerialType case GXS_MAIL_SUBTYPE_MAIL: { RsGxsMailItem* i = new RsGxsMailItem(); - ok &= i->recipients.GetTlv(data, *size, &offset); - ok &= getRawString(data, *size, &offset, i->body); + uint32_t offset = 0; + const uint8_t* dataPtr = reinterpret_cast(data); + ok = ok && i->deserialize(dataPtr, *size, offset); ret = i; break; } case GXS_MAIL_SUBTYPE_ACK: { RsGxsMailAckItem* i = new RsGxsMailAckItem(); - ok &= getRawUInt8(data, *size, &offset, &i->flags); + uint32_t offset = 0; ok &= i->recipient.deserialise(data, *size, offset); ret = i; break; diff --git a/libretroshare/src/services/p3gxsmails.cpp b/libretroshare/src/services/p3gxsmails.cpp index 079186a05..63a07f4d3 100644 --- a/libretroshare/src/services/p3gxsmails.cpp +++ b/libretroshare/src/services/p3gxsmails.cpp @@ -17,30 +17,137 @@ */ #include "p3gxsmails.h" +#include "util/stacktrace.h" -bool p3GxsMails::sendEmail(const RsGxsId& own_gxsid, const RsGxsId& recipient, const std::string& body) +bool p3GxsMails::sendMail( GxsMailsClient::GxsMailSubServices service, + const RsGxsId& own_gxsid, const RsGxsId& recipient, + const uint8_t* data, uint32_t size, + RsGxsMailBaseItem::EncryptionMode cm) +{ + std::vector recipients; + recipients.push_back(&recipient); + return sendMail(service, own_gxsid, recipients, data, size, cm); +} + +bool p3GxsMails::sendMail( GxsMailsClient::GxsMailSubServices service, + const RsGxsId& own_gxsid, + const std::vector& recipients, + const uint8_t* data, uint32_t size, + RsGxsMailBaseItem::EncryptionMode cm ) { std::cout << "p3GxsMails::sendEmail(...)" << std::endl; if(preferredGroupId.isNull()) { - requestGroupsList(); + requestGroupsData(); + std::cerr << "p3GxsMails::sendEmail(...) preferredGroupId.isNull()!" + << std::endl; return false; } - RsGxsMailItem* m = new RsGxsMailItem; - m->meta.mAuthorId = own_gxsid; - m->meta.mGroupId = preferredGroupId; - m->recipients.ids.insert(recipient); - m->body = body; + if(!idService.isOwnId(own_gxsid)) + { + std::cerr << "p3GxsMails::sendEmail(...) isOwnId(own_gxsid) false!" + << std::endl; + return false; + } + + std::set rcps; + typedef std::vector::const_iterator itT; + for(itT it = recipients.begin(); it != recipients.end(); it++) + { + const RsGxsId* gId = *it; + + if(!gId || gId->isNull()) + { + std::cerr << "p3GxsMails::sendEmail(...) got invalid recipient" + << std::endl; + print_stacktrace(); + return false; + } + + rcps.insert(*gId); + } + if(rcps.empty()) + { + std::cerr << "p3GxsMails::sendEmail(...) got no recipients" + << std::endl; + print_stacktrace(); + return false; + } + + RsGxsMailItem* item = new RsGxsMailItem(); + + // Public metadata + item->meta.mAuthorId = own_gxsid; + item->meta.mGroupId = preferredGroupId; + + typedef std::set::const_iterator siT; + for(siT it = rcps.begin(); it != rcps.end(); ++it) + item->saltRecipientHint(*it); + + // If there is jut one recipient salt with a random id to avoid leaking it + if(rcps.size() == 1) item->saltRecipientHint(RsGxsId::random()); + + + /* At this point we do a lot of memory copying, it doesn't look pretty but + * ATM haven't thinked of an elegant way to have the GxsMailSubServices + * travelling encrypted withuot copying memory around or responsabilize the + * client service to embed it in data array that is awful */ + + uint16_t serv = static_cast(service); + uint32_t clearTextPldSize = size+2; + item->payload.resize(clearTextPldSize); + uint32_t _discard = 0; + setRawUInt16(&item->payload[0], clearTextPldSize, &_discard, serv); + memcpy(&item->payload[2], data, size); + + switch (cm) + { + case RsGxsMailBaseItem::CLEAR_TEXT: + { + std::cerr << "p3GxsMails::sendMail(...) you are sending a mail without" + << " encryption, everyone can read it!" << std::endl; + print_stacktrace(); + break; + } + case RsGxsMailBaseItem::RSA: + { + uint8_t* encryptedData = NULL; + uint32_t encryptedSize = 0; + uint32_t encryptError = 0; + if( idService.encryptData( &item->payload[0], clearTextPldSize, + encryptedData, encryptedSize, + rcps, true, encryptError ) ) + { + item->payload.resize(encryptedSize); + memcpy(&item->payload[0], encryptedData, encryptedSize); + free(encryptedData); + break; + } + else + { + std::cerr << "p3GxsMails::sendMail(...) RSA encryption failed! " + << "error_status: " << encryptError << std::endl; + print_stacktrace(); + return false; + } + } + case RsGxsMailBaseItem::UNDEFINED_ENCRYPTION: + default: + std::cerr << "p3GxsMails::sendMail(...) attempt to send mail with wrong" + << " EncryptionMode " << cm << " dropping mail!" << std::endl; + print_stacktrace(); + return false; + } uint32_t token; - publishMsg(token, m); - + publishMsg(token, item); return true; } + void p3GxsMails::handleResponse(uint32_t token, uint32_t req_type) { //std::cout << "p3GxsMails::handleResponse(" << token << ", " << req_type << ")" << std::endl; @@ -54,21 +161,37 @@ void p3GxsMails::handleResponse(uint32_t token, uint32_t req_type) for( std::vector::iterator it = groups.begin(); it != groups.end(); ++it ) { - RsGxsGrpItem* grp = *it; - if(!IS_GROUP_SUBSCRIBED(grp->meta.mSubscribeFlags)) - { - std::cout << "p3GxsMails::handleResponse(...) subscribing to group: " << grp->meta.mGroupId << std::endl; - uint32_t token; - subscribeToGroup(token, grp->meta.mGroupId, true); - } + /* For each group check if it is better candidate then + * preferredGroupId, if it is supplant it and subscribe if it is not + * subscribed yet. + * Otherwise if it has recent messages subscribe. + * If the group was already subscribed has no recent messages + * unsubscribe. + */ - supersedePreferredGroup(grp->meta.mGroupId); + const RsGroupMetaData& meta = (*it)->meta; + bool subscribed = IS_GROUP_SUBSCRIBED(meta.mSubscribeFlags); + bool old = olderThen( meta.mLastPost, + UNUSED_GROUP_UNSUBSCRIBE_INTERVAL ); + bool supersede = supersedePreferredGroup(meta.mGroupId); + uint32_t token; + + if( !subscribed && ( !old || supersede )) + subscribeToGroup(token, meta.mGroupId, true); + else if( subscribed && old ) + subscribeToGroup(token, meta.mGroupId, false); } if(preferredGroupId.isNull()) { - std::cout << "p3GxsMails::handleResponse(...) preferredGroupId.isNull()" << std::endl; - // TODO: Should check if we have friends before of doing this? + /* This is true only at first run when we haven't received mail + * distribuition groups from friends + * TODO: We should check if we have some connected firend too, to + * avoid to create yet another never used mail distribution group. + */ + + std::cerr << "p3GxsMails::handleResponse(...) preferredGroupId.isNu" + << "ll() let's create a new group." << std::endl; uint32_t token; publishGroup(token, new RsGxsMailGroupItem()); queueRequest(token, GROUP_CREATE); @@ -78,45 +201,86 @@ void p3GxsMails::handleResponse(uint32_t token, uint32_t req_type) } case GROUP_CREATE: { - std::cout << "p3GxsMails::handleResponse(...) GROUP_CREATE" << std::endl; + std::cerr << "p3GxsMails::handleResponse(...) GROUP_CREATE" << std::endl; RsGxsGroupId grpId; acknowledgeTokenGrp(token, grpId); supersedePreferredGroup(grpId); break; } + case MAILS_UPDATE: + { + std::cout << "p3GxsMails::handleResponse(...) MAILS_UPDATE" << std::endl; + typedef std::map > GxsMsgDataMap; + GxsMsgDataMap gpMsgMap; + getMsgData(token, gpMsgMap); + for ( GxsMsgDataMap::iterator gIt = gpMsgMap.begin(); + gIt != gpMsgMap.end(); ++gIt ) + { + typedef std::vector vT; + vT& mv(gIt->second); + for( vT::const_iterator mIt = mv.begin(); mIt != mv.end(); ++mIt ) + { + RsGxsMsgItem* it = *mIt; + std::cout << "p3GxsMails::handleResponse(...) MAILS_UPDATE " + << (uint32_t)it->PacketSubType() << std::endl; + switch(it->PacketSubType()) + { + case GXS_MAIL_SUBTYPE_MAIL: + { + RsGxsMailItem* msg = dynamic_cast(it); + if(msg) + { + std::cout << "p3GxsMails::handleResponse(...) " + << "GXS_MAIL_SUBTYPE_MAIL got recipientsHint: " + << msg->recipientsHint << " cryptoType: " + << (uint32_t)msg->cryptoType + << " payload size: " << msg->payload.size() + << std::endl; + } + break; + } + default: + std::cerr << "p3GxsMails::handleResponse(...) MAILS_UPDATE " + << "Unknown mail subtype : " + << it->PacketSubType() << std::endl; + break; + } + } + } + } default: - std::cout << "p3GxsMails::handleResponse(...) Unknown req_type: " << req_type << std::endl; + std::cerr << "p3GxsMails::handleResponse(...) Unknown req_type: " + << req_type << std::endl; break; } -/* - GxsMsgDataMap msgItems; - if(!RsGenExchange::getMsgData(token, msgItems)) - { - std::cerr << "p3GxsMails::handleResponse(...) Cannot get msg data. " - << "Something's weird." << std::endl; - } -*/ } void p3GxsMails::service_tick() { static int tc = 0; ++tc; - if((tc % 100) == 0) - { -// std::cout << "p3GxsMails::service_tick() " << tc << " " -// << preferredGroupId << std::endl; - requestGroupsList(); - } -#if 0 + if(((tc % 1000) == 0) || (tc == 50)) requestGroupsData(); + if(tc == 500) { - RsGxsId own_gxsid("d0df7474bdde0464679e6ef787890287"); - RsGxsId recipient("d060bea09dfa14883b5e6e517eb580cd"); - sendEmail(own_gxsid, recipient, "Ciao!"); + RsGxsId gxsidA("d0df7474bdde0464679e6ef787890287"); + RsGxsId gxsidB("d060bea09dfa14883b5e6e517eb580cd"); + if(idService.isOwnId(gxsidA)) + { + std::string ciao("CiAone!"); + sendMail( GxsMailsClient::MSG_SERVICE, gxsidA, gxsidB, + reinterpret_cast(ciao.data()), + ciao.size()); + } + else if(idService.isOwnId(gxsidB)) + { + std::string ciao("CiBone!"); + sendMail( GxsMailsClient::MSG_SERVICE, gxsidB, gxsidA, + reinterpret_cast(ciao.data()), + ciao.size()); + } } -#endif GxsTokenQueue::checkRequests(); } @@ -138,20 +302,19 @@ void p3GxsMails::notifyChanges(std::vector& changes) if (grpChange) { - typedef std::list::const_iterator itT; - for( itT it = grpChange->mGrpIdList.begin(); - it != grpChange->mGrpIdList.end(); ++it ) - { - const RsGxsGroupId& grpId = *it; - std::cout << "p3GxsMails::notifyChanges(...) got group " - << grpId << std::endl; - supersedePreferredGroup(grpId); - } + std::cout << "p3GxsMails::notifyChanges(...) grpChange" << std::endl; + requestGroupsData(&(grpChange->mGrpIdList)); } else if(msgChange) { - typedef std::map > mT; - for( mT::const_iterator it = msgChange->msgChangeMap.begin(); + std::cout << "p3GxsMails::notifyChanges(...) msgChange" << std::endl; + uint32_t token; + RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; + getTokenService()->requestMsgInfo( token, 0xcaca, + opts, msgChange->msgChangeMap ); + GxsTokenQueue::queueRequest(token, MAILS_UPDATE); + + for( GxsMsgReq::const_iterator it = msgChange->msgChangeMap.begin(); it != msgChange->msgChangeMap.end(); ++it ) { const RsGxsGroupId& grpId = it->first; @@ -161,20 +324,22 @@ void p3GxsMails::notifyChanges(std::vector& changes) { const RsGxsMessageId& msgId = *vit; std::cout << "p3GxsMails::notifyChanges(...) got " - << "new message " << msgId << " in group " - << grpId << std::endl; + << "notification for message " << msgId + << " in group " << grpId << std::endl; } } } } } -bool p3GxsMails::requestGroupsList() +bool p3GxsMails::requestGroupsData(const std::list* groupIds) { - // std::cout << "p3GxsMails::requestGroupsList() GXS_REQUEST_TYPE_GROUP_META" << std::endl; + // std::cout << "p3GxsMails::requestGroupsList()" << std::endl; uint32_t token; RsTokReqOptions opts; opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA; - RsGenExchange::getTokenService()->requestGroupInfo(token, 0xcaca, opts); + if(!groupIds) getTokenService()->requestGroupInfo(token, 0xcaca, opts); + else getTokenService()->requestGroupInfo(token, 0xcaca, opts, *groupIds); GxsTokenQueue::queueRequest(token, GROUPS_LIST); return true; } + diff --git a/libretroshare/src/services/p3gxsmails.h b/libretroshare/src/services/p3gxsmails.h index 0a87d1575..197f5f5b3 100644 --- a/libretroshare/src/services/p3gxsmails.h +++ b/libretroshare/src/services/p3gxsmails.h @@ -24,21 +24,81 @@ #include "serialiser/rsgxsmailitems.h" // For RS_SERVICE_TYPE_GXS_MAIL #include "services/p3idservice.h" // For p3IdService +struct p3GxsMails; -class p3GxsMails : public RsGenExchange, public GxsTokenQueue +struct GxsMailsClient +{ + /// Subservices identifiers (like port for TCP) + enum GxsMailSubServices { MSG_SERVICE }; + + /** + * This is usually used to save a pointer to the p3GxsMails service (e.g. by + * coping it in a member variable), so as to be able to send mails, and to + * register as a mail receiver via + * p3GxsMails::registerGxsMailsClient(GxsMailSubServices, GxsMailsClient) + */ + //virtual void connectToGxsMails(p3GxsMails *pt) = 0 ; + + /** + * This will be called by p3GxsMails to dispatch mails to the subservice + * @param destinationId + * @param signingKey + * @param data + * @param dataSize + * @return true if dispatching goes fine, false otherwise + */ + virtual bool receiveGxsMail( const RsGxsId& destinationId, + const RsGxsId& signingKey, + uint8_t* data, uint32_t dataSize ) = 0; +}; + +struct p3GxsMails : RsGenExchange, GxsTokenQueue { -public: p3GxsMails( RsGeneralDataService* gds, RsNetworkExchangeService* nes, - p3IdService *identities ) : + p3IdService& identities ) : RsGenExchange( gds, nes, new RsGxsMailSerializer(), - RS_SERVICE_TYPE_GXS_MAIL, identities, + RS_SERVICE_TYPE_GXS_MAIL, &identities, AuthenPolicy(), RS_GXS_DEFAULT_MSG_STORE_PERIOD ), // TODO: Discuss with Cyril about this - GxsTokenQueue(this) {} + GxsTokenQueue(this), idService(identities) {} - /// Public interface - bool sendEmail( const RsGxsId& own_gxsid, const RsGxsId& recipient, - const std::string& body ); + /** + * Send an email to recipient, in the process author of the email is + * disclosed to the network (because the sent GXS item is signed), while + * recipient is not @see RsGxsMailBaseItem::recipientsHint for details on + * recipient protection. + * This method is part of the public interface of this service. + * @return true if the mail will be sent, false if not + */ + bool sendMail( GxsMailsClient::GxsMailSubServices service, + const RsGxsId& own_gxsid, const RsGxsId& recipient, + const uint8_t* data, uint32_t size, + RsGxsMailBaseItem::EncryptionMode cm = RsGxsMailBaseItem::RSA + ); + + /** + * Send an email to recipients, in the process author of the email is + * disclosed to the network (because the sent GXS item is signed), while + * recipients are not @see RsGxsMailBaseItem::recipientsHint for details on + * recipient protection. + * This method is part of the public interface of this service. + * @return true if the mail will be sent, false if not + */ + bool sendMail( GxsMailsClient::GxsMailSubServices service, + const RsGxsId& own_gxsid, + const std::vector& recipients, + const uint8_t* data, uint32_t size, + RsGxsMailBaseItem::EncryptionMode cm = RsGxsMailBaseItem::RSA + ); + + /** + * Register a client service to p3GxsMails to receive mails via + * GxsMailsClient::receiveGxsMail(...) callback + * This is NOT thread safe! + */ + void registerGxsMailsClient( GxsMailsClient::GxsMailSubServices serviceType, + GxsMailsClient* service ) + { servClients[serviceType] = service; } /** * @see GxsTokenQueue::handleResponse(uint32_t token, uint32_t req_type) @@ -59,17 +119,26 @@ protected: void notifyChanges(std::vector &changes); private: + /// Time interval of inactivity before a distribution group is unsubscribed + const static int32_t UNUSED_GROUP_UNSUBSCRIBE_INTERVAL = 0x76A700; // 3 months approx /// @brief AuthenPolicy check nothing ATM TODO talk with Cyril how this should be static uint32_t AuthenPolicy() { return 0; } /// Types to mark GXS queries and answhers - enum GxsReqResTypes { GROUPS_LIST, GROUP_CREATE }; + enum GxsReqResTypes { GROUPS_LIST, GROUP_CREATE, MAILS_UPDATE }; + /// Store the id of the preferred GXS group to send emails RsGxsGroupId preferredGroupId; + /// Used for items {de,en}cryption + p3IdService& idService; + + /// Stores pointers to client services to notify them about new mails + std::map servClients; + /// Request groups list to GXS backend. Async method. - bool requestGroupsList(); + bool requestGroupsData(const std::list* groupIds = NULL); /** * Check if current preferredGroupId is the best against potentialGrId, if @@ -84,21 +153,16 @@ private: //std::cout << "supersedePreferredGroup(const RsGxsGroupId& potentialGrId) " << preferredGroupId << " & encrypt_ids, + bool force_load, uint32_t& error_status ) +{ + std::set keyNotYetFoundIds; + + for( std::set::const_iterator it = encrypt_ids.begin(); + it != encrypt_ids.end(); ++it ) + { + const RsGxsId& gId(*it); + if(gId.isNull()) + { + std::cerr << "p3IdService::encryptData(...) (EE) got null GXS id" + << std::endl; + return false; + } + else keyNotYetFoundIds.insert(&gId); + } + + if(keyNotYetFoundIds.empty()) + { + std::cerr << "p3IdService::encryptData(...) (EE) got empty GXS ids set" + << std::endl; + print_stacktrace(); + return false; + } + + std::vector encryption_keys; + int maxRounds = force_load ? 6 : 1; + for( int i=0; i < maxRounds; ++i ) + { + for( std::set::iterator it = keyNotYetFoundIds.begin(); + it !=keyNotYetFoundIds.end(); ++it ) + { + RsTlvPublicRSAKey encryption_key; + if(getKey(**it, encryption_key) && !encryption_key.keyId.isNull()) + { + encryption_keys.push_back(encryption_key); + keyNotYetFoundIds.erase(it); + } + } + + if(keyNotYetFoundIds.empty()) break; + else usleep(500*1000); + } + + if(!keyNotYetFoundIds.empty()) + { + std::cerr << "p3IdService::encryptData(...) (EE) Cannot get " + << "encryption key for: "; + for( std::set::iterator it = keyNotYetFoundIds.begin(); + it !=keyNotYetFoundIds.end(); ++it ) + std::cerr << **it << " "; + std::cerr << std::endl; + print_stacktrace(); + + error_status = RS_GIXS_ERROR_KEY_NOT_AVAILABLE; + return false; + } + + if(!GxsSecurity::encrypt( encrypted_data, encrypted_data_size, + decrypted_data, decrypted_data_size, + encryption_keys )) + { + std::cerr << "p3IdService::encryptData(...) (EE) Encryption failed." + << std::endl; + print_stacktrace(); + + error_status = RS_GIXS_ERROR_UNKNOWN; + return false ; + } + + for( std::set::const_iterator it = encrypt_ids.begin(); + it != encrypt_ids.end(); ++it ) + { + timeStampKey( *it, + RsIdentityUsage( + serviceType(), + RsIdentityUsage::IDENTITY_GENERIC_ENCRYPTION ) ); + } + + error_status = RS_GIXS_ERROR_NO_ERROR; + return true; +} + bool p3IdService::decryptData(const uint8_t *encrypted_data,uint32_t encrypted_data_size,uint8_t *& decrypted_data,uint32_t& decrypted_size,const RsGxsId& key_id,uint32_t& error_status) { RsTlvPrivateRSAKey encryption_key ; diff --git a/libretroshare/src/services/p3idservice.h b/libretroshare/src/services/p3idservice.h index addff0d1b..1f95fa241 100644 --- a/libretroshare/src/services/p3idservice.h +++ b/libretroshare/src/services/p3idservice.h @@ -287,11 +287,37 @@ public: virtual bool isOwnId(const RsGxsId& key_id) ; - virtual bool signData(const uint8_t *data,uint32_t data_size,const RsGxsId& signer_id,RsTlvKeySignature& signature,uint32_t& signing_error) ; - virtual bool validateData(const uint8_t *data, uint32_t data_size, const RsTlvKeySignature& signature, bool force_load, const RsIdentityUsage &info, uint32_t& signing_error) ; + virtual bool signData( const uint8_t* data, + uint32_t data_size, + const RsGxsId& signer_id, + RsTlvKeySignature& signature, + uint32_t& signing_error); - virtual bool encryptData(const uint8_t *decrypted_data,uint32_t decrypted_data_size,uint8_t *& encrypted_data,uint32_t& encrypted_data_size,const RsGxsId& encryption_key_id,bool force_load,uint32_t& encryption_error) ; - virtual bool decryptData(const uint8_t *encrypted_data,uint32_t encrypted_data_size,uint8_t *& decrypted_data,uint32_t& decrypted_data_size,const RsGxsId& encryption_key_id,uint32_t& encryption_error) ; + virtual bool validateData( const uint8_t *data, uint32_t data_size, + const RsTlvKeySignature& signature, + bool force_load, const RsIdentityUsage &info, + uint32_t& signing_error ); + + virtual bool encryptData( const uint8_t* decrypted_data, + uint32_t decrypted_data_size, + uint8_t*& encrypted_data, + uint32_t& encrypted_data_size, + const RsGxsId& encryption_key_id, + bool force_load, uint32_t& encryption_error); + + bool encryptData( const uint8_t* decrypted_data, + uint32_t decrypted_data_size, + uint8_t*& encrypted_data, + uint32_t& encrypted_data_size, + const std::set& encrypt_ids, + bool force_load, uint32_t& error_status ); + + virtual bool decryptData( const uint8_t* encrypted_data, + uint32_t encrypted_data_size, + uint8_t*& decrypted_data, + uint32_t& decrypted_data_size, + const RsGxsId& encryption_key_id, + uint32_t& encryption_error ); virtual bool haveKey(const RsGxsId &id); virtual bool havePrivateKey(const RsGxsId &id); @@ -299,7 +325,9 @@ public: virtual bool getKey(const RsGxsId &id, RsTlvPublicRSAKey &key); virtual bool getPrivateKey(const RsGxsId &id, RsTlvPrivateRSAKey &key); - virtual bool requestKey(const RsGxsId &id, const std::list &peers, const RsIdentityUsage &use_info); + virtual bool requestKey( const RsGxsId &id, + const std::list &peers, + const RsIdentityUsage &use_info ); virtual bool requestPrivateKey(const RsGxsId &id);