Commit 1861fa9b authored by johan's avatar johan

Lime client use callback to provide users credentials

+ add md5 digest request on test server
all request processed by a single user
parent 656745b4
......@@ -41,6 +41,8 @@ namespace lime {
// a callback function must return a code and may return a string(could actually be empty) to detail what's happening
// callback is used on every operation possibly involving a connection to X3DH server: create_user, delete_user, encrypt
using limeCallback = std::function<void(lime::callbackReturn, std::string)>;
// the user authenticate callback will pass a sip_auth_event to be completed with user's password
using userAuthenticateCallback = std::function<void(belle_sip_auth_event_t *)>;
/* Forward declare the class managing one lime user*/
......@@ -52,6 +54,8 @@ namespace lime {
std::unordered_map<std::string, std::shared_ptr<LimeGeneric>> m_users_cache; // cache of already opened Lime Session, identified by user Id (GRUU)
std::string m_db_access; // DB access information forwarded to SOCI to correctly access database
belle_http_provider_t *m_http_provider; // used to access the X3DH key server
userAuthenticateCallback m_user_auth; // called to complete user authentication on server
public :
/* LimeManager is mostly a cache of Lime users, any command get as first parameter the device Id (Lime manage devices only, the link user(sip:uri)<->device(GRUU) is provided by upper level) */
......@@ -130,11 +134,12 @@ namespace lime {
/**
* @brief Lime Manager constructor
*
* @param[in] db_access string used to access DB: can be filename for sqlite3 or access params for mysql, directly forwarded to SOCI session opening
* @param[in] http_provider An http provider used to access X3DH server, no scheduling is done on it internally
* @param[in] db_access string used to access DB: can be filename for sqlite3 or access params for mysql, directly forwarded to SOCI session opening
* @param[in] http_provider An http provider used to access X3DH server, no scheduling is done on it internally
* @param[in] user_authentication_callback To complete user authentication on server: must provide user credentials
*/
LimeManager(const std::string &db_access, belle_http_provider_t *http_provider)
: m_users_cache{}, m_db_access{db_access}, m_http_provider{http_provider} {};
LimeManager(const std::string &db_access, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback)
: m_users_cache{}, m_db_access{db_access}, m_http_provider{http_provider}, m_user_auth{user_authentication_callback} {};
~LimeManager() = default;
};
......
......@@ -50,17 +50,19 @@ namespace lime {
* before calling this constructor, user existence in DB is checked and its Uid retrieved
* just load it into Lime class
*
* @param[in/out] localStorage pointer to DB accessor
* @param[in] deviceId device Id(shall be GRUU), stored in the structure
* @param[in] Uid the DB internal Id for this user, speed up DB operations by holding it in DB
* @param[in] url URL of the X3DH key server used to publish our keys(retrieved from DB)
* @param[in/out] localStorage pointer to DB accessor
* @param[in] deviceId device Id(shall be GRUU), stored in the structure
* @param[in] url URL of the X3DH key server used to publish our keys(retrieved from DB)
* @param[in] http_provider An HTTP stack
* @param[in] user_authentication_callback called to get users credentials to log on X3DH key server
* @param[in] Uid the DB internal Id for this user, speed up DB operations by holding it in DB
*/
template <typename Curve>
Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const long int Uid)
Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback, const long int Uid)
: m_RNG{bctbx_rng_context_new()}, m_selfDeviceId{deviceId},
m_Ik{}, m_Ik_loaded(false),
m_localStorage(std::move(localStorage)), m_db_Uid{Uid},
m_http_provider{http_provider}, m_X3DH_Server_URL{url},
m_http_provider{http_provider}, m_user_auth{user_authentication_callback}, m_X3DH_Server_URL{url},
m_DR_sessions_cache{}, m_ongoing_encryption{nullptr}, m_encryption_queue{}
{ }
......@@ -69,16 +71,18 @@ namespace lime {
* @brief Create user constructor
* Create a user in DB, if already existing, throw exception
*
* @param[in/out] localStorage pointer to DB accessor
* @param[in] deviceId device Id(shall be GRUU), stored in the structure
* @param[in] url URL of the X3DH key server used to publish our keys
* @param[in/out] localStorage pointer to DB accessor
* @param[in] deviceId device Id(shall be GRUU), stored in the structure
* @param[in] url URL of the X3DH key server used to publish our keys
* @param[in] http_provider An HTTP stack
* @param[in] user_authentication_callback called to get users credentials to log on X3DH key server
*/
template <typename Curve>
Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider)
Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback)
: m_RNG{bctbx_rng_context_new()}, m_selfDeviceId{deviceId},
m_Ik{}, m_Ik_loaded(false),
m_localStorage(std::move(localStorage)), m_db_Uid{0},
m_http_provider{http_provider}, m_X3DH_Server_URL{url},
m_http_provider{http_provider}, m_user_auth{user_authentication_callback}, m_X3DH_Server_URL{url},
m_DR_sessions_cache{}, m_ongoing_encryption{nullptr}, m_encryption_queue{}
{
try {
......@@ -247,18 +251,20 @@ namespace lime {
/****************************************************************************/
/**
* @brief : Insert user in database and return a pointer to the control class instanciating the appropriate Lime children class
m* Once created a user cannot be modified, insertion of existing deviceId will raise an exception.
* Once created a user cannot be modified, insertion of existing deviceId will raise an exception.
*
* @param[in] dbFilename Path to filename to use
* @param[in] deviceId User to create in DB, deviceId shall be the GRUU
* @param[in] url URL of X3DH key server to be used to publish our keys
* @param[in] curve Which curve shall we use for this account, select the implemenation to instanciate when using this user
* @param[in] http_provider An http provider used to communicate with x3dh key server
* @param[in] dbFilename Path to filename to use
* @param[in] deviceId User to create in DB, deviceId shall be the GRUU
* @param[in] url URL of X3DH key server to be used to publish our keys
* @param[in] curve Which curve shall we use for this account, select the implemenation to instanciate when using this user
* @param[in] http_provider An http provider used to communicate with x3dh key server
* @param[in] user_authentication_callback To complete user authentication on server: must provide user credentials
* @param[in] callback To provide caller the operation result
*
* @return a pointer to the LimeGeneric class allowing access to API declared in lime.hpp
*/
std::shared_ptr<LimeGeneric> insert_LimeUser(const std::string &dbFilename, const std::string &deviceId, const std::string &url, const lime::CurveId curve, belle_http_provider *http_provider,
const limeCallback &callback) {
const userAuthenticateCallback &user_authentication_callback, const limeCallback &callback) {
BCTBX_SLOGI<<"Create Lime user "<<deviceId;
/* first check the requested curve is instanciable and return an exception if not */
#ifndef EC25519_ENABLED
......@@ -282,7 +288,7 @@ namespace lime {
#ifdef EC25519_ENABLED
{
/* constructor will insert user in Db, if already present, raise an exception*/
auto lime_ptr = std::make_shared<Lime<C255>>(std::move(localStorage), deviceId, url, http_provider);
auto lime_ptr = std::make_shared<Lime<C255>>(std::move(localStorage), deviceId, url, http_provider, user_authentication_callback);
lime_ptr->publish_user(callback);
return lime_ptr;
}
......@@ -292,7 +298,7 @@ namespace lime {
case lime::CurveId::c448 :
#ifdef EC448_ENABLED
{
auto lime_ptr = std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, url, http_provider);
auto lime_ptr = std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, url, http_provider, user_authentication_callback);
lime_ptr->publish_user(callback);
return lime_ptr;
}
......@@ -314,13 +320,14 @@ namespace lime {
* @brief : Load user from database and return a pointer to the control class instanciating the appropriate Lime children class
* Fail to find the user will raise an exception
*
* @param[in] dbFilename Path to filename to use
* @param[in] deviceId User to lookup in DB, deviceId shall be the GRUU
* @param[in] http_provider An http provider used to communicate with x3dh key server
* @param[in] dbFilename Path to filename to use
* @param[in] deviceId User to lookup in DB, deviceId shall be the GRUU
* @param[in] http_provider An http provider used to communicate with x3dh key server
* @param[in] user_authentication_callback To complete user authentication on server: must provide user credentials
*
* @return a pointer to the LimeGeneric class allowing access to API declared in lime.hpp
*/
std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, belle_http_provider *http_provider) {
std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, belle_http_provider *http_provider, const userAuthenticateCallback &user_authentication_callback) {
/* open DB and load user */
auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(dbFilename)); // create as unique ptr, ownership is then passed to the Lime structure when instanciated
......@@ -348,14 +355,14 @@ namespace lime {
switch (curve) {
case lime::CurveId::c25519 :
#ifdef EC25519_ENABLED
return std::make_shared<Lime<C255>>(std::move(localStorage), deviceId, x3dh_server_url, http_provider, Uid);
return std::make_shared<Lime<C255>>(std::move(localStorage), deviceId, x3dh_server_url, http_provider, user_authentication_callback, Uid);
#endif
break;
case lime::CurveId::c448 :
#ifdef EC448_ENABLED
return std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, x3dh_server_url, http_provider, Uid);
return std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, x3dh_server_url, http_provider, user_authentication_callback, Uid);
#endif
break;
......
......@@ -56,6 +56,7 @@ namespace lime {
/* network related */
belle_http_provider_t *m_http_provider; // externally provided http stack used to communicate with x3dh server
userAuthenticateCallback m_user_auth; // called to complete user authentication on server
std::string m_X3DH_Server_URL; // url of x3dh key server
/* Double ratchet related */
......@@ -86,7 +87,9 @@ namespace lime {
/* network related */
void postToX3DHServer(callbackUserData<Curve> *userData, const std::vector<uint8_t> &message); // send a request to X3DH server
static void process_response(void *data, const belle_http_response_event_t *event) noexcept; // callback on server response (hence the static)
static void process_response(void *data, const belle_http_response_event_t *event) noexcept; // callback on server response (must be static)
static void process_io_error(void *data, const belle_sip_io_error_event_t *event) noexcept; // callback on server i/o error (must be static)
static void process_auth_requested(void *data, belle_sip_auth_event_t *event) noexcept; // callback on server requesting user credentials (must be static)
void cleanUserData(callbackUserData<Curve> *userData); // clean user data
public: /* Implement API defined in lime.hpp in factory abstract class */
......@@ -96,8 +99,8 @@ namespace lime {
* - one to load the user from db based on provided user Id(which shall be GRUU)
* Note: ownership of localStorage pointer is transfered to a shared pointer, private menber of Lime class
*/
Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider);
Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const long Uid);
Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback);
Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback, const long Uid);
~Lime();
Lime(Lime<Curve> &a) = delete; // can't copy a session, force usage of shared pointers
Lime<Curve> &operator=(Lime<Curve> &a) = delete; // can't copy a session
......
......@@ -42,27 +42,33 @@ namespace lime {
/* Lime Factory functions : return a pointer to the implementation using the specified elliptic curve. Two functions: one for creation, one for loading from local storage */
/**
* @brief Create a local lime user and insert all its needed data in a DB, it will trigger identity key creation and communication of it, SPKs and OPKs to key server
* @brief : Insert user in database and return a pointer to the control class instanciating the appropriate Lime children class
* Once created a user cannot be modified, insertion of existing deviceId will raise an exception.
*
* @param[in] dbFilename path to the database to be used
* @param[in] deviceId a unique identifier to a local user, if not already present in base it will be inserted. Recommended value: device's GRUU
* @param[in] keyServer URL of X3DH key server(WARNING : matching between elliptic curve usage of all clients on the same server is responsability of clients)
* @param[in] curve select which Elliptic curve to base X3DH and Double ratchet on: Curve25519 or Curve448,
* this is set once at user creation and can't be modified, it must reflect key server preference.
* @return a unique pointer to the object to be used by this user for any Lime operations
* @param[in] dbFilename Path to filename to use
* @param[in] deviceId User to create in DB, deviceId shall be the GRUU
* @param[in] url URL of X3DH key server to be used to publish our keys
* @param[in] curve Which curve shall we use for this account, select the implemenation to instanciate when using this user
* @param[in] http_provider An http provider used to communicate with x3dh key server
* @param[in] user_authentication_callback To complete user authentication on server: must provide user credentials
* @param[in] callback To provide caller the operation result
*
* @return a pointer to the LimeGeneric class allowing access to API declared in lime.hpp
*/
std::shared_ptr<LimeGeneric> insert_LimeUser(const std::string &dbFilename, const std::string &deviceId, const std::string &url, const lime::CurveId curve,
belle_http_provider_t *http_provider, const limeCallback &callback);
std::shared_ptr<LimeGeneric> insert_LimeUser(const std::string &dbFilename, const std::string &deviceId, const std::string &url, const lime::CurveId curve, belle_http_provider *http_provider,
const userAuthenticateCallback &user_authentication_callback, const limeCallback &callback);
/**
* @brief Load a local user from database
*
* @param[in] dbFilename path to the database to be used
* @param[in] deviceId a unique identifier to a local user, if not already present in base it will be inserted. Recommended value: device's GRUU
* @param[in] dbFilename path to the database to be used
* @param[in] deviceId a unique identifier to a local user, if not already present in base it will be inserted. Recommended value: device's GRUU
* @param[in] http_provider An http provider used to access X3DH server, no scheduling is done on it internally
* @param[in] user_authentication_callback To complete user authentication on server: must provide user credentials
*
* @return a unique pointer to the object to be used by this user for any Lime operations
*/
std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, belle_http_provider_t *http_provider);
std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, belle_http_provider_t *http_provider, const userAuthenticateCallback &user_authentication_callback);
}
#endif // lime_lime_hpp
......@@ -24,6 +24,7 @@
#include "lime/lime.hpp"
#include "lime_lime.hpp"
#include "lime_localStorage.hpp"
using namespace::std;
......@@ -39,14 +40,15 @@ namespace lime {
// first forward the callback
callback(returnCode, errorMessage);
// then check if it went well, if not remove the user from cache(it will trigger destruction of the lime generic object so do it last
// as it will also destroy the instance of this callback)
// then check if it went well, if not delete the user from localDB
if (returnCode != lime::callbackReturn::success) {
auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(thiz->m_db_access));
localStorage->delete_LimeUser(localDeviceId);
thiz->m_users_cache.erase(localDeviceId);
}
});
m_users_cache.insert({localDeviceId, insert_LimeUser(m_db_access, localDeviceId, x3dhServerUrl, curve, m_http_provider, managerCreateCallback)});
m_users_cache.insert({localDeviceId, insert_LimeUser(m_db_access, localDeviceId, x3dhServerUrl, curve, m_http_provider, m_user_auth, managerCreateCallback)});
}
void LimeManager::delete_user(const std::string &localDeviceId, const limeCallback &callback) {
......@@ -64,7 +66,7 @@ namespace lime {
auto userElem = m_users_cache.find(localDeviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) {
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider);
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user; // we must load it in cache otherwise object will be destroyed before getting into callback
} else {
user = userElem->second;
......@@ -78,7 +80,7 @@ namespace lime {
auto userElem = m_users_cache.find(localDeviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) { // not in cache, load it from DB
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider);
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user;
} else {
user = userElem->second;
......@@ -93,7 +95,7 @@ namespace lime {
auto userElem = m_users_cache.find(localDeviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) { // not in cache, load it from DB
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider);
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user;
} else {
user = userElem->second;
......
......@@ -414,6 +414,16 @@ namespace lime {
void Lime<Curve>::process_response(void *data, const belle_http_response_event_t *event) noexcept {
if (event->response){
auto code=belle_http_response_get_status_code(event->response);
callbackUserData<Curve> *userData = static_cast<callbackUserData<Curve> *>(data);
auto thiz = userData->limeObj.lock(); // get a shared pointer to Lime Object from the weak pointer stored in userData
// check it is valid (lock() returns nullptr)
if (!thiz) { // our Lime caller object doesn't exists anymore
BCTBX_SLOGE<<"Got response from X3DH server but our Lime Object has been destroyed";
delete(userData);
return;
}
auto callback = userData->callback; // get callback
if (code == 200) { // HTTP server is happy with our packet
// check response from X3DH server: header shall be X3DH protocol version || message type || curveId
belle_sip_message_t *message = BELLE_SIP_MESSAGE(event->response);
......@@ -421,15 +431,6 @@ namespace lime {
auto body = reinterpret_cast<const uint8_t *>(belle_sip_message_get_body(message));
auto bodySize = belle_sip_message_get_body_size(message);
callbackUserData<Curve> *userData = static_cast<callbackUserData<Curve> *>(data);
auto thiz = userData->limeObj.lock(); // get a shared pointer to Lime Object from the weak pointer stored in userData
// check it is valid (lock() returns nullptr)
if (!thiz) { // our Lime caller object doesn't exists anymore
BCTBX_SLOGE<<"Got response from X3DH server but our Lime Object has been destroyed";
delete(userData);
return;
}
auto callback = userData->callback; // get callback
lime::x3dh_protocol::x3dh_message_type message_type{x3dh_protocol::x3dh_message_type::unset_type};
lime::x3dh_protocol::x3dh_error_code error_code{x3dh_protocol::x3dh_error_code::unset_error_code};
......@@ -440,11 +441,6 @@ namespace lime {
// Is it an error message?
if (message_type == lime::x3dh_protocol::x3dh_message_type::error) {
// check error code: if we have a user_already_in it means we tried to insert a user on server but it failed, we must delete it from local Storagemak
if (error_code == lime::x3dh_protocol::x3dh_error_code::user_already_in) {
thiz->m_localStorage->delete_LimeUser(thiz->m_selfDeviceId); // do not use the lime delete function as it forwards the delete to X3DH server, local delete only
}
if (callback) callback(lime::callbackReturn::fail, "X3DH server error");
thiz->cleanUserData(userData);
return;
......@@ -508,19 +504,43 @@ namespace lime {
return;
}
} else { // response code is not 200Ok
//TODO : something here
if (callback) callback(lime::callbackReturn::fail, std::string("Got a non Ok response from server : ").append(std::to_string(code)));
thiz->cleanUserData(userData);
return;
}
}
}
static void process_io_error(void *data, const belle_sip_io_error_event_t *event){
//TODO : something here
template <typename Curve>
void Lime<Curve>::process_io_error(void *data, const belle_sip_io_error_event_t *event) noexcept{
callbackUserData<Curve> *userData = static_cast<callbackUserData<Curve> *>(data);
auto thiz = userData->limeObj.lock(); // get a shared pointer to Lime Object from the weak pointer stored in userData
// check it is valid (lock() returns nullptr)
if (!thiz) { // our Lime caller object doesn't exists anymore
BCTBX_SLOGE<<"Unable to contact X3DH server and our Lime Object has been destroyed";
delete(userData);
return;
}
auto callback = userData->callback; // get callback
if (callback) callback(lime::callbackReturn::fail, "Unable to contact X3DH server");
thiz->cleanUserData(userData);
}
static void process_auth_requested(void *data, belle_sip_auth_event_t *event){
if (belle_sip_auth_event_get_mode(event)==BELLE_SIP_AUTH_MODE_TLS){
//TODO : something here
template <typename Curve>
void Lime<Curve>::process_auth_requested(void *data, belle_sip_auth_event_t *event) noexcept{
callbackUserData<Curve> *userData = static_cast<callbackUserData<Curve> *>(data);
auto thiz = userData->limeObj.lock(); // get a shared pointer to Lime Object from the weak pointer stored in userData
// check it is valid (lock() returns nullptr)
if (!thiz) { // our Lime caller object doesn't exists anymore
BCTBX_SLOGE<<"Unable to contact X3DH server and our Lime Object has been destroyed";
delete(userData);
return;
}
// Set in the auth the username of current user (X3DH uses device Id)
belle_sip_auth_event_set_username(event, thiz->m_selfDeviceId.data());
if (thiz->m_user_auth) thiz->m_user_auth(event);
}
......@@ -546,8 +566,8 @@ namespace lime {
belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req),BELLE_SIP_BODY_HANDLER(bh));
cbs.process_response=Lime<Curve>::process_response;
cbs.process_response_headers=process_response_header;
cbs.process_io_error=process_io_error;
cbs.process_auth_requested=process_auth_requested;
cbs.process_io_error=Lime<Curve>::process_io_error;
cbs.process_auth_requested=Lime<Curve>::process_auth_requested;
l=belle_http_request_listener_create_from_callbacks(&cbs,static_cast<void *>(userData));
belle_sip_object_data_set(BELLE_SIP_OBJECT(req), "http_request_listener", l, belle_sip_object_unref); // Ensure the listener object is destroyed when the request is destroyed
belle_http_provider_send_request(m_http_provider,req,l);
......@@ -560,12 +580,16 @@ namespace lime {
#ifdef EC25519_ENABLED
template void Lime<C255>::postToX3DHServer(callbackUserData<C255> *userData, const std::vector<uint8_t> &message);
template void Lime<C255>::process_response(void *data, const belle_http_response_event_t *event) noexcept;
template void Lime<C255>::process_io_error(void *data, const belle_sip_io_error_event_t *event) noexcept;
template void Lime<C255>::process_auth_requested(void *data, belle_sip_auth_event_t *event) noexcept;
template void Lime<C255>::cleanUserData(callbackUserData<C255> *userData);
#endif
#ifdef EC448_ENABLED
template void Lime<C448>::postToX3DHServer(callbackUserData<C448> *userData, const std::vector<uint8_t> &message);
template void Lime<C448>::process_response(void *data, const belle_http_response_event_t *event) noexcept;
template void Lime<C448>::process_io_error(void *data, const belle_sip_io_error_event_t *event) noexcept;
template void Lime<C448>::process_auth_requested(void *data, belle_sip_auth_event_t *event) noexcept;
template void Lime<C448>::cleanUserData(callbackUserData<C448> *userData);
#endif
} //namespace lime
......@@ -109,6 +109,12 @@ int wait_for(belle_sip_stack_t*s1,int* counter,int value,int timeout);
extern template void dr_sessionsInit<C448>(std::shared_ptr<DR<C448>> &alice, std::shared_ptr<DR<C448>> &bob, std::shared_ptr<lime::Db> &localStorageAlice, std::shared_ptr<lime::Db> &localStorageBob, std::string dbFilenameAlice, std::string dbFilenameBob, bool initStorage);
extern template void dr_devicesInit<C448>(std::string dbBaseFilename, std::vector<std::vector<std::vector<std::vector<sessionDetails<C448>>>>> &users, std::vector<std::string> &usernames, std::vector<std::string> &createdDBfiles);
#endif
// the test server has only one user registered but accept commands from any users using this credentials
// a real server would obviously not do that and execute commands based on the username given as credentials
// not based on the From field of HTPP packets
const std::string test_server_user_name{"alice"};
const std::string test_server_user_password{"you see the problem is this"};
} // namespace lime
#endif
......@@ -83,6 +83,12 @@ static void getMessageFor(std::string recipient, std::vector<uint8_t> &cipherHea
BC_FAIL();
}
// define a context used to extract user authentication data
struct authContext {
std::string name;
std::string password;
authContext(std::string name, std::string password) : name{name}, password{password} {};
};
/* Basic usage scenario
* - Alice and Bob register themselves on X3DH server(use randomized device Ids to allow test server to run several test in parallel)
......@@ -121,16 +127,49 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
});
try {
// create Managers : they will open/create the database given in first parameter, and use the http provider given in second one to communicate with server.
// Any application using Lime shall instantiate one LimeManager only, even in case of multiple users managed by the application.
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
BCTBX_SLOGI<<"Create alice and bob LimeManagers"<<endl;
// create Random devices names (in case we use a shared test server, devices id shall be the GRUU, X3DH/Lime does not connect user(sip:uri) and device(gruu)
// From Lime perspective, only devices exists and they must be uniquely identifies on the X3DH server.
auto aliceDeviceId = makeRandomDeviceName("alice.");
auto bobDeviceId = makeRandomDeviceName("bob.");
// users must also provide an authentication context: a way to retrieve their password to authenticate on server
authContext aliceCredentials(*aliceDeviceId, test_server_user_password);
authContext bobCredentials(*bobDeviceId, test_server_user_password);
// create Managers : they will open/create the database given in first parameter, and use the http provider given in second one to communicate with server.
// The third parameter is a callback used to identify the user on server
// Any application using Lime shall instantiate one LimeManager only, even in case of multiple users managed by the application.
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov,
// closure copy from context its way to access users credentials
[&aliceCredentials](belle_sip_auth_event_t *event){
// the deviceId is set in the event username(accessible via belle_sip_auth_event_get_username(event);
BCTBX_SLOGI<<"Accessing credentials for user "<<std::string(belle_sip_auth_event_get_username(event))<<endl;
// for test purpose we use a server which accept commands in name of any user using credential of the only one user active on it
// so we will crash the username with the one test server accepts
belle_sip_auth_event_set_username(event, test_server_user_name.data());
// In real world we shall provide the password for the requested user as below
belle_sip_auth_event_set_passwd(event, aliceCredentials.password.data());
}));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov,
// closure copy from context its way to access users credentials
[&bobCredentials](belle_sip_auth_event_t *event){
// the deviceId is set in the event username(accessible via belle_sip_auth_event_get_username(event);
BCTBX_SLOGI<<"Accessing credentials for user "<<std::string(belle_sip_auth_event_get_username(event))<<endl;
// for test purpose we use a server which accept commands in name of any user using credential of the only one user active on it
// so we will crash the username with the one test server accepts
belle_sip_auth_event_set_username(event, test_server_user_name.data());
// In real world we shall provide the password for the requested user as below
belle_sip_auth_event_set_passwd(event, bobCredentials.password.data());
}));
BCTBX_SLOGI<<"Create "<<*aliceDeviceId<<" and "<<*bobDeviceId<<" users"<<endl;
// create users, this operation is asynchronous(as the user is also created on X3DH server)
// Last parameter is a callback acceptiong as parameters a return code and a string
// - In case of successfull operation the return code is lime::callbackReturn::success, and string is empty
......@@ -158,6 +197,7 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
auto message = make_shared<const std::vector<uint8_t>>(lime_messages_pattern[0].begin(), lime_messages_pattern[0].end());
auto cipherMessage = make_shared<std::vector<uint8_t>>(); // an empty buffer to get the encrypted message
BCTBX_SLOGI<<"Alice encrypt the message"<<endl;
/************** SENDER SIDE CODE *****************************/
// encrypt, parameters are:
// - localDeviceId to select which of the users managed by the LimeManager we shall use to perform the encryption(in our example we have only one local device). This one doesn't need to be a shared pointer.
......@@ -194,6 +234,7 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
}
});
BCTBX_SLOGI<<"Alice encrypt the message, out of encrypt call, wait for callback"<<endl;
// in real sending situation, the local instance of the shared pointer are destroyed by exiting the function where they've been declared
// and where we called the encrypt function. (The LimeManager shall instead never be destroyed until the application terminates)
recipients = nullptr;
......@@ -204,21 +245,29 @@ static void helloworld_basic_test(const lime::CurveId curve, const std::string &
// this is just waiting for the callback to increase the operation_success field in counters
// sending ticks to the belle-sip stack in order to process messages
BC_ASSERT_TRUE(wait_for(stack,&counters.operation_success,++expected_success,wait_for_timeout));
BCTBX_SLOGI<<"Alice encrypt the message, callback Ok or timeout reached"<<endl;
/****** end of SYNCHRO **************************************/
/************** RECIPIENT SIDE CODE **************************/
BCTBX_SLOGI<<"Bob decrypt the message"<<endl;
// retrieve message, in real situation the server shall fan-out only the part we need or client shall parse in the cipherHeaders to retrieve the one addressed to him.
std::vector<uint8_t> bobReceivedCipherHeader{};
std::vector<uint8_t> bobReceivedCipherMessage{};
getMessageFor("bob", bobReceivedCipherHeader, bobReceivedCipherMessage);
std::vector<uint8_t> plainTextMessage{}; // a data vector to store the decrypted message
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedCipherHeader, bobReceivedCipherMessage, plainTextMessage));
if (bobReceivedCipherHeader.size()>0 && bobReceivedCipherMessage.size()>0) {
// 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()};
BC_ASSERT_TRUE(plainTextMessageString == lime_messages_pattern[0]);
std::vector<uint8_t> plainTextMessage{}; // a data vector to store the decrypted message
BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, bobReceivedCipherHeader, bobReceivedCipherMessage, plainTextMessage));
// 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()};
BC_ASSERT_TRUE(plainTextMessageString == lime_messages_pattern[0]);
BCTBX_SLOGI<<"Bob decrypt the message completed"<<endl;
} else {
BCTBX_SLOGI<<"Bob decrypt the message : no message found"<<endl;
}
/******* end of RECIPIENT SIDE CODE **************************/
// delete the users
......
......@@ -68,13 +68,20 @@ static int http_after_all(void) {
return 0;
}
/* This is the callback used for authentication on the test server.
* Test server holds only one user which is used for all connections(which MUST not work on a real server)
*/
static userAuthenticateCallback user_auth_callback([](belle_sip_auth_event_t *event) {
belle_sip_auth_event_set_username(event, test_server_user_name.data());
belle_sip_auth_event_set_passwd(event, test_server_user_password.data());
});
/* This function will destroy and recreate managers given in parameter, force deleting all internal cache and start back from what is in local Storage */
static void managersClean(std::unique_ptr<LimeManager> &alice, std::unique_ptr<LimeManager> &bob, std::string aliceDb, std::string bobDb) {
alice = nullptr;
bob = nullptr;
alice = unique_ptr<lime::LimeManager>(new lime::LimeManager(aliceDb, prov));
bob = std::unique_ptr<lime::LimeManager>(new lime::LimeManager(bobDb, prov));
alice = unique_ptr<lime::LimeManager>(new lime::LimeManager(aliceDb, prov, user_auth_callback));
bob = std::unique_ptr<lime::LimeManager>(new lime::LimeManager(bobDb, prov, user_auth_callback));
BCTBX_SLOGI<<"Trash and reload alice and bob LimeManagers";
}
......@@ -108,14 +115,14 @@ static void x3dh_without_OPk_test(const lime::CurveId curve, const std::string &
});
try {
// create Manager and device for alice
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto aliceDeviceId = makeRandomDeviceName("alice.d1.");
aliceManager->create_user(*aliceDeviceId, x3dh_server_url, curve, callback);
BC_ASSERT_TRUE(wait_for(stack,&counters.operation_success, ++expected_success,wait_for_timeout));
for (auto i=0; i<lime::settings::OPk_batch_number +1; i++) {
// Create manager and device for bob
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
auto bobDeviceId = makeRandomDeviceName("bob.d");
bobManager->create_user(*bobDeviceId, x3dh_server_url, curve, callback);
BC_ASSERT_TRUE(wait_for(stack,&counters.operation_success, ++expected_success,wait_for_timeout));
......@@ -204,8 +211,8 @@ static void x3dh_sending_chain_limit_test(const lime::CurveId curve, const std::
});
try {
// create Manager
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
// create Random devices names
auto aliceDevice1 = makeRandomDeviceName("alice.d1.");
......@@ -356,8 +363,8 @@ static void x3dh_multiple_DRsessions_test(const lime::CurveId curve, const std::
try {
// create Manager
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
// create Random devices names
auto aliceDevice1 = makeRandomDeviceName("alice.d1.");
......@@ -532,8 +539,8 @@ static void x3dh_multidev_operation_queue_test(const lime::CurveId curve, const
try {
// create Manager
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
// create Random devices names
auto aliceDevice1 = makeRandomDeviceName("alice.d1.");
......@@ -737,8 +744,8 @@ static void x3dh_operation_queue_test(const lime::CurveId curve, const std::stri
try {
// create Manager
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
// create Random devices names
auto aliceDevice1 = makeRandomDeviceName("alice.d1.");
......@@ -861,8 +868,8 @@ static void x3dh_basic_test(const lime::CurveId curve, const std::string &dbBase
try {
// create Manager
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov));
auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, prov, user_auth_callback));
// create Random devices names
auto aliceDevice1 = makeRandomDeviceName("alice.d1.");
......@@ -1080,7 +1087,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
}
});
// we need a LimeManager
auto Manager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov));
auto Manager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, prov, user_auth_callback));
auto aliceDeviceName = makeRandomDeviceName("alice.");
try {
......@@ -1090,7 +1097,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
if (counters.operation_failed == 1) return; // skip the end of the test if we can't do this
/* load alice from from DB */
auto alice = load_LimeUser(dbFilenameAlice, *aliceDeviceName, prov);
auto alice = load_LimeUser(dbFilenameAlice, *aliceDeviceName, prov, user_auth_callback);
/* no need to wait here, it shall load alice immediately */
} catch (BctbxException &e) {
BCTBX_SLOGE <<e;;
......@@ -1100,7 +1107,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
bool gotExpectedException = false;
/* Try to create the same user in the same data base, it must fail with exception raised */
try {
auto alice = insert_LimeUser(dbFilenameAlice, *aliceDeviceName, x3dh_server_url, curve, prov, callback);
auto alice = insert_LimeUser(dbFilenameAlice, *aliceDeviceName, x3dh_server_url, curve, prov, user_auth_callback, callback);
/* no need to wait here, it must fail immediately */
} catch (BctbxException &e) {
gotExpectedException = true;
......@@ -1114,7 +1121,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
/* Try to load a user which is not in DB, it must fail with exception raised */
gotExpectedException = false;
try {
auto alice = load_LimeUser(dbFilenameAlice, "bob", prov);
auto alice = load_LimeUser(dbFilenameAlice, "bob", prov, user_auth_callback);
/* no need to wait here, it must fail immediately */
} catch (BctbxException &e) {
gotExpectedException = true;
......@@ -1155,7 +1162,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
/* Create Alice again */
try {
std::shared_ptr<LimeGeneric> alice = insert_LimeUser(dbFilenameAlice, *aliceDeviceName, x3dh_server_url, curve, prov, callback);
std::shared_ptr<LimeGeneric> alice = insert_LimeUser(dbFilenameAlice, *aliceDeviceName, x3dh_server_url, curve, prov, user_auth_callback, callback);
BC_ASSERT_TRUE(wait_for(stack,&counters.operation_success,++expected_success,wait_for_timeout)); // we must get a callback saying all went well
// create another manager with a fresh DB
......@@ -1163,7 +1170,7 @@ static void user_management_test(const lime::CurveId curve, const std::string &d
dbFilenameAliceTmp.append(".tmp.sqlite3");
// create a manager and try to create alice again, it shall pass local creation(db is empty) but server shall reject it
auto ManagerTmp = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAliceTmp, prov));
auto ManagerTmp = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAliceTmp, prov, user_auth_callback));
ManagerTmp->create_user(*aliceDeviceName, x3dh_server_url, curve, callback);
BC_ASSERT_TRUE(wait_for(stack,&counters.operation_failed,counters.operation_failed+1,wait_for_timeout)); // wait on this one but we shall get a fail from server
......
alice:limeTester:5a8428abbb669f03a8f28750fb2a6c6f
......@@ -47,17 +47,28 @@ const yargs = require('yargs')
type : 'number',
default : 300
})
.option('passwords', {
alias : 'h',
describe : 'the htpassword file',
type : 'string',
default : 'htpasswd'
})
.argv;