/*
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 .
*/
#include "lime_crypto_primitives.hpp"
#include "bctoolbox/crypto.h"
#include "bctoolbox/exception.hh"
namespace lime {
/* template instanciations for Curves 25519 and 448, done */
#if EC25519_ENABLED
template class X;
template class X;
template class X;
template class Xpair;
template class DSA;
template class DSA;
template class DSA;
template class DSApair;
#endif
#if EC448_ENABLED
template class X;
template class X;
template class X;
template class Xpair;
template class DSA;
template class DSA;
template class DSA;
template class DSApair;
#endif
/***** Random Number Generator ********/
/**
* A wrapper around the bctoolbox Random Number Generator
*/
class bctbx_RNG : public RNG {
private :
bctbx_rng_context_t *m_context; // the bctoolbox RNG context
/* only bctbx_EDDSA and bctbx_ECDH needs a direct access to the actual RNG context */
template friend class bctbx_EDDSA;
template friend class bctbx_ECDH;
/**
* @brief access internal RNG context
* Used internally by the bctoolbox wrapper, is not exposed to the lime_crypto_primitive API.
*
* @return a pointer to the RNG context
*/
bctbx_rng_context_t *get_context(void) {
return m_context;
}
public:
/* accessor */
/**
* @brief fill the given RandomSeed buffer with Random bytes
*
* @param[in,out] buffer point to the beginning of the buffer to be filled with random bytes
*/
void randomize(sBuffer &buffer) override {
bctbx_rng_get(m_context, buffer.data(), buffer.size());
};
/**
* @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.
*
* @return a random 32 bits unsigned integer
*/
uint32_t randomize() override {
std::array 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(buffer[0])<<23 | static_cast(buffer[1])<<16 | static_cast(buffer[2])<<8 | static_cast(buffer[3]));
};
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 make_RNG() {
return std::make_shared();
}
/***** Signature ********************/
/* bctbx_EdDSA specialized constructor */
template
bctbx_EDDSAContext_t *bctbx_EDDSAInit(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 Signature class constructor for your type");
return nullptr;
}
#ifdef EC25519_ENABLED
/* specialise ECDH context creation */
template <> bctbx_EDDSAContext_t *bctbx_EDDSAInit(void) {
return bctbx_CreateEDDSAContext(BCTBX_EDDSA_25519);
}
#endif // EC25519_ENABLED
#ifdef EC448_ENABLED
/* specialise ECDH context creation */
template <> bctbx_EDDSAContext_t *bctbx_EDDSAInit(void) {
return bctbx_CreateEDDSAContext(BCTBX_EDDSA_448);
}
#endif // EC448_ENABLED
/**
* a wrapper around bctoolbox Signature algorithm.
* Provides EdDSA on curves 25519 and 448
*/
template
class bctbx_EDDSA : public Signature {
private :
bctbx_EDDSAContext_t *m_context; // the EDDSA context
public :
/* accessors */
const DSA get_secret(void) override { /**< Secret key */
if (m_context->secretKey == nullptr) {
throw BCTBX_EXCEPTION << "invalid EdDSA secret key";
}
if (DSA::ssize() != m_context->secretLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store EdDSA secret key";
}
DSA s;
std::copy_n(m_context->secretKey, s.ssize(), s.data());
return s;
}
const DSA get_public(void) override {/**< Self Public key */
if (m_context->publicKey == nullptr) {
throw BCTBX_EXCEPTION << "invalid EdDSA public key";
}
if (DSA::ssize() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store EdDSA public key";
}
DSA p;
std::copy_n(m_context->publicKey, p.ssize(), p.data());
return p;
}
/* Setting keys */
void set_secret(const DSA &secretKey) override { /**< Secret key */
bctbx_EDDSA_setSecretKey(m_context, secretKey.data(), secretKey.ssize());
}
void set_public(const DSA &publicKey) override { /**< Self Public key */
bctbx_EDDSA_setPublicKey(m_context, publicKey.data(), publicKey.ssize());
}
/**
* @brief generate a new random EdDSA key pair
*
* @param[in] rng The Random Number Generator to be used to generate the private kay
*/
void createKeyPair(std::shared_ptr rng) override {
// the dynamic cast will generate an exception if RNG is not actually a bctbx_RNG
bctbx_EDDSACreateKeyPair(m_context, (int (*)(void *, uint8_t *, size_t))bctbx_rng_get, dynamic_cast(*rng).get_context());
}
/**
* @brief Compute the public key using the secret already set in context
*/
void derivePublic(void) override {
bctbx_EDDSADerivePublicKey(m_context);
}
/**
* @brief Sign a message using the key pair previously set in the object
*
* @param[in] message The message to be signed
* @param[out] signature The signature produced from the message with a key pair previously introduced in the object
*/
void sign(const std::vector &message, DSA &signature) override {
auto sigSize = signature.size();
bctbx_EDDSA_sign(m_context, message.data(), message.size(), nullptr, 0, signature.data(), &sigSize);
}
/**
* @overload void bctbx_EDDSA::sign(const X &message, DSA &signature)
* a convenience function to directly verify a key exchange public key
*/
void sign(const X &message, DSA &signature) override {
auto sigSize = signature.size();
bctbx_EDDSA_sign(m_context, message.data(), message.ssize(), nullptr, 0, signature.data(), &sigSize);
}
/**
* @brief Verify a message signature using the public key previously set in the object
*
* @param[in] message The message signed
* @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
*/
bool verify(const std::vector &message, const DSA &signature) override {
return (bctbx_EDDSA_verify(m_context, message.data(), message.size(), nullptr, 0, signature.data(), signature.size()) == BCTBX_VERIFY_SUCCESS);
}
/**
* @overload bool bctbx_EDDSA::verify(const X &message, const DSA &signature)
* a convenience function to directly verify a key exchange public key
*/
bool verify(const X &message, const DSA &signature) override {
return (bctbx_EDDSA_verify(m_context, message.data(), message.ssize(), nullptr, 0, signature.data(), signature.ssize()) == BCTBX_VERIFY_SUCCESS);
}
/**
* ctor/dtor
*/
bctbx_EDDSA() {
m_context = bctbx_EDDSAInit();
}
~bctbx_EDDSA(){
/* perform proper destroy cleaning buffers*/
bctbx_DestroyEDDSAContext(m_context);
m_context = nullptr;
}
}; // class bctbx_EDDSA
/***** Key Exchange ******************/
/* bctbx_ECDH specialized constructor */
template
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 keyExchange class contructor for your type");
return nullptr;
}
#ifdef EC25519_ENABLED
/* specialise ECDH context creation */
template <> bctbx_ECDHContext_t *bctbx_ECDHInit(void) {
return bctbx_CreateECDHContext(BCTBX_ECDH_X25519);
}
#endif //EC25519_ENABLED
#ifdef EC448_ENABLED
/* specialise ECDH context creation */
template <> bctbx_ECDHContext_t *bctbx_ECDHInit(void) {
return bctbx_CreateECDHContext(BCTBX_ECDH_X448);
}
#endif //EC448_ENABLED
/**
* a wrapper around bctoolbox key exchange algorithm.
* Provides X25519 and X448
*/
template
class bctbx_ECDH : public keyExchange {
private :
bctbx_ECDHContext_t *m_context; // the ECDH context
public :
/* accessors */
const X get_secret(void) override { /**< Secret key */
if (m_context->secret == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH secret key";
}
if (X::ssize() != m_context->secretLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH secret key";
}
X s;
std::copy_n(m_context->secret, s.ssize(), s.data());
return s;
}
const X get_selfPublic(void) override {/**< Self Public key */
if (m_context->selfPublic == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH self public key";
}
if (X::ssize() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH self public key";
}
X p;
std::copy_n(m_context->selfPublic, p.ssize(), p.data());
return p;
}
const X get_peerPublic(void) override { /**< Peer Public key */
if (m_context->peerPublic == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH peer public key";
}
if (X::ssize() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH peer public key";
}
X p;
std::copy_n(m_context->peerPublic, p.ssize(), p.data());
return p;
}
const X get_sharedSecret(void) override { /**< ECDH output */
if (m_context->sharedSecret == nullptr) {
throw BCTBX_EXCEPTION << "invalid ECDH shared secret";
}
if (X::ssize() != m_context->pointCoordinateLength) {
throw BCTBX_EXCEPTION << "Invalid buffer to store ECDH output";
}
X s;
std::copy_n(m_context->sharedSecret, s.ssize(), s.data());
return s;
}
/* Setting keys, accept Signature keys */
void set_secret(const X &secret) override { /**< Secret key */
bctbx_ECDHSetSecretKey(m_context, secret.data(), secret.ssize());
}
void set_secret(const DSA &secret) override { /**< Secret key */
// we must create a temporary bctbx_EDDSA context and set the given key in
auto tmp_context = bctbx_EDDSAInit();
bctbx_EDDSA_setSecretKey(tmp_context, secret.data(), secret.ssize());
// Convert
bctbx_EDDSA_ECDH_privateKeyConversion(tmp_context, m_context);
// Cleaning
bctbx_DestroyEDDSAContext(tmp_context);
}
void set_selfPublic(const X &selfPublic) override { /**< Self Public key */
bctbx_ECDHSetSelfPublicKey(m_context, selfPublic.data(), selfPublic.ssize());
}
void set_selfPublic(const DSA &selfPublic) override { /**< Self Public key */
// we must create a temporary bctbx_EDDSA context and set the given key in
auto tmp_context = bctbx_EDDSAInit();
bctbx_EDDSA_setPublicKey(tmp_context, selfPublic.data(), selfPublic.ssize());
// Convert in self Public
bctbx_EDDSA_ECDH_publicKeyConversion(tmp_context, m_context, BCTBX_ECDH_ISSELF);
// Cleaning
bctbx_DestroyEDDSAContext(tmp_context);
}
void set_peerPublic(const X &peerPublic) override {; /**< Peer Public key */
bctbx_ECDHSetPeerPublicKey(m_context, peerPublic.data(), peerPublic.ssize());
}
void set_peerPublic(const DSA &peerPublic) override {; /**< Peer Public key */
// we must create a temporary bctbx_EDDSA context and set the given key in
auto tmp_context = bctbx_EDDSAInit();
bctbx_EDDSA_setPublicKey(tmp_context, peerPublic.data(), peerPublic.ssize());
// Convert in peer Public
bctbx_EDDSA_ECDH_publicKeyConversion(tmp_context, m_context, BCTBX_ECDH_ISPEER);
// Cleaning
bctbx_DestroyEDDSAContext(tmp_context);
}
/**
* @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 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(*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();
}
~bctbx_ECDH(){
/* perform proper destroy cleaning buffers*/
bctbx_DestroyECDHContext(m_context);
m_context = nullptr;
}
}; // class bctbx_ECDH
/* Factory functions */
template
std::shared_ptr> make_keyExchange() {
return std::make_shared>();
}
template
std::shared_ptr> make_Signature() {
return std::make_shared>();
}
/* HMAC templates */
/* HMAC must use a specialized template */
template
void HMAC(const uint8_t *const key, const size_t keySize, const uint8_t *const input, const size_t inputSize, uint8_t *hash, size_t hashSize) {
/* if this template is instanciated the static_assert will fail but will give us an error message with faulty Curve type */
static_assert(sizeof(hashAlgo) != sizeof(hashAlgo), "You must specialize HMAC_KDF function template");
}
/* HMAC specialized template for SHA512 */
template <> void HMAC(const uint8_t *const key, const size_t keySize, const uint8_t *const input, const size_t inputSize, uint8_t *hash, size_t hashSize) {
bctbx_hmacSha512(key, keySize, input, inputSize, std::min(SHA512::ssize(),hashSize), hash);
}
/* generic implementation, of HKDF RFC-5869 */
template
void HMAC_KDF(const uint8_t *const salt, const size_t saltSize, const uint8_t *const ikm, const size_t ikmSize, const infoType &info, uint8_t *output, size_t outputSize) {
std::array prk; // hold the output of pre-computation
// extraction
HMAC(salt, saltSize, ikm, ikmSize, prk.data(), prk.size());
// expansion round 0
std::vector T(info.cbegin(), info.cend());
T.push_back(0x01);
HMAC(prk.data(), prk.size(), T.data(), T.size(), output, outputSize);
// successives expansion rounds
size_t index = std::min(outputSize, hashAlgo::ssize());
for(uint8_t i=0x02; index < outputSize; i++) {
T.assign(output+(i-2)*hashAlgo::ssize(), output+(i-1)*hashAlgo::ssize());
T.insert(T.end(), info.cbegin(), info.cend());
T.push_back(i);
HMAC(prk.data(), prk.size(), T.data(), T.size(), output+index, outputSize-index);
index += hashAlgo::ssize();
}
cleanBuffer(prk.data(), prk.size());
cleanBuffer(T.data(), T.size());
}
template
void HMAC_KDF(const std::vector &salt, const std::vector &ikm, const infoType &info, uint8_t *output, size_t outputSize) {
HMAC_KDF(salt.data(), salt.size(), ikm.data(), ikm.size(), info, output, outputSize);
};
/* instanciate HMAC_KDF template with SHA512 and string or vector info */
template void HMAC_KDF>(const uint8_t *const salt, const size_t saltSize, const uint8_t *const ikm, const size_t ikmSize, const std::vector &info, uint8_t *output, size_t outputSize);
template void HMAC_KDF(const uint8_t *const salt, const size_t saltSize, const uint8_t *const ikm, const size_t ikmSize, const std::string &info, uint8_t *output, size_t outputSize);
template void HMAC_KDF>(const std::vector &salt, const std::vector &ikm, const std::vector &info, uint8_t *output, size_t outputSize);
template void HMAC_KDF(const std::vector &salt, const std::vector &ikm, const std::string &info, uint8_t *output, size_t outputSize);
/* AEAD template must be specialized */
template
void AEAD_encrypt(const uint8_t *const key, const size_t keySize, const uint8_t *const IV, const size_t IVSize,
const uint8_t *const plain, const size_t plainSize, const uint8_t *const AD, const size_t ADSize,
uint8_t *tag, const size_t tagSize, uint8_t *cipher) {
/* if this template is instanciated the static_assert will fail but will give us an error message with faulty type */
static_assert(sizeof(AEADAlgo) != sizeof(AEADAlgo), "You must specialize AEAD_encrypt function template");
}
template
bool AEAD_decrypt(const uint8_t *const key, const size_t keySize, const uint8_t *const IV, const size_t IVSize,
const uint8_t *const cipher, const size_t cipherSize, const uint8_t *const AD, const size_t ADSize,
const uint8_t *const tag, const size_t tagSize, uint8_t *plain) {
/* if this template is instanciated the static_assert will fail but will give us an error message with faulty type */
static_assert(sizeof(AEADAlgo) != sizeof(AEADAlgo), "You must specialize AEAD_decrypt function template");
return false;
}
/* AEAD scheme specialiazed template with AES256-GCM, 16 bytes auth tag */
template <> void AEAD_encrypt(const uint8_t *const key, const size_t keySize, const uint8_t *const IV, const size_t IVSize,
const uint8_t *const plain, const size_t plainSize, const uint8_t *const AD, const size_t ADSize,
uint8_t *tag, const size_t tagSize, uint8_t *cipher) {
/* perforn checks on sizes */
if (keySize != AES256GCM::keySize() || tagSize != AES256GCM::tagSize()) {
throw BCTBX_EXCEPTION << "invalid arguments for AEAD_encrypt AES256-GCM";
}
auto ret = bctbx_aes_gcm_encrypt_and_tag(key, keySize, plain, plainSize, AD, ADSize, IV, IVSize, tag, tagSize, cipher);
if (ret != 0) {
throw BCTBX_EXCEPTION << "AEAD_encrypt AES256-GCM error: "< bool AEAD_decrypt(const uint8_t *const key, const size_t keySize, const uint8_t *const IV, const size_t IVSize,
const uint8_t *const cipher, const size_t cipherSize, const uint8_t *const AD, const size_t ADSize,
const uint8_t *const tag, const size_t tagSize, uint8_t *plain) {
/* perforn checks on sizes */
if (keySize != AES256GCM::keySize() || tagSize != AES256GCM::tagSize()) {
throw BCTBX_EXCEPTION << "invalid arguments for AEAD_decrypt AES256-GCM";
}
auto ret = bctbx_aes_gcm_decrypt_and_auth(key, keySize, cipher, cipherSize, AD, ADSize, IV, IVSize, tag, tagSize, plain);
if (ret == 0) return true;
if (ret == BCTBX_ERROR_AUTHENTICATION_FAILED) return false;
throw BCTBX_EXCEPTION << "AEAD_decrypt AES256-GCM error: "<::ssize(), "bctoolbox and local defines mismatch");
// for ECDH public value and shared secret have the same size
static_assert(BCTBX_ECDH_X25519_PUBLIC_SIZE == X::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_ECDH_X25519_PRIVATE_SIZE == X::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_25519_PUBLIC_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_25519_PRIVATE_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_25519_SIGNATURE_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
#endif //EC25519_ENABLED
#ifdef EC448_ENABLED
static_assert(BCTBX_ECDH_X448_PUBLIC_SIZE == X::ssize(), "bctoolbox and local defines mismatch");
// for ECDH public value and shared secret have the same size
static_assert(BCTBX_ECDH_X448_PUBLIC_SIZE == X::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_ECDH_X448_PRIVATE_SIZE == X::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_448_PUBLIC_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_448_PRIVATE_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
static_assert(BCTBX_EDDSA_448_SIGNATURE_SIZE == DSA::ssize(), "bctoolbox and local defines mismatch");
#endif //EC448_ENABLED
void cleanBuffer(uint8_t *buffer, size_t size) {
bctbx_clean(buffer, size);
}
/* template instanciations for Curve 25519 and Curve 448 */
#ifdef EC25519_ENABLED
template class bctbx_ECDH;
template class bctbx_EDDSA;
template std::shared_ptr> make_keyExchange();
template std::shared_ptr> make_Signature();
#endif //EC25519_ENABLED
#ifdef EC448_ENABLED
template class bctbx_ECDH;
template class bctbx_EDDSA;
template std::shared_ptr> make_keyExchange();
template std::shared_ptr> make_Signature();
#endif //EC448_ENABLED
} // namespace lime