Commit a34d95c3 authored by johan's avatar johan

Improve code documentation

+minor refactoring on recipientInfos struct in lime_double_ratchet.hpp
parent ef54abe5
......@@ -406,7 +406,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
EXTRACT_ALL = NO
EXTRACT_ALL = YES
# If the EXTRACT_PRIVATE tag is set to YES all private members of a class will
# be included in the documentation.
......@@ -424,7 +424,7 @@ EXTRACT_PACKAGE = NO
# included in the documentation.
# The default value is: NO.
EXTRACT_STATIC = NO
EXTRACT_STATIC = YES
# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) defined
# locally in source files will be included in the documentation. If set to NO
......
......@@ -33,7 +33,8 @@ namespace lime {
enum class CurveId : uint8_t {
unset=0, /**< used as default to detected incorrect behavior */
c25519=1, /**< Curve 25519 */
c448=2}; /**< Curve 448-goldilocks */
c448=2 /**< Curve 448-goldilocks */
};
/** Manage the encryption policy : how is the user's plaintext encrypted */
enum class EncryptionPolicy {
......@@ -52,16 +53,23 @@ namespace lime {
untrusted=0, /**< we know this device but do not trust it, that information shall be displayed to the end user, a colour code shall be enough */
trusted=1, /**< this peer device already got its public identity key validated, that information shall be displayed to the end user too */
unsafe=2, /**< this status is a helper for the library user. It is used only by the peerDeviceStatus accessor functions */
fail, /**< when returned by decrypt : we could not decrypt the incoming message
when returned by encrypt in the peerStatus: we could not encrypt to this recipient(probably because it does not published keys on the X3DH° server) */
unknown /**< when returned after encryption or decryption, means it is the first time we communicate with this device (and thus create a DR session with it),
fail, /**< when returned by decrypt : we could not decrypt the incoming message\n
when returned by encrypt in the peerStatus: we could not encrypt to this recipient(probably because it does not published keys on the X3DH server) */
unknown /**< when returned after encryption or decryption, means it is the first time we communicate with this device (and thus create a DR session with it)\n
when returned by a get_peerDeviceStatus: this device is not in localStorage */
};
/** Used to manage recipient list for encrypt function input: give a recipient GRUU and get it back with the header which must be sent to recipient with the cipher text*/
/** @brief The encrypt function input/output data structure
*
* give a recipient GRUU and get it back with the header which must be sent to recipient with the cipher text
*/
struct RecipientData {
const std::string deviceId; /**< input: recipient deviceId (shall be GRUU) */
lime::PeerDeviceStatus peerStatus; /**< output: after encrypt calls back, it will hold the status of this peer device: unknown (first interaction with this device), untrusted or trusted */
lime::PeerDeviceStatus peerStatus; /**< output: after encrypt calls back, it will hold the status of this peer device:\n
- unknown: first interaction with this device)
- untrusted: device is kown but we never confirmed its identity public key
- trusted: we already confirmed this device identity public key
- fail: we could not encrypt for this device, probably because it never published its keys on the X3DH server */
std::vector<uint8_t> DRmessage; /**< output: after encrypt calls back, it will hold the Double Ratchet message targeted to the specified recipient. */
/**
* recipient data are built giving a recipient id
......@@ -76,6 +84,7 @@ namespace lime {
fail /**< operation failed, we shall have an explanation string too */
};
/** @brief Callback use to give a status on asynchronous operation
*
* it returns a code and may return a string (could actually be empty) to detail what's happening
* callback is used on every operation possibly involving a connection to X3DH server: create_user, delete_user, encrypt and update
* @param[in] status success or fail
......@@ -106,7 +115,8 @@ namespace lime {
/* Forward declare the class managing one lime user*/
class LimeGeneric;
/** Manage Lime objects
/** @brief Manage several Lime objects(one is needed for each local user).
*
* 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)
* All interactions should take place through the LimeManager object, any endpoint shall have only one LimeManager object instanciated
*/
......@@ -122,6 +132,7 @@ namespace lime {
/**
* @brief Create a user in local database and publish it on the given X3DH server
*
* The Lime user shall be created at the same time the account is created on the device, this function shall not be called again, attempt to re-create an already existing user will fail.
* A user is identified by its deviceId (shall be the GRUU) and must at creation select a base Elliptic curve to use, this setting cannot be changed later
* A user is published on an X3DH key server who must run using the same elliptic curve selected for this user (creation will fail otherwise), the server url cannot be changed later
......@@ -129,14 +140,14 @@ namespace lime {
* @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] x3dhServerUrl The complete url(including port) of the X3DH key server. It must connect using HTTPS. Example: https://sip5.linphone.org:25519
* @param[in] curve Choice of elliptic curve to use as base for ECDH and EdDSA operation involved. Can be CurveId::c25519 or CurveId::c448.
* @param[in] initialOPkBatchSize Number of OPks in the first batch uploaded to X3DH server
* @param[in] OPkInitialBatchSize Number of OPks in the first batch uploaded to X3DH server
* @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
* @note
* The initialOPkBatchSize is optionnal, if not used, set to defaults defined in lime::settings
* The OPkInitialBatchSize is optionnal, 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 create_user(const std::string &localDeviceId, const std::string &x3dhServerUrl, const lime::CurveId curve, const uint16_t initialOPkBatchSize, const limeCallback &callback);
void create_user(const std::string &localDeviceId, const std::string &x3dhServerUrl, const lime::CurveId curve, const uint16_t OPkInitialBatchSize, const limeCallback &callback);
/**
* @overload void create_user(const std::string &localDeviceId, const std::string &x3dhServerUrl, const lime::CurveId curve, const limeCallback &callback)
*/
......@@ -144,6 +155,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
......@@ -154,6 +166,7 @@ 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:
......@@ -181,7 +194,7 @@ namespace lime {
* @param[in,out] recipients a list of RecipientData holding:
* - the recipient device Id(GRUU)
* - an empty buffer to store the DRmessage which must then be routed to that recipient
* - the peer Status. If peerStatus is set to fail, this entry is ignored otherwise the peerStatus is set by the encrypt, see PeerDeviceStatus definition for details
* - the peer Status. If peerStatus is set to fail, this entry is ignored otherwise the peerStatus is set by the encrypt, see ::PeerDeviceStatus definition for details
* @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(if one is produced, depends on encryption policy)
* @param[in] callback Performing encryption may involve the X3DH server and is thus asynchronous, when the operation is completed,
......@@ -195,6 +208,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
......@@ -216,6 +230,7 @@ namespace lime {
/**
* @brief Update: shall be called once a day at least, performs checks, updates and cleaning operations
*
* - check if we shall update a new SPk to X3DH server(SPk lifetime is set in 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)
......@@ -239,6 +254,7 @@ namespace lime {
/**
* @brief retrieve self Identity Key, an EdDSA formatted public key
*
* if specified localDeviceId is not found in local Storage, throw an exception
*
*
......@@ -279,6 +295,7 @@ namespace lime {
/**
* @brief set the peer device status flag in local storage: unsafe or untrusted.
*
* This variation allows to set a peer Device status to unsafe or untrusted only whithout providing its identity key Ik
*
* @param[in] peerDeviceId The device Id of peer, shall be its GRUU
......
......@@ -28,13 +28,6 @@ using namespace::std;
namespace lime {
/****************************************************************************/
/* */
/* Members helpers functions (privates) */
/* */
/****************************************************************************/
/****************************************************************************/
/* */
/* Constructors */
......@@ -42,6 +35,7 @@ namespace lime {
/****************************************************************************/
/**
* @brief Load user constructor
*
* before calling this constructor, user existence in DB is checked and its Uid retrieved
* just load it into Lime class
*
......@@ -50,6 +44,8 @@ namespace lime {
* @param[in] url URL of the X3DH key server used to publish our keys(retrieved from DB)
* @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)
......@@ -63,12 +59,15 @@ namespace lime {
/**
* @brief Create user constructor
* Create a user in DB, if already existing, throw exception
*
* Create a user in DB, if already existing, throw an exception
*
* @param[in,out] localStorage pointer to DB accessor
* @param[in] deviceId device Id(shall be GRUU), stored in the structure
* @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)
......@@ -86,16 +85,10 @@ namespace lime {
/****************************************************************************/
/* */
/* Public API */
/* Public API implenenting the virtual class LimeGeneric */
/* API documentation is in lime_lime.hpp */
/* */
/****************************************************************************/
/**
* @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] OPkInitialBatchSize 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) {
auto userData = make_shared<callbackUserData<Curve>>(this->shared_from_this(), callback, OPkInitialBatchSize, true);
......@@ -299,17 +292,18 @@ namespace lime {
/****************************************************************************/
/**
* @brief : Insert user in database and return a pointer to the control class instanciating the appropriate Lime children class
*
* 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] 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] initialOPkBatchSize Number of OPks in the first batch uploaded to X3DH server
* @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
*
* @return a pointer to the LimeGeneric class allowing access to API declared in lime.hpp
* @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) {
......@@ -366,13 +360,14 @@ namespace lime {
/**
* @brief : Load user from database and return a pointer to the control class instanciating the appropriate Lime children class
*
* Fail to find the user will raise an exception
*
* @param[in] dbFilename Path to filename to use
* @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
*
* @return a pointer to the LimeGeneric class allowing access to API declared in lime.hpp
* @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) {
......
This diff is collapsed.
This diff is collapsed.
......@@ -21,9 +21,15 @@
#define lime_defines_hpp
namespace lime {
// this namespace hold constants definition used as settings in all components of the lime library
// the constants defined in this file cannot be modified without some work on the source code
// unless you really know what you're doing, just leave them alone
/** @brief Hold constants definition used as settings in all components of the lime library
*
* in lime_setting.hpp: you can tweak the behavior of the library.
* No compatibility break between clients shall result by modifying this definitions
* @note : you can tweak values but not the types, uint16_t values are intended to be bounded by 2^16 -1.
*
* in lime_defines.hpp: the constants defined cannot be modified without some work on the source code
* unless you really know what you're doing, just leave them alone
*/
namespace settings {
/******************************************************************************/
......@@ -32,20 +38,30 @@ namespace settings {
/* */
/******************************************************************************/
// Sending, Receiving and Root key chain use 32 bytes keys (spec 3.2)
/// Sending, Receiving and Root key chain use 32 bytes keys (spec 3.2)
constexpr size_t DRChainKeySize=32;
const std::string hkdf_DRChainKey_info{"DR Root Chain Key Derivation"}; // String used as info in the root key derivation
/// String used as info in the root key derivation
const std::string hkdf_DRChainKey_info{"DR Root Chain Key Derivation"};
// DR Message Key are composed of a 32 bytes key and 16 bytes of IV
/// DR Message Key are composed of a 32 bytes key and 16 bytes of IV
constexpr size_t DRMessageKeySize=32;
/// DR Message Key are composed of a 32 bytes key and 16 bytes of IV
constexpr size_t DRMessageIVSize=16;
// Message Key is based on a message seed(sent in the DR message)
// Message key and nonce are derived from this seed and have the same length as DR Message Key
/** Size of the random seed used to generate the cipherMessage key
*
* Message Key is based on a message seed(sent in the DR message)
* Message key and nonce are derived(HKDF) from this seed and have the same length as DR Message Key
*/
constexpr size_t DRrandomSeedSize=32;
/** info string used in the derivation(HKDF) of random seed into the key used to encrypt the cipherMessage key
*
* Message Key is based on a message seed(sent in the DR message)
* Message key and nonce are derived(HKDF) from this seed and have the same length as DR Message Key
*/
const std::string hkdf_randomSeed_info{"DR Message Key Derivation"};
// AEAD generates tag 16 bytes long
/// AEAD generates tag 16 bytes long
constexpr size_t DRMessageAuthTagSize=16;
/******************************************************************************/
......@@ -53,8 +69,10 @@ namespace settings {
/* Local Storage related definitions */
/* */
/******************************************************************************/
/* define a version number for the DB schema as an integer 0xMMmmpp */
/* current version is 0.0.1 */
/** define a version number for the DB schema as an integer 0xMMmmpp
*
* current version is 0.0.1
*/
constexpr int DBuserVersion=0x000001;
/******************************************************************************/
......@@ -62,8 +80,10 @@ namespace settings {
/* X3DH related definitions */
/* */
/******************************************************************************/
const std::string X3DH_SK_info{"Lime"}; // shall be an ASCII string identifying the application (X3DH spec section 2.1)
const std::string X3DH_AD_info{"X3DH Associated Data"}; // used to generate a shared AD based on Ik and deviceID
/// shall be an ASCII string identifying the application (X3DH spec section 2.1)
const std::string X3DH_SK_info{"Lime"};
/// used to generate a shared AD based on Ik and deviceID
const std::string X3DH_AD_info{"X3DH Associated Data"};
} // namespace settings
} // namespace lime
......
......@@ -36,7 +36,8 @@ namespace lime {
/****************************************************************************/
/* Key derivation functions : KDF_RK (root key derivation function, for DH ratchet) and KDF_CK(chain key derivation function, for symmetric ratchet) */
/**
* @Brief Key Derivation Function used in Root key/Diffie-Hellman Ratchet chain.
* @brief Key Derivation Function used in Root key/Diffie-Hellman Ratchet chain.
*
* Use HKDF (see RFC5869) to derive CK and RK in one derivation
*
* @param[in,out] RK Input buffer used as salt also to store the 32 first byte of output key material
......@@ -55,16 +56,20 @@ namespace lime {
std::copy_n(HKDFoutput.cbegin()+lime::settings::DRChainKeySize, lime::settings::DRChainKeySize, CK.begin());
}
/* Set of constants used as input of HKDF like function, see double ratchet spec section 5.2 - KDF_CK */
/** 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}};
/** 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}};
/**
* @Brief Key Derivation Function used in Symmetric key ratchet chain.
* Implemented according to DR spec section 5.2 using HMAC-SHA512
* @brief Key Derivation Function used in Symmetric key ratchet chain.
*
* Implemented according to Double Ratchet spec section 5.2 using HMAC-SHA512
* @code{.unparsed}
* MK = HMAC-SHA512(CK, hkdf_mk_info) // get 48 bytes of it: first 32 to be key and last 16 to be IV
* CK = HMAC-SHA512(CK, hkdf_ck_info)
* hkdf_ck_info and hldf_mk_info being a distincts constants (0x02 and 0x01 as suggested in double ratchet - section 5.2)
* @endcode
*
* @param[in,out] CK Input/output buffer used as key to compute MK and then next CK
* @param[out] MK Message Key(32 bytes) and IV(16 bytes) computed from HMAC_SHA512 keyed with CK
......@@ -100,7 +105,12 @@ namespace lime {
ciphertext.data()+ciphertext.size() - lime::settings::DRMessageAuthTagSize, lime::settings::DRMessageAuthTagSize, // tag is in the last 16 bytes of buffer
plaintext.data());
}
// when plaintext is a fixed size buffer, no need to resize it
/**
* @overload
*
* used when the ouput is a fixed buffer: we decrypt the random seed used to generate the cipherMessage keys
* No need to resize the plaintext buffer when it has a fixed size.
*/
static bool decrypt(const lime::DRMKey &MK, const std::vector<uint8_t> &ciphertext, const size_t headerSize, std::vector<uint8_t> &AD, sBuffer<lime::settings::DRrandomSeedSize> &plaintext) {
return AEAD_decrypt<AES256GCM>(MK.data(), lime::settings::DRMessageKeySize, // MK buffer hold key<DRMessageKeySize bytes>||IV<DRMessageIVSize bytes>
MK.data()+lime::settings::DRMessageKeySize, lime::settings::DRMessageIVSize,
......@@ -192,7 +202,8 @@ namespace lime {
}
/**
* @brief Create a new DR session to be loaded from db,
* @brief Create a new DR session to be loaded from db
*
* m_dirty is already set to clean and DHR_valid to true as we won't save a session if no successfull sending or reception was performed
* if loading fails, caller should destroy the session
*
......@@ -214,12 +225,13 @@ namespace lime {
/**
* @brief Derive chain keys until reaching the requested Id. Handling unordered messages
*
* Store the derived but not used keys in a list indexed by peer DH and Nr
*
* @param[in] until index we must reach in that chain key
* @param[in] limit maximum number of allowed derivations
* @param[in] until index we must reach in that chain key
* @param[in] limit maximum number of allowed derivations
*
* @throws when we try to overpass the maximum number of key derivation since last valid message
* @throws BCTBX_EXCEPTION when we try to overpass the maximum number of key derivation since last valid message
*/
template <typename Curve>
void DR<Curve>::skipMessageKeys(const uint16_t until, const int limit) {
......@@ -290,6 +302,10 @@ namespace lime {
/**
* @brief Encrypt using the double-ratchet algorithm.
*
* @tparam inputContainer is used with
* - sBuffer: the input is a random seed used to decrypt the cipher message
* - std::vector<uint8_t>: the input is directly the plaintext message
*
* @param[in] plaintext the input to be encrypted, may actually be a 32 bytes buffer holding the seed used to generate key+IV for a AES-GCM encryption to the actual message
* @param[in] AD Associated Data, this buffer shall hold: source GRUU<...> || recipient GRUU<...> || [ actual message AEAD auth tag OR recipient User Id]
* @param[out] ciphertext buffer holding the header, cipher text and auth tag, shall contain the key and IV used to cipher the actual message, auth tag applies on AD || header
......@@ -338,6 +354,10 @@ namespace lime {
/**
* @brief Decrypt Double Ratchet message
*
* @tparam outputContainer is used with
* - sBuffer: the ouput is a random seed used to decrypt the cipher message
* - std::vector<uint8_t>: the output is directly the plaintext message
*
* @param[in] ciphertext Input to be decrypted, is likely to be a 32 bytes vector holding the crypted version of a random seed
* @param[in] AD Associated data authenticated along the encryption (initial session AD and DR message header are append to it)
* @param[out] plaintext Decrypted output
......@@ -423,7 +443,21 @@ namespace lime {
#ifdef EC448_ENABLED
template class DR<C448>;
#endif
/**
* @brief Encrypt a message to all recipients, identified by their device id
*
* The plaintext is first encrypted by one randomly generated key using aes-gcm
* The key and IV are then encrypted with DR Session specific to each device
*
* @param[in,out] recipients vector of recipients device id(gruu) and linked DR Session, DR Session are modified by the encryption\n
* The recipients struct also hold after encryption the double ratchet message targeted to that particular recipient
* @param[in] plaintext data to be encrypted
* @param[in] recipientUserId the recipient ID, not specific to a device(could be a sip-uri) or a user(could be a group sip-uri)
* @param[in] sourceDeviceId the Id of sender device(gruu)
* @param[out] cipherMessage message encrypted with a random generated key(and IV). May be an empty buffer depending on encryptionPolicy, recipients and plaintext characteristics
* @param[in] encryptionPolicy select how to manage the encryption: direct use of Double Ratchet message or encrypt in the cipher message and use the DR message to share the cipher message key\n
* default is optimized output size mode.
*/
template <typename Curve>
void encryptMessage(std::vector<RecipientInfos<Curve>>& recipients, const std::vector<uint8_t>& plaintext, const std::string& recipientUserId, const std::string& sourceDeviceId, std::vector<uint8_t>& cipherMessage, const lime::EncryptionPolicy encryptionPolicy) {
// Shall we set the payload in the DR message or in a separate cupher message buffer?
......@@ -529,6 +563,21 @@ namespace lime {
}
}
/**
* @brief Decrypt a message
*
* Decrypts the DR message and if applicable and the DR message was successfully decrypted, decrypt the cipherMessage
*
* @param[in] sourceDeviceId the device Id of sender(gruu)
* @param[in] recipientDeviceId the recipient ID, specific to current device(gruu)
* @param[in] recipientUserId the recipient ID, not specific to a device(could be a sip-uri) or a user(could be a group sip-uri)
* @param[in,out] DRSessions list of DR Sessions linked to sender device, first one shall be the one registered as active
* @param[out] DRmessage Double Ratcher message holding as payload either the encrypted plaintext or the random key used to encrypt it encrypted by the DR session
* @param[out] cipherMessage if not zero lenght, plain text encrypted with a random generated key(and IV)
* @param[out] plaintext decrypted message
*
* @return a shared pointer towards the session used to decrypt, nullptr if we couldn't find one to do it
*/
template <typename Curve>
std::shared_ptr<DR<Curve>> decryptMessage(const std::string& sourceDeviceId, const std::string& recipientDeviceId, const std::string& recipientUserId, std::vector<std::shared_ptr<DR<Curve>>>& DRSessions, const std::vector<uint8_t>& DRmessage, const std::vector<uint8_t>& cipherMessage, std::vector<uint8_t>& plaintext) {
bool payloadDirectEncryption = (cipherMessage.size() == 0); // if we do not have any cipher message, then we must be in payload direct encryption mode: the payload is in the DR message
......
......@@ -33,28 +33,36 @@ namespace lime {
class Db; // forward declaration of class Db used by DR<DHKey>, declared in lime_localStorage.hpp
// an enum to set the possible status of session regarding the Local Storage
// used to pick a subset of session to be saved in DB
enum class DRSessionDbStatus : uint8_t {clean, dirty_encrypt, dirty_decrypt, dirty_ratchet, dirty};
/**
* @brief the possible status of session regarding the Local Storage
*
* used to pick a subset of session to be saved in DB
*/
enum class DRSessionDbStatus : uint8_t {
clean, /**< session in cache match the one in local storage */
dirty_encrypt, /**< an encrypt was performed modifying part of the cached session */
dirty_decrypt, /**< a dencrypt was performed modifying part of the cached session */
dirty_ratchet, /**< a ratchet step was performed modifying part of cached session */
dirty /**< the whole session data must be saved to local storage */
};
// Double Rachet chain keys: Root key, Sender and receiver keys are 32 bytes arrays
/** Double Rachet chain keys: Root key, Sender and receiver keys are 32 bytes arrays */
using DRChainKey = lime::sBuffer<lime::settings::DRChainKeySize>;
// Double Ratchet Message keys : 32 bytes of encryption key followed by 16 bytes of IV
/** Double Ratchet Message keys : 32 bytes of encryption key followed by 16 bytes of IV */
using DRMKey = lime::sBuffer<lime::settings::DRMessageKeySize+lime::settings::DRMessageIVSize>;
// Shared Associated Data : stored at session initialisation, given by upper level(X3DH), shall be derived from Identity and Identity keys of sender and recipient, fixed size for storage convenience
/** Shared Associated Data : stored at session initialisation, given by upper level(X3DH), shall be derived from Identity and Identity keys of sender and recipient, fixed size for storage convenience */
using SharedADBuffer = std::array<uint8_t, lime::settings::DRSessionSharedADSize>;
/**
* Chain storing the DH and MKs associated with Nr(uint16_t map index)
* @brief Chain storing the DH and MKs associated with Nr(uint16_t map index)
* @tparam Curve The elliptic curve to use: C255 or C448
*/
template <typename Curve>
struct ReceiverKeyChain {
/// peer public key identifying this chain
X<Curve, lime::Xtype::publicKey> DHr;
/// message keys indexed by Nr
std::unordered_map<std::uint16_t, DRMKey> messageKeys;
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 */
/**
* Start a new empty chain
* @param[in] key the peer DH public key used on this chain
......@@ -63,10 +71,12 @@ namespace lime {
};
/**
* DR object store a Double Rachet session. 3 kinds of construction:
* - from scratch for sender
* - from scracth for receiver
* - unserialised object from local storage version
* @brief store a Double Rachet session.
*
* A session is associated to a local user and a peer device.
* It stores all the state variables described in Double Ratcher spec section 3.2 and provide encrypt/decrypt functions
*
* @tparam Curve The elliptic curve to use: C255 or C448
*/
template <typename Curve>
class DR {
......@@ -126,77 +136,35 @@ namespace lime {
/**
* Used for internal management of recipientData, includes the Double Ratchet session shared with the recipient
* @brief extend the RecipientData to add a Double Ratchet session shared with the recipient
*/
template <typename Curve>
struct RecipientInfos {
struct RecipientInfos : public RecipientData {
std::shared_ptr<DR<Curve>> DRSession; /**< DR Session to reach recipient */
const std::string deviceId; /**< recipient deviceId (shall be GRUU) */
lime::PeerDeviceStatus peerStatus; /**< after encrypt calls back, it will hold the status of this peer device: unknown (first interaction with this device), untrusted or trusted */
std::vector<uint8_t> DRmessage; /**< after encrypt calls back, it will hold the DR message targeted to the specified recipient. It may contain an X3DH init message. */
/**
* Constructor: the deviceId is a constant and must be provided to the constructor
* At construction, the peerStatus is always set to unknown as this status is then overriden with actual one fetched from DB, the ones not fetched are unknown
* The deviceId is a constant and must be provided to the constructor to instanciate the base RecipientData class.
* @note at construction, the peerStatus is always set to unknown as this status is then overriden with actual one fetched from DB, the ones not fetched are unknown
*
* @param[in] deviceId The device Id (GRUU) of this recipient
* @param[in] deviceId The device Id (GRUU) of this recipient, used to build the RecipientData
* @param[in] session The double ratchet session linking current device with this recipient.
*
*/
RecipientInfos(const std::string &deviceId, std::shared_ptr<DR<Curve>> session) : DRSession{session}, deviceId{deviceId}, peerStatus{lime::PeerDeviceStatus::unknown}, DRmessage{} {};
RecipientInfos(const std::string &deviceId, std::shared_ptr<DR<Curve>> session) : RecipientData(deviceId), DRSession{session} {};
/**
* @overload RecipientInfos(const std::string &deviceId)
* @overload
*
* forward the deviceId to the RecipientData constructor and set the DRSession pointer to nullptr
*/
RecipientInfos(const std::string &deviceId) : DRSession{nullptr}, deviceId{deviceId}, peerStatus{lime::PeerDeviceStatus::unknown}, DRmessage{} {};
RecipientInfos(const std::string &deviceId) : RecipientData(deviceId), DRSession{nullptr} {};
};
// helpers function wich are the one to be used to encrypt/decrypt messages
/**
* @brief Encrypt a message to all recipients, identified by their device id
* The plaintext is first encrypted by one randomly generated key using aes-gcm
* The key and IV are then encrypted with DR Session specific to each device
*
* @param[in,out] recipients vector of recipients device id(gruu) and linked DR Session, DR Session are modified by the encryption
* The recipients struct also hold after encryption the double ratchet message targeted to that particular recipient
* @param[in] plaintext data to be encrypted
* @param[in] recipientUserId the recipient ID, not specific to a device(could be a sip-uri) or a user(could be a group sip-uri)
* @param[in] sourceDeviceId the Id of sender device(gruu)
* @param[out] cipherMessage message encrypted with a random generated key(and IV). May be an empty buffer depending on encryptionPolicy, recipients and plaintext characteristics
* @param[in] encryptionPolicy select how to manage the encryption: direct use of Double Ratchet message or encrypt in the cipher message and use the DR message to share the cipher message key
* default is optimized output size mode.
*/
template <typename Curve>
void encryptMessage(std::vector<RecipientInfos<Curve>>& recipients, const std::vector<uint8_t>& plaintext, const std::string& recipientUserId, const std::string& sourceDeviceId, std::vector<uint8_t>& cipherMessage, const lime::EncryptionPolicy encryptionPolicy);
/**
* @brief Decrypt a message
* First try to decrypt the header using the DR Sessions given in parameter, then decrypt the message itself with key retrieved from header part
*
* @param[in] sourceDeviceId the device Id of sender(gruu)
* @param[in] recipientDeviceId the recipient ID, specific to current device(gruu)
* @param[in] recipientUserId the recipient ID, not specific to a device(could be a sip-uri) or a user(could be a group sip-uri)
* @param[int,out] DRSessions list of DR Sessions linked to sender device, first one shall be the one registered as active
* @param[out] DRmessage Double Ratcher message holding as payload either the encrypted plaintext or the random key used to encrypt it encrypted by the DR session
* @param[out] cipherMessage if not zero lenght, plain text encrypted with a random generated key(and IV)
* @param[out] plaintext decrypted message
*
* @return a shared pointer towards the session used to decrypt, nullptr if we couldn't find one to do it
*/
template <typename Curve>
std::shared_ptr<DR<Curve>> decryptMessage(const std::string& sourceDeviceId, const std::string& recipientDeviceId, const std::string& recipientUserId, std::vector<std::shared_ptr<DR<Curve>>>& DRSessions, const std::vector<uint8_t>& DRmessage, const std::vector<uint8_t>& cipherMessage, std::vector<uint8_t>& plaintext);
/**
* @brief check the message for presence of X3DH init in the header, parse it if there is one
*
* @param[in] header A buffer holding the message, it shall be DR header | DR message. If there is a X3DH init message it is in the DR header
* @param[out] x3dhInitMessage A buffer holding the X3DH input message
*
* @return true if a X3DH init message was found, false otherwise
*
* This one is implemented here as is deals with parsing the DR packet header but is not really related to DR session
*/
template <typename Curve>
bool get_X3DH_initMessage(const std::vector<uint8_t> &header, std::vector<uint8_t> &X3DH_initMessage);
/* this templates are instanciated once in the lime_double_ratchet.cpp file, explicitly tell anyone including this header that there is no need to re-instanciate them */
#ifdef EC25519_ENABLED
extern template class DR<C255>;
......
......@@ -25,36 +25,49 @@ using namespace::std;
using namespace::lime;
namespace lime {
// Group in this namespace all the functions related to building or parsing double ratchet packets
/** @brief Group in this namespace all the functions related to building or parsing double ratchet packets
*
* Implemented version of the DR session protocol (provide a way to handle future/alternative packets formats/crypto algorithm)
* Supported version description :
*
* @par Version 0x01:
*
* DRHeader is: Protocol Version Number<1 byte> || Message Type <1 byte> || curveId <1 byte> || [X3DH Init message < variable >] || Ns<2 bytes> || PN<2 bytes> || DHs<...>
*
* Message is : DRheader<...> || cipherMessageKeyK<32 bytes> || Key auth tag<16 bytes> || cipherText<...> || Message auth tag<16 bytes>
*
* Associated Data are transmitted separately: ADk for the Key auth tag, and ADm for the Message auth tag
*
* Message AEAD on : (ADm, message plain text) keyed by message Key(include IV)
*
* Key AEAD on : (ADk || Message auth tag || header, Message Key) keyed by Double Ratchet generated key/IV
*
* ADm is : source GRUU<...> || recipient sip-uri(can be a group uri)<...>
*
* ADk is : source GRUU<...> || recipient GRUU<...>
* @note: ADk is used with session stored AD provided by X3DH at session creation which is HKDF(initiator Ik || receiver Ik || initiator device Id || receiver device Id)
*
* Diffie-Hellman support: X25519 or X448 (not mixed, specified by X3DH server and client setting which must match)
*
* Packets types are : regular or x3dhinit
* - regular packet does not contain x3dh init message
* - x3dh init packet includes x3dh init message in the header as follow:\n
* haveOPk <flag 1 byte> ||\n
* self Ik < DSA<Curve, lime::DSAtype::publicKey>::ssize() bytes > ||\n
* Ek < X<Curve, lime::Xtype::publicKey>::keyLenght() bytes> ||\n
* peer SPk id < 4 bytes > ||\n
* [peer OPk id(if flag is set)<4bytes>]
*
*/
namespace double_ratchet_protocol {
/* Implemented version of the DR session protocol (provide a way to handle future/alternative packets formats/crypto algorithm)
* Supported version description :
*
* Version 0x01:
* DRHeader is: Protocol Version Number<1 byte> || Message Type <1 byte> || curveId <1 byte> || [X3DH Init message <variable>] || Ns<2 bytes> || PN<2 bytes> || DHs<...>
* Message is : DRheader<...> || cipherMessageKeyK<32 bytes> || Key auth tag<16 bytes> || cipherText<...> || Message auth tag<16 bytes>
*
* Associated Data are transmitted separately: ADk for the Key auth tag, and ADm for the Message auth tag
* Message AEAD on : (ADm, message plain text) keyed by message Key(include IV)
* Key AEAD on : (ADk || Message auth tag || header, Message Key) keyed by Double Ratchet generated key/IV
*
* ADm is : source GRUU<...> || recipient sip-uri(can be a group uri)<...>
* ADk is : source GRUU<...> || recipient GRUU<...>
* Note: ADk is used with session stored AD provided by X3DH at session creation which is HKDF(initiator Ik || receiver Ik || initiator device Id || receiver device Id)
*
* Diffie-Hellman support: X25519 or X448 (not mixed, specified by X3DH server and client setting which must match)
*
* Packets types are : regular or x3dhinit
* - regular packet does not contain x3dh init message
* - x3dh init packet includes x3dh init message in the header as follow:
* haveOPk <flag 1 byte> || self Ik < DSA<Curve, lime::DSAtype::publicKey>::ssize() bytes > || Ek < X<Curve, lime::Xtype::publicKey>::keyLenght() bytes> || peer SPk id < 4 bytes > || [peer OPk id(if flag is set)<4bytes>]
*
*/
/**
* @brief return the size of the double ratchet packet header
* header is: Protocol Version Number<1 byte> || Message Type <1 byte> || curveId <1 byte> || [X3DH Init message <variable>] || Ns<2 bytes> || PN<2 bytes> || DHs<...>
* This function return the size without optionnal X3DH init packet
*
* header is: Protocol Version Number<1 byte> || Message Type <1 byte> || curveId <1 byte> || [X3DH Init message < variable >] || Ns<2 bytes> || PN<2 bytes> || DHs<...>
*
* @return the header size without optionnal X3DH init packet
*/
template <typename Curve>
constexpr size_t headerSize() {
......@@ -63,7 +76,12 @@ namespace lime {
/**
* @brief build an X3DH init message to insert in DR header
* haveOPk <flag 1 byte> || self Ik < DSA<Curve, lime::DSAtype::publicKey>::ssize() bytes > || Ek < X<Curve, lime::Xtype::publicKey>::keyLenght() bytes> || peer SPk id < 4 bytes > || [peer OPk id(if flag is set)<4bytes>]
*
* haveOPk <flag 1 byte> ||\n
* self Ik < DSA<Curve, lime::DSAtype::publicKey>::ssize() bytes > ||\n
* Ek < X<Curve, lime::Xtype::publicKey>::keyLenght() bytes> ||\n
* peer SPk id < 4 bytes > ||\n
* [peer OPk id(if flag is set)<4bytes>]
*
* @param[out] message the X3DH init message
* @param[in] Ik self public identity key
......@@ -95,7 +113,12 @@ namespace lime {
/**
* @brief Parse the X3DH init message and extract peer Ik, peer Ek, self SPk id and seld OPk id if present
* usedOPk <flag on one byte> || peer Ik || peer Ek || self SPk id || self OPk id(if flag is set)
*
* usedOPk < flag on one byte > ||\n
* peer Ik ||\n
* peer Ek ||\n
* self SPk id ||\n
* self OPk id(if flag is set)
*
* When this function is called, we already parsed the DR message to extract the X3DH_initMessage
* all checks were already performed by the Double Ratchet packet parser, just grab the data
......@@ -135,8 +158,8 @@ namespace lime {
/**
* @brief check the message for presence of X3DH init in the header, extract it if there is one
*
* @param[in] message A buffer holding the message, it shall be DR header || DR message. If there is a X3DH init message it is in the DR header
* @param[out] x3dhInitMessage A buffer holding the X3DH input message
* @param[in] message A buffer holding the message, it shall be DR header || DR message. If there is a X3DH init message it is in the DR header
* @param[out] X3DH_initMessage A buffer holding the X3DH input message
*
* @return true if a X3DH init message was found, false otherwise (also in case of invalid packet)
*/
......@@ -177,7 +200,16 @@ namespace lime {
/**
* @brief Build a header string from needed info