/* 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