Commit 52db2da2 authored by Johan Pascal's avatar Johan Pascal

Add get_selfIdentityKey API

+ code factorisation in lime_manager.cpp
parent 11f48058
......@@ -56,6 +56,7 @@ namespace lime {
std::string m_db_access; // DB access information forwarded to SOCI to correctly access database
belle_http_provider_t *m_http_provider; // used to access the X3DH key server
userAuthenticateCallback m_user_auth; // called to complete user authentication on server
void load_user(std::shared_ptr<LimeGeneric> &user, const std::string &localDeviceId); // helper function, get from m_users_cache of local Storage the requested Lime object
public :
/* LimeManager is mostly a cache of Lime users, any command get as first parameter the device Id (Lime manage devices only, the link user(sip:uri)<->device(GRUU) is provided by upper level) */
......@@ -80,6 +81,7 @@ namespace lime {
/**
* @brief Delete a user from local database and from the X3DH server
* if specified localDeviceId is not found in local Storage, throw an exception
*
* @param[in] localDeviceId Identify the local user acount to use, it must be unique and is also be used as Id on the X3DH key server, it shall be the GRUU
* @param[in] callback This operation contact the X3DH server and is thus asynchronous, when server responds,
......@@ -89,6 +91,8 @@ namespace lime {
/**
* @brief Encrypt a buffer(text or file) for a given list of recipient devices
* if specified localDeviceId is not found in local Storage, throw an exception
*
* Clarification on recipients:
*
* recipients information needed are a list of the device Id and one userId. The device Id shall be their GRUU while the userId is a sip:uri.
......@@ -120,6 +124,7 @@ namespace lime {
/**
* @brief Decrypt the given message
* if specified localDeviceId is not found in local Storage, throw an exception
*
* @param[in] localDeviceId used to identify which local acount to use and also as the recipient device ID of the message, shall be the GRUU
* @param[in] recipientUserId the Id of intended recipient, shall be a sip:uri of user or conference, is used as associated data to ensure no-one can mess with intended recipient
......@@ -151,6 +156,16 @@ namespace lime {
void update(const limeCallback &callback);
void update(const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize);
/**
* @brief retrieve self Identity Key, an EdDSA formatted public key
* if specified localDeviceId is not found in local Storage, throw an exception
*
*
* @param[in] localDeviceId used to identify which local account we're dealing with, shall be the GRUU
* @param[out] Ik the EdDSA public identity key, formatted as in RFC8032
*/
void get_selfIdentityKey(const std::string &localDeviceId, std::vector<uint8_t> &Ik);
LimeManager() = delete; // no manager without Database and http provider
LimeManager(const LimeManager&) = delete; // no copy constructor
LimeManager operator=(const LimeManager &) = delete; // nor copy operator
......
......@@ -108,8 +108,8 @@ namespace lime {
* @brief Publish on X3DH server the user, it is performed just after creation in local storage
* this will, on success, trigger generation and sending of SPk and OPks for our new user
*
* @param[in] initialOPkBatchSize Number of OPks in the first batch uploaded to X3DH server
* @param[in] callback call when completed
* @param[in] initialOPkBatchSize Number of OPks in the first batch uploaded to X3DH server
*/
template <typename Curve>
void Lime<Curve>::publish_user(const limeCallback &callback, const uint16_t OPkInitialBatchSize) {
......@@ -160,6 +160,13 @@ namespace lime {
postToX3DHServer(userData, X3DHmessage); // in the response from server, if more OPks are needed, it will generate and post them before calling the callback
}
template <typename Curve>
void Lime<Curve>::get_Ik(std::vector<uint8_t> &Ik) {
get_SelfIdentityKey(); // make sure our Ik is loaded in object
// copy self Ik to output buffer
Ik.assign(m_Ik.publicKey().begin(), m_Ik.publicKey().end());
}
template <typename Curve>
void Lime<Curve>::encrypt(std::shared_ptr<const std::string> recipientUserId, std::shared_ptr<std::vector<recipientData>> recipients, std::shared_ptr<const std::vector<uint8_t>> plainMessage, std::shared_ptr<std::vector<uint8_t>> cipherMessage, const limeCallback &callback) {
bctbx_debug("encrypt from %s to %ld recipients", m_selfDeviceId.data(), recipients->size());
......
......@@ -129,6 +129,12 @@ namespace lime {
*/
void update_OPk(const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize) override;
/**
* @brief Retrieve self public Identity key
*
* @param[out] Ik the public EdDSA formatted Identity key
*/
virtual void get_Ik(std::vector<uint8_t> &Ik) override;
void encrypt(std::shared_ptr<const std::string> recipientUserId, std::shared_ptr<std::vector<recipientData>> recipients, std::shared_ptr<const std::vector<uint8_t>> plainMessage, std::shared_ptr<std::vector<uint8_t>> cipherMessage, const limeCallback &callback) override;
bool decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &cipherHeader, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) override;
......
......@@ -32,13 +32,98 @@ namespace lime {
class LimeGeneric {
public:
/* Encrypt/Decrypt */
// Encrypt/Decrypt
/**
* @brief Encrypt a buffer(text or file) for a given list of recipient devices
*
* Clarification on recipients:
*
* recipients information needed are a list of the device Id and one userId. The device Id shall be their GRUU while the userId is a sip:uri.
*
* recipient User Id is used to identify the actual intended recipient. Example: alice have two devices and is signed up on a conference having
* bob and claire as other members. The recipientUserId will be the conference sip:uri and device list will include:
* - alice other device
* - bob devices
* - claire devices
* If Alice write to Bob only, the recipientUserId will be bob sip:uri and recipient devices list :
* - alice other device
* - bob devices
*
* In all cases, the identified source of the message will be the localDeviceId
*
* Note: all parameters are shared pointers as the process being asynchronous, the ownership will be taken internally exempting caller to manage the buffers.
*
* @param[in] recipientUserId the Id of intended recipient, shall be a sip:uri of user or conference, is used as associated data to ensure no-one can mess with intended recipient
* @param[in/out] recipients a list of recipientData holding: the recipient device Id(GRUU) and an empty buffer to store the cipherHeader which must then be routed to that recipient
* @param[in] plainMessage a buffer holding the message to encrypt, can be text or data.
* @param[out] cipherMessage points to the buffer to store the encrypted message which must be routed to all recipients
* @param[in] callback This operation 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.
* It is advised to capture a copy of cipherMessage and recipients shared_ptr in this callback so they can access
* the output of encryption as it won't be part of the callback parameters.
*/
virtual void encrypt(std::shared_ptr<const std::string> recipientUserId, std::shared_ptr<std::vector<recipientData>> recipients, std::shared_ptr<const std::vector<uint8_t>> plainMessage, std::shared_ptr<std::vector<uint8_t>> cipherMessage, const limeCallback &callback) = 0;
/**
* @brief Decrypt the given message
*
* @param[in] recipientUserId the Id of intended recipient, shall be a sip:uri of user or conference, is used as associated data to ensure no-one can mess with intended recipient
* it is not necessarily the sip:uri base of the GRUU as this could be a message from alice first device intended to bob being decrypted on alice second device
* @param[in] cipherHeader the part of cipher which is targeted to current device
* @param[in] cipherMessage part of cipher routed to all recipient devices
* @param[out] plainMessage the output buffer
*
* @return true if the decryption is successfull, false otherwise
*/
virtual bool decrypt(const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &cipherHeader, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) = 0;
// User management
/**
* @brief Publish on X3DH server the user, it is performed just after creation in local storage
* this will, on success, trigger generation and sending of SPk and OPks for our new user
*
* @param[in] callback call when completed
* @param[in] initialOPkBatchSize Number of OPks in the first batch uploaded to X3DH server
*/
virtual void publish_user(const limeCallback &callback, const uint16_t OPkInitialBatchSize) = 0;
/**
* @brief Delete user from local Storage and from X3DH server
*
* @param[in] callback call when completed
*/
virtual void delete_user(const limeCallback &callback) = 0;
// User keys management
/**
* @brief Check if the current SPk needs to be updated, if yes, generate a new one and publish it on server
*
* @param[in] callback Called with success or failure when operation is completed.
*/
virtual void update_SPk(const limeCallback &callback) = 0;
/**
* @brief check if we shall upload more OPks on X3DH server
* - ask server four our keys (returns the count and all their Ids)
* - check if it's under the low limit, if yes, generate a batch of keys and upload them
*
* @param[in] callback Called with success or failure when operation is completed.
* @param[in] OPkServerLowLimit If server holds less OPk than this limit, generate and upload a batch of OPks
* @param[in] OPkBatchSize Number of OPks in a batch uploaded to server
*/
virtual void update_OPk(const limeCallback &callback, uint16_t OPkServerLowLimit, uint16_t OPkBatchSize) = 0;
/**
* @brief Retrieve self public Identity key
*
* @param[out] Ik the public EdDSA formatted Identity key
*/
virtual void get_Ik(std::vector<uint8_t> &Ik) = 0;
virtual ~LimeGeneric() {};
};
......
......@@ -31,6 +31,18 @@
using namespace::std;
namespace lime {
void LimeManager::load_user(std::shared_ptr<LimeGeneric> &user, const std::string &localDeviceId) {
// 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_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user;
} else {
user = userElem->second;
}
}
/****************************************************************************/
/* */
/* Lime Manager API */
......@@ -67,29 +79,17 @@ namespace lime {
thiz->m_users_cache.erase(localDeviceId);
});
// is the user load? if no we must load it to be able to delete it(generate an exception if user doesn't exists)
auto userElem = m_users_cache.find(localDeviceId);
// Load user object
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) {
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user; // we must load it in cache otherwise object will be destroyed before getting into callback
} else {
user = userElem->second;
}
LimeManager::load_user(user, localDeviceId);
user->delete_user(managerDeleteCallback);
}
void LimeManager::encrypt(const std::string &localDeviceId, std::shared_ptr<const std::string> recipientUserId, std::shared_ptr<std::vector<recipientData>> recipients, std::shared_ptr<const std::vector<uint8_t>> plainMessage, std::shared_ptr<std::vector<uint8_t>> cipherMessage, const limeCallback &callback) {
// Load user object
auto userElem = m_users_cache.find(localDeviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) { // not in cache, load it from DB
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user;
} else {
user = userElem->second;
}
LimeManager::load_user(user, localDeviceId);
// call the encryption function
user->encrypt(recipientUserId, recipients, plainMessage, cipherMessage, callback);
......@@ -97,14 +97,8 @@ namespace lime {
bool LimeManager::decrypt(const std::string &localDeviceId, const std::string &recipientUserId, const std::string &senderDeviceId, const std::vector<uint8_t> &cipherHeader, const std::vector<uint8_t> &cipherMessage, std::vector<uint8_t> &plainMessage) {
// Load user object
auto userElem = m_users_cache.find(localDeviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) { // not in cache, load it from DB
user = load_LimeUser(m_db_access, localDeviceId, m_http_provider, m_user_auth);
m_users_cache[localDeviceId]=user;
} else {
user = userElem->second;
}
LimeManager::load_user(user, localDeviceId);
// call the decryption function
return user->decrypt(recipientUserId, senderDeviceId, cipherHeader, cipherMessage, plainMessage);
......@@ -148,14 +142,8 @@ namespace lime {
for (auto deviceId : deviceIds) {
BCTBX_SLOGI<<"Lime update user "<<deviceId;
//load user
auto userElem = m_users_cache.find(deviceId);
std::shared_ptr<LimeGeneric> user;
if (userElem == m_users_cache.end()) { // not in cache, load it from DB
user = load_LimeUser(m_db_access, deviceId, m_http_provider, m_user_auth);
m_users_cache[deviceId]=user;
} else {
user = userElem->second;
}
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);
......@@ -163,10 +151,13 @@ namespace lime {
// update the SPk(if needed)
user->update_SPk(managerUpdateCallback);
}
}
void LimeManager::get_selfIdentityKey(const std::string &localDeviceId, std::vector<uint8_t> &Ik) {
std::shared_ptr<LimeGeneric> user;
LimeManager::load_user(user, localDeviceId);
/* SPk check */
//if (callback) callback(lime::callbackReturn::success, "");
user->get_Ik(Ik);
}
} // namespace lime
......@@ -207,6 +207,45 @@ static void lime_session_establishment(const lime::CurveId curve, const std::str
}
}
static void lime_getSelfIk_test(const lime::CurveId curve, const std::string &dbFilename, const std::vector<uint8_t> &pattern) {
// retrieve the Ik and check it matches given pattern
std::unique_ptr<LimeManager> aliceManager = nullptr;
std::vector<uint8_t> Ik{};
try {
// create Manager for alice
aliceManager = std::unique_ptr<LimeManager>(new LimeManager(dbFilename, prov, user_auth_callback));
// retrieve alice identity key
aliceManager->get_selfIdentityKey("alice", Ik);
BC_ASSERT_TRUE((Ik==pattern));
} catch (BctbxException &e) {
BCTBX_SLOGE <<e;;
BC_FAIL();
return;
}
// try to get the Ik of a user not in there, we shall get an exception
try {
aliceManager->get_selfIdentityKey("bob", Ik);
} catch (BctbxException &e) {
// just swallow it
BC_PASS();
return;
}
BC_FAIL("Get the Ik of a user not in local Storage didn't throw an exception");
}
static void lime_getSelfIk() {
#ifdef EC25519_ENABLED
std::vector<uint8_t> pattern_selfIk_C25519 {{0x55, 0x6b, 0x4a, 0xc2, 0x24, 0xc1, 0xd4, 0xff, 0xb7, 0x44, 0x82, 0xe2, 0x3c, 0x75, 0x1c, 0x2b, 0x1c, 0xcb, 0xf6, 0xe2, 0x96, 0xcb, 0x18, 0x01, 0xc6, 0x76, 0x2d, 0x30, 0xa0, 0xa2, 0xbb, 0x27}};
lime_getSelfIk_test(lime::CurveId::c25519, std::string(bc_tester_get_resource_dir_prefix()).append("/data/pattern_getSelfIk.C25519.sqlite3"), pattern_selfIk_C25519);
#endif
#ifdef EC448_ENABLED
std::vector<uint8_t> pattern_selfIk_C448 {{0xe7, 0x96, 0x9e, 0x53, 0xd3, 0xbf, 0xfb, 0x4c, 0x6d, 0xdb, 0x79, 0xd2, 0xd7, 0x24, 0x91, 0x7b, 0xa8, 0x99, 0x87, 0x20, 0x23, 0xe1, 0xec, 0xd4, 0xb5, 0x76, 0x0f, 0xc2, 0x83, 0xae, 0x5a, 0xf9, 0x1d, 0x25, 0x47, 0xda, 0x0e, 0x71, 0x50, 0xd5, 0xaf, 0x79, 0x92, 0x48, 0xb0, 0xb6, 0x0f, 0xdc, 0x6f, 0x73, 0x3f, 0xd9, 0x9c, 0x2c, 0x95, 0xe3, 0x00}};
lime_getSelfIk_test(lime::CurveId::c448, std::string(bc_tester_get_resource_dir_prefix()).append("/data/pattern_getSelfIk.C448.sqlite3"), pattern_selfIk_C448);
#endif
}
/**
* Scenario:
* - Create a user alice
......@@ -1779,7 +1818,8 @@ static test_t tests[] = {
TEST_NO_TAG("Without OPk", x3dh_without_OPk),
TEST_NO_TAG("Update - clean MK", lime_update_clean_MK),
TEST_NO_TAG("Update - SPk", lime_update_SPk),
TEST_NO_TAG("Update - OPk", lime_update_OPk)
TEST_NO_TAG("Update - OPk", lime_update_OPk),
TEST_NO_TAG("get self Identity Key", lime_getSelfIk)
};
test_suite_t lime_lime_test_suite = {
......
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