Commit e003dc3f authored by johan's avatar johan

Add OPks lifetime management + test on OPk update

+ tweak OPk and SPk id generation to resist bad
sqlite3 interpretation of unsigned int32
parent 757e47ef
......@@ -82,6 +82,7 @@ namespace lime {
void X3DH_get_SPk(uint32_t SPk_id, KeyPair<X<Curve>> &SPk); // retrieve matching SPk from localStorage, throw an exception if not found
bool is_currentSPk_valid(void); // check validity of current SPk
void X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<Curve>> &SPk); // retrieve matching OPk from localStorage, throw an exception if not found
void X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds); // update OPks to tag those not anymore on X3DH server but not used and destroyed yet
/* X3DH related - part related to X3DH DR session initiation, implemented in lime_x3dh.cpp */
void X3DH_init_sender_session(const std::vector<X3DH_peerBundle<Curve>> &peersBundle); // compute a sender X3DH using the data from peer bundle, then create and load the DR_Session
std::shared_ptr<DR<Curve>> X3DH_init_receiver_session(const std::vector<uint8_t> X3DH_initMessage, const std::string &senderDeviceId); // from received X3DH init packet, try to compute the shared secrets, then create the DR_Session
......
......@@ -189,11 +189,16 @@ Db::Db(std::string filename) : sql{sqlite3, filename}{
* - 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: can be published on X3DH Server(1) or not anymore on X3DH server(0), by default any newly inserted key is set to published
* - 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
......@@ -601,10 +606,11 @@ void Lime<Curve>::X3DH_generate_SPk(X<Curve> &publicSPk, Signature<Curve> &SPk_s
auto sig_size=SPk_sig.size();
bctbx_EDDSA_sign(EDDSA_Context, ECDH_Context->selfPublic, ECDH_Context->pointCoordinateLength, nullptr, 0, SPk_sig.data(), &sig_size);
// Generate a random SPk Id
// Generate a random SPk Id: Sqlite doesn't really support unsigned value.
// Be sure the MSbit is set to zero to avoid problem as even if declared unsigned this Id will be treated by sqlite as signed(but still unsigned in this lib)
std::array<uint8_t,4> randomId;
bctbx_rng_get(m_RNG, randomId.data(), randomId.size());
SPk_id = static_cast<uint32_t>(randomId[0])<<24 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
SPk_id = static_cast<uint32_t>(randomId[0])<<23 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
// insert all this in DB
try {
......@@ -655,10 +661,11 @@ void Lime<Curve>::X3DH_generate_OPks(std::vector<X<Curve>> &publicOPks, std::vec
// Generate a new ECDH Key pair
bctbx_ECDHCreateKeyPair(ECDH_Context, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, m_RNG);
// Generate a random SPk Id (uint32_t)
// Generate a random OPk Id: Sqlite doesn't really support unsigned value.
// Be sure the MSbit is set to zero to avoid problem as even if declared unsigned this Id will be treated by sqlite as signed(but still unsigned in this lib)
std::array<uint8_t,4> randomId;
bctbx_rng_get(m_RNG, randomId.data(), randomId.size());
OPk_id = static_cast<uint32_t>(randomId[0])<<24 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
OPk_id = static_cast<uint32_t>(randomId[0])<<23 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
// Insert in DB: store Public Key || Private Key
OPk.write(0, (char *)(ECDH_Context->selfPublic), ECDH_Context->pointCoordinateLength);
......@@ -813,16 +820,43 @@ bool Lime<Curve>::is_currentSPk_valid(void) {
template <typename Curve>
void Lime<Curve>::X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<Curve>> &OPk) {
blob OPk_blob(m_localStorage->sql);
m_localStorage->sql<<"SELECT OPk FROM X3DH_OPk WHERE Uid = :Uid AND OPKid = :OPk_id LIMIT 1;", into(OPk_blob), use(m_db_Uid), use(OPk_id);
m_localStorage->sql<<"SELECT OPk FROM X3DH_OPK WHERE Uid = :Uid AND OPKid = :OPk_id LIMIT 1;", into(OPk_blob), use(m_db_Uid), use(OPk_id);
if (m_localStorage->sql.got_data()) { // Found it, it is stored in one buffer Public || Private
OPk_blob.read(0, (char *)(OPk.publicKey().data()), OPk.publicKey().size()); // Read the public key
OPk_blob.read(OPk.publicKey().size(), (char *)(OPk.privateKey().data()), OPk.privateKey().size()); // Read the private key
m_localStorage->sql<<"DELETE FROM X3DH_OPk WHERE Uid = :Uid AND OPKid = :OPk_id;", use(m_db_Uid), use(OPk_id); // And remove it from local Storage
m_localStorage->sql<<"DELETE FROM X3DH_OPK WHERE Uid = :Uid AND OPKid = :OPk_id;", use(m_db_Uid), use(OPk_id); // And remove it from local Storage
} else {
throw BCTBX_EXCEPTION << "X3DH "<<m_selfDeviceId<<"look up for OPk id "<<OPk_id<<" failed";
}
}
/**
* @brief update OPk Status so we can get an idea of what's on server and what was dispatched but not used yet
* get rid of anyone with status 0 and oldest than OPk_limboTime_days
*
* @param[in] OPkIds List of Ids found on server
*/
template <typename Curve>
void Lime<Curve>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds) {
if (OPkIds.size()>0) { /* we have keys on server */
// build a comma-separated list of OPk id on server
std::string sqlString_OPkIds{""};
for (auto OPkId : OPkIds) {
sqlString_OPkIds.append(to_string(OPkId)).append(",");
}
sqlString_OPkIds.pop_back(); // remove the last ','
// Update Status and timeStamp in DB for keys we own and are not anymore on server
m_localStorage->sql << "UPDATE X3DH_OPK SET Status = 0, timeStamp=CURRENT_TIMESTAMP WHERE Status = 1 AND Uid = :Uid AND OPKid NOT IN ("<<sqlString_OPkIds<<");", use(m_db_Uid);
} else { /* we have no keys on server */
m_localStorage->sql << "UPDATE X3DH_OPK SET Status = 0, timeStamp=CURRENT_TIMESTAMP WHERE Status = 1 AND Uid = :Uid;", use(m_db_Uid);
}
// Delete keys not anymore on server since too long
m_localStorage->sql << "DELETE FROM X3DH_OPK WHERE Uid = :Uid AND Status = 0 AND timeStamp < date('now', '-"<<lime::settings::OPk_limboTime_days<<" day');", use(m_db_Uid);
}
/* template instanciations for Curves 25519 and 448 */
#ifdef EC25519_ENABLED
template bool Lime<C255>::create_user();
......@@ -835,6 +869,7 @@ void Lime<Curve>::X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<Curve>> &OPk) {
template void Lime<C255>::X3DH_get_SPk(uint32_t SPk_id, KeyPair<X<C255>> &SPk);
template bool Lime<C255>::is_currentSPk_valid(void);
template void Lime<C255>::X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<C255>> &SPk);
template void Lime<C255>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
#endif
#ifdef EC448_ENABLED
......@@ -848,6 +883,7 @@ void Lime<Curve>::X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<Curve>> &OPk) {
template void Lime<C448>::X3DH_get_SPk(uint32_t SPk_id, KeyPair<X<C448>> &SPk);
template bool Lime<C448>::is_currentSPk_valid(void);
template void Lime<C448>::X3DH_get_OPk(uint32_t OPk_id, KeyPair<X<C448>> &SPk);
template void Lime<C448>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds);
#endif
......
......@@ -86,6 +86,7 @@ namespace settings {
constexpr uint16_t OPk_batchSize = 25; // default batch size when uploading OPks to X3DH server
constexpr uint16_t OPk_initialBatchSize = 2*OPk_batchSize; // default batch size when creating a new user
constexpr uint16_t OPk_serverLowLimit = 50; // this is a default value but it can be set by parameter to the update function
constexpr unsigned int OPk_limboTime_days=SPK_lifeTime_days+SPK_limboTime_days; // in days, How long shall we keep an OPk in localStorage once we've noticed X3DH server dispatched it
}
}
......
......@@ -548,7 +548,9 @@ namespace lime {
return;
}
// TODO: tag as seen on server all the Ids so one day we can remove the OPk distributed by server but not used
// update in LocalStorage the OPk status: tag removed from server and delete old keys
thiz->X3DH_updateOPkStatus(selfOPkIds);
// Check if we shall upload more packets
if (selfOPkIds.size() < userData->OPkServerLowLimit) {
// generate and publish the OPks
......
......@@ -409,12 +409,29 @@ bool get_SPks(const std::string &dbFilename, const std::string &selfDeviceId, si
} else {
return false;
}
} catch (exception &e) { // swallow any error on DB
BCTBX_SLOGE<<"Got an error while getting the MK count in DB: "<<e.what();
BCTBX_SLOGE<<"Got an error while getting the SPk count in DB: "<<e.what();
count=0;
return false;
}
}
/* For the given deviceId, count the number of associated OPk
*/
size_t get_OPks(const std::string &dbFilename, const std::string &selfDeviceId) noexcept {
try {
soci::session sql(sqlite3, dbFilename); // open the DB
auto count=0;
sql<< "SELECT count(OPKid) FROM X3DH_OPK as o INNER JOIN lime_LocalUsers as u on u.Uid = o.Uid WHERE u.UserId = :selfId;", into(count), use(selfDeviceId);
if (sql.got_data()) {
return count;
} else {
return 0;
}
} catch (exception &e) { // swallow any error on DB
BCTBX_SLOGE<<"Got an error while getting the OPk count in DB: "<<e.what();
return 0;
}
}
......@@ -427,6 +444,7 @@ void forwardTime(const std::string &dbFilename, int days) noexcept {
/* move back by days all timeStamp, we have some in DR_sessions and X3DH_SPk 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');";
} catch (exception &e) { // swallow any error on DB
BCTBX_SLOGE<<"Got an error forwarding time in DB: "<<e.what();
}
......
......@@ -100,6 +100,10 @@ unsigned int get_StoredMessageKeyCount(const std::string &dbFilename, const std:
*/
bool get_SPks(const std::string &dbFilename, const std::string &selfDeviceId, size_t &count, uint32_t &activeId) noexcept;
/* For the given deviceId, count the number of associated OPk
*/
size_t get_OPks(const std::string &dbFilename, const std::string &selfDeviceId) noexcept;
/* Move back in time all timeStamps by the given amout of days
* DB holds timeStamps in DR_sessions and X3DH_SPK tables
*/
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment