Fork of the espurna firmware for `mhsw` switches
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
7.6 KiB

  1. // -----------------------------------------------------------------------------
  2. // WiFiClientSecure validation helpers
  3. // -----------------------------------------------------------------------------
  4. #pragma once
  5. #include "../espurna.h"
  6. #if SECURE_CLIENT != SECURE_CLIENT_NONE
  7. #include "../ntp.h"
  8. #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
  9. #include <WiFiClientSecureBearSSL.h>
  10. #elif SECURE_CLIENT == SECURE_CLIENT_AXTLS
  11. #include <WiFiClientSecureAxTLS.h>
  12. #endif
  13. namespace SecureClientHelpers {
  14. using host_callback_f = std::function<String()>;
  15. using check_callback_f = std::function<int()>;
  16. using fp_callback_f = std::function<String()>;
  17. using cert_callback_f = std::function<const char*()>;
  18. using mfln_callback_f = std::function<uint16_t()>;
  19. // TODO: workaround for `multiple definition of `SecureClientHelpers::_secureClientCheckAsString(int);'
  20. inline const char * _secureClientCheckAsString(int check) {
  21. switch (check) {
  22. case SECURE_CLIENT_CHECK_NONE: return "no validation";
  23. case SECURE_CLIENT_CHECK_FINGERPRINT: return "fingerprint validation";
  24. case SECURE_CLIENT_CHECK_CA: return "CA validation";
  25. default: return "unknown";
  26. }
  27. }
  28. #if SECURE_CLIENT == SECURE_CLIENT_AXTLS
  29. using SecureClientClass = axTLS::WiFiClientSecure;
  30. struct SecureClientConfig {
  31. SecureClientConfig(const char* tag, host_callback_f host_cb, check_callback_f check_cb, fp_callback_f fp_cb, bool debug = false) :
  32. tag(tag),
  33. on_host(host_cb),
  34. on_check(check_cb),
  35. on_fingerprint(fp_cb),
  36. debug(debug)
  37. {}
  38. String tag;
  39. host_callback_f on_host;
  40. check_callback_f on_check;
  41. fp_callback_f on_fingerprint;
  42. bool debug;
  43. };
  44. struct SecureClientChecks {
  45. SecureClientChecks(SecureClientConfig& config) :
  46. config(config)
  47. {}
  48. int getCheck() {
  49. return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK);
  50. }
  51. bool beforeConnected(SecureClientClass& client) {
  52. return true;
  53. }
  54. // Special condition for legacy client!
  55. // Otherwise, we are required to connect twice. And it is deemed broken & deprecated anyways...
  56. bool afterConnected(SecureClientClass& client) {
  57. bool result = false;
  58. int check = getCheck();
  59. if(config.debug) {
  60. DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check));
  61. }
  62. if (check == SECURE_CLIENT_CHECK_NONE) {
  63. if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str());
  64. result = true;
  65. } else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
  66. if (config.on_fingerprint) {
  67. char _buffer[60] = {0};
  68. if (config.on_fingerprint && config.on_host && sslFingerPrintChar(config.on_fingerprint().c_str(), _buffer)) {
  69. result = client.verify(_buffer, config.on_host().c_str());
  70. }
  71. if (!result) DEBUG_MSG_P(PSTR("[%s] Wrong fingerprint, cannot connect\n"), config.tag.c_str());
  72. }
  73. } else if (check == SECURE_CLIENT_CHECK_CA) {
  74. if (config.debug) DEBUG_MSG_P(PSTR("[%s] CA verification is not supported with axTLS client\n"), config.tag.c_str());
  75. }
  76. return result;
  77. }
  78. SecureClientConfig& config;
  79. bool debug;
  80. };
  81. #endif // SECURE_CLIENT_AXTLS
  82. #if SECURE_CLIENT == SECURE_CLIENT_BEARSSL
  83. using SecureClientClass = BearSSL::WiFiClientSecure;
  84. struct SecureClientConfig {
  85. SecureClientConfig(const char* tag, check_callback_f check_cb, cert_callback_f cert_cb, fp_callback_f fp_cb, mfln_callback_f mfln_cb, bool debug = false) :
  86. tag(tag),
  87. on_check(check_cb),
  88. on_certificate(cert_cb),
  89. on_fingerprint(fp_cb),
  90. on_mfln(mfln_cb),
  91. debug(debug)
  92. {}
  93. String tag;
  94. check_callback_f on_check;
  95. cert_callback_f on_certificate;
  96. fp_callback_f on_fingerprint;
  97. mfln_callback_f on_mfln;
  98. bool debug;
  99. };
  100. struct SecureClientChecks {
  101. SecureClientChecks(SecureClientConfig& config) :
  102. config(config)
  103. {}
  104. int getCheck() {
  105. return (config.on_check) ? config.on_check() : (SECURE_CLIENT_CHECK);
  106. }
  107. bool prepareMFLN(SecureClientClass& client) {
  108. const uint16_t requested_mfln = (config.on_mfln) ? config.on_mfln() : (SECURE_CLIENT_MFLN);
  109. bool result = false;
  110. switch (requested_mfln) {
  111. // default, do nothing
  112. case 0:
  113. result = true;
  114. break;
  115. // match valid sizes only
  116. case 512:
  117. case 1024:
  118. case 2048:
  119. case 4096:
  120. {
  121. client.setBufferSizes(requested_mfln, requested_mfln);
  122. result = true;
  123. if (config.debug) {
  124. DEBUG_MSG_P(PSTR("[%s] MFLN buffer size set to %u\n"), config.tag.c_str(), requested_mfln);
  125. }
  126. break;
  127. }
  128. default:
  129. {
  130. if (config.debug) {
  131. DEBUG_MSG_P(PSTR("[%s] Warning: MFLN buffer size must be one of 512, 1024, 2048 or 4096\n"), config.tag.c_str());
  132. }
  133. }
  134. }
  135. return result;
  136. }
  137. bool beforeConnected(SecureClientClass& client) {
  138. int check = getCheck();
  139. bool settime = (check == SECURE_CLIENT_CHECK_CA);
  140. if(config.debug) {
  141. DEBUG_MSG_P(PSTR("[%s] Using SSL check type: %s\n"), config.tag.c_str(), _secureClientCheckAsString(check));
  142. }
  143. if (!ntpSynced() && settime) {
  144. if (config.debug) DEBUG_MSG_P(PSTR("[%s] Time not synced! Cannot use CA validation\n"), config.tag.c_str());
  145. return false;
  146. }
  147. prepareMFLN(client);
  148. if (check == SECURE_CLIENT_CHECK_NONE) {
  149. if (config.debug) DEBUG_MSG_P(PSTR("[%s] !!! Secure connection will not be validated !!!\n"), config.tag.c_str());
  150. client.setInsecure();
  151. } else if (check == SECURE_CLIENT_CHECK_FINGERPRINT) {
  152. uint8_t _buffer[20] = {0};
  153. if (config.on_fingerprint && sslFingerPrintArray(config.on_fingerprint().c_str(), _buffer)) {
  154. client.setFingerprint(_buffer);
  155. }
  156. } else if (check == SECURE_CLIENT_CHECK_CA) {
  157. #if NTP_LEGACY_SUPPORT
  158. client.setX509Time(ntpLocal2UTC(now()));
  159. #else
  160. client.setX509Time(now());
  161. #endif
  162. if (!certs.getCount()) {
  163. if (config.on_certificate) certs.append(config.on_certificate());
  164. }
  165. client.setTrustAnchors(&certs);
  166. }
  167. return true;
  168. }
  169. bool afterConnected(SecureClientClass&) {
  170. return true;
  171. }
  172. bool debug;
  173. SecureClientConfig& config;
  174. BearSSL::X509List certs;
  175. };
  176. #endif // SECURE_CLIENT_BEARSSL
  177. class SecureClient {
  178. public:
  179. SecureClient(SecureClientConfig& config) :
  180. _config(config),
  181. _checks(_config),
  182. _client(std::make_unique<SecureClientClass>())
  183. {}
  184. bool afterConnected() {
  185. return _checks.afterConnected(get());
  186. }
  187. bool beforeConnected() {
  188. return _checks.beforeConnected(get());
  189. }
  190. SecureClientClass& get() {
  191. return *_client.get();
  192. }
  193. private:
  194. SecureClientConfig _config;
  195. SecureClientChecks _checks;
  196. std::unique_ptr<SecureClientClass> _client;
  197. };
  198. };
  199. using SecureClientConfig = SecureClientHelpers::SecureClientConfig;
  200. using SecureClientChecks = SecureClientHelpers::SecureClientChecks;
  201. using SecureClient = SecureClientHelpers::SecureClient;
  202. #endif // SECURE_CLIENT != SECURE_CLIENT_NONE