lime
Lime is a C++ library implementing Open Whisper System Signal protocol
lime_double_ratchet.hpp
Go to the documentation of this file.
1 /*
2  lime_double_ratchet.hpp
3  @author Johan Pascal
4  @copyright Copyright (C) 2017 Belledonne Communications SARL
5 
6  This program is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19 #ifndef lime_double_ratchet_hpp
20 #define lime_double_ratchet_hpp
21 
22 #include <array>
23 #include <string>
24 #include <unordered_map>
25 #include <vector>
26 #include <memory>
27 
28 #include "lime_settings.hpp"
29 #include "lime_defines.hpp"
31 
32 namespace lime {
33 
34  class Db; // forward declaration of class Db used by DR<Curve>, declared in lime_localStorage.hpp
35 
41  enum class DRSessionDbStatus : uint8_t {
42  clean,
46  dirty
47  };
48 
51 
54 
56  using SharedADBuffer = std::array<uint8_t, lime::settings::DRSessionSharedADSize>;
57 
62  template <typename Curve>
65  std::unordered_map<std::uint16_t, DRMKey> messageKeys;
70  ReceiverKeyChain(X<Curve, lime::Xtype::publicKey> key) :DHr{std::move(key)}, messageKeys{} {};
71  };
72 
81  template <typename Curve>
82  class DR {
83  private:
84  /* State variables for Double Ratchet, see Double Ratchet spec section 3.2 for details */
85  X<Curve, lime::Xtype::publicKey> m_DHr; // Remote public key
86  bool m_DHr_valid; // do we have a valid remote public key, flag used to spot the first message arriving at session creation in receiver mode
87  Xpair<Curve> m_DHs; // self Key pair
88  DRChainKey m_RK; // 32 bytes root key
89  DRChainKey m_CKs; // 32 bytes key chain for sending
90  DRChainKey m_CKr; // 32 bytes key chain for receiving
91  std::uint16_t m_Ns,m_Nr; // Message index in sending and receiving chain
92  std::uint16_t m_PN; // Number of messages in previous sending chain
93  SharedADBuffer m_sharedAD; // Associated Data derived from self and peer device Identity key, set once at session creation, given by X3DH
94  std::vector<lime::ReceiverKeyChain<Curve>> m_mkskipped; // list of skipped message indexed by DH receiver public key and Nr, store MK generated during on-going decrypt, lookup is done directly in DB.
95 
96  /* helpers variables */
97  std::shared_ptr<RNG> m_RNG; // Random Number Generator context
98  long int m_dbSessionId; // used to store row id from Database Storage
99  uint16_t m_usedNr; // store the index of message key used for decryption if it came from mkskipped db
100  long m_usedDHid; // store the index of DHr message key used for decryption if it came from mkskipped db(not zero only if used)
101  uint32_t m_usedOPkId; // when the session is created on receiver side, store the OPk id used so we can remove it from local storage when saving session for the first time.
102  std::shared_ptr<lime::Db> m_localStorage; // enable access to the database holding sessions and skipped message keys
103  DRSessionDbStatus m_dirty; // status of the object regarding its instance in local storage, could be: clean, dirty_encrypt, dirty_decrypt or dirty
104  long int m_peerDid; // used during session creation only to hold the peer device id in DB as we need it to insert the session in local Storage
105  std::string m_peerDeviceId; // used during session creation only, if the deviceId is not yet in local storage, to hold the peer device Id so we can insert it in DB when session is saved for the first time
106  DSA<Curve, lime::DSAtype::publicKey> m_peerIk; // used during session creation only, if the deviceId is not yet in local storage, to hold the peer device Ik so we can insert it in DB when session is saved for the first time
107  long int m_db_Uid; // used to link session to a local device Id
108  bool m_active_status; // current status of this session, true if it is the active one, false if it is stale
109  std::vector<uint8_t> m_X3DH_initMessage; // store the X3DH init message to be able to prepend it to any message until we got a first response from peer so we're sure he was able to init the session on his side
110 
111  /*helpers functions */
112  void skipMessageKeys(const uint16_t until, const int limit); /* check if we skipped some messages in current receiving chain, generate and store in session intermediate message keys */
113  void DHRatchet(const X<Curve, lime::Xtype::publicKey> &headerDH); /* perform a Diffie-Hellman ratchet using the given peer public key */
114  /* local storage related implemented in lime_localStorage.cpp */
115  bool session_save(); /* save/update session in database : updated component depends m_dirty value */
116  bool session_load(); /* load session in database */
117  bool trySkippedMessageKeys(const uint16_t Nr, const X<Curve, lime::Xtype::publicKey> &DHr, DRMKey &MK); /* check in DB if we have a message key matching public DH and Ns */
118 
119  public:
120  DR() = delete; // make sure the Double Ratchet is not initialised without parameters
121  DR(std::shared_ptr<lime::Db> localStorage, const DRChainKey &SK, const SharedADBuffer &AD, const X<Curve, lime::Xtype::publicKey> &peerPublicKey, const long int peerDid, const std::string &peerDeviceId, const DSA<Curve, lime::DSAtype::publicKey> &peerIk, long int selfDeviceId, const std::vector<uint8_t> &X3DH_initMessage, std::shared_ptr<RNG> RNG_context); // call to initialise a session for sender: we have Shared Key and peer Public key
122  DR(std::shared_ptr<lime::Db> localStorage, const DRChainKey &SK, const SharedADBuffer &AD, const Xpair<Curve> &selfKeyPair, long int peerDid, const std::string &peerDeviceId, const uint32_t OPk_id, const DSA<Curve, lime::DSAtype::publicKey> &peerIk, long int selfDeviceId, std::shared_ptr<RNG> RNG_context); // call at initialisation of a session for receiver: we have Share Key and self key pair
123  DR(std::shared_ptr<lime::Db> localStorage, long sessionId, std::shared_ptr<RNG> RNG_context); // load session from DB
124  DR(DR<Curve> &a) = delete; // can't copy a session, force usage of shared pointers
125  DR<Curve> &operator=(DR<Curve> &a) = delete; // can't copy a session
126  ~DR();
127 
128  template<typename inputContainer>
129  void ratchetEncrypt(const inputContainer &plaintext, std::vector<uint8_t> &&AD, std::vector<uint8_t> &ciphertext, const bool payloadDirectEncryption);
130  template<typename outputContainer>
131  bool ratchetDecrypt(const std::vector<uint8_t> &cipherText, const std::vector<uint8_t> &AD, outputContainer &plaintext, const bool payloadDirectEncryption);
133  long int dbSessionId(void) const {return m_dbSessionId;};
135  bool isActive(void) const {return m_active_status;}
136  };
137 
138 
142  template <typename Curve>
143  struct RecipientInfos : public RecipientData {
144  std::shared_ptr<DR<Curve>> DRSession;
153  RecipientInfos(const std::string &deviceId, std::shared_ptr<DR<Curve>> session) : RecipientData(deviceId), DRSession{session} {};
159  RecipientInfos(const std::string &deviceId) : RecipientData(deviceId), DRSession{nullptr} {};
160  };
161 
162  // helpers function wich are the one to be used to encrypt/decrypt messages
163  template <typename Curve>
164  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);
165 
166  template <typename Curve>
167  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);
168 
169  /* 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 */
170 #ifdef EC25519_ENABLED
171  extern template class DR<C255>;
172  extern template void encryptMessage<C255>(std::vector<RecipientInfos<C255>>& recipients, const std::vector<uint8_t>& plaintext, const std::string& recipientUserId, const std::string& sourceDeviceId, std::vector<uint8_t>& cipherMessage, const lime::EncryptionPolicy encryptionPolicy);
173  extern template std::shared_ptr<DR<C255>> decryptMessage<C255>(const std::string& sourceDeviceId, const std::string& recipientDeviceId, const std::string& recipientUserId, std::vector<std::shared_ptr<DR<C255>>>& DRSessions, const std::vector<uint8_t>& DRmessage, const std::vector<uint8_t>& cipherMessage, std::vector<uint8_t>& plaintext);
174 #endif
175 #ifdef EC448_ENABLED
176  extern template class DR<C448>;
177  extern template void encryptMessage<C448>(std::vector<RecipientInfos<C448>>& recipients, const std::vector<uint8_t>& plaintext, const std::string& recipientUserId, const std::string& sourceDeviceId, std::vector<uint8_t>& cipherMessage, const lime::EncryptionPolicy encryptionPolicy);
178  extern template std::shared_ptr<DR<C448>> decryptMessage<C448>(const std::string& sourceDeviceId, const std::string& recipientDeviceId, const std::string& recipientUserId, std::vector<std::shared_ptr<DR<C448>>>& DRSessions, const std::vector<uint8_t>& DRmessage, const std::vector<uint8_t>& cipherMessage, std::vector<uint8_t>& plaintext);
179 #endif
180 
181 }
182 
183 #endif /* lime_double_ratchet_hpp */
RecipientInfos(const std::string &deviceId)
Definition: lime_double_ratchet.hpp:159
store a Double Rachet session.
Definition: lime_double_ratchet.hpp:82
DRSessionDbStatus
the possible status of session regarding the Local Storage
Definition: lime_double_ratchet.hpp:41
std::unordered_map< std::uint16_t, DRMKey > messageKeys
Definition: lime_double_ratchet.hpp:65
Chain storing the DH and MKs associated with Nr(uint16_t map index)
Definition: lime_double_ratchet.hpp:63
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)
Decrypt a message.
Definition: lime_double_ratchet.cpp:589
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)
Encrypt a message to all recipients, identified by their device id.
Definition: lime_double_ratchet.cpp:469
Key pair structure for key exchange algorithm.
Definition: lime_crypto_primitives.hpp:71
X< Curve, lime::Xtype::publicKey > DHr
Definition: lime_double_ratchet.hpp:64
bool isActive(void) const
return the current status of session
Definition: lime_double_ratchet.hpp:135
std::array< uint8_t, lime::settings::DRSessionSharedADSize > SharedADBuffer
Definition: lime_double_ratchet.hpp:56
std::shared_ptr< DR< Curve > > DRSession
Definition: lime_double_ratchet.hpp:144
RecipientInfos(const std::string &deviceId, std::shared_ptr< DR< Curve >> session)
Definition: lime_double_ratchet.hpp:153
ReceiverKeyChain(X< Curve, lime::Xtype::publicKey > key)
Definition: lime_double_ratchet.hpp:70
long int dbSessionId(void) const
return the session&#39;s local storage id
Definition: lime_double_ratchet.hpp:133
Definition: lime.cpp:30
extend the RecipientData to add a Double Ratchet session shared with the recipient ...
Definition: lime_double_ratchet.hpp:143
The encrypt function input/output data structure.
Definition: lime.hpp:67
EncryptionPolicy
Definition: lime.hpp:41