Commit 8b04749d authored by Johan Pascal's avatar Johan Pascal

Decrypt function now returns a status on sender Device so you can:

- detect incoming message from unknown device to warn the user
- get the trust status of sender Device to display it to the user
parent 792fd144
......@@ -42,6 +42,17 @@ namespace lime {
optimizeGlobalBandwidth /**< optimize bandwith usage: encrypt in DR message if plaintext is short enougth to beat the overhead introduced by cipher message scheme, otherwise use cipher message. Selection is made on uploadand download(from server to recipients) sizes added. */
};
/**
* A peer device status returned after encrypt, decrypt or when directly asking for the peer device status to spot new devices and give information on our trust on this device
*/
enum class PeerDeviceStatus {
fail, /**< this status is return by decrypt operation only, when we could not decrypt the incoming message */
unknown, /**< when returned after encryption or decryption, means it is the first time we communicate with this device(and thus create a DR session with it),
when returned by a get_peerDeviceStatus: this device is not in localStorage */
untrusted, /**< we know this device but do not trust it, that information shall be displayed to the end user, a colour code shall be enough */
trusted /**< this peer device already got its public identity key validated, that information shall be displayed to the end user too */
};
/** 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) */
......@@ -183,14 +194,14 @@ namespace lime {
* @param[in] cipherMessage when present(depends on encryption policy) holds a common part of the encrypted message. Can be ignored or set to empty vector if not present in the incoming message.
* @param[out] plainMessage the output buffer
*
* @return true if the decryption is successfull, false otherwise
* @return fail if we cannot decrypt the message, newUntrusted when it is the first message we ever receive from the sender device, untrusted for known but untrusted sender device, or trusted if it is
*/
bool decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage);
lime::PeerDeviceStatus decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage);
/**
* @overload decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, std::vector<uint8_t> &plainMessage)
* convenience form to be called when no cipher message is received
*/
bool decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, std::vector<uint8_t> &plainMessage);
lime::PeerDeviceStatus decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, std::vector<uint8_t> &plainMessage);
/**
* @brief Update: shall be called once a day at least, performs checks, updates and cleaning operations
......@@ -238,13 +249,13 @@ namespace lime {
void set_peerIdentityVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status);
/**
* @brief get the identity verified flag for peer device
* @brief get the status of a peer device: unknown, untrusted, trusted
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return the stored Verified status, false if peer Device is not present in local Storage
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
*/
bool get_peerIdentityVerifiedStatus(const std::string &peerDeviceId);
lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId);
LimeManager() = delete; // no manager without Database and http provider
LimeManager(const LimeManager&) = delete; // no copy constructor
......
......@@ -210,7 +210,13 @@ namespace lime {
}
template <typename Curve>
bool Lime<Curve>::decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) {
lime::PeerDeviceStatus Lime<Curve>::decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) {
// before trying to decrypt, we must check if the sender device is known in the local Storage and if we trust it
// a successful decryption will insert it in local storage so we must check first if it is there in order to detect new devices
// Note: a device could already be trusted in DB even before the first message (if we established trust before sending the first message)
// senderDeviceStatus can only be unknown, untrusted or trusted, if decryption succeed, we will return this status.
auto senderDeviceStatus = m_localStorage->get_peerDeviceStatus(senderDeviceId);
LIME_LOGD<<"decrypt from "<<senderDeviceId<<" to "<<recipientUserId;
// do we have any session (loaded or not) matching that senderDeviceId ?
auto sessionElem = m_DR_sessions_cache.find(senderDeviceId);
......@@ -219,7 +225,8 @@ namespace lime {
db_sessionIdInCache = sessionElem->second->dbSessionId();
std::vector<std::shared_ptr<DR<Curve>>> DRSessions{1, sessionElem->second}; // copy the session pointer into a vector as the decryot function ask for it
if (decryptMessage<Curve>(senderDeviceId, m_selfDeviceId, recipientUserId, DRSessions, DRmessage, cipherMessage, plainMessage) != nullptr) {
return true; // we manage to decrypt the message with the current active session loaded in cache, nothing else to do
// we manage to decrypt the message with the current active session loaded in cache
return senderDeviceStatus;
} else { // remove session from cache
// session in local storage is not modified, so it's still the active one, it will change status to stale when an other active session will be created
m_DR_sessions_cache.erase(sessionElem);
......@@ -232,14 +239,14 @@ namespace lime {
get_DRSessions(senderDeviceId, db_sessionIdInCache, DRSessions);
auto usedDRSession = decryptMessage<Curve>(senderDeviceId, m_selfDeviceId, recipientUserId, DRSessions, DRmessage, cipherMessage, plainMessage);
if (usedDRSession != nullptr) { // we manage to decrypt with a session
m_DR_sessions_cache[senderDeviceId] = std::move(usedDRSession);
return true;
m_DR_sessions_cache[senderDeviceId] = std::move(usedDRSession); // store it in cache
return senderDeviceStatus;
}
// No luck yet, is this message holds a X3DH header - if no we must give up
std::vector<uint8_t> X3DH_initMessage{};
if (!double_ratchet_protocol::parseMessage_get_X3DHinit<Curve>(DRmessage, X3DH_initMessage)) {
return false;
return lime::PeerDeviceStatus::fail;
}
// parse the X3DH init message, get keys from localStorage, compute the shared secrets, create DR_Session and return a shared pointer to it
......@@ -249,15 +256,15 @@ namespace lime {
DRSessions.push_back(DRSession);
} catch (BctbxException &e) {
LIME_LOGE<<"Fail to create the DR session from the X3DH init message : "<<e;
return false;
return lime::PeerDeviceStatus::fail;
}
if (decryptMessage<Curve>(senderDeviceId, m_selfDeviceId, recipientUserId, DRSessions, DRmessage, cipherMessage, plainMessage) != 0) {
// we manage to decrypt the message with this session, set it in cache
m_DR_sessions_cache[senderDeviceId] = std::move(DRSessions.front());
return true;
return senderDeviceStatus;
}
return false;
return lime::PeerDeviceStatus::fail;
}
/* instantiate Lime for C255 and C448 */
......
......@@ -134,7 +134,7 @@ namespace lime {
virtual void get_Ik(std::vector<uint8_t> &Ik) override;
void encrypt(std::shared_ptr<const std::string> recipientUserId, std::shared_ptr<std::vector<RecipientData>> recipients, std::shared_ptr<const std::vector<uint8_t>> plainMessage, const lime::EncryptionPolicy encryptionPolicy, std::shared_ptr<std::vector<uint8_t>> cipherMessage, const limeCallback &callback) override;
bool decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) override;
lime::PeerDeviceStatus decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) override;
};
/**
......
......@@ -76,7 +76,7 @@ namespace lime {
*
* @return true if the decryption is successfull, false otherwise
*/
virtual bool decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) = 0;
virtual lime::PeerDeviceStatus decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) = 0;
......
......@@ -290,7 +290,7 @@ void Db::get_allLocalDevices(std::vector<std::string> &deviceIds) {
* throw an exception if given key doesn't match the one present in local storage
* if peer Device is not present in local storage and status is true, it is added, if status is false, it is just ignored
*/
void Db::set_PeerDevicesVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status) {
void Db::set_peerDeviceVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status) {
// Do we have this peerDevice in lime_PeerDevices
blob Ik_blob(sql);
long long id;
......@@ -315,24 +315,24 @@ void Db::set_PeerDevicesVerifiedStatus(const std::string &peerDeviceId, const st
}
/**
* @brief get the identity verified flag for peer device
* @brief get the status of a peer device: unknown, untrusted, trusted
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return the stored Verified status, false if peer Device is not present in local Storage
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
*/
bool Db::get_PeerDevicesIdentityVerifiedStatus(const std::string &peerDeviceId) {
lime::PeerDeviceStatus Db::get_peerDeviceStatus(const std::string &peerDeviceId) {
int verified;
sql<<"SELECT Verified FROM Lime_PeerDevices WHERE DeviceId = :peerDeviceId LIMIT 1;", into(verified), use(peerDeviceId);
if (sql.got_data()) { // Found it
if (verified == 1) {
return true;
if (verified == 1) { // and trusted
return lime::PeerDeviceStatus::trusted;
}
return false; // verified is stored as false
return lime::PeerDeviceStatus::untrusted; // verified is stored as false
}
// peerDeviceId not found in local storage: return false
return false;
// peerDeviceId not found in local storage
return lime::PeerDeviceStatus::unknown;
}
/**
......
......@@ -96,16 +96,16 @@ namespace lime {
* throw an exception if given key doesn't match the one present in local storage
* if peer Device is not present in local storage and status is true, it is added, if status is false, it is just ignored
*/
void set_PeerDevicesVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status);
void set_peerDeviceVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status);
/**
* @brief get the identity verified flag for peer device
* @brief get the status of a peer device: unknown, untrusted, trusted
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return the stored Verified status, false if peer Device is not present in local Storage
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
*/
bool get_PeerDevicesIdentityVerifiedStatus(const std::string &peerDeviceId);
lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId);
};
......
/*
lime_manager.cpp
@author Johan Pascal
......@@ -90,7 +91,7 @@ namespace lime {
user->encrypt(recipientUserId, recipients, plainMessage, encryptionPolicy, cipherMessage, callback);
}
bool LimeManager::decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) {
lime::PeerDeviceStatus LimeManager::decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) {
// Load user object
std::shared_ptr<LimeGeneric> user;
LimeManager::load_user(user, localDeviceId);
......@@ -101,7 +102,7 @@ namespace lime {
// convenience definition, have a decrypt without cipherMessage input for the case we don't have it(DR message encryption policy)
// just use an create an empty cipherMessage to be able to call Lime::decrypt which needs the cipherMessage even if empty for code simplicity
bool LimeManager::decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, std::vector<uint8_t> &plainMessage) {
lime::PeerDeviceStatus LimeManager::decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &DRmessage, std::vector<uint8_t> &plainMessage) {
// Load user object
std::shared_ptr<LimeGeneric> user;
LimeManager::load_user(user, localDeviceId);
......@@ -173,14 +174,14 @@ namespace lime {
// open local DB
auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access));
localStorage->set_PeerDevicesVerifiedStatus(peerDeviceId, Ik, status);
localStorage->set_peerDeviceVerifiedStatus(peerDeviceId, Ik, status);
}
bool LimeManager::get_peerIdentityVerifiedStatus(const std::string &peerDeviceId) {
lime::PeerDeviceStatus LimeManager::get_peerDeviceStatus(const std::string &peerDeviceId) {
// open local DB
auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access));
return localStorage->get_PeerDevicesIdentityVerifiedStatus(peerDeviceId);
return localStorage->get_peerDeviceStatus(peerDeviceId);
}
} // namespace lime
......@@ -238,7 +238,7 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
/*** alice encrypt a message to bob, all parameters given to encrypt function are shared_ptr. ***/
// The encryption generates:
// - one common cipher message which must be sent to all recipient devices
// - one common cipher message which must be sent to all recipient devices(depends on encryption policy, message length and recipient number, it may be actually empty)
// - a cipher header per recipient device, each recipient device shall receive its specific one
// Create an empty RecipientData vector, in this basic case we will encrypt to one device only but we can do it to any number of recipient devices.
......@@ -321,7 +321,9 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
if (bobReceivedDRmessage.size()>0 && bobReceivedCipherMessage.size()>0) {
std::vector<uint8_t> plainTextMessage{}; // a data vector to store the decrypted message
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedDRmessage, bobReceivedCipherMessage, plainTextMessage));
// it is the first time bob's Device is in communication with Alice's one, so the decrypt will return PeerDeviceStatus::unknown
// successive messages from Alice shall get a PeerDeviceStatus::untrusted as we did not take care of peer identity validation
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedDRmessage, bobReceivedCipherMessage, plainTextMessage) == lime::PeerDeviceStatus::unknown);
// it's a text message, so turn it into a string and compare with the one alice sent
std::string plainTextMessageString{plainTextMessage.begin(), plainTextMessage.end()};
......@@ -465,9 +467,14 @@ static void helloworld_verifyIdentity_test(const lime::CurveId curve, const std:
/*** alice encrypt a message to bob, all parameters given to encrypt function are shared_ptr. ***/
// The encryption generates:
// - one common cipher message which must be sent to all recipient devices
// - one common cipher message which must be sent to all recipient devices(depends on encryption policy, message length and recipient number, it may be actually empty)
// - a cipher header per recipient device, each recipient device shall receive its specific one
// [verify] before encryption we can verify that recipient identity is a trusted peer(and eventually decide to not perform the encryption if it is not)
// This information will be provided by the encrypt function anyway for each recipient device
// Here Bob's device is trusted as we just set its identity as verified
BC_ASSERT_TRUE(aliceManager->get_peerDeviceStatus(*bobDeviceId) == lime::PeerDeviceStatus::trusted);
// Create an empty RecipientData vector, in this basic case we will encrypt to one device only but we can do it to any number of recipient devices.
// RecipientData holds:
// - recipient device id (identify the recipient)
......@@ -548,11 +555,14 @@ static void helloworld_verifyIdentity_test(const lime::CurveId curve, const std:
if (bobReceivedDRmessage.size()>0 && bobReceivedCipherMessage.size()>0) {
// [verify] before decryption we can verify that sender is a trusted peer
BC_ASSERT_TRUE(bobManager->get_peerIdentityVerifiedStatus(*aliceDeviceId));
// [verify] before decryption we can verify that sender is a trusted peer,
// it is not really needed as this information will be provided by the decrypt function anyway
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::trusted);
std::vector<uint8_t> plainTextMessage{}; // a data vector to store the decrypted message
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedDRmessage, bobReceivedCipherMessage, plainTextMessage));
// [verify] it is the first time bob's Device is in communication with Alice's one via message
// but they already exchanged their identity keys so they Bob's device trust Alice's one since the first incoming message
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedDRmessage, bobReceivedCipherMessage, plainTextMessage) == lime::PeerDeviceStatus::trusted);
// it's a text message, so turn it into a string and compare with the one alice sent
std::string plainTextMessageString{plainTextMessage.begin(), plainTextMessage.end()};
......
This diff is collapsed.
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