Commit 324b862d authored by johan's avatar johan

Improve RNG management

- crypto primitives exposes only what is really needed by the lib
- tests make direct usage of std::random_device
+ code cleaning: template parameter name consistency
parent b3a890ed
......@@ -46,7 +46,6 @@ namespace lime {
template class DSApair<C448>;
#endif
/***** Random Number Generator ********/
/**
* A wrapper around the bctoolbox Random Number Generator
......@@ -55,8 +54,10 @@ class bctbx_RNG : public RNG {
private :
bctbx_rng_context_t *m_context; // the bctoolbox RNG context
public:
/* accessor */
/* only bctbx_EDDSA and bctbx_ECDH needs a direct access to the actual RNG context */
template <typename Curve> friend class bctbx_EDDSA;
template <typename Curve> friend class bctbx_ECDH;
/**
* @brief access internal RNG context
* Used internally by the bctoolbox wrapper, is not exposed to the lime_crypto_primitive API.
......@@ -67,23 +68,30 @@ class bctbx_RNG : public RNG {
return m_context;
}
public:
/* accessor */
/**
* @brief fill given buffer with Random bytes
* @brief fill the given RandomSeed buffer with Random bytes
*
* @param[out] buffer point to the beginning of the buffer to be filled with random bytes
* @param[in] size size of the buffer to be filled
* @param[in,out] buffer point to the beginning of the buffer to be filled with random bytes
*/
void randomize(uint8_t *buffer, const size_t size) override {
bctbx_rng_get(m_context, buffer, size);
void randomize(sBuffer<lime::settings::DRrandomSeedSize> &buffer) override {
bctbx_rng_get(m_context, buffer.data(), buffer.size());
};
/**
* @brief fill given buffer with Random bytes
* @brief Generate a 32 bits unsigned integer(used to generate keys Id)
* The MSbit is forced to 0 to avoid dealing with DB misinterpreting unsigned values into signed one
* Our random number is actually on 31 bits.
*
* @param[out] buffer vector to be filled with random bytes(based on original vector size)
* @return a random 32 bits unsigned integer
*/
void randomize(std::vector<uint8_t> buffer) override {
randomize(buffer.data(), buffer.size());
uint32_t randomize() override {
std::array<uint8_t, 4> buffer;
bctbx_rng_get(m_context, buffer.data(), buffer.size());
// buffer[0] is shifted by 23 instead of 24 to keep the MSb to 0.
return (static_cast<uint32_t>(buffer[0])<<23 | static_cast<uint32_t>(buffer[1])<<16 | static_cast<uint32_t>(buffer[2])<<8 | static_cast<uint32_t>(buffer[3]));
};
bctbx_RNG() {
......@@ -402,14 +410,14 @@ class bctbx_ECDH : public keyExchange<Curve> {
/* Factory functions */
template <typename Base>
std::shared_ptr<keyExchange<Base>> make_keyExchange() {
return std::make_shared<bctbx_ECDH<Base>>();
template <typename Curve>
std::shared_ptr<keyExchange<Curve>> make_keyExchange() {
return std::make_shared<bctbx_ECDH<Curve>>();
}
template <typename Base>
std::shared_ptr<Signature<Base>> make_Signature() {
return std::make_shared<bctbx_EDDSA<Base>>();
template <typename Curve>
std::shared_ptr<Signature<Curve>> make_Signature() {
return std::make_shared<bctbx_EDDSA<Curve>>();
}
/* HMAC templates */
......
......@@ -24,6 +24,7 @@
#include <vector>
#include "lime_keys.hpp"
#include "lime_defines.hpp"
namespace lime {
/*************************************************************************************************/
......@@ -53,36 +54,36 @@ namespace lime {
/**
* Base buffer definition for Key Exchange data structure : easy use of array types with correct size
*/
template <typename Base, lime::Xtype dataType>
class X : public sBuffer<static_cast<size_t>(Base::Xsize(dataType))>{
template <typename Curve, lime::Xtype dataType>
class X : public sBuffer<static_cast<size_t>(Curve::Xsize(dataType))>{
public :
/// provide a static size function to be able to call the function not on an object
constexpr static size_t ssize(void) {return Base::Xsize(dataType);};
constexpr static size_t ssize(void) {return Curve::Xsize(dataType);};
/// construct from a std::vector<uint8_t>
X(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Base::Xsize(dataType), this->begin());}
X(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Curve::Xsize(dataType), this->begin());}
X() {};
/// copy from a std::vector<uint8_t>
void assign(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Base::Xsize(dataType), this->begin());}
void assign(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Curve::Xsize(dataType), this->begin());}
};
/**
* Key pair structure for key exchange algorithm
*/
template <typename Base>
template <typename Curve>
class Xpair {
private:
X<Base, lime::Xtype::publicKey> m_pubKey;
X<Base, lime::Xtype::privateKey> m_privKey;
X<Curve, lime::Xtype::publicKey> m_pubKey;
X<Curve, lime::Xtype::privateKey> m_privKey;
public:
/// access the private key
X<Base, lime::Xtype::privateKey> &privateKey(void) {return m_privKey;};
X<Curve, lime::Xtype::privateKey> &privateKey(void) {return m_privKey;};
/// access the public key
X<Base, lime::Xtype::publicKey> &publicKey(void) {return m_pubKey;};
X<Curve, lime::Xtype::publicKey> &publicKey(void) {return m_pubKey;};
/// copy construct a key pair from public and private keys (no verification on validity of keys is performed)
Xpair(X<Base, lime::Xtype::publicKey> &pub, X<Base, lime::Xtype::privateKey> &priv):m_pubKey(pub),m_privKey(priv) {};
Xpair(X<Curve, lime::Xtype::publicKey> &pub, X<Curve, lime::Xtype::privateKey> &priv):m_pubKey(pub),m_privKey(priv) {};
Xpair() :m_pubKey{},m_privKey{}{};
/// == operator assert that public and private keys are the same
bool operator==(Xpair<Base> b) const {return (m_privKey==b.privateKey() && m_pubKey==b.publicKey());};
bool operator==(Xpair<Curve> b) const {return (m_privKey==b.privateKey() && m_pubKey==b.publicKey());};
};
......@@ -92,36 +93,36 @@ namespace lime {
/**
* Base buffer definition for DSA data structure : easy use of array types with correct size
*/
template <typename Base, lime::DSAtype dataType>
class DSA : public sBuffer<static_cast<size_t>(Base::DSAsize(dataType))>{
template <typename Curve, lime::DSAtype dataType>
class DSA : public sBuffer<static_cast<size_t>(Curve::DSAsize(dataType))>{
public :
/// provide a static size function to be able to call the function not on an object
constexpr static size_t ssize(void) {return Base::DSAsize(dataType);};
constexpr static size_t ssize(void) {return Curve::DSAsize(dataType);};
/// contruct from a std::vector<uint8_t>
DSA(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Base::DSAsize(dataType), this->begin());}
DSA(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Curve::DSAsize(dataType), this->begin());}
DSA() {};
/// copy from a std::vector<uint8_t>
void assign(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Base::DSAsize(dataType), this->begin());}
void assign(std::vector<uint8_t>::const_iterator buffer) {std::copy_n(buffer, Curve::DSAsize(dataType), this->begin());}
};
/**
* Key pair structure for DSA algorithm
*/
template <typename Base>
template <typename Curve>
class DSApair {
private:
DSA<Base, lime::DSAtype::publicKey> m_pubKey;
DSA<Base, lime::DSAtype::privateKey> m_privKey;
DSA<Curve, lime::DSAtype::publicKey> m_pubKey;
DSA<Curve, lime::DSAtype::privateKey> m_privKey;
public:
/// access the private key
DSA<Base, lime::DSAtype::privateKey> &privateKey(void) {return m_privKey;};
DSA<Curve, lime::DSAtype::privateKey> &privateKey(void) {return m_privKey;};
/// access the public key
DSA<Base, lime::DSAtype::publicKey> &publicKey(void) {return m_pubKey;};
DSA<Curve, lime::DSAtype::publicKey> &publicKey(void) {return m_pubKey;};
/// copy construct a key pair from public and private keys (no verification on validity of keys is performed)
DSApair(DSA<Base, lime::DSAtype::publicKey> &pub, DSA<Base, lime::DSAtype::privateKey> &priv):m_pubKey(pub),m_privKey(priv) {};
DSApair(DSA<Curve, lime::DSAtype::publicKey> &pub, DSA<Curve, lime::DSAtype::privateKey> &priv):m_pubKey(pub),m_privKey(priv) {};
DSApair() :m_pubKey{},m_privKey{}{};
/// == operator assert that public and private keys are the same
bool operator==(DSApair<Base> b) const {return (m_privKey==b.privateKey() && m_pubKey==b.publicKey());};
bool operator==(DSApair<Curve> b) const {return (m_privKey==b.privateKey() && m_pubKey==b.publicKey());};
};
......@@ -131,23 +132,28 @@ namespace lime {
/**
* Random number generator
* This abstract class is used to hold a RNG object which then is passed to internal crypto primitives
* who may need it (and dynamically cast to their need which it must fit)
* The only "external" use of RNG is to generate a random seed and some 32 bits id, provide explicit
* functions for that.
*/
class RNG {
public:
/**
* @brief fill given buffer with Random bytes
* @brief fill the given RandomSeed buffer with Random bytes
*
* @param[out] buffer point to the beginning of the buffer to be filled with random bytes
* @param[in] size size of the buffer to be filled
* @param[in,out] buffer point to the beginning of the buffer to be filled with random bytes
*/
virtual void randomize(uint8_t *buffer, const size_t size) = 0;
virtual void randomize(sBuffer<lime::settings::DRrandomSeedSize> &buffer) = 0;
/**
* @brief fill given buffer with Random bytes
* @brief Generate a 32 bits unsigned integer(used to generate keys Id)
* The MSbit is forced to 0 to avoid dealing with DB misinterpreting unsigned values into signed one
* Our random number is actually on 31 bits.
*
* @param[out] buffer vector to be filled with random bytes(based on original vector size)
* @return a random 32 bits unsigned integer
*/
virtual void randomize(std::vector<uint8_t> buffer) = 0;
virtual uint32_t randomize() = 0;
virtual ~RNG() = default;
}; //class RNG
......@@ -157,22 +163,22 @@ class RNG {
* - shall be able to wrap around any algorithm providing key exchange
* - wrapped in algorithms must support key format convertion function from matching Digital Signature algorithm
*/
template <typename Base>
template <typename Curve>
class keyExchange {
public:
/* accessors */
virtual const X<Base, lime::Xtype::privateKey> get_secret(void) = 0; /**< Secret key */
virtual const X<Base, lime::Xtype::publicKey> get_selfPublic(void) = 0; /**< Self Public key */
virtual const X<Base, lime::Xtype::publicKey> get_peerPublic(void) = 0; /**< Peer Public key */
virtual const X<Base, lime::Xtype::sharedSecret> get_sharedSecret(void) = 0; /**< ECDH output */
virtual const X<Curve, lime::Xtype::privateKey> get_secret(void) = 0; /**< Secret key */
virtual const X<Curve, lime::Xtype::publicKey> get_selfPublic(void) = 0; /**< Self Public key */
virtual const X<Curve, lime::Xtype::publicKey> get_peerPublic(void) = 0; /**< Peer Public key */
virtual const X<Curve, lime::Xtype::sharedSecret> get_sharedSecret(void) = 0; /**< ECDH output */
/* set keys in context, publics and private keys directly accept Signature formatted keys which are converted to keyExchange format */
virtual void set_secret(const X<Base, lime::Xtype::privateKey> &secret) = 0; /**< Secret key */
virtual void set_secret(const DSA<Base, lime::DSAtype::privateKey> &secret) = 0; /**< Secret key */
virtual void set_selfPublic(const X<Base, lime::Xtype::publicKey> &selfPublic) = 0; /**< Self Public key */
virtual void set_selfPublic(const DSA<Base, lime::DSAtype::publicKey> &selfPublic) = 0; /**< Self Public key */
virtual void set_peerPublic(const X<Base, lime::Xtype::publicKey> &peerPublic) = 0; /**< Peer Public key */
virtual void set_peerPublic(const DSA<Base, lime::DSAtype::publicKey> &peerPublic) = 0; /**< Peer Public key */
virtual void set_secret(const X<Curve, lime::Xtype::privateKey> &secret) = 0; /**< Secret key */
virtual void set_secret(const DSA<Curve, lime::DSAtype::privateKey> &secret) = 0; /**< Secret key */
virtual void set_selfPublic(const X<Curve, lime::Xtype::publicKey> &selfPublic) = 0; /**< Self Public key */
virtual void set_selfPublic(const DSA<Curve, lime::DSAtype::publicKey> &selfPublic) = 0; /**< Self Public key */
virtual void set_peerPublic(const X<Curve, lime::Xtype::publicKey> &peerPublic) = 0; /**< Peer Public key */
virtual void set_peerPublic(const DSA<Curve, lime::DSAtype::publicKey> &peerPublic) = 0; /**< Peer Public key */
/**
* @brief generate a new random key pair
......@@ -197,15 +203,15 @@ class keyExchange {
* Digital Signature wrapper
* - shall be able to wrap around any algorithm providing key exchange
*/
template <typename Base>
template <typename Curve>
class Signature {
public:
/* accessors */
virtual const DSA<Base, lime::DSAtype::privateKey> get_secret(void) = 0; /**< Secret key */
virtual const DSA<Base, lime::DSAtype::publicKey> get_public(void) = 0; /**< Self Public key */
virtual const DSA<Curve, lime::DSAtype::privateKey> get_secret(void) = 0; /**< Secret key */
virtual const DSA<Curve, lime::DSAtype::publicKey> get_public(void) = 0; /**< Self Public key */
virtual void set_secret(const DSA<Base, lime::DSAtype::privateKey> &secretKey) = 0; /**< Secret key */
virtual void set_public(const DSA<Base, lime::DSAtype::publicKey> &publicKey) = 0; /**< Self Public key */
virtual void set_secret(const DSA<Curve, lime::DSAtype::privateKey> &secretKey) = 0; /**< Secret key */
virtual void set_public(const DSA<Curve, lime::DSAtype::publicKey> &publicKey) = 0; /**< Self Public key */
/**
* @brief generate a new random EdDSA key pair
......@@ -225,12 +231,12 @@ class Signature {
* @param[in] message The message to be signed (we can sign any vector or more specifically a key exchange public key)
* @param[out] signature The signature produced from the message with a key pair previously introduced in the object
*/
virtual void sign(const std::vector<uint8_t> &message, DSA<Base, lime::DSAtype::signature> &signature) = 0;
virtual void sign(const std::vector<uint8_t> &message, DSA<Curve, lime::DSAtype::signature> &signature) = 0;
/**
* @overload virtual void sign(const X<Base, lime::Xtype::publicKey> &message, DSA<Base, lime::DSAtype::signature> &signature)
* @overload virtual void sign(const X<Curve, lime::Xtype::publicKey> &message, DSA<Curve, lime::DSAtype::signature> &signature)
* a convenience function to directly verify a key exchange public key
*/
virtual void sign(const X<Base, lime::Xtype::publicKey> &message, DSA<Base, lime::DSAtype::signature> &signature) = 0;
virtual void sign(const X<Curve, lime::Xtype::publicKey> &message, DSA<Curve, lime::DSAtype::signature> &signature) = 0;
/**
* @brief Verify a message signature using the public key previously set in the object
......@@ -240,12 +246,12 @@ class Signature {
*
* @return true if the signature is valid, false otherwise
*/
virtual bool verify(const std::vector<uint8_t> &message, const DSA<Base, lime::DSAtype::signature> &signature) = 0;
virtual bool verify(const std::vector<uint8_t> &message, const DSA<Curve, lime::DSAtype::signature> &signature) = 0;
/**
* @overload virtual bool verify(const X<Base, lime::Xtype::publicKey> &message, const DSA<Base, lime::DSAtype::signature> &signature)
* @overload virtual bool verify(const X<Curve, lime::Xtype::publicKey> &message, const DSA<Curve, lime::DSAtype::signature> &signature)
* a convenience function to directly verify a key exchange public key
*/
virtual bool verify(const X<Base, lime::Xtype::publicKey> &message, const DSA<Base, lime::DSAtype::signature> &signature) = 0;
virtual bool verify(const X<Curve, lime::Xtype::publicKey> &message, const DSA<Curve, lime::DSAtype::signature> &signature) = 0;
virtual ~Signature() = default;
}; //class EdDSA
......@@ -359,11 +365,11 @@ template <> bool AEAD_decrypt<AES256GCM>(const uint8_t *const key, const size_t
/* Use these to instantiate an object as they will pick the correct undurlying implemenation of virtual classes */
std::shared_ptr<RNG> make_RNG();
template <typename Base>
std::shared_ptr<keyExchange<Base>> make_keyExchange();
template <typename Curve>
std::shared_ptr<keyExchange<Curve>> make_keyExchange();
template <typename Base>
std::shared_ptr<Signature<Base>> make_Signature();
template <typename Curve>
std::shared_ptr<Signature<Curve>> make_Signature();
/*************************************************************************************************/
/********************** Template Instanciation ***************************************************/
......
......@@ -465,7 +465,7 @@ namespace lime {
// First generate a key and IV, use it to encrypt the given message, Associated Data are : sourceDeviceId || recipientUserId
// generate the random seed
auto RNG_context = make_RNG();
RNG_context->randomize(randomSeed.data(), randomSeed.size());
RNG_context->randomize(randomSeed);
// expansion of randomSeed to 48 bytes: 32 bytes random key + 16 bytes nonce, use HKDF with empty salt
std::vector<uint8_t> emptySalt;
......
......@@ -659,11 +659,9 @@ void Lime<Curve>::X3DH_generate_SPk(X<Curve, lime::Xtype::publicKey> &publicSPk,
SPkSign->set_secret(m_Ik.privateKey());
SPkSign->sign(publicSPk, SPk_sig);
// Generate a random SPk Id: Sqlite doesn't really support unsigned value.
// Be sure the MSbit is set to zero to avoid problem as even if declared unsigned this Id will be treated by sqlite as signed(but still unsigned in this lib)
std::array<uint8_t,4> randomId;
m_RNG->randomize(randomId.data(), randomId.size());
SPk_id = static_cast<uint32_t>(randomId[0])<<23 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
// Generate a random SPk Id
// Sqlite doesn't really support unsigned value, the randomize function makes sure that the MSbit is set to 0 to not fall into strange bugs with that
SPk_id = m_RNG->randomize();
// insert all this in DB
try {
......@@ -705,11 +703,9 @@ void Lime<Curve>::X3DH_generate_OPks(std::vector<X<Curve, lime::Xtype::publicKey
// Generate a new ECDH Key pair
DH->createKeyPair(m_RNG);
// Generate a random OPk Id: Sqlite doesn't really support unsigned value.
// Be sure the MSbit is set to zero to avoid problem as even if declared unsigned this Id will be treated by sqlite as signed(but still unsigned in this lib)
std::array<uint8_t,4> randomId;
m_RNG->randomize(randomId.data(), randomId.size());
OPk_id = static_cast<uint32_t>(randomId[0])<<23 | static_cast<uint32_t>(randomId[1])<<16 | static_cast<uint32_t>(randomId[2])<<8 | static_cast<uint32_t>(randomId[3]);
// Generate a random OPk Id
// Sqlite doesn't really support unsigned value, the randomize function makes sure that the MSbit is set to 0 to not fall into strange bugs with that
OPk_id = m_RNG->randomize();
// Insert in DB: store Public Key || Private Key
OPk.write(0, (const char *)(DH->get_selfPublic().data()), X<Curve, lime::Xtype::publicKey>::ssize());
......
......@@ -35,6 +35,10 @@ using namespace::soci;
namespace lime_tester {
/* for testing purpose RNG, no need to be good one */
std::random_device rd;
std::uniform_int_distribution<uint8_t> uniform_dist(0,255);
// default value for the timeout
int wait_for_timeout=4000;
......@@ -154,6 +158,15 @@ std::vector<std::string> messages_pattern = {
{"I have come here to chew bubble gum and kick ass, and I'm all out of bubble gum."}
};
/**
* @brief Simple RNG function, used to generate random values for testing purpose, they do not need to be real random
* so use directly std::random_device
*/
void randomize(uint8_t *buffer, const size_t size) {
for (size_t i=0; i<size; i++) {
buffer[i] = lime_tester::uniform_dist(rd);
}
}
/**
* @brief Create and initialise the two sessions given in parameter. Alice as sender session and Bob as receiver one
* Alice must then send the first message, once bob got it, sessions are fully initialised
......@@ -177,8 +190,8 @@ void dr_sessionsInit(std::shared_ptr<DR<Curve>> &alice, std::shared_ptr<DR<Curve
/* generate a shared secret and AD */
lime::DRChainKey SK;
lime::SharedADBuffer AD;
RNG_context->randomize(SK.data(), SK.size());
RNG_context->randomize(AD.data(), AD.size());
lime_tester::randomize(SK.data(), SK.size());
lime_tester::randomize(AD.data(), AD.size());
// insert the peer Device (with dummy datas in lime_PeerDevices and lime_LocalUsers tables, not used in the DR tests but needed to satisfy foreign key condition on session insertion)
long int aliceUid,bobUid,bobDid,aliceDid;
......@@ -495,13 +508,11 @@ const char charset[] =
*/
std::shared_ptr<std::string> makeRandomDeviceName(const char *basename) {
auto ret = make_shared<std::string>(basename);
bctbx_rng_context_t *RNG = bctbx_rng_context_new();
std::array<uint8_t,6> rnd;
bctbx_rng_get(RNG, rnd.data(), rnd.size());
lime_tester::randomize(rnd.data(), rnd.size());
for (auto x : rnd) {
ret->append(1, charset[x%(sizeof(charset)-1)]);
}
bctbx_rng_context_free(RNG);
return ret;
}
......
......@@ -27,6 +27,7 @@
#include "lime_crypto_primitives.hpp"
#include "soci/sqlite3/soci-sqlite3.h"
#include <random>
using namespace::lime;
......@@ -44,6 +45,15 @@ extern int wait_for_timeout;
// default value for initial OPk batch size, keep it small so not too many OPks generated
extern uint16_t OPkInitialBatchSize;
/**
* @brief Simple RNG function, used to generate random values for testing purpose, they do not need to be real random
* so use directly std::random_device
*
* @param[in] buffer pointer to the begining of the buffer to be filled
* @param[in] size how many random bytes you want
*/
void randomize(uint8_t *buffer, const size_t size);
/**
* @brief Create and initialise the two sessions given in parameter. Alice as sender session and Bob as receiver one
* Alice must then send the first message, once bob got it, sessions are fully initialised
......
......@@ -321,7 +321,7 @@ static void hashMac_KDF_bench(uint64_t runTime_ms, size_t IKMsize) {
auto rng_source = make_RNG();
/* input lenght is the same used by X3DH */
std::vector<uint8_t> IKM(IKMsize, 0);
rng_source->randomize(IKM.data(), IKM.size());
lime_tester::randomize(IKM.data(), IKM.size());
std::string info{"The lime tester info string"};
std::vector<uint8_t> salt(SHA512::ssize(), 0); // salt is the same used in X3DH
std::array<uint8_t, 64> output;
......
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