Commit 636df6c7 authored by johan's avatar johan

Add a peer device status unsafe

+improve set/get peer device status API
parent 20147330
......@@ -45,13 +45,16 @@ namespace lime {
/**
* 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
* The values explicitely mapped to specific integers(untrusted, trusted, unsafe) are stored in local storage as integer
* Do not modify the mapping or we will loose backward compatibility with existing databases
*/
enum class PeerDeviceStatus {
enum class PeerDeviceStatus : uint8_t {
untrusted=0, /**< 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=1, /**< this peer device already got its public identity key validated, that information shall be displayed to the end user too */
unsafe=2, /**< this status is a helper for the library user. It is used only by the peerDeviceStatus accessor functions */
fail, /**< this status is returned 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),
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*/
......@@ -238,23 +241,25 @@ namespace lime {
void get_selfIdentityKey(const std::string &localDeviceId, std::vector<uint8_t> &Ik);
/**
* @brief set the identity verified flag for peer device
* @brief set the peer device status flag in local storage: unsafe, trusted or untrusted.
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
* @param[in] Ik the EdDSA peer public identity key, formatted as in RFC8032
* @param[in] status value of flag to set
* @param[in] status value of flag to set: accepted values are trusted, untrusted, unsafe
*
* 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
* throw an exception if the status flag value is unexpected (not one of trusted, untrusted, unsafe)
*
* if peer Device is not present in local storage and status is trusted or unsafe, it is added, if status is untrusted, it is just ignored
*/
void set_peerIdentityVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status);
void set_peerDeviceStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, lime::PeerDeviceStatus status);
/**
* @brief get the status of a peer device: unknown, untrusted, trusted
* @brief get the status of a peer device: unknown, untrusted, trusted, unsafe
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
* @return unknown if the device is not in localStorage, untrusted, trusted or unsafe according to the stored value of peer device status flag otherwise
*/
lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId);
......
......@@ -159,7 +159,8 @@ Db::Db(std::string filename) : sql{sqlite3, filename}{
* - Did : primary key, used to make link with DR_sessions table.
* - DeviceId: peer device id (shall be its GRUU)
* - Ik : Peer device Identity public key, got it from X3DH server or X3DH init message
* - Verified : a flag, 0 : peer identity was not verified, 1 : peer identity confirmed
* - Status : a flag, 0 : untrusted, 1 : trusted, 2 : unsafe
* The mapping is done in lime.hpp by the PeerDeviceStatus enum class definition
*
* Note: peer device information is shared by all local device, hence they are not linked to particular local devices from lime_LocalUsers table
*/
......@@ -167,7 +168,7 @@ Db::Db(std::string filename) : sql{sqlite3, filename}{
Did INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \
DeviceId TEXT NOT NULL, \
Ik BLOB NOT NULL, \
Verified UNSIGNED INTEGER DEFAULT 0);";
Status UNSIGNED INTEGER DEFAULT 0);";
/*** X3DH tables ***/
/* Signed pre-key :
......@@ -281,16 +282,27 @@ void Db::get_allLocalDevices(std::vector<std::string> &deviceIds) {
}
/**
* @brief set the identity verified flag for peer device
* @brief set the peer device status flag in local storage: unsafe, trusted or untrusted.
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
* @param[in] Ik the EdDSA peer public identity key, formatted as in RFC8032
* @param[in] status value of flag to set
* @param[in] status value of flag to set: accepted values are trusted, untrusted, unsafe
*
* 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
* throw an exception if the status flag value is unexpected (not one of trusted, untrusted, unsafe)
*
* if peer Device is not present in local storage and status is trusted or unsafe, it is added, if status is untrusted, it is just ignored
*/
void Db::set_peerDeviceVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status) {
void Db::set_peerDeviceStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, lime::PeerDeviceStatus status) {
// Check the status flag value, accepted values are: trusted, untrusted, unsafe
if (status != lime::PeerDeviceStatus::unsafe
&& status != lime::PeerDeviceStatus::untrusted
&& status != lime::PeerDeviceStatus::trusted) {
throw BCTBX_EXCEPTION << "Trying to set a status for peer device "<<peerDeviceId<<" which is not acceptable (differs from unsafe, untrusted or trusted)";
}
uint8_t statusInteger = static_cast<uint8_t>(status);
// Do we have this peerDevice in lime_PeerDevices
blob Ik_blob(sql);
long long id;
......@@ -301,34 +313,41 @@ void Db::set_peerDeviceVerifiedStatus(const std::string &peerDeviceId, const std
storedIk.resize(IkSize);
Ik_blob.read(0, (char *)(storedIk.data()), IkSize); // Read the public key
if (storedIk == Ik) {
sql<<"UPDATE Lime_PeerDevices SET Verified = :Verified WHERE Did = :id;", use((status==true)?1:0), use(id);
sql<<"UPDATE Lime_PeerDevices SET Status = :Status WHERE Did = :id;", use(statusInteger), use(id);
} else { // Ik in local Storage differs than the one given... raise an exception
throw BCTBX_EXCEPTION << "Trying to insert an Identity key for peer device "<<peerDeviceId<<" which differs from one already in local storage";
}
} else { // peer is not in local Storage
if (status) { // insert it only if status is true, if false just ignore the request
if (status != lime::PeerDeviceStatus::untrusted) { // insert it only if status is trusted or unsafe just ignore the request
blob Ik_insert_blob(sql);
Ik_insert_blob.write(0, (char *)(Ik.data()), Ik.size());
sql<<"INSERT INTO Lime_PeerDevices(DeviceId, Ik, Verified) VALUES(:peerDeviceId, :Ik, 1);", use(peerDeviceId), use(Ik_insert_blob);
sql<<"INSERT INTO Lime_PeerDevices(DeviceId, Ik, Status) VALUES(:peerDeviceId, :Ik, :Status);", use(peerDeviceId), use(Ik_insert_blob), use(statusInteger);
}
}
}
/**
* @brief get the status of a peer device: unknown, untrusted, trusted
* @brief get the status of a peer device: unknown, untrusted, trusted, unsafe
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
* @return unknown if the device is not in localStorage, untrusted, trusted or unsafe according to the stored value of peer device status flag otherwise
*/
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);
int status;
sql<<"SELECT Status FROM Lime_PeerDevices WHERE DeviceId = :peerDeviceId LIMIT 1;", into(status), use(peerDeviceId);
if (sql.got_data()) { // Found it
if (verified == 1) { // and trusted
return lime::PeerDeviceStatus::trusted;
switch (status) {
case static_cast<uint8_t>(lime::PeerDeviceStatus::untrusted) :
return lime::PeerDeviceStatus::untrusted;
case static_cast<uint8_t>(lime::PeerDeviceStatus::trusted) :
return lime::PeerDeviceStatus::trusted;
case static_cast<uint8_t>(lime::PeerDeviceStatus::unsafe) :
return lime::PeerDeviceStatus::unsafe;
default:
throw BCTBX_EXCEPTION << "Trying to get the status for peer device "<<peerDeviceId<<" but get an unexpected value "<<status<<" from local storage";
}
return lime::PeerDeviceStatus::untrusted; // verified is stored as false
}
// peerDeviceId not found in local storage
......@@ -842,25 +861,32 @@ void Lime<Curve>::cache_DR_sessions(std::vector<RecipientInfos<Curve>> &internal
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 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<<");");
// Fill the peer device status
rowset<row> rs_devices = (m_localStorage->sql.prepare << "SELECT d.DeviceId, d.Status 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) {
auto deviceId = r.get<std::string>(0);
auto verified = r.get<int>(1);
auto status = 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;
switch (status) {
case static_cast<uint8_t>(lime::PeerDeviceStatus::trusted) :
recipient.peerStatus = lime::PeerDeviceStatus::trusted;
break;
case static_cast<uint8_t>(lime::PeerDeviceStatus::untrusted) :
recipient.peerStatus = lime::PeerDeviceStatus::untrusted;
break;
case static_cast<uint8_t>(lime::PeerDeviceStatus::unsafe) :
recipient.peerStatus = lime::PeerDeviceStatus::unsafe;
break;
default : // something is wrong with the local storage
throw BCTBX_EXCEPTION << "Trying to get the status for peer device "<<deviceId<<" but get an unexpected value "<<status<<" from local storage";
}
break;
}
}
}
......
......@@ -88,23 +88,25 @@ namespace lime {
void get_allLocalDevices(std::vector<std::string> &deviceIds);
/**
* @brief set the identity verified flag for peer device
* @brief set the peer device status flag in local storage: unsafe, trusted or untrusted.
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
* @param[in] Ik the EdDSA peer public identity key, formatted as in RFC8032
* @param[in] status value of flag to set
* @param[in] status value of flag to set: accepted values are trusted, untrusted, unsafe
*
* 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
* throw an exception if the status flag value is unexpected (not one of trusted, untrusted, unsafe)
*
* if peer Device is not present in local storage and status is trusted or unsafe, it is added, if status is untrusted, it is just ignored
*/
void set_peerDeviceVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status);
void set_peerDeviceStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, lime::PeerDeviceStatus status);
/**
* @brief get the status of a peer device: unknown, untrusted, trusted
* @brief get the status of a peer device: unknown, untrusted, trusted, unsafe
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*
* @return unknown if the device is not in localStorage, untrusted or trusted according to the stored value of identity verified flag otherwise
* @return unknown if the device is not in localStorage, untrusted, trusted or unsafe according to the stored value of peer device status flag otherwise
*/
lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId);
......
......@@ -170,11 +170,11 @@ namespace lime {
user->get_Ik(Ik);
}
void LimeManager::set_peerIdentityVerifiedStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, bool status) {
void LimeManager::set_peerDeviceStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, lime::PeerDeviceStatus status) {
// open local DB
auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access));
localStorage->set_peerDeviceVerifiedStatus(peerDeviceId, Ik, status);
localStorage->set_peerDeviceStatus(peerDeviceId, Ik, status);
}
lime::PeerDeviceStatus LimeManager::get_peerDeviceStatus(const std::string &peerDeviceId) {
......
......@@ -458,8 +458,8 @@ static void helloworld_verifyIdentity_test(const lime::CurveId curve, const std:
// This call can be performed before or after the beginning of a Lime conversation, if something is really bad happen, it will generate an exception.
// When calling it with true as trusted flag after a SAS validation confirms the peer identity key, if an exception is raised
// it MUST be reported to user as it means that all previously established Lime session with that device were actually compromised(or someone broke ZRTP)
aliceManager->set_peerIdentityVerifiedStatus(*bobDeviceId, bobIk, true);
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, true);
aliceManager->set_peerDeviceStatus(*bobDeviceId, bobIk, lime::PeerDeviceStatus::trusted);
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::trusted);
/*** alice encrypt a message to bob, all parameters given to encrypt function are shared_ptr. ***/
// The encryption generates:
......
......@@ -681,6 +681,9 @@ static void lime_encryptionPolicy() {
* - check if they are verified -> they shall not be
* - set alice key as verified in bob's context
* - check it is now verified
* - set it to unsafe and check
* - try to set it to unknown, we shall have and exception
* - try to set it to fail, we shall have and exception
* - set it as non verified and check
* - try to set a different alice identity key in bob's context, we shall have an exception
* - bob encrypts a message to alice -> check return status give NOT all recipients trusted
......@@ -738,11 +741,15 @@ static void lime_identityVerifiedStatus_test(const lime::CurveId curve, const st
BC_ASSERT_TRUE(aliceManager->get_peerDeviceStatus(*bobDeviceId) == lime::PeerDeviceStatus::unknown);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::unknown);
// set alice Id key as vertified in Bob's Manager and check it worked
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, true);
// set alice Id key as verified in Bob's Manager and check it worked
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::trusted);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::trusted);
// reset it and check it worked : now Bob knows alice but he doesn't trust her
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, false);
// set it to unsafe and check it worked
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::unsafe);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::unsafe);
// reset it to untrusted and check it worked : now Bob knows alice but he doesn't trust her
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::untrusted);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::untrusted);
} catch (BctbxException &e) {
......@@ -750,14 +757,36 @@ static void lime_identityVerifiedStatus_test(const lime::CurveId curve, const st
BC_FAIL();
}
// try to set another key for alice in bob's context, it shall generate an exception
auto gotException = false;
// set it to unknown, it shall generate an exception
try {
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::unknown);
} catch (BctbxException &e) {
BC_PASS();
gotException = true;
}
BC_ASSERT_TRUE(gotException);
gotException = false;
// set it to fail, it shall generate an exception
try {
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::fail);
} catch (BctbxException &e) {
BC_PASS();
gotException = true;
}
BC_ASSERT_TRUE(gotException);
gotException = false;
// try to set another key for alice in bob's context, it shall generate an exception
try {
// copy alice Ik but modify it
std::vector<uint8_t> fakeIk = aliceIk;
fakeIk[0] ^= 0xFF;
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, fakeIk, true);
bobManager->set_peerDeviceStatus(*aliceDeviceId, fakeIk, lime::PeerDeviceStatus::trusted);
} catch (BctbxException &e) {
BC_PASS();
gotException = true;
......@@ -777,7 +806,7 @@ static void lime_identityVerifiedStatus_test(const lime::CurveId curve, const st
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);
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::trusted);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::trusted);
// Bob encrypts a message for Alice, alice device status shall now be : trusted
......@@ -792,7 +821,7 @@ static void lime_identityVerifiedStatus_test(const lime::CurveId curve, const st
// set a fake bob key in alice context(set is as verified otherwise the request is just ignored)
std::vector<uint8_t> fakeIk = bobIk;
fakeIk[0] ^= 0xFF;
aliceManager->set_peerIdentityVerifiedStatus(*bobDeviceId, fakeIk, true);
aliceManager->set_peerDeviceStatus(*bobDeviceId, fakeIk, lime::PeerDeviceStatus::trusted);
// alice decrypt but it will fail as the identity key in X3DH init packet is not matching the one we assert as verified
std::vector<uint8_t> receivedMessage{};
......@@ -909,22 +938,22 @@ static void lime_peerDeviceStatus_test(const lime::CurveId curve, const std::str
daveManager->get_selfIdentityKey(*daveDeviceId, daveIk);
// exchange trust between alice and bob
bobManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, true);
aliceManager->set_peerIdentityVerifiedStatus(*bobDeviceId, bobIk, true);
bobManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::trusted);
aliceManager->set_peerDeviceStatus(*bobDeviceId, bobIk, lime::PeerDeviceStatus::trusted);
BC_ASSERT_TRUE(bobManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::trusted);
BC_ASSERT_TRUE(aliceManager->get_peerDeviceStatus(*bobDeviceId) == lime::PeerDeviceStatus::trusted);
// alice and carol gets trust and back to not trust so the Ik gets registered in their local storage
carolManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, true);
aliceManager->set_peerIdentityVerifiedStatus(*carolDeviceId, carolIk, true);
carolManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, false);
aliceManager->set_peerIdentityVerifiedStatus(*carolDeviceId, carolIk, false);
carolManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::trusted);
aliceManager->set_peerDeviceStatus(*carolDeviceId, carolIk, lime::PeerDeviceStatus::trusted);
carolManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::untrusted);
aliceManager->set_peerDeviceStatus(*carolDeviceId, carolIk, lime::PeerDeviceStatus::untrusted);
BC_ASSERT_TRUE(carolManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::untrusted);
BC_ASSERT_TRUE(aliceManager->get_peerDeviceStatus(*carolDeviceId) == lime::PeerDeviceStatus::untrusted);
// alice and dave gets just an untrusted setting, as they do not know each other, it shall not affect their respective local storage and they would remain unknown
daveManager->set_peerIdentityVerifiedStatus(*aliceDeviceId, aliceIk, false);
aliceManager->set_peerIdentityVerifiedStatus(*daveDeviceId, daveIk, false);
daveManager->set_peerDeviceStatus(*aliceDeviceId, aliceIk, lime::PeerDeviceStatus::untrusted);
aliceManager->set_peerDeviceStatus(*daveDeviceId, daveIk, lime::PeerDeviceStatus::untrusted);
BC_ASSERT_TRUE(daveManager->get_peerDeviceStatus(*aliceDeviceId) == lime::PeerDeviceStatus::unknown);
BC_ASSERT_TRUE(aliceManager->get_peerDeviceStatus(*daveDeviceId) == lime::PeerDeviceStatus::unknown);
......
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