Commit c9e8fae5 authored by johan's avatar johan
Browse files

Add an API to stale an active session

parent cfda50e8
......@@ -5,30 +5,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [4.5.0] - XXXX-XX-XX
## Changed
### Added
- Ability to force an active session to stale
### Changed
- use bctoolbox C++ API for RNG to improve error detections
## Fixed
### Fixed
- The Changelog!
## [4.4.0] - 2020-05-11
## Fixed
### Fixed
- minor bug fix in build scripts
- flexibility on server error code
## [4.3.0] - 2019-11-14
## Added
### Added
- Java API
- user republishing on X3DH server
- support multithreading
- improved peer device management
## Fixed
### Fixed
- Key's Id generation
## Removed
### Removed
- PHP test server
## Changed
### Changed
- X3DH register user in one command
......
......@@ -349,6 +349,17 @@ namespace lime {
*/
void delete_peerDevice(const std::string &peerDeviceId);
/**
* @brief Stale all sessions between localDeviceId and peerDevice.
* If peerDevice keep using this session to encrypt and we decrypt with success, the session will be reactivated
* but to encrypt a message to this peerDevice, a new session will be created.
* If no session is active between the given device, this call has no effect
*
* @param[in] localDeviceId Identify the local user account, it must be unique and is also be used as Id on the X3DH key server, it shall be the GRUU
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*/
void stale_sessions(const std::string &localDeviceId, const std::string &peerDeviceId);
/**
* @brief Set the X3DH key server URL for this identified user
*
......
......@@ -381,6 +381,17 @@ enum lime_ffi_PeerDeviceStatus lime_ffi_get_peerDeviceStatus(lime_manager_t mana
*/
int lime_ffi_delete_peerDevice(lime_manager_t manager, const char *peerDeviceId);
/**
* @brief Stale all sessions between localDeviceId and peerDevice.
* If peerDevice keep using this session to encrypt and we decrypt with success, the session will be reactivated
* but to encrypt a message to this peerDevice, a new session will be created.
* If no session is active between the given device, this call has no effect
*
* @param[in] manager pointer to the opaque structure used to interact with lime
* @param[in] localDeviceId Identify the local user account, it must be unique and is also be used as Id on the X3DH key server, it shall be the GRUU
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*/
int lime_ffi_stale_sessions(lime_manager_t manager, const char *localDeviceId, const char *peerDeviceId);
/**
* @brief Update: shall be called once a day at least, performs checks, updates and cleaning operations
*
......
......@@ -307,6 +307,18 @@ public class LimeManager {
*/
public native void set_x3dhServerUrl(String localDeviceId, String serverURL) throws LimeException;
/**
* @brief Stale all sessions between localDeviceId and peerDevice.
* If peerDevice keep using this session to encrypt and we decrypt with success, the session will be reactivated
* but to encrypt a message to this peerDevice, a new session will be created.
* If no session is active between the given device, this call has no effect
*
* @param[in] localDeviceId Identify the local user account, it must be unique and is also be used as Id on the X3DH key server, it shall be the GRUU
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*/
public native void stale_sessions(String localDeviceId, String peerDeviceId) throws LimeException;
/**
* @brief Get the X3DH key server URL for this identified user
* if specified localDeviceId is not found in local Storage, throw an exception
......
......@@ -327,6 +327,7 @@ namespace lime {
extern template void Lime<C255>::X3DH_get_OPk(uint32_t OPk_id, Xpair<C255> &SPk);
extern template void Lime<C255>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
extern template void Lime<C255>::set_x3dhServerUrl(const std::string &x3dhServerUrl);
extern template void Lime<C255>::stale_sessions(const std::string &peerDeviceId);
/* These extern templates are defined in lime_x3dh.cpp*/
extern template void Lime<C255>::X3DH_init_sender_session(const std::vector<X3DH_peerBundle<C255>> &peerBundle);
extern template std::shared_ptr<DR<C255>> Lime<C255>::X3DH_init_receiver_session(const std::vector<uint8_t> X3DH_initMessage, const std::string &peerDeviceId);
......@@ -352,6 +353,7 @@ namespace lime {
extern template void Lime<C448>::X3DH_get_OPk(uint32_t OPk_id, Xpair<C448> &SPk);
extern template void Lime<C448>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
extern template void Lime<C448>::set_x3dhServerUrl(const std::string &x3dhServerUrl);
extern template void Lime<C448>::stale_sessions(const std::string &peerDeviceId);
/* These extern templates are defined in lime_x3dh.cpp*/
extern template void Lime<C448>::X3DH_init_sender_session(const std::vector<X3DH_peerBundle<C448>> &peerBundle);
extern template std::shared_ptr<DR<C448>> Lime<C448>::X3DH_init_receiver_session(const std::vector<uint8_t> X3DH_initMessage, const std::string &peerDeviceId);
......
......@@ -415,6 +415,11 @@ int lime_ffi_delete_peerDevice(lime_manager_t manager, const char *peerDeviceId)
return LIME_FFI_SUCCESS;
}
int lime_ffi_stale_sessions(lime_manager_t manager, const char *localDeviceId, const char *peerDeviceId) {
manager->context->stale_sessions(std::string(localDeviceId), std::string(peerDeviceId));
return LIME_FFI_SUCCESS;
}
int lime_ffi_update(lime_manager_t manager, const lime_ffi_Callback callback, void *callbackUserData, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize) {
// just intercept the lime callback, convert the arguments to the correct types, add the userData and forward it to the C side
limeCallback cb([callback, callbackUserData](const lime::CallbackReturn status, const std::string message){
......
......@@ -113,6 +113,7 @@ namespace lime {
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;
void set_x3dhServerUrl(const std::string &x3dhServerUrl) override;
std::string get_x3dhServerUrl() override;
void stale_sessions(const std::string &peerDeviceId) override;
};
/**
......
......@@ -480,6 +480,17 @@ struct jLimeManager {
}
}
void stale_sessions(jni::JNIEnv &env, const jni::String &jlocalDeviceId, const jni::String &jpeerDeviceId) {
try {
m_manager->stale_sessions(jni::Make<std::string>(env, jlocalDeviceId), jni::Make<std::string>(env, jpeerDeviceId));
} catch (BctbxException const &e) {
ThrowJavaLimeException(env, e.str());
} catch (std::exception const &e) { // catch anything
ThrowJavaLimeException(env, e.what());
}
}
jni::Local<jni::String> get_x3dhServerUrl(jni::JNIEnv &env, const jni::String &jlocalDeviceId) {
std::string url{};
try {
......
......@@ -157,6 +157,16 @@ namespace lime {
*/
virtual std::string get_x3dhServerUrl() = 0;
/**
* @brief Stale all sessions between localDeviceId and peerDevice.
* If peerDevice keep using this session to encrypt and we decrypt with success, the session will be reactivated
* but to encrypt a message to this peerDevice, a new session will be created.
* If no session is active between the given device, this call has no effect
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
*/
virtual void stale_sessions(const std::string &peerDeviceId) = 0;
virtual ~LimeGeneric() {};
};
......
......@@ -1348,6 +1348,22 @@ void Lime<Curve>::set_x3dhServerUrl(const std::string &x3dhServerUrl) {
tr.commit();
}
template <typename Curve>
void Lime<Curve>::stale_sessions(const std::string &peerDeviceId) {
std::lock_guard<std::recursive_mutex> lock(*(m_localStorage->m_db_mutex));
transaction tr(m_localStorage->sql);
// update in DB, do not check presence as we're called after a load_user who already ensure that
try {
m_localStorage->sql<<"UPDATE DR_sessions SET Status = 0, timeStamp = CURRENT_TIMESTAMP WHERE Uid = :Uid AND Status = 1 AND Did = (SELECT Did FROM lime_PeerDevices WHERE DeviceId= :peerDeviceId LIMIT 1)", use(m_db_Uid), use(peerDeviceId);
} catch (exception const &e) {
tr.rollback();
throw BCTBX_EXCEPTION << "Cannot stale sessions between user "<<m_selfDeviceId<<" and user "<<peerDeviceId<<". DB backend says: "<<e.what();
}
tr.commit();
}
/* template instanciations for Curves 25519 and 448 */
#ifdef EC25519_ENABLED
template bool Lime<C255>::create_user();
......@@ -1362,6 +1378,7 @@ void Lime<Curve>::set_x3dhServerUrl(const std::string &x3dhServerUrl) {
template void Lime<C255>::X3DH_get_OPk(uint32_t OPk_id, Xpair<C255> &SPk);
template void Lime<C255>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
template void Lime<C255>::set_x3dhServerUrl(const std::string &x3dhServerUrl);
template void Lime<C255>::stale_sessions(const std::string &peerDeviceId);
#endif
#ifdef EC448_ENABLED
......@@ -1377,6 +1394,7 @@ void Lime<Curve>::set_x3dhServerUrl(const std::string &x3dhServerUrl) {
template void Lime<C448>::X3DH_get_OPk(uint32_t OPk_id, Xpair<C448> &SPk);
template void Lime<C448>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
template void Lime<C448>::set_x3dhServerUrl(const std::string &x3dhServerUrl);
template void Lime<C448>::stale_sessions(const std::string &peerDeviceId);
#endif
......
......@@ -241,6 +241,18 @@ namespace lime {
localStorage->delete_peerDevice(peerDeviceId);
}
void LimeManager::stale_sessions(const std::string &localDeviceId, const std::string &peerDeviceId) {
// load user (generate an exception if not found, let it flow up)
std::shared_ptr<LimeGeneric> user;
LimeManager::load_user(user, localDeviceId);
// Delete session from cache - if any
user->delete_peerDevice(peerDeviceId);
// stale session in DB
user->stale_sessions(peerDeviceId);
}
void LimeManager::set_x3dhServerUrl(const std::string &localDeviceId, const std::string &x3dhServerUrl) {
// load user (generate an exception if not found, let it flow up)
std::shared_ptr<LimeGeneric> user;
......
......@@ -3986,6 +3986,117 @@ static void lime_multithread(void) {
}
}
/*
* Scenario
* - Establish a session between Alice and Bob
* - Alice encrypts to Bob, check it does not holds a X3DH init message
* - Alice cancel session with bob
* - Alice encrypts to Bob, check it does holds a X3DH init message
* - Bob encrypts a message to Alice using the old session
* - Bob and Alice decrypt them all (including the one encrypted with now stale session)
*/
static void lime_session_cancel_test(const lime::CurveId curve, const std::string &dbBaseFilename, const std::string &x3dh_server_url, bool continuousSession=true) {
std::string dbFilenameAlice;
std::shared_ptr<std::string> aliceDeviceId;
std::unique_ptr<LimeManager> aliceManager;
std::string dbFilenameBob;
std::shared_ptr<std::string> bobDeviceId;
std::unique_ptr<LimeManager> bobManager;
lime_tester::events_counters_t counters={};
int expected_success=0;
limeCallback callback([&counters](lime::CallbackReturn returnCode, std::string anythingToSay) {
if (returnCode == lime::CallbackReturn::success) {
counters.operation_success++;
} else {
counters.operation_failed++;
LIME_LOGE<<"Lime operation failed : "<<anythingToSay;
}
});
try {
lime_session_establishment(curve, dbBaseFilename, x3dh_server_url,
dbFilenameAlice, aliceDeviceId, aliceManager,
dbFilenameBob, bobDeviceId, bobManager);
/* Alice encrypts to Bob */
auto aliceRecipients = make_shared<std::vector<RecipientData>>();
aliceRecipients->emplace_back(*bobDeviceId);
auto aliceMessage = make_shared<const std::vector<uint8_t>>(lime_tester::messages_pattern[0].begin(), lime_tester::messages_pattern[0].end());
auto aliceCipherMessage = make_shared<std::vector<uint8_t>>();
aliceManager->encrypt(*aliceDeviceId, make_shared<const std::string>("bob"), aliceRecipients, aliceMessage, aliceCipherMessage, callback);
BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
BC_ASSERT_FALSE(lime_tester::DR_message_holdsX3DHInit((*aliceRecipients)[0].DRmessage)); // no X3DH init as this session is fully establsihed
/* force reset so the active session is not in cache anymore */
if (!continuousSession) { managersClean (aliceManager, bobManager, dbFilenameAlice, dbFilenameBob);}
/* Alice stale session with Bob */
aliceManager->stale_sessions(*aliceDeviceId, *bobDeviceId);
/* Alice encrypts to Bob again */
auto aliceRecipients2 = make_shared<std::vector<RecipientData>>();
aliceRecipients2->emplace_back(*bobDeviceId);
aliceMessage = make_shared<const std::vector<uint8_t>>(lime_tester::messages_pattern[1].begin(), lime_tester::messages_pattern[1].end());
auto aliceCipherMessage2 = make_shared<std::vector<uint8_t>>();
aliceManager->encrypt(*aliceDeviceId, make_shared<const std::string>("bob"), aliceRecipients2, aliceMessage, aliceCipherMessage2, callback);
BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
BC_ASSERT_TRUE(lime_tester::DR_message_holdsX3DHInit((*aliceRecipients2)[0].DRmessage)); // X3DH init message in, it is a new session
/* Bob encrypts to Alice, he uses the old session staled by Alice */
auto bobRecipients = make_shared<std::vector<RecipientData>>();
bobRecipients->emplace_back(*aliceDeviceId);
auto bobMessage = make_shared<const std::vector<uint8_t>>(lime_tester::messages_pattern[2].begin(), lime_tester::messages_pattern[2].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(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
BC_ASSERT_FALSE(lime_tester::DR_message_holdsX3DHInit((*bobRecipients)[0].DRmessage)); // no X3DH init as this session is fully establsihed
/* Everyone decrypt */
std::vector<uint8_t> receivedMessage{};
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, (*aliceRecipients)[0].DRmessage, *aliceCipherMessage, receivedMessage) != lime::PeerDeviceStatus::fail);
auto receivedMessageString = std::string{receivedMessage.begin(), receivedMessage.end()};
BC_ASSERT_TRUE(receivedMessageString == lime_tester::messages_pattern[0]);
receivedMessage.clear();
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, (*aliceRecipients2)[0].DRmessage, *aliceCipherMessage2, receivedMessage) != lime::PeerDeviceStatus::fail);
receivedMessageString = std::string{receivedMessage.begin(), receivedMessage.end()};
BC_ASSERT_TRUE(receivedMessageString == lime_tester::messages_pattern[1]);
receivedMessage.clear();
BC_ASSERT_TRUE(aliceManager->decrypt(*aliceDeviceId, "alice", *bobDeviceId, (*bobRecipients)[0].DRmessage, *bobCipherMessage, receivedMessage) != lime::PeerDeviceStatus::fail);
receivedMessageString = std::string{receivedMessage.begin(), receivedMessage.end()};
BC_ASSERT_TRUE(receivedMessageString == lime_tester::messages_pattern[2]);
// cleaning
if (cleanDatabase) {
aliceManager->delete_user(*aliceDeviceId, callback);
bobManager->delete_user(*bobDeviceId, callback);
BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,expected_success+2,lime_tester::wait_for_timeout));
remove(dbFilenameAlice.data());
remove(dbFilenameBob.data());
}
} catch (BctbxException &e) {
LIME_LOGE << e;
BC_FAIL("");
}
}
static void lime_session_cancel(void) {
#ifdef EC25519_ENABLED
lime_session_cancel_test(lime::CurveId::c25519, "lime_session_cancel", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c25519_server_port).data());
lime_session_cancel_test(lime::CurveId::c25519, "lime_session_cancel", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c25519_server_port).data(), false);
#endif
#ifdef EC448_ENABLED
lime_session_cancel_test(lime::CurveId::c448, "lime_session_cancel", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c448_server_port).data());
lime_session_cancel_test(lime::CurveId::c448, "lime_session_cancel", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c448_server_port).data(), false);
#endif
}
static test_t tests[] = {
TEST_NO_TAG("Basic", x3dh_basic),
TEST_NO_TAG("User Management", user_management),
......@@ -4007,7 +4118,8 @@ static test_t tests[] = {
TEST_NO_TAG("Encryption Policy", lime_encryptionPolicy),
TEST_NO_TAG("Encryption Policy Error", lime_encryptionPolicyError),
TEST_NO_TAG("Identity theft", lime_identity_theft),
TEST_NO_TAG("Multithread", lime_multithread)
TEST_NO_TAG("Multithread", lime_multithread),
TEST_NO_TAG("Session cancel", lime_session_cancel),
};
test_suite_t lime_lime_test_suite = {
......
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