Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package lime for openSUSE:Factory checked in at 2023-06-29 17:28:25 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/lime (Old) and /work/SRC/openSUSE:Factory/.lime.new.13546 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "lime" Thu Jun 29 17:28:25 2023 rev:22 rq:1095747 version:5.2.73 Changes: -------- --- /work/SRC/openSUSE:Factory/lime/lime.changes 2023-04-07 18:17:26.980933718 +0200 +++ /work/SRC/openSUSE:Factory/.lime.new.13546/lime.changes 2023-06-29 17:28:42.406392321 +0200 @@ -1,0 +2,5 @@ +Wed Jun 28 06:37:19 UTC 2023 - Paolo Stivanin <i...@paolostivanin.com> + +- Update to 5.2.73 (no changelog) + +------------------------------------------------------------------- Old: ---- lime-5.2.49.tar.bz2 New: ---- lime-5.2.73.tar.bz2 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ lime.spec ++++++ --- /var/tmp/diff_new_pack.Ipl6ou/_old 2023-06-29 17:28:43.770400311 +0200 +++ /var/tmp/diff_new_pack.Ipl6ou/_new 2023-06-29 17:28:43.774400334 +0200 @@ -19,7 +19,7 @@ %define soname liblime %define sover 0 Name: lime -Version: 5.2.49 +Version: 5.2.73 Release: 0 Summary: Instant Message End-to-End Encryption Library License: GPL-3.0-or-later ++++++ add-cstdint.patch ++++++ --- /var/tmp/diff_new_pack.Ipl6ou/_old 2023-06-29 17:28:43.806400521 +0200 +++ /var/tmp/diff_new_pack.Ipl6ou/_new 2023-06-29 17:28:43.810400545 +0200 @@ -1,6 +1,7 @@ -diff -ru orig/src/lime_double_ratchet.cpp mod/src/lime_double_ratchet.cpp ---- orig/src/lime_double_ratchet.cpp 2022-11-09 23:33:57.000000000 +0100 -+++ mod/src/lime_double_ratchet.cpp 2023-03-28 08:12:25.447825643 +0200 +Index: lime-5.2.73/src/lime_double_ratchet.cpp +=================================================================== +--- lime-5.2.73.orig/src/lime_double_ratchet.cpp ++++ lime-5.2.73/src/lime_double_ratchet.cpp @@ -25,6 +25,7 @@ #include "bctoolbox/exception.hh" @@ -9,13 +10,14 @@ using namespace::std; -diff -ru orig/src/lime_settings.hpp mod/src/lime_settings.hpp ---- orig/src/lime_settings.hpp 2022-11-09 23:33:57.000000000 +0100 -+++ mod/src/lime_settings.hpp 2023-03-28 08:11:58.683669300 +0200 -@@ -20,6 +20,8 @@ - #ifndef lime_settings_hpp +Index: lime-5.2.73/src/lime_settings.hpp +=================================================================== +--- lime-5.2.73.orig/src/lime_settings.hpp ++++ lime-5.2.73/src/lime_settings.hpp +@@ -21,6 +21,8 @@ #define lime_settings_hpp + +#include <cstdint> + namespace lime { ++++++ lime-5.2.49.tar.bz2 -> lime-5.2.73.tar.bz2 ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/CHANGELOG.md new/lime-5.2.73/CHANGELOG.md --- old/lime-5.2.49/CHANGELOG.md 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/CHANGELOG.md 2023-05-10 07:45:32.000000000 +0200 @@ -3,8 +3,18 @@ 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). +## [5.3.0] - XXXX-XX-XX +### Changed +- Lime manager keeps an open connexion to the db +- Update timer is managed internally: keep track of last successful update for each local user +- Db schema updated from version 0.0.1 to 0.1.0 + + +## [5.2.0] - 2022-11-08 +### Added +- API to compute peer status for a list of peer device -## [4.5.0] - XXXX-XX-XX +## [4.5.0] - 2021-03-29 ### Added - Ability to force an active session to stale ### Changed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/CMakeLists.txt new/lime-5.2.73/CMakeLists.txt --- old/lime-5.2.49/CMakeLists.txt 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/CMakeLists.txt 2023-05-10 07:45:32.000000000 +0200 @@ -108,11 +108,7 @@ set(STRICT_OPTIONS_CXX ) set(STRICT_OPTIONS_OBJC ) -if(ENABLE_JNI) - set(CMAKE_CXX_STANDARD 14) -else() - set(CMAKE_CXX_STANDARD 11) -endif() +set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_C_STANDARD 99) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/include/lime/lime.hpp new/lime-5.2.73/include/lime/lime.hpp --- old/lime-5.2.49/include/lime/lime.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/include/lime/lime.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -115,8 +115,9 @@ */ using limeX3DHServerPostData = std::function<void(const std::string &url, const std::string &from, const std::vector<uint8_t> &message, const limeX3DHServerResponseProcess &reponseProcess)>; - /* Forward declare the class managing one lime user*/ + /* Forward declare the class managing one lime user and class managing database */ class LimeGeneric; + class Db; /** @brief Manage several Lime objects(one is needed for each local user). * @@ -128,8 +129,7 @@ private : std::unordered_map<std::string, std::shared_ptr<LimeGeneric>> m_users_cache; // cache of already opened Lime Session, identified by user Id (GRUU) std::mutex m_users_mutex; // m_users_cache mutex - std::string m_db_access; // DB access information forwarded to SOCI to correctly access database - std::shared_ptr<std::recursive_mutex> m_db_mutex; // database access mutex + std::shared_ptr<lime::Db> m_localStorage; // DB access information forwarded to SOCI to correctly access database limeX3DHServerPostData m_X3DH_post_data; // send data to the X3DH key server void load_user(std::shared_ptr<LimeGeneric> &user, const std::string &localDeviceId, const bool allStatus=false); // helper function, get from m_users_cache of local Storage the requested Lime object @@ -243,14 +243,15 @@ 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 + * @brief Update: shall be called regularly, once a day at least, performs checks, updates and cleaning operations + * The update is performed each OPk_updatePeriod (defined in lime::settings to be one day). If the function is called before + * this interval is over, it just does nothing. * - * - check if we shall update a new SPk to X3DH server(SPk lifetime is set in settings) + * - check if we shall update a new SPk to X3DH server(SPk lifetime is set in lime::settings) * - check if we need to upload OPks to X3DH server * - remove old SPks, clean double ratchet sessions (remove staled, clean their stored keys for skipped messages) * - * Is performed for all users founds in local storage - * + * @param[in] localDeviceId Identify the local user acount to use, it must be unique and is also used as Id on the X3DH key server, it shall be the GRUU * @param[in] callback This operation may contact the X3DH server and is thus asynchronous, when server responds, * this callback will be called giving the exit status and an error message in case of failure. * @param[in] OPkServerLowLimit If server holds less OPk than this limit, generate and upload a batch of OPks @@ -260,11 +261,11 @@ * The last two parameters are optional, if not used, set to defaults defined in lime::settings * (not done with param default value as the lime::settings shall not be available in public include) */ - void update(const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize); + void update(const std::string &localDeviceId, const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize); /** * @overload void update(const limeCallback &callback) */ - void update(const limeCallback &callback); + void update(const std::string &localDeviceId, const limeCallback &callback); /** * @brief retrieve self Identity Key, an EdDSA formatted public key diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/include/lime/lime_ffi.h new/lime-5.2.73/include/lime/lime_ffi.h --- old/lime-5.2.49/include/lime/lime_ffi.h 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/include/lime/lime_ffi.h 2023-05-10 07:45:32.000000000 +0200 @@ -402,6 +402,7 @@ * Is performed for all users founds in local storage * * @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] callback Performing encryption may involve the X3DH server and is thus asynchronous, when the operation is completed, * this callback will be called giving the exit status and an error message in case of failure. * @param[in] callbackUserData this pointer will be forwarded to the callback as first parameter @@ -410,7 +411,7 @@ * * @return LIME_FFI_SUCCESS or a negative error code */ -int lime_ffi_update(lime_manager_t manager, const lime_ffi_Callback callback, void *callbackUserData, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize); +int lime_ffi_update(lime_manager_t manager, const char *localDeviceId, const lime_ffi_Callback callback, void *callbackUserData, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize); /** * @brief Set the X3DH key server URL for this identified user diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime.cpp new/lime-5.2.73/src/lime.cpp --- old/lime-5.2.49/src/lime.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -46,13 +46,12 @@ * @param[in] X3DH_post_data A function used to communicate with the X3DH server * @param[in] Uid the DB internal Id for this user, speed up DB operations by holding it in DB * - * @note: ownership of localStorage pointer is transfered to a shared pointer, private menber of Lime class */ template <typename Curve> - Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data, const long int Uid) + Lime<Curve>::Lime(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data, const long int Uid) : m_RNG{make_RNG()}, m_selfDeviceId{deviceId}, m_Ik{}, m_Ik_loaded(false), - m_localStorage(std::move(localStorage)), m_db_Uid{Uid}, + m_localStorage(localStorage), m_db_Uid{Uid}, m_X3DH_post_data{X3DH_post_data}, m_X3DH_Server_URL{url}, m_DR_sessions_cache{}, m_ongoing_encryption{nullptr}, m_encryption_queue{} { } @@ -68,13 +67,12 @@ * @param[in] url URL of the X3DH key server used to publish our keys * @param[in] X3DH_post_data A function used to communicate with the X3DH server * - * @note: ownership of localStorage pointer is transfered to a shared pointer, private menber of Lime class */ template <typename Curve> - Lime<Curve>::Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data) + Lime<Curve>::Lime(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data) : m_RNG{make_RNG()}, m_selfDeviceId{deviceId}, m_Ik{}, m_Ik_loaded(false), - m_localStorage(std::move(localStorage)), m_db_Uid{0}, + m_localStorage(localStorage), m_db_Uid{0}, m_X3DH_post_data{X3DH_post_data}, m_X3DH_Server_URL{url}, m_DR_sessions_cache{}, m_ongoing_encryption{nullptr}, m_encryption_queue{} { @@ -378,19 +376,18 @@ * * 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] localStorage Database access * @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] OPkInitialBatchSize Number of OPks in the first batch uploaded to X3DH server * @param[in] X3DH_post_data A function used to communicate with the X3DH server * @param[in] callback To provide caller the operation result - * @param[in] db_mutex a mutex to protect db access * * @return a pointer to the LimeGeneric class allowing access to API declared in lime_lime.hpp */ - std::shared_ptr<LimeGeneric> insert_LimeUser(const std::string &dbFilename, const std::string &deviceId, const std::string &url, const lime::CurveId curve, const uint16_t OPkInitialBatchSize, - const limeX3DHServerPostData &X3DH_post_data, const limeCallback &callback, std::shared_ptr<std::recursive_mutex> db_mutex) { + std::shared_ptr<LimeGeneric> insert_LimeUser(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const lime::CurveId curve, const uint16_t OPkInitialBatchSize, + const limeX3DHServerPostData &X3DH_post_data, const limeCallback &callback) { LIME_LOGI<<"Create Lime user "<<deviceId; /* first check the requested curve is instanciable and return an exception if not */ #ifndef EC25519_ENABLED @@ -404,16 +401,13 @@ } #endif - /* open DB */ - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(dbFilename, db_mutex)); // create as unique ptr, ownership is then passed to the Lime structure when instanciated - //instanciate the correct Lime object switch (curve) { case lime::CurveId::c25519 : #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, X3DH_post_data); + auto lime_ptr = std::make_shared<Lime<C255>>(localStorage, deviceId, url, X3DH_post_data); lime_ptr->publish_user(callback, OPkInitialBatchSize); return lime_ptr; } @@ -423,7 +417,7 @@ case lime::CurveId::c448 : #ifdef EC448_ENABLED { - auto lime_ptr = std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, url, X3DH_post_data); + auto lime_ptr = std::make_shared<Lime<C448>>(localStorage, deviceId, url, X3DH_post_data); lime_ptr->publish_user(callback, OPkInitialBatchSize); return lime_ptr; } @@ -444,18 +438,16 @@ * Fail to find the user will raise an exception * If allStatus flag is set to false (default value), raise an exception on inactive users otherwise load inactive user. * - * @param[in] dbFilename Path to filename to use + * @param[in] localStorage Database access * @param[in] deviceId User to lookup in DB, deviceId shall be the GRUU * @param[in] X3DH_post_data A function used to communicate with the X3DH server - * @param[in] db_mutex a mutex to protect db access * @param[in] allStatus allow loading of inactive user if set to true * * @return a pointer to the LimeGeneric class allowing access to API declared in lime_lime.hpp */ - std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, const limeX3DHServerPostData &X3DH_post_data, std::shared_ptr<std::recursive_mutex> db_mutex, const bool allStatus) { + std::shared_ptr<LimeGeneric> load_LimeUser(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const limeX3DHServerPostData &X3DH_post_data, const bool allStatus) { - /* open DB and load user */ - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(dbFilename, db_mutex)); // create as unique ptr, ownership is then passed to the Lime structure when instanciated + /* load user */ auto curve = CurveId::unset; long int Uid=0; std::string x3dh_server_url; @@ -479,13 +471,13 @@ switch (curve) { case lime::CurveId::c25519 : #ifdef EC25519_ENABLED - return std::make_shared<Lime<C255>>(std::move(localStorage), deviceId, x3dh_server_url, X3DH_post_data, Uid); + return std::make_shared<Lime<C255>>(localStorage, deviceId, x3dh_server_url, X3DH_post_data, Uid); #endif break; case lime::CurveId::c448 : #ifdef EC448_ENABLED - return std::make_shared<Lime<C448>>(std::move(localStorage), deviceId, x3dh_server_url, X3DH_post_data, Uid); + return std::make_shared<Lime<C448>>(localStorage, deviceId, x3dh_server_url, X3DH_post_data, Uid); #endif break; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_defines.hpp new/lime-5.2.73/src/lime_defines.hpp --- old/lime-5.2.49/src/lime_defines.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_defines.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -20,6 +20,8 @@ #ifndef lime_defines_hpp #define lime_defines_hpp +#include <string> + namespace lime { /** @brief Hold constants definition used as settings in all components of the lime library * @@ -71,9 +73,9 @@ /******************************************************************************/ /** define a version number for the DB schema as an integer 0xMMmmpp * - * current version is 0.0.1 + * current version is 0.1.0 */ - constexpr int DBuserVersion=0x000001; + constexpr int DBuserVersion=0x000100; constexpr uint16_t DBInactiveUserBit = 0x0100; constexpr uint16_t DBCurveIdByte = 0x00FF; constexpr uint8_t DBInvalidIk = 0x00; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_double_ratchet.cpp new/lime-5.2.73/src/lime_double_ratchet.cpp --- old/lime-5.2.49/src/lime_double_ratchet.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_double_ratchet.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -57,9 +57,9 @@ } /** constant used as input of HKDF like function, see double ratchet spec section 5.2 - KDF_CK */ - const std::array<std::uint8_t,1> hkdf_ck_info{{0x02}}; + const std::array<uint8_t,1> hkdf_ck_info{{0x02}}; /** constant used as input of HKDF like function, see double ratchet spec section 5.2 - KDF_CK */ - const std::array<std::uint8_t,1> hkdf_mk_info{{0x01}}; + const std::array<uint8_t,1> hkdf_mk_info{{0x01}}; /** * @brief Key Derivation Function used in Symmetric key ratchet chain. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_double_ratchet.hpp new/lime-5.2.73/src/lime_double_ratchet.hpp --- old/lime-5.2.49/src/lime_double_ratchet.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_double_ratchet.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -62,7 +62,7 @@ template <typename Curve> struct ReceiverKeyChain { X<Curve, lime::Xtype::publicKey> DHr; /**< peer public key identifying this chain */ - std::unordered_map<std::uint16_t, DRMKey> messageKeys; /**< message keys indexed by Nr */ + std::unordered_map<uint16_t, DRMKey> messageKeys; /**< message keys indexed by Nr */ /** * Start a new empty chain * @param[in] key the peer DH public key used on this chain @@ -88,8 +88,8 @@ DRChainKey m_RK; // 32 bytes root key DRChainKey m_CKs; // 32 bytes key chain for sending DRChainKey m_CKr; // 32 bytes key chain for receiving - std::uint16_t m_Ns,m_Nr; // Message index in sending and receiving chain - std::uint16_t m_PN; // Number of messages in previous sending chain + uint16_t m_Ns,m_Nr; // Message index in sending and receiving chain + uint16_t m_PN; // Number of messages in previous sending chain SharedADBuffer m_sharedAD; // Associated Data derived from self and peer device Identity key, set once at session creation, given by X3DH std::vector<lime::ReceiverKeyChain<Curve>> m_mkskipped; // list of skipped message indexed by DH receiver public key and Nr, store MK generated during on-going decrypt, lookup is done directly in DB. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_double_ratchet_protocol.hpp new/lime-5.2.73/src/lime_double_ratchet_protocol.hpp --- old/lime-5.2.49/src/lime_double_ratchet_protocol.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_double_ratchet_protocol.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -111,7 +111,7 @@ #endif /* These constants are needed only for tests purpose, otherwise their usage is internal only to double_ratchet_protocol.hpp */ /** Double ratchet protocol version number */ - constexpr std::uint8_t DR_v01=0x01; + constexpr uint8_t DR_v01=0x01; /** @brief DR message type byte bit mapping * @code{.unparsed} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_ffi.cpp new/lime-5.2.73/src/lime_ffi.cpp --- old/lime-5.2.49/src/lime_ffi.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_ffi.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -420,14 +420,14 @@ 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) { +int lime_ffi_update(lime_manager_t manager, const char *localDeviceId, 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){ callback(callbackUserData, lime2ffi_CallbackReturn(status), message.data()); }); try { - manager->context->update(cb, OPkServerLowLimit, OPkBatchSize); + manager->context->update(std::string(localDeviceId), cb, OPkServerLowLimit, OPkBatchSize); } catch (BctbxException const &e) { LIME_LOGE<<"FFI failed during update: "<<e.str(); return LIME_FFI_INTERNAL_ERROR; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_impl.hpp new/lime-5.2.73/src/lime_impl.hpp --- old/lime-5.2.49/src/lime_impl.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_impl.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -98,8 +98,8 @@ void cleanUserData(std::shared_ptr<callbackUserData<Curve>> userData); // clean user data public: /* Implement API defined in lime_lime.hpp in LimeGeneric abstract class */ - Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data); - Lime(std::unique_ptr<lime::Db> &&localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data, const long int Uid); + Lime(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data); + Lime(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const limeX3DHServerPostData &X3DH_post_data, const long int 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 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_jni.cpp new/lime-5.2.73/src/lime_jni.cpp --- old/lime-5.2.49/src/lime_jni.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_jni.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -402,16 +402,17 @@ return c2jPeerDeviceStatus(status); } - void update(jni::JNIEnv &env, jni::Object<jStatusCallback> &jstatusObj, jni::jint jOPkServerLowLimit, jni::jint jOPkBatchSize) { + void update(jni::JNIEnv &env, const jni::String &jlocalDeviceId, jni::Object<jStatusCallback> &jstatusObj, jni::jint jOPkServerLowLimit, jni::jint jOPkBatchSize) { JavaVM *c_vm; env.GetJavaVM(&c_vm); - LIME_LOGD<<"JNI update"; + LIME_LOGD<<"JNI update for "<<(jni::Make<std::string>(env, jlocalDeviceId)); // see create_user for details on this auto jstatusObjRef = std::make_shared<jni::Global<jni::Object<jStatusCallback>, jni::EnvGettingDeleter>>(jni::NewGlobal<jni::EnvGettingDeleter>(env, jstatusObj)); - m_manager->update([c_vm, jstatusObjRef] (const lime::CallbackReturn status, const std::string message) + m_manager->update(jni::Make<std::string>(env, jlocalDeviceId), + [c_vm, jstatusObjRef] (const lime::CallbackReturn status, const std::string message) { // get the env from VM jni::JNIEnv& g_env { jni::GetEnv(*c_vm)}; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_lime.hpp new/lime-5.2.73/src/lime_lime.hpp --- old/lime-5.2.49/src/lime_lime.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_lime.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -172,10 +172,10 @@ /* 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 */ - std::shared_ptr<LimeGeneric> insert_LimeUser(const std::string &dbFilename, const std::string &deviceId, const std::string &url, const lime::CurveId curve, const uint16_t OPkInitialBatchSize, - const limeX3DHServerPostData &X3DH_post_data, const limeCallback &callback, std::shared_ptr<std::recursive_mutex> mutex); + std::shared_ptr<LimeGeneric> insert_LimeUser(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const std::string &url, const lime::CurveId curve, const uint16_t OPkInitialBatchSize, + const limeX3DHServerPostData &X3DH_post_data, const limeCallback &callback); - std::shared_ptr<LimeGeneric> load_LimeUser(const std::string &dbFilename, const std::string &deviceId, const limeX3DHServerPostData &X3DH_post_data, std::shared_ptr<std::recursive_mutex> mutex, const bool allStatus=false); + std::shared_ptr<LimeGeneric> load_LimeUser(std::shared_ptr<lime::Db> localStorage, const std::string &deviceId, const limeX3DHServerPostData &X3DH_post_data, const bool allStatus=false); } #endif // lime_lime_hpp diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_localStorage.cpp new/lime-5.2.73/src/lime_localStorage.cpp --- old/lime-5.2.49/src/lime_localStorage.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_localStorage.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -39,176 +39,190 @@ /* Db public API */ /* */ /******************************************************************************/ -Db::Db(const std::string &filename, std::shared_ptr<std::recursive_mutex> db_mutex) : sql{"sqlite3", filename}, m_db_mutex{db_mutex} { +Db::Db(const std::string &filename, std::shared_ptr<std::recursive_mutex> db_mutex) : m_db_mutex{db_mutex} { std::lock_guard<std::recursive_mutex> lock(*m_db_mutex); constexpr int db_module_table_not_holding_lime_row = -1; int userVersion=db_module_table_not_holding_lime_row; - sql<<"PRAGMA foreign_keys = ON;"; // make sure this connection enable foreign keys - transaction tr(sql); - // CREATE OR INGORE TABLE db_module_version( - sql<<"CREATE TABLE IF NOT EXISTS db_module_version(" - "name VARCHAR(16) PRIMARY KEY," - "version UNSIGNED INTEGER NOT NULL" - ")"; - sql<<"SELECT version FROM db_module_version WHERE name='lime'", into(userVersion); - - // Enforce value in case there is no lime version number in table db_module_version - if (!sql.got_data()) { - userVersion=db_module_table_not_holding_lime_row; - } + try { + sql.open("sqlite3", filename); + sql<<"PRAGMA foreign_keys = ON;"; // make sure this connection enable foreign keys + transaction tr(sql); + // CREATE OR IGNORE TABLE db_module_version( + sql<<"CREATE TABLE IF NOT EXISTS db_module_version(" + "name VARCHAR(16) PRIMARY KEY," + "version UNSIGNED INTEGER NOT NULL" + ")"; + sql<<"SELECT version FROM db_module_version WHERE name='lime'", into(userVersion); + + // Enforce value in case there is no lime version number in table db_module_version + if (!sql.got_data()) { + userVersion=db_module_table_not_holding_lime_row; + } - if (userVersion == lime::settings::DBuserVersion) { - return; - } + if (userVersion == lime::settings::DBuserVersion) { + return; + } - if (userVersion > lime::settings::DBuserVersion) { /* nothing to do if we encounter a superior version number than expected, just hope it is compatible */ - LIME_LOGE<<"Lime module database schema version found in DB(v "<<userVersion<<") is more recent than the one currently supported by the lime module(v "<<static_cast<unsigned int>(lime::settings::DBuserVersion)<<")"; - return; - } + if (userVersion > lime::settings::DBuserVersion) { /* nothing to do if we encounter a superior version number than expected, just hope it is compatible */ + LIME_LOGE<<"Lime module database schema version found in DB(v "<<userVersion<<") is more recent than the one currently supported by the lime module(v "<<static_cast<unsigned int>(lime::settings::DBuserVersion)<<")"; + return; + } - /* Perform update if needed */ - // update the schema version in DB - if (userVersion == db_module_table_not_holding_lime_row) { // but not any lime row in it - sql<<"INSERT INTO db_module_version(name,version) VALUES('lime',:DbVersion)", use(lime::settings::DBuserVersion); - } else { // and we had an older version - sql<<"UPDATE db_module_version SET version = :DbVersion WHERE name='lime'", use(lime::settings::DBuserVersion); - /* Do the update here */ + /* Perform update if needed */ + // update the schema version in DB + if (userVersion == db_module_table_not_holding_lime_row) { // but not any lime row in it + sql<<"INSERT INTO db_module_version(name,version) VALUES('lime',:DbVersion)", use(lime::settings::DBuserVersion); + } else { // and we had an older version + /* Do the update here */ + sql<<"ALTER TABLE lime_LocalUsers ADD COLUMN updateTs DATETIME"; + sql<<"UPDATE lime_LocalUsers SET updateTs = CURRENT_TIMESTAMP"; + // update version number + sql<<"UPDATE db_module_version SET version = :DbVersion WHERE name='lime'", use(lime::settings::DBuserVersion); + tr.commit(); // commit all the previous queries + LIME_LOGI<<"Perform lime database migration from version "<<userVersion<<" to version "<<lime::settings::DBuserVersion; + return; + } + + // create the lime DB: + + /*** Double Ratchet tables ***/ + /* DR Session: + * - DId : link to lime_PeerDevices table, identify which peer is associated to this session + * - Uid: link to LocalUsers table, identify which local device is associated to this session + * - SessionId(primary key) + * - Ns, Nr, PN : index for sending, receivind and previous sending chain + * - DHr : peer current public ECDH key + * - DHs : self current ECDH key. (public || private keys) + * - RK, CKs, CKr : Root key, sender and receiver chain keys + * - AD : Associated data : provided once at session creation by X3DH, is derived from initiator public Ik and id, receiver public Ik and id + * - Status : 0 is for stale and 1 is for active, only one session shall be active for a peer device, by default created as active + * - timeStamp : is updated when session change status and is used to remove stale session after determined time in cleaning operation + * - X3DHInit : when we are initiator, store the generated X3DH init message and keep sending it until we've got at least a reply from peer + */ + sql<<"CREATE TABLE DR_sessions( \ + Did INTEGER NOT NULL DEFAULT 0, \ + Uid INTEGER NOT NULL DEFAULT 0, \ + sessionId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + Ns UNSIGNED INTEGER NOT NULL, \ + Nr UNSIGNED INTEGER NOT NULL, \ + PN UNSIGNED INTEGER NOT NULL, \ + DHr BLOB NOT NULL, \ + DHs BLOB NOT NULL, \ + RK BLOB NOT NULL, \ + CKs BLOB NOT NULL, \ + CKr BLOB NOT NULL, \ + AD BLOB NOT NULL, \ + Status INTEGER NOT NULL DEFAULT 1, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + X3DHInit BLOB DEFAULT NULL, \ + FOREIGN KEY(Did) REFERENCES lime_PeerDevices(Did) ON UPDATE CASCADE ON DELETE CASCADE, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + + /* DR Message Skipped DH : Store chains of skipped message keys, this table store the DHr identifying the chain + * - DHid (primary key) + * - SessionId : foreign key, link to the DR session the skipped keys are attached + * - DHr : the peer ECDH public key used in this key chain + * - received : count messages successfully decoded since the last MK insertion in that chain, allow to delete chains that are too old + */ + sql<<"CREATE TABLE DR_MSk_DHr( \ + DHid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + sessionId INTEGER NOT NULL DEFAULT 0, \ + DHr BLOB NOT NULL, \ + received UNSIGNED INTEGER NOT NULL DEFAULT 0, \ + FOREIGN KEY(sessionId) REFERENCES DR_sessions(sessionId) ON UPDATE CASCADE ON DELETE CASCADE);"; + + /* DR Message Skipped MK : Store chains of skipped message keys, this table store the message keys with their index in the chain + * - DHid : foreign key, link to the key chain table: DR_Message_Skipped_DH + * - Nr : the id in the key chain + * - MK : the message key stored + * primary key is [DHid,Nr] + */ + sql<<"CREATE TABLE DR_MSk_MK( \ + DHid INTEGER NOT NULL, \ + Nr INTEGER NOT NULL, \ + MK BLOB NOT NULL, \ + PRIMARY KEY( DHid , Nr ), \ + FOREIGN KEY(DHid) REFERENCES DR_MSk_DHr(DHid) ON UPDATE CASCADE ON DELETE CASCADE);"; + + /*** Lime tables : local user identities, peer devices identities ***/ + /* List each self account enable on device : + * - Uid : primary key, used to make link with Peer Devices, SPk and OPk tables + * - UserId : shall be the GRUU + * - Ik : public||private indentity key (EdDSA key) + * - server : the URL of key Server + * - curveId : identifies the curve used by this user - MUST be in sync with server. This integer stores also the activation byte. + * Mapping is: <Activation byte>||<CurveId byte> + * Activation byte is: 0x00 Active, 0x01 inactive + * CurveId byte: as set in lime.hpp + * default the curveId value to 0 which is not one of the possible values (defined in lime.hpp) + * - updateTs : Last update timestamp. When was performed an update operation for this user. + */ + sql<<"CREATE TABLE lime_LocalUsers( \ + Uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + UserId TEXT NOT NULL, \ + Ik BLOB NOT NULL, \ + server TEXT NOT NULL, \ + curveId INTEGER NOT NULL DEFAULT 0, \ + updateTs DATETIME DEFAULT CURRENT_TIMESTAMP);"; + + /* Peer Devices : + * - 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 + * - 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 + * + * Note2: The Ik field should be able to be NULL but it is not for historical reason. + * When a peer device is inserted without Ik(through the set_peerDeviceStatus with a unsafe status is the only way to do that) + * it will be given an Ik set to invalid_Ik (one byte at 0x00) with the purpose of being unable to match a real Ik as NULL would have done + */ + sql<<"CREATE TABLE lime_PeerDevices( \ + Did INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + DeviceId TEXT NOT NULL, \ + Ik BLOB NOT NULL, \ + Status UNSIGNED INTEGER DEFAULT 0);"; + + /*** X3DH tables ***/ + /* Signed pre-key : + * - SPKid : the primary key must be a random number as it is public, so avoid leaking information on number of key used + * - SPK : Public key||Private Key (ECDH keys) + * - timeStamp : Application shall renew SPK regurlarly (SPK_LifeTime). Old key are disactivated and deleted after a period (SPK_LimboTime)) + * - Status : a boolean: can be active(1) or stale(0), by default any newly inserted key is set to active + * - Uid : User Id from lime_LocalUsers table: who's key is this + */ + sql<<"CREATE TABLE X3DH_SPK( \ + SPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ + SPK BLOB NOT NULL, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + Status INTEGER NOT NULL DEFAULT 1, \ + Uid INTEGER NOT NULL, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + + /* One time pre-key : deleted after usage, generated at user creation and on X3DH server request + * - OPKid : the primary key must be a random number as it is public, so avoid leaking information on number of key used + * - OPK : Public key||Private Key (ECDH keys) + * - Uid : User Id from lime_LocalUsers table: who's key is this + * - Status : a boolean: is likely to be present on X3DH Server(1), not anymore on X3DH server(0), by default any newly inserted key is set to 1 + * - timeStamp : timeStamp is set during update if we found out a key is no more on server(and we didn't used it as usage delete key). + * So after a limbo period, key is considered missing in action and removed from storage. + */ + sql<<"CREATE TABLE X3DH_OPK( \ + OPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ + OPK BLOB NOT NULL, \ + Uid INTEGER NOT NULL, \ + Status INTEGER NOT NULL DEFAULT 1, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + tr.commit(); // commit all the previous queries - return; + } catch (BctbxException const &e) { + throw BCTBX_EXCEPTION << "Db instanciation on file "<<filename<<" check failed: "<<e.str(); + } catch (exception const &e) { + throw BCTBX_EXCEPTION << "Db instanciation on file "<<filename<<" check failed: "<<e.what(); } - - // create the lime DB: - - /*** Double Ratchet tables ***/ - /* DR Session: - * - DId : link to lime_PeerDevices table, identify which peer is associated to this session - * - Uid: link to LocalUsers table, identify which local device is associated to this session - * - SessionId(primary key) - * - Ns, Nr, PN : index for sending, receivind and previous sending chain - * - DHr : peer current public ECDH key - * - DHs : self current ECDH key. (public || private keys) - * - RK, CKs, CKr : Root key, sender and receiver chain keys - * - AD : Associated data : provided once at session creation by X3DH, is derived from initiator public Ik and id, receiver public Ik and id - * - Status : 0 is for stale and 1 is for active, only one session shall be active for a peer device, by default created as active - * - timeStamp : is updated when session change status and is used to remove stale session after determined time in cleaning operation - * - X3DHInit : when we are initiator, store the generated X3DH init message and keep sending it until we've got at least a reply from peer - */ - sql<<"CREATE TABLE DR_sessions( \ - Did INTEGER NOT NULL DEFAULT 0, \ - Uid INTEGER NOT NULL DEFAULT 0, \ - sessionId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ - Ns UNSIGNED INTEGER NOT NULL, \ - Nr UNSIGNED INTEGER NOT NULL, \ - PN UNSIGNED INTEGER NOT NULL, \ - DHr BLOB NOT NULL, \ - DHs BLOB NOT NULL, \ - RK BLOB NOT NULL, \ - CKs BLOB NOT NULL, \ - CKr BLOB NOT NULL, \ - AD BLOB NOT NULL, \ - Status INTEGER NOT NULL DEFAULT 1, \ - timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ - X3DHInit BLOB DEFAULT NULL, \ - FOREIGN KEY(Did) REFERENCES lime_PeerDevices(Did) ON UPDATE CASCADE ON DELETE CASCADE, \ - FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; - - /* DR Message Skipped DH : Store chains of skipped message keys, this table store the DHr identifying the chain - * - DHid (primary key) - * - SessionId : foreign key, link to the DR session the skipped keys are attached - * - DHr : the peer ECDH public key used in this key chain - * - received : count messages successfully decoded since the last MK insertion in that chain, allow to delete chains that are too old - */ - sql<<"CREATE TABLE DR_MSk_DHr( \ - DHid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ - sessionId INTEGER NOT NULL DEFAULT 0, \ - DHr BLOB NOT NULL, \ - received UNSIGNED INTEGER NOT NULL DEFAULT 0, \ - FOREIGN KEY(sessionId) REFERENCES DR_sessions(sessionId) ON UPDATE CASCADE ON DELETE CASCADE);"; - - /* DR Message Skipped MK : Store chains of skipped message keys, this table store the message keys with their index in the chain - * - DHid : foreign key, link to the key chain table: DR_Message_Skipped_DH - * - Nr : the id in the key chain - * - MK : the message key stored - * primary key is [DHid,Nr] - */ - sql<<"CREATE TABLE DR_MSk_MK( \ - DHid INTEGER NOT NULL, \ - Nr INTEGER NOT NULL, \ - MK BLOB NOT NULL, \ - PRIMARY KEY( DHid , Nr ), \ - FOREIGN KEY(DHid) REFERENCES DR_MSk_DHr(DHid) ON UPDATE CASCADE ON DELETE CASCADE);"; - - /*** Lime tables : local user identities, peer devices identities ***/ - /* List each self account enable on device : - * - Uid : primary key, used to make link with Peer Devices, SPk and OPk tables - * - User Id : shall be the GRUU - * - Ik : public||private indentity key (EdDSA key) - * - server : the URL of key Server - * - curveId : identifies the curve used by this user - MUST be in sync with server. This integer stores also the activation byte. - * Mapping is: <Activation byte>||<CurveId byte> - * Activation byte is: 0x00 Active, 0x01 inactive - * CurveId byte: as set in lime.hpp - */ - sql<<"CREATE TABLE lime_LocalUsers( \ - Uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ - UserId TEXT NOT NULL, \ - Ik BLOB NOT NULL, \ - server TEXT NOT NULL, \ - curveId INTEGER NOT NULL DEFAULT 0);"; // default the curveId value to 0 which is not one of the possible values (defined in lime.hpp) - - /* Peer Devices : - * - 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 - * - 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 - * - * Note2: The Ik field should be able to be NULL but it is not for historical reason. - * When a peer device is inserted without Ik(through the set_peerDeviceStatus with a unsafe status is the only way to do that) - * it will be given an Ik set to invalid_Ik (one byte at 0x00) with the purpose of being unable to match a real Ik as NULL would have done - */ - sql<<"CREATE TABLE lime_PeerDevices( \ - Did INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ - DeviceId TEXT NOT NULL, \ - Ik BLOB NOT NULL, \ - Status UNSIGNED INTEGER DEFAULT 0);"; - - /*** X3DH tables ***/ - /* Signed pre-key : - * - SPKid : the primary key must be a random number as it is public, so avoid leaking information on number of key used - * - SPK : Public key||Private Key (ECDH keys) - * - timeStamp : Application shall renew SPK regurlarly (SPK_LifeTime). Old key are disactivated and deleted after a period (SPK_LimboTime)) - * - Status : a boolean: can be active(1) or stale(0), by default any newly inserted key is set to active - * - Uid : User Id from lime_LocalUsers table: who's key is this - */ - sql<<"CREATE TABLE X3DH_SPK( \ - SPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ - SPK BLOB NOT NULL, \ - timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ - Status INTEGER NOT NULL DEFAULT 1, \ - Uid INTEGER NOT NULL, \ - FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; - - /* One time pre-key : deleted after usage, generated at user creation and on X3DH server request - * - OPKid : the primary key must be a random number as it is public, so avoid leaking information on number of key used - * - OPK : Public key||Private Key (ECDH keys) - * - Uid : User Id from lime_LocalUsers table: who's key is this - * - Status : a boolean: is likely to be present on X3DH Server(1), not anymore on X3DH server(0), by default any newly inserted key is set to 1 - * - timeStamp : timeStamp is set during update if we found out a key is no more on server(and we didn't used it as usage delete key). - * So after a limbo period, key is considered missing in action and removed from storage. - */ - sql<<"CREATE TABLE X3DH_OPK( \ - OPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ - OPK BLOB NOT NULL, \ - Uid INTEGER NOT NULL, \ - Status INTEGER NOT NULL DEFAULT 1, \ - timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ - FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; - - tr.commit(); // commit all the previous queries }; /** @@ -290,20 +304,6 @@ } /** - * @brief Get a list of deviceIds of all local users present in localStorage - * - * @param[out] deviceIds the list of all local users (their device Id) - */ -void Db::get_allLocalDevices(std::vector<std::string> &deviceIds) { - std::lock_guard<std::recursive_mutex> lock(*m_db_mutex); - deviceIds.clear(); - rowset<row> rs = (sql.prepare << "SELECT UserId FROM lime_LocalUsers;"); - for (const auto &r : rs) { - deviceIds.push_back(r.get<std::string>(0)); - } -} - -/** * @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 @@ -558,6 +558,33 @@ } /** + * @brief checks if a device needs to be updated + * return true if the device exists and updateTs is older than OPk_updatePeriod + * + * @param[in] deviceId The device Id + * + * @return true the updateTs is older than OPk_updatePeriod, false otherwise + */ +bool Db::is_updateRequested(const std::string &deviceId) { + std::lock_guard<std::recursive_mutex> lock(*m_db_mutex); + int count = 0; + sql<<"SELECT count(*) FROM Lime_LocalUsers WHERE UserId = :deviceId AND updateTs < date('now', '-"<<lime::settings::OPk_updatePeriod<<" seconds') LIMIT 1;", into(count), use(deviceId); + return sql.got_data() && count > 0; +} + +/** + * @brief update the update timestamp to now() + * + * @param[in] deviceId The device Id + * + */ +void Db::set_updateTs(const std::string &deviceId) { + std::lock_guard<std::recursive_mutex> lock(*m_db_mutex); + sql<<"UPDATE Lime_LocalUsers SET updateTs = CURRENT_TIMESTAMP WHERE UserId = :deviceID", use(deviceId); +} + + +/** * @brief delete a peerDevice from local storage * * @param[in] peerDeviceId The device Id to be removed from local storage, shall be its GRUU @@ -1014,7 +1041,7 @@ // set the inactive user bit on, user is not active until X3DH server's confirmation int curveId = lime::settings::DBInactiveUserBit | static_cast<uint16_t>(Curve::curveId()); - m_localStorage->sql<<"INSERT INTO lime_LocalUsers(UserId,Ik,server,curveId) VALUES (:userId,:Ik,:server,:curveId) ", use(m_selfDeviceId), use(Ik), use(m_X3DH_Server_URL), use(curveId); + m_localStorage->sql<<"INSERT INTO lime_LocalUsers(UserId,Ik,server,curveId,updateTs) VALUES (:userId,:Ik,:server,:curveId, CURRENT_TIMESTAMP) ", use(m_selfDeviceId), use(Ik), use(m_X3DH_Server_URL), use(curveId); } catch (exception const &e) { tr.rollback(); throw BCTBX_EXCEPTION << "Lime user insertion failed. DB backend says: "<<e.what(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_localStorage.hpp new/lime-5.2.73/src/lime_localStorage.hpp --- old/lime-5.2.49/src/lime_localStorage.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_localStorage.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -53,7 +53,8 @@ void delete_LimeUser(const std::string &deviceId); void clean_DRSessions(); void clean_SPk(); - void get_allLocalDevices(std::vector<std::string> &deviceIds); + bool is_updateRequested(const std::string &deviceId); + void set_updateTs(const std::string &deviceId); void set_peerDeviceStatus(const std::string &peerDeviceId, const std::vector<uint8_t> &Ik, lime::PeerDeviceStatus status); void set_peerDeviceStatus(const std::string &peerDeviceId, lime::PeerDeviceStatus status); lime::PeerDeviceStatus get_peerDeviceStatus(const std::string &peerDeviceId); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_manager.cpp new/lime-5.2.73/src/lime_manager.cpp --- old/lime-5.2.49/src/lime_manager.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_manager.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -30,11 +30,11 @@ namespace lime { LimeManager::LimeManager(const std::string &db_access, const limeX3DHServerPostData &X3DH_post_data, std::shared_ptr<std::recursive_mutex> db_mutex) - : m_users_cache{}, m_db_access{db_access}, m_db_mutex{db_mutex}, m_X3DH_post_data{X3DH_post_data} { } + : m_users_cache{}, m_localStorage{std::make_shared<lime::Db>(db_access, db_mutex)}, m_X3DH_post_data{X3DH_post_data} { } // When no mutex is provided for database access, create one LimeManager::LimeManager(const std::string &db_access, const limeX3DHServerPostData &X3DH_post_data) - : m_users_cache{}, m_db_access{db_access}, m_db_mutex{std::make_shared<std::recursive_mutex>()}, m_X3DH_post_data{X3DH_post_data} { } + : m_users_cache{}, m_localStorage{std::make_shared<lime::Db>(db_access, std::make_shared<std::recursive_mutex>())}, m_X3DH_post_data{X3DH_post_data} { } void LimeManager::load_user(std::shared_ptr<LimeGeneric> &user, const std::string &localDeviceId, const bool allStatus) { // get the Lime manager lock @@ -42,7 +42,7 @@ // Load user object auto userElem = m_users_cache.find(localDeviceId); if (userElem == m_users_cache.end()) { // not in cache, load it from DB - user = load_LimeUser(m_db_access, localDeviceId, m_X3DH_post_data, m_db_mutex, allStatus); + user = load_LimeUser(m_localStorage, localDeviceId, m_X3DH_post_data, allStatus); m_users_cache[localDeviceId]=user; } else { user = userElem->second; @@ -65,9 +65,7 @@ // 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, thiz->m_db_mutex)); - - localStorage->delete_LimeUser(localDeviceId); + thiz->m_localStorage->delete_LimeUser(localDeviceId); // Failure can occur only on X3DH server response(local failure generate an exception so we would never // arrive in this callback)), so the lock acquired by create_user has already expired when we arrive here @@ -77,7 +75,7 @@ }); std::lock_guard<std::mutex> lock(m_users_mutex); - m_users_cache.insert({localDeviceId, insert_LimeUser(m_db_access, localDeviceId, x3dhServerUrl, curve, OPkInitialBatchSize, m_X3DH_post_data, managerCreateCallback, m_db_mutex)}); + m_users_cache.insert({localDeviceId, insert_LimeUser(m_localStorage, localDeviceId, x3dhServerUrl, curve, OPkInitialBatchSize, m_X3DH_post_data, managerCreateCallback)}); } void LimeManager::delete_user(const std::string &localDeviceId, const limeCallback &callback) { @@ -145,52 +143,50 @@ /* This version use default settings */ - void LimeManager::update(const limeCallback &callback) { - update(callback, lime::settings::OPk_serverLowLimit, lime::settings::OPk_batchSize); + void LimeManager::update(const std::string &localDeviceId, const limeCallback &callback) { + update(localDeviceId, callback, lime::settings::OPk_serverLowLimit, lime::settings::OPk_batchSize); } - void LimeManager::update(const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize) { - // open local DB - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access, m_db_mutex)); - - /* DR sessions and old stale SPk cleaning */ - localStorage->clean_DRSessions(); - localStorage->clean_SPk(); - - // get all users from localStorage - std::vector<std::string> deviceIds{}; - localStorage->get_allLocalDevices(deviceIds); - - //This counter will trace number of callbacks, to trace how many operation did end. - // we expect two callback per local user account: one for update SPk, one for get OPk number on server - auto callbackCount = make_shared<size_t>(deviceIds.size()*2); + void LimeManager::update(const std::string &localDeviceId, const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize) { + // Check if the last update was performed more than OPk_updatePeriod seconds ago + if (!m_localStorage->is_updateRequested(localDeviceId)) { + if (callback) callback(lime::CallbackReturn::success, "No update needed"); + return; + } + + LIME_LOGI<<"Update user "<<localDeviceId; + + /* DR sessions and old stale SPk cleaning - This cleaning is performed for all local users as it is easier this way */ + m_localStorage->clean_DRSessions(); + m_localStorage->clean_SPk(); + + // Load user object + std::shared_ptr<LimeGeneric> user; + LimeManager::load_user(user, localDeviceId); + + // we expect two callback: one for update SPk, one for get OPk number on server + auto callbackCount = make_shared<size_t>(2); auto globalReturnCode = make_shared<lime::CallbackReturn>(lime::CallbackReturn::success); + auto localStorage = m_localStorage; // this callback will get all callbacks from update OPk and SPk on all users, when everyone is done, call the callback given to LimeManager::update - limeCallback managerUpdateCallback([callbackCount, globalReturnCode, callback](lime::CallbackReturn returnCode, std::string errorMessage) { + limeCallback managerUpdateCallback([callbackCount, globalReturnCode, callback, localStorage, localDeviceId](lime::CallbackReturn returnCode, std::string errorMessage) { (*callbackCount)--; if (returnCode == lime::CallbackReturn::fail) { *globalReturnCode = lime::CallbackReturn::fail; // if one fail, return fail at the end of it } if (*callbackCount == 0) { + // update the timestamp + localStorage->set_updateTs(localDeviceId); if (callback) callback(*globalReturnCode, ""); } }); + // send a request to X3DH server to check how many OPk are left on server, upload more if needed + user->update_OPk(managerUpdateCallback, OPkServerLowLimit, OPkBatchSize); - // for each user - for (auto deviceId : deviceIds) { - LIME_LOGI<<"Lime update user "<<deviceId; - //load user - std::shared_ptr<LimeGeneric> user; - LimeManager::load_user(user, deviceId); - - // send a request to X3DH server to check how many OPk are left on server, upload more if needed - user->update_OPk(managerUpdateCallback, OPkServerLowLimit, OPkBatchSize); - - // update the SPk(if needed) - user->update_SPk(managerUpdateCallback); - } + // update the SPk(if needed) + user->update_SPk(managerUpdateCallback); } void LimeManager::get_selfIdentityKey(const std::string &localDeviceId, std::vector<uint8_t> &Ik) { @@ -201,38 +197,23 @@ } 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, m_db_mutex)); - - localStorage->set_peerDeviceStatus(peerDeviceId, Ik, status); + m_localStorage->set_peerDeviceStatus(peerDeviceId, Ik, status); } void LimeManager::set_peerDeviceStatus(const std::string &peerDeviceId, lime::PeerDeviceStatus status) { - // open local DB - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access, m_db_mutex)); - - localStorage->set_peerDeviceStatus(peerDeviceId, status); + m_localStorage->set_peerDeviceStatus(peerDeviceId, status); } 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, m_db_mutex)); - - return localStorage->get_peerDeviceStatus(peerDeviceId); + return m_localStorage->get_peerDeviceStatus(peerDeviceId); } lime::PeerDeviceStatus LimeManager::get_peerDeviceStatus(const std::list<std::string> &peerDeviceIds) { - // open local DB - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access, m_db_mutex)); - - return localStorage->get_peerDeviceStatus(peerDeviceIds); + return m_localStorage->get_peerDeviceStatus(peerDeviceIds); } bool LimeManager::is_localUser(const std::string &deviceId) { - // open local DB - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access, m_db_mutex)); - - return localStorage->is_localUser(deviceId); + return m_localStorage->is_localUser(deviceId); } void LimeManager::delete_peerDevice(const std::string &peerDeviceId) { @@ -242,10 +223,7 @@ userElem.second->delete_peerDevice(peerDeviceId); } - // open local DB - auto localStorage = std::unique_ptr<lime::Db>(new lime::Db(m_db_access, m_db_mutex)); - - localStorage->delete_peerDevice(peerDeviceId); + m_localStorage->delete_peerDevice(peerDeviceId); } void LimeManager::stale_sessions(const std::string &localDeviceId, const std::string &peerDeviceId) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/src/lime_settings.hpp new/lime-5.2.73/src/lime_settings.hpp --- old/lime-5.2.49/src/lime_settings.hpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/src/lime_settings.hpp 2023-05-10 07:45:32.000000000 +0200 @@ -20,6 +20,7 @@ #ifndef lime_settings_hpp #define lime_settings_hpp + namespace lime { /** @brief Hold constants definition used as settings in all components of the lime library * @@ -46,12 +47,12 @@ static_assert(DRSessionSharedADSize<64, "Shared AD is generated through HKDF-Sha512 with only one round implemented so its size can't be more than Sha512 max output size"); /** Maximum number of Message we can skip(and store their keys) at reception of one message */ - constexpr std::uint16_t maxMessageSkip=1024; + constexpr uint16_t maxMessageSkip=1024; /** after a message key is stored, count how many messages we can receive from peer before deleting the key at next update * @note: implemented by receiving key chain, so any new skipped message in a chain will reset the counter to 0 */ - constexpr std::uint16_t maxMessagesReceivedAfterSkip = 128; + constexpr uint16_t maxMessagesReceivedAfterSkip = 128; /** @brief Maximum length of Sending chain * @@ -59,7 +60,7 @@ * the DR session is set to stale and we must create another one to send messages * Can't be more than 2^16 as message number is send on 2 bytes */ - constexpr std::uint16_t maxSendingChain=1000; + constexpr uint16_t maxSendingChain=1000; /** Lifetime of a session once not active anymore, unit is day */ constexpr unsigned int DRSession_limboTime_days=30; @@ -83,6 +84,8 @@ constexpr uint16_t OPk_serverLowLimit = 100; /// in days, How long shall we keep an OPk in localStorage once we've noticed X3DH server dispatched it constexpr unsigned int OPk_limboTime_days=SPK_lifeTime_days+SPK_limboTime_days; + /// in seconds, how often should we perform an update (check if we should publish new OPk, cleaning DB routine etc...) + constexpr unsigned int OPk_updatePeriod=86400; // 1 day } // namespace settings Binary files old/lime-5.2.49/tester/data/pattern_getSelfIk.C25519.sqlite3 and new/lime-5.2.73/tester/data/pattern_getSelfIk.C25519.sqlite3 differ Binary files old/lime-5.2.49/tester/data/pattern_getSelfIk.C448.sqlite3 and new/lime-5.2.73/tester/data/pattern_getSelfIk.C448.sqlite3 differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/tester/lime-tester-utils.cpp new/lime-5.2.73/tester/lime-tester-utils.cpp --- old/lime-5.2.49/tester/lime-tester-utils.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/tester/lime-tester-utils.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -502,11 +502,13 @@ */ void forwardTime(const std::string &dbFilename, int days) noexcept { try { + LIME_LOGI<<"Set timestamps back by "<<days<<" days"; soci::session sql("sqlite3", dbFilename); // open the DB - /* move back by days all timeStamp, we have some in DR_sessions and X3DH_SPk tables */ + /* move back by days all timeStamp, we have some in DR_sessions, X3DH_SPk and LocalUsers tables */ sql<<"UPDATE DR_sessions SET timeStamp = date (timeStamp, '-"<<days<<" day');"; sql<<"UPDATE X3DH_SPK SET timeStamp = date (timeStamp, '-"<<days<<" day');"; sql<<"UPDATE X3DH_OPK SET timeStamp = date (timeStamp, '-"<<days<<" day');"; + sql<<"UPDATE Lime_LocalUsers SET updateTs = date (updateTs, '-"<<days<<" day');"; } catch (exception &e) { // swallow any error on DB LIME_LOGE<<"Got an error forwarding time in DB: "<<e.what(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/tester/lime_ffi-tester.c new/lime-5.2.73/tester/lime_ffi-tester.c --- old/lime-5.2.49/tester/lime_ffi-tester.c 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/tester/lime_ffi-tester.c 2023-05-10 07:45:32.000000000 +0200 @@ -453,18 +453,16 @@ /* Around once a day the update function shall be called on LimeManagers * it will perform localStorage cleanings * update of cryptographic material (Signed Pre-key and One-time Pre-keys) + * if called more often than once a day it is ignored * The update take as optionnal parameters : * - lower bound for One-time Pre-key available on server * - One-time Pre-key batch size to be generated and uploaded if lower limit on server is reached * - * Important : Avoid calling this function when connection to network is impossible - * try to first fetch any available message on server, process anything and then update - * * This update shall have no effect as Alice still have ffi_defaultInitialOPkBatchSize keys on X3DH server */ - lime_ffi_update(aliceManager, statusCallback, NULL, ffi_defaultInitialOPkBatchSize, 3); /* if less than ffi_defaultInitialOPkBatchSize keys are availables on server, upload a batch of 3, typical values shall be higher. */ + lime_ffi_update(aliceManager, aliceDeviceId, statusCallback, NULL, ffi_defaultInitialOPkBatchSize, 3); /* if less than ffi_defaultInitialOPkBatchSize keys are availables on server, upload a batch of 3, typical values shall be higher. */ /* That one instead shall upload 3 new OPks to server as we used one of Bob's keys */ - lime_ffi_update(bobManager, statusCallback, NULL, ffi_defaultInitialOPkBatchSize, 3); /* if less than ffi_defaultInitialOPkBatchSize keys are availables on server, upload a batch of 3, typical values shall be higher. */ + lime_ffi_update(bobManager, aliceDeviceId, statusCallback, NULL, ffi_defaultInitialOPkBatchSize, 3); /* if less than ffi_defaultInitialOPkBatchSize keys are availables on server, upload a batch of 3, typical values shall be higher. */ /* wait for updates to complete */ expected_success += 2; BC_ASSERT_TRUE(wait_for(stack, &success_counter, expected_success, ffi_wait_for_timeout)); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/tester/lime_helloworld-tester.cpp new/lime-5.2.73/tester/lime_helloworld-tester.cpp --- old/lime-5.2.49/tester/lime_helloworld-tester.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/tester/lime_helloworld-tester.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -341,11 +341,9 @@ // The update take as optionnal parameters : // - lower bound for One-time Pre-key available on server // - One-time Pre-key batch size to be generated and uploaded if lower limit on server is reached - // - // Important : Avoid calling this function when connection to network is impossible - // try to first fetch any available message on server, process anything and then update - aliceManager->update(callback, 10, 3); // if less than 10 keys are availables on server, upload a batch of 3, typical values shall be higher. - bobManager->update(callback); // use default values for the limit and batch size + // If called more often than once a day, it just does nothing + aliceManager->update(*aliceDeviceId, callback, 10, 3); // if less than 10 keys are availables on server, upload a batch of 3, typical values shall be higher. + bobManager->update(*bobDeviceId, callback); // use default values for the limit and batch size expected_success+=2; /******* end of Users maintenance ****************************/ // wait for updates to complete @@ -585,8 +583,8 @@ // // Important : Avoid calling this function when connection to network is impossible // try to first fetch any available message on server, process anything and then update - aliceManager->update(callback, 10, 3); // if less than 10 keys are availables on server, upload a batch of 3, typical values shall be higher. - bobManager->update(callback); // use default values for the limit and batch size + aliceManager->update(*aliceDeviceId, callback, 10, 3); // if less than 10 keys are availables on server, upload a batch of 3, typical values shall be higher. + bobManager->update(*bobDeviceId, callback); // use default values for the limit and batch size expected_success+=2; /******* end of Users maintenance ****************************/ // wait for updates to complete diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/tester/lime_lime-tester.cpp new/lime-5.2.73/tester/lime_lime-tester.cpp --- old/lime-5.2.49/tester/lime_lime-tester.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/tester/lime_lime-tester.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -208,7 +208,6 @@ }); try { - auto bobCount=0; for (auto batches=0; batches<batch_number; batches++) { for (auto i=0; i<batch_size; i++) { auto patternIndex = messageCount % lime_tester::messages_pattern.size(); @@ -242,7 +241,6 @@ BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout)); // bob decrypt - bobCount++; std::vector<uint8_t> receivedMessage{}; // in that context we cannot know the expected decrypt return value, just check it is not fail BC_ASSERT_TRUE(bobManager->decrypt(*bobDeviceId, "bob", *aliceDeviceId, (*aliceRecipients)[0].DRmessage, *aliceCipherMessage, receivedMessage) != lime::PeerDeviceStatus::fail); @@ -1374,6 +1372,117 @@ #endif } +static void lime_db_migration() { + // Write a db version 1 + std::string dbFilename("lime_db_migration-v1.sqlite3"); + remove(dbFilename.data()); + soci::session sql; + try{ + sql.open("sqlite3", dbFilename); + sql<<"PRAGMA foreign_keys = ON;"; // make sure this connection enable foreign keys + soci::transaction tr(sql); + // CREATE OR IGNORE TABLE db_module_version( + sql<<"CREATE TABLE IF NOT EXISTS db_module_version(" + "name VARCHAR(16) PRIMARY KEY," + "version UNSIGNED INTEGER NOT NULL" + ")"; + auto dbVersion = 1; + sql<<"INSERT INTO db_module_version(name,version) VALUES('lime',:DbVersion)", soci::use(dbVersion); + sql<<"CREATE TABLE DR_sessions( \ + Did INTEGER NOT NULL DEFAULT 0, \ + Uid INTEGER NOT NULL DEFAULT 0, \ + sessionId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + Ns UNSIGNED INTEGER NOT NULL, \ + Nr UNSIGNED INTEGER NOT NULL, \ + PN UNSIGNED INTEGER NOT NULL, \ + DHr BLOB NOT NULL, \ + DHs BLOB NOT NULL, \ + RK BLOB NOT NULL, \ + CKs BLOB NOT NULL, \ + CKr BLOB NOT NULL, \ + AD BLOB NOT NULL, \ + Status INTEGER NOT NULL DEFAULT 1, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + X3DHInit BLOB DEFAULT NULL, \ + FOREIGN KEY(Did) REFERENCES lime_PeerDevices(Did) ON UPDATE CASCADE ON DELETE CASCADE, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + sql<<"CREATE TABLE DR_MSk_DHr( \ + DHid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + sessionId INTEGER NOT NULL DEFAULT 0, \ + DHr BLOB NOT NULL, \ + received UNSIGNED INTEGER NOT NULL DEFAULT 0, \ + FOREIGN KEY(sessionId) REFERENCES DR_sessions(sessionId) ON UPDATE CASCADE ON DELETE CASCADE);"; + sql<<"CREATE TABLE DR_MSk_MK( \ + DHid INTEGER NOT NULL, \ + Nr INTEGER NOT NULL, \ + MK BLOB NOT NULL, \ + PRIMARY KEY( DHid , Nr ), \ + FOREIGN KEY(DHid) REFERENCES DR_MSk_DHr(DHid) ON UPDATE CASCADE ON DELETE CASCADE);"; + sql<<"CREATE TABLE lime_LocalUsers( \ + Uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + UserId TEXT NOT NULL, \ + Ik BLOB NOT NULL, \ + server TEXT NOT NULL, \ + curveId INTEGER NOT NULL DEFAULT 0);"; + sql<<"CREATE TABLE lime_PeerDevices( \ + Did INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, \ + DeviceId TEXT NOT NULL, \ + Ik BLOB NOT NULL, \ + Status UNSIGNED INTEGER DEFAULT 0);"; + sql<<"CREATE TABLE X3DH_SPK( \ + SPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ + SPK BLOB NOT NULL, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + Status INTEGER NOT NULL DEFAULT 1, \ + Uid INTEGER NOT NULL, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + sql<<"CREATE TABLE X3DH_OPK( \ + OPKid UNSIGNED INTEGER PRIMARY KEY NOT NULL, \ + OPK BLOB NOT NULL, \ + Uid INTEGER NOT NULL, \ + Status INTEGER NOT NULL DEFAULT 1, \ + timeStamp DATETIME DEFAULT CURRENT_TIMESTAMP, \ + FOREIGN KEY(Uid) REFERENCES lime_LocalUsers(Uid) ON UPDATE CASCADE ON DELETE CASCADE);"; + // Insert a dummy row in the table modified by the migration as some operation are permitted over empty tables but not ones with data + sql<<"INSERT INTO lime_LocalUsers(UserId, Ik, server, curveId) VALUES ('sip:notauser', '0x1234556', 'http://notalimeserver.com', 1);"; + + tr.commit(); // commit all the previous queries + sql.close(); + } catch (BctbxException &e) { + LIME_LOGE << e; + BC_FAIL("Can't create test version 1 DB"); + return; + } + + // Open a manager giving the same DB, it shall migrate the structure to version 2 + try { + // create Manager + std::unique_ptr<LimeManager> manager = std::make_unique<LimeManager>(dbFilename, X3DHServerPost); + } catch (BctbxException &e) { + LIME_LOGE << e; + BC_FAIL("Can't open manager to perform DB migration"); + return; + } + + // Version 2 of db added a Timestamp + try { + sql.open("sqlite3", dbFilename); + int userVersion=-1; + sql<<"SELECT version FROM db_module_version WHERE name='lime'", soci::into(userVersion); + BC_ASSERT_EQUAL(userVersion, 0x100, int, "%d"); + int haveTs=0; + sql<<"SELECT COUNT(*) FROM pragma_table_info('lime_LocalUsers') WHERE name='updateTs'", soci::into(haveTs); + BC_ASSERT_EQUAL(haveTs, 1, int, "%d"); + } catch (BctbxException &e) { + LIME_LOGE << e; + BC_FAIL("Can't check DB migration done"); + return; + } + if (cleanDatabase) { + remove(dbFilename.data()); + } +} + /** * Scenario: * - Create a user alice @@ -1417,7 +1526,7 @@ // call the update, set the serverLimit to initialBatch size and upload an other initial batch if needed // As all the keys are still on server, it shall have no effect, check it then - aliceManager->update(callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDeviceId, callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), lime_tester::OPkInitialBatchSize, int, "%d"); @@ -1452,7 +1561,8 @@ // call the update, set the serverLimit to initialBatch size and upload an other initial batch if needed // As some keys were removed from server this time we shall generate and upload a new batch - aliceManager->update(callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); + lime_tester::forwardTime(dbFilenameAlice, 2); // Forward time by 2 days so the update actually do something + aliceManager->update(*aliceDeviceId, callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // we uploaded a new batch but no key were removed from localStorage so we now have 2*batch size keys BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 2*lime_tester::OPkInitialBatchSize, int, "%d"); @@ -1475,7 +1585,7 @@ BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 2*lime_tester::OPkInitialBatchSize - 1, int, "%d"); // call the update, set the serverLimit to 0, we don't want to upload more keys, but too old unused local OPk dispatched by server long ago shall be deleted - aliceManager->update(callback, 0, 0); + aliceManager->update(*aliceDeviceId, callback, 0, 0); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // check the local OPk missing on server for a long time has been deleted @@ -1594,7 +1704,7 @@ aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, X3DHServerPost)); // call the update, it shall create and upload a new SPk but keep the old ones - aliceManager->update(callback, 0, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDeviceId, callback, 0, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); SPkExpectedCount++; @@ -1634,7 +1744,7 @@ aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, X3DHServerPost)); // call the update, it shall create and upload a new SPk but keep the old ones and delete one - aliceManager->update(callback, 0, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDeviceId, callback, 0, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // there shall not be any rise in the number of SPk keys found in DB, check that SPkCount=0; @@ -1749,8 +1859,9 @@ /* update belle-sip stack processing possible incoming messages from server */ belle_sip_stack_sleep(bc_stack,0); - /* call the update function */ - bobManager->update(callback, 0, lime_tester::OPkInitialBatchSize); + /* call the update function, forwardtime by 2 days before or the update is skipped */ + lime_tester::forwardTime(dbFilenameBob, 2); + bobManager->update(*bobDeviceId, callback, 0, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success, lime_tester::wait_for_timeout)); /* Check that bob got 0 message key in local Storage */ @@ -1839,9 +1950,10 @@ BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // Start a new manager using the backuped local base, so the user is present in local but no more on remote aliceManager = nullptr; + lime_tester::forwardTime(dbFilenameAliceBackup, 2); // forward time otherwise the update won't do anything aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAliceBackup, X3DHServerPost)); // Update: that shall set all current OPk as dispatched, create a new batch of default creation size and republish the user on server - aliceManager->update(callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDeviceId, callback, lime_tester::OPkInitialBatchSize, lime_tester::OPkInitialBatchSize); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // Bob encrypt a message to Alice, it will fetch keys from server auto bobRecipients = make_shared<std::vector<RecipientData>>(); @@ -2152,6 +2264,7 @@ limeCallback callback([&counters](lime::CallbackReturn returnCode, std::string anythingToSay) { if (returnCode == lime::CallbackReturn::success) { + LIME_LOGI<<"Lime operation success : "<<anythingToSay; counters.operation_success++; } else { counters.operation_failed++; @@ -2275,8 +2388,8 @@ BC_ASSERT_EQUAL((int)bobSessionsId.size(), 2, int, "%d"); // run the update function - aliceManager->update(callback, 0, lime_tester::OPkInitialBatchSize); - bobManager->update(callback, 0, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDevice1, callback, 0, lime_tester::OPkInitialBatchSize); + bobManager->update(*bobDevice1, callback, 0, lime_tester::OPkInitialBatchSize); expected_success+=2; BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,expected_success,lime_tester::wait_for_timeout)); @@ -2303,8 +2416,8 @@ bobManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameBob, X3DHServerPost)); // run the update function - aliceManager->update(callback, 0, lime_tester::OPkInitialBatchSize); - bobManager->update(callback, 0, lime_tester::OPkInitialBatchSize); + aliceManager->update(*aliceDevice1, callback, 0, lime_tester::OPkInitialBatchSize); + bobManager->update(*bobDevice1, callback, 0, lime_tester::OPkInitialBatchSize); expected_success+=2; BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,expected_success,lime_tester::wait_for_timeout)); @@ -3328,9 +3441,6 @@ auto Manager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, X3DHServerPost)); auto aliceDeviceName = lime_tester::makeRandomDeviceName("alice."); - // a mutex to feed internal functions - auto m_mutex = std::make_shared<std::recursive_mutex>(); - try { /*Check if Alice exists in the database */ BC_ASSERT_FALSE(Manager->is_user(*aliceDeviceName)); @@ -3340,13 +3450,9 @@ BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout)); if (counters.operation_failed == 1) return; // skip the end of the test if we can't do this - /*Check if Alice exists in the database */ + /*Check if Alice exists in the database, it will also load it */ BC_ASSERT_TRUE(Manager->is_user(*aliceDeviceName)); - /* load alice from from DB */ - auto alice = load_LimeUser(dbFilenameAlice, *aliceDeviceName, X3DHServerPost, m_mutex); - /* no need to wait here, it shall load alice immediately */ - // Get alice x3dh server url BC_ASSERT_TRUE(Manager->get_x3dhServerUrl(*aliceDeviceName) == x3dh_server_url); @@ -3368,7 +3474,7 @@ 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, lime_tester::OPkInitialBatchSize, X3DHServerPost, callback, m_mutex); + Manager->create_user(*aliceDeviceName, x3dh_server_url, curve, lime_tester::OPkInitialBatchSize, callback); /* no need to wait here, it must fail immediately */ } catch (BctbxException &) { gotExpectedException = true; @@ -3379,17 +3485,12 @@ return; } - /* 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", X3DHServerPost, m_mutex); - /* no need to wait here, it must fail immediately */ - } catch (BctbxException &) { - gotExpectedException = true; - } - if (!gotExpectedException) { - // we didn't got any exception on trying to load bob from DB - BC_FAIL("No exception arised when loading inexistent user from DB"); + /* Check a non existent user is not found */ + BC_ASSERT_FALSE(Manager->is_user("bob")); + } catch (BctbxException &e) { + LIME_LOGE << e; + BC_FAIL(""); return; } @@ -3423,7 +3524,7 @@ /* Create Alice again */ try { - std::shared_ptr<LimeGeneric> alice = insert_LimeUser(dbFilenameAlice, *aliceDeviceName, x3dh_server_url, curve, lime_tester::OPkInitialBatchSize, X3DHServerPost, callback, m_mutex); + Manager->create_user(*aliceDeviceName, x3dh_server_url, curve, lime_tester::OPkInitialBatchSize, callback); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout)); // create another manager with a fresh DB @@ -3876,7 +3977,7 @@ // wait for a random period betwwen 25 and 100 ms std::this_thread::sleep_for(std::chrono::milliseconds(dis(gen))); // Update - thread_arg.manager->update(callback, serverLimit, batchSize); + thread_arg.manager->update(thread_arg.userlist[thread_arg.userIndex], callback, serverLimit, batchSize); expected_success++; serverLimit+=rnd_serverLimit(gen); // improver server limit by a random 0 to 4 // make sure we process possible message incoming from X3DH server @@ -3959,7 +4060,7 @@ } activeThreads.clear(); - // encrypt, three encryotion threads per user + // encrypt, three encryption threads per user for (auto i=0; i<3; i++) { for (const auto &arg : devArg) { activeThreads.emplace_back(lime_multithread_encrypt_thread, arg); @@ -4151,6 +4252,7 @@ TEST_NO_TAG("Identity theft", lime_identity_theft), TEST_NO_TAG("Multithread", lime_multithread), TEST_NO_TAG("Session cancel", lime_session_cancel), + TEST_NO_TAG("DB Migration", lime_db_migration) }; test_suite_t lime_lime_test_suite = { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-5.2.49/tester/lime_server-tester.cpp new/lime-5.2.73/tester/lime_server-tester.cpp --- old/lime-5.2.49/tester/lime_server-tester.cpp 2022-11-09 23:33:57.000000000 +0100 +++ new/lime-5.2.73/tester/lime_server-tester.cpp 2023-05-10 07:45:32.000000000 +0200 @@ -152,7 +152,7 @@ }); try { // create Manager and device for alice - auto aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, X3DHServerPost)); + auto aliceManager = std::make_unique<LimeManager>(dbFilenameAlice, X3DHServerPost); auto aliceDeviceId = lime_tester::makeRandomDeviceName("alice."); aliceManager->create_user(*aliceDeviceId, x3dh_server_url, curve, 500, callback); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_failed, ++expected_failure,lime_tester::wait_for_timeout)); @@ -162,29 +162,38 @@ BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); // call the update, set the serverLimit 200 and upload an other 100 - aliceManager->update(callback, 200, 100); + aliceManager=nullptr; // destroy manager before modifying DB + lime_tester::forwardTime(dbFilenameAlice, 2); + aliceManager = std::make_unique<LimeManager>(dbFilenameAlice, X3DHServerPost); + aliceManager->update(*aliceDeviceId, callback, 200, 100); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 200, int, "%d"); // call the update, set the serverLimit 300 and upload an other 100 -> it shall fail but we have 300 OPks in DB - aliceManager->update(callback, 300, 100); + aliceManager=nullptr; // destroy manager before modifying DB + lime_tester::forwardTime(dbFilenameAlice, 2); + aliceManager = std::make_unique<LimeManager>(dbFilenameAlice, X3DHServerPost); + aliceManager->update(*aliceDeviceId, callback, 300, 100); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_failed, ++expected_failure,lime_tester::wait_for_timeout)); BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 300, int, "%d"); // update again, with correct values, server already holds 200 keys, so the only effect would be to set the failed 100 OPks status to dispatched as they are not on server - aliceManager->update(callback, 125, 25); + aliceManager=nullptr; // destroy manager before modifying DB + lime_tester::forwardTime(dbFilenameAlice, 2); + aliceManager = std::make_unique<LimeManager>(dbFilenameAlice, X3DHServerPost); + aliceManager->update(*aliceDeviceId, callback, 125, 25); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 300, int, "%d"); // forward time by OPK_limboTime_days aliceManager=nullptr; // destroy manager before modifying DB lime_tester::forwardTime(dbFilenameAlice, lime::settings::OPk_limboTime_days+1); - aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilenameAlice, X3DHServerPost)); + aliceManager = std::make_unique<LimeManager>(dbFilenameAlice, X3DHServerPost); // update one last time, with correct values, server already holds 200 keys // so the only effect would be to remove the OPk keys status was set to dispatch before we forward the time // We now hold 200 keys - aliceManager->update(callback, 125, 25); + aliceManager->update(*aliceDeviceId, callback, 125, 25); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success, ++expected_success,lime_tester::wait_for_timeout)); BC_ASSERT_EQUAL((int)lime_tester::get_OPks(dbFilenameAlice, *aliceDeviceId), 200, int, "%d"); @@ -455,7 +464,8 @@ aliceManager->encrypt(*aliceDeviceId2, make_shared<const std::string>("friends"), recipients, message, cipherMessage, callback); BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout)); receivedMessage.clear(); - BC_ASSERT_TRUE(aliceManager->decrypt(*aliceDeviceId, "friends", *aliceDeviceId2, (*recipients)[0].DRmessage, *cipherMessage, receivedMessage) == lime::PeerDeviceStatus::untrusted); + // alice2 is a local device for alice, so it is trusted + BC_ASSERT_TRUE(aliceManager->decrypt(*aliceDeviceId, "friends", *aliceDeviceId2, (*recipients)[0].DRmessage, *cipherMessage, receivedMessage) == lime::PeerDeviceStatus::trusted); receivedMessageString = std::string{receivedMessage.begin(), receivedMessage.end()}; BC_ASSERT_TRUE(receivedMessageString == lime_tester::messages_pattern[2*i+1]); ++++++ set_current_version.patch ++++++ --- /var/tmp/diff_new_pack.Ipl6ou/_old 2023-06-29 17:28:44.094402208 +0200 +++ /var/tmp/diff_new_pack.Ipl6ou/_new 2023-06-29 17:28:44.098402233 +0200 @@ -5,7 +5,7 @@ endif() -project(lime VERSION 5.2.0 LANGUAGES ${LANGUAGES_LIST}) -+project(lime VERSION 5.2.49 LANGUAGES ${LANGUAGES_LIST}) ++project(lime VERSION 5.2.73 LANGUAGES ${LANGUAGES_LIST}) set(LIME_SO_VERSION "0") set(LIME_VERSION ${PROJECT_VERSION})