Commit c0844d40 authored by johan's avatar johan Committed by johan
Browse files

register user command is now atomic

user is activated when command is successful
TODO: try to reactivate at user_create when local is not activated
but present in local storage
parent 1de646b9
......@@ -91,10 +91,21 @@ namespace lime {
/****************************************************************************/
template <typename Curve>
void Lime<Curve>::publish_user(const limeCallback &callback, const uint16_t OPkInitialBatchSize) {
auto userData = make_shared<callbackUserData<Curve>>(this->shared_from_this(), callback, OPkInitialBatchSize, true);
auto userData = make_shared<callbackUserData<Curve>>(this->shared_from_this(), callback, OPkInitialBatchSize);
get_SelfIdentityKey(); // make sure our Ik is loaded in object
/* Generate the SPk */
X<Curve, lime::Xtype::publicKey> SPk{};
DSA<Curve, lime::DSAtype::signature> SPk_sig{};
uint32_t SPk_id=0;
X3DH_generate_SPk(SPk, SPk_sig, SPk_id);
// Generate the OPks
std::vector<X<Curve, lime::Xtype::publicKey>> OPks{};
std::vector<uint32_t> OPk_ids{};
X3DH_generate_OPks(OPks, OPk_ids, OPkInitialBatchSize);
// Build and post the message to server
std::vector<uint8_t> X3DHmessage{};
x3dh_protocol::buildMessage_registerUser<Curve>(X3DHmessage, m_Ik.publicKey());
x3dh_protocol::buildMessage_registerUser<Curve>(X3DHmessage, m_Ik.publicKey(), SPk, SPk_sig, SPk_id, OPks, OPk_ids);
postToX3DHServer(userData, X3DHmessage);
}
......@@ -448,7 +459,7 @@ namespace lime {
case lime::CurveId::unset :
default: // asking for an unsupported type
throw BCTBX_EXCEPTION << "Cannot create load user "<<deviceId;//<<". Unsupported curve (id <<"static_cast<uint8_t>(curve)") requested";
throw BCTBX_EXCEPTION << "Cannot create load user "<<deviceId;
break;
}
} catch (BctbxException &) {
......
......@@ -74,6 +74,8 @@ namespace settings {
* current version is 0.0.1
*/
constexpr int DBuserVersion=0x000001;
constexpr uint16_t DBInactiveUserBit = 0x0100;
constexpr uint16_t DBCurveIdByte = 0x00FF;
/******************************************************************************/
/* */
......
......@@ -72,6 +72,8 @@ namespace lime {
/* database related functions, implementation is in lime_localStorage.cpp */
// create user in DB, throw an exception if already there or something went wrong
bool create_user();
// Once X3DH server confirms user registration, active it locally
bool activate_user();
// user load from DB is implemented directly as a Db member function, output of it is passed to Lime<> ctor
void get_SelfIdentityKey(); // check our Identity key pair is loaded in Lime object, retrieve it from DB if it isn't
void cache_DR_sessions(std::vector<RecipientInfos<Curve>> &internal_recipients, std::vector<std::string> &missing_devices); // loop on internal recipient an try to load in DR session cache the one which have no session attached
......@@ -126,8 +128,6 @@ namespace lime {
std::shared_ptr<const std::vector<uint8_t>> plainMessage;
/// ciphertext buffer. Needed for encryption: get a shared ref to keep params alive
std::shared_ptr<std::vector<uint8_t>> cipherMessage;
/// used to run a simple state machine at user creation to perform sequence of packet sending: registerUser, postSPk, postOPks
lime::network_state network_state_machine;
/// the encryption policy from the original encryption request(if running an encryption request), copy its value instead of holding a shared_ptr on it
lime::EncryptionPolicy encryptionPolicy;
/// Used when fetching from server self OPk to check if we shall upload more
......@@ -136,15 +136,15 @@ namespace lime {
uint16_t OPkBatchSize;
/// created at user create/delete and keys Post. EncryptionPolicy is not used, set it to the default value anyway
callbackUserData(std::weak_ptr<Lime<Curve>> thiz, const limeCallback &callbackRef, uint16_t OPkInitialBatchSize=lime::settings::OPk_initialBatchSize, bool startRegisterUserSequence=false)
callbackUserData(std::weak_ptr<Lime<Curve>> thiz, const limeCallback &callbackRef, uint16_t OPkInitialBatchSize=lime::settings::OPk_initialBatchSize)
: limeObj{thiz}, callback{callbackRef},
recipientUserId{nullptr}, recipients{nullptr}, plainMessage{nullptr}, cipherMessage{nullptr}, network_state_machine{startRegisterUserSequence?lime::network_state::sendSPk:lime::network_state::done},
recipientUserId{nullptr}, recipients{nullptr}, plainMessage{nullptr}, cipherMessage{nullptr},
encryptionPolicy(lime::EncryptionPolicy::optimizeUploadSize), OPkServerLowLimit(0), OPkBatchSize(OPkInitialBatchSize) {};
/// created at update: getSelfOPks. EncryptionPolicy is not used, set it to the default value anyway
callbackUserData(std::weak_ptr<Lime<Curve>> thiz, const limeCallback &callbackRef, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize)
: limeObj{thiz}, callback{callbackRef},
recipientUserId{nullptr}, recipients{nullptr}, plainMessage{nullptr}, cipherMessage{nullptr}, network_state_machine{lime::network_state::done},
recipientUserId{nullptr}, recipients{nullptr}, plainMessage{nullptr}, cipherMessage{nullptr},
encryptionPolicy(lime::EncryptionPolicy::optimizeUploadSize), OPkServerLowLimit{OPkServerLowLimit}, OPkBatchSize{OPkBatchSize} {};
/// created at encrypt(getPeerBundle)
......@@ -153,7 +153,7 @@ namespace lime {
std::shared_ptr<const std::vector<uint8_t>> plainMessage, std::shared_ptr<std::vector<uint8_t>> cipherMessage,
lime::EncryptionPolicy policy)
: limeObj{thiz}, callback{callbackRef},
recipientUserId{recipientUserId}, recipients{recipients}, plainMessage{plainMessage}, cipherMessage{cipherMessage}, network_state_machine {lime::network_state::done}, // copy construct all shared_ptr
recipientUserId{recipientUserId}, recipients{recipients}, plainMessage{plainMessage}, cipherMessage{cipherMessage}, // copy construct all shared_ptr
encryptionPolicy(policy), OPkServerLowLimit(0), OPkBatchSize(0) {};
/// do not copy callback data, force passing the pointer around after creation
......
......@@ -148,7 +148,10 @@ Db::Db(std::string filename) : sql{"sqlite3", filename}{
* - 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
* - 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, \
......@@ -223,11 +226,17 @@ Db::Db(std::string filename) : sql{"sqlite3", filename}{
void Db::load_LimeUser(const std::string &deviceId, long int &Uid, lime::CurveId &curveId, std::string &url)
{
int curve=0;
sql<<"SELECT Uid,CurveId,server FROM lime_LocalUsers WHERE UserId = :userId LIMIT 1;", into(Uid), into(curve), into(url), use(deviceId);
sql<<"SELECT Uid,curveId,server FROM lime_LocalUsers WHERE UserId = :userId LIMIT 1;", into(Uid), into(curve), into(url), use(deviceId);
if (sql.got_data()) { // we found someone
// Check if the user has been activated
if (curve&lime::settings::DBInactiveUserBit) { // user is inactive
Uid = 0; // be sure to reset the db_Uid to 0
throw BCTBX_EXCEPTION << "Lime User "<<deviceId<<" is in DB but has not been activated yet, call create_user again to try to activate";
}
// turn back integer value retrieved from DB into a lime::CurveId
switch (curve) {
switch (curve&lime::settings::DBCurveIdByte) {
case static_cast<uint8_t>(lime::CurveId::c25519):
curveId=lime::CurveId::c25519;
break;
......@@ -781,8 +790,9 @@ bool DR<Curve>::trySkippedMessageKeys(const uint16_t Nr, const X<Curve, lime::Xt
/******************************************************************************/
/**
* @brief Create a new local user based on its userId(GRUU) from table lime_LocalUsers
* The user will be activated only after being published successfully on X3DH server
*
* use m_selfDeviceId as input, use m_X3DH_Server as input
* use m_selfDeviceId as input
* populate m_db_Uid
*
* @return true if user was created successfully, exception is thrown otherwise.
......@@ -793,9 +803,11 @@ template <typename Curve>
bool Lime<Curve>::create_user()
{
// check if the user is not already in the DB
int dummy_Uid;
m_localStorage->sql<<"SELECT Uid FROM lime_LocalUsers WHERE UserId = :userId LIMIT 1;", into(dummy_Uid), use(m_selfDeviceId);
int Uid;
m_localStorage->sql<<"SELECT Uid,curveId FROM lime_LocalUsers WHERE UserId = :userId LIMIT 1;", into(Uid), use(m_selfDeviceId);
if (m_localStorage->sql.got_data()) {
// TODO: if already there but not active, try to publish it.
throw BCTBX_EXCEPTION << "Lime user "<<m_selfDeviceId<<" cannot be created: it is already in Database - delete it before if you really want to replace it";
}
......@@ -816,7 +828,8 @@ bool Lime<Curve>::create_user()
// insert in DB
try {
// Don't create stack variable in the method call directly
uint8_t curveId = static_cast<int8_t>(Curve::curveId());
// 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);
} catch (exception const &e) {
......@@ -841,6 +854,46 @@ bool Lime<Curve>::create_user()
return true;
}
/**
* @brief Create a new local user based on its userId(GRUU) from table lime_LocalUsers
* The user will be activated only after being published successfully on X3DH server
*
* use m_selfDeviceId as input
* populate m_db_Uid
*
* @return true if user was activated successfully.
*
* @exception BCTBX_EXCEPTION thrown if user is not found in base
*/
template <typename Curve>
bool Lime<Curve>::activate_user() {
// check if the user is the DB
int Uid = 0;
int curveId = 0;
m_localStorage->sql<<"SELECT Uid,curveId FROM lime_LocalUsers WHERE UserId = :userId LIMIT 1;", into(Uid), into(curveId), use(m_selfDeviceId);
if (!m_localStorage->sql.got_data()) {
throw BCTBX_EXCEPTION << "Lime user "<<m_selfDeviceId<<" cannot be activated, it is not present in local storage";
}
transaction tr(m_localStorage->sql);
// update in DB
try {
// Don't create stack variable in the method call directly
uint8_t curveId = static_cast<int8_t>(Curve::curveId());
m_localStorage->sql<<"UPDATE lime_LocalUsers SET curveId = :curveId WHERE Uid = :Uid;", use(curveId), use(Uid);
} catch (exception const &e) {
tr.rollback();
throw BCTBX_EXCEPTION << "Lime user activation failed. DB backend says: "<<e.what();
}
m_db_Uid = Uid;
tr.commit();
return true;
}
template <typename Curve>
void Lime<Curve>::get_SelfIdentityKey() {
if (m_Ik_loaded == false) {
......@@ -1145,6 +1198,7 @@ void Lime<Curve>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds) {
/* template instanciations for Curves 25519 and 448 */
#ifdef EC25519_ENABLED
template bool Lime<C255>::create_user();
template bool Lime<C255>::activate_user();
template void Lime<C255>::get_SelfIdentityKey();
template void Lime<C255>::X3DH_generate_SPk(X<C255, lime::Xtype::publicKey> &publicSPk, DSA<C255, DSAtype::signature> &SPk_sig, uint32_t &SPk_id);
template void Lime<C255>::X3DH_generate_OPks(std::vector<X<C255, lime::Xtype::publicKey>> &publicOPks, std::vector<uint32_t> &OPk_ids, const uint16_t OPk_number);
......@@ -1158,6 +1212,7 @@ void Lime<Curve>::X3DH_updateOPkStatus(const std::vector<uint32_t> &OPkIds) {
#ifdef EC448_ENABLED
template bool Lime<C448>::create_user();
template bool Lime<C448>::activate_user();
template void Lime<C448>::get_SelfIdentityKey();
template void Lime<C448>::X3DH_generate_SPk(X<C448, lime::Xtype::publicKey> &publicSPk, DSA<C448, DSAtype::signature> &SPk_sig, uint32_t &SPk_id);
template void Lime<C448>::X3DH_generate_OPks(std::vector<X<C448, lime::Xtype::publicKey>> &publicOPks, std::vector<uint32_t> &OPk_ids, const uint16_t OPk_number);
......
......@@ -42,7 +42,13 @@ namespace lime {
* except for getPeerBundle which shall be answered with a peerBundle message
*
* Message types description :
* - registerUser : Identity Key<EDDSA Public Key length>
* - registerUser : Identity Key<EDDSA Public Key length>\n
* SPk< ECDH Public key length > ||\n
* SPk Signature< Signature Length > ||\n
* SPk Id < 4 bytes>\n
* OPk Keys Count<2 bytes unsigned integer Big endian> ||\n
* ( OPk< ECDH Public key length > || OPk Id <4 bytes>){Keys Count}
*
* - deleteUser : empty message, user to delete is retrieved from header From
*
* - postSPk : SPk< ECDH Public key length > ||
......@@ -79,7 +85,7 @@ namespace lime {
* @brief the x3dh message type exchanged with the X3DH server
* @note Do not change the mapped values as they must be synced with X3DH server definition
*/
enum class x3dh_message_type : uint8_t{ registerUser=0x01,
enum class x3dh_message_type : uint8_t{ deprecated_registerUser=0x01, // The usage of this value is deprecated, but kept in the define so it is not recycled.
deleteUser=0x02,
postSPk=0x03,
postOPks=0x04,
......@@ -87,6 +93,7 @@ namespace lime {
peerBundle=0x06,
getSelfOPks=0x07,
selfOPks=0x08,
registerUser=0x09,
error=0xff};
/**
......@@ -113,6 +120,8 @@ namespace lime {
*/
static std::string x3dh_messageTypeString(const x3dh_message_type message_type) {
switch (message_type) {
case x3dh_message_type::deprecated_registerUser :
return "deprecated_registerUser";
case x3dh_message_type::registerUser :
return "registerUser";
case x3dh_message_type::deleteUser :
......@@ -159,11 +168,29 @@ namespace lime {
* @param[in] Ik Self public identity key (formatted for signature algorithm)
*/
template <typename Curve>
void buildMessage_registerUser(std::vector<uint8_t> &message, const DSA<Curve, lime::DSAtype::publicKey> &Ik) noexcept {
void buildMessage_registerUser(std::vector<uint8_t> &message, const DSA<Curve, lime::DSAtype::publicKey> &Ik, const X<Curve, lime::Xtype::publicKey> &SPk, const DSA<Curve, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<Curve, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept {
// create the header
message = X3DH_makeHeader(x3dh_message_type::registerUser, Curve::curveId());
// append the Ik
message.insert(message.end(), Ik.cbegin(), Ik.cend());
// append SPk, Signature and SPkId
message.insert(message.end(), SPk.cbegin(), SPk.cend());
message.insert(message.end(), Sig.cbegin(), Sig.cend());
message.push_back(static_cast<uint8_t>((SPk_id>>24)&0xFF));
message.push_back(static_cast<uint8_t>((SPk_id>>16)&0xFF));
message.push_back(static_cast<uint8_t>((SPk_id>>8)&0xFF));
message.push_back(static_cast<uint8_t>((SPk_id)&0xFF));
// check we do not try to upload more than 2^16 OPks as the counter is on 2 bytes
auto OPkCount = OPks.size();
if (OPkCount > 0xFFFF) {
OPkCount = 0xFFFF;
LIME_LOGW << "Trying to publish "<<static_cast<unsigned int>(OPks.size())<<" OPks wich is more than the maximum allowed. Actually publish the first 2^16 and discard the rest";
}
// append OPks number and a sequence of OPk || OPk_id
message.push_back(static_cast<uint8_t>(((OPkCount)>>8)&0xFF));
message.push_back(static_cast<uint8_t>((OPkCount)&0xFF));
// debug trace
ostringstream message_trace;
......@@ -171,6 +198,33 @@ namespace lime {
std::for_each(Ik.cbegin(), Ik.cend(), [&message_trace] (unsigned int i) {
message_trace << setw(2) << i << ", ";
});
message_trace <<endl<<" SPk:";
std::for_each(SPk.cbegin(), SPk.cend(), [&message_trace] (unsigned int i) {
message_trace << setw(2) << i << ", ";
});
message_trace << endl <<" SPk Signature:";
std::for_each(Sig.cbegin(), Sig.cend(), [&message_trace] (unsigned int i) {
message_trace << setw(2) << i << ", ";
});
message_trace << endl <<" SPk Id: 0x"<< setw(8) << static_cast<unsigned int>(SPk_id);
message_trace << endl << dec << setfill('0') << " " << static_cast<unsigned int>(OPkCount)<<" OPks."<< hex;
for (decltype(OPkCount) i=0; i<OPkCount; i++) {
message.insert(message.end(), OPks[i].cbegin(), OPks[i].cend());
message.push_back(static_cast<uint8_t>((OPk_ids[i]>>24)&0xFF));
message.push_back(static_cast<uint8_t>((OPk_ids[i]>>16)&0xFF));
message.push_back(static_cast<uint8_t>((OPk_ids[i]>>8)&0xFF));
message.push_back(static_cast<uint8_t>((OPk_ids[i])&0xFF));
// debug trace
message_trace << endl <<" OPk id: 0x"<< setw(8) << static_cast<unsigned int>(OPk_ids[i]) <<" OPk:";
std::for_each(OPks[i].cbegin(), OPks[i].cend(), [&message_trace] (unsigned int i) {
message_trace << setw(2) << i << ", ";
});
}
LIME_LOGD<<message_trace.str();
}
......@@ -681,7 +735,7 @@ namespace lime {
/* Instanciate templated functions */
#ifdef EC25519_ENABLED
template void buildMessage_registerUser<C255>(std::vector<uint8_t> &message, const DSA<C255, lime::DSAtype::publicKey> &Ik) noexcept;
template void buildMessage_registerUser<C255>(std::vector<uint8_t> &message, const DSA<C255, lime::DSAtype::publicKey> &Ik, const X<C255, lime::Xtype::publicKey> &SPk, const DSA<C255, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<C255, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
template void buildMessage_deleteUser<C255>(std::vector<uint8_t> &message) noexcept;
template void buildMessage_publishSPk<C255>(std::vector<uint8_t> &message, const X<C255, lime::Xtype::publicKey> &SPk, const DSA<C255, lime::DSAtype::signature> &Sig, const uint32_t SPk_id) noexcept;
template void buildMessage_publishOPks<C255>(std::vector<uint8_t> &message, const std::vector<X<C255, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
......@@ -690,7 +744,7 @@ namespace lime {
#endif
#ifdef EC448_ENABLED
template void buildMessage_registerUser<C448>(std::vector<uint8_t> &message, const DSA<C448, lime::DSAtype::publicKey> &Ik) noexcept;
template void buildMessage_registerUser<C448>(std::vector<uint8_t> &message, const DSA<C448, lime::DSAtype::publicKey> &Ik, const X<C448, lime::Xtype::publicKey> &SPk, const DSA<C448, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<C448, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
template void buildMessage_deleteUser<C448>(std::vector<uint8_t> &message) noexcept;
template void buildMessage_publishSPk<C448>(std::vector<uint8_t> &message, const X<C448, lime::Xtype::publicKey> &SPk, const DSA<C448, lime::DSAtype::signature> &Sig, const uint32_t SPk_id) noexcept;
template void buildMessage_publishOPks<C448>(std::vector<uint8_t> &message, const std::vector<X<C448, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
......@@ -745,53 +799,24 @@ namespace lime {
}
switch (message_type) {
// for registerUser, deleteUser, postSPk and postOPks, on success, server will respond with an identical header
// but we cannot get from server getPeerBundle or getSelfOPks message
case x3dh_protocol::x3dh_message_type::getPeerBundle:
case x3dh_protocol::x3dh_message_type::getSelfOPks: {
if (callback) callback(lime::CallbackReturn::fail, "X3DH unexpected message from server");
cleanUserData(userData);
}
return;
case x3dh_protocol::x3dh_message_type::registerUser: {
// server response to a registerUser, we shall have the network state machine set to next action: sendSPk, just check it
if (userData->network_state_machine == lime::network_state::sendSPk) {
userData->network_state_machine = lime::network_state::sendOPk;
// generate and publish the SPk
X<Curve, lime::Xtype::publicKey> SPk{};
DSA<Curve, lime::DSAtype::signature> SPk_sig{};
uint32_t SPk_id=0;
X3DH_generate_SPk(SPk, SPk_sig, SPk_id);
std::vector<uint8_t> X3DHmessage{};
x3dh_protocol::buildMessage_publishSPk(X3DHmessage, SPk, SPk_sig, SPk_id);
postToX3DHServer(userData, X3DHmessage);
} else { // after registering a user, we must post SPk and OPks
if (callback) callback(lime::CallbackReturn::fail, "Internal Error: we registered a new user on X3DH server but do not plan to post SPk or OPks");
// server response to a registerUser
// activate the local user
try {
activate_user();
} catch (BctbxException const &e) {
LIME_LOGE<<"Cannot activate user "<< m_selfDeviceId << ". Backend says: "<< e.str();
if (callback) callback(lime::CallbackReturn::fail, std::string{"Cannot activate user : "}.append(e.str()));
cleanUserData(userData);
}
}
return;
case x3dh_protocol::x3dh_message_type::postSPk: {
// server response to a post SPk, if we are at user creation, the state machine is set to next action post OPk, do it
if (userData->network_state_machine == lime::network_state::sendOPk) {
userData->network_state_machine = lime::network_state::done;
// generate and publish the OPks
std::vector<X<Curve, lime::Xtype::publicKey>> OPks{};
std::vector<uint32_t> OPk_ids{};
X3DH_generate_OPks(OPks, OPk_ids, userData->OPkBatchSize);
std::vector<uint8_t> X3DHmessage{};
x3dh_protocol::buildMessage_publishOPks(X3DHmessage, OPks, OPk_ids);
postToX3DHServer(userData, X3DHmessage);
return;
}
}
break;
case x3dh_protocol::x3dh_message_type::postSPk:
case x3dh_protocol::x3dh_message_type::deleteUser:
case x3dh_protocol::x3dh_message_type::postOPks:
// server response to deleteUser or postOPks, nothing to do really
// server response to deleteUser, postSPk or postOPks, nothing to do really
// success callback is the common behavior, performed after the switch
break;
......@@ -873,6 +898,17 @@ namespace lime {
cleanUserData(userData);
}
return;
// for registerUser, deleteUser, postSPk and postOPks, on success, server will respond with an identical header
// but we cannot get from server getPeerBundle or getSelfOPks message
case x3dh_protocol::x3dh_message_type::deprecated_registerUser:
case x3dh_protocol::x3dh_message_type::getPeerBundle:
case x3dh_protocol::x3dh_message_type::getSelfOPks: {
if (callback) callback(lime::CallbackReturn::fail, "X3DH unexpected message from server");
cleanUserData(userData);
}
return;
}
// we get here only if processing is over and response was the expected one
......
......@@ -77,7 +77,7 @@ namespace lime {
namespace x3dh_protocol {
template <typename Curve>
void buildMessage_registerUser(std::vector<uint8_t> &message, const DSA<Curve, lime::DSAtype::publicKey> &Ik) noexcept;
void buildMessage_registerUser(std::vector<uint8_t> &message, const DSA<Curve, lime::DSAtype::publicKey> &Ik, const X<Curve, lime::Xtype::publicKey> &SPk, const DSA<Curve, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<Curve, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
template <typename Curve>
void buildMessage_deleteUser(std::vector<uint8_t> &message) noexcept;
......@@ -96,7 +96,7 @@ namespace lime {
/* this templates are intanciated in lime_x3dh_procotocol.cpp, do not re-instanciate it anywhere else */
#ifdef EC25519_ENABLED
extern template void buildMessage_registerUser<C255>(std::vector<uint8_t> &message, const DSA<C255, lime::DSAtype::publicKey> &Ik) noexcept;
extern template void buildMessage_registerUser<C255>(std::vector<uint8_t> &message, const DSA<C255, lime::DSAtype::publicKey> &Ik, const X<C255, lime::Xtype::publicKey> &SPk, const DSA<C255, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<C255, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
extern template void buildMessage_deleteUser<C255>(std::vector<uint8_t> &message) noexcept;
extern template void buildMessage_publishSPk<C255>(std::vector<uint8_t> &message, const X<C255, lime::Xtype::publicKey> &SPk, const DSA<C255, lime::DSAtype::signature> &Sig, const uint32_t SPk_id) noexcept;
extern template void buildMessage_publishOPks<C255>(std::vector<uint8_t> &message, const std::vector<X<C255, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
......@@ -105,7 +105,7 @@ namespace lime {
#endif
#ifdef EC448_ENABLED
extern template void buildMessage_registerUser<C448>(std::vector<uint8_t> &message, const DSA<C448, lime::DSAtype::publicKey> &Ik) noexcept;
extern template void buildMessage_registerUser<C448>(std::vector<uint8_t> &message, const DSA<C448, lime::DSAtype::publicKey> &Ik, const X<C448, lime::Xtype::publicKey> &SPk, const DSA<C448, lime::DSAtype::signature> &Sig, const uint32_t SPk_id, const std::vector<X<C448, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
extern template void buildMessage_deleteUser<C448>(std::vector<uint8_t> &message) noexcept;
extern template void buildMessage_publishSPk<C448>(std::vector<uint8_t> &message, const X<C448, lime::Xtype::publicKey> &SPk, const DSA<C448, lime::DSAtype::signature> &Sig, const uint32_t SPk_id) noexcept;
extern template void buildMessage_publishOPks<C448>(std::vector<uint8_t> &message, const std::vector<X<C448, lime::Xtype::publicKey>> &OPks, const std::vector<uint32_t> &OPk_ids) noexcept;
......
......@@ -93,7 +93,7 @@ const X3DH_headerSize = 3;
const enum_messageTypes = {
unset_type:0x00,
registerUser:0x01,
deprecated_registerUser:0x01,
deleteUser:0x02,
postSPk:0x03,
postOPks:0x04,
......@@ -101,6 +101,7 @@ const enum_messageTypes = {
peerBundle:0x06,
getSelfOPks:0x07,
selfOPks:0x08,
registerUser:0x09,
error:0xff
};
......@@ -263,9 +264,9 @@ https.createServer(options, (req, res) => {
var returnHeader = Buffer.from([protocolVersion, messageType, message_curveId]); // acknowledge message by sending an empty message with same header (modified in case of getPeerBundle request)
switch (messageType) {
/* Register User Identity Key : Identity Key <EDDSA Public key size >*/
case enum_messageTypes.registerUser:
console.log("Got a registerUser Message from "+userId);
/* Deprecated Register User Identity Key : Identity Key <EDDSA Public key size >*/
case enum_messageTypes.deprecated_registerUser:
console.log("Got a deprecated registerUser Message from "+userId);
var x3dh_expectedSize = keySizes[curveId]['ED_pub'];
if (body.length<X3DH_headerSize + x3dh_expectedSize) {
returnError(enum_errorCodes.bad_size, "Register Identity packet is expexted to be "+(X3DH_headerSize+x3dh_expectedSize)+" bytes, but we got "+body.length+" bytes");
......@@ -297,6 +298,109 @@ https.createServer(options, (req, res) => {
});
break;
/* Register User Identity Key :
* Identity Key <EDDSA Public key size > |
* Signed Pre Key <ECDH public key size> | SPk Signature <Signature length> | SPk id <uint32_t big endian: 4 bytes>
* OPk number < uint16_t big endian: 2 bytes> | (OPk <ECDH public key size> | OPk id <uint32_t big endian: 4 bytes> ){OPk number} */
case enum_messageTypes.registerUser:
console.log("Got a registerUser Message from "+userId);
// check we have at least Ik, SPk, SPk_sig, SPk_Id and OPk_count
var x3dh_expectedSize = keySizes[curveId]['ED_pub'] // Ik
+ keySizes[curveId]['X_pub'] + keySizes[curveId]['Sig'] + 4 //SPk, SPk_Sig, SPk_id
+2; // OPk count
if (body.length<X3DH_headerSize + x3dh_expectedSize) {
returnError(enum_errorCodes.bad_size, "Register User packet is expected to be at least(without OPk) "+(X3DH_headerSize+x3dh_expectedSize)+" bytes, but we got "+body.length+" bytes");
return;
}
// Now get the OPk count
var bufferIndex = X3DH_headerSize;
var OPk_number = body.readUInt16BE(bufferIndex+x3dh_expectedSize-2);
// And check again the size with OPks
x3dh_expectedSize += OPk_number*(keySizes[curveId]['X_pub'] + 4);
if (body.length<X3DH_headerSize + x3dh_expectedSize) {
returnError(enum_errorCodes.bad_size, "Register User packet is expected to be (with "+OPk_number+" OPks) "+(X3DH_headerSize+x3dh_expectedSize)+" bytes, but we got "+body.length+" bytes");
return;
}
// Read Ik
var Ik = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['ED_pub']);
bufferIndex += keySizes[curveId]['ED_pub'];
// SPk
var SPk = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['X_pub']);
bufferIndex += keySizes[curveId]['X_pub'];
// SPk Sig
var Sig = body.slice(bufferIndex, bufferIndex + keySizes[curveId]['Sig']);
bufferIndex += keySizes[curveId]['Sig'];
// SPk Id
var SPk_id = body.readUInt32BE(bufferIndex); // SPk id is a 32 bits unsigned integer in Big endian
bufferIndex += 6; // 4 from SPk_id and 2 from OPk count.
// all OPks
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([OPk, OPk_id]);
}
lock.writeLock(function (release) {
// check it is not already present in DB
db.get("SELECT Uid FROM Users WHERE UserId = ?;", userId , function (err, row) {
if (row != undefined) { // usedId is already present in base
release();
returnError(enum_errorCodes.user_already_in, "Can't insert user "+userId+" - is already present in base");
} else {
db.run("begin transaction");
db.run("INSERT INTO Users(UserId,Ik,SPk,SPk_sig,SPk_id) VALUES(?,?,?,?,?);", [userId, Ik, SPk, Sig, SPk_id], function(errInsert){
if (errInsert == null) {
// Now bulk insert the OPks
var Uid = this.lastID;
var stmt_ran = 0;
var stmt_success = 0;
var stmt_err_message = '';
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([Uid, param[0], param[1]], 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();
if (yargs.lifetime!=0) {
console.log("User inserted will be deleted in "+yargs.lifetime*1000);
setTimeout(deleteUser, yargs.lifetime*1000, userId);
}
returnOk(returnHeader);
}
}
});
}
} else {
release();
console.log("INSERT failed err is "); console.log(errInsert);
returnError(enum_errorCodes.db_error, "Error while trying to insert user "+userId);
}
});
}