From 373bed91210d8e9953bc65c353caada6789f945a Mon Sep 17 00:00:00 2001 From: Thomas Kolb Date: Mon, 25 Nov 2019 22:00:48 +0100 Subject: [PATCH] Added class for challenge-response authentication --- include/ChallengeResponse.h | 29 ++++++++++++++++++ include/Crypto_Config.h.example | 8 +++++ src/ChallengeResponse.cpp | 54 +++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+) create mode 100644 include/ChallengeResponse.h create mode 100644 include/Crypto_Config.h.example create mode 100644 src/ChallengeResponse.cpp diff --git a/include/ChallengeResponse.h b/include/ChallengeResponse.h new file mode 100644 index 0000000..74b6319 --- /dev/null +++ b/include/ChallengeResponse.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +class ChallengeResponse +{ + public: + const unsigned long NONCE_LIFETIME_MS = 3000; + + ChallengeResponse(const std::string &pw); + + bool verify(const std::string &hash); + + /*! + * Initiate a new challenge-response round. + * + * Updates the internal state and returns the nonce to be sent to the client. + * The challenge must be answered within NONCE_LIFETIME_MS milliseconds. + * + * \returns The nonce. + */ + uint32_t nonce(void); + + private: + std::string m_passwd; + uint32_t m_currentNonce; + + unsigned long m_expireTime; +}; \ No newline at end of file diff --git a/include/Crypto_Config.h.example b/include/Crypto_Config.h.example new file mode 100644 index 0000000..031e505 --- /dev/null +++ b/include/Crypto_Config.h.example @@ -0,0 +1,8 @@ +#pragma once + +namespace crypto +{ + // replace these with your own values! + static const char * const HTTP_PASSWORD = "secure!1"; + static const char * const SALT = "1234567890abcdefghijklmnopqrstuv"; +} \ No newline at end of file diff --git a/src/ChallengeResponse.cpp b/src/ChallengeResponse.cpp new file mode 100644 index 0000000..3ca8ee8 --- /dev/null +++ b/src/ChallengeResponse.cpp @@ -0,0 +1,54 @@ +#include +#include + +#include // for esp_random() and millis() +#include + +#include "ChallengeResponse.h" + +#include "Crypto_Config.h" + +ChallengeResponse::ChallengeResponse(const std::string &pw) + : m_passwd(pw), m_expireTime(0) +{ +} + +bool ChallengeResponse::verify(const std::string &hash) +{ + if(millis() > m_expireTime) { + // challenge timed out + return false; + } + + std::ostringstream refResponse; + refResponse << m_passwd << ":" << m_currentNonce << ":" << crypto::SALT; + + // calculate hash of reference response + uint8_t sha256sum[32]; + mbedtls_sha256_ret(reinterpret_cast(refResponse.str().data()), + refResponse.str().length(), sha256sum, 0); + + // convert hash to hex + std::ostringstream hexHash; + for(size_t i = 0; i < 32; i++) { + static const char *conv = "0123456789abcdef"; + + uint8_t b = sha256sum[i]; + + hexHash << conv[(b >> 4)]; + hexHash << conv[(b &0x0F)]; + } + + std::string lowerHash; + std::transform(hash.begin(), hash.end(), lowerHash.begin(), + [](char c) { return std::tolower(c);}); + + return hexHash.str() == hash; +} + +uint32_t ChallengeResponse::nonce(void) +{ + m_currentNonce = esp_random(); + m_expireTime = millis() + NONCE_LIFETIME_MS; + return m_currentNonce; +} \ No newline at end of file