Commit f05db5ed authored by Johan Pascal's avatar Johan Pascal

add keyEchange class

parent 729ee505
......@@ -30,11 +30,13 @@ set(LIME_HEADER_FILES
lime_double_ratchet.hpp
lime_double_ratchet_protocol.hpp
lime_lime.hpp
lime_crypto_primitives.hpp
)
set(LIME_SOURCE_FILES_C )
set(LIME_SOURCE_FILES_CXX
lime.cpp
lime_keys.cpp
lime_crypto_primitives.cpp
lime_x3dh.cpp
lime_x3dh_protocol.cpp
lime_localStorage.cpp
......
/*
lime_crypto_primitives.cpp
@author Johan Pascal
@copyright Copyright (C) 2017 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "lime_crypto_primitives.hpp"
#include "bctoolbox/crypto.h"
#include "bctoolbox/exception.hh"
namespace lime {
/***** Random Number Generator ********/
class bctbx_RNG : public RNG {
private :
bctbx_rng_context_t *m_context; // the bctoolbox RNG context
public:
/* accessor */
bctbx_rng_context_t *get_context(void) {
return m_context;
}
void randomize(uint8_t *buffer, const size_t size) override {
bctbx_rng_get(m_context, buffer, size);
};
void randomize(std::vector<uint8_t> buffer) override {
randomize(buffer.data(), buffer.size());
};
bctbx_RNG() {
m_context = bctbx_rng_context_new();
}
~bctbx_RNG() {
bctbx_rng_context_free(m_context);
m_context = nullptr;
}
}; // class bctbx_RNG
/* Factory function */
std::shared_ptr<RNG> make_RNG() {
return std::make_shared<bctbx_RNG>();
}
/***** Key Exchange ******************/
/* bctbx_ECDH specialized constructor */
template <typename Curve>
bctbx_ECDHContext_t *bctbx_ECDHInit(void) {
/* if this template is instanciated the static_assert will fail but will give us an error message with faulty Curve type */
static_assert(sizeof(Curve) != sizeof(Curve), "You must specialize sessionsInit<> for your type: correctly initialise the ECDH context");
return nullptr;
}
#ifdef EC25519_ENABLED
/* specialise ECDH context creation */
template <> bctbx_ECDHContext_t *bctbx_ECDHInit<C255>(void) {
return bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
}
#endif
#ifdef EC448_ENABLED
/* specialise ECDH context creation */
template <> bctbx_ECDHContext_t *bctbx_ECDHInit<C448>(void) {
return bctbx_CreateECDHContext(BCTBX_ECDH_X448);
}
#endif
template <typename Curve>
class bctbx_ECDH : public keyExchange<Curve> {
private :
bctbx_ECDHContext_t *m_context; // the ECDH RNG context
public :
/* accessors */
const X<Curve> get_secret(void) override { /**< Secret key */
if (m_context->secret == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH secret key";
}
if (X<Curve>::keyLength() != m_context->secretLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH secret key";
}
X<Curve> s;
std::copy_n(m_context->secret, s.keyLength(), s.data());
return s;
}
const X<Curve> get_selfPublic(void) override {/**< Self Public key */
if (m_context->selfPublic == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH self public key";
}
if (X<Curve>::keyLength() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH self public key";
}
X<Curve> p;
std::copy_n(m_context->selfPublic, p.keyLength(), p.data());
return p;
}
const X<Curve> get_peerPublic(void) override { /**< Peer Public key */
if (m_context->peerPublic == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH peer public key";
}
if (X<Curve>::keyLength() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH peer public key";
}
X<Curve> p;
std::copy_n(m_context->peerPublic, p.keyLength(), p.data());
return p;
}
const X<Curve> get_sharedSecret(void) override { /**< ECDH output */
if (m_context->sharedSecret == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH shared secret";
}
if (X<Curve>::keyLength() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH output";
}
X<Curve> s;
std::copy_n(m_context->sharedSecret, s.keyLength(), s.data());
return s;
}
void set_secret(const X<Curve> &secret) override { /**< Secret key */
bctbx_ECDHSetSecretKey(m_context, secret.data(), secret.keyLength());
}
void set_selfPublic(const X<Curve> &selfPublic) override { /**< Self Public key */
bctbx_ECDHSetSelfPublicKey(m_context, selfPublic.data(), selfPublic.keyLength());
}
void set_peerPublic(const X<Curve> &peerPublic) override {; /**< Peer Public key */
bctbx_ECDHSetPeerPublicKey(m_context, peerPublic.data(), peerPublic.keyLength());
}
/**
* @Brief generate a new random ECDH key pair
*
* @param[in] rng The Random Number Generator to be used to generate the private kay
*/
void createKeyPair(std::shared_ptr<lime::RNG> rng) override {
// the dynamic cast will generate an exception if RNG is not actually a bctbx_RNG
bctbx_ECDHCreateKeyPair(m_context, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, dynamic_cast<lime::bctbx_RNG&>(*rng).get_context());
}
/**
* @brief Compute the self public key using the secret already set in context
*/
void deriveSelfPublic(void) override {
bctbx_ECDHDerivePublicKey(m_context);
}
/**
* @brief Perform the ECDH computation, shared secret is then available in the object via get_sharedSecret
*/
void computeSharedSecret(void) override {
bctbx_ECDHComputeSecret(m_context, nullptr, nullptr);
}
/**
* ctor/dtor
*/
bctbx_ECDH() {
m_context = bctbx_ECDHInit<Curve>();
}
~bctbx_ECDH(){
/* perform proper destroy cleaning buffers*/
bctbx_DestroyECDHContext(m_context);
m_context = nullptr;
}
}; // class bctbx_ECDH
/* Factory function */
template <typename Base>
std::shared_ptr<keyExchange<Base>> make_keyExchange() {
return std::make_shared<bctbx_ECDH<Base>>();
}
/* template instanciations for Curve 25519 and Curve 448 */
#ifdef EC25519_ENABLED
template class bctbx_ECDH<C255>;
template std::shared_ptr<keyExchange<C255>> make_keyExchange();
#endif
#ifdef EC448_ENABLED
template class bctbx_ECDH<C448>;
template std::shared_ptr<keyExchange<C448>> make_keyExchange();
#endif
/***** Signature ********************/
} // namespace lime
/*
lime_crypto_primitives.hpp
@author Johan Pascal
@copyright Copyright (C) 2017 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef lime_crypto_primitives_hpp
#define lime_crypto_primitives_hpp
#include <memory>
#include <vector>
#include "lime_keys.hpp"
namespace lime {
class RNG {
public:
/**
* @Brief fill given 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
*/
virtual void randomize(uint8_t *buffer, const size_t size) = 0;
/**
* @Brief fill given buffer with Random bytes
*
* @param[out] buffer vector to be filled with random bytes(based on original vector size)
*/
virtual void randomize(std::vector<uint8_t> buffer) = 0;
virtual ~RNG() = default;
}; //class RNG
/* Factory function */
std::shared_ptr<RNG> make_RNG();
template <typename Base>
class keyExchange {
public:
/* accessors */
virtual const X<Base> get_secret(void) = 0; /**< Secret key */
virtual const X<Base> get_selfPublic(void) = 0; /**< Self Public key */
virtual const X<Base> get_peerPublic(void) = 0; /**< Peer Public key */
virtual const X<Base> get_sharedSecret(void) = 0; /**< ECDH output */
virtual void set_secret(const X<Base> &secret) = 0; /**< Secret key */
virtual void set_selfPublic(const X<Base> &selfPublic) = 0; /**< Self Public key */
virtual void set_peerPublic(const X<Base> &peerPublic) = 0; /**< Peer Public key */
/**
* @Brief generate a new random key pair
*
* @param[in] rng The Random Number Generator to be used to generate the private kay
*/
virtual void createKeyPair(std::shared_ptr<lime::RNG> rng) = 0;
/**
* @brief Compute the self public key using the secret already set in context
*/
virtual void deriveSelfPublic(void) = 0;
/**
* @brief Perform the shared secret computation, it is then available in the object via get_sharedSecret
*/
virtual void computeSharedSecret(void) = 0;
virtual ~keyExchange() = default;
}; //class keyExchange
/* Factory function */
template <typename Curve>
std::shared_ptr<keyExchange<Curve>> make_keyExchange();
template <typename Curve>
class EdDSA {
public:
/* accessors */
virtual std::array<uint8_t, static_cast<size_t>(Curve::EDkeySize())> get_secret(void) = 0; /**< Secret key */
virtual std::array<uint8_t, static_cast<size_t>(Curve::EDkeySize())> get_public(void) = 0; /**< Self Public key */
virtual void set_secret(const std::array<uint8_t, static_cast<size_t>(Curve::EDkeySize())> &secret) = 0; /**< Secret key */
virtual void set_public(const std::array<uint8_t, static_cast<size_t>(Curve::EDkeySize())> &selfPublic) = 0; /**< Self Public key */
/**
* @Brief generate a new random EdDSA key pair
*
* @param[in] rng The Random Number Generator to be used to generate the private kay
*/
virtual void createKeyPair(std::shared_ptr<lime::RNG> rng) = 0;
/**
* @brief Compute the public key using the secret already set in context
*/
virtual void derivePublic(void) = 0;
/**
* @brief Sign a message using the key pair previously set in the object
*
* @param[in] message The message to be signed
* @param[in] associatedData A context for this signature, up to 255 bytes
* @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, const std::vector<uint8_t> &associatedData, std::array<uint8_t, static_cast<size_t>(Curve::EDSigSize())> &signature) = 0;
/**
* @brief Verify a message signature using the public key previously set in the object
*
* @param[in] message The message signed
* @param[in] associatedData A context for this signature, up to 255 bytes
* @param[in] signature The signature produced from the message with a key pair previously introduced in the object
*
* @return true if the signature is valid, false otherwise
*/
virtual bool verify(const std::vector<uint8_t> &message, const std::vector<uint8_t> &associatedData, const std::array<uint8_t, static_cast<size_t>(Curve::EDSigSize())> &signature) = 0;
/**
* @brief Key Format conversion(From EdDSA format to ECDH format)
*
* @param[out] target The ECDH object to store the key in
*/
virtual void convertPublicKeyToECDH(keyExchange<Curve> &target) = 0;
virtual void convertPrivateKeyToECDH(keyExchange<Curve> &target) = 0;
virtual ~EdDSA() = default;
}; //class EdDSA
/* this templates are instanciated once in the lime_crypto_primitives.cpp file, explicitly tell anyone including this header that there is no need to re-instanciate them */
#ifdef EC25519_ENABLED
extern template std::shared_ptr<keyExchange<C255>> make_keyExchange();
#endif
#ifdef EC448_ENABLED
extern template std::shared_ptr<keyExchange<C448>> make_keyExchange();
#endif
} // namespace lime
#endif //lime_crypto_primitives_hpp
......@@ -38,6 +38,7 @@ set(SOURCE_FILES_CXX
lime_double_ratchet-tester.cpp
lime_lime-tester.cpp
lime_helloworld-tester.cpp
lime_crypto-tester.cpp
)
bc_apply_compile_flags(SOURCE_FILES_CXX STRICT_OPTIONS_CPP STRICT_OPTIONS_CXX)
......
......@@ -30,6 +30,7 @@
static FILE * log_file = NULL;
static const char *log_domain = "lime";
bool cleanDatabase = true;
bool bench = false;
// settings used in lime suite
extern std::string test_x3dh_server_url;
......@@ -57,6 +58,7 @@ void lime_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list
if (ftester_printf == NULL) ftester_printf = log_handler;
bc_tester_init(ftester_printf, BCTBX_LOG_MESSAGE, BCTBX_LOG_ERROR, "data");
bc_tester_add_suite(&lime_crypto_test_suite);
bc_tester_add_suite(&lime_double_ratchet_test_suite);
bc_tester_add_suite(&lime_lime_test_suite);
bc_tester_add_suite(&lime_helloworld_test_suite);
......@@ -104,9 +106,10 @@ static const char* lime_helper =
"\t\t\t--c448-x3dh-server-port <port to use on x3dh server for instance running on curve448>, default : 25520\n"
#endif
"\t\t\t--operation-timeout <delay in ms to complete basic operations involving server>, default : 4000\n\t\t\t you may want to increase this value if you are not using a local X3DH server and experience tests failures\n"
"\t\t\t--keep-tmp-db, when set don't delete temporary db files created by tests, usefull for debug\n"
"\t\t\t--keep-tmp-db, when set don't delete temporary db files created by tests, useful for debug\n"
"\t\t\t--log-file <output log file path>\n";
"\t\t\t--log-file <output log file path>\n"
"\t\t\t--bench run benchmarks when set";
int main(int argc, char *argv[]) {
int i;
......@@ -148,6 +151,8 @@ int main(int argc, char *argv[]) {
lime_tester::wait_for_timeout=std::atoi(argv[i]);
} else if (strcmp(argv[i],"--keep-tmp-db")==0){
cleanDatabase=false;
} else if (strcmp(argv[i],"--bench")==0){
bench=true;
}else {
int ret = bc_tester_parse_args(argc, argv, i);
if (ret>0) {
......
......@@ -29,9 +29,11 @@
extern "C" {
extern bool cleanDatabase;
extern bool bench;
extern test_suite_t lime_double_ratchet_test_suite;
extern test_suite_t lime_lime_test_suite;
extern test_suite_t lime_helloworld_test_suite;
extern test_suite_t lime_crypto_test_suite;
void lime_tester_init(void(*ftester_printf)(int level, const char *fmt, va_list args));
void lime_tester_uninit(void);
......
/*
lime_crypto-tester.cpp
@author Johan Pascal
@copyright Copyright (C) 2018 Belledonne Communications SARL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define BCTBX_LOG_DOMAIN "lime-tester"
#include <bctoolbox/logging.h>
#include "lime-tester.hpp"
#include "lime-tester-utils.hpp"
#include "lime_keys.hpp"
#include "lime_crypto_primitives.hpp"
#include <bctoolbox/tester.h>
#include <bctoolbox/port.h>
#include <bctoolbox/exception.hh>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <stdio.h>
#include <string.h>
using namespace::std;
using namespace::lime;
constexpr uint64_t BENCH_TIMING_MS=200;
/* Function */
static void snprintSI(std::string &output, double x, const char *unit, const char *spacer = " ") {
const char *small[] = {" ","m","µ","n","p"};
const char *big[] = {" ","k","M","G","T"};
constexpr size_t tempBufferSize = 100;
char tempBuffer[tempBufferSize]; // hoping no one will use this function to print more than 100 chars...
if (12+strlen(spacer)+strlen(unit)>sizeof(tempBuffer)) {// 6 digit, 1 point, 2 digits + unit + 1 prefix + spacer + NULL term
throw BCTBX_EXCEPTION << "snprintSI tmpBuffer is too small to hold your data";
}
if (x < 1) {
unsigned di=0;
for (di=0; di<sizeof(small)/sizeof(*small)-1 && x && x < 1; di++) {
x *= 1000.0;
}
snprintf(tempBuffer, sizeof(tempBuffer), "%6.2f%s%s%s", x, spacer, small[di], unit);
} else {
unsigned di=0;
for (di=0; di<sizeof(big)/sizeof(*big)-1 && x && x >= 1000; di++) {
x /= 1000.0;
}
snprintf(tempBuffer, sizeof(tempBuffer), "%6.2f%s%s%s", x, spacer, big[di], unit);
}
output = tempBuffer;
}
template <typename Curve>
void keyExchange_test(void) {
/* We need a RNG */
std::shared_ptr<RNG> rng = make_RNG();
/* Create Alice and Bob ECDH context */
std::shared_ptr<keyExchange<Curve>> Alice = make_keyExchange<Curve>();
std::shared_ptr<keyExchange<Curve>> Bob = make_keyExchange<Curve>();
/* Generate key pairs */
Alice->createKeyPair(rng);
Bob->createKeyPair(rng);
/* Exchange keys */
Alice->set_peerPublic(Bob->get_selfPublic());
Bob->set_peerPublic(Alice->get_selfPublic());
/* Compute shared secret */
Alice->computeSharedSecret();
Bob->computeSharedSecret();
/* Compare them */
BC_ASSERT_TRUE(Alice->get_sharedSecret()==Bob->get_sharedSecret());
}
template <typename Curve>
void keyExchange_bench(uint64_t runTime_ms) {
constexpr size_t batch_size = 100;
/* We need a RNG */
std::shared_ptr<RNG> rng = make_RNG();
/* Create Alice and Bob ECDH context */
std::shared_ptr<keyExchange<Curve>> Alice = make_keyExchange<Curve>();
std::shared_ptr<keyExchange<Curve>> Bob = make_keyExchange<Curve>();
auto start = bctbx_get_cur_time_ms();
uint64_t span=0;
size_t runCount = 0;
while (span<runTime_ms) {
for (size_t i=0; i<batch_size; i++) {
/* Generate key pairs */
Alice->createKeyPair(rng);
Bob->createKeyPair(rng);
}
span = bctbx_get_cur_time_ms() - start;
runCount += batch_size;
}
auto freq = 2000*runCount/static_cast<double>(span);
std::string freq_unit, period_unit;
snprintSI(freq_unit, freq, "keys/s");
snprintSI(period_unit, 1/freq, "s/keys");
std::cout<<"Key generation "<<int(2*runCount)<<" ECDH keys in "<<int(span)<<" ms : "<<period_unit<<" "<<freq_unit<<endl;
/* Exchange keys */
Alice->set_peerPublic(Bob->get_selfPublic());
Bob->set_peerPublic(Alice->get_selfPublic());
start = bctbx_get_cur_time_ms();
span=0;
runCount = 0;
while (span<runTime_ms) {
for (size_t i=0; i<batch_size; i++) {
/* Compute shared secret */
Alice->computeSharedSecret();
}
span = bctbx_get_cur_time_ms() - start;
runCount += batch_size;
}
freq = 1000*runCount/static_cast<double>(span);
snprintSI(freq_unit, freq, "computations/s");
snprintSI(period_unit, 1/freq, "s/computation");
std::cout<<"Shared Secret "<<int(runCount)<<" computations in "<<int(span)<<" ms : "<<period_unit<<" "<<freq_unit<<endl<<endl;
}
static void exchange(void) {
#ifdef EC25519_ENABLED
keyExchange_test<C255>();
if (bench) {
std::cout<<"Bench for Curve 25519:"<<endl;
keyExchange_bench<C255>(BENCH_TIMING_MS);
}
#endif
#ifdef EC448_ENABLED
keyExchange_test<C448>();
if (bench) {
std::cout<<"Bench for Curve 448:"<<endl;
keyExchange_bench<C448>(BENCH_TIMING_MS);
}
#endif
}
static test_t tests[] = {
TEST_NO_TAG("Key Exchange", exchange),
};
test_suite_t lime_crypto_test_suite = {
"Crypto",
NULL,
NULL,
NULL,
NULL,
sizeof(tests) / sizeof(tests[0]),
tests
};
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