Hello community,
here is the log from the commit of package lime for openSUSE:Factory checked in at 2020-08-03 14:14:46
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/lime (Old)
and /work/SRC/openSUSE:Factory/.lime.new.3592 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "lime"
Mon Aug 3 14:14:46 2020 rev:2 rq:823434 version:4.4.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/lime/lime.changes 2020-06-10 00:41:37.541904845 +0200
+++ /work/SRC/openSUSE:Factory/.lime.new.3592/lime.changes 2020-08-03 14:15:33.740509696 +0200
@@ -1,0 +2,6 @@
+Wed Jul 29 14:46:36 UTC 2020 - Alexei Sorokin <sor.alexei(a)meowr.ru>
+
+- Update to version 4.4.0:
+ * Manage server resource limit error response.
+
+-------------------------------------------------------------------
Old:
----
lime-4.3.1.tar.bz2
New:
----
lime-4.4.0.tar.bz2
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ lime.spec ++++++
--- /var/tmp/diff_new_pack.CHLHt3/_old 2020-08-03 14:15:35.080511040 +0200
+++ /var/tmp/diff_new_pack.CHLHt3/_new 2020-08-03 14:15:35.084511044 +0200
@@ -18,7 +18,7 @@
%define sover 0
Name: lime
-Version: 4.3.1
+Version: 4.4.0
Release: 0
Summary: Instant Message End-to-End Encryption Library
License: GPL-3.0-or-later
@@ -29,8 +29,8 @@
BuildRequires: pkgconfig
BuildRequires: soci-devel
BuildRequires: soci-sqlite3-devel
-BuildRequires: pkgconfig(bctoolbox) >= 4.3.0
-BuildRequires: pkgconfig(belle-sip) >= 4.3.0
+BuildRequires: pkgconfig(bctoolbox) >= 4.4.0
+BuildRequires: pkgconfig(belle-sip) >= 4.4.0
%description
LIME is an encryption library for one-to-one and group instant
++++++ lime-4.3.1.tar.bz2 -> lime-4.4.0.tar.bz2 ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-4.3.1/CMakeLists.txt new/lime-4.4.0/CMakeLists.txt
--- old/lime-4.3.1/CMakeLists.txt 2019-12-13 16:18:59.000000000 +0100
+++ new/lime-4.4.0/CMakeLists.txt 2020-05-11 19:05:34.000000000 +0200
@@ -46,7 +46,7 @@
set (LANGUAGES_LIST ${LANGUAGES_LIST} Java)
endif()
-project(lime VERSION 4.3.0 LANGUAGES ${LANGUAGES_LIST})
+project(lime VERSION 4.4.0 LANGUAGES ${LANGUAGES_LIST})
set(LIME_SO_VERSION "0")
set(LIME_VERSION ${PROJECT_VERSION})
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-4.3.1/src/lime_x3dh_protocol.cpp new/lime-4.4.0/src/lime_x3dh_protocol.cpp
--- old/lime-4.3.1/src/lime_x3dh_protocol.cpp 2019-12-13 16:18:59.000000000 +0100
+++ new/lime-4.4.0/src/lime_x3dh_protocol.cpp 2020-05-11 19:05:34.000000000 +0200
@@ -110,6 +110,9 @@
user_not_found=0x06,
db_error=0x07,
bad_request=0x08,
+ server_failure=0x09,
+ resource_limit_reached=0x0a,
+ unknown_error_code=0xfe,
unset_error_code=0xff};
/* X3DH protocol messages builds */
/**
@@ -420,6 +423,7 @@
if (body.size()<X3DH_headerSize) {
LIME_LOGE<<"Got an invalid response from X3DH server"<<endl<< message_trace.str()<<endl<<" Invalid Incoming X3DH message";
if (callback) callback(lime::CallbackReturn::fail, "Got an invalid response from X3DH server");
+ LIME_LOGE<<message_trace.str()<<endl;
return false;
}
@@ -427,6 +431,7 @@
if (body[0] != static_cast<uint8_t>(X3DH_protocolVersion)) {
LIME_LOGE<<"X3DH server runs an other version of X3DH protocol(server "<<static_cast<unsigned int>(body[0])<<" - local "<<static_cast<unsigned int>(X3DH_protocolVersion)<<")"<<endl<<message_trace.str()<<endl<<" Invalid Incoming X3DH message";
if (callback) callback(lime::CallbackReturn::fail, "X3DH server and client protocol version mismatch");
+ LIME_LOGE<<message_trace.str()<<endl;
return false;
}
@@ -490,6 +495,7 @@
// retrieve the error code if needed
if (message_type == x3dh_message_type::error) {
if (body.size()<X3DH_headerSize+1) { // error message contains at least 1 byte of error code + possible message
+ LIME_LOGE<<message_trace.str()<<endl;
return false;
}
@@ -527,8 +533,15 @@
case static_cast<uint8_t>(x3dh_error_code::bad_request):
error_code = x3dh_error_code::bad_request;
break;
- default: // unknown error code: invalid packet
- return false;
+ case static_cast<uint8_t>(x3dh_error_code::server_failure):
+ error_code = x3dh_error_code::server_failure;
+ break;
+ case static_cast<uint8_t>(x3dh_error_code::resource_limit_reached):
+ error_code = x3dh_error_code::resource_limit_reached;
+ break;
+ default: // unknown error code - could be an updated server
+ LIME_LOGW<<"Receive unknown error code in X3DH message"<< static_cast<uint8_t>(body[X3DH_headerSize]);
+ error_code = x3dh_error_code::unknown_error_code;
}
}
LIME_LOGD<<message_trace.str()<<endl<<" Valid Incoming X3DH message";
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-4.3.1/tester/lime_lime-tester.cpp new/lime-4.4.0/tester/lime_lime-tester.cpp
--- old/lime-4.3.1/tester/lime_lime-tester.cpp 2019-12-13 16:18:59.000000000 +0100
+++ new/lime-4.4.0/tester/lime_lime-tester.cpp 2020-05-11 19:05:34.000000000 +0200
@@ -3986,6 +3986,89 @@
}
}
+/**
+ * Scenario: Server is set to accept 200 OPks per device
+ * - Create user alice with 500 OPks -> it shall fail
+ * - Create user alice with 100 OPks -> it shall pass
+ * - Add 100 OPks to alice -> it shall pass
+ * - Add 100 OPks to alice -> is shall fail
+ */
+static void lime_server_resource_limit_reached_test(const lime::CurveId curve, const std::string &dbBaseFilename, const std::string &x3dh_server_url) {
+ // create DB
+ std::string dbFilenameAlice{dbBaseFilename};
+ dbFilenameAlice.append(".alice.").append((curve==CurveId::c25519)?"C25519":"C448").append(".sqlite3");
+
+ remove(dbFilenameAlice.data()); // delete the database file if already exists
+
+ lime_tester::events_counters_t counters={};
+ int expected_success=0;
+ int expected_failure=0;
+
+ limeCallback callback([&counters](lime::CallbackReturn returnCode, std::string anythingToSay) {
+ if (returnCode == lime::CallbackReturn::success) {
+ counters.operation_success++;
+ } else {
+ counters.operation_failed++;
+ LIME_LOGE<<"Lime operation failed : "<<anythingToSay;
+ }
+ });
+ try {
+ // create Manager and device for alice
+ auto aliceManager = std::unique_ptr<LimeManager>(new 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));
+
+ // Try again to create the device, with 100 OPks, it shall pass
+ aliceManager->create_user(*aliceDeviceId, x3dh_server_url, curve, 100, callback);
+ 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);
+ 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);
+ 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);
+ 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));
+
+ // 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);
+ 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");
+
+ if (cleanDatabase) {
+ aliceManager->delete_user(*aliceDeviceId, callback);
+ BC_ASSERT_TRUE(lime_tester::wait_for(bc_stack,&counters.operation_success,++expected_success,lime_tester::wait_for_timeout));
+ remove(dbFilenameAlice.data());
+ }
+ } catch (BctbxException &e) {
+ LIME_LOGE << e;
+ BC_FAIL("");
+ }
+}
+
+static void lime_server_resource_limit_reached() {
+#ifdef EC25519_ENABLED
+ lime_server_resource_limit_reached_test(lime::CurveId::c25519, "lime_server_resource_limit_reached", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c25519_server_port).data());
+#endif
+#ifdef EC448_ENABLED
+ lime_server_resource_limit_reached_test(lime::CurveId::c448, "lime_server_resource_limit_reached", std::string("https://").append(lime_tester::test_x3dh_server_url).append(":").append(lime_tester::test_x3dh_c448_server_port).data());
+#endif
+}
static test_t tests[] = {
TEST_NO_TAG("Basic", x3dh_basic),
@@ -4008,7 +4091,8 @@
TEST_NO_TAG("Encryption Policy", lime_encryptionPolicy),
TEST_NO_TAG("Encryption Policy Error", lime_encryptionPolicyError),
TEST_NO_TAG("Identity theft", lime_identity_theft),
- TEST_NO_TAG("Multithread", lime_multithread)
+ TEST_NO_TAG("Multithread", lime_multithread),
+ TEST_NO_TAG("Server resource limit reached", lime_server_resource_limit_reached)
};
test_suite_t lime_lime_test_suite = {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-4.3.1/tester/server/nodejs/package.json new/lime-4.4.0/tester/server/nodejs/package.json
--- old/lime-4.3.1/tester/server/nodejs/package.json 2019-12-13 16:18:59.000000000 +0100
+++ new/lime-4.4.0/tester/server/nodejs/package.json 2020-05-11 19:05:34.000000000 +0200
@@ -5,8 +5,8 @@
"main": "x3dh.js",
"dependencies": {
"rwlock": "^5.0.0",
- "sqlite3": "^4.0.2",
- "yargs": "^9.0.1"
+ "sqlite3": "^4.2.0",
+ "yargs": "^15.3.1"
},
"devDependencies": {},
"scripts": {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lime-4.3.1/tester/server/nodejs/x3dh.js new/lime-4.4.0/tester/server/nodejs/x3dh.js
--- old/lime-4.3.1/tester/server/nodejs/x3dh.js 2019-12-13 16:18:59.000000000 +0100
+++ new/lime-4.4.0/tester/server/nodejs/x3dh.js 2020-05-11 19:05:34.000000000 +0200
@@ -86,6 +86,14 @@
CURVE448 : 2
};
+// Resource abuse protection
+// Setting to 0 disable the function
+//lime_max_device_per_user does not apply to this test server as it is used to test the lime lib only and device id are not gruub in the tests
+// Do not set this value too low, it shall not be lower than the server_low_limit+batch_size used by client - default is 100+25
+const lime_max_opk_per_device = 200;
+
+
+
var curveId = enum_curveId.CURVE25519;
const X3DH_protocolVersion = 0x01;
@@ -114,7 +122,9 @@
user_already_in : 0x05,
user_not_found : 0x06,
db_error : 0x07,
- bad_request : 0x08
+ bad_request : 0x08,
+ server_failure : 0x09,
+ resource_limit_reached : 0x0a
};
const enum_keyBundleFlag = {
@@ -317,6 +327,10 @@
// Now get the OPk count
var bufferIndex = X3DH_headerSize;
var OPk_number = body.readUInt16BE(bufferIndex+x3dh_expectedSize-2);
+ if ((lime_max_opk_per_device > 0) && (OPk_number > lime_max_opk_per_device)) { // too much OPk, reject registration
+ returnError(enum_errorCodes.resource_limit_reached, userId+" is trying to register itself with "+OPk_number+" OPks but server has a limit of "+lime_max_opk_per_device);
+ return;
+ }
// And check again the size with OPks
x3dh_expectedSize += OPk_number*(keySizes[curveId]['X_pub'] + 4);
if (body.length<X3DH_headerSize + x3dh_expectedSize) {
@@ -482,6 +496,11 @@
console.log("It contains "+OPk_number+" keys");
+ if ((lime_max_opk_per_device > 0) && (OPk_number > lime_max_opk_per_device)) { // too much OPk, reject registration
+ returnError(enum_errorCodes.resource_limit_reached, userId+" is trying to insert "+OPk_number+" OPks but server has a limit of "+lime_max_opk_per_device);
+ return;
+ }
+
// check we have a matching user in DB
lock.writeLock(function (release) {
db.get("SELECT Uid FROM Users WHERE UserId = ?;", userId , function (err, row) {
@@ -490,43 +509,107 @@
returnError(enum_errorCodes.user_not_found, "Post OPks but "+userId+" not found in db");
} else {
var Uid = row['Uid'];
- // parse all OPks to be inserted
- var OPks_param = [];
- for (let i = 0; i < OPk_number; i++) {
- var OPk = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['X_pub']);
- bufferIndex += keySizes[curveId]['X_pub'];
- var OPk_id = body.readUInt32BE(bufferIndex); // SPk id is a 32 bits unsigned integer in Big endian
- bufferIndex += 4;
- OPks_param.push([Uid, OPk, OPk_id]);
- }
- // bulk insert of OPks
- var stmt_ran = 0;
- var stmt_success = 0;
- var stmt_err_message = '';
- db.run("begin transaction");
- var stmt = db.prepare("INSERT INTO OPk(Uid, OPk, OPk_id) VALUES(?,?,?);")
- for (let i=0; i<OPks_param.length; i++) {
- param = OPks_param[i];
- stmt.run(param, function(stmt_err, stmt_res){ // callback is called for each insertion, so we shall count errors and success
- stmt_ran++;
- if (stmt_err) {
- stmt_err_message +=' ## '+stmt_err;
- } else {
- stmt_success++;
+ // Check we won't get over the maximum OPks allowed
+ if (lime_max_opk_per_device > 0) {
+ db.get("SELECT COUNT(OPK_id) as OPk_count FROM Users as u INNER JOIN OPk as o ON u.Uid=o.Uid WHERE UserId = ?;", userId, function (err, row) {
+ if (err) {
+ release();
+ returnError(enum_errorCodes.db_error, " Database error in postOPks by "+userId+" : "+err2);
+ return;
}
- if (stmt_ran == OPk_number) { // and react only when we reach correct count
- if (stmt_ran != stmt_success) {
- db.run("rollback")
- release();
- returnError(enum_errorCodes.db_error, "Error while trying to insert OPk for user "+userId+". Backend says :"+stmt_err);
- } else {
- db.run("commit")
- release();
- returnOk(returnHeader);
- }
+
+ var OPk_count = 0;
+ if (row != undefined) {
+ OPk_count = row['OPk_count'];
+ }
+
+ if (OPk_count+OPk_number > lime_max_opk_per_device) { // too much OPks
+ release();
+ returnError(enum_errorCodes.resource_limit_reached, userId+" is trying to insert "+OPk_number+" OPks but server has a limit of "+lime_max_opk_per_device+" and it already holds "+OPk_count);
+ return;
}
+
+ // parse all OPks to be inserted
+ var OPks_param = [];
+ for (let i = 0; i < OPk_number; i++) {
+ var OPk = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['X_pub']);
+ bufferIndex += keySizes[curveId]['X_pub'];
+ var OPk_id = body.readUInt32BE(bufferIndex); // OPk id is a 32 bits unsigned integer in Big endian
+ bufferIndex += 4;
+ OPks_param.push([Uid, OPk, OPk_id]);
+ }
+
+ // bulk insert of OPks
+ var stmt_ran = 0;
+ var stmt_success = 0;
+ var stmt_err_message = '';
+ db.run("begin transaction");
+ var stmt = db.prepare("INSERT INTO OPk(Uid, OPk, OPk_id) VALUES(?,?,?);")
+ for (let i=0; i<OPks_param.length; i++) {
+ param = OPks_param[i];
+ stmt.run(param, function(stmt_err, stmt_res){ // callback is called for each insertion, so we shall count errors and success
+ stmt_ran++;
+ if (stmt_err) {
+ stmt_err_message +=' ## '+stmt_err;
+ } else {
+ stmt_success++;
+ }
+ if (stmt_ran == OPk_number) { // and react only when we reach correct count
+ if (stmt_ran != stmt_success) {
+ db.run("rollback")
+ release();
+ returnError(enum_errorCodes.db_error, "Error while trying to insert OPk for user "+userId+". Backend says :"+stmt_err);
+ } else {
+ db.run("commit")
+ release();
+ returnOk(returnHeader);
+ }
+ }
+ });
+ }
+ return;
});
+ } else { // Note : this code shall be factorized
+
+ // parse all OPks to be inserted
+ var OPks_param = [];
+ for (let i = 0; i < OPk_number; i++) {
+ var OPk = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['X_pub']);
+ bufferIndex += keySizes[curveId]['X_pub'];
+ var OPk_id = body.readUInt32BE(bufferIndex); // OPk id is a 32 bits unsigned integer in Big endian
+ bufferIndex += 4;
+ OPks_param.push([Uid, OPk, OPk_id]);
+ }
+
+ // bulk insert of OPks
+ var stmt_ran = 0;
+ var stmt_success = 0;
+ var stmt_err_message = '';
+ db.run("begin transaction");
+ var stmt = db.prepare("INSERT INTO OPk(Uid, OPk, OPk_id) VALUES(?,?,?);")
+ for (let i=0; i<OPks_param.length; i++) {
+ param = OPks_param[i];
+ stmt.run(param, function(stmt_err, stmt_res){ // callback is called for each insertion, so we shall count errors and success
+ stmt_ran++;
+ if (stmt_err) {
+ stmt_err_message +=' ## '+stmt_err;
+ } else {
+ stmt_success++;
+ }
+ if (stmt_ran == OPk_number) { // and react only when we reach correct count
+ if (stmt_ran != stmt_success) {
+ db.run("rollback")
+ release();
+ returnError(enum_errorCodes.db_error, "Error while trying to insert OPk for user "+userId+". Backend says :"+stmt_err);
+ } else {
+ db.run("commit")
+ release();
+ returnOk(returnHeader);
+ }
+ }
+ });
+ }
}
}
});
@@ -680,7 +763,7 @@
case enum_messageTypes.getSelfOPks:
console.log("Process a getSelfOPks Message from "+userId);
// check we have a matching user in DB
- db.get("SELECT Uid FROM Users WHERE UserId = ?;", userId , function (errU, row) {
+ db.get("SELECT Uid FROM Users WHERE UserId = ?;", userId , function (err, row) {
if (row == undefined) { // user not found in DB
returnError(enum_errorCodes.user_not_found, "Get Self OPks but "+userId+" not found in db");
} else {