Commit ad8d089e authored by Johan Pascal's avatar Johan Pascal

Encrypt function gives more information on peer Device status

parent 8b04749d
......@@ -56,13 +56,13 @@ namespace lime {
/** Used to manage recipient list for encrypt function input: give a recipient GRUU and get it back with the header which must be sent to recipient with the cipher text*/
struct RecipientData {
const std::string deviceId; /**< input: recipient deviceId (shall be GRUU) */
bool identityVerified; /**< output: after encrypt calls back, it will hold the status of this peer device: identity verified or not */
lime::PeerDeviceStatus peerStatus; /**< output: after encrypt calls back, it will hold the status of this peer device: unknown (first interaction with this device), untrusted or trusted */
std::vector<uint8_t> DRmessage; /**< output: after encrypt calls back, it will hold the Double Ratchet message targeted to the specified recipient. */
/**
* recipient data are built giving a recipient id
* @param[in] deviceId the recipient device Id (its GRUU)
*/
RecipientData(const std::string &deviceId) : deviceId{deviceId}, identityVerified{false}, DRmessage{} {};
RecipientData(const std::string &deviceId) : deviceId{deviceId}, peerStatus{lime::PeerDeviceStatus::unknown}, DRmessage{} {};
};
/** what a Lime callback could possibly say */
......
......@@ -159,6 +159,7 @@ namespace lime {
std::vector<std::string> missingPeers; /* vector of deviceId(GRUU) which are requested to perform X3DH before the encryption can occurs */
/* Create the appropriate recipient infos and fill it with sessions found in cache */
// internal_recipients is a vector duplicating the recipients one in the same order(to allow fast copying of relevant information back to recipients when encryption is completed)
std::vector<RecipientInfos<Curve>> internal_recipients{};
for (const auto &recipient : *recipients) {
auto sessionElem = m_DR_sessions_cache.find(recipient.deviceId);
......@@ -174,7 +175,7 @@ namespace lime {
}
}
/* try to load all the session that are not in cache */
/* try to load all the session that are not in cache and set the peer Device status for all recipients*/
std::vector<std::string> missing_devices{};
cache_DR_sessions(internal_recipients, missing_devices);
......@@ -197,7 +198,7 @@ namespace lime {
// move DR messages to the input/output structure
for (size_t i=0; i<recipients->size(); i++) {
(*recipients)[i].DRmessage = std::move(internal_recipients[i].DRmessage);
(*recipients)[i].identityVerified = internal_recipients[i].identityVerified;
(*recipients)[i].peerStatus = internal_recipients[i].peerStatus;
}
if (callback) callback(lime::CallbackReturn::success, "");
// is there no one in an asynchronous encryption process and do we have something in encryption queue to process
......
......@@ -130,19 +130,21 @@ namespace lime {
struct RecipientInfos {
std::shared_ptr<DR<Curve>> DRSession; /**< DR Session to reach recipient */
const std::string deviceId; /**< recipient deviceId (shall be GRUU) */
bool identityVerified; /**< after encrypt calls back, it will hold the status of this peer device: identity verified or not */
lime::PeerDeviceStatus peerStatus; /**< after encrypt calls back, it will hold the status of this peer device: unknown (first interaction with this device), untrusted or trusted */
std::vector<uint8_t> DRmessage; /**< after encrypt calls back, it will hold the DR message targeted to the specified recipient. It may contain an X3DH init message. */
/**
* Constructor: the deviceId is a constant and must be provided to the constructor
* At construction, the peerStatus is always set to unknown as this status is then overriden with actual one fetched from DB, the ones not fetched are unknown
*
* @param[in] deviceId The device Id (GRUU) of this recipient
* @param[in] session The double ratchet session linking current device with this recipient.
*
*/
RecipientInfos(const std::string &deviceId, std::shared_ptr<DR<Curve>> session) : DRSession{session}, deviceId{deviceId}, identityVerified{false}, DRmessage{} {};
RecipientInfos(const std::string &deviceId, std::shared_ptr<DR<Curve>> session) : DRSession{session}, deviceId{deviceId}, peerStatus{lime::PeerDeviceStatus::unknown}, DRmessage{} {};
/**
* @overload RecipientInfos(const std::string &deviceId)
*/
RecipientInfos(const std::string &deviceId) : DRSession{nullptr}, deviceId{deviceId}, identityVerified{false}, DRmessage{} {};
RecipientInfos(const std::string &deviceId) : DRSession{nullptr}, deviceId{deviceId}, peerStatus{lime::PeerDeviceStatus::unknown}, DRmessage{} {};
};
// helpers function wich are the one to be used to encrypt/decrypt messages
......
......@@ -726,44 +726,48 @@ void Lime<Curve>::X3DH_generate_OPks(std::vector<X<Curve, lime::Xtype::publicKey
template <typename Curve>
void Lime<Curve>::cache_DR_sessions(std::vector<RecipientInfos<Curve>> &internal_recipients, std::vector<std::string> &missing_devices) {
// build a user list of missing ones : produce a list ready to be sent to SQL query: 'user','user','user',... also build a map to store shared_ptr to sessions
// build also a list of all peer devices used to fetch from DB the ones with identity not verified
// build also a list of all peer devices used to fetch from DB their status: unknown, untrusted or trusted
std::string sqlString_requestedDevices{""};
std::string sqlString_allDevices{""};
size_t requestedDevicesCount = 0;
size_t allDevicesCount = 0;
// internal recipients holds all recipients
for (const auto &recipient : internal_recipients) {
if (recipient.DRSession == nullptr) {
if (recipient.DRSession == nullptr) { // query the local storage for those without DR session associated
sqlString_requestedDevices.append("'").append(recipient.deviceId).append("',");
requestedDevicesCount++;
}
sqlString_allDevices.append("'").append(recipient.deviceId).append("',");
sqlString_allDevices.append("'").append(recipient.deviceId).append("',"); // we also build a query for all devices in the list
allDevicesCount++;
}
// fetch devices with identity verified flag to false
if (allDevicesCount==0) return; // the device list was empty... this is very strange
// Fill the peer device status : fetch devices with identity verified flag to false
sqlString_allDevices.pop_back(); // remove the last ','
// fetch all the verified devices (we don't directly fetch unverified device as some devices may not be in local storage at all)
rowset<row> rs_devices = (m_localStorage->sql.prepare << "SELECT d.DeviceId FROM lime_PeerDevices as d WHERE d.Verified = 1 AND d.DeviceId IN ("<<sqlString_allDevices<<");");
std::vector<std::string> verifiedDevices{}; // vector of verified deviceId
// fetch the verified status for all devices
rowset<row> rs_devices = (m_localStorage->sql.prepare << "SELECT d.DeviceId, d.Verified FROM lime_PeerDevices as d WHERE d.DeviceId IN ("<<sqlString_allDevices<<");");
std::vector<std::string> knownDevices{}; // vector of known deviceId
// loop all the found devices and then find them in the internal_recipient vector by looping it until find, this is not at all efficient but
// shall not be a real problem as recipient list won't get massive(and if they do, this part we not be the blocking one)
// by default at construction the RecipientInfos object have a peerStatus set to unknown so it will be kept to it for all devices not found in the localStorage
for (const auto &r : rs_devices) {
verifiedDevices.push_back(r.get<string>(0));
}
// loop on internal recipient and mark the one verified as verified
for (auto &recipient : internal_recipients) {
recipient.identityVerified = false;
for (const auto &verifiedDevice : verifiedDevices) {
if (verifiedDevice == recipient.deviceId) {
recipient.identityVerified = true;
// get the deviceId and verified fields
auto deviceId = r.get<string>(0);
auto verified = r.get<int>(1);
for (auto &recipient : internal_recipients) { //look for it in the list
if (recipient.deviceId == deviceId) {
if (verified == 1) {
recipient.peerStatus = lime::PeerDeviceStatus::trusted;
} else {
recipient.peerStatus = lime::PeerDeviceStatus::untrusted;
}
break;
}
}
}
// Now do we have sessions to load?
if (requestedDevicesCount==0) return; // we already got them all
......
......@@ -522,7 +522,7 @@ static void helloworld_verifyIdentity_test(const lime::CurveId curve, const std:
// In this example we know that bodDevice is in recipients[0], real code shall loop on recipients vector
sendMessageTo("bob", (*recipients)[0].DRmessage, *cipherMessage);
// [verify] now we can also check the trusted status of recipients, as we set as trusted Bob's key, it shall be trusted
BC_ASSERT_TRUE((*recipients)[0].identityVerified);
BC_ASSERT_TRUE((*recipients)[0].peerStatus == lime::PeerDeviceStatus::trusted);
} else {
counters.operation_failed++;
// The encryption failed.
......
......@@ -771,27 +771,27 @@ static void lime_identityVerifiedStatus_test(const lime::CurveId curve, const st
try {
// Bob encrypts a message for Alice, alice identity verified status shall be : not verified
// Bob encrypts a message for Alice, alice device status shall be : untrusted(it is the first message bob sends but he already knows about alice's device)
auto bobRecipients = make_shared<std::vector<RecipientData>>();
bobRecipients->emplace_back(*aliceDeviceId);
auto bobMessage = make_shared<const std::vector<uint8_t>>(lime_tester::messages_pattern[0].begin(), lime_tester::messages_pattern[0].end());
auto bobCipherMessage = make_shared<std::vector<uint8_t>>();
bobManager->encrypt(*bobDeviceId, make_shared<const std::string>("alice"), bobRecipients, bobMessage, bobCipherMessage, callback);
BC_ASSERT_TRUE(lime_tester::wait_for(stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
BC_ASSERT_FALSE((*bobRecipients)[0].identityVerified);
BC_ASSERT_TRUE((*bobRecipients)[0].peerStatus == lime::PeerDeviceStatus::untrusted);
// set again the key as verified in bob's context
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, true);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::trusted);
// Bob encrypts a message for Alice, alice identity verified status shall be : verified
// Bob encrypts a message for Alice, alice device status shall now be : trusted
bobRecipients = make_shared<std::vector<RecipientData>>();
bobRecipients->emplace_back(*aliceDeviceId);
bobMessage = make_shared<const std::vector<uint8_t>>(lime_tester::messages_pattern[1].begin(), lime_tester::messages_pattern[1].end());
bobCipherMessage = make_shared<std::vector<uint8_t>>();
bobManager->encrypt(*bobDeviceId, make_shared<const std::string>("alice"), bobRecipients, bobMessage, bobCipherMessage, callback);
BC_ASSERT_TRUE(lime_tester::wait_for(stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
BC_ASSERT_TRUE((*bobRecipients)[0].identityVerified);
BC_ASSERT_TRUE((*bobRecipients)[0].peerStatus == lime::PeerDeviceStatus::trusted);
// set a fake bob key in alice context(set is as verified otherwise the request is just ignored)
std::vector<uint8_t> fakeIk = bobIk;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment