From 464d1c155e001c340bd3e92c5215a3507da2de13 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Sep 2021 21:22:10 +0200 Subject: [PATCH 001/367] [MQTT TLS] Add TLS support for MQTT --- docs/source/Controller/C016.rst | 2 +- docs/source/Controller/_Controller.rst | 63 +++++++++++++ platformio_esp82xx_envs.ini | 1 + src/_C002.ino | 3 + src/_C005.ino | 4 + src/_C006.ino | 4 + src/_C014.ino | 4 + .../DataStructs/ControllerSettingsStruct.cpp | 40 ++++++++ .../DataStructs/ControllerSettingsStruct.h | 8 +- src/src/DataStructs/ProtocolStruct.cpp | 11 ++- src/src/DataStructs/ProtocolStruct.h | 37 ++++---- src/src/DataTypes/TLS_types.cpp | 12 +++ src/src/DataTypes/TLS_types.h | 20 ++++ src/src/ESPEasyCore/Controller.cpp | 94 ++++++++++++++++++- src/src/Globals/MQTT.cpp | 17 +++- src/src/Globals/MQTT.h | 22 ++++- src/src/Helpers/_CPlugin_Helper_webform.cpp | 35 ++++++- src/src/WebServer/ControllerPage.cpp | 8 +- tools/pio/pre_custom_esp32.py | 1 + tools/pio/pre_custom_esp82xx.py | 1 + 20 files changed, 356 insertions(+), 31 deletions(-) create mode 100644 src/src/DataTypes/TLS_types.cpp create mode 100644 src/src/DataTypes/TLS_types.h diff --git a/docs/source/Controller/C016.rst b/docs/source/Controller/C016.rst index 53ee64861d..40eb6484cb 100644 --- a/docs/source/Controller/C016.rst +++ b/docs/source/Controller/C016.rst @@ -45,7 +45,7 @@ Each time a plugin sends data to this controller, a sample set is stored. A typical sample set contains: -- Timestamp (Default: Unix Time, but can be switched to "local time" in the controller settings) +- Timestamp (Default: Unix Time, but can be switched to "local time" in the controller settings with the "Use Local System Time" checkbox) - task index delivering the data - 4 float values diff --git a/docs/source/Controller/_Controller.rst b/docs/source/Controller/_Controller.rst index e990276c9d..ed2ee60359 100644 --- a/docs/source/Controller/_Controller.rst +++ b/docs/source/Controller/_Controller.rst @@ -74,6 +74,69 @@ before WiFi connection is made or during lost connection. For almost all controllers, sending data is a blocking call, so it may halt execution of other code on the node. With timouts longer than 2 seconds, the ESP may reboot as the software watchdog may step in. +TLS configuration +----------------- + +Added: 2021-09-26 + +Some protocols like MQTT may use TLS to provide a secure connection to the broker. + + +Still under development. +Notes: + +BearSSL::WiFiClientSecure net; + +Retrieve CA root certificate: +net.setCACert(local_root_ca); +BearSSL::X509List cert(digicert); +net.setTrustAnchors(&cert); + + +Retrieve public key of a specific certificate: ``openssl x509 -pubkey -noout -in ca.crt`` +BearSSL::PublicKey key(pubkey); +net.setKnownKey(&key); + + +Use certificate fingerprint (HEX checksum of certificate): +openssl x509 -fingerrint -in ca.crt + +net.setFingerprint(fp); + +Self Signed certificate Mosquitto: http://www.steves-internet-guide.com/mosquitto-tls/ +Let's encrypt Mosquitto: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-the-mosquitto-mqtt-messaging-broker-on-ubuntu-18-04-quickstart + +See: https://www.youtube.com/watch?v=ytQUbyab4es + +https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt + +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- Sample ThingSpeak configuration diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index c52c850dbd..87654eaf8f 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -36,6 +36,7 @@ build_flags = ${regular_platform.build_flags} -DPLUGIN_BUILD_CUSTOM lib_ignore = ${esp8266_custom_common.lib_ignore} extra_scripts = ${esp8266_custom_common.extra_scripts} +board_build.f_cpu = 160000000L [env:custom_IR_ESP8266_4M1M] extends = esp8266_4M1M diff --git a/src/_C002.ino b/src/_C002.ino index 12888b832c..0e89a3fee5 100644 --- a/src/_C002.ino +++ b/src/_C002.ino @@ -38,6 +38,9 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = true; + #ifdef USE_MQTT_TLS + Protocol[protocolCount].usesTLS = true; + #endif break; } diff --git a/src/_C005.ino b/src/_C005.ino index 0f50026784..e728d3fcc2 100644 --- a/src/_C005.ino +++ b/src/_C005.ino @@ -36,6 +36,10 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; + #ifdef USE_MQTT_TLS + Protocol[protocolCount].usesTLS = true; + #endif + break; } diff --git a/src/_C006.ino b/src/_C006.ino index cdd7fd2256..a20df0b38c 100644 --- a/src/_C006.ino +++ b/src/_C006.ino @@ -37,6 +37,10 @@ bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; + #ifdef USE_MQTT_TLS + Protocol[protocolCount].usesTLS = true; + #endif + break; } diff --git a/src/_C014.ino b/src/_C014.ino index 939aae002b..2627733e79 100644 --- a/src/_C014.ino +++ b/src/_C014.ino @@ -181,6 +181,10 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; + #ifdef USE_MQTT_TLS + Protocol[protocolCount].usesTLS = true; + #endif + break; } diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 90844cece3..5fe05a4561 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -3,6 +3,7 @@ #include "../../ESPEasy_common.h" #include "../CustomBuild/ESPEasyLimits.h" +#include "../ESPEasyCore/ESPEasy_Log.h" #include "../ESPEasyCore/ESPEasyNetwork.h" #include "../Helpers/Misc.h" #include "../Helpers/Networking.h" @@ -14,6 +15,7 @@ #include #include + ControllerSettingsStruct::ControllerSettingsStruct() { reset(); @@ -75,6 +77,29 @@ void ControllerSettingsStruct::validate() { ZERO_TERMINATE(MQTTLwtTopic); ZERO_TERMINATE(LWTMessageConnect); ZERO_TERMINATE(LWTMessageDisconnect); + + #ifdef USES_MQTT + #ifdef USE_MQTT_TLS + if (TLStype() == TLS_types::NoTLS) { + if (Port == 8883) { + Port = 1883; + addLog(LOG_LEVEL_ERROR, F("Not using TLS, but port set to secure 8883. Use port 1883 instead")); + } + } else { + if (Port == 1883) { + Port = 8883; + addLog(LOG_LEVEL_ERROR, F("Using TLS, but port set to insecure port 1883. Use port 8883 instead")); + } + } + #else + if (Port == 8883) { + // No TLS support, so when switching builds, make sure it can still work. + Port = 1883; + addLog(LOG_LEVEL_ERROR, F("Not using TLS, but port set to secure 8883. Use port 1883 instead")); + } + #endif + #endif + } IPAddress ControllerSettingsStruct::getIP() const { @@ -286,3 +311,18 @@ void ControllerSettingsStruct::useLocalSystemTime(bool value) { bitWrite(VariousFlags, 11, value); } + +TLS_types ControllerSettingsStruct::TLStype() const +{ + // Store it in bits 12, 13, 14 + const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0x7); + return tls_type; +} + +void ControllerSettingsStruct::TLStype(TLS_types tls_type) +{ + const uint32_t mask = ~(0x7); + VariousFlags &= mask; // Clear the bits + const uint32_t tls_type_val = static_cast(tls_type) << 12; + VariousFlags |= tls_type_val; +} diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 6a87f97943..06ce6c3107 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -9,6 +9,7 @@ #include // for std::nothrow #include "../../ESPEasy_common.h" +#include "../DataTypes/TLS_types.h" #include "../Globals/Plugins.h" class IPAddress; @@ -63,6 +64,7 @@ struct ControllerSettingsStruct CONTROLLER_HOSTNAME, CONTROLLER_IP, CONTROLLER_PORT, + CONTROLLER_MQTT_TLS_TYPE, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, @@ -145,10 +147,14 @@ struct ControllerSettingsStruct bool useLocalSystemTime() const; void useLocalSystemTime(bool value); + + + TLS_types TLStype() const; + void TLStype(TLS_types tls_type); boolean UseDNS; - uint8_t IP[4]; + uint8_t IP[4]; unsigned int Port; char HostName[65]; char Publish[129]; diff --git a/src/src/DataStructs/ProtocolStruct.cpp b/src/src/DataStructs/ProtocolStruct.cpp index 6306ce42f2..5fcc004c26 100644 --- a/src/src/DataStructs/ProtocolStruct.cpp +++ b/src/src/DataStructs/ProtocolStruct.cpp @@ -1,10 +1,11 @@ #include "ProtocolStruct.h" ProtocolStruct::ProtocolStruct() : - defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), - usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), - usesQueue(true), usesCheckReply(true), usesTimeout(true), usesSampleSets(false), - usesExtCreds(false), needsNetwork(true), allowsExpire(true), allowLocalSystemTime(false) {} + defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), + usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), + usesQueue(true), usesCheckReply(true), usesTimeout(true), usesSampleSets(false), + usesExtCreds(false), needsNetwork(true), allowsExpire(true), allowLocalSystemTime(false), + usesTLS(false) {} bool ProtocolStruct::useCredentials() const { return usesAccount || usesPassword; @@ -15,4 +16,4 @@ bool ProtocolStruct::useExtendedCredentials() const { return useCredentials(); } return false; -} \ No newline at end of file +} diff --git a/src/src/DataStructs/ProtocolStruct.h b/src/src/DataStructs/ProtocolStruct.h index e580f0b6b5..e8f7737471 100644 --- a/src/src/DataStructs/ProtocolStruct.h +++ b/src/src/DataStructs/ProtocolStruct.h @@ -16,23 +16,26 @@ struct ProtocolStruct bool useExtendedCredentials() const; uint16_t defaultPort; - uint8_t Number; - bool usesMQTT : 1; - bool usesAccount : 1; - bool usesPassword : 1; - bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics - bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data - bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page - bool usesHost : 1; - bool usesPort : 1; - bool usesQueue : 1; - bool usesCheckReply : 1; - bool usesTimeout : 1; - bool usesSampleSets : 1; - bool usesExtCreds : 1; - bool needsNetwork : 1; - bool allowsExpire : 1; - bool allowLocalSystemTime : 1; + uint8_t Number; + bool usesMQTT : 1; // Indicating whether it is a MQTT controller + bool usesAccount : 1; // Offer to enter credentials + bool usesPassword : 1; + bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics + bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data + bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page + bool usesHost : 1; // Offer either DNS hostname or IP + bool usesPort : 1; // Offer to set a port nr. This can be network port, but depending on controller this may be a + // different type of port. See LoRaWAN for example. + bool usesQueue : 1; // Allow to queue messages + bool usesCheckReply : 1; // Allow optional wait for reply + bool usesTimeout : 1; // Offer to set a timeout. + bool usesSampleSets : 1; // A sample set is an extra counter which is incremented as soon as a new value of set task is seen. + // (to keep track of bursts of messages where some may be lost) + bool usesExtCreds : 1; // Offer to store longer credentials + bool needsNetwork : 1; // Whether it needs a network connection to work + bool allowsExpire : 1; // Whether queued messages may be removed from the queue after some time + bool allowLocalSystemTime : 1; // Allow switching between Unix time and local time (including timezone and DST) + bool usesTLS : 1; // May offer TLS related settings and options }; typedef std::vector ProtocolVector; diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp new file mode 100644 index 0000000000..5eaf38b4b1 --- /dev/null +++ b/src/src/DataTypes/TLS_types.cpp @@ -0,0 +1,12 @@ +#include "../DataTypes/TLS_types.h" + +const __FlashStringHelper* toString(TLS_types tls_type) +{ + switch (tls_type) { + case TLS_types::NoTLS: break; + case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); + case TLS_types::TLS_CA_CERT: return F("TLS CA Cert"); + case TLS_types::TLS_insecure: return F("TLS No Checks (insecure)"); + } + return F("No TLS"); +} diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h new file mode 100644 index 0000000000..8cf0e257d5 --- /dev/null +++ b/src/src/DataTypes/TLS_types.h @@ -0,0 +1,20 @@ +#ifndef DATATYPES_TLS_TYPES_H +#define DATATYPES_TLS_TYPES_H + + +#include +#include + +// Value is stored, so do not change assigned integer values. +enum class TLS_types { + NoTLS = 0, // Do not use encryption + TLS_PSK = 1, // Pre-Shared-Key + TLS_CA_CERT = 2, // Validate server certificate against known CA +//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_insecure = 7 // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. +}; + +const __FlashStringHelper* toString(TLS_types tls_type); + + +#endif // ifndef DATATYPES_TLS_TYPES_H diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 6d712c15f0..faa6495a2a 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -173,7 +173,6 @@ bool MQTTConnect(controllerIndex_t controller_idx) { ++mqtt_reconnect_count; MakeControllerSettings(ControllerSettings); - if (!AllocatedControllerSettings()) { addLog(LOG_LEVEL_ERROR, F("MQTT : Cannot connect, out of RAM")); return false; @@ -192,13 +191,73 @@ bool MQTTConnect(controllerIndex_t controller_idx) // mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864 delay(0); + + uint16_t mqttPort = ControllerSettings.Port; + +#ifdef USE_MQTT_TLS + mqtt_tls_last_errorstr = EMPTY_STRING; + mqtt_tls_last_error = 0; + const TLS_types TLS_type = ControllerSettings.TLStype(); + switch(TLS_type) { + case TLS_types::NoTLS: + { + mqtt.setTimeout(ControllerSettings.ClientTimeout); + MQTTclient.setClient(mqtt); + break; + } + case TLS_types::TLS_PSK: + { + //mqtt_tls.setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + break; + } + case TLS_types::TLS_CA_CERT: + { + #ifdef ESP32 + mqtt_tls.setCACert(mqtt_rootCA); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA); + mqtt_tls.setTrustAnchors(&mqtt_X509List); + #endif + break; + } + /* + case TLS_types::TLS_CA_CLI_CERT: + { + //mqtt_tls.setCertificate(const char *client_ca); + break; + } + */ + case TLS_types::TLS_insecure: + { + mqtt_tls.setInsecure(); + break; + } + } + if (TLS_type != TLS_types::NoTLS) { + mqtt_tls.setTimeout(ControllerSettings.ClientTimeout); + #ifdef ESP8266 + mqtt_tls.setBufferSizes(1024,1024); + #endif + MQTTclient.setClient(mqtt_tls); + if (mqttPort == 1883) { + mqttPort = 8883; + } + } else { + if (mqttPort == 8883) { + mqttPort = 1883; + } + } + +#else mqtt.setTimeout(ControllerSettings.ClientTimeout); MQTTclient.setClient(mqtt); +#endif if (ControllerSettings.UseDNS) { - MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port); + MQTTclient.setServer(ControllerSettings.getHost().c_str(), mqttPort); } else { - MQTTclient.setServer(ControllerSettings.getIP(), ControllerSettings.Port); + MQTTclient.setServer(ControllerSettings.getIP(), mqttPort); } MQTTclient.setCallback(incoming_mqtt_callback); @@ -212,6 +271,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) bool willRetain = ControllerSettings.mqtt_willRetain() && ControllerSettings.mqtt_sendLWT(); bool cleanSession = ControllerSettings.mqtt_cleanSession(); // As suggested here: + mqtt_last_connect_attempt.setNow(); + // https://github.com/knolleary/pubsubclient/issues/458#issuecomment-493875150 if (hasControllerCredentialsSet(controller_idx, ControllerSettings)) { @@ -240,8 +301,31 @@ bool MQTTConnect(controllerIndex_t controller_idx) uint8_t controller_number = Settings.Protocol[controller_idx]; count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number); + #ifdef USE_MQTT_TLS + { + char buf[128] = {0}; + #ifdef ESP8266 + mqtt_tls_last_error = mqtt_tls.getLastSSLError(buf,128); + #endif + #ifdef ESP32 + mqtt_tls_last_error = mqtt_tls.lastError(buf,128); + #endif + mqtt_tls_last_errorstr = buf; + } + #endif + if (!MQTTresult) { + #ifdef USE_MQTT_TLS + if ((mqtt_tls_last_error != 0) && loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("MQTT : TLS error code: "); + log += mqtt_tls_last_error; + log += ' '; + log += mqtt_tls_last_errorstr; + addLog(LOG_LEVEL_ERROR, log); + } + #endif + MQTTclient.disconnect(); updateMQTTclient_connected(); return false; @@ -354,6 +438,10 @@ bool MQTTCheck(controllerIndex_t controller_idx) if (MQTTclient_should_reconnect || !MQTTclient.connected()) { + if (mqtt_last_connect_attempt.isSet() && mqtt_last_connect_attempt.millisPassedSince() < 5000) { + return false; + } + if (MQTTclient_should_reconnect) { addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect")); } diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index e79a16c218..c5f5527ae0 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -3,12 +3,27 @@ #ifdef USES_MQTT // MQTT client -WiFiClient mqtt; +WiFiClient mqtt; +# ifdef USE_MQTT_TLS +String mqtt_tls_last_errorstr; +int32_t mqtt_tls_last_error = 0; + +# ifdef ESP32 +WiFiClientSecure mqtt_tls; +# endif // ifdef ESP32 +# ifdef ESP8266 +BearSSL::WiFiClientSecure mqtt_tls; +BearSSL::X509List mqtt_X509List; +# endif // ifdef ESP8266 +const char *mqtt_rootCA = nullptr; +# endif // ifdef USE_MQTT_TLS + PubSubClient MQTTclient(mqtt); bool MQTTclient_should_reconnect = true; bool MQTTclient_must_send_LWT_connected = false; bool MQTTclient_connected = false; int mqtt_reconnect_count = 0; +LongTermTimer mqtt_last_connect_attempt; #endif // USES_MQTT #ifdef USES_P037 diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 69ef6b3104..af673b4e6f 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -5,17 +5,37 @@ #ifdef USES_MQTT +# include "../Helpers/LongTermTimer.h" # include # include +# ifdef USE_MQTT_TLS +# include +# endif // ifdef USE_MQTT_TLS + // MQTT client -extern WiFiClient mqtt; +extern WiFiClient mqtt; +# ifdef USE_MQTT_TLS +extern String mqtt_tls_last_errorstr; +extern int32_t mqtt_tls_last_error; +# ifdef ESP32 +extern WiFiClientSecure mqtt_tls; +# endif // ifdef ESP32 +# ifdef ESP8266 +extern BearSSL::WiFiClientSecure mqtt_tls; +extern BearSSL::X509List mqtt_X509List; + +# endif // ifdef ESP8266 + +extern const char *mqtt_rootCA; +# endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; extern bool MQTTclient_must_send_LWT_connected; extern bool MQTTclient_connected; extern int mqtt_reconnect_count; +extern LongTermTimer mqtt_last_connect_attempt; #endif // USES_MQTT #ifdef USES_P037 diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 4e599bbc6b..1b80dbb22f 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -20,7 +20,8 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_USE_DNS: return F("Locate Controller"); case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); - case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); + case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -152,6 +153,28 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin addFormNumericBox(displayName, internalName, ControllerSettings.Port, 1, 65535); break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: + { + #ifdef USE_MQTT_TLS + const int choice = static_cast(ControllerSettings.TLStype()); + #define NR_MQTT_TLS_TYPES 3 + const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { + toString(TLS_types::NoTLS), +// toString(TLS_types::TLS_PSK), +// toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_insecure) + }; + const int indices[NR_MQTT_TLS_TYPES] = { + static_cast(TLS_types::NoTLS), +// static_cast(TLS_types::TLS_PSK), +// static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_insecure) + }; + addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); + #undef NR_MQTT_TLS_TYPES + #endif + break; + } case ControllerSettingsStruct::CONTROLLER_USER: { const size_t fieldMaxLength = @@ -306,6 +329,16 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet case ControllerSettingsStruct::CONTROLLER_PORT: ControllerSettings.Port = getFormItemInt(internalName, ControllerSettings.Port); break; + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: + { + #ifdef USE_MQTT_TLS + const int current = static_cast(ControllerSettings.TLStype()); + const TLS_types tls_type = static_cast(getFormItemInt(internalName, current)); + ControllerSettings.TLStype(tls_type); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); break; diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 1e2d206419..53627c63a8 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -135,6 +135,7 @@ void handle_controllers_clearLoadDefaults(uint8_t controllerindex, ControllerSet ControllerSettings.reset(); ControllerSettings.Port = Protocol[ProtocolIndex].defaultPort; + ControllerSettings.TLStype(TLS_types::NoTLS); // Load some templates from the controller. struct EventStruct TempEvent; @@ -301,7 +302,6 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtmlError(F("Out of memory, cannot load page")); } else { LoadControllerSettings(controllerindex, ControllerSettings); - if (!Protocol[ProtocolIndex].Custom) { if (Protocol[ProtocolIndex].usesHost) { @@ -319,6 +319,12 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (Protocol[ProtocolIndex].usesPort) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); } + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT && Protocol[ProtocolIndex].usesTLS) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE); + addFormNote(F("Default ports: MQTT: 1883 / MQTT TLS: 8883")); + } + #endif if (Protocol[ProtocolIndex].usesQueue) { addTableSeparator(F("Controller Queue"), 2, 3); diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index 860a2e0fea..e0feb280f9 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -57,6 +57,7 @@ "-DFEATURE_SD", "-DFEATURE_I2CMULTIPLEXER", "-DUSE_TRIGONOMETRIC_FUNCTIONS_RULES", + "-DUSE_MQTT_TLS", "-DUSE_SETTINGS_ARCHIVE" ] diff --git a/tools/pio/pre_custom_esp82xx.py b/tools/pio/pre_custom_esp82xx.py index 200d4356b5..9c8de6a297 100644 --- a/tools/pio/pre_custom_esp82xx.py +++ b/tools/pio/pre_custom_esp82xx.py @@ -57,6 +57,7 @@ # "-DFEATURE_MDNS", # "-DFEATURE_SD", "-DUSE_EXT_RTC", + "-DUSE_MQTT_TLS", "-DFEATURE_I2CMULTIPLEXER", "-DUSE_TRIGONOMETRIC_FUNCTIONS_RULES", From 5fd64f217155efbc4034ed453451b043e56a8554 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 19 Oct 2021 16:02:40 +0200 Subject: [PATCH 002/367] [MQTT TLS] Add cert.py to extract certificate info --- platformio_core_defs.ini | 2 +- requirements.txt | 1 + tools/pio/cert.py | 127 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 tools/pio/cert.py diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index bedee8a98d..750355cb54 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -150,7 +150,7 @@ build_flags = -DESP32_STAGE [core_esp32_3_3_0] platform = espressif32 @ 3.3.0 platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE +build_flags = [core_esp32_3_3_2] platform = espressif32 @ 3.3.2 diff --git a/requirements.txt b/requirements.txt index 4d9c717833..d33e574598 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,6 +9,7 @@ chardet==4.0.0 click==7.1.2 colorama==0.4.3 commonmark==0.9.1 +cryptography==35.0.0 docutils==0.16 idna==2.10 imagesize==1.2.0 diff --git a/tools/pio/cert.py b/tools/pio/cert.py new file mode 100644 index 0000000000..3ade0f5ccc --- /dev/null +++ b/tools/pio/cert.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +# Script to download/update certificates and public keys +# and generate compilable source files for c++/Arduino. +# released to public domain + +# Original: https://github.com/esp8266/Arduino/blob/master/tools/cert.py + +import urllib.request +import re +import ssl +import sys +import socket +import argparse +import datetime + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.serialization import pkcs7 +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.primitives.serialization import PublicFormat + +def printData(data, showPub = True): + try: + xcert = x509.load_der_x509_certificate(data) + except: + try: + xcert = x509.load_pem_x509_certificate(data) + except: + try: + xcert = pkcs7.load_der_pkcs7_certificates(data) + except: + xcert = pkcs7.load_pem_pkcs7_certificates(data) + if len(xcert) > 1: + print('// Warning: TODO: pkcs7 has {} entries'.format(len(xcert))) + xcert = xcert[0] + + cn = '' + for dn in xcert.subject.rfc4514_string().split(','): + keyval = dn.split('=') + if keyval[0] == 'CN': + cn += keyval[1] + name = re.sub('[^a-zA-Z0-9_]', '_', cn) + print('// CN: {} => name: {}'.format(cn, name)) + + print('// not valid before:', xcert.not_valid_before) + print('// not valid after: ', xcert.not_valid_after) + + if showPub: + + fingerprint = xcert.fingerprint(hashes.SHA1()).hex(':') + print('const char fingerprint_{} [] PROGMEM = "{}";'.format(name, fingerprint)) + + pem = xcert.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode('utf-8') + print('const char pubkey_{} [] PROGMEM = R"PUBKEY('.format(name)) + print(pem + ')PUBKEY";') + + else: + + cert = xcert.public_bytes(Encoding.PEM).decode('utf-8') + print('const char cert_{} [] PROGMEM = R"CERT('.format(name)) + print(cert + ')CERT";') + + cas = [] + for ext in xcert.extensions: + if ext.oid == x509.ObjectIdentifier("1.3.6.1.5.5.7.1.1"): + for desc in ext.value: + if desc.access_method == x509.oid.AuthorityInformationAccessOID.CA_ISSUERS: + cas.append(desc.access_location.value) + for ca in cas: + with urllib.request.urlopen(ca) as crt: + print() + print('// ' + ca) + printData(crt.read(), False) + print() + +def get_certificate(hostname, port, name): + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket.create_connection((hostname, port)) as sock: + with context.wrap_socket(sock, server_hostname=hostname) as ssock: + print('////////////////////////////////////////////////////////////') + print('// certificate chain for {}:{}'.format(hostname, port)) + print() + if name: + print('const char* {}_host = "{}";'.format(name, hostname)); + print('const uint16_t {}_port = {};'.format(name, port)); + print() + printData(ssock.getpeercert(binary_form=True)) + print('// end of certificate chain for {}:{}'.format(hostname, port)) + print('////////////////////////////////////////////////////////////') + print() + return 0 + +def main(): + parser = argparse.ArgumentParser(description='download certificate chain and public keys under a C++/Arduino compilable form') + parser.add_argument('-s', '--server', action='store', required=True, help='TLS server dns name') + parser.add_argument('-p', '--port', action='store', required=False, help='TLS server port') + parser.add_argument('-n', '--name', action='store', required=False, help='variable name') + port = 443 + args = parser.parse_args() + server = args.server + port = 443 + try: + split = server.split(':') + server = split[0] + port = int(split[1]) + except: + pass + try: + port = int(args.port) + except: + pass + + print() + print('// this file is autogenerated - any modification will be overwritten') + print('// unused symbols will not be linked in the final binary') + print('// generated on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + print('// by {}'.format(sys.argv)) + print() + print('#pragma once') + print() + return get_certificate(server, port, args.name) + +if __name__ == '__main__': + sys.exit(main()) From 93b0c6db3ef6b7ba77b10f00010246b797f46ab4 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:32:14 +0100 Subject: [PATCH 003/367] [MQTT TLS] Allow to load CA root cert from file --- src/src/DataStructs/Caches.h | 8 +-- .../DataStructs/ControllerSettingsStruct.cpp | 33 +++++++++- .../DataStructs/ControllerSettingsStruct.h | 2 + src/src/DataTypes/TLS_types.h | 10 +-- src/src/ESPEasyCore/Controller.cpp | 29 ++++++-- src/src/Globals/MQTT.cpp | 2 +- src/src/Globals/MQTT.h | 2 +- src/src/Helpers/ESPEasy_Storage.cpp | 66 +++++++++++++++++-- src/src/Helpers/ESPEasy_Storage.h | 4 ++ src/src/Helpers/_CPlugin_Helper_webform.cpp | 18 ++++- src/src/WebServer/WebServer.cpp | 14 ++-- 11 files changed, 155 insertions(+), 33 deletions(-) diff --git a/src/src/DataStructs/Caches.h b/src/src/DataStructs/Caches.h index 4960c0436d..f1ebb331b1 100644 --- a/src/src/DataStructs/Caches.h +++ b/src/src/DataStructs/Caches.h @@ -5,9 +5,9 @@ #include "../../ESPEasy_common.h" #include "../Globals/Plugins.h" -typedef std::mapTaskIndexNameMap; -typedef std::map TaskIndexValueNameMap; -typedef std::map FilePresenceMap; +typedef std::map TaskIndexNameMap; +typedef std::map TaskIndexValueNameMap; +typedef std::map FilePresenceMap; struct Caches { void clearAllCaches(); @@ -18,7 +18,7 @@ struct Caches { TaskIndexNameMap taskIndexName; TaskIndexValueNameMap taskIndexValueName; - FilePresenceMap fileExistsMap; + FilePresenceMap fileExistsMap; // Filesize. -1 if not present bool activeTaskUseSerial0 = false; }; diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 5fe05a4561..0a272123b1 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -314,15 +314,42 @@ void ControllerSettingsStruct::useLocalSystemTime(bool value) TLS_types ControllerSettingsStruct::TLStype() const { - // Store it in bits 12, 13, 14 - const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0x7); + // Store it in bits 12, 13, 14, 15 + const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0xF); return tls_type; } void ControllerSettingsStruct::TLStype(TLS_types tls_type) { - const uint32_t mask = ~(0x7); + const uint32_t mask = ~(0xF); VariousFlags &= mask; // Clear the bits const uint32_t tls_type_val = static_cast(tls_type) << 12; VariousFlags |= tls_type_val; } + +String ControllerSettingsStruct::getCertificateFilename() const +{ + String certFile = HostName; + if (certFile.isEmpty()) { + certFile = F(""); + } + + switch (TLStype()) { + case TLS_types::NoTLS: + case TLS_types::TLS_insecure: + return EMPTY_STRING; + case TLS_types::TLS_PSK: + certFile += F(".psk"); + break; + /* + case TLS_types::TLS_CA_CLI_CERT: + certFile += F(".caclicert"); + break; + */ + case TLS_types::TLS_CA_CERT: + certFile += F(".cacert"); + break; + } + + return certFile; +} \ No newline at end of file diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index eaa9f9376b..73aff6dc70 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -151,6 +151,8 @@ struct ControllerSettingsStruct TLS_types TLStype() const; void TLStype(TLS_types tls_type); + + String getCertificateFilename() const; boolean UseDNS; diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index 8cf0e257d5..a8aec9aa14 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -7,11 +7,11 @@ // Value is stored, so do not change assigned integer values. enum class TLS_types { - NoTLS = 0, // Do not use encryption - TLS_PSK = 1, // Pre-Shared-Key - TLS_CA_CERT = 2, // Validate server certificate against known CA -//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication - TLS_insecure = 7 // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. + NoTLS = 0, // Do not use encryption + TLS_PSK = 1, // Pre-Shared-Key + TLS_CA_CERT = 2, // Validate server certificate against known CA +//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_insecure = 0xF // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. }; const __FlashStringHelper* toString(TLS_types tls_type); diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 47dfd3ce3a..0866687986 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -213,13 +213,28 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_CA_CERT: { - #ifdef ESP32 - mqtt_tls.setCACert(mqtt_rootCA); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA); - mqtt_tls.setTrustAnchors(&mqtt_X509List); - #endif + const String certFile = ControllerSettings.getCertificateFilename(); + const size_t size = fileSize(certFile); + if (size > 0) { + if (mqtt_rootCA != nullptr) { + free(mqtt_rootCA); + } + mqtt_rootCA = (char*)malloc(size + 1); + if (mqtt_rootCA != nullptr) { + LoadFromFile(certFile.c_str(), 0, (uint8_t*)mqtt_rootCA, size); + mqtt_rootCA[size] = '\0'; + } + } + + if (mqtt_rootCA != nullptr) { + #ifdef ESP32 + mqtt_tls.setCACert(mqtt_rootCA); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA); + mqtt_tls.setTrustAnchors(&mqtt_X509List); + #endif + } break; } /* diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index c5f5527ae0..ee9929fcf4 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -15,7 +15,7 @@ WiFiClientSecure mqtt_tls; BearSSL::WiFiClientSecure mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -const char *mqtt_rootCA = nullptr; +char *mqtt_rootCA = nullptr; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index af673b4e6f..00b7ebd266 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -28,7 +28,7 @@ extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -extern const char *mqtt_rootCA; +extern char *mqtt_rootCA; # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 5172d926a2..47cda56c13 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -118,14 +118,26 @@ String appendToFile(const String& fname, const uint8_t *data, unsigned int size) } bool fileExists(const String& fname) { + return (fileSize(fname) >= 0); +} + +int fileSize(const String& fname) { const String patched_fname = patch_fname(fname); auto search = Cache.fileExistsMap.find(patched_fname); if (search != Cache.fileExistsMap.end()) { - return search->second; + return search->second >= 0; + } + int size = -1; + if (ESPEASY_FS.exists(patched_fname)) { + size = 0; + fs::File f = ESPEASY_FS.open(patched_fname, "r"); + if (f) { + size = f.size(); + f.close(); + } } - bool res = ESPEASY_FS.exists(patched_fname); - Cache.fileExistsMap[patched_fname] = res; - return res; + Cache.fileExistsMap[patched_fname] = size; + return size; } fs::File tryOpenFile(const String& fname, const String& mode) { @@ -1237,6 +1249,52 @@ String LoadFromFile(const char *fname, int offset, uint8_t *memAddress, int data return String(); } +String LoadFromFile(const char *fname, String& data, int offset) +{ + fs::File f = tryOpenFile(fname, "r"); + SPIFFS_CHECK(f, fname); + #ifndef BUILD_NO_DEBUG + String log = F("LoadFromFile: "); + log += fname; + #else + String log = F("Load error"); + #endif + + if (!f || offset < 0 || (offset >= f.size())) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, invalid position in file"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } + delay(0); + START_TIMER; + #ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("LoadFromFile")); + #endif + + SPIFFS_CHECK(f.seek(offset, fs::SeekSet), fname); + if (f) { + if (!data.reserve(f.size() - offset)) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Out of memory"); + #endif + addLog(LOG_LEVEL_ERROR, log); + f.close(); + return log; + } + + while (f.available()) { data += (char)f.read(); } + f.close(); + } + + + STOP_TIMER(LOADFILE_STATS); + delay(0); + + return String(); +} + /********************************************************************************************\ Wrapper functions to handle errors in accessing settings \*********************************************************************************************/ diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c9d88ceac6..c079c4350d 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -29,6 +29,8 @@ String appendToFile(const String& fname, const uint8_t *data, unsigned int size) bool fileExists(const String& fname); +int fileSize(const String& fname); + fs::File tryOpenFile(const String& fname, const String& mode); bool tryRenameFile(const String& fname_old, const String& fname_new); @@ -198,6 +200,8 @@ String ClearInFile(const char *fname, int index, int datasize); \*********************************************************************************************/ String LoadFromFile(const char *fname, int offset, uint8_t *memAddress, int datasize); +String LoadFromFile(const char *fname, String& data, int offset = 0); + /********************************************************************************************\ Wrapper functions to handle errors in accessing settings \*********************************************************************************************/ diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index f6a0c6ac09..7c89a385c8 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -161,17 +161,31 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), -// toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_CA_CERT), toString(TLS_types::TLS_insecure) }; const int indices[NR_MQTT_TLS_TYPES] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), -// static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_CA_CERT), static_cast(TLS_types::TLS_insecure) }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES + const String certFile = ControllerSettings.getCertificateFilename(); + if (!certFile.isEmpty()) + { + const String certFile = ControllerSettings.getCertificateFilename(); + String note = F("Certificate or PSK must be stored on the filesystem in "); + note += certFile; + note += F(" "); + if (fileExists(certFile)) { + note += F("(File exists)"); + } else { + note += F("(Not found)"); + } + addFormNote(note); + } #endif break; } diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index e37debab8c..8191c26430 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -129,14 +129,16 @@ void sendHeadandTail(const String& tmplName, boolean Tail, boolean rebooting) { String fileName = tmplName; fileName += F(".htm"); - fs::File f = tryOpenFile(fileName, "r"); - if (f) { - pageTemplate.reserve(f.size()); + bool loadedFromFile = false; - while (f.available()) { pageTemplate += (char)f.read(); } - f.close(); - } else { + if (fileExists(fileName)) { + String res = LoadFromFile(fileName.c_str(), pageTemplate); + if (res.isEmpty()) { + loadedFromFile = true; + } + } + if (!loadedFromFile) { // TODO TD-er: Should send data directly to TXBuffer instead of using large strings. getWebPageTemplateDefault(tmplName, pageTemplate); } From 921c0f8fea49e43e3867df17beaa5fed8b078cd8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:34:22 +0100 Subject: [PATCH 004/367] [Web] Allow to stream from file system (e.g. CSS inline) This may prevent additional calls to load the CSS from the file system in a separate HTTP GET call and also not loading the file into memory when streaming. --- src/src/Static/WebStaticData.cpp | 24 ++--- src/src/WebServer/404.cpp | 4 +- src/src/WebServer/CustomPage.cpp | 10 -- src/src/WebServer/LoadFromFS.cpp | 154 ++++++++++++++++++------------- src/src/WebServer/LoadFromFS.h | 6 +- src/src/WebServer/RootPage.cpp | 11 +-- 6 files changed, 113 insertions(+), 96 deletions(-) diff --git a/src/src/Static/WebStaticData.cpp b/src/src/Static/WebStaticData.cpp index 7c77168b47..3281ff1e0d 100644 --- a/src/src/Static/WebStaticData.cpp +++ b/src/src/Static/WebStaticData.cpp @@ -3,6 +3,7 @@ #include "../Globals/Cache.h" #include "../Helpers/ESPEasy_Storage.h" #include "../WebServer/HTML_wrappers.h" +#include "../WebServer/LoadFromFS.h" String generate_external_URL(const String& fname) { String url; @@ -28,13 +29,9 @@ void serve_CSS() { return; #endif } - - addHtml(F("'); + addHtml(F("")); } void serve_favicon() { @@ -112,10 +109,15 @@ void serve_JS(JSfiles_e JSfile) { html_add_script_end(); return; #endif + addHtml(F("'); + html_add_script_end(); + return; } - addHtml(F("'); + // Now stream the file directly from the file system. + html_add_script(false); + streamFromFS(url); html_add_script_end(); } \ No newline at end of file diff --git a/src/src/WebServer/404.cpp b/src/src/WebServer/404.cpp index 1edbfc006a..35883426ad 100644 --- a/src/src/WebServer/404.cpp +++ b/src/src/WebServer/404.cpp @@ -32,9 +32,7 @@ void handleNotFound() { if (handle_rules_edit(web_server.uri())) { return; } #endif - if (loadFromFS(true, web_server.uri())) { return; } - - if (loadFromFS(false, web_server.uri())) { return; } + if (loadFromFS(web_server.uri())) { return; } String message = F("URI: "); message += web_server.uri(); message += F("\nMethod: "); diff --git a/src/src/WebServer/CustomPage.cpp b/src/src/WebServer/CustomPage.cpp index 8135cb3f58..98ed4d4381 100644 --- a/src/src/WebServer/CustomPage.cpp +++ b/src/src/WebServer/CustomPage.cpp @@ -31,19 +31,9 @@ boolean handle_custom(String path) { if (!clientIPallowed()) { return false; } -#ifdef ESP8266 - // For ESP32 remove the leading slash - path = path.substring(1); -#endif - // create a dynamic custom page, parsing task values into [#] placeholders and parsing %xx% system variables fs::File dataFile = tryOpenFile(path.c_str(), "r"); -#ifdef ESP8266 const bool dashboardPage = path.startsWith(F("dashboard")); -#endif -#ifdef ESP32 - const bool dashboardPage = path.startsWith(F("/dashboard")); -#endif if (!dataFile && !dashboardPage) { return false; // unknown file that does not exist... diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 6f9992a7c6..0a7c322496 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -1,46 +1,66 @@ #include "../WebServer/LoadFromFS.h" -#include "../WebServer/WebServer.h" -#include "../WebServer/CustomPage.h" #include "../Globals/RamTracker.h" + #include "../Helpers/ESPEasy_Storage.h" #include "../Helpers/Network.h" +#include "../WebServer/CustomPage.h" +#include "../WebServer/HTML_wrappers.h" +#include "../WebServer/WebServer.h" + #ifdef FEATURE_SD -#include -#endif +# include +#endif // ifdef FEATURE_SD -bool match_ext(const String& path, const __FlashStringHelper * ext) { - return (path.endsWith(ext) || path.endsWith(String(ext) + F(".gz"))); +bool match_ext(const String& path, const __FlashStringHelper *ext) { + return path.endsWith(ext) || path.endsWith(String(ext) + F(".gz")); } +bool gzipEncoded(const String& path) { + return path.endsWith(F(".gz")); +} -// ******************************************************************************** -// Web Interface server web file from FS -// ******************************************************************************** -bool loadFromFS(boolean spiffs, String path) { - // path is a deepcopy, since it will be changed here. - #ifndef BUILD_NO_RAM_TRACKER - checkRAM(F("loadFromFS")); - #endif - - statusLED(true); - - String dataType = F("text/plain"); - bool mustCheckCredentials = false; - +String fileFromUrl(String path) { const int questionmarkPos = path.indexOf('?'); + if (questionmarkPos >= 0) { path = path.substring(0, questionmarkPos); } + // First prepend slash if (!path.startsWith(F("/"))) { path = String(F("/")) + path; } - if (path.endsWith(F("/"))) { path += F("index.htm"); } + #ifdef ESP8266 + // Remove leading slash to generate filename from it. + if (path.startsWith(F("/"))) { + path = path.substring(1); + } + #endif + + return path; +} + +// ******************************************************************************** +// Web Interface server web file from FS +// ******************************************************************************** +bool loadFromFS(String path) { + // path is a deepcopy, since it will be changed here. + #ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("loadFromFS")); + #endif // ifndef BUILD_NO_RAM_TRACKER + + statusLED(true); + + String dataType = F("text/plain"); + bool mustCheckCredentials = false; + + path = fileFromUrl(path); + if (path.endsWith(F(".src"))) { path = path.substring(0, path.lastIndexOf(".")); } else if (match_ext(path, F(".htm")) || match_ext(path, F(".html"))) { dataType = F("text/html"); } else if (match_ext(path, F(".css"))) { dataType = F("text/css"); } @@ -52,15 +72,15 @@ bool loadFromFS(boolean spiffs, String path) { else if (path.endsWith(F(".svg"))) { dataType = F("image/svg+xml"); } else if (path.endsWith(F(".json"))) { dataType = F("application/json"); } else if (path.endsWith(F(".txt")) || - path.endsWith(F(".dat"))) { + path.endsWith(F(".dat"))) { mustCheckCredentials = true; - dataType = F("application/octet-stream"); + dataType = F("application/octet-stream"); } #ifdef WEBSERVER_CUSTOM else if (path.endsWith(F(".esp"))) { - return handle_custom(path); + return handle_custom(path); } -#endif +#endif // ifdef WEBSERVER_CUSTOM else { mustCheckCredentials = true; } @@ -78,53 +98,63 @@ bool loadFromFS(boolean spiffs, String path) { } #endif // ifndef BUILD_NO_DEBUG -#if !defined(ESP32) - path = path.substring(1); -#endif // if !defined(ESP32) + fs::File f; - if (spiffs) - { - if (!fileExists(path)) { - return false; - } - fs::File dataFile = tryOpenFile(path.c_str(), "r"); - - if (!dataFile) { - return false; - } + // Search flash file system first, then SD if present + f = tryOpenFile(path.c_str(), "r"); + #ifdef FEATURE_SD + if (!f) { + f = SD.open(path.c_str(), "r"); + } + #endif // ifdef FEATURE_SD - // prevent reloading stuff on every click - web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public")); - web_server.sendHeader(F("Vary"), "*"); - web_server.sendHeader(F("ETag"), F("\"2.0.0\"")); + if (!f) { + return false; + } - if (path.endsWith(F(".dat"))) { - web_server.sendHeader(F("Content-Disposition"), F("attachment;")); - } + // prevent reloading stuff on every click + web_server.sendHeader(F("Cache-Control"), F("max-age=3600, public")); + web_server.sendHeader(F("Vary"), "*"); + web_server.sendHeader(F("ETag"), F("\"2.0.0\"")); - web_server.streamFile(dataFile, dataType); - dataFile.close(); + if (path.endsWith(F(".dat"))) { + web_server.sendHeader(F("Content-Disposition"), F("attachment;")); } - else - { -#ifdef FEATURE_SD - File dataFile = SD.open(path.c_str()); + if (gzipEncoded(path)) { + web_server.sendHeader(F("Content-Encoding"), F("gzip")); + } + + web_server.streamFile(f, dataType); + f.close(); + + statusLED(true); + return true; +} + +bool streamFromFS(String path) { + // path is a deepcopy, since it will be changed here. + path = fileFromUrl(path); + statusLED(true); - if (!dataFile) { - return false; - } + fs::File f; - if (path.endsWith(F(".DAT"))) { - web_server.sendHeader(F("Content-Disposition"), F("attachment;")); - } - web_server.streamFile(dataFile, dataType); - dataFile.close(); -#else // ifdef FEATURE_SD + // Search flash file system first, then SD if present + f = tryOpenFile(path.c_str(), "r"); + #ifdef FEATURE_SD + if (!f) { + f = SD.open(path.c_str(), "r"); + } + #endif // ifdef FEATURE_SD - // File from SD requested, but no SD support. + if (!f) { return false; -#endif // ifdef FEATURE_SD + } + + while (f.available()) { + addHtml((char)f.read()); } statusLED(true); + + f.close(); return true; } diff --git a/src/src/WebServer/LoadFromFS.h b/src/src/WebServer/LoadFromFS.h index de51704aa5..352ad980ad 100644 --- a/src/src/WebServer/LoadFromFS.h +++ b/src/src/WebServer/LoadFromFS.h @@ -4,6 +4,10 @@ #include "../WebServer/common.h" -bool loadFromFS(boolean spiffs, String path); +bool loadFromFS(String path); + + +// Send the content of a file directly to the webserver, like addHtml() +bool streamFromFS(String path); #endif \ No newline at end of file diff --git a/src/src/WebServer/RootPage.cpp b/src/src/WebServer/RootPage.cpp index f939d0fbaa..dac0c3d11c 100644 --- a/src/src/WebServer/RootPage.cpp +++ b/src/src/WebServer/RootPage.cpp @@ -81,15 +81,8 @@ void handle_root() { navMenuIndex = 0; // if index.htm exists on FS serve that one (first check if gziped version exists) - if (loadFromFS(true, F("/index.htm.gz"))) { return; } - #ifdef FEATURE_SD - if (loadFromFS(false, F("/index.htm.gz"))) { return; } - #endif - - if (loadFromFS(true, F("/index.htm"))) { return; } - #ifdef FEATURE_SD - if (loadFromFS(false, F("/index.htm"))) { return; } - #endif + if (loadFromFS(F("/index.htm.gz"))) { return; } + if (loadFromFS(F("/index.htm"))) { return; } TXBuffer.startStream(); From 596a73987913bbf9673af75185da75c0f99a4b44 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:36:21 +0100 Subject: [PATCH 005/367] [MQTT TLS] Patch CA root cert to match strict layout Making it easier for users to copy/paste certificate code into a file. It will be patched at load from the file system. --- src/src/Helpers/ESPEasy_Storage.cpp | 131 +++++++++++++++++++++++++++- src/src/Helpers/ESPEasy_Storage.h | 7 ++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 47cda56c13..874ddfdeda 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1012,6 +1012,135 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int return LoadFromFile(SettingsType::Enum::NotificationSettings_Type, NotificationIndex, memAddress, datasize); } + +/********************************************************************************************\ + Handle certificate files on the file system. + The content will be stripped from unusable character like quotes, spaces etc. + \*********************************************************************************************/ +static inline bool is_base64(char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +bool cleanupCertificate(String & certificate, bool &changed) +{ + changed = false; + // "-----BEGIN CERTIFICATE-----" positions in dash_pos[0] and dash_pos[1] + // "-----END CERTIFICATE-----" positions in dash_pos[2] and dash_pos[3] + int dash_pos[4] = { 0 }; + int last_pos = 0; + for (int i = 0; i < 4 && last_pos != -1; ++i) { + dash_pos[i] = certificate.indexOf(F("-----"), last_pos); + last_pos = dash_pos[i] + 5; + addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); + } + if (last_pos == -1) return false; + + int read_pos = dash_pos[1] + 5; // next char after "-----BEGIN CERTIFICATE-----" + String newCert; + newCert.reserve((dash_pos[3] + 6) - dash_pos[0]); + + // "-----BEGIN CERTIFICATE-----" + newCert += certificate.substring(dash_pos[0], read_pos); + + char last_char = certificate[read_pos - 1]; + for (; read_pos < dash_pos[2]; ++read_pos) { + const char c = certificate[read_pos]; + if ((c == 'n' && last_char == '\\') || (c == '\n')) { + if (!newCert.endsWith(String('\n'))) { + newCert += '\n'; + } + } else if (is_base64(c) || c == '=') { + newCert += c; + } + last_char = c; + } + + // "-----END CERTIFICATE-----" + newCert += certificate.substring(dash_pos[2], dash_pos[3] + 5); + newCert += '\n'; + + changed = !certificate.equals(newCert); + certificate = std::move(newCert); + return true; +} + + +String SaveCertificate(const String& fname, const String& certificate) +{ + return SaveToFile(fname.c_str(), 0, (const uint8_t *)certificate.c_str(), certificate.length() + 1); +} + +String LoadCertificate(const String& fname, String& certificate) +{ + bool changed = false; + if (fileExists(fname)) { + fs::File f = tryOpenFile(fname, "r"); + SPIFFS_CHECK(f, fname.c_str()); + #ifndef BUILD_NO_DEBUG + String log = F("LoadCertificate: "); + log += fname; + #else + String log = F("LoadCertificate error"); + #endif + + certificate.clear(); + + if (!certificate.reserve(f.size())) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Out of memory"); + #endif + addLog(LOG_LEVEL_ERROR, log); + f.close(); + return log; + } + bool done = false; + while (f.available() && !done) { + const char c = (char)f.read(); + if (c == '\0') { + done = true; + } else { + certificate += c; + } + } + f.close(); + + String analyse = F("Cleanup: Before: "); + analyse += certificate.length(); + analyse += F(" After: "); + + if (!cleanupCertificate(certificate, changed)) { + certificate.clear(); + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Invalid certificate format"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } else if (changed) { + //return SaveCertificate(fname, certificate); + } +// addLog(LOG_LEVEL_INFO, F("After")); +// addLog(LOG_LEVEL_INFO, certificate); + static int previousFree = FreeMem(); + const int freemem = FreeMem(); + + analyse += certificate.length(); + analyse += changed ? F(" changed") : F(" same"); + analyse += F(" free memory: "); + analyse += freemem; + analyse += F(" largest free block: "); + analyse += getMaxFreeBlock(); + + analyse += F(" Difference: "); + analyse += previousFree - freemem; + + addLog(LOG_LEVEL_INFO, analyse); + previousFree = freemem; + } + + return EMPTY_STRING; +} + + /********************************************************************************************\ Init a file with zeros on file system \*********************************************************************************************/ @@ -1260,7 +1389,7 @@ String LoadFromFile(const char *fname, String& data, int offset) String log = F("Load error"); #endif - if (!f || offset < 0 || (offset >= f.size())) { + if (!f || offset < 0 || (offset >= static_cast(f.size()))) { #ifndef BUILD_NO_DEBUG log += F(" ERROR, invalid position in file"); #endif diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c079c4350d..620c041bbe 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -172,6 +172,13 @@ String SaveNotificationSettings(int NotificationIndex, const uint8_t *memAddress \*********************************************************************************************/ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int datasize); +/********************************************************************************************\ + Handle certificate files on the file system. + The content will be stripped from unusable character like quotes, spaces etc. + \*********************************************************************************************/ +String SaveCertificate(const String& fname, const String& certificate); +String LoadCertificate(const String& fname, String& certificate); + /********************************************************************************************\ Init a file with zeros on file system From e006c19f569b05b6dab93ac5a124fbbd9929da91 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:38:35 +0100 Subject: [PATCH 006/367] [MQTT TLS] Work-around for memory leak in MbedTLS (not finished) There is a memory leak in Mbed TLS, when connection failed. For example when using a CA root certificate which does not match the certificate of the host we're connecting to. This will take about 1880 bytes of memory on each attempt. Still a work-in-progress as it is not yet fixed. --- src/src/ESPEasyCore/Controller.cpp | 80 ++- src/src/Globals/MQTT.cpp | 6 +- src/src/Globals/MQTT.h | 10 +- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 341 ++++++++++++ src/src/Helpers/ESPEasy_WiFiClientSecure.h | 113 ++++ src/src/Helpers/ESPEasy_ssl_client.cpp | 530 +++++++++++++++++++ src/src/Helpers/ESPEasy_ssl_client.h | 53 ++ 7 files changed, 1104 insertions(+), 29 deletions(-) create mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.cpp create mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.h create mode 100644 src/src/Helpers/ESPEasy_ssl_client.cpp create mode 100644 src/src/Helpers/ESPEasy_ssl_client.h diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 0866687986..ed30513d43 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -25,6 +25,7 @@ #include "../Globals/Protocol.h" #include "../Helpers/_CPlugin_Helper.h" +#include "../Helpers/Memory.h" #include "../Helpers/Misc.h" #include "../Helpers/Network.h" #include "../Helpers/PeriodicalActions.h" @@ -186,6 +187,14 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (MQTTclient.connected()) { MQTTclient.disconnect(); + # ifdef USE_MQTT_TLS + /* + if (mqtt_tls != nullptr) { + delete mqtt_tls; + mqtt_tls = nullptr; + } + */ + #endif } updateMQTTclient_connected(); @@ -199,6 +208,19 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_tls_last_errorstr = EMPTY_STRING; mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); + if (TLS_type != TLS_types::NoTLS) { + #ifdef ESP32 + mqtt_tls = new ESPEasy_WiFiClientSecure; + #endif + #ifdef ESP8266 + mqtt_tls = new BearSSL::WiFiClientSecure; + #endif + + if (mqtt_tls == nullptr) { + addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); + return false; + } + } switch(TLS_type) { case TLS_types::NoTLS: { @@ -208,31 +230,39 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_PSK: { - //mqtt_tls.setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + //mqtt_tls->setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex break; } case TLS_types::TLS_CA_CERT: { - const String certFile = ControllerSettings.getCertificateFilename(); - const size_t size = fileSize(certFile); - if (size > 0) { - if (mqtt_rootCA != nullptr) { - free(mqtt_rootCA); - } - mqtt_rootCA = (char*)malloc(size + 1); - if (mqtt_rootCA != nullptr) { - LoadFromFile(certFile.c_str(), 0, (uint8_t*)mqtt_rootCA, size); - mqtt_rootCA[size] = '\0'; - } +// mqtt_rootCA.clear(); + if (mqtt_rootCA.isEmpty()) + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + + { + static int previousFree = FreeMem(); + const int freemem = FreeMem(); + + String analyse = F(" free memory: "); + analyse += freemem; + analyse += F(" largest free block: "); + analyse += getMaxFreeBlock(); + + analyse += F(" Difference: "); + analyse += previousFree - freemem; + + addLog(LOG_LEVEL_INFO, analyse); + previousFree = freemem; } - if (mqtt_rootCA != nullptr) { + + if (mqtt_rootCA.length() > 0) { #ifdef ESP32 - mqtt_tls.setCACert(mqtt_rootCA); + mqtt_tls->setCACert(mqtt_rootCA.c_str()); #endif #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA); - mqtt_tls.setTrustAnchors(&mqtt_X509List); + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); #endif } break; @@ -240,22 +270,22 @@ bool MQTTConnect(controllerIndex_t controller_idx) /* case TLS_types::TLS_CA_CLI_CERT: { - //mqtt_tls.setCertificate(const char *client_ca); + //mqtt_tls->setCertificate(const char *client_ca); break; } */ case TLS_types::TLS_insecure: { - mqtt_tls.setInsecure(); + mqtt_tls->setInsecure(); break; } } if (TLS_type != TLS_types::NoTLS) { - mqtt_tls.setTimeout(ControllerSettings.ClientTimeout); + mqtt_tls->setTimeout(ControllerSettings.ClientTimeout); #ifdef ESP8266 - mqtt_tls.setBufferSizes(1024,1024); + mqtt_tls->setBufferSizes(1024,1024); #endif - MQTTclient.setClient(mqtt_tls); + MQTTclient.setClient(*mqtt_tls); if (mqttPort == 1883) { mqttPort = 8883; } @@ -321,10 +351,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) { char buf[128] = {0}; #ifdef ESP8266 - mqtt_tls_last_error = mqtt_tls.getLastSSLError(buf,128); + mqtt_tls_last_error = mqtt_tls->getLastSSLError(buf,128); #endif #ifdef ESP32 - mqtt_tls_last_error = mqtt_tls.lastError(buf,128); + mqtt_tls_last_error = mqtt_tls->lastError(buf,128); #endif mqtt_tls_last_errorstr = buf; } @@ -343,6 +373,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif MQTTclient.disconnect(); + #ifdef USE_MQTT_TLS + mqtt_tls->stop(); + #endif + updateMQTTclient_connected(); return false; } diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index ee9929fcf4..d859f7b138 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -9,13 +9,13 @@ String mqtt_tls_last_errorstr; int32_t mqtt_tls_last_error = 0; # ifdef ESP32 -WiFiClientSecure mqtt_tls; +ESPEasy_WiFiClientSecure* mqtt_tls; # endif // ifdef ESP32 # ifdef ESP8266 -BearSSL::WiFiClientSecure mqtt_tls; +BearSSL::WiFiClientSecure* mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -char *mqtt_rootCA = nullptr; +String mqtt_rootCA; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 00b7ebd266..3cf7ff14b3 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -11,7 +11,11 @@ # include # ifdef USE_MQTT_TLS +# ifdef ESP32 +# include "../Helpers/ESPEasy_WiFiClientSecure.h" +# else # include +# endif # endif // ifdef USE_MQTT_TLS // MQTT client @@ -20,15 +24,15 @@ extern WiFiClient mqtt; extern String mqtt_tls_last_errorstr; extern int32_t mqtt_tls_last_error; # ifdef ESP32 -extern WiFiClientSecure mqtt_tls; +extern ESPEasy_WiFiClientSecure* mqtt_tls; # endif // ifdef ESP32 # ifdef ESP8266 -extern BearSSL::WiFiClientSecure mqtt_tls; +extern BearSSL::WiFiClientSecure* mqtt_tls; extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -extern char *mqtt_rootCA; +extern String mqtt_rootCA; # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp new file mode 100644 index 0000000000..f84c5aeb40 --- /dev/null +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -0,0 +1,341 @@ +#include "../Helpers/ESPEasy_WiFiClientSecure.h" + +/* + ESPEasy_WiFiClientSecure.cpp - Client Secure class for ESP32 + Copyright (c) 2016 Hristo Gochkov All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library 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 + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#undef connect +#undef write +#undef read + + +ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure() +{ + _connected = false; + + sslclient = new ESPEasy_sslclient_context; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + _use_insecure = false; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + + +ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock) +{ + _connected = false; + _timeout = 0; + + sslclient = new ESPEasy_sslclient_context; + ssl_init(sslclient); + sslclient->socket = sock; + sslclient->handshake_timeout = 120000; + + if (sock >= 0) { + _connected = true; + } + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure() +{ + stop(); + delete sslclient; +} + +ESPEasy_WiFiClientSecure &ESPEasy_WiFiClientSecure::operator=(const ESPEasy_WiFiClientSecure &other) +{ + stop(); + sslclient->socket = other.sslclient->socket; + _connected = other._connected; + return *this; +} + +void ESPEasy_WiFiClientSecure::stop() +{ + if (sslclient->socket >= 0) { + close(sslclient->socket); + sslclient->socket = -1; + _connected = false; + _peek = -1; + } + stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(ip, port, _pskIdent, _psKey); + return connect(ip, port, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(ip, port); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(host, port, _pskIdent, _psKey); + return connect(host, port, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(host, port); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) +{ + return connect(ip.toString().c_str(), port, CA_cert, cert, private_key); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) +{ + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { + return connect(ip.toString().c_str(), port, pskIdent, psKey); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { + log_v("start_ssl_client with PSK"); + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int ESPEasy_WiFiClientSecure::peek(){ + if(_peek >= 0){ + return _peek; + } + _peek = timedRead(); + return _peek; +} + +size_t ESPEasy_WiFiClientSecure::write(uint8_t data) +{ + return write(&data, 1); +} + +int ESPEasy_WiFiClientSecure::read() +{ + uint8_t data = -1; + int res = read(&data, 1); + if (res < 0) { + return res; + } + return data; +} + +size_t ESPEasy_WiFiClientSecure::write(const uint8_t *buf, size_t size) +{ + if (!_connected) { + return 0; + } + int res = send_ssl_data(sslclient, buf, size); + if (res < 0) { + stop(); + res = 0; + } + return res; +} + +int ESPEasy_WiFiClientSecure::read(uint8_t *buf, size_t size) +{ + int peeked = 0; + int avail = available(); + if ((!buf && size) || avail <= 0) { + return -1; + } + if(!size){ + return 0; + } + if(_peek >= 0){ + buf[0] = _peek; + _peek = -1; + size--; + avail--; + if(!size || !avail){ + return 1; + } + buf++; + peeked = 1; + } + + int res = get_ssl_receive(sslclient, buf, size); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res + peeked; +} + +int ESPEasy_WiFiClientSecure::available() +{ + int peeked = (_peek >= 0); + if (!_connected) { + return peeked; + } + int res = data_to_read(sslclient); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res+peeked; +} + +uint8_t ESPEasy_WiFiClientSecure::connected() +{ + uint8_t dummy = 0; + read(&dummy, 0); + + return _connected; +} + +void ESPEasy_WiFiClientSecure::setInsecure() +{ + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + _use_insecure = true; +} + +void ESPEasy_WiFiClientSecure::setCACert (const char *rootCA) +{ + _CA_cert = rootCA; +} + +void ESPEasy_WiFiClientSecure::setCertificate (const char *client_ca) +{ + _cert = client_ca; +} + +void ESPEasy_WiFiClientSecure::setPrivateKey (const char *private_key) +{ + _private_key = private_key; +} + +void ESPEasy_WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) { + _pskIdent = pskIdent; + _psKey = psKey; +} + +bool ESPEasy_WiFiClientSecure::verify(const char* fp, const char* domain_name) +{ + if (!sslclient) + return false; + + return verify_ssl_fingerprint(sslclient, fp, domain_name); +} + +char *ESPEasy_WiFiClientSecure::_streamLoad(Stream& stream, size_t size) { + char *dest = (char*)malloc(size+1); + if (!dest) { + return nullptr; + } + if (size != stream.readBytes(dest, size)) { + free(dest); + dest = nullptr; + return nullptr; + } + dest[size] = '\0'; + return dest; +} + +bool ESPEasy_WiFiClientSecure::loadCACert(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCACert(dest); + ret = true; + } + return ret; +} + +bool ESPEasy_WiFiClientSecure::loadCertificate(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCertificate(dest); + ret = true; + } + return ret; +} + +bool ESPEasy_WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setPrivateKey(dest); + ret = true; + } + return ret; +} + +int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size) +{ + if (!_lastError) { + return 0; + } + mbedtls_strerror(_lastError, buf, size); + return _lastError; +} + +void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout) +{ + sslclient->handshake_timeout = handshake_timeout * 1000; +} \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h new file mode 100644 index 0000000000..08e129446b --- /dev/null +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -0,0 +1,113 @@ +/* + ESPEasy_WiFiClientSecure.h - Base class that provides Client SSL to ESP32 + Copyright (c) 2011 Adrian McEwen. All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library 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 + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Almost literal copy of https://github.com/brokentoaster/arduino-esp32/blob/master/libraries/WiFiClientSecure/src/WiFiClientSecure.h + Changed into "ESPEasy" version to incorporate some fixes + for memory leaks not yet present in the "older" core versions. +*/ + +#ifndef ESPEASY_WiFiClientSecure_h +#define ESPEASY_WiFiClientSecure_h +#include +#include +#include +#include "../Helpers/ESPEasy_ssl_client.h" + +class ESPEasy_WiFiClientSecure : public WiFiClient +{ +protected: + ESPEasy_sslclient_context *sslclient; + + int _lastError = 0; + int _peek = -1; + int _timeout = 0; + bool _use_insecure; + const char *_CA_cert; + const char *_cert; + const char *_private_key; + const char *_pskIdent; // identity for PSK cipher suites + const char *_psKey; // key in hex for PSK cipher suites + +public: + ESPEasy_WiFiClientSecure *next; + ESPEasy_WiFiClientSecure(); + ESPEasy_WiFiClientSecure(int socket); + ~ESPEasy_WiFiClientSecure(); + int connect(IPAddress ip, uint16_t port); + int connect(IPAddress ip, uint16_t port, int32_t timeout); + int connect(const char *host, uint16_t port); + int connect(const char *host, uint16_t port, int32_t timeout); + int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); + int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); + int peek(); + size_t write(uint8_t data); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + void flush() {} + void stop(); + uint8_t connected(); + int lastError(char *buf, const size_t size); + void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE! + void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + void setCACert(const char *rootCA); + void setCertificate(const char *client_ca); + void setPrivateKey (const char *private_key); + bool loadCACert(Stream& stream, size_t size); + bool loadCertificate(Stream& stream, size_t size); + bool loadPrivateKey(Stream& stream, size_t size); + bool verify(const char* fingerprint, const char* domain_name); + void setHandshakeTimeout(unsigned long handshake_timeout); + + int setTimeout(uint32_t seconds){ return 0; } + + operator bool() + { + return connected(); + } + ESPEasy_WiFiClientSecure &operator=(const ESPEasy_WiFiClientSecure &other); + bool operator==(const bool value) + { + return bool() == value; + } + bool operator!=(const bool value) + { + return bool() != value; + } + bool operator==(const ESPEasy_WiFiClientSecure &); + bool operator!=(const ESPEasy_WiFiClientSecure &rhs) + { + return !this->operator==(rhs); + }; + + int socket() + { + return sslclient->socket = -1; + } + +private: + char *_streamLoad(Stream& stream, size_t size); + + //friend class WiFiServer; + using Print::write; +}; + +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp new file mode 100644 index 0000000000..4ff9996b3b --- /dev/null +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -0,0 +1,530 @@ +#include "../Helpers/ESPEasy_ssl_client.h" + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE +* +* Adapted from the ssl_client1 example of mbedtls. +* +* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. +* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED +# error "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" +#endif + +const char *ESPEasy_pers = "esp32-tls"; + +static int _handle_error(int err, const char * file, int line) +{ + if(err == -30848){ + return err; + } +#ifdef MBEDTLS_ERROR_C + char error_buf[100]; + mbedtls_strerror(err, error_buf, 100); + log_e("[%s():%d]: (%d) %s", file, line, err, error_buf); +#else + log_e("[%s():%d]: code %d", file, line, err); +#endif + return err; +} + +#define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__) + +ESPEasy_sslclient_context::ESPEasy_sslclient_context() +{ + memset(&ssl_ctx, 0, sizeof(ssl_ctx)); + memset(&ssl_conf, 0, sizeof(ssl_conf)); + memset(&drbg_ctx, 0, sizeof(drbg_ctx)); + memset(&entropy_ctx, 0, sizeof(entropy_ctx)); + memset(&ca_cert, 0, sizeof(ca_cert)); + memset(&client_cert, 0, sizeof(client_cert)); + memset(&client_key, 0, sizeof(client_key)); +} + + +ESPEasy_sslclient_context::~ESPEasy_sslclient_context() +{ + free_ca_cert(); + free_client_cert(); + free_client_key(); +} + + +void ESPEasy_sslclient_context::free_ca_cert() +{ +// if (ca_cert.p != nullptr) { + if (ca_cert_init) { + ca_cert_init = false; + } + mbedtls_x509_crt_free(&ca_cert); +// } +} + +void ESPEasy_sslclient_context::free_client_cert() +{ + if (client_cert_init) { + client_cert_init = false; + } + mbedtls_x509_crt_free(&client_cert); +// } +} + +void ESPEasy_sslclient_context::free_client_key() +{ + if (client_key_init) { + client_key_init = false; + } + mbedtls_pk_free(&client_key); +// } +} + + +void ssl_init(ESPEasy_sslclient_context *ssl_client) +{ + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + + mbedtls_ssl_init(&ssl_client->ssl_ctx); + mbedtls_ssl_config_init(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); +} + + +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure) +{ + char buf[512]; + int ret, flags; + int enable = 1; + log_v("Free internal heap before TLS %u", ESP.getFreeHeap()); + + if (rootCABuff == NULL && pskIdent == NULL && psKey == NULL && !insecure) { + return -1; + } + + log_v("Starting socket"); + ssl_client->socket = -1; + + ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ssl_client->socket < 0) { + log_e("ERROR opening socket"); + return ssl_client->socket; + } + + IPAddress srv((uint32_t)0); + if(!WiFiGenericClass::hostByName(host, srv)){ + return -1; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = srv; + serv_addr.sin_port = htons(port); + + if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { + if(timeout <= 0){ + timeout = 30000; // Milli seconds. + } + timeval so_timeout = { .tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000 }; + +#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &so_timeout, sizeof(so_timeout)),"SO_RCVTIMEO"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &so_timeout, sizeof(so_timeout)),"SO_SNDTIMEO"); + + ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); + } else { + log_e("Connect to Server failed!"); + return -1; + } + + fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); + + log_v("Seeding the random number generator"); + mbedtls_entropy_free(&ssl_client->entropy_ctx); + mbedtls_entropy_init(&ssl_client->entropy_ctx); + + ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, + &ssl_client->entropy_ctx, (const unsigned char *) ESPEasy_pers, strlen(ESPEasy_pers)); + if (ret < 0) { + return handle_error(ret); + } + + log_v("Setting up the SSL/TLS structure..."); + + if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + return handle_error(ret); + } + + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and + // MBEDTLS_SSL_VERIFY_NONE if not. + + if (insecure) { + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); + log_i("WARNING: Skipping SSL Verification. INSECURE!"); + } else if (rootCABuff != NULL) { + log_v("Loading CA cert"); + mbedtls_x509_crt_init(&ssl_client->ca_cert); + ssl_client->ca_cert_init = true; + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); + mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); + //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); + if (ret < 0) { + // free the ca_cert in the case parse failed, otherwise, the old ca_cert still in the heap memory, that lead to "out of memory" crash. + ssl_client->free_ca_cert(); + return handle_error(ret); + } + } else if (pskIdent != NULL && psKey != NULL) { + log_v("Setting up PSK"); + // convert PSK from hex to binary + if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) { + log_e("pre-shared key not valid hex or too long"); + return -1; + } + unsigned char psk[MBEDTLS_PSK_MAX_LEN]; + size_t psk_len = strlen(psKey)/2; + for (int j=0; j= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] = c<<4; + c = psKey[j+1]; + if (c >= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] |= c; + } + // set mbedtls config + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, + (const unsigned char *)pskIdent, strlen(pskIdent)); + if (ret != 0) { + log_e("mbedtls_ssl_conf_psk returned %d", ret); + return handle_error(ret); + } + } else { + return -1; + } + + if (!insecure && cli_cert != NULL && cli_key != NULL) { + mbedtls_x509_crt_init(&ssl_client->client_cert); + mbedtls_pk_init(&ssl_client->client_key); + + + log_v("Loading CRT cert"); + + ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); + ssl_client->client_cert_init = true; + if (ret < 0) { + // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. + ssl_client->free_client_cert(); + return handle_error(ret); + } + + log_v("Loading private key"); + ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); + ssl_client->client_key_init = true; + + if (ret != 0) { + return handle_error(ret); + } + + mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); + } + + log_v("Setting hostname for TLS session..."); + + // Hostname set here should match CN in server certificate + if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0){ + return handle_error(ret); + } + + mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); + + if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) { + return handle_error(ret); + } + + mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL ); + + log_v("Performing the SSL/TLS handshake..."); + unsigned long handshake_start_time=millis(); + while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + return handle_error(ret); + } + if ((millis()-handshake_start_time) > ssl_client->handshake_timeout) { + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + return -1; + } + vTaskDelay(2);//2 ticks + } + + + if (cli_cert != NULL && cli_key != NULL) { + log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); + if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) { + log_d("Record expansion is %d", ret); + } else { + log_w("Record expansion is unknown (compression)"); + } + } + + log_v("Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) { + memset(buf, 0, sizeof(buf)); + mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); + log_e("Failed to verify peer certificate! verification info: %s", buf); + stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + + return handle_error(ret); + } else { + log_v("Certificate verified."); + } + + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + ssl_client->free_client_key(); + + log_v("Free internal heap after TLS %u", ESP.getFreeHeap()); + + return ssl_client->socket; +} + + +void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key) +{ + log_v("Cleaning SSL connection."); + + if (ssl_client->socket >= 0) { + close(ssl_client->socket); + ssl_client->socket = -1; + } + + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + mbedtls_entropy_free(&ssl_client->entropy_ctx); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + ssl_client->free_client_key(); +} + + +int data_to_read(ESPEasy_sslclient_context *ssl_client) +{ + int ret, res; + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); + //log_e("RET: %i",ret); //for low level debug + res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); + //log_e("RES: %i",res); //for low level debug + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { + return handle_error(ret); + } + + return res; +} + +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len) +{ + log_v("Writing HTTP request with %d bytes...", len); //for low level debug + int ret = -1; + + if ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0){ + log_v("Handling error %d", ret); //for low level debug + return handle_error(ret); + } else{ + log_v("Returning with %d bytes written", ret); //for low level debug + } + + return ret; +} + +int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length) +{ + //log_d( "Reading HTTP response..."); //for low level debug + int ret = -1; + + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); + + //log_v( "%d bytes read", ret); //for low level debug + return ret; +} + +static bool parseHexNibble(char pb, uint8_t* res) +{ + if (pb >= '0' && pb <= '9') { + *res = (uint8_t) (pb - '0'); return true; + } else if (pb >= 'a' && pb <= 'f') { + *res = (uint8_t) (pb - 'a' + 10); return true; + } else if (pb >= 'A' && pb <= 'F') { + *res = (uint8_t) (pb - 'A' + 10); return true; + } + return false; +} + +// Compare a name from certificate and domain name, return true if they match +static bool matchName(const std::string& name, const std::string& domainName) +{ + size_t wildcardPos = name.find('*'); + if (wildcardPos == std::string::npos) { + // Not a wildcard, expect an exact match + return name == domainName; + } + + size_t firstDotPos = name.find('.'); + if (wildcardPos > firstDotPos) { + // Wildcard is not part of leftmost component of domain name + // Do not attempt to match (rfc6125 6.4.3.1) + return false; + } + if (wildcardPos != 0 || firstDotPos != 1) { + // Matching of wildcards such as baz*.example.com and b*z.example.com + // is optional. Maybe implement this in the future? + return false; + } + size_t domainNameFirstDotPos = domainName.find('.'); + if (domainNameFirstDotPos == std::string::npos) { + return false; + } + return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos); +} + +// Verifies certificate provided by the peer to match specified SHA256 fingerprint +bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name) +{ + // Convert hex string to byte array + uint8_t fingerprint_local[32]; + int len = strlen(fp); + int pos = 0; + for (size_t i = 0; i < sizeof(fingerprint_local); ++i) { + while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) { + ++pos; + } + if (pos > len - 2) { + log_d("pos:%d len:%d fingerprint too short", pos, len); + return false; + } + uint8_t high, low; + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) { + log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos+1]); + return false; + } + pos += 2; + fingerprint_local[i] = low | (high << 4); + } + + // Get certificate provided by the peer + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + if (!crt) + { + log_d("could not fetch peer certificate"); + return false; + } + + // Calculate certificate's SHA256 fingerprint + uint8_t fingerprint_remote[32]; + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); + mbedtls_sha256_free(&sha256_ctx); + + // Check if fingerprints match + if (memcmp(fingerprint_local, fingerprint_remote, 32)) + { + log_d("fingerprint doesn't match"); + return false; + } + + // Additionally check if certificate has domain name if provided + if (domain_name) + return verify_ssl_dn(ssl_client, domain_name); + else + return true; +} + +// Checks if peer certificate has specified domain in CN or SANs +bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name) +{ + log_d("domain name: '%s'", (domain_name)?domain_name:"(null)"); + std::string domain_name_str(domain_name); + std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower); + + // Get certificate provided by the peer + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + // Check for domain name in SANs + const mbedtls_x509_sequence* san = &crt->subject_alt_names; + while (san != nullptr) + { + std::string san_str((const char*)san->buf.p, san->buf.len); + std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower); + + if (matchName(san_str, domain_name_str)) + return true; + + log_d("SAN '%s': no match", san_str.c_str()); + + // Fetch next SAN + san = san->next; + } + + // Check for domain name in CN + const mbedtls_asn1_named_data* common_name = &crt->subject; + while (common_name != nullptr) + { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + std::string common_name_str((const char*)common_name->val.p, common_name->val.len); + + if (matchName(common_name_str, domain_name_str)) + return true; + + log_d("CN '%s': not match", common_name_str.c_str()); + } + + // Fetch next DN object + common_name = common_name->next; + } + + return false; +} \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h new file mode 100644 index 0000000000..46bc0d649f --- /dev/null +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -0,0 +1,53 @@ +/* Provide SSL/TLS functions to ESP32 with Arduino IDE + * by Evandro Copercini - 2017 - Apache 2.0 License + */ + +#ifndef ESPEASY_ARD_SSL_H +#define ESPEASY_ARD_SSL_H +#include +#include +#include +#include +#include +#include +#include + +typedef struct ESPEasy_sslclient_context { + + ESPEasy_sslclient_context(); + + ~ESPEasy_sslclient_context(); + + void free_ca_cert(); + void free_client_cert(); + void free_client_key(); + + int socket = 0; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + + mbedtls_ctr_drbg_context drbg_ctx; + mbedtls_entropy_context entropy_ctx; + + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt client_cert; + mbedtls_pk_context client_key; + + bool ca_cert_init = false; + bool client_cert_init = false; + bool client_key_init = false; + + unsigned long handshake_timeout = 120000; +} ESPEasy_sslclient_context; + + +void ssl_init(ESPEasy_sslclient_context *ssl_client); +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure); +void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); +int data_to_read(ESPEasy_sslclient_context *ssl_client); +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); +int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); +bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); +bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); + +#endif \ No newline at end of file From b9b7e45634e633951c546006e88e34df0a25643c Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 11:14:04 +0100 Subject: [PATCH 007/367] [MQTT TLS] Fix memory leak... finally --- src/src/ESPEasyCore/Controller.cpp | 32 +++++++++++++++----------- src/src/Helpers/ESPEasy_Storage.cpp | 21 ----------------- src/src/Helpers/ESPEasy_ssl_client.cpp | 22 +++--------------- src/src/Helpers/ESPEasy_ssl_client.h | 6 +---- 4 files changed, 22 insertions(+), 59 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index ed30513d43..d3012112a9 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -188,12 +188,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (MQTTclient.connected()) { MQTTclient.disconnect(); # ifdef USE_MQTT_TLS - /* if (mqtt_tls != nullptr) { delete mqtt_tls; mqtt_tls = nullptr; } - */ #endif } @@ -208,7 +206,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_tls_last_errorstr = EMPTY_STRING; mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); - if (TLS_type != TLS_types::NoTLS) { + if (TLS_type != TLS_types::NoTLS && nullptr == mqtt_tls) { #ifdef ESP32 mqtt_tls = new ESPEasy_WiFiClientSecure; #endif @@ -219,6 +217,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (mqtt_tls == nullptr) { addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); return false; + } else { + mqtt_rootCA.clear(); } } switch(TLS_type) { @@ -235,9 +235,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_CA_CERT: { -// mqtt_rootCA.clear(); - if (mqtt_rootCA.isEmpty()) + mqtt_rootCA.clear(); + bool certLoaded = false; + if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + certLoaded = true; + } { static int previousFree = FreeMem(); @@ -255,15 +258,16 @@ bool MQTTConnect(controllerIndex_t controller_idx) previousFree = freemem; } - - if (mqtt_rootCA.length() > 0) { - #ifdef ESP32 - mqtt_tls->setCACert(mqtt_rootCA.c_str()); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA.c_str()); - mqtt_tls->setTrustAnchors(&mqtt_X509List); - #endif + if (certLoaded) { + if (mqtt_rootCA.length() > 0) { + #ifdef ESP32 + mqtt_tls->setCACert(mqtt_rootCA.c_str()); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); + #endif + } } break; } diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 874ddfdeda..e6fcdc8070 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1104,10 +1104,6 @@ String LoadCertificate(const String& fname, String& certificate) } f.close(); - String analyse = F("Cleanup: Before: "); - analyse += certificate.length(); - analyse += F(" After: "); - if (!cleanupCertificate(certificate, changed)) { certificate.clear(); #ifndef BUILD_NO_DEBUG @@ -1118,23 +1114,6 @@ String LoadCertificate(const String& fname, String& certificate) } else if (changed) { //return SaveCertificate(fname, certificate); } -// addLog(LOG_LEVEL_INFO, F("After")); -// addLog(LOG_LEVEL_INFO, certificate); - static int previousFree = FreeMem(); - const int freemem = FreeMem(); - - analyse += certificate.length(); - analyse += changed ? F(" changed") : F(" same"); - analyse += F(" free memory: "); - analyse += freemem; - analyse += F(" largest free block: "); - analyse += getMaxFreeBlock(); - - analyse += F(" Difference: "); - analyse += previousFree - freemem; - - addLog(LOG_LEVEL_INFO, analyse); - previousFree = freemem; } return EMPTY_STRING; diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index 4ff9996b3b..baa7b67b1c 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -65,30 +65,17 @@ ESPEasy_sslclient_context::~ESPEasy_sslclient_context() void ESPEasy_sslclient_context::free_ca_cert() { -// if (ca_cert.p != nullptr) { - if (ca_cert_init) { - ca_cert_init = false; - } - mbedtls_x509_crt_free(&ca_cert); -// } + mbedtls_x509_crt_free(&ca_cert); } void ESPEasy_sslclient_context::free_client_cert() { - if (client_cert_init) { - client_cert_init = false; - } - mbedtls_x509_crt_free(&client_cert); -// } + mbedtls_x509_crt_free(&client_cert); } void ESPEasy_sslclient_context::free_client_key() { - if (client_key_init) { - client_key_init = false; - } - mbedtls_pk_free(&client_key); -// } + mbedtls_pk_free(&client_key); } @@ -182,7 +169,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui } else if (rootCABuff != NULL) { log_v("Loading CA cert"); mbedtls_x509_crt_init(&ssl_client->ca_cert); - ssl_client->ca_cert_init = true; mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); @@ -234,7 +220,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui log_v("Loading CRT cert"); ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); - ssl_client->client_cert_init = true; if (ret < 0) { // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. ssl_client->free_client_cert(); @@ -243,7 +228,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui log_v("Loading private key"); ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); - ssl_client->client_key_init = true; if (ret != 0) { return handle_error(ret); diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 46bc0d649f..9704b3ff80 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -22,7 +22,7 @@ typedef struct ESPEasy_sslclient_context { void free_client_cert(); void free_client_key(); - int socket = 0; + int socket = -1; mbedtls_ssl_context ssl_ctx; mbedtls_ssl_config ssl_conf; @@ -33,10 +33,6 @@ typedef struct ESPEasy_sslclient_context { mbedtls_x509_crt client_cert; mbedtls_pk_context client_key; - bool ca_cert_init = false; - bool client_cert_init = false; - bool client_key_init = false; - unsigned long handshake_timeout = 120000; } ESPEasy_sslclient_context; From 53fadc5134680d1f25883c62b2cdeaa0db028294 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 12:06:20 +0100 Subject: [PATCH 008/367] [MQTT TLS] Clear CA certificate when starting a new connection Otherwise you may not always use the latest CA root certificate stored on the file system --- src/src/ESPEasyCore/Controller.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index d3012112a9..3728f2d195 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -192,6 +192,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) delete mqtt_tls; mqtt_tls = nullptr; } + mqtt_rootCA.clear(); #endif } @@ -213,12 +214,11 @@ bool MQTTConnect(controllerIndex_t controller_idx) #ifdef ESP8266 mqtt_tls = new BearSSL::WiFiClientSecure; #endif + mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); return false; - } else { - mqtt_rootCA.clear(); } } switch(TLS_type) { @@ -236,12 +236,6 @@ bool MQTTConnect(controllerIndex_t controller_idx) case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); - bool certLoaded = false; - if (mqtt_rootCA.isEmpty()) { - LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); - certLoaded = true; - } - { static int previousFree = FreeMem(); const int freemem = FreeMem(); @@ -258,16 +252,16 @@ bool MQTTConnect(controllerIndex_t controller_idx) previousFree = freemem; } - if (certLoaded) { - if (mqtt_rootCA.length() > 0) { - #ifdef ESP32 - mqtt_tls->setCACert(mqtt_rootCA.c_str()); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA.c_str()); - mqtt_tls->setTrustAnchors(&mqtt_X509List); - #endif - } + if (mqtt_rootCA.isEmpty()) { + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + + #ifdef ESP32 + mqtt_tls->setCACert(mqtt_rootCA.c_str()); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); + #endif } break; } From 6ca2c6f5c8824d2db835547a44f5b33139ec7a4b Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 12:18:06 +0100 Subject: [PATCH 009/367] [Cleanup] Fix lots of missing delete calls to free memory In a lot of places an object was allocated on the heap, but not always it was deleted thus leading to memory leaks. --- src/_C018.ino | 20 ++++-- src/_P016_IR.ino | 2 +- src/_P035_IRTX.ino | 30 ++++++-- src/_P038_NeoPixel.ino | 25 +++++-- src/_P041_NeoClock.ino | 19 +++-- src/_P042_Candle.ino | 21 ++++-- src/_P046_VentusW266.ino | 2 +- src/_P054_DMX512.ino | 8 ++- src/_P055_Chiming.ino | 84 +++++++++++----------- src/_P056_SDS011-Dust.ino | 8 ++- src/_P062_MPR121_KeyPad.ino | 4 +- src/_P065_DRF0299_MP3.ino | 9 +++ src/_P078_Eastron.ino | 4 +- src/_P088_HeatpumpIR.ino | 3 +- src/_P095_ILI9341.ino | 27 +++++-- src/_P096_eInk.ino | 44 ++++++++---- src/_P102_PZEM004Tv3.ino | 2 +- src/_P109_ThermOLED.ino | 4 +- src/src/DataStructs/Modbus.cpp | 15 +++- src/src/DataStructs/Modbus.h | 1 + src/src/Helpers/Networking.cpp | 10 ++- src/src/PluginStructs/P020_data_struct.cpp | 1 + src/src/PluginStructs/P044_data_struct.cpp | 1 + src/src/PluginStructs/P062_data_struct.cpp | 9 ++- src/src/PluginStructs/P062_data_struct.h | 1 + src/src/PluginStructs/P082_data_struct.cpp | 2 +- src/src/PluginStructs/P111_data_struct.cpp | 15 +++- src/src/PluginStructs/P111_data_struct.h | 3 +- 28 files changed, 261 insertions(+), 113 deletions(-) diff --git a/src/_C018.ino b/src/_C018.ino index c3b944d6a5..93f763c058 100644 --- a/src/_C018.ino +++ b/src/_C018.ino @@ -77,13 +77,25 @@ struct C018_data_struct { _baudrate = baudrate; // FIXME TD-er: Make force SW serial a proper setting. + if (C018_easySerial != nullptr) { + delete C018_easySerial; + } + C018_easySerial = new (std::nothrow) ESPeasySerial(static_cast(port), serial_rx, serial_tx, false, 64); if (C018_easySerial != nullptr) { - myLora = new rn2xx3(*C018_easySerial); - myLora->setAsyncMode(true); - myLora->setLastUsedJoinMode(joinIsOTAA); - triggerAutobaud(); + if (myLora != nullptr) { + delete myLora; + } + myLora = new (std::nothrow) rn2xx3(*C018_easySerial); + if (myLora == nullptr) { + delete C018_easySerial; + C018_easySerial = nullptr; + } else { + myLora->setAsyncMode(true); + myLora->setLastUsedJoinMode(joinIsOTAA); + triggerAutobaud(); + } } return isInitialized(); } diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index c9ff19c87a..f2229bc8d9 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -226,7 +226,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) addLog(LOG_LEVEL_INFO, F("INIT: IR RX")); addLog(LOG_LEVEL_INFO, F("IR lib Version: " _IRREMOTEESP8266_VERSION_)); } - irReceiver = new IRrecv(irPin, kCaptureBufferSize, P016_TIMEOUT, true); + irReceiver = new (std::nothrow) IRrecv(irPin, kCaptureBufferSize, P016_TIMEOUT, true); # ifdef PLUGIN_016_DEBUG addLog(LOG_LEVEL_INFO, F("P016_PLUGIN_INIT IR receiver created")); # endif // PLUGIN_016_DEBUG diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index bc6425632c..69b094e9dc 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -99,21 +99,23 @@ boolean Plugin_035(uint8_t function, struct EventStruct *event, String &command) case PLUGIN_INIT: { int irPin = CONFIG_PIN1; - if (Plugin_035_irSender == 0 && validGpio(irPin)) + if (Plugin_035_irSender == nullptr && validGpio(irPin)) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, F("INIT: IR TX")); addLog(LOG_LEVEL_INFO, F("IR lib Version: " _IRREMOTEESP8266_VERSION_)); addLog(LOG_LEVEL_INFO, String(F("Supported Protocols by IRSEND: ")) + listProtocols()); } - Plugin_035_irSender = new IRsend(irPin); - Plugin_035_irSender->begin(); // Start the sender + Plugin_035_irSender = new (std::nothrow) IRsend(irPin); + if (Plugin_035_irSender != nullptr) { + Plugin_035_irSender->begin(); // Start the sender + } } - if (Plugin_035_irSender != 0 && irPin == -1) + if (Plugin_035_irSender != nullptr && irPin == -1) { addLog(LOG_LEVEL_INFO, F("INIT: IR TX Removed")); delete Plugin_035_irSender; - Plugin_035_irSender = 0; + Plugin_035_irSender = nullptr; } #ifdef P016_P035_Extended_AC @@ -129,7 +131,7 @@ boolean Plugin_035(uint8_t function, struct EventStruct *event, String &command) { addLog(LOG_LEVEL_INFO, F("INIT AC: IR TX Removed")); delete Plugin_035_commonAc; - Plugin_035_commonAc = 0; + Plugin_035_commonAc = nullptr; } #endif @@ -137,6 +139,22 @@ boolean Plugin_035(uint8_t function, struct EventStruct *event, String &command) break; } + case PLUGIN_EXIT: + { + if (Plugin_035_irSender != nullptr) { + delete Plugin_035_irSender; + Plugin_035_irSender = nullptr; + } + #ifdef P016_P035_Extended_AC + if (Plugin_035_commonAc != nullptr) { + delete Plugin_035_commonAc; + Plugin_035_commonAc = nullptr; + } + #endif + success = true; + break; + } + case PLUGIN_WRITE: { String cmdCode = parseString(command,1); diff --git a/src/_P038_NeoPixel.ino b/src/_P038_NeoPixel.ino index bee1239d1e..564d98de72 100644 --- a/src/_P038_NeoPixel.ino +++ b/src/_P038_NeoPixel.ino @@ -31,7 +31,7 @@ #include -Adafruit_NeoPixel *Plugin_038_pixels; +Adafruit_NeoPixel *Plugin_038_pixels = nullptr; #define PLUGIN_038 #define PLUGIN_ID_038 38 @@ -94,23 +94,36 @@ boolean Plugin_038(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (!Plugin_038_pixels) + if (Plugin_038_pixels == nullptr) { uint8_t striptype = PCONFIG(1); if (striptype == 1) - Plugin_038_pixels = new Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRB + NEO_KHZ800); + Plugin_038_pixels = new (std::nothrow) Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRB + NEO_KHZ800); else if (striptype == 2) - Plugin_038_pixels = new Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRBW + NEO_KHZ800); + Plugin_038_pixels = new (std::nothrow) Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRBW + NEO_KHZ800); else - Plugin_038_pixels = new Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRB + NEO_KHZ800); + Plugin_038_pixels = new (std::nothrow) Adafruit_NeoPixel(PCONFIG(0), CONFIG_PIN1, NEO_GRB + NEO_KHZ800); - Plugin_038_pixels->begin(); // This initializes the NeoPixel library. + if (Plugin_038_pixels != nullptr) { + Plugin_038_pixels->begin(); // This initializes the NeoPixel library. + } } MaxPixels = PCONFIG(0); + success = Plugin_038_pixels != nullptr; + break; + } + + case PLUGIN_EXIT: + { + if (Plugin_038_pixels != nullptr) { + delete Plugin_038_pixels; + Plugin_038_pixels = nullptr; + } success = true; break; } + case PLUGIN_WRITE: { if (Plugin_038_pixels) diff --git a/src/_P041_NeoClock.ino b/src/_P041_NeoClock.ino index f0cd6f45c4..7371a68aca 100644 --- a/src/_P041_NeoClock.ino +++ b/src/_P041_NeoClock.ino @@ -80,15 +80,26 @@ boolean Plugin_041(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (!Plugin_041_pixels) + if (Plugin_041_pixels == nullptr) { - Plugin_041_pixels = new Adafruit_NeoPixel(NUM_LEDS, CONFIG_PIN1, NEO_GRB + NEO_KHZ800); - Plugin_041_pixels->begin(); // This initializes the NeoPixel library. + Plugin_041_pixels = new (std::nothrow) Adafruit_NeoPixel(NUM_LEDS, CONFIG_PIN1, NEO_GRB + NEO_KHZ800); + if (Plugin_041_pixels != nullptr) { + Plugin_041_pixels->begin(); // This initializes the NeoPixel library. + } } Plugin_041_red = PCONFIG(0); Plugin_041_green = PCONFIG(1); Plugin_041_blue = PCONFIG(2); - success = true; + success = Plugin_041_pixels != nullptr; + break; + } + + case PLUGIN_EXIT: + { + if (Plugin_041_pixels != nullptr) { + delete Plugin_041_pixels; + Plugin_041_pixels = nullptr; + } break; } diff --git a/src/_P042_Candle.ino b/src/_P042_Candle.ino index 88af364a96..fd4995e94c 100644 --- a/src/_P042_Candle.ino +++ b/src/_P042_Candle.ino @@ -273,10 +273,12 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) if (Candle_pixels) { delete Candle_pixels; } - Candle_pixels = new Adafruit_NeoPixel(NUM_PIXEL, CONFIG_PIN1, NEO_GRB + NEO_KHZ800); - SetPixelsBlack(); - Candle_pixels->setBrightness(Candle_bright); - Candle_pixels->begin(); + Candle_pixels = new (std::nothrow) Adafruit_NeoPixel(NUM_PIXEL, CONFIG_PIN1, NEO_GRB + NEO_KHZ800); + if (Candle_pixels != nullptr) { + SetPixelsBlack(); + Candle_pixels->setBrightness(Candle_bright); + Candle_pixels->begin(); + } #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { @@ -287,7 +289,16 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) #endif } - success = true; + success = Candle_pixels != nullptr; + break; + } + + case PLUGIN_EXIT: + { + if (Candle_pixels != nullptr) { + delete Candle_pixels; + Candle_pixels = nullptr; + } break; } diff --git a/src/_P046_VentusW266.ino b/src/_P046_VentusW266.ino index 93a7e916f6..09f196b7d0 100644 --- a/src/_P046_VentusW266.ino +++ b/src/_P046_VentusW266.ino @@ -279,7 +279,7 @@ boolean Plugin_046(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { if (!P046_data) { - P046_data = new P046_data_struct(); + P046_data = new (std::nothrow) P046_data_struct(); } uint8_t choice = PCONFIG(0); diff --git a/src/_P054_DMX512.ino b/src/_P054_DMX512.ino index 7e41c022d2..28956720a8 100644 --- a/src/_P054_DMX512.ino +++ b/src/_P054_DMX512.ino @@ -127,10 +127,12 @@ boolean Plugin_054(uint8_t function, struct EventStruct *event, String& string) if (Plugin_054_DMXBuffer) { delete [] Plugin_054_DMXBuffer; } - Plugin_054_DMXBuffer = new uint8_t[Plugin_054_DMXSize]; - memset(Plugin_054_DMXBuffer, 0, Plugin_054_DMXSize); + Plugin_054_DMXBuffer = new (std::nothrow) uint8_t[Plugin_054_DMXSize]; + if (Plugin_054_DMXBuffer != nullptr) { + memset(Plugin_054_DMXBuffer, 0, Plugin_054_DMXSize); + } - success = true; + success = Plugin_054_DMXBuffer != nullptr; break; } diff --git a/src/_P055_Chiming.ino b/src/_P055_Chiming.ino index c471cbf75a..d23e84bf1a 100644 --- a/src/_P055_Chiming.ino +++ b/src/_P055_Chiming.ino @@ -62,35 +62,20 @@ class CPlugin_055_Data { public: - long millisStateEnd; - long millisChimeTime; - long millisPauseTime; + long millisStateEnd = 0; + long millisChimeTime = 60; + long millisPauseTime = 400; - int pin[4]; - uint8_t lowActive; - uint8_t chimeClock; + int pin[4] = {0}; + uint8_t lowActive = false; + uint8_t chimeClock = true; - char FIFO[PLUGIN_055_FIFO_SIZE]; - uint8_t FIFO_IndexR; - uint8_t FIFO_IndexW; - - void Plugin_055_Data() - { - millisStateEnd = 0; - millisChimeTime = 60; - millisPauseTime = 400; - - for (uint8_t i=0; i<4; i++) - pin[i] = -1; - lowActive = false; - chimeClock = true; - - FIFO_IndexR = 0; - FIFO_IndexW = 0; - } + char FIFO[PLUGIN_055_FIFO_SIZE] = {0}; + uint8_t FIFO_IndexR = 0; + uint8_t FIFO_IndexW = 0; }; -static CPlugin_055_Data* Plugin_055_Data = NULL; +static CPlugin_055_Data* Plugin_055_Data = nullptr; boolean Plugin_055(uint8_t function, struct EventStruct *event, String& string) @@ -188,31 +173,42 @@ boolean Plugin_055(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { if (!Plugin_055_Data) - Plugin_055_Data = new CPlugin_055_Data(); + Plugin_055_Data = new (std::nothrow) CPlugin_055_Data(); - Plugin_055_Data->lowActive = Settings.TaskDevicePin1Inversed[event->TaskIndex]; - Plugin_055_Data->millisChimeTime = PCONFIG(0); - Plugin_055_Data->millisPauseTime = PCONFIG(1); - Plugin_055_Data->chimeClock = PCONFIG(2); + if (Plugin_055_Data != nullptr) { + Plugin_055_Data->lowActive = Settings.TaskDevicePin1Inversed[event->TaskIndex]; + Plugin_055_Data->millisChimeTime = PCONFIG(0); + Plugin_055_Data->millisPauseTime = PCONFIG(1); + Plugin_055_Data->chimeClock = PCONFIG(2); - String log = F("Chime: GPIO: "); - for (uint8_t i=0; i<4; i++) - { - int pin = Settings.TaskDevicePin[i][event->TaskIndex]; - Plugin_055_Data->pin[i] = pin; - if (pin >= 0) + String log = F("Chime: GPIO: "); + for (uint8_t i=0; i<4; i++) { - pinMode(pin, OUTPUT); - digitalWrite(pin, Plugin_055_Data->lowActive); + int pin = Settings.TaskDevicePin[i][event->TaskIndex]; + Plugin_055_Data->pin[i] = pin; + if (pin >= 0) + { + pinMode(pin, OUTPUT); + digitalWrite(pin, Plugin_055_Data->lowActive); + } + log += pin; + log += ' '; } - log += pin; - log += ' '; + if (Plugin_055_Data->lowActive) + log += F("!"); + addLog(LOG_LEVEL_INFO, log); + success = true; } - if (Plugin_055_Data->lowActive) - log += F("!"); - addLog(LOG_LEVEL_INFO, log); - success = true; + break; + } + + case PLUGIN_EXIT: + { + if (Plugin_055_Data != nullptr) { + delete Plugin_055_Data; + Plugin_055_Data = nullptr; + } break; } diff --git a/src/_P056_SDS011-Dust.ino b/src/_P056_SDS011-Dust.ino index 987e9a5a37..8d57cfeee0 100644 --- a/src/_P056_SDS011-Dust.ino +++ b/src/_P056_SDS011-Dust.ino @@ -22,7 +22,7 @@ #include "ESPEasy-Globals.h" -CjkSDS011 *Plugin_056_SDS = NULL; +CjkSDS011 *Plugin_056_SDS = nullptr; boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string) @@ -105,12 +105,13 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - if (Plugin_056_SDS) + if (Plugin_056_SDS) { delete Plugin_056_SDS; + } const int16_t serial_rx = CONFIG_PIN1; const int16_t serial_tx = CONFIG_PIN2; const ESPEasySerialPort port = static_cast(CONFIG_PORT); - Plugin_056_SDS = new CjkSDS011(port, serial_rx, serial_tx); + Plugin_056_SDS = new (std::nothrow) CjkSDS011(port, serial_rx, serial_tx); String log = F("SDS : Init OK ESP GPIO-pin RX:"); log += serial_rx; log += F(" TX:"); @@ -124,6 +125,7 @@ boolean Plugin_056(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_EXIT: { // //FIXME: if this plugin is used more than once at the same time, things go horribly wrong :) + // FIXME TD-er: Must implement plugin_data_struct for this // // if (Plugin_056_SDS) // delete Plugin_056_SDS; diff --git a/src/_P062_MPR121_KeyPad.ino b/src/_P062_MPR121_KeyPad.ino index 3c8ad607f4..f15943828b 100644 --- a/src/_P062_MPR121_KeyPad.ino +++ b/src/_P062_MPR121_KeyPad.ino @@ -210,6 +210,7 @@ boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string) SaveCustomTaskSettings(event->TaskIndex, reinterpret_cast(&(P062_data->StoredSettings)), sizeof(P062_data->StoredSettings)); if (!canCalibrate) { delete P062_data; + P062_data = nullptr; } else { bool clearCalibration = isFormItemChecked(F("p062_clear_calibrate")); if (clearCalibration) { @@ -232,11 +233,12 @@ boolean Plugin_062(uint8_t function, struct EventStruct *event, String& string) P062_data_struct *P062_data = static_cast(getPluginTaskData(event->TaskIndex)); if (nullptr != P062_data) { - success = true; if (!P062_data->init(event->TaskIndex, PCONFIG(0), PCONFIG(1), tbUseCalibration)) { clearPluginTaskData(event->TaskIndex); P062_data = nullptr; } else { + success = true; + uint8_t touch_treshold = PCONFIG(2); if(touch_treshold == 0) { touch_treshold = P062_DEFAULT_TOUCH_TRESHOLD; //default value diff --git a/src/_P065_DRF0299_MP3.ino b/src/_P065_DRF0299_MP3.ino index 480dbacdc9..0e80d0dacd 100644 --- a/src/_P065_DRF0299_MP3.ino +++ b/src/_P065_DRF0299_MP3.ino @@ -121,6 +121,15 @@ boolean Plugin_065(uint8_t function, struct EventStruct *event, String& string) break; } + case PLUGIN_EXIT: + { + if (P065_easySerial != nullptr) { + delete P065_easySerial; + P065_easySerial = nullptr; + } + break; + } + case PLUGIN_WRITE: { if (!P065_easySerial) { diff --git a/src/_P078_Eastron.ino b/src/_P078_Eastron.ino index 4116936094..924dbcdea7 100644 --- a/src/_P078_Eastron.ino +++ b/src/_P078_Eastron.ino @@ -217,7 +217,6 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) case PLUGIN_INIT: { - Plugin_078_init = true; if (Plugin_078_SoftSerial != NULL) { delete Plugin_078_SoftSerial; Plugin_078_SoftSerial=NULL; @@ -233,9 +232,10 @@ boolean Plugin_078(uint8_t function, struct EventStruct *event, String& string) delete Plugin_078_SDM; Plugin_078_SDM=NULL; } - Plugin_078_SDM = new SDM(*Plugin_078_SoftSerial, baudrate, P078_DEPIN); + Plugin_078_SDM = new (std::nothrow) SDM(*Plugin_078_SoftSerial, baudrate, P078_DEPIN); if (Plugin_078_SDM != nullptr) { Plugin_078_SDM->begin(); + Plugin_078_init = true; success = true; } break; diff --git a/src/_P088_HeatpumpIR.ino b/src/_P088_HeatpumpIR.ino index ef944840c3..55585d2b10 100644 --- a/src/_P088_HeatpumpIR.ino +++ b/src/_P088_HeatpumpIR.ino @@ -207,9 +207,10 @@ boolean Plugin_088(uint8_t function, struct EventStruct *event, String& string) { addLog(LOG_LEVEL_INFO, F("P088: Heatpump IR transmitter deactivated")); - if (Plugin_088_irSender != NULL) + if (Plugin_088_irSender != nullptr) { delete Plugin_088_irSender; + Plugin_088_irSender = nullptr; } break; diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index de35e94acb..65a6c101a0 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -261,16 +261,31 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) TFT_Settings.address_tft_dc = PIN(1); TFT_Settings.address_tft_rst = PIN(2); TFT_Settings.rotation = PCONFIG(1); + if (tft != nullptr) { + delete tft; + tft = nullptr; + } - tft = new Adafruit_ILI9341(TFT_Settings.address_tft_cs, TFT_Settings.address_tft_dc, TFT_Settings.address_tft_rst); - tft->begin(); - tft->setRotation(TFT_Settings.rotation); - tft->fillScreen(ILI9341_WHITE); - Plugin_095_printText("ESPEasy", 1, 1); - success = true; + tft = new (std::nothrow) Adafruit_ILI9341(TFT_Settings.address_tft_cs, TFT_Settings.address_tft_dc, TFT_Settings.address_tft_rst); + if (tft != nullptr) { + tft->begin(); + tft->setRotation(TFT_Settings.rotation); + tft->fillScreen(ILI9341_WHITE); + Plugin_095_printText("ESPEasy", 1, 1); + success = true; + } break; } + case PLUGIN_EXIT: + { + if (tft != nullptr) { + delete tft; + tft = nullptr; + } + break; + } + case PLUGIN_WRITE: { String tmpString = String(string); diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index dcfc425498..dc7521448c 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -285,20 +285,36 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) EPD_Settings.width = PCONFIG(2); EPD_Settings.height = PCONFIG(3); - eInkScreen = new LOLIN_IL3897(EPD_Settings.width, EPD_Settings.height, EPD_Settings.address_epd_dc, EPD_Settings.address_epd_rst, EPD_Settings.address_epd_cs, EPD_Settings.address_epd_busy); //hardware SPI - plugin_096_sequence_in_progress = false; - eInkScreen->begin(); - eInkScreen->clearBuffer(); - - eInkScreen->setTextColor(EPD_BLACK); - eInkScreen->setTextSize(3); - eInkScreen->println("ESP Easy"); - eInkScreen->setTextSize(2); - eInkScreen->println("eInk shield"); - eInkScreen->display(); - delay(100); - - success = true; + if (eInkScreen != nullptr) { + delete eInkScreen; + eInkScreen = nullptr; + } + + eInkScreen = new (std::nothrow) LOLIN_IL3897(EPD_Settings.width, EPD_Settings.height, EPD_Settings.address_epd_dc, EPD_Settings.address_epd_rst, EPD_Settings.address_epd_cs, EPD_Settings.address_epd_busy); //hardware SPI + if (eInkScreen != nullptr) { + plugin_096_sequence_in_progress = false; + eInkScreen->begin(); + eInkScreen->clearBuffer(); + + eInkScreen->setTextColor(EPD_BLACK); + eInkScreen->setTextSize(3); + eInkScreen->println("ESP Easy"); + eInkScreen->setTextSize(2); + eInkScreen->println("eInk shield"); + eInkScreen->display(); + delay(100); + + success = true; + } + break; + } + + case PLUGIN_EXIT: + { + if (eInkScreen != nullptr) { + delete eInkScreen; + eInkScreen = nullptr; + } break; } diff --git a/src/_P102_PZEM004Tv3.ino b/src/_P102_PZEM004Tv3.ino index cf6e9e9a28..9f0864fcbc 100644 --- a/src/_P102_PZEM004Tv3.ino +++ b/src/_P102_PZEM004Tv3.ino @@ -232,7 +232,7 @@ boolean Plugin_102(uint8_t function, struct EventStruct *event, String& string) } // Hardware serial is RX on 3 and TX on 1 - P102_PZEM_sensor = new PZEM004Tv30(port, rxPin, txPin); + P102_PZEM_sensor = new (std::nothrow) PZEM004Tv30(port, rxPin, txPin); // Sequence for changing PZEM address if (P102_PZEM_ADDR_SET == 1) // if address programming confirmed diff --git a/src/_P109_ThermOLED.ino b/src/_P109_ThermOLED.ino index 7cbd812dce..b466a10914 100644 --- a/src/_P109_ThermOLED.ino +++ b/src/_P109_ThermOLED.ino @@ -279,9 +279,9 @@ boolean Plugin_109(byte function, struct EventStruct *event, String& string) uint8_t OLED_address = Settings.TaskDevicePluginConfig[event->TaskIndex][0]; if (Settings.TaskDevicePluginConfig[event->TaskIndex][2] == 1) { - P109_display = new SSD1306Wire(OLED_address, Settings.Pin_i2c_sda, Settings.Pin_i2c_scl); + P109_display = new (std::nothrow) SSD1306Wire(OLED_address, Settings.Pin_i2c_sda, Settings.Pin_i2c_scl); } else { - P109_display = new SH1106Wire(OLED_address, Settings.Pin_i2c_sda, Settings.Pin_i2c_scl); + P109_display = new (std::nothrow) SH1106Wire(OLED_address, Settings.Pin_i2c_sda, Settings.Pin_i2c_scl); } P109_display->init(); // call to local override of init function P109_display->displayOn(); diff --git a/src/src/DataStructs/Modbus.cpp b/src/src/DataStructs/Modbus.cpp index 843b28b5e1..5155f095ba 100644 --- a/src/src/DataStructs/Modbus.cpp +++ b/src/src/DataStructs/Modbus.cpp @@ -8,13 +8,26 @@ Modbus::Modbus() : ModbusClient(nullptr), errcnt(0), timeout(0), TXRXstate(MODBUS_IDLE), RXavailable(0), payLoad(0) {} +Modbus::~Modbus() { + if (ModbusClient) { + ModbusClient->flush(); + ModbusClient->stop(); + delete (ModbusClient); + delay(1); + ModbusClient = nullptr; + } +} + bool Modbus::begin(uint8_t function, uint8_t ModbusID, uint16_t ModbusRegister, MODBUS_registerTypes_t type, char *IPaddress) { currentRegister = ModbusRegister; currentFunction = function; incomingValue = type; resultReceived = false; - ModbusClient = new WiFiClient(); + ModbusClient = new (std::nothrow) WiFiClient(); + if (ModbusClient == nullptr) { + return false; + } ModbusClient->setNoDelay(true); ModbusClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_DFLT); timeout = millis(); diff --git a/src/src/DataStructs/Modbus.h b/src/src/DataStructs/Modbus.h index a8f97104a1..316525d5dd 100644 --- a/src/src/DataStructs/Modbus.h +++ b/src/src/DataStructs/Modbus.h @@ -14,6 +14,7 @@ class Modbus { public: Modbus(void); + ~Modbus(); bool handle(); bool begin(uint8_t function, uint8_t ModbusID, diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index 747413d967..854a0d07d9 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -627,13 +627,17 @@ static const IPAddress SSDP_MULTICAST_ADDR(239, 255, 255, 250); bool SSDP_begin() { _pending = false; - if (_server) { + if (_server != nullptr) { _server->unref(); + // FIXME TD-er: Shouldn't this also call delete _server ? - _server = 0; + _server = nullptr; } - _server = new UdpContext; + _server = new (std::nothrow) UdpContext; + if (_server == nullptr) { + return false; + } _server->ref(); ip_addr_t ifaddr; diff --git a/src/src/PluginStructs/P020_data_struct.cpp b/src/src/PluginStructs/P020_data_struct.cpp index 4f19f0f7e8..c0fe27e33e 100644 --- a/src/src/PluginStructs/P020_data_struct.cpp +++ b/src/src/PluginStructs/P020_data_struct.cpp @@ -20,6 +20,7 @@ P020_Task::P020_Task(taskIndex_t taskIndex) : _taskIndex(taskIndex) { P020_Task::~P020_Task() { stopServer(); + serialEnd(); } bool P020_Task::serverActive(WiFiServer *server) { diff --git a/src/src/PluginStructs/P044_data_struct.cpp b/src/src/PluginStructs/P044_data_struct.cpp index ed1cd37a80..785a7ff21b 100644 --- a/src/src/PluginStructs/P044_data_struct.cpp +++ b/src/src/PluginStructs/P044_data_struct.cpp @@ -19,6 +19,7 @@ P044_Task::P044_Task() { P044_Task::~P044_Task() { stopServer(); + serialEnd(); } bool P044_Task::serverActive(WiFiServer *server) { diff --git a/src/src/PluginStructs/P062_data_struct.cpp b/src/src/PluginStructs/P062_data_struct.cpp index 21fd3671f9..924c79bbf6 100644 --- a/src/src/PluginStructs/P062_data_struct.cpp +++ b/src/src/PluginStructs/P062_data_struct.cpp @@ -14,6 +14,13 @@ P062_data_struct::P062_data_struct() { clearCalibrationData(); // Reset } +P062_data_struct::~P062_data_struct() { + if (keypad != nullptr) { + delete keypad; + keypad = nullptr; + } +} + bool P062_data_struct::init(taskIndex_t taskIndex, uint8_t i2c_addr, bool scancode, @@ -26,7 +33,7 @@ bool P062_data_struct::init(taskIndex_t taskIndex, _keepCalibrationData = keepCalibrationData; if (!keypad) { - keypad = new Adafruit_MPR121(); + keypad = new (std::nothrow) Adafruit_MPR121(); } if (keypad) { keypad->begin(_i2c_addr); diff --git a/src/src/PluginStructs/P062_data_struct.h b/src/src/PluginStructs/P062_data_struct.h index 7fc3c27989..92a9acca50 100644 --- a/src/src/PluginStructs/P062_data_struct.h +++ b/src/src/PluginStructs/P062_data_struct.h @@ -14,6 +14,7 @@ struct P062_data_struct : public PluginTaskData_base { public: P062_data_struct(); + ~P062_data_struct(); bool init(taskIndex_t taskIndex, uint8_t i2c_addr, bool scancode, diff --git a/src/src/PluginStructs/P082_data_struct.cpp b/src/src/PluginStructs/P082_data_struct.cpp index 95a7fd4877..29e7275f8e 100644 --- a/src/src/PluginStructs/P082_data_struct.cpp +++ b/src/src/PluginStructs/P082_data_struct.cpp @@ -51,7 +51,7 @@ bool P082_data_struct::init(ESPEasySerialPort port, const int16_t serial_rx, con return false; } reset(); - gps = new (std::nothrow) TinyGPSPlus(); + gps = new (std::nothrow) TinyGPSPlus(); easySerial = new (std::nothrow) ESPeasySerial(port, serial_rx, serial_tx); if (easySerial != nullptr) { diff --git a/src/src/PluginStructs/P111_data_struct.cpp b/src/src/PluginStructs/P111_data_struct.cpp index 913f4a77f9..7ff358b5f0 100644 --- a/src/src/PluginStructs/P111_data_struct.cpp +++ b/src/src/PluginStructs/P111_data_struct.cpp @@ -11,9 +11,20 @@ P111_data_struct::P111_data_struct(uint8_t csPin, uint8_t rstPin) : mfrc522(nullptr), _csPin(csPin), _rstPin(rstPin) {} +P111_data_struct::~P111_data_struct() { + if (mfrc522 != nullptr) { + delete mfrc522; + mfrc522 = nullptr; + } +} + void P111_data_struct::init() { - if (mfrc522 == nullptr){ - mfrc522 = new MFRC522 (_csPin, _rstPin); // Instantiate a MFRC522 + if (mfrc522 != nullptr) { + delete mfrc522; + mfrc522 = nullptr; + } + mfrc522 = new (std::nothrow) MFRC522(_csPin, _rstPin); // Instantiate a MFRC522 + if (mfrc522 != nullptr) { mfrc522->PCD_Init(); // Initialize MFRC522 reader } } diff --git a/src/src/PluginStructs/P111_data_struct.h b/src/src/PluginStructs/P111_data_struct.h index 45f6ed66ac..08eff6cbe3 100644 --- a/src/src/PluginStructs/P111_data_struct.h +++ b/src/src/PluginStructs/P111_data_struct.h @@ -9,11 +9,12 @@ struct P111_data_struct : public PluginTaskData_base { P111_data_struct(uint8_t csPin, uint8_t rstPin); + ~P111_data_struct(); void init(); uint8_t readCardStatus(unsigned long *key, bool *removedTag); String getCardName(); - MFRC522 *mfrc522; + MFRC522 *mfrc522 = nullptr; uint8_t counter = 0; From c993904f4d8e262548156acf93527fbe51e37ddd Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 23:58:22 +0100 Subject: [PATCH 010/367] [Webserver] Fix serving CSS I made an error in previous commit for this PR --- src/src/Helpers/ESPEasy_Storage.cpp | 2 +- src/src/Static/WebStaticData.cpp | 25 +++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index e6fcdc8070..69e179414d 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -125,7 +125,7 @@ int fileSize(const String& fname) { const String patched_fname = patch_fname(fname); auto search = Cache.fileExistsMap.find(patched_fname); if (search != Cache.fileExistsMap.end()) { - return search->second >= 0; + return search->second; } int size = -1; if (ESPEASY_FS.exists(patched_fname)) { diff --git a/src/src/Static/WebStaticData.cpp b/src/src/Static/WebStaticData.cpp index 3281ff1e0d..37c39ac6f7 100644 --- a/src/src/Static/WebStaticData.cpp +++ b/src/src/Static/WebStaticData.cpp @@ -15,23 +15,28 @@ String generate_external_URL(const String& fname) { void serve_CSS() { - String url = F("esp.css"); - if (!fileExists(url)) + const String cssFile = F("esp.css"); + if (fileExists(cssFile)) { - #ifndef WEBSERVER_CSS - url = generate_external_URL(F("espeasy_default.css")); - #else addHtml(F("")); return; - #endif } + #ifndef WEBSERVER_CSS + addHtml(F("'); + #else addHtml(F("")); + #endif } void serve_favicon() { From dd774da16d650a41a70d7857964d5ea595ef6a10 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 17:21:22 +0100 Subject: [PATCH 011/367] [TLS] Add ALPN protocol --- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 11 ++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 4 ++ src/src/Helpers/ESPEasy_ssl_client.cpp | 48 +++++++++++++------- src/src/Helpers/ESPEasy_ssl_client.h | 3 +- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index f84c5aeb40..c08a7d22bd 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -41,6 +41,7 @@ ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure() _pskIdent = NULL; _psKey = NULL; next = NULL; + _alpn_protos = NULL; } @@ -64,6 +65,7 @@ ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock) _pskIdent = NULL; _psKey = NULL; next = NULL; + _alpn_protos = NULL; } ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure() @@ -125,7 +127,7 @@ int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const cha if(_timeout > 0){ sslclient->handshake_timeout = _timeout; } - int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure); + int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure, _alpn_protos); _lastError = ret; if (ret < 0) { log_e("start_ssl_client: %d", ret); @@ -145,7 +147,7 @@ int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const cha if(_timeout > 0){ sslclient->handshake_timeout = _timeout; } - int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure); + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure, _alpn_protos); _lastError = ret; if (ret < 0) { log_e("start_ssl_client: %d", ret); @@ -338,4 +340,9 @@ int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size) void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout) { sslclient->handshake_timeout = handshake_timeout * 1000; +} + +void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) +{ + _alpn_protos = alpn_protos; } \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index 08e129446b..e219b37c40 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -42,6 +42,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient const char *_private_key; const char *_pskIdent; // identity for PSK cipher suites const char *_psKey; // key in hex for PSK cipher suites + const char **_alpn_protos; public: ESPEasy_WiFiClientSecure *next; @@ -76,6 +77,9 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool loadPrivateKey(Stream& stream, size_t size); bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); + void setAlpnProtocols(const char **alpn_protos); + const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; + bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; int setTimeout(uint32_t seconds){ return 0; } diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index baa7b67b1c..ebeabe99c5 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -91,7 +91,7 @@ void ssl_init(ESPEasy_sslclient_context *ssl_client) } -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure) +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos) { char buf[512]; int ret, flags; @@ -160,6 +160,13 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return handle_error(ret); } + if (alpn_protos != NULL) { + log_v("Setting ALPN protocols"); + if ((ret = mbedtls_ssl_conf_alpn_protocols(&ssl_client->ssl_conf, alpn_protos) ) != 0) { + return handle_error(ret); + } + } + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and // MBEDTLS_SSL_VERIFY_NONE if not. @@ -433,23 +440,10 @@ bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* f fingerprint_local[i] = low | (high << 4); } - // Get certificate provided by the peer - const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); - - if (!crt) - { - log_d("could not fetch peer certificate"); - return false; - } - // Calculate certificate's SHA256 fingerprint uint8_t fingerprint_remote[32]; - mbedtls_sha256_context sha256_ctx; - mbedtls_sha256_init(&sha256_ctx); - mbedtls_sha256_starts(&sha256_ctx, false); - mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); - mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); - mbedtls_sha256_free(&sha256_ctx); + if(!get_peer_fingerprint(ssl_client, fingerprint_remote)) + return false; // Check if fingerprints match if (memcmp(fingerprint_local, fingerprint_remote, 32)) @@ -465,6 +459,28 @@ bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* f return true; } +bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]) +{ + if (!ssl_client) { + log_d("Invalid ssl_client pointer"); + return false; + }; + + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + if (!crt) { + log_d("Failed to get peer cert."); + return false; + }; + + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, sha256); + + return true; +} + // Checks if peer certificate has specified domain in CN or SANs bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name) { diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 9704b3ff80..de17f30709 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -38,12 +38,13 @@ typedef struct ESPEasy_sslclient_context { void ssl_init(ESPEasy_sslclient_context *ssl_client); -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure); +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos); void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); int data_to_read(ESPEasy_sslclient_context *ssl_client); int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); +bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); #endif \ No newline at end of file From eca630835c9a1d7bc4e70b6551586f29c974d011 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 18:01:27 +0100 Subject: [PATCH 012/367] [TLS] Add fix for WiFiClientSecure connection timeout --- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 2 +- src/src/Helpers/ESPEasy_ssl_client.cpp | 100 ++++++++++++++------- src/src/Helpers/ESPEasy_ssl_client.h | 3 +- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index e219b37c40..c228befd7a 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -57,7 +57,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); - int peek(); + int peek(); size_t write(uint8_t data); size_t write(const uint8_t *buf, size_t size); int available(); diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index ebeabe99c5..a867641a8c 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -21,12 +21,12 @@ #include #ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED -# error "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" -#endif +# warning "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" +#else const char *ESPEasy_pers = "esp32-tls"; -static int _handle_error(int err, const char * file, int line) +static int _handle_error(int err, const char * function, int line) { if(err == -30848){ return err; @@ -34,15 +34,16 @@ static int _handle_error(int err, const char * file, int line) #ifdef MBEDTLS_ERROR_C char error_buf[100]; mbedtls_strerror(err, error_buf, 100); - log_e("[%s():%d]: (%d) %s", file, line, err, error_buf); + log_e("[%s():%d]: (%d) %s", function, line, err, error_buf); #else - log_e("[%s():%d]: code %d", file, line, err); + log_e("[%s():%d]: code %d", function, line, err); #endif return err; } #define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__) + ESPEasy_sslclient_context::ESPEasy_sslclient_context() { memset(&ssl_ctx, 0, sizeof(ssl_ctx)); @@ -116,30 +117,67 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return -1; } + fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = srv; serv_addr.sin_port = htons(port); - if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { - if(timeout <= 0){ - timeout = 30000; // Milli seconds. - } - timeval so_timeout = { .tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000 }; + if(timeout <= 0){ + timeout = 30000; // Milli seconds. + } -#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &so_timeout, sizeof(so_timeout)),"SO_RCVTIMEO"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &so_timeout, sizeof(so_timeout)),"SO_SNDTIMEO"); + fd_set fdset; + struct timeval tv; + FD_ZERO(&fdset); + FD_SET(ssl_client->socket, &fdset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; - ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); - } else { - log_e("Connect to Server failed!"); + int res = lwip_connect(ssl_client->socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + if (res < 0 && errno != EINPROGRESS) { + log_e("connect on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); return -1; } - fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); + res = select(ssl_client->socket + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv); + if (res < 0) { + log_e("select on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); + return -1; + } else if (res == 0) { + log_i("select returned due to timeout %d ms for fd %d", timeout, ssl_client->socket); + close(ssl_client->socket); + return -1; + } else { + int sockerr; + socklen_t len = (socklen_t)sizeof(int); + res = getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &sockerr, &len); + + if (res < 0) { + log_e("getsockopt on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); + return -1; + } + + if (sockerr != 0) { + log_e("socket error on fd %d, errno: %d, \"%s\"", ssl_client->socket, sockerr, strerror(sockerr)); + close(ssl_client->socket); + return -1; + } + } + + +#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),"SO_RCVTIMEO"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),"SO_SNDTIMEO"); + + ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); + + log_v("Seeding the random number generator"); mbedtls_entropy_free(&ssl_client->entropy_ctx); @@ -166,7 +204,7 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return handle_error(ret); } } - + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and // MBEDTLS_SSL_VERIFY_NONE if not. @@ -223,12 +261,11 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui mbedtls_x509_crt_init(&ssl_client->client_cert); mbedtls_pk_init(&ssl_client->client_key); - log_v("Loading CRT cert"); ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); if (ret < 0) { - // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. + // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. ssl_client->free_client_cert(); return handle_error(ret); } @@ -275,9 +312,9 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui ssl_client->free_ca_cert(); ssl_client->free_client_cert(); // ++++++++++ END ++++++++++ - return -1; + return -1; } - vTaskDelay(2);//2 ticks + vTaskDelay(2);//2 ticks } @@ -351,16 +388,18 @@ int data_to_read(ESPEasy_sslclient_context *ssl_client) return res; } -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len) +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len) { log_v("Writing HTTP request with %d bytes...", len); //for low level debug int ret = -1; - if ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0){ - log_v("Handling error %d", ret); //for low level debug - return handle_error(ret); - } else{ - log_v("Returning with %d bytes written", ret); //for low level debug + while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { + log_v("Handling error %d", ret); //for low level debug + return handle_error(ret); + } + //wait for space to become available + vTaskDelay(2); } return ret; @@ -527,4 +566,5 @@ bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_nam } return false; -} \ No newline at end of file +} +#endif diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index de17f30709..86560b0faa 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -41,10 +41,9 @@ void ssl_init(ESPEasy_sslclient_context *ssl_client); int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos); void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); int data_to_read(ESPEasy_sslclient_context *ssl_client); -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len); int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); - #endif \ No newline at end of file From c909eaa22c39cb520b3e7127c8d9ab35c05ff52b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 21:52:29 +0100 Subject: [PATCH 013/367] [MQTT TLS]Display connection info on controller page --- src/src/ESPEasyCore/Controller.cpp | 15 ++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 22 ++++++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 4 +++ src/src/Helpers/ESPEasy_ssl_client.cpp | 3 ++ src/src/Helpers/ESPEasy_ssl_client.h | 3 +- src/src/WebServer/ControllerPage.cpp | 33 ++++++++++++++++++++ 6 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 3728f2d195..1335380de0 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -204,7 +204,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) uint16_t mqttPort = ControllerSettings.Port; #ifdef USE_MQTT_TLS - mqtt_tls_last_errorstr = EMPTY_STRING; + mqtt_tls_last_errorstr.clear(); mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); if (TLS_type != TLS_types::NoTLS && nullptr == mqtt_tls) { @@ -279,6 +279,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) } } if (TLS_type != TLS_types::NoTLS) { + // Certificate expiry not enabled in Mbed TLS. +// mqtt_tls->setX509Time(node_time.getUnixTime()); mqtt_tls->setTimeout(ControllerSettings.ClientTimeout); #ifdef ESP8266 mqtt_tls->setBufferSizes(1024,1024); @@ -382,6 +384,17 @@ bool MQTTConnect(controllerIndex_t controller_idx) log += clientid; addLog(LOG_LEVEL_INFO, log); + + #ifdef USE_MQTT_TLS + #ifdef ESP32 + { + log = F("MQTT : Peer certificate info: "); + log += mqtt_tls->getPeerCertificateInfo(); + addLog(LOG_LEVEL_INFO, log); + log.clear(); + } + #endif + #endif String subscribeTo = ControllerSettings.Subscribe; parseSystemVariables(subscribeTo, false); diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index c08a7d22bd..b9ee2e5e43 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -17,6 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef ESP32 #include #include #include @@ -345,4 +346,23 @@ void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeo void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) { _alpn_protos = alpn_protos; -} \ No newline at end of file +} + +String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() +{ + const mbedtls_x509_crt* peer = getPeerCertificate(); + String res; + if (peer != nullptr) { + char buf[1024] = {0}; + int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer); + if (l > 0) { + if (res.reserve(l)) { + for (int i = 0; i < l; ++i) { + res += buf[i]; + } + } + } + } + return res; +} +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index c228befd7a..0ddaffc20f 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -21,6 +21,8 @@ for memory leaks not yet present in the "older" core versions. */ +#ifdef ESP32 + #ifndef ESPEASY_WiFiClientSecure_h #define ESPEASY_WiFiClientSecure_h #include @@ -78,6 +80,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); void setAlpnProtocols(const char **alpn_protos); + String getPeerCertificateInfo(); const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; @@ -114,4 +117,5 @@ class ESPEasy_WiFiClientSecure : public WiFiClient using Print::write; }; +#endif #endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index a867641a8c..0cb0394e6d 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -8,6 +8,8 @@ * Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. */ +#ifdef ESP32 + #include #include #include @@ -568,3 +570,4 @@ bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_nam return false; } #endif +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 86560b0faa..5c612c3ae7 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -1,7 +1,7 @@ /* Provide SSL/TLS functions to ESP32 with Arduino IDE * by Evandro Copercini - 2017 - Apache 2.0 License */ - +#ifdef ESP32 #ifndef ESPEASY_ARD_SSL_H #define ESPEASY_ARD_SSL_H #include @@ -46,4 +46,5 @@ int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int le bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); +#endif #endif \ No newline at end of file diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 01934f7132..6980cc9a65 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -17,6 +17,10 @@ #include "../Globals/Protocol.h" #include "../Globals/Settings.h" +#ifdef USES_MQTT +#include "../Globals/MQTT.h" +#endif + #include "../Helpers/_CPlugin_Helper_webform.h" #include "../Helpers/_Plugin_SensorTypeHelper.h" #include "../Helpers/ESPEasy_Storage.h" @@ -417,6 +421,35 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtmlError(F("Bug in CPlugin::Function::CPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead")); } } + { + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT) { + addFormSubHeader(F("Connection Info")); + addRowLabel(F("MQTT Client Connected")); + addEnabled(MQTTclient_connected); + +#ifdef USE_MQTT_TLS + if (Protocol[ProtocolIndex].usesTLS) { + addRowLabel(F("Last Error")); + addHtmlInt(mqtt_tls_last_error); + addHtml(F(": ")); + addHtml(mqtt_tls_last_errorstr); + + #ifdef ESP32 + if (MQTTclient_connected) { + addRowLabel(F("Peer Certificate")); + String peerInfo = mqtt_tls->getPeerCertificateInfo(); + peerInfo.replace(F("\n"), F("
")); + addTextBox(F("peer_cert"), peerInfo, peerInfo.length(), true); + } + #endif + + } +#endif + } + #endif + } + // Separate enabled checkbox as it doesn't need to use the ControllerSettings. // So ControllerSettings object can be destructed before controller specific settings are loaded. addControllerEnabledForm(controllerindex); From fc22043edbae52add8b986f66e0b2058687c2397 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 21:53:26 +0100 Subject: [PATCH 014/367] [Cleanup] Use .clear() on strings instead of assigning empty string --- src/_C015.ino | 2 +- src/_P016_IR.ino | 8 ++++---- src/_P050_TCS34725.ino | 4 ++-- src/_P073_7DGT.ino | 2 +- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- src/src/PluginStructs/P104_data_struct.cpp | 6 +++--- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/_C015.ino b/src/_C015.ino index 4379528472..6821f547d8 100644 --- a/src/_C015.ino +++ b/src/_C015.ino @@ -194,7 +194,7 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String& if (!isvalid) { // send empty string to Blynk in case of error - formattedValue = EMPTY_STRING; + formattedValue.clear(); } String valueName = ExtraTaskSettings.TaskDeviceValueNames[x]; diff --git a/src/_P016_IR.ino b/src/_P016_IR.ino index f2229bc8d9..70a11c632e 100644 --- a/src/_P016_IR.ino +++ b/src/_P016_IR.ino @@ -378,7 +378,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 1), bitRead(P016_data->CommandLines[varNr].CodeFlags, P16_FLAGS_REPEAT)); html_TD(); - strCode = EMPTY_STRING; + strCode.clear(); if (P016_data->CommandLines[varNr].Code > 0) { strCode = uint64ToString(P016_data->CommandLines[varNr].Code, 16); // convert code to hex for display @@ -393,7 +393,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) html_TD(); addCheckBox(getPluginCustomArgName(rowCnt + 4), bitRead(P016_data->CommandLines[varNr].AlternativeCodeFlags, P16_FLAGS_REPEAT)); html_TD(); - strCode = EMPTY_STRING; + strCode.clear(); if (P016_data->CommandLines[varNr].AlternativeCode > 0) { strCode = uint64ToString(P016_data->CommandLines[varNr].AlternativeCode, 16); // convert code to hex for display @@ -470,7 +470,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) for (uint8_t varNr = 0; varNr < P16_Nlines; varNr++) { P016_data->CommandLines.push_back(tCommandLinesV2()); - strError = EMPTY_STRING; + strError.clear(); // Normal Code & flags P016_data->CommandLines[varNr].CodeDecodeType = static_cast(getFormItemInt(getPluginCustomArgName(rowCnt + 0))); @@ -754,7 +754,7 @@ boolean Plugin_016(uint8_t function, struct EventStruct *event, String& string) if (state.clock >= 0) { doc[F("clock")] = state.clock; // Nr. of mins past midnight to set the clock to. (< 0 means off.) } - output = EMPTY_STRING; + output.clear(); serializeJson(doc, output); event->String2 = output; diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index 73219d028b..ba4a3cd794 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -430,7 +430,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += String(static_cast(b) / t * sRGBFactor, 4); break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { @@ -466,7 +466,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += c; break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index 14ed3d6de4..91946539ae 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -401,7 +401,7 @@ struct P073_data_struct : public PluginTaskData_base { } void setTextToScroll(const String& text) { - _textToScroll = EMPTY_STRING; + _textToScroll.clear(); if (text.length() > 0) { int bufToFill = getBufferLength(displayModel); diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 7d0c1d81cd..2750262741 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -252,7 +252,7 @@ String rulesProcessingFile(const String& fileName, const String& event) { } // Prepare for new line - line = EMPTY_STRING; + line.clear(); line.reserve(longestLineSize); firstNonSpaceRead = false; commentFound = false; @@ -743,7 +743,7 @@ void parseCompleteNonCommentLine(String& line, const String& event, String eventTrigger; - action = EMPTY_STRING; + action.clear(); if (!codeBlock) // do not check "on" rules if a block of actions is to be // processed diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index effa3775b5..0ea77e8444 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -334,7 +334,7 @@ void P104_data_struct::loadSettings() { zones.push_back(P104_zone_struct(zoneIndex + 1)); if (zones[zoneIndex].text == F("\"\"")) { // Special case - zones[zoneIndex].text = EMPTY_STRING; + zones[zoneIndex].text.clear(); } zoneIndex++; @@ -1565,7 +1565,7 @@ String P104_data_struct::enquoteString(const String& input) { * saveSettings gather the zones data from the UI and store in customsettings **************************************/ bool P104_data_struct::saveSettings() { - error = EMPTY_STRING; // Clear + error.clear(); // Clear String zbuffer; # ifdef P104_DEBUG_DEV @@ -1680,7 +1680,7 @@ bool P104_data_struct::saveSettings() { if (zbuffer.reserve(P104_SETTINGS_BUFFER_V2 + 2)) { for (auto it = zones.begin(); it != zones.end() && error.length() == 0; ++it) { - zbuffer = EMPTY_STRING; + zbuffer.clear(); // WARNING: Order of values should match the numeric order of P104_OFFSET_* values zbuffer += it->size; // 2 From c8d56bea50dbdbf3111a64196dffdb38cfca481c Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 7 Nov 2021 01:05:32 +0100 Subject: [PATCH 015/367] [TLS] Making proper datastructure init --- src/src/Helpers/ESPEasy_ssl_client.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index 0cb0394e6d..bf8ff5dbd6 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -48,13 +48,14 @@ static int _handle_error(int err, const char * function, int line) ESPEasy_sslclient_context::ESPEasy_sslclient_context() { - memset(&ssl_ctx, 0, sizeof(ssl_ctx)); - memset(&ssl_conf, 0, sizeof(ssl_conf)); - memset(&drbg_ctx, 0, sizeof(drbg_ctx)); - memset(&entropy_ctx, 0, sizeof(entropy_ctx)); - memset(&ca_cert, 0, sizeof(ca_cert)); - memset(&client_cert, 0, sizeof(client_cert)); - memset(&client_key, 0, sizeof(client_key)); + mbedtls_ssl_init(&ssl_ctx); + mbedtls_ssl_config_init(&ssl_conf); + mbedtls_ctr_drbg_init(&drbg_ctx); + + mbedtls_entropy_init(&entropy_ctx); + mbedtls_x509_crt_init(&ca_cert); + mbedtls_x509_crt_init(&client_cert); + mbedtls_pk_init(&client_key); } From d801c36c2a132a35c07658d6f3672edd8e320923 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 7 Nov 2021 01:06:15 +0100 Subject: [PATCH 016/367] [MQTT TLS] Improve controller setup page show peer certificate --- src/src/WebServer/ControllerPage.cpp | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 6980cc9a65..3e154427c0 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -422,9 +422,9 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex } } { - #ifdef USES_MQTT +#ifdef USES_MQTT if (Protocol[ProtocolIndex].usesMQTT) { - addFormSubHeader(F("Connection Info")); + addFormSubHeader(F("Connection Status")); addRowLabel(F("MQTT Client Connected")); addEnabled(MQTTclient_connected); @@ -436,23 +436,46 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtml(mqtt_tls_last_errorstr); #ifdef ESP32 - if (MQTTclient_connected) { - addRowLabel(F("Peer Certificate")); - String peerInfo = mqtt_tls->getPeerCertificateInfo(); - peerInfo.replace(F("\n"), F("
")); - addTextBox(F("peer_cert"), peerInfo, peerInfo.length(), true); + if (MQTTclient_connected && mqtt_tls != nullptr) { + addFormSubHeader(F("Peer Certificate")); + + { + addRowLabel(F("Certificate Info")); + addHtml(F("")); + } + { + uint8_t sha256_result[32] = {0}; + if (mqtt_tls->getFingerprintSHA256(sha256_result)) { + String fingerprint; + fingerprint.reserve(64); + for (size_t i = 0; i < 32; ++i) { + fingerprint += String(sha256_result[i], HEX); + } + fingerprint.toLowerCase(); + addFormTextBox(F("Certificate Fingerprint"), + F("fingerprint"), + fingerprint, + 64, + true); // ReadOnly + } + } + + } #endif } #endif } - #endif +#endif } // Separate enabled checkbox as it doesn't need to use the ControllerSettings. // So ControllerSettings object can be destructed before controller specific settings are loaded. addControllerEnabledForm(controllerindex); + } addFormSeparator(2); From 8af28b820b818e3bfb34d46e54fa8ea2768c5d3c Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 25 Nov 2021 01:46:05 +0100 Subject: [PATCH 017/367] [MQTT TLS] Add certificate fingerprint validation --- .../DataStructs/ControllerSettingsStruct.cpp | 3 + .../DataStructs/ControllerSettingsStruct.h | 1 + src/src/DataStructs/Web_StreamingBuffer.cpp | 5 +- src/src/DataTypes/TLS_types.cpp | 1 + src/src/DataTypes/TLS_types.h | 1 + src/src/ESPEasyCore/Controller.cpp | 88 +++++++++++++++++- src/src/ESPEasyCore/Controller.h | 6 ++ src/src/Globals/MQTT.cpp | 1 + src/src/Globals/MQTT.h | 2 + src/src/Helpers/ESPEasy_Storage.cpp | 90 ++++++++++--------- src/src/Helpers/ESPEasy_Storage.h | 2 +- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 1 + src/src/Helpers/_CPlugin_Helper_webform.cpp | 69 ++++++++++---- src/src/WebServer/ControllerPage.cpp | 19 ++-- src/src/WebServer/SysInfoPage.cpp | 17 ++-- src/src/WebServer/WebServer.cpp | 26 ++++-- src/src/WebServer/WebServer.h | 12 +-- 17 files changed, 248 insertions(+), 96 deletions(-) diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 0a272123b1..b5252c4257 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -349,6 +349,9 @@ String ControllerSettingsStruct::getCertificateFilename() const case TLS_types::TLS_CA_CERT: certFile += F(".cacert"); break; + case TLS_types::TLS_FINGERPRINT: + certFile += F(".fp"); + break; } return certFile; diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 73aff6dc70..e5d529f6a7 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -65,6 +65,7 @@ struct ControllerSettingsStruct CONTROLLER_IP, CONTROLLER_PORT, CONTROLLER_MQTT_TLS_TYPE, + CONTROLLER_MQTT_TLS_STORE_FINGERPRINT, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, diff --git a/src/src/DataStructs/Web_StreamingBuffer.cpp b/src/src/DataStructs/Web_StreamingBuffer.cpp index 4898333e2d..114b05afa8 100644 --- a/src/src/DataStructs/Web_StreamingBuffer.cpp +++ b/src/src/DataStructs/Web_StreamingBuffer.cpp @@ -11,8 +11,11 @@ #include "../Helpers/ESPEasy_time_calc.h" - +#ifdef ESP32 +#define CHUNKED_BUFFER_SIZE 1500 +#else #define CHUNKED_BUFFER_SIZE 400 +#endif Web_StreamingBuffer::Web_StreamingBuffer(void) : lowMemorySkip(false), initialRam(0), beforeTXRam(0), duringTXRam(0), finalRam(0), maxCoreUsage(0), diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp index 5eaf38b4b1..805d8bc5a3 100644 --- a/src/src/DataTypes/TLS_types.cpp +++ b/src/src/DataTypes/TLS_types.cpp @@ -7,6 +7,7 @@ const __FlashStringHelper* toString(TLS_types tls_type) case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); case TLS_types::TLS_CA_CERT: return F("TLS CA Cert"); case TLS_types::TLS_insecure: return F("TLS No Checks (insecure)"); + case TLS_types::TLS_FINGERPRINT: return F("TLS Certficate Fingerprint"); } return F("No TLS"); } diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index a8aec9aa14..3cf81fd398 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -11,6 +11,7 @@ enum class TLS_types { TLS_PSK = 1, // Pre-Shared-Key TLS_CA_CERT = 2, // Validate server certificate against known CA //TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_FINGERPRINT = 4, // Use certificate fingerprint TLS_insecure = 0xF // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. }; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 1335380de0..ee15b26e21 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -217,7 +217,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { - addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); + mqtt_tls_last_errorstr = F("MQTT : Could not create TLS client, out of memory"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); return false; } } @@ -254,6 +255,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + if (mqtt_rootCA.isEmpty()) { + // Fingerprint must be of some minimal length to continue. + mqtt_tls_last_errorstr = F("MQTT : No TLS root CA"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); + return false; + } #ifdef ESP32 mqtt_tls->setCACert(mqtt_rootCA.c_str()); @@ -272,8 +279,24 @@ bool MQTTConnect(controllerIndex_t controller_idx) break; } */ + case TLS_types::TLS_FINGERPRINT: + { + // Fingerprint is checked when making the connection. + mqtt_rootCA.clear(); + mqtt_fingerprint.clear(); + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_fingerprint, false); + if (mqtt_fingerprint.length() < 32) { + // Fingerprint must be of some minimal length to continue. + mqtt_tls_last_errorstr = F("MQTT : Stored TLS fingerprint too small"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); + return false; + } + mqtt_tls->setInsecure(); + break; + } case TLS_types::TLS_insecure: { + mqtt_rootCA.clear(); mqtt_tls->setInsecure(); break; } @@ -355,9 +378,41 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif #ifdef ESP32 mqtt_tls_last_error = mqtt_tls->lastError(buf,128); + mqtt_tls->clearLastError(); #endif mqtt_tls_last_errorstr = buf; } + if (TLS_type == TLS_types::TLS_FINGERPRINT) + { + // Check fingerprint + if (MQTTresult) { + const int newlinepos = mqtt_fingerprint.indexOf('\n'); + String fp; + String dn; + if (ControllerSettings.UseDNS) dn = ControllerSettings.getHost(); + if (newlinepos == -1) { + fp = mqtt_fingerprint; + } else { + fp = mqtt_fingerprint.substring(0, newlinepos); + const int newlinepos2 = mqtt_fingerprint.indexOf('\n', newlinepos); + if (newlinepos2 == -1) + dn = mqtt_fingerprint.substring(newlinepos + 1); + else + dn = mqtt_fingerprint.substring(newlinepos + 1, newlinepos2); + dn.trim(); + + } + if (!mqtt_tls->verify( + fp.c_str(), + dn.isEmpty() ? nullptr : dn.c_str())) + { + mqtt_tls_last_errorstr += F("TLS Fingerprint does not match"); + addLog(LOG_LEVEL_INFO, mqtt_fingerprint); + MQTTresult = false; + } + } + } + #endif @@ -715,6 +770,37 @@ void MQTTStatus(struct EventStruct *event, const String& status) } } + +#ifdef USE_MQTT_TLS +bool GetTLSfingerprint(String& fp) +{ + #ifdef ESP32 + if (MQTTclient_connected && mqtt_tls != nullptr) { + uint8_t sha256_result[32] = {0}; + if (mqtt_tls->getFingerprintSHA256(sha256_result)) { + fp.reserve(64); + for (size_t i = 0; i < 32; ++i) { + const String tmp(sha256_result[i], HEX); + switch (tmp.length()) { + case 0: + fp += '0'; + // fall through + case 1: + fp += '0'; + break; + } + fp += tmp; + } + fp.toLowerCase(); + return true; + } + } + #endif + return false; +} + +#endif + #endif // USES_MQTT diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index 0a69b48a4f..e14ac48748 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -69,6 +69,12 @@ bool MQTTpublish(controllerIndex_t controller_idx, taskIndex_t taskIndex, Strin * Send status info back to channel where request came from \*********************************************************************************************/ void MQTTStatus(struct EventStruct *event, const String& status); + +#ifdef USE_MQTT_TLS +bool GetTLSfingerprint(String& fp); + +#endif + #endif //USES_MQTT diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index d859f7b138..23eb7dd410 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -16,6 +16,7 @@ BearSSL::WiFiClientSecure* mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 String mqtt_rootCA; +String mqtt_fingerprint; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 3cf7ff14b3..d73894f02b 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -33,6 +33,8 @@ extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 extern String mqtt_rootCA; +extern String mqtt_fingerprint; + # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 69e179414d..b2a46b1af7 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1070,7 +1070,7 @@ String SaveCertificate(const String& fname, const String& certificate) return SaveToFile(fname.c_str(), 0, (const uint8_t *)certificate.c_str(), certificate.length() + 1); } -String LoadCertificate(const String& fname, String& certificate) +String LoadCertificate(const String& fname, String& certificate, bool cleanup) { bool changed = false; if (fileExists(fname)) { @@ -1104,15 +1104,17 @@ String LoadCertificate(const String& fname, String& certificate) } f.close(); - if (!cleanupCertificate(certificate, changed)) { - certificate.clear(); - #ifndef BUILD_NO_DEBUG - log += F(" ERROR, Invalid certificate format"); - #endif - addLog(LOG_LEVEL_ERROR, log); - return log; - } else if (changed) { - //return SaveCertificate(fname, certificate); + if (cleanup) { + if (!cleanupCertificate(certificate, changed)) { + certificate.clear(); + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Invalid certificate format"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } else if (changed) { + //return SaveCertificate(fname, certificate); + } } } @@ -1532,56 +1534,60 @@ size_t SpiffsUsedBytes() { } size_t SpiffsTotalBytes() { - size_t result = 1; // Do not output 0, this may be used in divisions. - - #ifdef ESP32 - result = ESPEASY_FS.totalBytes(); - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.totalBytes; - #endif // ifdef ESP8266 + static size_t result = 1; // Do not output 0, this may be used in divisions. + if (result == 1) { + #ifdef ESP32 + result = ESPEASY_FS.totalBytes(); + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.totalBytes; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsBlocksize() { - size_t result = 8192; // Some default viable for most 1 MB file systems - - #ifdef ESP32 - result = 8192; // Just assume 8k, since we cannot query it - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.blockSize; - #endif // ifdef ESP8266 + static size_t result = 1; + if (result == 1) { + #ifdef ESP32 + result = 8192; // Just assume 8k, since we cannot query it + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.blockSize; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsPagesize() { - size_t result = 256; // Most common - - #ifdef ESP32 - result = 256; // Just assume 256, since we cannot query it - #endif // ifdef ESP32 - #ifdef ESP8266 - fs::FSInfo fs_info; - ESPEASY_FS.info(fs_info); - result = fs_info.pageSize; - #endif // ifdef ESP8266 + static size_t result = 1; + if (result == 1) { + #ifdef ESP32 + result = 256; // Just assume 256, since we cannot query it + #endif // ifdef ESP32 + #ifdef ESP8266 + fs::FSInfo fs_info; + ESPEASY_FS.info(fs_info); + result = fs_info.pageSize; + #endif // ifdef ESP8266 + } return result; } size_t SpiffsFreeSpace() { int freeSpace = SpiffsTotalBytes() - SpiffsUsedBytes(); + const size_t blocksize = SpiffsBlocksize(); - if (freeSpace < static_cast(2 * SpiffsBlocksize())) { + if (freeSpace < static_cast(2 * blocksize)) { // Not enough free space left to store anything // There needs to be minimum of 2 free blocks. return 0; } - return freeSpace - 2 * SpiffsBlocksize(); + return freeSpace - 2 * blocksize; } bool SpiffsFull() { diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index 620c041bbe..e450c7917b 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -177,7 +177,7 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int The content will be stripped from unusable character like quotes, spaces etc. \*********************************************************************************************/ String SaveCertificate(const String& fname, const String& certificate); -String LoadCertificate(const String& fname, String& certificate); +String LoadCertificate(const String& fname, String& certificate, bool cleanup = true); /********************************************************************************************\ diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index 0ddaffc20f..b5241bfda5 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -69,6 +69,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient void stop(); uint8_t connected(); int lastError(char *buf, const size_t size); + void clearLastError() { _lastError = 0; } void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE! void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex void setCACert(const char *rootCA); diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 7c89a385c8..8fe2075fdf 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -21,7 +21,8 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); - case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: return F("Store Fingerprint"); case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -118,6 +119,26 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } +void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description) { + #ifdef USE_MQTT_TLS + const String certFile = ControllerSettings.getCertificateFilename(); + if (!certFile.isEmpty()) + { + const String certFile = ControllerSettings.getCertificateFilename(); + String note = description; + note += F(" "); + note += certFile; + note += F(" "); + if (fileExists(certFile)) { + note += F("(File exists)"); + } else { + note += F("(Not found)"); + } + addFormNote(note); + } + #endif +} + void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (!validProtocolIndex(ProtocolIndex)) { @@ -157,35 +178,33 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin { #ifdef USE_MQTT_TLS const int choice = static_cast(ControllerSettings.TLStype()); - #define NR_MQTT_TLS_TYPES 3 + #define NR_MQTT_TLS_TYPES 4 const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_FINGERPRINT), toString(TLS_types::TLS_insecure) }; const int indices[NR_MQTT_TLS_TYPES] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_FINGERPRINT), static_cast(TLS_types::TLS_insecure) }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES - const String certFile = ControllerSettings.getCertificateFilename(); - if (!certFile.isEmpty()) - { - const String certFile = ControllerSettings.getCertificateFilename(); - String note = F("Certificate or PSK must be stored on the filesystem in "); - note += certFile; - note += F(" "); - if (fileExists(certFile)) { - note += F("(File exists)"); - } else { - note += F("(Not found)"); - } - addFormNote(note); - } + addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in")); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: + { + #ifdef USE_MQTT_TLS + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename()); + addFormCheckBox(displayName, internalName, false, saveDisabled); + addCertificateFileNote(ControllerSettings, F("Store fingerprint in")); #endif break; } @@ -353,6 +372,24 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: + { + #ifdef USE_MQTT_TLS + if (isFormItemChecked(internalName)) { + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { + if (ControllerSettings.UseDNS) { + fingerprint += '\n'; + fingerprint += ControllerSettings.getHost(); + } + SaveCertificate(ControllerSettings.getCertificateFilename(), fingerprint); + } + } + #endif + break; + } + + case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); break; diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 3e154427c0..02e2c63980 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -446,23 +446,22 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtml(F("")); } { - uint8_t sha256_result[32] = {0}; - if (mqtt_tls->getFingerprintSHA256(sha256_result)) { - String fingerprint; - fingerprint.reserve(64); - for (size_t i = 0; i < 32; ++i) { - fingerprint += String(sha256_result[i], HEX); - } - fingerprint.toLowerCase(); + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { addFormTextBox(F("Certificate Fingerprint"), F("fingerprint"), fingerprint, 64, true); // ReadOnly + MakeControllerSettings(ControllerSettings); //-V522 + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, ControllerSettings); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); + } } } - - } #endif diff --git a/src/src/WebServer/SysInfoPage.cpp b/src/src/WebServer/SysInfoPage.cpp index cb12214040..ebb31b6272 100644 --- a/src/src/WebServer/SysInfoPage.cpp +++ b/src/src/WebServer/SysInfoPage.cpp @@ -612,8 +612,8 @@ void handle_sysinfo_Storage() { uint32_t flashDevice = (flashChipId & 0xFF00) | ((flashChipId >> 16) & 0xFF); addHtml(formatToHex(flashDevice)); } - uint32_t realSize = getFlashRealSizeInBytes(); - uint32_t ideSize = ESP.getFlashChipSize(); + const uint32_t realSize = getFlashRealSizeInBytes(); + const uint32_t ideSize = ESP.getFlashChipSize(); addRowLabel(LabelType::FLASH_CHIP_REAL_SIZE); addHtmlInt(realSize / 1024); @@ -632,17 +632,14 @@ void handle_sysinfo_Storage() { FlashMode_t ideMode = ESP.getFlashChipMode(); addRowLabel(LabelType::FLASH_IDE_MODE); { - String html; - switch (ideMode) { - case FM_QIO: html += F("QIO"); break; - case FM_QOUT: html += F("QOUT"); break; - case FM_DIO: html += F("DIO"); break; - case FM_DOUT: html += F("DOUT"); break; + case FM_QIO: addHtml(F("QIO")); break; + case FM_QOUT: addHtml(F("QOUT")); break; + case FM_DIO: addHtml(F("DIO")); break; + case FM_DOUT: addHtml(F("DOUT")); break; default: - html += getUnknownString(); break; + addHtml(getUnknownString()); break; } - addHtml(html); } # endif // if defined(ESP8266) diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index 8191c26430..83da72c798 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -1041,11 +1041,21 @@ String getControllerSymbol(uint8_t index) return ret; } */ -void addSVG_param(const String& key, float value) { + +void addSVG_param(const __FlashStringHelper * key, int value) { + addHtml(' '); + addHtml(key); + addHtml('='); + addHtml('\"'); + addHtmlInt(value); + addHtml('\"'); +} + +void addSVG_param(const __FlashStringHelper * key, float value) { addSVG_param(key, String(value, 2)); } -void addSVG_param(const String& key, const String& value) { +void addSVG_param(const __FlashStringHelper * key, const String& value) { addHtml(' '); addHtml(key); addHtml('='); @@ -1078,8 +1088,8 @@ void createSvgRect(const String& classname, addSVG_param(F("stroke"), formatToHex(strokeColor, F("#"))); addSVG_param(F("stroke-width"), strokeWidth); } - addSVG_param("x", xoffset); - addSVG_param("y", yoffset); + addSVG_param(F("x"), xoffset); + addSVG_param(F("y"), yoffset); addSVG_param(F("width"), width); addSVG_param(F("height"), height); addSVG_param(F("rx"), rx); @@ -1123,10 +1133,6 @@ void createSvgTextElement(const String& text, float textXoffset, float textYoffs #define SVG_BAR_HEIGHT 16 #define SVG_BAR_WIDTH 400 -void write_SVG_image_header(int width, int height) { - write_SVG_image_header(width, height, false); -} - void write_SVG_image_header(int width, int height, bool useViewbox) { addHtml(F(" Date: Tue, 30 Nov 2021 00:40:47 +0100 Subject: [PATCH 018/367] [MQTT TLS] Add view of certificates + option to store --- .../ControllerQueue/DelayQueueElements.cpp | 4 + .../DataStructs/ControllerSettingsStruct.cpp | 12 ++- .../DataStructs/ControllerSettingsStruct.h | 3 + src/src/ESPEasyCore/Controller.cpp | 39 ++++++-- src/src/ESPEasyCore/Controller.h | 2 + src/src/Helpers/ESPEasy_Storage.cpp | 10 ++- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 73 ++++++++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 8 +- src/src/Helpers/ESPEasy_ssl_client.h | 1 + src/src/Helpers/_CPlugin_Helper_webform.cpp | 49 ++++++++-- src/src/WebServer/ControllerPage.cpp | 89 ++++++++++++++----- 11 files changed, 245 insertions(+), 45 deletions(-) diff --git a/src/src/ControllerQueue/DelayQueueElements.cpp b/src/src/ControllerQueue/DelayQueueElements.cpp index d7d99aca43..34bc4c270e 100644 --- a/src/src/ControllerQueue/DelayQueueElements.cpp +++ b/src/src/ControllerQueue/DelayQueueElements.cpp @@ -9,6 +9,9 @@ ControllerDelayHandlerStruct *MQTTDelayHandler = nullptr; bool init_mqtt_delay_queue(controllerIndex_t ControllerIndex, String& pubname, bool& retainFlag) { + // Make sure the controller is re-connecting with the current settings. + MQTTDisconnect(); + MakeControllerSettings(ControllerSettings); //-V522 if (!AllocatedControllerSettings()) { return false; @@ -30,6 +33,7 @@ bool init_mqtt_delay_queue(controllerIndex_t ControllerIndex, String& pubname, b void exit_mqtt_delay_queue() { if (MQTTDelayHandler != nullptr) { + MQTTDisconnect(); delete MQTTDelayHandler; MQTTDelayHandler = nullptr; } diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index b5252c4257..2af808ca9b 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -328,13 +328,18 @@ void ControllerSettingsStruct::TLStype(TLS_types tls_type) } String ControllerSettingsStruct::getCertificateFilename() const +{ + return getCertificateFilename(TLStype()); +} + +String ControllerSettingsStruct::getCertificateFilename(TLS_types tls_type) const { String certFile = HostName; if (certFile.isEmpty()) { certFile = F(""); } - switch (TLStype()) { + switch (tls_type) { case TLS_types::NoTLS: case TLS_types::TLS_insecure: return EMPTY_STRING; @@ -353,6 +358,11 @@ String ControllerSettingsStruct::getCertificateFilename() const certFile += F(".fp"); break; } + + // Only use the last 29 bytes of the filename + if (certFile.length() > 28) { + certFile = certFile.substring(certFile.length() - 28); + } return certFile; } \ No newline at end of file diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index e5d529f6a7..5f4169f298 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -66,6 +66,8 @@ struct ControllerSettingsStruct CONTROLLER_PORT, CONTROLLER_MQTT_TLS_TYPE, CONTROLLER_MQTT_TLS_STORE_FINGERPRINT, + CONTROLLER_MQTT_TLS_STORE_CERT, + CONTROLLER_MQTT_TLS_STORE_CACERT, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, @@ -154,6 +156,7 @@ struct ControllerSettingsStruct void TLStype(TLS_types tls_type); String getCertificateFilename() const; + String getCertificateFilename(TLS_types tls_type) const; boolean UseDNS; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index ee15b26e21..66cf39f148 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -237,6 +237,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); + /* { static int previousFree = FreeMem(); const int freemem = FreeMem(); @@ -252,6 +253,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) addLog(LOG_LEVEL_INFO, analyse); previousFree = freemem; } + */ if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); @@ -435,18 +437,23 @@ bool MQTTConnect(controllerIndex_t controller_idx) updateMQTTclient_connected(); return false; } - String log = F("MQTT : Connected to broker with client ID: "); + if (loglevelActiveFor(LOG_LEVEL_INFO)) + { + String log = F("MQTT : Connected to broker with client ID: "); - log += clientid; - addLog(LOG_LEVEL_INFO, log); + log += clientid; + addLog(LOG_LEVEL_INFO, log); + } #ifdef USE_MQTT_TLS #ifdef ESP32 + if (loglevelActiveFor(LOG_LEVEL_INFO)) { - log = F("MQTT : Peer certificate info: "); + String log = F("MQTT : Peer certificate info: "); + log += ControllerSettings.getHost(); + log += ' '; log += mqtt_tls->getPeerCertificateInfo(); addLog(LOG_LEVEL_INFO, log); - log.clear(); } #endif #endif @@ -454,9 +461,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) parseSystemVariables(subscribeTo, false); MQTTclient.subscribe(subscribeTo.c_str()); - log = F("Subscribed to: "); - log += subscribeTo; - addLog(LOG_LEVEL_INFO, log); + if (loglevelActiveFor(LOG_LEVEL_INFO)) + { + String log = F("Subscribed to: "); + log += subscribeTo; + addLog(LOG_LEVEL_INFO, log); + } updateMQTTclient_connected(); statusLED(true); @@ -799,6 +809,19 @@ bool GetTLSfingerprint(String& fp) return false; } +bool GetTLS_Certificate(String& cert, bool caRoot) +{ + #ifdef ESP32 + if (MQTTclient_connected && mqtt_tls != nullptr) { + String subject; + if (mqtt_tls->getPeerCertificate(cert, subject, caRoot) == 0) { + return true; + } + } + #endif + return false; +} + #endif #endif // USES_MQTT diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index e14ac48748..4529d17375 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -73,6 +73,8 @@ void MQTTStatus(struct EventStruct *event, const String& status); #ifdef USE_MQTT_TLS bool GetTLSfingerprint(String& fp); +bool GetTLS_Certificate(String& cert, bool caRoot); + #endif #endif //USES_MQTT diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index b2a46b1af7..c9a216b355 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1031,7 +1031,7 @@ bool cleanupCertificate(String & certificate, bool &changed) for (int i = 0; i < 4 && last_pos != -1; ++i) { dash_pos[i] = certificate.indexOf(F("-----"), last_pos); last_pos = dash_pos[i] + 5; - addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); +// addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); } if (last_pos == -1) return false; @@ -1164,7 +1164,9 @@ String InitFile(SettingsType::SettingsFileEnum file_type) \*********************************************************************************************/ String SaveToFile(const char *fname, int index, const uint8_t *memAddress, int datasize) { - return doSaveToFile(fname, index, memAddress, datasize, "r+"); + return doSaveToFile( + fname, index, memAddress, datasize, + fileExists(fname) ? "r+" : "w+"); } // See for mode description: https://github.com/esp8266/Arduino/blob/master/doc/filesystem.rst @@ -1215,7 +1217,9 @@ String doSaveToFile(const char *fname, int index, const uint8_t *memAddress, int if (f) { clearAllCaches(); SPIFFS_CHECK(f, fname); - SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname); + if (index > 0) { + SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname); + } const uint8_t *pointerToByteToSave = memAddress; for (int x = 0; x < datasize; x++) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index b9ee2e5e43..5d924ca8b0 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -22,6 +22,9 @@ #include #include +// FIXME TD-er: Feels wrong this needs to be included here to use mbedtls_pem_write_buffer +#include + #undef connect #undef write #undef read @@ -348,9 +351,11 @@ void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) _alpn_protos = alpn_protos; } -String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() +String ESPEasy_WiFiClientSecure::getPeerCertificateInfo(const mbedtls_x509_crt* peer) { - const mbedtls_x509_crt* peer = getPeerCertificate(); + if (peer == nullptr) { + peer = getPeerCertificate(); + } String res; if (peer != nullptr) { char buf[1024] = {0}; @@ -365,4 +370,68 @@ String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() } return res; } + +int ESPEasy_WiFiClientSecure::getPeerCertificate(String& pem, String& subject, bool caRoot) +{ + const mbedtls_x509_crt *chain; + + chain = getPeerCertificate(); + + int error {0}; + bool done = false; + while (chain != nullptr && error == 0 && !done) { + if (!caRoot || (chain->ca_istrue && chain->next == nullptr)) { + done = true; + error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); + } + chain = chain->next; + } + return error; +} + +int ESPEasy_WiFiClientSecure::cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject) +{ + const String pem_begin_crt = F("-----BEGIN CERTIFICATE-----\n"); + const String pem_end_crt = F("-----END CERTIFICATE-----"); + pem.clear(); + subject.clear(); + + const mbedtls_asn1_named_data* common_name = &crt->subject; + while (common_name != nullptr) { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + + subject.reserve(common_name->val.len); + const unsigned char* p = common_name->val.p; + for (auto i = 0; i < common_name->val.len; ++i, ++p) { + subject += static_cast(*p); + } + } + + // Fetch next DN object + common_name = common_name->next; + } + + size_t written{}; + + const size_t buffer_size = + pem_begin_crt.length() + + pem_end_crt.length() + + 2* crt->raw.len; + + std::vector pem_buf; + pem_buf.resize(buffer_size); + int ret = mbedtls_pem_write_buffer( + pem_begin_crt.c_str(), pem_end_crt.c_str(), + crt->raw.p, crt->raw.len, + &pem_buf[0], buffer_size, &written); + if (ret == 0) { + pem.reserve(written); + for (auto i = 0; i < written; ++i) { + pem += static_cast(pem_buf[i]); + } + } + return ret; +} #endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index b5241bfda5..a35fa92bfb 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -81,10 +81,16 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); void setAlpnProtocols(const char **alpn_protos); - String getPeerCertificateInfo(); + String getPeerCertificateInfo(const mbedtls_x509_crt* crt = nullptr); const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; + int getPeerCertificate(String& pem, String& subject, bool caRoot); + + // See: https://stackoverflow.com/a/63730321/8708166 + static int cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject); + + int setTimeout(uint32_t seconds){ return 0; } operator bool() diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 5c612c3ae7..7f6eebf034 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -46,5 +46,6 @@ int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int le bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); + #endif #endif \ No newline at end of file diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 8fe2075fdf..6628446c3f 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -23,6 +23,9 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: return F("Store Fingerprint"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: return F("Store Certificate"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: return F("Store CA Certificate"); + case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -119,12 +122,11 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } -void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description) { +void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description, TLS_types tls_type) { #ifdef USE_MQTT_TLS - const String certFile = ControllerSettings.getCertificateFilename(); + const String certFile = ControllerSettings.getCertificateFilename(tls_type); if (!certFile.isEmpty()) { - const String certFile = ControllerSettings.getCertificateFilename(); String note = description; note += F(" "); note += certFile; @@ -195,19 +197,38 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES - addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in")); + addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in"), ControllerSettings.TLStype()); #endif break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { #ifdef USE_MQTT_TLS - const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename()); + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT)); addFormCheckBox(displayName, internalName, false, saveDisabled); - addCertificateFileNote(ControllerSettings, F("Store fingerprint in")); + addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); #endif break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: + // fall through + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: + { + #ifdef USE_MQTT_TLS + /* + const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? + TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; + */ + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT)); + addFormCheckBox(displayName, internalName, false, saveDisabled); + if (saveDisabled) { + addUnit(F("File Exists")); + } + addCertificateFileNote(ControllerSettings, F("Store CA Certificate in"), TLS_types::TLS_CA_CERT); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_USER: { const size_t fieldMaxLength = @@ -382,13 +403,27 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet fingerprint += '\n'; fingerprint += ControllerSettings.getHost(); } - SaveCertificate(ControllerSettings.getCertificateFilename(), fingerprint); + SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT), fingerprint); } } #endif break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: + // fall through + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: + { + #ifdef USE_MQTT_TLS + if (isFormItemChecked(internalName)) { + String cacert; + if (GetTLS_Certificate(cacert, true)) { + SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT), cacert); + } + } + #endif + break; + } case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 02e2c63980..9824f31af1 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -27,7 +27,6 @@ #include "../Helpers/StringConverter.h" - // ******************************************************************************** // Web Interface controller page // ******************************************************************************** @@ -437,34 +436,78 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex #ifdef ESP32 if (MQTTclient_connected && mqtt_tls != nullptr) { - addFormSubHeader(F("Peer Certificate")); - - { - addRowLabel(F("Certificate Info")); - addHtml(F("")); - } - { - String fingerprint; - if (GetTLSfingerprint(fingerprint)) { - addFormTextBox(F("Certificate Fingerprint"), - F("fingerprint"), - fingerprint, - 64, - true); // ReadOnly - MakeControllerSettings(ControllerSettings); //-V522 - if (!AllocatedControllerSettings()) { - addHtmlError(F("Out of memory, cannot load page")); - } else { - LoadControllerSettings(controllerindex, ControllerSettings); + MakeControllerSettings(ControllerSettings); //-V522 + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, ControllerSettings); + + addFormSubHeader(F("Peer Certificate")); + + { + addRowLabel(F("Certificate Info")); + addHtml(F("")); + } + { + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { + addFormTextBox(F("Certificate Fingerprint"), + F("fingerprint"), + fingerprint, + 64, + true); // ReadOnly addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); } } + addFormSubHeader(F("Peer Certificate Chain")); + { + // FIXME TD-er: Must wrap this in divs to be able to fold it by default. + const mbedtls_x509_crt *chain; + + chain = mqtt_tls->getPeerCertificate(); + + int error {0}; + while (chain != nullptr && error == 0) { + /* + const bool mustShow = !chain->ca_istrue || chain->next == nullptr; + if (mustShow) { + */ + String pem, subject; + error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); + { + String label; + if (chain->ca_istrue) { + label = F("CA "); + } + label += F("Certificate "); + label += subject; + label += F(""); + addRowLabel(label); + } + if (error == 0) { + addHtml(F("")); + + addHtml(F("")); + } else { + addHtmlInt(error); + } + if (chain->ca_istrue && chain->next == nullptr) { + // Add checkbox to store CA cert + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT); + } +// } + chain = chain->next; + } + } } } #endif - } #endif } From f3b617c897d7056e59e73f0c46cf7ab02be64432 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 25 Sep 2021 21:22:10 +0200 Subject: [PATCH 019/367] [MQTT TLS] Add TLS support for MQTT --- docs/source/Controller/C016.rst | 2 +- docs/source/Controller/_Controller.rst | 63 +++++++++++++ platformio_esp82xx_envs.ini | 1 + .../DataStructs/ControllerSettingsStruct.cpp | 40 ++++++++ .../DataStructs/ControllerSettingsStruct.h | 6 ++ src/src/DataStructs/ProtocolStruct.cpp | 11 ++- src/src/DataStructs/ProtocolStruct.h | 37 ++++---- src/src/DataTypes/TLS_types.cpp | 12 +++ src/src/DataTypes/TLS_types.h | 20 ++++ src/src/ESPEasyCore/Controller.cpp | 93 ++++++++++++++++++- src/src/Globals/MQTT.cpp | 17 +++- src/src/Globals/MQTT.h | 22 ++++- src/src/Helpers/_CPlugin_Helper_webform.cpp | 35 ++++++- src/src/WebServer/ControllerPage.cpp | 8 +- tools/pio/pre_custom_esp32.py | 1 + tools/pio/pre_custom_esp82xx.py | 1 + 16 files changed, 340 insertions(+), 29 deletions(-) create mode 100644 src/src/DataTypes/TLS_types.cpp create mode 100644 src/src/DataTypes/TLS_types.h diff --git a/docs/source/Controller/C016.rst b/docs/source/Controller/C016.rst index 53ee64861d..40eb6484cb 100644 --- a/docs/source/Controller/C016.rst +++ b/docs/source/Controller/C016.rst @@ -45,7 +45,7 @@ Each time a plugin sends data to this controller, a sample set is stored. A typical sample set contains: -- Timestamp (Default: Unix Time, but can be switched to "local time" in the controller settings) +- Timestamp (Default: Unix Time, but can be switched to "local time" in the controller settings with the "Use Local System Time" checkbox) - task index delivering the data - 4 float values diff --git a/docs/source/Controller/_Controller.rst b/docs/source/Controller/_Controller.rst index e990276c9d..ed2ee60359 100644 --- a/docs/source/Controller/_Controller.rst +++ b/docs/source/Controller/_Controller.rst @@ -74,6 +74,69 @@ before WiFi connection is made or during lost connection. For almost all controllers, sending data is a blocking call, so it may halt execution of other code on the node. With timouts longer than 2 seconds, the ESP may reboot as the software watchdog may step in. +TLS configuration +----------------- + +Added: 2021-09-26 + +Some protocols like MQTT may use TLS to provide a secure connection to the broker. + + +Still under development. +Notes: + +BearSSL::WiFiClientSecure net; + +Retrieve CA root certificate: +net.setCACert(local_root_ca); +BearSSL::X509List cert(digicert); +net.setTrustAnchors(&cert); + + +Retrieve public key of a specific certificate: ``openssl x509 -pubkey -noout -in ca.crt`` +BearSSL::PublicKey key(pubkey); +net.setKnownKey(&key); + + +Use certificate fingerprint (HEX checksum of certificate): +openssl x509 -fingerrint -in ca.crt + +net.setFingerprint(fp); + +Self Signed certificate Mosquitto: http://www.steves-internet-guide.com/mosquitto-tls/ +Let's encrypt Mosquitto: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-the-mosquitto-mqtt-messaging-broker-on-ubuntu-18-04-quickstart + +See: https://www.youtube.com/watch?v=ytQUbyab4es + +https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt + +-----BEGIN CERTIFICATE----- +MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ +MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT +DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow +SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT +GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC +AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF +q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 +SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 +Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA +a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj +/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T +AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG +CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv +bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k +c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw +VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC +ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz +MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu +Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF +AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo +uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ +wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu +X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG +PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 +KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== +-----END CERTIFICATE----- Sample ThingSpeak configuration diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index 33f3b11b1f..6b40e8e71b 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -36,6 +36,7 @@ build_flags = ${regular_platform.build_flags} -DPLUGIN_BUILD_CUSTOM lib_ignore = ${esp8266_custom_common.lib_ignore} extra_scripts = ${esp8266_custom_common.extra_scripts} +board_build.f_cpu = 160000000L [env:custom_IR_ESP8266_4M1M] extends = esp8266_4M1M diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index f06a37379f..871daa4f28 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -3,6 +3,7 @@ #include "../../ESPEasy_common.h" #include "../CustomBuild/ESPEasyLimits.h" +#include "../ESPEasyCore/ESPEasy_Log.h" #include "../ESPEasyCore/ESPEasyNetwork.h" #include "../Helpers/Misc.h" #include "../Helpers/Networking.h" @@ -14,6 +15,7 @@ #include #include + ControllerSettingsStruct::ControllerSettingsStruct() { reset(); @@ -75,6 +77,29 @@ void ControllerSettingsStruct::validate() { ZERO_TERMINATE(MQTTLwtTopic); ZERO_TERMINATE(LWTMessageConnect); ZERO_TERMINATE(LWTMessageDisconnect); + + #ifdef USES_MQTT + #ifdef USE_MQTT_TLS + if (TLStype() == TLS_types::NoTLS) { + if (Port == 8883) { + Port = 1883; + addLog(LOG_LEVEL_ERROR, F("Not using TLS, but port set to secure 8883. Use port 1883 instead")); + } + } else { + if (Port == 1883) { + Port = 8883; + addLog(LOG_LEVEL_ERROR, F("Using TLS, but port set to insecure port 1883. Use port 8883 instead")); + } + } + #else + if (Port == 8883) { + // No TLS support, so when switching builds, make sure it can still work. + Port = 1883; + addLog(LOG_LEVEL_ERROR, F("Not using TLS, but port set to secure 8883. Use port 1883 instead")); + } + #endif + #endif + } IPAddress ControllerSettingsStruct::getIP() const { @@ -286,3 +311,18 @@ void ControllerSettingsStruct::useLocalSystemTime(bool value) { bitWrite(VariousFlags, 11, value); } + +TLS_types ControllerSettingsStruct::TLStype() const +{ + // Store it in bits 12, 13, 14 + const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0x7); + return tls_type; +} + +void ControllerSettingsStruct::TLStype(TLS_types tls_type) +{ + const uint32_t mask = ~(0x7); + VariousFlags &= mask; // Clear the bits + const uint32_t tls_type_val = static_cast(tls_type) << 12; + VariousFlags |= tls_type_val; +} diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index e37fec1889..3fcc8d1b49 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -9,6 +9,7 @@ #include // for std::nothrow #include "../../ESPEasy_common.h" +#include "../DataTypes/TLS_types.h" #include "../Globals/Plugins.h" class IPAddress; @@ -63,6 +64,7 @@ struct ControllerSettingsStruct CONTROLLER_HOSTNAME, CONTROLLER_IP, CONTROLLER_PORT, + CONTROLLER_MQTT_TLS_TYPE, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, @@ -145,6 +147,10 @@ struct ControllerSettingsStruct bool useLocalSystemTime() const; void useLocalSystemTime(bool value); + + + TLS_types TLStype() const; + void TLStype(TLS_types tls_type); boolean UseDNS; diff --git a/src/src/DataStructs/ProtocolStruct.cpp b/src/src/DataStructs/ProtocolStruct.cpp index d0ed32f245..7cd351583e 100644 --- a/src/src/DataStructs/ProtocolStruct.cpp +++ b/src/src/DataStructs/ProtocolStruct.cpp @@ -1,10 +1,11 @@ #include "../DataStructs/ProtocolStruct.h" ProtocolStruct::ProtocolStruct() : - defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), - usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), - usesQueue(true), usesCheckReply(true), usesTimeout(true), usesSampleSets(false), - usesExtCreds(false), needsNetwork(true), allowsExpire(true), allowLocalSystemTime(false) {} + defaultPort(0), Number(0), usesMQTT(false), usesAccount(false), usesPassword(false), + usesTemplate(false), usesID(false), Custom(false), usesHost(true), usesPort(true), + usesQueue(true), usesCheckReply(true), usesTimeout(true), usesSampleSets(false), + usesExtCreds(false), needsNetwork(true), allowsExpire(true), allowLocalSystemTime(false), + usesTLS(false) {} bool ProtocolStruct::useCredentials() const { return usesAccount || usesPassword; @@ -15,4 +16,4 @@ bool ProtocolStruct::useExtendedCredentials() const { return useCredentials(); } return false; -} \ No newline at end of file +} diff --git a/src/src/DataStructs/ProtocolStruct.h b/src/src/DataStructs/ProtocolStruct.h index e580f0b6b5..e8f7737471 100644 --- a/src/src/DataStructs/ProtocolStruct.h +++ b/src/src/DataStructs/ProtocolStruct.h @@ -16,23 +16,26 @@ struct ProtocolStruct bool useExtendedCredentials() const; uint16_t defaultPort; - uint8_t Number; - bool usesMQTT : 1; - bool usesAccount : 1; - bool usesPassword : 1; - bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics - bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data - bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page - bool usesHost : 1; - bool usesPort : 1; - bool usesQueue : 1; - bool usesCheckReply : 1; - bool usesTimeout : 1; - bool usesSampleSets : 1; - bool usesExtCreds : 1; - bool needsNetwork : 1; - bool allowsExpire : 1; - bool allowLocalSystemTime : 1; + uint8_t Number; + bool usesMQTT : 1; // Indicating whether it is a MQTT controller + bool usesAccount : 1; // Offer to enter credentials + bool usesPassword : 1; + bool usesTemplate : 1; // When set, the protocol will pre-load some templates like default MQTT topics + bool usesID : 1; // Whether a controller supports sending an IDX value sent along with plugin data + bool Custom : 1; // When set, the controller has to define all parameters on the controller setup page + bool usesHost : 1; // Offer either DNS hostname or IP + bool usesPort : 1; // Offer to set a port nr. This can be network port, but depending on controller this may be a + // different type of port. See LoRaWAN for example. + bool usesQueue : 1; // Allow to queue messages + bool usesCheckReply : 1; // Allow optional wait for reply + bool usesTimeout : 1; // Offer to set a timeout. + bool usesSampleSets : 1; // A sample set is an extra counter which is incremented as soon as a new value of set task is seen. + // (to keep track of bursts of messages where some may be lost) + bool usesExtCreds : 1; // Offer to store longer credentials + bool needsNetwork : 1; // Whether it needs a network connection to work + bool allowsExpire : 1; // Whether queued messages may be removed from the queue after some time + bool allowLocalSystemTime : 1; // Allow switching between Unix time and local time (including timezone and DST) + bool usesTLS : 1; // May offer TLS related settings and options }; typedef std::vector ProtocolVector; diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp new file mode 100644 index 0000000000..5eaf38b4b1 --- /dev/null +++ b/src/src/DataTypes/TLS_types.cpp @@ -0,0 +1,12 @@ +#include "../DataTypes/TLS_types.h" + +const __FlashStringHelper* toString(TLS_types tls_type) +{ + switch (tls_type) { + case TLS_types::NoTLS: break; + case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); + case TLS_types::TLS_CA_CERT: return F("TLS CA Cert"); + case TLS_types::TLS_insecure: return F("TLS No Checks (insecure)"); + } + return F("No TLS"); +} diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h new file mode 100644 index 0000000000..8cf0e257d5 --- /dev/null +++ b/src/src/DataTypes/TLS_types.h @@ -0,0 +1,20 @@ +#ifndef DATATYPES_TLS_TYPES_H +#define DATATYPES_TLS_TYPES_H + + +#include +#include + +// Value is stored, so do not change assigned integer values. +enum class TLS_types { + NoTLS = 0, // Do not use encryption + TLS_PSK = 1, // Pre-Shared-Key + TLS_CA_CERT = 2, // Validate server certificate against known CA +//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_insecure = 7 // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. +}; + +const __FlashStringHelper* toString(TLS_types tls_type); + + +#endif // ifndef DATATYPES_TLS_TYPES_H diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 6791e2898b..59b51f94ec 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -190,13 +190,73 @@ bool MQTTConnect(controllerIndex_t controller_idx) // mqtt = WiFiClient(); // workaround see: https://github.com/esp8266/Arduino/issues/4497#issuecomment-373023864 delay(0); + + uint16_t mqttPort = ControllerSettings.Port; + +#ifdef USE_MQTT_TLS + mqtt_tls_last_errorstr = EMPTY_STRING; + mqtt_tls_last_error = 0; + const TLS_types TLS_type = ControllerSettings.TLStype(); + switch(TLS_type) { + case TLS_types::NoTLS: + { + mqtt.setTimeout(ControllerSettings.ClientTimeout); + MQTTclient.setClient(mqtt); + break; + } + case TLS_types::TLS_PSK: + { + //mqtt_tls.setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + break; + } + case TLS_types::TLS_CA_CERT: + { + #ifdef ESP32 + mqtt_tls.setCACert(mqtt_rootCA); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA); + mqtt_tls.setTrustAnchors(&mqtt_X509List); + #endif + break; + } + /* + case TLS_types::TLS_CA_CLI_CERT: + { + //mqtt_tls.setCertificate(const char *client_ca); + break; + } + */ + case TLS_types::TLS_insecure: + { + mqtt_tls.setInsecure(); + break; + } + } + if (TLS_type != TLS_types::NoTLS) { + mqtt_tls.setTimeout(ControllerSettings.ClientTimeout); + #ifdef ESP8266 + mqtt_tls.setBufferSizes(1024,1024); + #endif + MQTTclient.setClient(mqtt_tls); + if (mqttPort == 1883) { + mqttPort = 8883; + } + } else { + if (mqttPort == 8883) { + mqttPort = 1883; + } + } + +#else mqtt.setTimeout(ControllerSettings.ClientTimeout); MQTTclient.setClient(mqtt); +#endif if (ControllerSettings.UseDNS) { - MQTTclient.setServer(ControllerSettings.getHost().c_str(), ControllerSettings.Port); + MQTTclient.setServer(ControllerSettings.getHost().c_str(), mqttPort); } else { - MQTTclient.setServer(ControllerSettings.getIP(), ControllerSettings.Port); + MQTTclient.setServer(ControllerSettings.getIP(), mqttPort); } MQTTclient.setCallback(incoming_mqtt_callback); @@ -210,6 +270,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) bool willRetain = ControllerSettings.mqtt_willRetain() && ControllerSettings.mqtt_sendLWT(); bool cleanSession = ControllerSettings.mqtt_cleanSession(); // As suggested here: + mqtt_last_connect_attempt.setNow(); + // https://github.com/knolleary/pubsubclient/issues/458#issuecomment-493875150 if (hasControllerCredentialsSet(controller_idx, ControllerSettings)) { @@ -238,8 +300,31 @@ bool MQTTConnect(controllerIndex_t controller_idx) uint8_t controller_number = Settings.Protocol[controller_idx]; count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number); + #ifdef USE_MQTT_TLS + { + char buf[128] = {0}; + #ifdef ESP8266 + mqtt_tls_last_error = mqtt_tls.getLastSSLError(buf,128); + #endif + #ifdef ESP32 + mqtt_tls_last_error = mqtt_tls.lastError(buf,128); + #endif + mqtt_tls_last_errorstr = buf; + } + #endif + if (!MQTTresult) { + #ifdef USE_MQTT_TLS + if ((mqtt_tls_last_error != 0) && loglevelActiveFor(LOG_LEVEL_ERROR)) { + String log = F("MQTT : TLS error code: "); + log += mqtt_tls_last_error; + log += ' '; + log += mqtt_tls_last_errorstr; + addLog(LOG_LEVEL_ERROR, log); + } + #endif + MQTTclient.disconnect(); updateMQTTclient_connected(); return false; @@ -352,6 +437,10 @@ bool MQTTCheck(controllerIndex_t controller_idx) if (MQTTclient_should_reconnect || !MQTTclient.connected()) { + if (mqtt_last_connect_attempt.isSet() && mqtt_last_connect_attempt.millisPassedSince() < 5000) { + return false; + } + if (MQTTclient_should_reconnect) { addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect")); } diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 80f80bd690..10efc391c9 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -6,12 +6,27 @@ // MQTT client -WiFiClient mqtt; +WiFiClient mqtt; +# ifdef USE_MQTT_TLS +String mqtt_tls_last_errorstr; +int32_t mqtt_tls_last_error = 0; + +# ifdef ESP32 +WiFiClientSecure mqtt_tls; +# endif // ifdef ESP32 +# ifdef ESP8266 +BearSSL::WiFiClientSecure mqtt_tls; +BearSSL::X509List mqtt_X509List; +# endif // ifdef ESP8266 +const char *mqtt_rootCA = nullptr; +# endif // ifdef USE_MQTT_TLS + PubSubClient MQTTclient(mqtt); bool MQTTclient_should_reconnect = true; bool MQTTclient_must_send_LWT_connected = false; bool MQTTclient_connected = false; int mqtt_reconnect_count = 0; +LongTermTimer mqtt_last_connect_attempt; #endif // USES_MQTT #ifdef USES_P037 diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 69ef6b3104..af673b4e6f 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -5,17 +5,37 @@ #ifdef USES_MQTT +# include "../Helpers/LongTermTimer.h" # include # include +# ifdef USE_MQTT_TLS +# include +# endif // ifdef USE_MQTT_TLS + // MQTT client -extern WiFiClient mqtt; +extern WiFiClient mqtt; +# ifdef USE_MQTT_TLS +extern String mqtt_tls_last_errorstr; +extern int32_t mqtt_tls_last_error; +# ifdef ESP32 +extern WiFiClientSecure mqtt_tls; +# endif // ifdef ESP32 +# ifdef ESP8266 +extern BearSSL::WiFiClientSecure mqtt_tls; +extern BearSSL::X509List mqtt_X509List; + +# endif // ifdef ESP8266 + +extern const char *mqtt_rootCA; +# endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; extern bool MQTTclient_must_send_LWT_connected; extern bool MQTTclient_connected; extern int mqtt_reconnect_count; +extern LongTermTimer mqtt_last_connect_attempt; #endif // USES_MQTT #ifdef USES_P037 diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 7365dc6a27..09b0c0d5e4 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -20,7 +20,8 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_USE_DNS: return F("Locate Controller"); case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); - case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); + case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -153,6 +154,28 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin addFormNumericBox(displayName, internalName, ControllerSettings.Port, 1, 65535); break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: + { + #ifdef USE_MQTT_TLS + const int choice = static_cast(ControllerSettings.TLStype()); + #define NR_MQTT_TLS_TYPES 3 + const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { + toString(TLS_types::NoTLS), +// toString(TLS_types::TLS_PSK), +// toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_insecure) + }; + const int indices[NR_MQTT_TLS_TYPES] = { + static_cast(TLS_types::NoTLS), +// static_cast(TLS_types::TLS_PSK), +// static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_insecure) + }; + addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); + #undef NR_MQTT_TLS_TYPES + #endif + break; + } case ControllerSettingsStruct::CONTROLLER_USER: { const size_t fieldMaxLength = @@ -309,6 +332,16 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet case ControllerSettingsStruct::CONTROLLER_PORT: ControllerSettings.Port = getFormItemInt(internalName, ControllerSettings.Port); break; + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: + { + #ifdef USE_MQTT_TLS + const int current = static_cast(ControllerSettings.TLStype()); + const TLS_types tls_type = static_cast(getFormItemInt(internalName, current)); + ControllerSettings.TLStype(tls_type); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); break; diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 502182c938..788dc385f5 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -135,6 +135,7 @@ void handle_controllers_clearLoadDefaults(uint8_t controllerindex, ControllerSet ControllerSettings.reset(); ControllerSettings.Port = Protocol[ProtocolIndex].defaultPort; + ControllerSettings.TLStype(TLS_types::NoTLS); // Load some templates from the controller. struct EventStruct TempEvent; @@ -298,7 +299,6 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtmlError(F("Out of memory, cannot load page")); } else { LoadControllerSettings(controllerindex, ControllerSettings); - if (!Protocol[ProtocolIndex].Custom) { if (Protocol[ProtocolIndex].usesHost) { @@ -316,6 +316,12 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (Protocol[ProtocolIndex].usesPort) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); } + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT && Protocol[ProtocolIndex].usesTLS) { + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE); + addFormNote(F("Default ports: MQTT: 1883 / MQTT TLS: 8883")); + } + #endif if (Protocol[ProtocolIndex].usesQueue) { addTableSeparator(F("Controller Queue"), 2, 3); diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index a97afcb4a4..d75e5b2bc5 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -61,6 +61,7 @@ "-DFEATURE_SD", "-DFEATURE_I2CMULTIPLEXER", "-DUSE_TRIGONOMETRIC_FUNCTIONS_RULES", + "-DUSE_MQTT_TLS", "-DUSE_SETTINGS_ARCHIVE" ] diff --git a/tools/pio/pre_custom_esp82xx.py b/tools/pio/pre_custom_esp82xx.py index bc3ee3d5ce..00a5d15366 100644 --- a/tools/pio/pre_custom_esp82xx.py +++ b/tools/pio/pre_custom_esp82xx.py @@ -59,6 +59,7 @@ # "-DFEATURE_MDNS", # "-DFEATURE_SD", "-DUSE_EXT_RTC", + "-DUSE_MQTT_TLS", "-DFEATURE_I2CMULTIPLEXER", "-DUSE_TRIGONOMETRIC_FUNCTIONS_RULES", From 30465332a324df290b3c985cbe18c851b684dfc6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 19 Oct 2021 16:02:40 +0200 Subject: [PATCH 020/367] [MQTT TLS] Add cert.py to extract certificate info --- platformio_core_defs.ini | 2 +- tools/pio/cert.py | 127 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 tools/pio/cert.py diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 1a51b13f78..8a9a7c63e1 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -165,7 +165,7 @@ build_flags = -DESP32_STAGE [core_esp32_3_3_0] platform = espressif32 @ 3.3.0 platform_packages = framework-arduinoespressif32 -build_flags = -DESP32_STAGE +build_flags = [core_esp32_3_3_2] platform = espressif32 @ 3.3.2 diff --git a/tools/pio/cert.py b/tools/pio/cert.py new file mode 100644 index 0000000000..3ade0f5ccc --- /dev/null +++ b/tools/pio/cert.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 + +# Script to download/update certificates and public keys +# and generate compilable source files for c++/Arduino. +# released to public domain + +# Original: https://github.com/esp8266/Arduino/blob/master/tools/cert.py + +import urllib.request +import re +import ssl +import sys +import socket +import argparse +import datetime + +from cryptography import x509 +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.serialization import pkcs7 +from cryptography.hazmat.primitives.serialization import Encoding +from cryptography.hazmat.primitives.serialization import PublicFormat + +def printData(data, showPub = True): + try: + xcert = x509.load_der_x509_certificate(data) + except: + try: + xcert = x509.load_pem_x509_certificate(data) + except: + try: + xcert = pkcs7.load_der_pkcs7_certificates(data) + except: + xcert = pkcs7.load_pem_pkcs7_certificates(data) + if len(xcert) > 1: + print('// Warning: TODO: pkcs7 has {} entries'.format(len(xcert))) + xcert = xcert[0] + + cn = '' + for dn in xcert.subject.rfc4514_string().split(','): + keyval = dn.split('=') + if keyval[0] == 'CN': + cn += keyval[1] + name = re.sub('[^a-zA-Z0-9_]', '_', cn) + print('// CN: {} => name: {}'.format(cn, name)) + + print('// not valid before:', xcert.not_valid_before) + print('// not valid after: ', xcert.not_valid_after) + + if showPub: + + fingerprint = xcert.fingerprint(hashes.SHA1()).hex(':') + print('const char fingerprint_{} [] PROGMEM = "{}";'.format(name, fingerprint)) + + pem = xcert.public_key().public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo).decode('utf-8') + print('const char pubkey_{} [] PROGMEM = R"PUBKEY('.format(name)) + print(pem + ')PUBKEY";') + + else: + + cert = xcert.public_bytes(Encoding.PEM).decode('utf-8') + print('const char cert_{} [] PROGMEM = R"CERT('.format(name)) + print(cert + ')CERT";') + + cas = [] + for ext in xcert.extensions: + if ext.oid == x509.ObjectIdentifier("1.3.6.1.5.5.7.1.1"): + for desc in ext.value: + if desc.access_method == x509.oid.AuthorityInformationAccessOID.CA_ISSUERS: + cas.append(desc.access_location.value) + for ca in cas: + with urllib.request.urlopen(ca) as crt: + print() + print('// ' + ca) + printData(crt.read(), False) + print() + +def get_certificate(hostname, port, name): + context = ssl.create_default_context() + context.check_hostname = False + context.verify_mode = ssl.CERT_NONE + with socket.create_connection((hostname, port)) as sock: + with context.wrap_socket(sock, server_hostname=hostname) as ssock: + print('////////////////////////////////////////////////////////////') + print('// certificate chain for {}:{}'.format(hostname, port)) + print() + if name: + print('const char* {}_host = "{}";'.format(name, hostname)); + print('const uint16_t {}_port = {};'.format(name, port)); + print() + printData(ssock.getpeercert(binary_form=True)) + print('// end of certificate chain for {}:{}'.format(hostname, port)) + print('////////////////////////////////////////////////////////////') + print() + return 0 + +def main(): + parser = argparse.ArgumentParser(description='download certificate chain and public keys under a C++/Arduino compilable form') + parser.add_argument('-s', '--server', action='store', required=True, help='TLS server dns name') + parser.add_argument('-p', '--port', action='store', required=False, help='TLS server port') + parser.add_argument('-n', '--name', action='store', required=False, help='variable name') + port = 443 + args = parser.parse_args() + server = args.server + port = 443 + try: + split = server.split(':') + server = split[0] + port = int(split[1]) + except: + pass + try: + port = int(args.port) + except: + pass + + print() + print('// this file is autogenerated - any modification will be overwritten') + print('// unused symbols will not be linked in the final binary') + print('// generated on {}'.format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"))) + print('// by {}'.format(sys.argv)) + print() + print('#pragma once') + print() + return get_certificate(server, port, args.name) + +if __name__ == '__main__': + sys.exit(main()) From 4ffa604c47f542e57cde45aed902f8308d299713 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:32:14 +0100 Subject: [PATCH 021/367] [MQTT TLS] Allow to load CA root cert from file --- src/src/DataStructs/Caches.h | 8 +-- .../DataStructs/ControllerSettingsStruct.cpp | 33 +++++++++- .../DataStructs/ControllerSettingsStruct.h | 2 + src/src/DataTypes/TLS_types.h | 10 +-- src/src/ESPEasyCore/Controller.cpp | 29 ++++++--- src/src/Globals/MQTT.cpp | 2 +- src/src/Globals/MQTT.h | 2 +- src/src/Helpers/ESPEasy_Storage.cpp | 62 +++++++++++++++++-- src/src/Helpers/ESPEasy_Storage.h | 4 ++ src/src/Helpers/_CPlugin_Helper_webform.cpp | 18 +++++- src/src/WebServer/WebServer.cpp | 1 - 11 files changed, 143 insertions(+), 28 deletions(-) diff --git a/src/src/DataStructs/Caches.h b/src/src/DataStructs/Caches.h index 4960c0436d..f1ebb331b1 100644 --- a/src/src/DataStructs/Caches.h +++ b/src/src/DataStructs/Caches.h @@ -5,9 +5,9 @@ #include "../../ESPEasy_common.h" #include "../Globals/Plugins.h" -typedef std::mapTaskIndexNameMap; -typedef std::map TaskIndexValueNameMap; -typedef std::map FilePresenceMap; +typedef std::map TaskIndexNameMap; +typedef std::map TaskIndexValueNameMap; +typedef std::map FilePresenceMap; struct Caches { void clearAllCaches(); @@ -18,7 +18,7 @@ struct Caches { TaskIndexNameMap taskIndexName; TaskIndexValueNameMap taskIndexValueName; - FilePresenceMap fileExistsMap; + FilePresenceMap fileExistsMap; // Filesize. -1 if not present bool activeTaskUseSerial0 = false; }; diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 871daa4f28..791013cece 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -314,15 +314,42 @@ void ControllerSettingsStruct::useLocalSystemTime(bool value) TLS_types ControllerSettingsStruct::TLStype() const { - // Store it in bits 12, 13, 14 - const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0x7); + // Store it in bits 12, 13, 14, 15 + const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0xF); return tls_type; } void ControllerSettingsStruct::TLStype(TLS_types tls_type) { - const uint32_t mask = ~(0x7); + const uint32_t mask = ~(0xF); VariousFlags &= mask; // Clear the bits const uint32_t tls_type_val = static_cast(tls_type) << 12; VariousFlags |= tls_type_val; } + +String ControllerSettingsStruct::getCertificateFilename() const +{ + String certFile = HostName; + if (certFile.isEmpty()) { + certFile = F(""); + } + + switch (TLStype()) { + case TLS_types::NoTLS: + case TLS_types::TLS_insecure: + return EMPTY_STRING; + case TLS_types::TLS_PSK: + certFile += F(".psk"); + break; + /* + case TLS_types::TLS_CA_CLI_CERT: + certFile += F(".caclicert"); + break; + */ + case TLS_types::TLS_CA_CERT: + certFile += F(".cacert"); + break; + } + + return certFile; +} \ No newline at end of file diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 3fcc8d1b49..1c021015b7 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -151,6 +151,8 @@ struct ControllerSettingsStruct TLS_types TLStype() const; void TLStype(TLS_types tls_type); + + String getCertificateFilename() const; boolean UseDNS; diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index 8cf0e257d5..a8aec9aa14 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -7,11 +7,11 @@ // Value is stored, so do not change assigned integer values. enum class TLS_types { - NoTLS = 0, // Do not use encryption - TLS_PSK = 1, // Pre-Shared-Key - TLS_CA_CERT = 2, // Validate server certificate against known CA -//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication - TLS_insecure = 7 // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. + NoTLS = 0, // Do not use encryption + TLS_PSK = 1, // Pre-Shared-Key + TLS_CA_CERT = 2, // Validate server certificate against known CA +//TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_insecure = 0xF // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. }; const __FlashStringHelper* toString(TLS_types tls_type); diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 59b51f94ec..5449783e7c 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -211,13 +211,28 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_CA_CERT: { - #ifdef ESP32 - mqtt_tls.setCACert(mqtt_rootCA); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA); - mqtt_tls.setTrustAnchors(&mqtt_X509List); - #endif + const String certFile = ControllerSettings.getCertificateFilename(); + const size_t size = fileSize(certFile); + if (size > 0) { + if (mqtt_rootCA != nullptr) { + free(mqtt_rootCA); + } + mqtt_rootCA = (char*)malloc(size + 1); + if (mqtt_rootCA != nullptr) { + LoadFromFile(certFile.c_str(), 0, (uint8_t*)mqtt_rootCA, size); + mqtt_rootCA[size] = '\0'; + } + } + + if (mqtt_rootCA != nullptr) { + #ifdef ESP32 + mqtt_tls.setCACert(mqtt_rootCA); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA); + mqtt_tls.setTrustAnchors(&mqtt_X509List); + #endif + } break; } /* diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 10efc391c9..3c0c169754 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -18,7 +18,7 @@ WiFiClientSecure mqtt_tls; BearSSL::WiFiClientSecure mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -const char *mqtt_rootCA = nullptr; +char *mqtt_rootCA = nullptr; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index af673b4e6f..00b7ebd266 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -28,7 +28,7 @@ extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -extern const char *mqtt_rootCA; +extern char *mqtt_rootCA; # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 3201a6b441..4293e7c2c6 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -125,11 +125,19 @@ bool fileExists(const String& fname) { const String patched_fname = patch_fname(fname); auto search = Cache.fileExistsMap.find(patched_fname); if (search != Cache.fileExistsMap.end()) { - return search->second; + return search->second >= 0; + } + int size = -1; + if (ESPEASY_FS.exists(patched_fname)) { + size = 0; + fs::File f = ESPEASY_FS.open(patched_fname, "r"); + if (f) { + size = f.size(); + f.close(); + } } - bool res = ESPEASY_FS.exists(patched_fname); - Cache.fileExistsMap[patched_fname] = res; - return res; + Cache.fileExistsMap[patched_fname] = size; + return size; } fs::File tryOpenFile(const String& fname, const String& mode) { @@ -1252,6 +1260,52 @@ String LoadFromFile(const char *fname, int offset, uint8_t *memAddress, int data return String(); } +String LoadFromFile(const char *fname, String& data, int offset) +{ + fs::File f = tryOpenFile(fname, "r"); + SPIFFS_CHECK(f, fname); + #ifndef BUILD_NO_DEBUG + String log = F("LoadFromFile: "); + log += fname; + #else + String log = F("Load error"); + #endif + + if (!f || offset < 0 || (offset >= f.size())) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, invalid position in file"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } + delay(0); + START_TIMER; + #ifndef BUILD_NO_RAM_TRACKER + checkRAM(F("LoadFromFile")); + #endif + + SPIFFS_CHECK(f.seek(offset, fs::SeekSet), fname); + if (f) { + if (!data.reserve(f.size() - offset)) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Out of memory"); + #endif + addLog(LOG_LEVEL_ERROR, log); + f.close(); + return log; + } + + while (f.available()) { data += (char)f.read(); } + f.close(); + } + + + STOP_TIMER(LOADFILE_STATS); + delay(0); + + return String(); +} + /********************************************************************************************\ Wrapper functions to handle errors in accessing settings \*********************************************************************************************/ diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c9d88ceac6..c079c4350d 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -29,6 +29,8 @@ String appendToFile(const String& fname, const uint8_t *data, unsigned int size) bool fileExists(const String& fname); +int fileSize(const String& fname); + fs::File tryOpenFile(const String& fname, const String& mode); bool tryRenameFile(const String& fname_old, const String& fname_new); @@ -198,6 +200,8 @@ String ClearInFile(const char *fname, int index, int datasize); \*********************************************************************************************/ String LoadFromFile(const char *fname, int offset, uint8_t *memAddress, int datasize); +String LoadFromFile(const char *fname, String& data, int offset = 0); + /********************************************************************************************\ Wrapper functions to handle errors in accessing settings \*********************************************************************************************/ diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 09b0c0d5e4..ebc493a272 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -162,17 +162,31 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), -// toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_CA_CERT), toString(TLS_types::TLS_insecure) }; const int indices[NR_MQTT_TLS_TYPES] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), -// static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_CA_CERT), static_cast(TLS_types::TLS_insecure) }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES + const String certFile = ControllerSettings.getCertificateFilename(); + if (!certFile.isEmpty()) + { + const String certFile = ControllerSettings.getCertificateFilename(); + String note = F("Certificate or PSK must be stored on the filesystem in "); + note += certFile; + note += F(" "); + if (fileExists(certFile)) { + note += F("(File exists)"); + } else { + note += F("(Not found)"); + } + addFormNote(note); + } #endif break; } diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index 2e81800d1c..3dfe586618 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -91,7 +91,6 @@ void sendHeadandTail(const __FlashStringHelper * tmplName, boolean Tail, boolean String fileName = tmplName; fileName += F(".htm"); - fs::File f = tryOpenFile(fileName, "r"); WebTemplateParser templateParser(Tail, rebooting); if (f) { From 375391741b62d3baa44de959d0bc6ffce4979334 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:34:22 +0100 Subject: [PATCH 022/367] [Web] Allow to stream from file system (e.g. CSS inline) This may prevent additional calls to load the CSS from the file system in a separate HTTP GET call and also not loading the file into memory when streaming. --- src/src/WebServer/LoadFromFS.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/src/WebServer/LoadFromFS.cpp b/src/src/WebServer/LoadFromFS.cpp index 81dd288f2a..e10e71de59 100644 --- a/src/src/WebServer/LoadFromFS.cpp +++ b/src/src/WebServer/LoadFromFS.cpp @@ -176,6 +176,10 @@ size_t streamFromFS(String path, bool htmlEscape) { available = 0; } } + + while (f.available()) { + addHtml((char)f.read()); + } statusLED(true); f.close(); From 0567e79c92fc980ea66990a21a8e9d96aeb92f86 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:36:21 +0100 Subject: [PATCH 023/367] [MQTT TLS] Patch CA root cert to match strict layout Making it easier for users to copy/paste certificate code into a file. It will be patched at load from the file system. --- src/src/Helpers/ESPEasy_Storage.cpp | 131 +++++++++++++++++++++++++++- src/src/Helpers/ESPEasy_Storage.h | 7 ++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 4293e7c2c6..b461bd914c 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1023,6 +1023,135 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int return LoadFromFile(SettingsType::Enum::NotificationSettings_Type, NotificationIndex, memAddress, datasize); } + +/********************************************************************************************\ + Handle certificate files on the file system. + The content will be stripped from unusable character like quotes, spaces etc. + \*********************************************************************************************/ +static inline bool is_base64(char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +bool cleanupCertificate(String & certificate, bool &changed) +{ + changed = false; + // "-----BEGIN CERTIFICATE-----" positions in dash_pos[0] and dash_pos[1] + // "-----END CERTIFICATE-----" positions in dash_pos[2] and dash_pos[3] + int dash_pos[4] = { 0 }; + int last_pos = 0; + for (int i = 0; i < 4 && last_pos != -1; ++i) { + dash_pos[i] = certificate.indexOf(F("-----"), last_pos); + last_pos = dash_pos[i] + 5; + addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); + } + if (last_pos == -1) return false; + + int read_pos = dash_pos[1] + 5; // next char after "-----BEGIN CERTIFICATE-----" + String newCert; + newCert.reserve((dash_pos[3] + 6) - dash_pos[0]); + + // "-----BEGIN CERTIFICATE-----" + newCert += certificate.substring(dash_pos[0], read_pos); + + char last_char = certificate[read_pos - 1]; + for (; read_pos < dash_pos[2]; ++read_pos) { + const char c = certificate[read_pos]; + if ((c == 'n' && last_char == '\\') || (c == '\n')) { + if (!newCert.endsWith(String('\n'))) { + newCert += '\n'; + } + } else if (is_base64(c) || c == '=') { + newCert += c; + } + last_char = c; + } + + // "-----END CERTIFICATE-----" + newCert += certificate.substring(dash_pos[2], dash_pos[3] + 5); + newCert += '\n'; + + changed = !certificate.equals(newCert); + certificate = std::move(newCert); + return true; +} + + +String SaveCertificate(const String& fname, const String& certificate) +{ + return SaveToFile(fname.c_str(), 0, (const uint8_t *)certificate.c_str(), certificate.length() + 1); +} + +String LoadCertificate(const String& fname, String& certificate) +{ + bool changed = false; + if (fileExists(fname)) { + fs::File f = tryOpenFile(fname, "r"); + SPIFFS_CHECK(f, fname.c_str()); + #ifndef BUILD_NO_DEBUG + String log = F("LoadCertificate: "); + log += fname; + #else + String log = F("LoadCertificate error"); + #endif + + certificate.clear(); + + if (!certificate.reserve(f.size())) { + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Out of memory"); + #endif + addLog(LOG_LEVEL_ERROR, log); + f.close(); + return log; + } + bool done = false; + while (f.available() && !done) { + const char c = (char)f.read(); + if (c == '\0') { + done = true; + } else { + certificate += c; + } + } + f.close(); + + String analyse = F("Cleanup: Before: "); + analyse += certificate.length(); + analyse += F(" After: "); + + if (!cleanupCertificate(certificate, changed)) { + certificate.clear(); + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Invalid certificate format"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } else if (changed) { + //return SaveCertificate(fname, certificate); + } +// addLog(LOG_LEVEL_INFO, F("After")); +// addLog(LOG_LEVEL_INFO, certificate); + static int previousFree = FreeMem(); + const int freemem = FreeMem(); + + analyse += certificate.length(); + analyse += changed ? F(" changed") : F(" same"); + analyse += F(" free memory: "); + analyse += freemem; + analyse += F(" largest free block: "); + analyse += getMaxFreeBlock(); + + analyse += F(" Difference: "); + analyse += previousFree - freemem; + + addLog(LOG_LEVEL_INFO, analyse); + previousFree = freemem; + } + + return EMPTY_STRING; +} + + /********************************************************************************************\ Init a file with zeros on file system \*********************************************************************************************/ @@ -1271,7 +1400,7 @@ String LoadFromFile(const char *fname, String& data, int offset) String log = F("Load error"); #endif - if (!f || offset < 0 || (offset >= f.size())) { + if (!f || offset < 0 || (offset >= static_cast(f.size()))) { #ifndef BUILD_NO_DEBUG log += F(" ERROR, invalid position in file"); #endif diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index c079c4350d..620c041bbe 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -172,6 +172,13 @@ String SaveNotificationSettings(int NotificationIndex, const uint8_t *memAddress \*********************************************************************************************/ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int datasize); +/********************************************************************************************\ + Handle certificate files on the file system. + The content will be stripped from unusable character like quotes, spaces etc. + \*********************************************************************************************/ +String SaveCertificate(const String& fname, const String& certificate); +String LoadCertificate(const String& fname, String& certificate); + /********************************************************************************************\ Init a file with zeros on file system From 5ab83c56f6c8c0d772a184975d348f4f3832c349 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 01:38:35 +0100 Subject: [PATCH 024/367] [MQTT TLS] Work-around for memory leak in MbedTLS (not finished) There is a memory leak in Mbed TLS, when connection failed. For example when using a CA root certificate which does not match the certificate of the host we're connecting to. This will take about 1880 bytes of memory on each attempt. Still a work-in-progress as it is not yet fixed. --- src/src/ESPEasyCore/Controller.cpp | 80 ++- src/src/Globals/MQTT.cpp | 6 +- src/src/Globals/MQTT.h | 10 +- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 341 ++++++++++++ src/src/Helpers/ESPEasy_WiFiClientSecure.h | 113 ++++ src/src/Helpers/ESPEasy_ssl_client.cpp | 530 +++++++++++++++++++ src/src/Helpers/ESPEasy_ssl_client.h | 53 ++ 7 files changed, 1104 insertions(+), 29 deletions(-) create mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.cpp create mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.h create mode 100644 src/src/Helpers/ESPEasy_ssl_client.cpp create mode 100644 src/src/Helpers/ESPEasy_ssl_client.h diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 5449783e7c..fafa1f4717 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -25,6 +25,7 @@ #include "../Globals/Protocol.h" #include "../Helpers/_CPlugin_Helper.h" +#include "../Helpers/Memory.h" #include "../Helpers/Misc.h" #include "../Helpers/Network.h" #include "../Helpers/PeriodicalActions.h" @@ -184,6 +185,14 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (MQTTclient.connected()) { MQTTclient.disconnect(); + # ifdef USE_MQTT_TLS + /* + if (mqtt_tls != nullptr) { + delete mqtt_tls; + mqtt_tls = nullptr; + } + */ + #endif } updateMQTTclient_connected(); @@ -197,6 +206,19 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_tls_last_errorstr = EMPTY_STRING; mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); + if (TLS_type != TLS_types::NoTLS) { + #ifdef ESP32 + mqtt_tls = new ESPEasy_WiFiClientSecure; + #endif + #ifdef ESP8266 + mqtt_tls = new BearSSL::WiFiClientSecure; + #endif + + if (mqtt_tls == nullptr) { + addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); + return false; + } + } switch(TLS_type) { case TLS_types::NoTLS: { @@ -206,31 +228,39 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_PSK: { - //mqtt_tls.setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + //mqtt_tls->setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex break; } case TLS_types::TLS_CA_CERT: { - const String certFile = ControllerSettings.getCertificateFilename(); - const size_t size = fileSize(certFile); - if (size > 0) { - if (mqtt_rootCA != nullptr) { - free(mqtt_rootCA); - } - mqtt_rootCA = (char*)malloc(size + 1); - if (mqtt_rootCA != nullptr) { - LoadFromFile(certFile.c_str(), 0, (uint8_t*)mqtt_rootCA, size); - mqtt_rootCA[size] = '\0'; - } +// mqtt_rootCA.clear(); + if (mqtt_rootCA.isEmpty()) + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + + { + static int previousFree = FreeMem(); + const int freemem = FreeMem(); + + String analyse = F(" free memory: "); + analyse += freemem; + analyse += F(" largest free block: "); + analyse += getMaxFreeBlock(); + + analyse += F(" Difference: "); + analyse += previousFree - freemem; + + addLog(LOG_LEVEL_INFO, analyse); + previousFree = freemem; } - if (mqtt_rootCA != nullptr) { + + if (mqtt_rootCA.length() > 0) { #ifdef ESP32 - mqtt_tls.setCACert(mqtt_rootCA); + mqtt_tls->setCACert(mqtt_rootCA.c_str()); #endif #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA); - mqtt_tls.setTrustAnchors(&mqtt_X509List); + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); #endif } break; @@ -238,22 +268,22 @@ bool MQTTConnect(controllerIndex_t controller_idx) /* case TLS_types::TLS_CA_CLI_CERT: { - //mqtt_tls.setCertificate(const char *client_ca); + //mqtt_tls->setCertificate(const char *client_ca); break; } */ case TLS_types::TLS_insecure: { - mqtt_tls.setInsecure(); + mqtt_tls->setInsecure(); break; } } if (TLS_type != TLS_types::NoTLS) { - mqtt_tls.setTimeout(ControllerSettings.ClientTimeout); + mqtt_tls->setTimeout(ControllerSettings.ClientTimeout); #ifdef ESP8266 - mqtt_tls.setBufferSizes(1024,1024); + mqtt_tls->setBufferSizes(1024,1024); #endif - MQTTclient.setClient(mqtt_tls); + MQTTclient.setClient(*mqtt_tls); if (mqttPort == 1883) { mqttPort = 8883; } @@ -319,10 +349,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) { char buf[128] = {0}; #ifdef ESP8266 - mqtt_tls_last_error = mqtt_tls.getLastSSLError(buf,128); + mqtt_tls_last_error = mqtt_tls->getLastSSLError(buf,128); #endif #ifdef ESP32 - mqtt_tls_last_error = mqtt_tls.lastError(buf,128); + mqtt_tls_last_error = mqtt_tls->lastError(buf,128); #endif mqtt_tls_last_errorstr = buf; } @@ -341,6 +371,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif MQTTclient.disconnect(); + #ifdef USE_MQTT_TLS + mqtt_tls->stop(); + #endif + updateMQTTclient_connected(); return false; } diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 3c0c169754..3434c4b42b 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -12,13 +12,13 @@ String mqtt_tls_last_errorstr; int32_t mqtt_tls_last_error = 0; # ifdef ESP32 -WiFiClientSecure mqtt_tls; +ESPEasy_WiFiClientSecure* mqtt_tls; # endif // ifdef ESP32 # ifdef ESP8266 -BearSSL::WiFiClientSecure mqtt_tls; +BearSSL::WiFiClientSecure* mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -char *mqtt_rootCA = nullptr; +String mqtt_rootCA; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 00b7ebd266..3cf7ff14b3 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -11,7 +11,11 @@ # include # ifdef USE_MQTT_TLS +# ifdef ESP32 +# include "../Helpers/ESPEasy_WiFiClientSecure.h" +# else # include +# endif # endif // ifdef USE_MQTT_TLS // MQTT client @@ -20,15 +24,15 @@ extern WiFiClient mqtt; extern String mqtt_tls_last_errorstr; extern int32_t mqtt_tls_last_error; # ifdef ESP32 -extern WiFiClientSecure mqtt_tls; +extern ESPEasy_WiFiClientSecure* mqtt_tls; # endif // ifdef ESP32 # ifdef ESP8266 -extern BearSSL::WiFiClientSecure mqtt_tls; +extern BearSSL::WiFiClientSecure* mqtt_tls; extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 -extern char *mqtt_rootCA; +extern String mqtt_rootCA; # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp new file mode 100644 index 0000000000..f84c5aeb40 --- /dev/null +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -0,0 +1,341 @@ +#include "../Helpers/ESPEasy_WiFiClientSecure.h" + +/* + ESPEasy_WiFiClientSecure.cpp - Client Secure class for ESP32 + Copyright (c) 2016 Hristo Gochkov All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library 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 + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include +#include +#include + +#undef connect +#undef write +#undef read + + +ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure() +{ + _connected = false; + + sslclient = new ESPEasy_sslclient_context; + ssl_init(sslclient); + sslclient->socket = -1; + sslclient->handshake_timeout = 120000; + _use_insecure = false; + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + + +ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock) +{ + _connected = false; + _timeout = 0; + + sslclient = new ESPEasy_sslclient_context; + ssl_init(sslclient); + sslclient->socket = sock; + sslclient->handshake_timeout = 120000; + + if (sock >= 0) { + _connected = true; + } + + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + next = NULL; +} + +ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure() +{ + stop(); + delete sslclient; +} + +ESPEasy_WiFiClientSecure &ESPEasy_WiFiClientSecure::operator=(const ESPEasy_WiFiClientSecure &other) +{ + stop(); + sslclient->socket = other.sslclient->socket; + _connected = other._connected; + return *this; +} + +void ESPEasy_WiFiClientSecure::stop() +{ + if (sslclient->socket >= 0) { + close(sslclient->socket); + sslclient->socket = -1; + _connected = false; + _peek = -1; + } + stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(ip, port, _pskIdent, _psKey); + return connect(ip, port, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(ip, port); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port) +{ + if (_pskIdent && _psKey) + return connect(host, port, _pskIdent, _psKey); + return connect(host, port, _CA_cert, _cert, _private_key); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, int32_t timeout){ + _timeout = timeout; + return connect(host, port); +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) +{ + return connect(ip.toString().c_str(), port, CA_cert, cert, private_key); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) +{ + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { + return connect(ip.toString().c_str(), port, pskIdent, psKey); +} + +int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { + log_v("start_ssl_client with PSK"); + if(_timeout > 0){ + sslclient->handshake_timeout = _timeout; + } + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure); + _lastError = ret; + if (ret < 0) { + log_e("start_ssl_client: %d", ret); + stop(); + return 0; + } + _connected = true; + return 1; +} + +int ESPEasy_WiFiClientSecure::peek(){ + if(_peek >= 0){ + return _peek; + } + _peek = timedRead(); + return _peek; +} + +size_t ESPEasy_WiFiClientSecure::write(uint8_t data) +{ + return write(&data, 1); +} + +int ESPEasy_WiFiClientSecure::read() +{ + uint8_t data = -1; + int res = read(&data, 1); + if (res < 0) { + return res; + } + return data; +} + +size_t ESPEasy_WiFiClientSecure::write(const uint8_t *buf, size_t size) +{ + if (!_connected) { + return 0; + } + int res = send_ssl_data(sslclient, buf, size); + if (res < 0) { + stop(); + res = 0; + } + return res; +} + +int ESPEasy_WiFiClientSecure::read(uint8_t *buf, size_t size) +{ + int peeked = 0; + int avail = available(); + if ((!buf && size) || avail <= 0) { + return -1; + } + if(!size){ + return 0; + } + if(_peek >= 0){ + buf[0] = _peek; + _peek = -1; + size--; + avail--; + if(!size || !avail){ + return 1; + } + buf++; + peeked = 1; + } + + int res = get_ssl_receive(sslclient, buf, size); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res + peeked; +} + +int ESPEasy_WiFiClientSecure::available() +{ + int peeked = (_peek >= 0); + if (!_connected) { + return peeked; + } + int res = data_to_read(sslclient); + if (res < 0) { + stop(); + return peeked?peeked:res; + } + return res+peeked; +} + +uint8_t ESPEasy_WiFiClientSecure::connected() +{ + uint8_t dummy = 0; + read(&dummy, 0); + + return _connected; +} + +void ESPEasy_WiFiClientSecure::setInsecure() +{ + _CA_cert = NULL; + _cert = NULL; + _private_key = NULL; + _pskIdent = NULL; + _psKey = NULL; + _use_insecure = true; +} + +void ESPEasy_WiFiClientSecure::setCACert (const char *rootCA) +{ + _CA_cert = rootCA; +} + +void ESPEasy_WiFiClientSecure::setCertificate (const char *client_ca) +{ + _cert = client_ca; +} + +void ESPEasy_WiFiClientSecure::setPrivateKey (const char *private_key) +{ + _private_key = private_key; +} + +void ESPEasy_WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) { + _pskIdent = pskIdent; + _psKey = psKey; +} + +bool ESPEasy_WiFiClientSecure::verify(const char* fp, const char* domain_name) +{ + if (!sslclient) + return false; + + return verify_ssl_fingerprint(sslclient, fp, domain_name); +} + +char *ESPEasy_WiFiClientSecure::_streamLoad(Stream& stream, size_t size) { + char *dest = (char*)malloc(size+1); + if (!dest) { + return nullptr; + } + if (size != stream.readBytes(dest, size)) { + free(dest); + dest = nullptr; + return nullptr; + } + dest[size] = '\0'; + return dest; +} + +bool ESPEasy_WiFiClientSecure::loadCACert(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCACert(dest); + ret = true; + } + return ret; +} + +bool ESPEasy_WiFiClientSecure::loadCertificate(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setCertificate(dest); + ret = true; + } + return ret; +} + +bool ESPEasy_WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) { + char *dest = _streamLoad(stream, size); + bool ret = false; + if (dest) { + setPrivateKey(dest); + ret = true; + } + return ret; +} + +int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size) +{ + if (!_lastError) { + return 0; + } + mbedtls_strerror(_lastError, buf, size); + return _lastError; +} + +void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout) +{ + sslclient->handshake_timeout = handshake_timeout * 1000; +} \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h new file mode 100644 index 0000000000..08e129446b --- /dev/null +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -0,0 +1,113 @@ +/* + ESPEasy_WiFiClientSecure.h - Base class that provides Client SSL to ESP32 + Copyright (c) 2011 Adrian McEwen. All right reserved. + Additions Copyright (C) 2017 Evandro Luis Copercini. + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + This library 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 + Lesser General Public License for more details. + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +/* + Almost literal copy of https://github.com/brokentoaster/arduino-esp32/blob/master/libraries/WiFiClientSecure/src/WiFiClientSecure.h + Changed into "ESPEasy" version to incorporate some fixes + for memory leaks not yet present in the "older" core versions. +*/ + +#ifndef ESPEASY_WiFiClientSecure_h +#define ESPEASY_WiFiClientSecure_h +#include +#include +#include +#include "../Helpers/ESPEasy_ssl_client.h" + +class ESPEasy_WiFiClientSecure : public WiFiClient +{ +protected: + ESPEasy_sslclient_context *sslclient; + + int _lastError = 0; + int _peek = -1; + int _timeout = 0; + bool _use_insecure; + const char *_CA_cert; + const char *_cert; + const char *_private_key; + const char *_pskIdent; // identity for PSK cipher suites + const char *_psKey; // key in hex for PSK cipher suites + +public: + ESPEasy_WiFiClientSecure *next; + ESPEasy_WiFiClientSecure(); + ESPEasy_WiFiClientSecure(int socket); + ~ESPEasy_WiFiClientSecure(); + int connect(IPAddress ip, uint16_t port); + int connect(IPAddress ip, uint16_t port, int32_t timeout); + int connect(const char *host, uint16_t port); + int connect(const char *host, uint16_t port, int32_t timeout); + int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); + int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); + int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); + int peek(); + size_t write(uint8_t data); + size_t write(const uint8_t *buf, size_t size); + int available(); + int read(); + int read(uint8_t *buf, size_t size); + void flush() {} + void stop(); + uint8_t connected(); + int lastError(char *buf, const size_t size); + void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE! + void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + void setCACert(const char *rootCA); + void setCertificate(const char *client_ca); + void setPrivateKey (const char *private_key); + bool loadCACert(Stream& stream, size_t size); + bool loadCertificate(Stream& stream, size_t size); + bool loadPrivateKey(Stream& stream, size_t size); + bool verify(const char* fingerprint, const char* domain_name); + void setHandshakeTimeout(unsigned long handshake_timeout); + + int setTimeout(uint32_t seconds){ return 0; } + + operator bool() + { + return connected(); + } + ESPEasy_WiFiClientSecure &operator=(const ESPEasy_WiFiClientSecure &other); + bool operator==(const bool value) + { + return bool() == value; + } + bool operator!=(const bool value) + { + return bool() != value; + } + bool operator==(const ESPEasy_WiFiClientSecure &); + bool operator!=(const ESPEasy_WiFiClientSecure &rhs) + { + return !this->operator==(rhs); + }; + + int socket() + { + return sslclient->socket = -1; + } + +private: + char *_streamLoad(Stream& stream, size_t size); + + //friend class WiFiServer; + using Print::write; +}; + +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp new file mode 100644 index 0000000000..4ff9996b3b --- /dev/null +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -0,0 +1,530 @@ +#include "../Helpers/ESPEasy_ssl_client.h" + +/* Provide SSL/TLS functions to ESP32 with Arduino IDE +* +* Adapted from the ssl_client1 example of mbedtls. +* +* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. +* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED +# error "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" +#endif + +const char *ESPEasy_pers = "esp32-tls"; + +static int _handle_error(int err, const char * file, int line) +{ + if(err == -30848){ + return err; + } +#ifdef MBEDTLS_ERROR_C + char error_buf[100]; + mbedtls_strerror(err, error_buf, 100); + log_e("[%s():%d]: (%d) %s", file, line, err, error_buf); +#else + log_e("[%s():%d]: code %d", file, line, err); +#endif + return err; +} + +#define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__) + +ESPEasy_sslclient_context::ESPEasy_sslclient_context() +{ + memset(&ssl_ctx, 0, sizeof(ssl_ctx)); + memset(&ssl_conf, 0, sizeof(ssl_conf)); + memset(&drbg_ctx, 0, sizeof(drbg_ctx)); + memset(&entropy_ctx, 0, sizeof(entropy_ctx)); + memset(&ca_cert, 0, sizeof(ca_cert)); + memset(&client_cert, 0, sizeof(client_cert)); + memset(&client_key, 0, sizeof(client_key)); +} + + +ESPEasy_sslclient_context::~ESPEasy_sslclient_context() +{ + free_ca_cert(); + free_client_cert(); + free_client_key(); +} + + +void ESPEasy_sslclient_context::free_ca_cert() +{ +// if (ca_cert.p != nullptr) { + if (ca_cert_init) { + ca_cert_init = false; + } + mbedtls_x509_crt_free(&ca_cert); +// } +} + +void ESPEasy_sslclient_context::free_client_cert() +{ + if (client_cert_init) { + client_cert_init = false; + } + mbedtls_x509_crt_free(&client_cert); +// } +} + +void ESPEasy_sslclient_context::free_client_key() +{ + if (client_key_init) { + client_key_init = false; + } + mbedtls_pk_free(&client_key); +// } +} + + +void ssl_init(ESPEasy_sslclient_context *ssl_client) +{ + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + + mbedtls_ssl_init(&ssl_client->ssl_ctx); + mbedtls_ssl_config_init(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); +} + + +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure) +{ + char buf[512]; + int ret, flags; + int enable = 1; + log_v("Free internal heap before TLS %u", ESP.getFreeHeap()); + + if (rootCABuff == NULL && pskIdent == NULL && psKey == NULL && !insecure) { + return -1; + } + + log_v("Starting socket"); + ssl_client->socket = -1; + + ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + if (ssl_client->socket < 0) { + log_e("ERROR opening socket"); + return ssl_client->socket; + } + + IPAddress srv((uint32_t)0); + if(!WiFiGenericClass::hostByName(host, srv)){ + return -1; + } + + struct sockaddr_in serv_addr; + memset(&serv_addr, 0, sizeof(serv_addr)); + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = srv; + serv_addr.sin_port = htons(port); + + if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { + if(timeout <= 0){ + timeout = 30000; // Milli seconds. + } + timeval so_timeout = { .tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000 }; + +#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &so_timeout, sizeof(so_timeout)),"SO_RCVTIMEO"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &so_timeout, sizeof(so_timeout)),"SO_SNDTIMEO"); + + ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); + } else { + log_e("Connect to Server failed!"); + return -1; + } + + fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); + + log_v("Seeding the random number generator"); + mbedtls_entropy_free(&ssl_client->entropy_ctx); + mbedtls_entropy_init(&ssl_client->entropy_ctx); + + ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, + &ssl_client->entropy_ctx, (const unsigned char *) ESPEasy_pers, strlen(ESPEasy_pers)); + if (ret < 0) { + return handle_error(ret); + } + + log_v("Setting up the SSL/TLS structure..."); + + if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, + MBEDTLS_SSL_IS_CLIENT, + MBEDTLS_SSL_TRANSPORT_STREAM, + MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { + return handle_error(ret); + } + + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and + // MBEDTLS_SSL_VERIFY_NONE if not. + + if (insecure) { + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); + log_i("WARNING: Skipping SSL Verification. INSECURE!"); + } else if (rootCABuff != NULL) { + log_v("Loading CA cert"); + mbedtls_x509_crt_init(&ssl_client->ca_cert); + ssl_client->ca_cert_init = true; + mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); + ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); + mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); + //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); + if (ret < 0) { + // free the ca_cert in the case parse failed, otherwise, the old ca_cert still in the heap memory, that lead to "out of memory" crash. + ssl_client->free_ca_cert(); + return handle_error(ret); + } + } else if (pskIdent != NULL && psKey != NULL) { + log_v("Setting up PSK"); + // convert PSK from hex to binary + if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) { + log_e("pre-shared key not valid hex or too long"); + return -1; + } + unsigned char psk[MBEDTLS_PSK_MAX_LEN]; + size_t psk_len = strlen(psKey)/2; + for (int j=0; j= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] = c<<4; + c = psKey[j+1]; + if (c >= '0' && c <= '9') c -= '0'; + else if (c >= 'A' && c <= 'F') c -= 'A' - 10; + else if (c >= 'a' && c <= 'f') c -= 'a' - 10; + else return -1; + psk[j/2] |= c; + } + // set mbedtls config + ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, + (const unsigned char *)pskIdent, strlen(pskIdent)); + if (ret != 0) { + log_e("mbedtls_ssl_conf_psk returned %d", ret); + return handle_error(ret); + } + } else { + return -1; + } + + if (!insecure && cli_cert != NULL && cli_key != NULL) { + mbedtls_x509_crt_init(&ssl_client->client_cert); + mbedtls_pk_init(&ssl_client->client_key); + + + log_v("Loading CRT cert"); + + ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); + ssl_client->client_cert_init = true; + if (ret < 0) { + // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. + ssl_client->free_client_cert(); + return handle_error(ret); + } + + log_v("Loading private key"); + ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); + ssl_client->client_key_init = true; + + if (ret != 0) { + return handle_error(ret); + } + + mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); + } + + log_v("Setting hostname for TLS session..."); + + // Hostname set here should match CN in server certificate + if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0){ + return handle_error(ret); + } + + mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); + + if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) { + return handle_error(ret); + } + + mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL ); + + log_v("Performing the SSL/TLS handshake..."); + unsigned long handshake_start_time=millis(); + while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + return handle_error(ret); + } + if ((millis()-handshake_start_time) > ssl_client->handshake_timeout) { + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + return -1; + } + vTaskDelay(2);//2 ticks + } + + + if (cli_cert != NULL && cli_key != NULL) { + log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); + if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) { + log_d("Record expansion is %d", ret); + } else { + log_w("Record expansion is unknown (compression)"); + } + } + + log_v("Verifying peer X.509 certificate..."); + + if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) { + memset(buf, 0, sizeof(buf)); + mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); + log_e("Failed to verify peer certificate! verification info: %s", buf); + stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. + // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ + ssl_client->free_client_key(); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + // ++++++++++ END ++++++++++ + + return handle_error(ret); + } else { + log_v("Certificate verified."); + } + + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + ssl_client->free_client_key(); + + log_v("Free internal heap after TLS %u", ESP.getFreeHeap()); + + return ssl_client->socket; +} + + +void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key) +{ + log_v("Cleaning SSL connection."); + + if (ssl_client->socket >= 0) { + close(ssl_client->socket); + ssl_client->socket = -1; + } + + mbedtls_ssl_free(&ssl_client->ssl_ctx); + mbedtls_ssl_config_free(&ssl_client->ssl_conf); + mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); + mbedtls_entropy_free(&ssl_client->entropy_ctx); + ssl_client->free_ca_cert(); + ssl_client->free_client_cert(); + ssl_client->free_client_key(); +} + + +int data_to_read(ESPEasy_sslclient_context *ssl_client) +{ + int ret, res; + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); + //log_e("RET: %i",ret); //for low level debug + res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); + //log_e("RES: %i",res); //for low level debug + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { + return handle_error(ret); + } + + return res; +} + +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len) +{ + log_v("Writing HTTP request with %d bytes...", len); //for low level debug + int ret = -1; + + if ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0){ + log_v("Handling error %d", ret); //for low level debug + return handle_error(ret); + } else{ + log_v("Returning with %d bytes written", ret); //for low level debug + } + + return ret; +} + +int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length) +{ + //log_d( "Reading HTTP response..."); //for low level debug + int ret = -1; + + ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); + + //log_v( "%d bytes read", ret); //for low level debug + return ret; +} + +static bool parseHexNibble(char pb, uint8_t* res) +{ + if (pb >= '0' && pb <= '9') { + *res = (uint8_t) (pb - '0'); return true; + } else if (pb >= 'a' && pb <= 'f') { + *res = (uint8_t) (pb - 'a' + 10); return true; + } else if (pb >= 'A' && pb <= 'F') { + *res = (uint8_t) (pb - 'A' + 10); return true; + } + return false; +} + +// Compare a name from certificate and domain name, return true if they match +static bool matchName(const std::string& name, const std::string& domainName) +{ + size_t wildcardPos = name.find('*'); + if (wildcardPos == std::string::npos) { + // Not a wildcard, expect an exact match + return name == domainName; + } + + size_t firstDotPos = name.find('.'); + if (wildcardPos > firstDotPos) { + // Wildcard is not part of leftmost component of domain name + // Do not attempt to match (rfc6125 6.4.3.1) + return false; + } + if (wildcardPos != 0 || firstDotPos != 1) { + // Matching of wildcards such as baz*.example.com and b*z.example.com + // is optional. Maybe implement this in the future? + return false; + } + size_t domainNameFirstDotPos = domainName.find('.'); + if (domainNameFirstDotPos == std::string::npos) { + return false; + } + return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos); +} + +// Verifies certificate provided by the peer to match specified SHA256 fingerprint +bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name) +{ + // Convert hex string to byte array + uint8_t fingerprint_local[32]; + int len = strlen(fp); + int pos = 0; + for (size_t i = 0; i < sizeof(fingerprint_local); ++i) { + while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) { + ++pos; + } + if (pos > len - 2) { + log_d("pos:%d len:%d fingerprint too short", pos, len); + return false; + } + uint8_t high, low; + if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) { + log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos+1]); + return false; + } + pos += 2; + fingerprint_local[i] = low | (high << 4); + } + + // Get certificate provided by the peer + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + if (!crt) + { + log_d("could not fetch peer certificate"); + return false; + } + + // Calculate certificate's SHA256 fingerprint + uint8_t fingerprint_remote[32]; + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); + mbedtls_sha256_free(&sha256_ctx); + + // Check if fingerprints match + if (memcmp(fingerprint_local, fingerprint_remote, 32)) + { + log_d("fingerprint doesn't match"); + return false; + } + + // Additionally check if certificate has domain name if provided + if (domain_name) + return verify_ssl_dn(ssl_client, domain_name); + else + return true; +} + +// Checks if peer certificate has specified domain in CN or SANs +bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name) +{ + log_d("domain name: '%s'", (domain_name)?domain_name:"(null)"); + std::string domain_name_str(domain_name); + std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower); + + // Get certificate provided by the peer + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + + // Check for domain name in SANs + const mbedtls_x509_sequence* san = &crt->subject_alt_names; + while (san != nullptr) + { + std::string san_str((const char*)san->buf.p, san->buf.len); + std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower); + + if (matchName(san_str, domain_name_str)) + return true; + + log_d("SAN '%s': no match", san_str.c_str()); + + // Fetch next SAN + san = san->next; + } + + // Check for domain name in CN + const mbedtls_asn1_named_data* common_name = &crt->subject; + while (common_name != nullptr) + { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + std::string common_name_str((const char*)common_name->val.p, common_name->val.len); + + if (matchName(common_name_str, domain_name_str)) + return true; + + log_d("CN '%s': not match", common_name_str.c_str()); + } + + // Fetch next DN object + common_name = common_name->next; + } + + return false; +} \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h new file mode 100644 index 0000000000..46bc0d649f --- /dev/null +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -0,0 +1,53 @@ +/* Provide SSL/TLS functions to ESP32 with Arduino IDE + * by Evandro Copercini - 2017 - Apache 2.0 License + */ + +#ifndef ESPEASY_ARD_SSL_H +#define ESPEASY_ARD_SSL_H +#include +#include +#include +#include +#include +#include +#include + +typedef struct ESPEasy_sslclient_context { + + ESPEasy_sslclient_context(); + + ~ESPEasy_sslclient_context(); + + void free_ca_cert(); + void free_client_cert(); + void free_client_key(); + + int socket = 0; + mbedtls_ssl_context ssl_ctx; + mbedtls_ssl_config ssl_conf; + + mbedtls_ctr_drbg_context drbg_ctx; + mbedtls_entropy_context entropy_ctx; + + mbedtls_x509_crt ca_cert; + mbedtls_x509_crt client_cert; + mbedtls_pk_context client_key; + + bool ca_cert_init = false; + bool client_cert_init = false; + bool client_key_init = false; + + unsigned long handshake_timeout = 120000; +} ESPEasy_sslclient_context; + + +void ssl_init(ESPEasy_sslclient_context *ssl_client); +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure); +void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); +int data_to_read(ESPEasy_sslclient_context *ssl_client); +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); +int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); +bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); +bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); + +#endif \ No newline at end of file From eb8d956b46a884e07d645bfeb383de7a4da1bf0e Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 11:14:04 +0100 Subject: [PATCH 025/367] [MQTT TLS] Fix memory leak... finally --- src/src/ESPEasyCore/Controller.cpp | 32 +++++++++++++++----------- src/src/Helpers/ESPEasy_Storage.cpp | 21 ----------------- src/src/Helpers/ESPEasy_ssl_client.cpp | 22 +++--------------- src/src/Helpers/ESPEasy_ssl_client.h | 6 +---- 4 files changed, 22 insertions(+), 59 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index fafa1f4717..56c897ffa5 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -186,12 +186,10 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (MQTTclient.connected()) { MQTTclient.disconnect(); # ifdef USE_MQTT_TLS - /* if (mqtt_tls != nullptr) { delete mqtt_tls; mqtt_tls = nullptr; } - */ #endif } @@ -206,7 +204,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_tls_last_errorstr = EMPTY_STRING; mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); - if (TLS_type != TLS_types::NoTLS) { + if (TLS_type != TLS_types::NoTLS && nullptr == mqtt_tls) { #ifdef ESP32 mqtt_tls = new ESPEasy_WiFiClientSecure; #endif @@ -217,6 +215,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (mqtt_tls == nullptr) { addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); return false; + } else { + mqtt_rootCA.clear(); } } switch(TLS_type) { @@ -233,9 +233,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) } case TLS_types::TLS_CA_CERT: { -// mqtt_rootCA.clear(); - if (mqtt_rootCA.isEmpty()) + mqtt_rootCA.clear(); + bool certLoaded = false; + if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + certLoaded = true; + } { static int previousFree = FreeMem(); @@ -253,15 +256,16 @@ bool MQTTConnect(controllerIndex_t controller_idx) previousFree = freemem; } - - if (mqtt_rootCA.length() > 0) { - #ifdef ESP32 - mqtt_tls->setCACert(mqtt_rootCA.c_str()); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA.c_str()); - mqtt_tls->setTrustAnchors(&mqtt_X509List); - #endif + if (certLoaded) { + if (mqtt_rootCA.length() > 0) { + #ifdef ESP32 + mqtt_tls->setCACert(mqtt_rootCA.c_str()); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); + #endif + } } break; } diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index b461bd914c..c89b6ea45e 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1115,10 +1115,6 @@ String LoadCertificate(const String& fname, String& certificate) } f.close(); - String analyse = F("Cleanup: Before: "); - analyse += certificate.length(); - analyse += F(" After: "); - if (!cleanupCertificate(certificate, changed)) { certificate.clear(); #ifndef BUILD_NO_DEBUG @@ -1129,23 +1125,6 @@ String LoadCertificate(const String& fname, String& certificate) } else if (changed) { //return SaveCertificate(fname, certificate); } -// addLog(LOG_LEVEL_INFO, F("After")); -// addLog(LOG_LEVEL_INFO, certificate); - static int previousFree = FreeMem(); - const int freemem = FreeMem(); - - analyse += certificate.length(); - analyse += changed ? F(" changed") : F(" same"); - analyse += F(" free memory: "); - analyse += freemem; - analyse += F(" largest free block: "); - analyse += getMaxFreeBlock(); - - analyse += F(" Difference: "); - analyse += previousFree - freemem; - - addLog(LOG_LEVEL_INFO, analyse); - previousFree = freemem; } return EMPTY_STRING; diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index 4ff9996b3b..baa7b67b1c 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -65,30 +65,17 @@ ESPEasy_sslclient_context::~ESPEasy_sslclient_context() void ESPEasy_sslclient_context::free_ca_cert() { -// if (ca_cert.p != nullptr) { - if (ca_cert_init) { - ca_cert_init = false; - } - mbedtls_x509_crt_free(&ca_cert); -// } + mbedtls_x509_crt_free(&ca_cert); } void ESPEasy_sslclient_context::free_client_cert() { - if (client_cert_init) { - client_cert_init = false; - } - mbedtls_x509_crt_free(&client_cert); -// } + mbedtls_x509_crt_free(&client_cert); } void ESPEasy_sslclient_context::free_client_key() { - if (client_key_init) { - client_key_init = false; - } - mbedtls_pk_free(&client_key); -// } + mbedtls_pk_free(&client_key); } @@ -182,7 +169,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui } else if (rootCABuff != NULL) { log_v("Loading CA cert"); mbedtls_x509_crt_init(&ssl_client->ca_cert); - ssl_client->ca_cert_init = true; mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); @@ -234,7 +220,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui log_v("Loading CRT cert"); ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); - ssl_client->client_cert_init = true; if (ret < 0) { // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. ssl_client->free_client_cert(); @@ -243,7 +228,6 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui log_v("Loading private key"); ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); - ssl_client->client_key_init = true; if (ret != 0) { return handle_error(ret); diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 46bc0d649f..9704b3ff80 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -22,7 +22,7 @@ typedef struct ESPEasy_sslclient_context { void free_client_cert(); void free_client_key(); - int socket = 0; + int socket = -1; mbedtls_ssl_context ssl_ctx; mbedtls_ssl_config ssl_conf; @@ -33,10 +33,6 @@ typedef struct ESPEasy_sslclient_context { mbedtls_x509_crt client_cert; mbedtls_pk_context client_key; - bool ca_cert_init = false; - bool client_cert_init = false; - bool client_key_init = false; - unsigned long handshake_timeout = 120000; } ESPEasy_sslclient_context; From 57a82b1df2b4fb3aac49544ba48c061bbb587b3a Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 12:06:20 +0100 Subject: [PATCH 026/367] [MQTT TLS] Clear CA certificate when starting a new connection Otherwise you may not always use the latest CA root certificate stored on the file system --- src/src/ESPEasyCore/Controller.cpp | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 56c897ffa5..38cdc20f3f 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -190,6 +190,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) delete mqtt_tls; mqtt_tls = nullptr; } + mqtt_rootCA.clear(); #endif } @@ -211,12 +212,11 @@ bool MQTTConnect(controllerIndex_t controller_idx) #ifdef ESP8266 mqtt_tls = new BearSSL::WiFiClientSecure; #endif + mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); return false; - } else { - mqtt_rootCA.clear(); } } switch(TLS_type) { @@ -234,12 +234,6 @@ bool MQTTConnect(controllerIndex_t controller_idx) case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); - bool certLoaded = false; - if (mqtt_rootCA.isEmpty()) { - LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); - certLoaded = true; - } - { static int previousFree = FreeMem(); const int freemem = FreeMem(); @@ -256,16 +250,16 @@ bool MQTTConnect(controllerIndex_t controller_idx) previousFree = freemem; } - if (certLoaded) { - if (mqtt_rootCA.length() > 0) { - #ifdef ESP32 - mqtt_tls->setCACert(mqtt_rootCA.c_str()); - #endif - #ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA.c_str()); - mqtt_tls->setTrustAnchors(&mqtt_X509List); - #endif - } + if (mqtt_rootCA.isEmpty()) { + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + + #ifdef ESP32 + mqtt_tls->setCACert(mqtt_rootCA.c_str()); + #endif + #ifdef ESP8266 + mqtt_X509List.append(mqtt_rootCA.c_str()); + mqtt_tls->setTrustAnchors(&mqtt_X509List); + #endif } break; } From baf705df3e5ab2009b731c73096bca1fc73be676 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 12:18:06 +0100 Subject: [PATCH 027/367] [Cleanup] Fix lots of missing delete calls to free memory In a lot of places an object was allocated on the heap, but not always it was deleted thus leading to memory leaks. --- src/_P095_ILI9341.ino | 13 +++++++++++++ src/_P096_eInk.ino | 9 +++++++++ 2 files changed, 22 insertions(+) diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index 9fb383959a..a8cb5fc05e 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -246,6 +246,10 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) TFT_Settings.address_tft_dc = PIN(1); TFT_Settings.address_tft_rst = PIN(2); TFT_Settings.rotation = PCONFIG(1); + if (tft != nullptr) { + delete tft; + tft = nullptr; + } initPluginTaskData(event->TaskIndex, new (std::nothrow) P095_data_struct( @@ -264,6 +268,15 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) break; } + case PLUGIN_EXIT: + { + if (tft != nullptr) { + delete tft; + tft = nullptr; + } + break; + } + case PLUGIN_WRITE: { String tmpString = String(string); diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 083a18d493..74adafe53d 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -305,6 +305,15 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) break; } + case PLUGIN_EXIT: + { + if (eInkScreen != nullptr) { + delete eInkScreen; + eInkScreen = nullptr; + } + break; + } + case PLUGIN_WRITE: { #ifndef BUILD_NO_DEBUG From df010cdae0727e30bfaedd7d1cd7f8750cfd6922 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 4 Nov 2021 23:58:22 +0100 Subject: [PATCH 028/367] [Webserver] Fix serving CSS I made an error in previous commit for this PR --- src/src/Helpers/ESPEasy_Storage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index c89b6ea45e..4df93d332d 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -125,7 +125,7 @@ bool fileExists(const String& fname) { const String patched_fname = patch_fname(fname); auto search = Cache.fileExistsMap.find(patched_fname); if (search != Cache.fileExistsMap.end()) { - return search->second >= 0; + return search->second; } int size = -1; if (ESPEASY_FS.exists(patched_fname)) { From 40d17e04037701aaf21bf21b52e24b79fceca105 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 17:21:22 +0100 Subject: [PATCH 029/367] [TLS] Add ALPN protocol --- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 11 ++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 4 ++ src/src/Helpers/ESPEasy_ssl_client.cpp | 48 +++++++++++++------- src/src/Helpers/ESPEasy_ssl_client.h | 3 +- 4 files changed, 47 insertions(+), 19 deletions(-) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index f84c5aeb40..c08a7d22bd 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -41,6 +41,7 @@ ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure() _pskIdent = NULL; _psKey = NULL; next = NULL; + _alpn_protos = NULL; } @@ -64,6 +65,7 @@ ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock) _pskIdent = NULL; _psKey = NULL; next = NULL; + _alpn_protos = NULL; } ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure() @@ -125,7 +127,7 @@ int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const cha if(_timeout > 0){ sslclient->handshake_timeout = _timeout; } - int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure); + int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure, _alpn_protos); _lastError = ret; if (ret < 0) { log_e("start_ssl_client: %d", ret); @@ -145,7 +147,7 @@ int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const cha if(_timeout > 0){ sslclient->handshake_timeout = _timeout; } - int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure); + int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure, _alpn_protos); _lastError = ret; if (ret < 0) { log_e("start_ssl_client: %d", ret); @@ -338,4 +340,9 @@ int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size) void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout) { sslclient->handshake_timeout = handshake_timeout * 1000; +} + +void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) +{ + _alpn_protos = alpn_protos; } \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index 08e129446b..e219b37c40 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -42,6 +42,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient const char *_private_key; const char *_pskIdent; // identity for PSK cipher suites const char *_psKey; // key in hex for PSK cipher suites + const char **_alpn_protos; public: ESPEasy_WiFiClientSecure *next; @@ -76,6 +77,9 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool loadPrivateKey(Stream& stream, size_t size); bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); + void setAlpnProtocols(const char **alpn_protos); + const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; + bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; int setTimeout(uint32_t seconds){ return 0; } diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index baa7b67b1c..ebeabe99c5 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -91,7 +91,7 @@ void ssl_init(ESPEasy_sslclient_context *ssl_client) } -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure) +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos) { char buf[512]; int ret, flags; @@ -160,6 +160,13 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return handle_error(ret); } + if (alpn_protos != NULL) { + log_v("Setting ALPN protocols"); + if ((ret = mbedtls_ssl_conf_alpn_protocols(&ssl_client->ssl_conf, alpn_protos) ) != 0) { + return handle_error(ret); + } + } + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and // MBEDTLS_SSL_VERIFY_NONE if not. @@ -433,23 +440,10 @@ bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* f fingerprint_local[i] = low | (high << 4); } - // Get certificate provided by the peer - const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); - - if (!crt) - { - log_d("could not fetch peer certificate"); - return false; - } - // Calculate certificate's SHA256 fingerprint uint8_t fingerprint_remote[32]; - mbedtls_sha256_context sha256_ctx; - mbedtls_sha256_init(&sha256_ctx); - mbedtls_sha256_starts(&sha256_ctx, false); - mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); - mbedtls_sha256_finish(&sha256_ctx, fingerprint_remote); - mbedtls_sha256_free(&sha256_ctx); + if(!get_peer_fingerprint(ssl_client, fingerprint_remote)) + return false; // Check if fingerprints match if (memcmp(fingerprint_local, fingerprint_remote, 32)) @@ -465,6 +459,28 @@ bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* f return true; } +bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]) +{ + if (!ssl_client) { + log_d("Invalid ssl_client pointer"); + return false; + }; + + const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); + if (!crt) { + log_d("Failed to get peer cert."); + return false; + }; + + mbedtls_sha256_context sha256_ctx; + mbedtls_sha256_init(&sha256_ctx); + mbedtls_sha256_starts(&sha256_ctx, false); + mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); + mbedtls_sha256_finish(&sha256_ctx, sha256); + + return true; +} + // Checks if peer certificate has specified domain in CN or SANs bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name) { diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 9704b3ff80..de17f30709 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -38,12 +38,13 @@ typedef struct ESPEasy_sslclient_context { void ssl_init(ESPEasy_sslclient_context *ssl_client); -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure); +int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos); void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); int data_to_read(ESPEasy_sslclient_context *ssl_client); int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); +bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); #endif \ No newline at end of file From 97a4594fa93895ed3396e28baa6f3006152ff026 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 18:01:27 +0100 Subject: [PATCH 030/367] [TLS] Add fix for WiFiClientSecure connection timeout --- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 2 +- src/src/Helpers/ESPEasy_ssl_client.cpp | 100 ++++++++++++++------- src/src/Helpers/ESPEasy_ssl_client.h | 3 +- 3 files changed, 72 insertions(+), 33 deletions(-) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index e219b37c40..c228befd7a 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -57,7 +57,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); - int peek(); + int peek(); size_t write(uint8_t data); size_t write(const uint8_t *buf, size_t size); int available(); diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index ebeabe99c5..a867641a8c 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -21,12 +21,12 @@ #include #ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED -# error "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" -#endif +# warning "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" +#else const char *ESPEasy_pers = "esp32-tls"; -static int _handle_error(int err, const char * file, int line) +static int _handle_error(int err, const char * function, int line) { if(err == -30848){ return err; @@ -34,15 +34,16 @@ static int _handle_error(int err, const char * file, int line) #ifdef MBEDTLS_ERROR_C char error_buf[100]; mbedtls_strerror(err, error_buf, 100); - log_e("[%s():%d]: (%d) %s", file, line, err, error_buf); + log_e("[%s():%d]: (%d) %s", function, line, err, error_buf); #else - log_e("[%s():%d]: code %d", file, line, err); + log_e("[%s():%d]: code %d", function, line, err); #endif return err; } #define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__) + ESPEasy_sslclient_context::ESPEasy_sslclient_context() { memset(&ssl_ctx, 0, sizeof(ssl_ctx)); @@ -116,30 +117,67 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return -1; } + fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = srv; serv_addr.sin_port = htons(port); - if (lwip_connect(ssl_client->socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == 0) { - if(timeout <= 0){ - timeout = 30000; // Milli seconds. - } - timeval so_timeout = { .tv_sec = timeout / 1000, .tv_usec = (timeout % 1000) * 1000 }; + if(timeout <= 0){ + timeout = 30000; // Milli seconds. + } -#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &so_timeout, sizeof(so_timeout)),"SO_RCVTIMEO"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &so_timeout, sizeof(so_timeout)),"SO_SNDTIMEO"); + fd_set fdset; + struct timeval tv; + FD_ZERO(&fdset); + FD_SET(ssl_client->socket, &fdset); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; - ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); - } else { - log_e("Connect to Server failed!"); + int res = lwip_connect(ssl_client->socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); + if (res < 0 && errno != EINPROGRESS) { + log_e("connect on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); return -1; } - fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); + res = select(ssl_client->socket + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv); + if (res < 0) { + log_e("select on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); + return -1; + } else if (res == 0) { + log_i("select returned due to timeout %d ms for fd %d", timeout, ssl_client->socket); + close(ssl_client->socket); + return -1; + } else { + int sockerr; + socklen_t len = (socklen_t)sizeof(int); + res = getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &sockerr, &len); + + if (res < 0) { + log_e("getsockopt on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); + close(ssl_client->socket); + return -1; + } + + if (sockerr != 0) { + log_e("socket error on fd %d, errno: %d, \"%s\"", ssl_client->socket, sockerr, strerror(sockerr)); + close(ssl_client->socket); + return -1; + } + } + + +#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),"SO_RCVTIMEO"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),"SO_SNDTIMEO"); + + ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); + ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); + + log_v("Seeding the random number generator"); mbedtls_entropy_free(&ssl_client->entropy_ctx); @@ -166,7 +204,7 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui return handle_error(ret); } } - + // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and // MBEDTLS_SSL_VERIFY_NONE if not. @@ -223,12 +261,11 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui mbedtls_x509_crt_init(&ssl_client->client_cert); mbedtls_pk_init(&ssl_client->client_key); - log_v("Loading CRT cert"); ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); if (ret < 0) { - // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. + // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. ssl_client->free_client_cert(); return handle_error(ret); } @@ -275,9 +312,9 @@ int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, ui ssl_client->free_ca_cert(); ssl_client->free_client_cert(); // ++++++++++ END ++++++++++ - return -1; + return -1; } - vTaskDelay(2);//2 ticks + vTaskDelay(2);//2 ticks } @@ -351,16 +388,18 @@ int data_to_read(ESPEasy_sslclient_context *ssl_client) return res; } -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len) +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len) { log_v("Writing HTTP request with %d bytes...", len); //for low level debug int ret = -1; - if ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0){ - log_v("Handling error %d", ret); //for low level debug - return handle_error(ret); - } else{ - log_v("Returning with %d bytes written", ret); //for low level debug + while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) { + if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { + log_v("Handling error %d", ret); //for low level debug + return handle_error(ret); + } + //wait for space to become available + vTaskDelay(2); } return ret; @@ -527,4 +566,5 @@ bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_nam } return false; -} \ No newline at end of file +} +#endif diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index de17f30709..86560b0faa 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -41,10 +41,9 @@ void ssl_init(ESPEasy_sslclient_context *ssl_client); int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos); void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); int data_to_read(ESPEasy_sslclient_context *ssl_client); -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, uint16_t len); +int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len); int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); - #endif \ No newline at end of file From 89e9f23e2ce0000266f623a92b1efc4e5ac08309 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 21:52:29 +0100 Subject: [PATCH 031/367] [MQTT TLS]Display connection info on controller page --- src/src/ESPEasyCore/Controller.cpp | 15 ++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 22 ++++++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 4 +++ src/src/Helpers/ESPEasy_ssl_client.cpp | 3 ++ src/src/Helpers/ESPEasy_ssl_client.h | 3 +- src/src/WebServer/ControllerPage.cpp | 33 ++++++++++++++++++++ 6 files changed, 77 insertions(+), 3 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 38cdc20f3f..84772d6dd3 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -202,7 +202,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) uint16_t mqttPort = ControllerSettings.Port; #ifdef USE_MQTT_TLS - mqtt_tls_last_errorstr = EMPTY_STRING; + mqtt_tls_last_errorstr.clear(); mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); if (TLS_type != TLS_types::NoTLS && nullptr == mqtt_tls) { @@ -277,6 +277,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) } } if (TLS_type != TLS_types::NoTLS) { + // Certificate expiry not enabled in Mbed TLS. +// mqtt_tls->setX509Time(node_time.getUnixTime()); mqtt_tls->setTimeout(ControllerSettings.ClientTimeout); #ifdef ESP8266 mqtt_tls->setBufferSizes(1024,1024); @@ -380,6 +382,17 @@ bool MQTTConnect(controllerIndex_t controller_idx) log += clientid; addLog(LOG_LEVEL_INFO, log); + + #ifdef USE_MQTT_TLS + #ifdef ESP32 + { + log = F("MQTT : Peer certificate info: "); + log += mqtt_tls->getPeerCertificateInfo(); + addLog(LOG_LEVEL_INFO, log); + log.clear(); + } + #endif + #endif String subscribeTo = ControllerSettings.Subscribe; parseSystemVariables(subscribeTo, false); diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index c08a7d22bd..b9ee2e5e43 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -17,6 +17,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +#ifdef ESP32 #include #include #include @@ -345,4 +346,23 @@ void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeo void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) { _alpn_protos = alpn_protos; -} \ No newline at end of file +} + +String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() +{ + const mbedtls_x509_crt* peer = getPeerCertificate(); + String res; + if (peer != nullptr) { + char buf[1024] = {0}; + int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer); + if (l > 0) { + if (res.reserve(l)) { + for (int i = 0; i < l; ++i) { + res += buf[i]; + } + } + } + } + return res; +} +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index c228befd7a..0ddaffc20f 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -21,6 +21,8 @@ for memory leaks not yet present in the "older" core versions. */ +#ifdef ESP32 + #ifndef ESPEASY_WiFiClientSecure_h #define ESPEASY_WiFiClientSecure_h #include @@ -78,6 +80,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); void setAlpnProtocols(const char **alpn_protos); + String getPeerCertificateInfo(); const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; @@ -114,4 +117,5 @@ class ESPEasy_WiFiClientSecure : public WiFiClient using Print::write; }; +#endif #endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index a867641a8c..0cb0394e6d 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -8,6 +8,8 @@ * Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. */ +#ifdef ESP32 + #include #include #include @@ -568,3 +570,4 @@ bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_nam return false; } #endif +#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 86560b0faa..5c612c3ae7 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -1,7 +1,7 @@ /* Provide SSL/TLS functions to ESP32 with Arduino IDE * by Evandro Copercini - 2017 - Apache 2.0 License */ - +#ifdef ESP32 #ifndef ESPEASY_ARD_SSL_H #define ESPEASY_ARD_SSL_H #include @@ -46,4 +46,5 @@ int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int le bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); +#endif #endif \ No newline at end of file diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 788dc385f5..f6cb91e9f6 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -17,6 +17,10 @@ #include "../Globals/Protocol.h" #include "../Globals/Settings.h" +#ifdef USES_MQTT +#include "../Globals/MQTT.h" +#endif + #include "../Helpers/_CPlugin_Helper_webform.h" #include "../Helpers/_Plugin_SensorTypeHelper.h" #include "../Helpers/ESPEasy_Storage.h" @@ -414,6 +418,35 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtmlError(F("Bug in CPlugin::Function::CPLUGIN_WEBFORM_LOAD, should not append to string, use addHtml() instead")); } } + { + #ifdef USES_MQTT + if (Protocol[ProtocolIndex].usesMQTT) { + addFormSubHeader(F("Connection Info")); + addRowLabel(F("MQTT Client Connected")); + addEnabled(MQTTclient_connected); + +#ifdef USE_MQTT_TLS + if (Protocol[ProtocolIndex].usesTLS) { + addRowLabel(F("Last Error")); + addHtmlInt(mqtt_tls_last_error); + addHtml(F(": ")); + addHtml(mqtt_tls_last_errorstr); + + #ifdef ESP32 + if (MQTTclient_connected) { + addRowLabel(F("Peer Certificate")); + String peerInfo = mqtt_tls->getPeerCertificateInfo(); + peerInfo.replace(F("\n"), F("
")); + addTextBox(F("peer_cert"), peerInfo, peerInfo.length(), true); + } + #endif + + } +#endif + } + #endif + } + // Separate enabled checkbox as it doesn't need to use the ControllerSettings. // So ControllerSettings object can be destructed before controller specific settings are loaded. addControllerEnabledForm(controllerindex); From 3cf92b9f6f4055caf268eaefd8cd5bb00db94698 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 6 Nov 2021 21:53:26 +0100 Subject: [PATCH 032/367] [Cleanup] Use .clear() on strings instead of assigning empty string --- src/_C015.cpp | 2 +- src/_P050_TCS34725.ino | 4 ++-- src/src/ESPEasyCore/ESPEasyRules.cpp | 4 ++-- src/src/PluginStructs/P104_data_struct.cpp | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/_C015.cpp b/src/_C015.cpp index 8ca412089f..8a47f09372 100644 --- a/src/_C015.cpp +++ b/src/_C015.cpp @@ -194,7 +194,7 @@ bool CPlugin_015(CPlugin::Function function, struct EventStruct *event, String& if (!isvalid) { // send empty string to Blynk in case of error - formattedValue = EMPTY_STRING; + formattedValue.clear(); } String valueName = ExtraTaskSettings.TaskDeviceValueNames[x]; diff --git a/src/_P050_TCS34725.ino b/src/_P050_TCS34725.ino index 302a37cdc5..26c33b831c 100644 --- a/src/_P050_TCS34725.ino +++ b/src/_P050_TCS34725.ino @@ -430,7 +430,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += toString(static_cast(b) / t * sRGBFactor, 4); break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { @@ -466,7 +466,7 @@ boolean Plugin_050(uint8_t function, struct EventStruct *event, String& string) RuleEvent += c; break; default: - RuleEvent = EMPTY_STRING; + RuleEvent.clear(); break; } if (!RuleEvent.isEmpty()) { diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index cc246f5082..01d70b2fae 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -252,7 +252,7 @@ String rulesProcessingFile(const String& fileName, const String& event) { } // Prepare for new line - line = EMPTY_STRING; + line.clear(); line.reserve(longestLineSize); firstNonSpaceRead = false; commentFound = false; @@ -752,7 +752,7 @@ void parseCompleteNonCommentLine(String& line, const String& event, String eventTrigger; - action = EMPTY_STRING; + action.clear(); if (!codeBlock) // do not check "on" rules if a block of actions is to be // processed diff --git a/src/src/PluginStructs/P104_data_struct.cpp b/src/src/PluginStructs/P104_data_struct.cpp index 5aa1ee2f27..cc5058e240 100644 --- a/src/src/PluginStructs/P104_data_struct.cpp +++ b/src/src/PluginStructs/P104_data_struct.cpp @@ -334,7 +334,7 @@ void P104_data_struct::loadSettings() { zones.push_back(P104_zone_struct(zoneIndex + 1)); if (zones[zoneIndex].text == F("\"\"")) { // Special case - zones[zoneIndex].text = EMPTY_STRING; + zones[zoneIndex].text.clear(); } zoneIndex++; @@ -1566,7 +1566,7 @@ String P104_data_struct::enquoteString(const String& input) { * saveSettings gather the zones data from the UI and store in customsettings **************************************/ bool P104_data_struct::saveSettings() { - error = EMPTY_STRING; // Clear + error.clear(); // Clear String zbuffer; # ifdef P104_DEBUG_DEV @@ -1681,7 +1681,7 @@ bool P104_data_struct::saveSettings() { if (zbuffer.reserve(P104_SETTINGS_BUFFER_V2 + 2)) { for (auto it = zones.begin(); it != zones.end() && error.length() == 0; ++it) { - zbuffer = EMPTY_STRING; + zbuffer.clear(); // WARNING: Order of values should match the numeric order of P104_OFFSET_* values zbuffer += it->size; // 2 From 4bb3bbf8f346379a711e38a739a1b4b12406663a Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 7 Nov 2021 01:05:32 +0100 Subject: [PATCH 033/367] [TLS] Making proper datastructure init --- src/src/Helpers/ESPEasy_ssl_client.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index 0cb0394e6d..bf8ff5dbd6 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -48,13 +48,14 @@ static int _handle_error(int err, const char * function, int line) ESPEasy_sslclient_context::ESPEasy_sslclient_context() { - memset(&ssl_ctx, 0, sizeof(ssl_ctx)); - memset(&ssl_conf, 0, sizeof(ssl_conf)); - memset(&drbg_ctx, 0, sizeof(drbg_ctx)); - memset(&entropy_ctx, 0, sizeof(entropy_ctx)); - memset(&ca_cert, 0, sizeof(ca_cert)); - memset(&client_cert, 0, sizeof(client_cert)); - memset(&client_key, 0, sizeof(client_key)); + mbedtls_ssl_init(&ssl_ctx); + mbedtls_ssl_config_init(&ssl_conf); + mbedtls_ctr_drbg_init(&drbg_ctx); + + mbedtls_entropy_init(&entropy_ctx); + mbedtls_x509_crt_init(&ca_cert); + mbedtls_x509_crt_init(&client_cert); + mbedtls_pk_init(&client_key); } From 5127f006d13a42da27b7b0f53f6becd4650d3643 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 7 Nov 2021 01:06:15 +0100 Subject: [PATCH 034/367] [MQTT TLS] Improve controller setup page show peer certificate --- src/src/WebServer/ControllerPage.cpp | 39 ++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index f6cb91e9f6..0a91d32df9 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -419,9 +419,9 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex } } { - #ifdef USES_MQTT +#ifdef USES_MQTT if (Protocol[ProtocolIndex].usesMQTT) { - addFormSubHeader(F("Connection Info")); + addFormSubHeader(F("Connection Status")); addRowLabel(F("MQTT Client Connected")); addEnabled(MQTTclient_connected); @@ -433,23 +433,46 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtml(mqtt_tls_last_errorstr); #ifdef ESP32 - if (MQTTclient_connected) { - addRowLabel(F("Peer Certificate")); - String peerInfo = mqtt_tls->getPeerCertificateInfo(); - peerInfo.replace(F("\n"), F("
")); - addTextBox(F("peer_cert"), peerInfo, peerInfo.length(), true); + if (MQTTclient_connected && mqtt_tls != nullptr) { + addFormSubHeader(F("Peer Certificate")); + + { + addRowLabel(F("Certificate Info")); + addHtml(F("")); + } + { + uint8_t sha256_result[32] = {0}; + if (mqtt_tls->getFingerprintSHA256(sha256_result)) { + String fingerprint; + fingerprint.reserve(64); + for (size_t i = 0; i < 32; ++i) { + fingerprint += String(sha256_result[i], HEX); + } + fingerprint.toLowerCase(); + addFormTextBox(F("Certificate Fingerprint"), + F("fingerprint"), + fingerprint, + 64, + true); // ReadOnly + } + } + + } #endif } #endif } - #endif +#endif } // Separate enabled checkbox as it doesn't need to use the ControllerSettings. // So ControllerSettings object can be destructed before controller specific settings are loaded. addControllerEnabledForm(controllerindex); + } addFormSeparator(2); From 9f0e3cf636bf2bc1df1b0728f7c88b7975ffeb09 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 25 Nov 2021 01:46:05 +0100 Subject: [PATCH 035/367] [MQTT TLS] Add certificate fingerprint validation --- .../DataStructs/ControllerSettingsStruct.cpp | 3 + .../DataStructs/ControllerSettingsStruct.h | 1 + src/src/DataTypes/TLS_types.cpp | 1 + src/src/DataTypes/TLS_types.h | 1 + src/src/ESPEasyCore/Controller.cpp | 88 ++++++++++++++++++- src/src/ESPEasyCore/Controller.h | 6 ++ src/src/Globals/MQTT.cpp | 1 + src/src/Globals/MQTT.h | 2 + src/src/Helpers/ESPEasy_Storage.cpp | 22 ++--- src/src/Helpers/ESPEasy_Storage.h | 2 +- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 1 + src/src/Helpers/_CPlugin_Helper_webform.cpp | 69 +++++++++++---- src/src/WebServer/ControllerPage.cpp | 19 ++-- 13 files changed, 178 insertions(+), 38 deletions(-) diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 791013cece..ade97bf8b5 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -349,6 +349,9 @@ String ControllerSettingsStruct::getCertificateFilename() const case TLS_types::TLS_CA_CERT: certFile += F(".cacert"); break; + case TLS_types::TLS_FINGERPRINT: + certFile += F(".fp"); + break; } return certFile; diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 1c021015b7..92592efd19 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -65,6 +65,7 @@ struct ControllerSettingsStruct CONTROLLER_IP, CONTROLLER_PORT, CONTROLLER_MQTT_TLS_TYPE, + CONTROLLER_MQTT_TLS_STORE_FINGERPRINT, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp index 5eaf38b4b1..805d8bc5a3 100644 --- a/src/src/DataTypes/TLS_types.cpp +++ b/src/src/DataTypes/TLS_types.cpp @@ -7,6 +7,7 @@ const __FlashStringHelper* toString(TLS_types tls_type) case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); case TLS_types::TLS_CA_CERT: return F("TLS CA Cert"); case TLS_types::TLS_insecure: return F("TLS No Checks (insecure)"); + case TLS_types::TLS_FINGERPRINT: return F("TLS Certficate Fingerprint"); } return F("No TLS"); } diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index a8aec9aa14..3cf81fd398 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -11,6 +11,7 @@ enum class TLS_types { TLS_PSK = 1, // Pre-Shared-Key TLS_CA_CERT = 2, // Validate server certificate against known CA //TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication + TLS_FINGERPRINT = 4, // Use certificate fingerprint TLS_insecure = 0xF // Set as last option, do not check supplied certificate. Ideal for man-in-the-middle attack. }; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 84772d6dd3..c633ff61f0 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -215,7 +215,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { - addLog(LOG_LEVEL_ERROR, F("MQTT : Could not create TLS client, out of memory")); + mqtt_tls_last_errorstr = F("MQTT : Could not create TLS client, out of memory"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); return false; } } @@ -252,6 +253,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); + if (mqtt_rootCA.isEmpty()) { + // Fingerprint must be of some minimal length to continue. + mqtt_tls_last_errorstr = F("MQTT : No TLS root CA"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); + return false; + } #ifdef ESP32 mqtt_tls->setCACert(mqtt_rootCA.c_str()); @@ -270,8 +277,24 @@ bool MQTTConnect(controllerIndex_t controller_idx) break; } */ + case TLS_types::TLS_FINGERPRINT: + { + // Fingerprint is checked when making the connection. + mqtt_rootCA.clear(); + mqtt_fingerprint.clear(); + LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_fingerprint, false); + if (mqtt_fingerprint.length() < 32) { + // Fingerprint must be of some minimal length to continue. + mqtt_tls_last_errorstr = F("MQTT : Stored TLS fingerprint too small"); + addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); + return false; + } + mqtt_tls->setInsecure(); + break; + } case TLS_types::TLS_insecure: { + mqtt_rootCA.clear(); mqtt_tls->setInsecure(); break; } @@ -353,9 +376,41 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif #ifdef ESP32 mqtt_tls_last_error = mqtt_tls->lastError(buf,128); + mqtt_tls->clearLastError(); #endif mqtt_tls_last_errorstr = buf; } + if (TLS_type == TLS_types::TLS_FINGERPRINT) + { + // Check fingerprint + if (MQTTresult) { + const int newlinepos = mqtt_fingerprint.indexOf('\n'); + String fp; + String dn; + if (ControllerSettings.UseDNS) dn = ControllerSettings.getHost(); + if (newlinepos == -1) { + fp = mqtt_fingerprint; + } else { + fp = mqtt_fingerprint.substring(0, newlinepos); + const int newlinepos2 = mqtt_fingerprint.indexOf('\n', newlinepos); + if (newlinepos2 == -1) + dn = mqtt_fingerprint.substring(newlinepos + 1); + else + dn = mqtt_fingerprint.substring(newlinepos + 1, newlinepos2); + dn.trim(); + + } + if (!mqtt_tls->verify( + fp.c_str(), + dn.isEmpty() ? nullptr : dn.c_str())) + { + mqtt_tls_last_errorstr += F("TLS Fingerprint does not match"); + addLog(LOG_LEVEL_INFO, mqtt_fingerprint); + MQTTresult = false; + } + } + } + #endif @@ -713,6 +768,37 @@ void MQTTStatus(struct EventStruct *event, const String& status) } } + +#ifdef USE_MQTT_TLS +bool GetTLSfingerprint(String& fp) +{ + #ifdef ESP32 + if (MQTTclient_connected && mqtt_tls != nullptr) { + uint8_t sha256_result[32] = {0}; + if (mqtt_tls->getFingerprintSHA256(sha256_result)) { + fp.reserve(64); + for (size_t i = 0; i < 32; ++i) { + const String tmp(sha256_result[i], HEX); + switch (tmp.length()) { + case 0: + fp += '0'; + // fall through + case 1: + fp += '0'; + break; + } + fp += tmp; + } + fp.toLowerCase(); + return true; + } + } + #endif + return false; +} + +#endif + #endif // USES_MQTT diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index 0a69b48a4f..e14ac48748 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -69,6 +69,12 @@ bool MQTTpublish(controllerIndex_t controller_idx, taskIndex_t taskIndex, Strin * Send status info back to channel where request came from \*********************************************************************************************/ void MQTTStatus(struct EventStruct *event, const String& status); + +#ifdef USE_MQTT_TLS +bool GetTLSfingerprint(String& fp); + +#endif + #endif //USES_MQTT diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 3434c4b42b..89cb8e9470 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -19,6 +19,7 @@ BearSSL::WiFiClientSecure* mqtt_tls; BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 String mqtt_rootCA; +String mqtt_fingerprint; # endif // ifdef USE_MQTT_TLS PubSubClient MQTTclient(mqtt); diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 3cf7ff14b3..d73894f02b 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -33,6 +33,8 @@ extern BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 extern String mqtt_rootCA; +extern String mqtt_fingerprint; + # endif // ifdef USE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 4df93d332d..77a49d21cb 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1081,7 +1081,7 @@ String SaveCertificate(const String& fname, const String& certificate) return SaveToFile(fname.c_str(), 0, (const uint8_t *)certificate.c_str(), certificate.length() + 1); } -String LoadCertificate(const String& fname, String& certificate) +String LoadCertificate(const String& fname, String& certificate, bool cleanup) { bool changed = false; if (fileExists(fname)) { @@ -1115,15 +1115,17 @@ String LoadCertificate(const String& fname, String& certificate) } f.close(); - if (!cleanupCertificate(certificate, changed)) { - certificate.clear(); - #ifndef BUILD_NO_DEBUG - log += F(" ERROR, Invalid certificate format"); - #endif - addLog(LOG_LEVEL_ERROR, log); - return log; - } else if (changed) { - //return SaveCertificate(fname, certificate); + if (cleanup) { + if (!cleanupCertificate(certificate, changed)) { + certificate.clear(); + #ifndef BUILD_NO_DEBUG + log += F(" ERROR, Invalid certificate format"); + #endif + addLog(LOG_LEVEL_ERROR, log); + return log; + } else if (changed) { + //return SaveCertificate(fname, certificate); + } } } diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index 620c041bbe..e450c7917b 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -177,7 +177,7 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int The content will be stripped from unusable character like quotes, spaces etc. \*********************************************************************************************/ String SaveCertificate(const String& fname, const String& certificate); -String LoadCertificate(const String& fname, String& certificate); +String LoadCertificate(const String& fname, String& certificate, bool cleanup = true); /********************************************************************************************\ diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index 0ddaffc20f..b5241bfda5 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -69,6 +69,7 @@ class ESPEasy_WiFiClientSecure : public WiFiClient void stop(); uint8_t connected(); int lastError(char *buf, const size_t size); + void clearLastError() { _lastError = 0; } void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE! void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex void setCACert(const char *rootCA); diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index ebc493a272..07663abbfe 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -21,7 +21,8 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); - case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: return F("Store Fingerprint"); case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -118,6 +119,26 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } +void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description) { + #ifdef USE_MQTT_TLS + const String certFile = ControllerSettings.getCertificateFilename(); + if (!certFile.isEmpty()) + { + const String certFile = ControllerSettings.getCertificateFilename(); + String note = description; + note += F(" "); + note += certFile; + note += F(" "); + if (fileExists(certFile)) { + note += F("(File exists)"); + } else { + note += F("(Not found)"); + } + addFormNote(note); + } + #endif +} + void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (!validProtocolIndex(ProtocolIndex)) { @@ -158,35 +179,33 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin { #ifdef USE_MQTT_TLS const int choice = static_cast(ControllerSettings.TLStype()); - #define NR_MQTT_TLS_TYPES 3 + #define NR_MQTT_TLS_TYPES 4 const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_FINGERPRINT), toString(TLS_types::TLS_insecure) }; const int indices[NR_MQTT_TLS_TYPES] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), static_cast(TLS_types::TLS_CA_CERT), + static_cast(TLS_types::TLS_FINGERPRINT), static_cast(TLS_types::TLS_insecure) }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES - const String certFile = ControllerSettings.getCertificateFilename(); - if (!certFile.isEmpty()) - { - const String certFile = ControllerSettings.getCertificateFilename(); - String note = F("Certificate or PSK must be stored on the filesystem in "); - note += certFile; - note += F(" "); - if (fileExists(certFile)) { - note += F("(File exists)"); - } else { - note += F("(Not found)"); - } - addFormNote(note); - } + addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in")); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: + { + #ifdef USE_MQTT_TLS + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename()); + addFormCheckBox(displayName, internalName, false, saveDisabled); + addCertificateFileNote(ControllerSettings, F("Store fingerprint in")); #endif break; } @@ -356,6 +375,24 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: + { + #ifdef USE_MQTT_TLS + if (isFormItemChecked(internalName)) { + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { + if (ControllerSettings.UseDNS) { + fingerprint += '\n'; + fingerprint += ControllerSettings.getHost(); + } + SaveCertificate(ControllerSettings.getCertificateFilename(), fingerprint); + } + } + #endif + break; + } + + case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); break; diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 0a91d32df9..f6400a6f70 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -443,23 +443,22 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHtml(F("")); } { - uint8_t sha256_result[32] = {0}; - if (mqtt_tls->getFingerprintSHA256(sha256_result)) { - String fingerprint; - fingerprint.reserve(64); - for (size_t i = 0; i < 32; ++i) { - fingerprint += String(sha256_result[i], HEX); - } - fingerprint.toLowerCase(); + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { addFormTextBox(F("Certificate Fingerprint"), F("fingerprint"), fingerprint, 64, true); // ReadOnly + MakeControllerSettings(ControllerSettings); //-V522 + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, ControllerSettings); + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); + } } } - - } #endif From a7750e8145a8a3d5c14a731a493ee207b2c6190e Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 30 Nov 2021 00:40:47 +0100 Subject: [PATCH 036/367] [MQTT TLS] Add view of certificates + option to store --- .../ControllerQueue/DelayQueueElements.cpp | 4 + .../DataStructs/ControllerSettingsStruct.cpp | 12 ++- .../DataStructs/ControllerSettingsStruct.h | 3 + src/src/ESPEasyCore/Controller.cpp | 39 ++++++-- src/src/ESPEasyCore/Controller.h | 2 + src/src/Helpers/ESPEasy_Storage.cpp | 10 ++- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 73 ++++++++++++++- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 8 +- src/src/Helpers/ESPEasy_ssl_client.h | 1 + src/src/Helpers/_CPlugin_Helper_webform.cpp | 49 ++++++++-- src/src/WebServer/ControllerPage.cpp | 89 ++++++++++++++----- 11 files changed, 245 insertions(+), 45 deletions(-) diff --git a/src/src/ControllerQueue/DelayQueueElements.cpp b/src/src/ControllerQueue/DelayQueueElements.cpp index 0d1b5dfc66..1116d0545e 100644 --- a/src/src/ControllerQueue/DelayQueueElements.cpp +++ b/src/src/ControllerQueue/DelayQueueElements.cpp @@ -9,6 +9,9 @@ ControllerDelayHandlerStruct *MQTTDelayHandler = nullptr; bool init_mqtt_delay_queue(controllerIndex_t ControllerIndex, String& pubname, bool& retainFlag) { + // Make sure the controller is re-connecting with the current settings. + MQTTDisconnect(); + MakeControllerSettings(ControllerSettings); //-V522 if (!AllocatedControllerSettings()) { return false; @@ -34,6 +37,7 @@ bool init_mqtt_delay_queue(controllerIndex_t ControllerIndex, String& pubname, b void exit_mqtt_delay_queue() { if (MQTTDelayHandler != nullptr) { + MQTTDisconnect(); delete MQTTDelayHandler; MQTTDelayHandler = nullptr; } diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index ade97bf8b5..8cc1b6a3fe 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -328,13 +328,18 @@ void ControllerSettingsStruct::TLStype(TLS_types tls_type) } String ControllerSettingsStruct::getCertificateFilename() const +{ + return getCertificateFilename(TLStype()); +} + +String ControllerSettingsStruct::getCertificateFilename(TLS_types tls_type) const { String certFile = HostName; if (certFile.isEmpty()) { certFile = F(""); } - switch (TLStype()) { + switch (tls_type) { case TLS_types::NoTLS: case TLS_types::TLS_insecure: return EMPTY_STRING; @@ -353,6 +358,11 @@ String ControllerSettingsStruct::getCertificateFilename() const certFile += F(".fp"); break; } + + // Only use the last 29 bytes of the filename + if (certFile.length() > 28) { + certFile = certFile.substring(certFile.length() - 28); + } return certFile; } \ No newline at end of file diff --git a/src/src/DataStructs/ControllerSettingsStruct.h b/src/src/DataStructs/ControllerSettingsStruct.h index 92592efd19..b56e142385 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.h +++ b/src/src/DataStructs/ControllerSettingsStruct.h @@ -66,6 +66,8 @@ struct ControllerSettingsStruct CONTROLLER_PORT, CONTROLLER_MQTT_TLS_TYPE, CONTROLLER_MQTT_TLS_STORE_FINGERPRINT, + CONTROLLER_MQTT_TLS_STORE_CERT, + CONTROLLER_MQTT_TLS_STORE_CACERT, CONTROLLER_USER, CONTROLLER_PASS, CONTROLLER_MIN_SEND_INTERVAL, @@ -154,6 +156,7 @@ struct ControllerSettingsStruct void TLStype(TLS_types tls_type); String getCertificateFilename() const; + String getCertificateFilename(TLS_types tls_type) const; boolean UseDNS; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index c633ff61f0..6614941337 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -235,6 +235,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); + /* { static int previousFree = FreeMem(); const int freemem = FreeMem(); @@ -250,6 +251,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) addLog(LOG_LEVEL_INFO, analyse); previousFree = freemem; } + */ if (mqtt_rootCA.isEmpty()) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); @@ -433,18 +435,23 @@ bool MQTTConnect(controllerIndex_t controller_idx) updateMQTTclient_connected(); return false; } - String log = F("MQTT : Connected to broker with client ID: "); + if (loglevelActiveFor(LOG_LEVEL_INFO)) + { + String log = F("MQTT : Connected to broker with client ID: "); - log += clientid; - addLog(LOG_LEVEL_INFO, log); + log += clientid; + addLog(LOG_LEVEL_INFO, log); + } #ifdef USE_MQTT_TLS #ifdef ESP32 + if (loglevelActiveFor(LOG_LEVEL_INFO)) { - log = F("MQTT : Peer certificate info: "); + String log = F("MQTT : Peer certificate info: "); + log += ControllerSettings.getHost(); + log += ' '; log += mqtt_tls->getPeerCertificateInfo(); addLog(LOG_LEVEL_INFO, log); - log.clear(); } #endif #endif @@ -452,9 +459,12 @@ bool MQTTConnect(controllerIndex_t controller_idx) parseSystemVariables(subscribeTo, false); MQTTclient.subscribe(subscribeTo.c_str()); - log = F("Subscribed to: "); - log += subscribeTo; - addLog(LOG_LEVEL_INFO, log); + if (loglevelActiveFor(LOG_LEVEL_INFO)) + { + String log = F("Subscribed to: "); + log += subscribeTo; + addLog(LOG_LEVEL_INFO, log); + } updateMQTTclient_connected(); statusLED(true); @@ -797,6 +807,19 @@ bool GetTLSfingerprint(String& fp) return false; } +bool GetTLS_Certificate(String& cert, bool caRoot) +{ + #ifdef ESP32 + if (MQTTclient_connected && mqtt_tls != nullptr) { + String subject; + if (mqtt_tls->getPeerCertificate(cert, subject, caRoot) == 0) { + return true; + } + } + #endif + return false; +} + #endif #endif // USES_MQTT diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index e14ac48748..4529d17375 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -73,6 +73,8 @@ void MQTTStatus(struct EventStruct *event, const String& status); #ifdef USE_MQTT_TLS bool GetTLSfingerprint(String& fp); +bool GetTLS_Certificate(String& cert, bool caRoot); + #endif #endif //USES_MQTT diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 77a49d21cb..3618fe6a21 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1042,7 +1042,7 @@ bool cleanupCertificate(String & certificate, bool &changed) for (int i = 0; i < 4 && last_pos != -1; ++i) { dash_pos[i] = certificate.indexOf(F("-----"), last_pos); last_pos = dash_pos[i] + 5; - addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); +// addLog(LOG_LEVEL_INFO, String(F(" dash_pos: ")) + String(dash_pos[i])); } if (last_pos == -1) return false; @@ -1175,7 +1175,9 @@ String InitFile(SettingsType::SettingsFileEnum file_type) \*********************************************************************************************/ String SaveToFile(const char *fname, int index, const uint8_t *memAddress, int datasize) { - return doSaveToFile(fname, index, memAddress, datasize, "r+"); + return doSaveToFile( + fname, index, memAddress, datasize, + fileExists(fname) ? "r+" : "w+"); } // See for mode description: https://github.com/esp8266/Arduino/blob/master/doc/filesystem.rst @@ -1226,7 +1228,9 @@ String doSaveToFile(const char *fname, int index, const uint8_t *memAddress, int if (f) { clearAllCaches(); SPIFFS_CHECK(f, fname); - SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname); + if (index > 0) { + SPIFFS_CHECK(f.seek(index, fs::SeekSet), fname); + } const uint8_t *pointerToByteToSave = memAddress; for (int x = 0; x < datasize; x++) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index b9ee2e5e43..5d924ca8b0 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -22,6 +22,9 @@ #include #include +// FIXME TD-er: Feels wrong this needs to be included here to use mbedtls_pem_write_buffer +#include + #undef connect #undef write #undef read @@ -348,9 +351,11 @@ void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) _alpn_protos = alpn_protos; } -String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() +String ESPEasy_WiFiClientSecure::getPeerCertificateInfo(const mbedtls_x509_crt* peer) { - const mbedtls_x509_crt* peer = getPeerCertificate(); + if (peer == nullptr) { + peer = getPeerCertificate(); + } String res; if (peer != nullptr) { char buf[1024] = {0}; @@ -365,4 +370,68 @@ String ESPEasy_WiFiClientSecure::getPeerCertificateInfo() } return res; } + +int ESPEasy_WiFiClientSecure::getPeerCertificate(String& pem, String& subject, bool caRoot) +{ + const mbedtls_x509_crt *chain; + + chain = getPeerCertificate(); + + int error {0}; + bool done = false; + while (chain != nullptr && error == 0 && !done) { + if (!caRoot || (chain->ca_istrue && chain->next == nullptr)) { + done = true; + error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); + } + chain = chain->next; + } + return error; +} + +int ESPEasy_WiFiClientSecure::cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject) +{ + const String pem_begin_crt = F("-----BEGIN CERTIFICATE-----\n"); + const String pem_end_crt = F("-----END CERTIFICATE-----"); + pem.clear(); + subject.clear(); + + const mbedtls_asn1_named_data* common_name = &crt->subject; + while (common_name != nullptr) { + // While iterating through DN objects, check for CN object + if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) + { + + subject.reserve(common_name->val.len); + const unsigned char* p = common_name->val.p; + for (auto i = 0; i < common_name->val.len; ++i, ++p) { + subject += static_cast(*p); + } + } + + // Fetch next DN object + common_name = common_name->next; + } + + size_t written{}; + + const size_t buffer_size = + pem_begin_crt.length() + + pem_end_crt.length() + + 2* crt->raw.len; + + std::vector pem_buf; + pem_buf.resize(buffer_size); + int ret = mbedtls_pem_write_buffer( + pem_begin_crt.c_str(), pem_end_crt.c_str(), + crt->raw.p, crt->raw.len, + &pem_buf[0], buffer_size, &written); + if (ret == 0) { + pem.reserve(written); + for (auto i = 0; i < written; ++i) { + pem += static_cast(pem_buf[i]); + } + } + return ret; +} #endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h index b5241bfda5..a35fa92bfb 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.h @@ -81,10 +81,16 @@ class ESPEasy_WiFiClientSecure : public WiFiClient bool verify(const char* fingerprint, const char* domain_name); void setHandshakeTimeout(unsigned long handshake_timeout); void setAlpnProtocols(const char **alpn_protos); - String getPeerCertificateInfo(); + String getPeerCertificateInfo(const mbedtls_x509_crt* crt = nullptr); const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; + int getPeerCertificate(String& pem, String& subject, bool caRoot); + + // See: https://stackoverflow.com/a/63730321/8708166 + static int cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject); + + int setTimeout(uint32_t seconds){ return 0; } operator bool() diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h index 5c612c3ae7..7f6eebf034 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ b/src/src/Helpers/ESPEasy_ssl_client.h @@ -46,5 +46,6 @@ int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int le bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); + #endif #endif \ No newline at end of file diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 07663abbfe..cf9be9f9d0 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -23,6 +23,9 @@ const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameter case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: return F("Store Fingerprint"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: return F("Store Certificate"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: return F("Store CA Certificate"); + case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); @@ -119,12 +122,11 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } -void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description) { +void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description, TLS_types tls_type) { #ifdef USE_MQTT_TLS - const String certFile = ControllerSettings.getCertificateFilename(); + const String certFile = ControllerSettings.getCertificateFilename(tls_type); if (!certFile.isEmpty()) { - const String certFile = ControllerSettings.getCertificateFilename(); String note = description; note += F(" "); note += certFile; @@ -196,19 +198,38 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin }; addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); #undef NR_MQTT_TLS_TYPES - addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in")); + addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in"), ControllerSettings.TLStype()); #endif break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { #ifdef USE_MQTT_TLS - const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename()); + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT)); addFormCheckBox(displayName, internalName, false, saveDisabled); - addCertificateFileNote(ControllerSettings, F("Store fingerprint in")); + addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); #endif break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: + // fall through + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: + { + #ifdef USE_MQTT_TLS + /* + const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? + TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; + */ + const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT)); + addFormCheckBox(displayName, internalName, false, saveDisabled); + if (saveDisabled) { + addUnit(F("File Exists")); + } + addCertificateFileNote(ControllerSettings, F("Store CA Certificate in"), TLS_types::TLS_CA_CERT); + #endif + break; + } + case ControllerSettingsStruct::CONTROLLER_USER: { const size_t fieldMaxLength = @@ -385,13 +406,27 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet fingerprint += '\n'; fingerprint += ControllerSettings.getHost(); } - SaveCertificate(ControllerSettings.getCertificateFilename(), fingerprint); + SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT), fingerprint); } } #endif break; } + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: + // fall through + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: + { + #ifdef USE_MQTT_TLS + if (isFormItemChecked(internalName)) { + String cacert; + if (GetTLS_Certificate(cacert, true)) { + SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT), cacert); + } + } + #endif + break; + } case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index f6400a6f70..52118eab35 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -27,7 +27,6 @@ #include "../Helpers/StringConverter.h" - // ******************************************************************************** // Web Interface controller page // ******************************************************************************** @@ -434,34 +433,78 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex #ifdef ESP32 if (MQTTclient_connected && mqtt_tls != nullptr) { - addFormSubHeader(F("Peer Certificate")); - - { - addRowLabel(F("Certificate Info")); - addHtml(F("")); - } - { - String fingerprint; - if (GetTLSfingerprint(fingerprint)) { - addFormTextBox(F("Certificate Fingerprint"), - F("fingerprint"), - fingerprint, - 64, - true); // ReadOnly - MakeControllerSettings(ControllerSettings); //-V522 - if (!AllocatedControllerSettings()) { - addHtmlError(F("Out of memory, cannot load page")); - } else { - LoadControllerSettings(controllerindex, ControllerSettings); + MakeControllerSettings(ControllerSettings); //-V522 + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, ControllerSettings); + + addFormSubHeader(F("Peer Certificate")); + + { + addRowLabel(F("Certificate Info")); + addHtml(F("")); + } + { + String fingerprint; + if (GetTLSfingerprint(fingerprint)) { + addFormTextBox(F("Certificate Fingerprint"), + F("fingerprint"), + fingerprint, + 64, + true); // ReadOnly addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); } } + addFormSubHeader(F("Peer Certificate Chain")); + { + // FIXME TD-er: Must wrap this in divs to be able to fold it by default. + const mbedtls_x509_crt *chain; + + chain = mqtt_tls->getPeerCertificate(); + + int error {0}; + while (chain != nullptr && error == 0) { + /* + const bool mustShow = !chain->ca_istrue || chain->next == nullptr; + if (mustShow) { + */ + String pem, subject; + error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); + { + String label; + if (chain->ca_istrue) { + label = F("CA "); + } + label += F("Certificate "); + label += subject; + label += F(""); + addRowLabel(label); + } + if (error == 0) { + addHtml(F("")); + + addHtml(F("")); + } else { + addHtmlInt(error); + } + if (chain->ca_istrue && chain->next == nullptr) { + // Add checkbox to store CA cert + addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT); + } +// } + chain = chain->next; + } + } } } #endif - } #endif } From c23a07d62f54ab0ef17dbd0ed811921b802c618b Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 8 Feb 2022 14:00:00 +0100 Subject: [PATCH 037/367] [Webserver] Fix merge issue (MQTT TLS) --- src/src/WebServer/WebServer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/src/WebServer/WebServer.cpp b/src/src/WebServer/WebServer.cpp index 3dfe586618..2e81800d1c 100644 --- a/src/src/WebServer/WebServer.cpp +++ b/src/src/WebServer/WebServer.cpp @@ -91,6 +91,7 @@ void sendHeadandTail(const __FlashStringHelper * tmplName, boolean Tail, boolean String fileName = tmplName; fileName += F(".htm"); + fs::File f = tryOpenFile(fileName, "r"); WebTemplateParser templateParser(Tail, rebooting); if (f) { From 821af361a893c364b066f8c4fae94c85d8dd0e1d Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 8 Feb 2022 17:03:29 +0100 Subject: [PATCH 038/367] [Merge Fix] Restore P095 and P096 from mega branch Was by accident merged in the wrong direction. --- src/_P095_ILI9341.ino | 13 ------------- src/_P096_eInk.ino | 9 --------- 2 files changed, 22 deletions(-) diff --git a/src/_P095_ILI9341.ino b/src/_P095_ILI9341.ino index a8cb5fc05e..9fb383959a 100644 --- a/src/_P095_ILI9341.ino +++ b/src/_P095_ILI9341.ino @@ -246,10 +246,6 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) TFT_Settings.address_tft_dc = PIN(1); TFT_Settings.address_tft_rst = PIN(2); TFT_Settings.rotation = PCONFIG(1); - if (tft != nullptr) { - delete tft; - tft = nullptr; - } initPluginTaskData(event->TaskIndex, new (std::nothrow) P095_data_struct( @@ -268,15 +264,6 @@ boolean Plugin_095(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: - { - if (tft != nullptr) { - delete tft; - tft = nullptr; - } - break; - } - case PLUGIN_WRITE: { String tmpString = String(string); diff --git a/src/_P096_eInk.ino b/src/_P096_eInk.ino index 74adafe53d..083a18d493 100644 --- a/src/_P096_eInk.ino +++ b/src/_P096_eInk.ino @@ -305,15 +305,6 @@ boolean Plugin_096(uint8_t function, struct EventStruct *event, String& string) break; } - case PLUGIN_EXIT: - { - if (eInkScreen != nullptr) { - delete eInkScreen; - eInkScreen = nullptr; - } - break; - } - case PLUGIN_WRITE: { #ifndef BUILD_NO_DEBUG From d51ace5c103fbb0ae911e1ef6a8569ab0d147850 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 8 Feb 2022 18:04:46 +0100 Subject: [PATCH 039/367] [MQTT TLS] Fix missing include mbedtls/oid.h --- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index 5d924ca8b0..1ee80eaba5 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -22,8 +22,10 @@ #include #include + // FIXME TD-er: Feels wrong this needs to be included here to use mbedtls_pem_write_buffer #include +#include #undef connect #undef write From 7a06dc4acd496c20ab6f0ea447b433a53e717175 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 8 Feb 2022 18:31:03 +0100 Subject: [PATCH 040/367] [MQTT TLS] Fix missing include --- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index 1ee80eaba5..52e4065cb0 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -27,6 +27,8 @@ #include #include +#include + #undef connect #undef write #undef read From 90b06ea2cc1b33811f3a6d2fa0097b57db5d15d0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 22 Apr 2022 09:46:26 +0200 Subject: [PATCH 041/367] [Build] Fix merge issue. --- src/src/ESPEasyCore/Controller.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 8118a689e1..96f3a07651 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -345,8 +345,6 @@ bool MQTTConnect(controllerIndex_t controller_idx) bool willRetain = ControllerSettings.mqtt_willRetain() && ControllerSettings.mqtt_sendLWT(); bool cleanSession = ControllerSettings.mqtt_cleanSession(); // As suggested here: - mqtt_last_connect_attempt.setNow(); - if (MQTTclient_should_reconnect) { addLog(LOG_LEVEL_ERROR, F("MQTT : Intentional reconnect")); } From eec2a4e5a3fc059b764526e2da6031b050147b29 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 2 May 2022 21:24:01 +0200 Subject: [PATCH 042/367] [MQTT TLS] Fix build issues --- platformio_core_defs.ini | 2 ++ src/src/ESPEasyCore/Controller.cpp | 15 ++++++++++----- src/src/Helpers/ESPEasy_ssl_client.cpp | 3 ++- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 948010d823..f5efa84a50 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -170,6 +170,8 @@ build_flags = -DESP32_STAGE platform = https://github.com/tasmota/platform-espressif32/releases/download/v2.0.3rc1/platform-espressif32-2.0.3new.zip platform_packages = build_flags = -DESP32_STAGE + -I$PROJECT_DIR/include + -include "sdkconfig.h" [core_esp32_3_5_0] platform = espressif32 @ 3.5.0 diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 96f3a07651..e8117ac6e6 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -258,7 +258,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) } */ - if (mqtt_rootCA.isEmpty()) { + if (mqtt_rootCA.isEmpty() && mqtt_tls != nullptr) { LoadCertificate(ControllerSettings.getCertificateFilename(), mqtt_rootCA); if (mqtt_rootCA.isEmpty()) { // Fingerprint must be of some minimal length to continue. @@ -296,17 +296,21 @@ bool MQTTConnect(controllerIndex_t controller_idx) addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); return false; } - mqtt_tls->setInsecure(); + if (mqtt_tls != nullptr) { + mqtt_tls->setInsecure(); + } break; } case TLS_types::TLS_insecure: { mqtt_rootCA.clear(); - mqtt_tls->setInsecure(); + if (mqtt_tls != nullptr) { + mqtt_tls->setInsecure(); + } break; } } - if (TLS_type != TLS_types::NoTLS) { + if (TLS_type != TLS_types::NoTLS && mqtt_tls != nullptr) { // Certificate expiry not enabled in Mbed TLS. // mqtt_tls->setX509Time(node_time.getUnixTime()); mqtt_tls->setTimeout(ControllerSettings.ClientTimeout); @@ -377,6 +381,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number); #ifdef USE_MQTT_TLS + if (mqtt_tls != nullptr) { char buf[128] = {0}; #ifdef ESP8266 @@ -452,7 +457,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) #ifdef USE_MQTT_TLS #ifdef ESP32 - if (loglevelActiveFor(LOG_LEVEL_INFO)) + if (mqtt_tls != nullptr && loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("MQTT : Peer certificate info: "); log += ControllerSettings.getHost(); diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp index bf8ff5dbd6..7e20438a52 100644 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ b/src/src/Helpers/ESPEasy_ssl_client.cpp @@ -22,10 +22,11 @@ #include #include -#ifndef MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED +#if !defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && !defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) # warning "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" #else + const char *ESPEasy_pers = "esp32-tls"; static int _handle_error(int err, const char * function, int line) From a63fc9772f706f3ebd3c9347e2b3ef87fa575e2d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 12 Jun 2022 11:45:54 +0200 Subject: [PATCH 043/367] [MQTT TLS] Disable fingerprint verify on ESP8266 as not implemented --- src/src/ESPEasyCore/Controller.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index e8117ac6e6..22eec8d33a 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -393,6 +393,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif mqtt_tls_last_errorstr = buf; } + #ifdef ESP32 + // FIXME TD-er: There seems to be no verify function in BearSSL used on ESP8266 if (TLS_type == TLS_types::TLS_FINGERPRINT) { // Check fingerprint @@ -423,6 +425,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) } } } + #endif #endif From 3b3ac15c283c949e3d86333568429437a2f10482 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 12 Jun 2022 12:01:01 +0200 Subject: [PATCH 044/367] [MQTT TLS] Disable USE_MQTT_TLS for LIMIT_BUILD_SIZE and 1M builds --- src/src/CustomBuild/define_plugin_sets.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index a9906a5dea..f7f6228cef 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1869,4 +1869,10 @@ To create/register a plugin, you have to : #endif #endif +#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) + #ifdef USE_MQTT_TLS + #undef USE_MQTT_TLS + #endif +#endif + #endif // CUSTOMBUILD_DEFINE_PLUGIN_SETS_H \ No newline at end of file From ff7f8675e9b705ad8389603ab7d0386a6baa2629 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 12 Jun 2022 12:21:22 +0200 Subject: [PATCH 045/367] [MQTT TLS] Disable TLS for ESP8266 completely --- src/src/CustomBuild/define_plugin_sets.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index f7f6228cef..19dd2f7665 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1869,7 +1869,9 @@ To create/register a plugin, you have to : #endif #endif -#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) +#ifdef ESP8266 +// It just doesn't work on ESP8266, too slow, too high memory requirements +//#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) #ifdef USE_MQTT_TLS #undef USE_MQTT_TLS #endif From 1a105617a54a0dca90b5b529067db6b56ba43c9d Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 16 Oct 2022 17:01:54 +0200 Subject: [PATCH 046/367] [MQTT TLS] Rename USE_MQTT_TLS to FEATURE_MQTT_TLS --- src/_C002.cpp | 2 +- src/_C005.cpp | 2 +- src/_C006.cpp | 2 +- src/_C014.cpp | 2 +- src/src/CustomBuild/define_plugin_sets.h | 9 +++++++-- src/src/DataStructs/ControllerSettingsStruct.cpp | 4 ++-- src/src/ESPEasyCore/Controller.cpp | 14 +++++++------- src/src/ESPEasyCore/Controller.h | 2 +- src/src/Globals/MQTT.cpp | 4 ++-- src/src/Globals/MQTT.h | 8 ++++---- src/src/Helpers/_CPlugin_Helper_webform.cpp | 14 +++++++------- src/src/WebServer/ControllerPage.cpp | 8 ++++---- tools/pio/pre_custom_esp32.py | 2 +- tools/pio/pre_custom_esp82xx.py | 2 +- 14 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/_C002.cpp b/src/_C002.cpp index 31dd6b5e9d..47244a3b97 100644 --- a/src/_C002.cpp +++ b/src/_C002.cpp @@ -37,7 +37,7 @@ bool CPlugin_002(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = true; - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS Protocol[protocolCount].usesTLS = true; #endif break; diff --git a/src/_C005.cpp b/src/_C005.cpp index 5f3a117108..87b98dac81 100644 --- a/src/_C005.cpp +++ b/src/_C005.cpp @@ -36,7 +36,7 @@ bool CPlugin_005(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS Protocol[protocolCount].usesTLS = true; #endif diff --git a/src/_C006.cpp b/src/_C006.cpp index cfb9c15927..a34f1342ea 100644 --- a/src/_C006.cpp +++ b/src/_C006.cpp @@ -36,7 +36,7 @@ bool CPlugin_006(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS Protocol[protocolCount].usesTLS = true; #endif diff --git a/src/_C014.cpp b/src/_C014.cpp index 6f23b27887..0b8bd86e42 100644 --- a/src/_C014.cpp +++ b/src/_C014.cpp @@ -183,7 +183,7 @@ bool CPlugin_014(CPlugin::Function function, struct EventStruct *event, String& Protocol[protocolCount].usesExtCreds = true; Protocol[protocolCount].defaultPort = 1883; Protocol[protocolCount].usesID = false; - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS Protocol[protocolCount].usesTLS = true; #endif diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index efd49cce52..e4c0a65e80 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2294,11 +2294,16 @@ To create/register a plugin, you have to : #endif #endif +#ifndef FEATURE_MQTT_TLS +#define FEATURE_MQTT_TLS 0 +#endif + #ifdef ESP8266 // It just doesn't work on ESP8266, too slow, too high memory requirements //#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) - #ifdef USE_MQTT_TLS - #undef USE_MQTT_TLS + #if FEATURE_MQTT_TLS + #undef FEATURE_MQTT_TLS + #define FEATURE_MQTT_TLS 0 #endif #endif diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 28130ffc1d..b5b4ddbfd7 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -78,8 +78,8 @@ void ControllerSettingsStruct::validate() { ZERO_TERMINATE(LWTMessageConnect); ZERO_TERMINATE(LWTMessageDisconnect); - #ifdef USES_MQTT - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT + #if FEATURE_MQTT_TLS if (TLStype() == TLS_types::NoTLS) { if (Port == 8883) { Port = 1883; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 6c3e07a84e..782a90d20c 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -190,7 +190,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (MQTTclient.connected()) { MQTTclient.disconnect(); - # ifdef USE_MQTT_TLS + # if FEATURE_MQTT_TLS if (mqtt_tls != nullptr) { delete mqtt_tls; mqtt_tls = nullptr; @@ -205,7 +205,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) delay(0); uint16_t mqttPort = ControllerSettings.Port; -#ifdef USE_MQTT_TLS +#if FEATURE_MQTT_TLS mqtt_tls_last_errorstr.clear(); mqtt_tls_last_error = 0; const TLS_types TLS_type = ControllerSettings.TLStype(); @@ -431,7 +431,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) uint8_t controller_number = Settings.Protocol[controller_idx]; count_connection_results(MQTTresult, F("MQTT : Broker "), controller_number, connect_start_time); - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS if (mqtt_tls != nullptr) { char buf[128] = {0}; @@ -481,7 +481,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif if (!MQTTresult) { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS if ((mqtt_tls_last_error != 0) && loglevelActiveFor(LOG_LEVEL_ERROR)) { String log = F("MQTT : TLS error code: "); log += mqtt_tls_last_error; @@ -492,7 +492,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) #endif MQTTclient.disconnect(); - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS mqtt_tls->stop(); #endif @@ -507,7 +507,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) addLogMove(LOG_LEVEL_INFO, log); } - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS #ifdef ESP32 if (mqtt_tls != nullptr && loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -841,7 +841,7 @@ void MQTTStatus(struct EventStruct *event, const String& status) } } -#ifdef USE_MQTT_TLS +#if FEATURE_MQTT_TLS bool GetTLSfingerprint(String& fp) { #ifdef ESP32 diff --git a/src/src/ESPEasyCore/Controller.h b/src/src/ESPEasyCore/Controller.h index 7ba94da154..228fd7df04 100644 --- a/src/src/ESPEasyCore/Controller.h +++ b/src/src/ESPEasyCore/Controller.h @@ -71,7 +71,7 @@ bool MQTTpublish(controllerIndex_t controller_idx, taskIndex_t taskIndex, Strin \*********************************************************************************************/ void MQTTStatus(struct EventStruct *event, const String& status); -#ifdef USE_MQTT_TLS +#if FEATURE_MQTT_TLS bool GetTLSfingerprint(String& fp); bool GetTLS_Certificate(String& cert, bool caRoot); diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 0e47515e86..65e752e75f 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -7,7 +7,7 @@ // MQTT client WiFiClient mqtt; -# ifdef USE_MQTT_TLS +# if FEATURE_MQTT_TLS String mqtt_tls_last_errorstr; int32_t mqtt_tls_last_error = 0; @@ -20,7 +20,7 @@ BearSSL::X509List mqtt_X509List; # endif // ifdef ESP8266 String mqtt_rootCA; String mqtt_fingerprint; -# endif // ifdef USE_MQTT_TLS +# endif // if FEATURE_MQTT_TLS PubSubClient MQTTclient(mqtt); bool MQTTclient_should_reconnect = true; diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 073a075f49..4422fdfe63 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -10,17 +10,17 @@ # include # include -# ifdef USE_MQTT_TLS +# if FEATURE_MQTT_TLS # ifdef ESP32 # include "../Helpers/ESPEasy_WiFiClientSecure.h" # else # include # endif -# endif // ifdef USE_MQTT_TLS +# endif // if FEATURE_MQTT_TLS // MQTT client extern WiFiClient mqtt; -# ifdef USE_MQTT_TLS +# if FEATURE_MQTT_TLS extern String mqtt_tls_last_errorstr; extern int32_t mqtt_tls_last_error; # ifdef ESP32 @@ -35,7 +35,7 @@ extern BearSSL::X509List mqtt_X509List; extern String mqtt_rootCA; extern String mqtt_fingerprint; -# endif // ifdef USE_MQTT_TLS +# endif // if FEATURE_MQTT_TLS extern PubSubClient MQTTclient; extern bool MQTTclient_should_reconnect; extern bool MQTTclient_must_send_LWT_connected; diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index ef914b0325..d63fbd94ac 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -125,7 +125,7 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { } void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description, TLS_types tls_type) { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS const String certFile = ControllerSettings.getCertificateFilename(tls_type); if (!certFile.isEmpty()) { @@ -181,7 +181,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS const int choice = static_cast(ControllerSettings.TLStype()); #define NR_MQTT_TLS_TYPES 4 const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { @@ -206,7 +206,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT)); addFormCheckBox(displayName, internalName, false, saveDisabled); addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); @@ -217,7 +217,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS /* const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; @@ -390,7 +390,7 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet break; case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS const int current = static_cast(ControllerSettings.TLStype()); const TLS_types tls_type = static_cast(getFormItemInt(internalName, current)); ControllerSettings.TLStype(tls_type); @@ -400,7 +400,7 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS if (isFormItemChecked(internalName)) { String fingerprint; if (GetTLSfingerprint(fingerprint)) { @@ -419,7 +419,7 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - #ifdef USE_MQTT_TLS + #if FEATURE_MQTT_TLS if (isFormItemChecked(internalName)) { String cacert; if (GetTLS_Certificate(cacert, true)) { diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 70857f854e..31dce95178 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -17,7 +17,7 @@ # include "../Globals/Protocol.h" # include "../Globals/Settings.h" -# ifdef USES_MQTT +# if FEATURE_MQTT # include "../Globals/MQTT.h" # endif @@ -332,7 +332,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (Protocol[ProtocolIndex].usesPort) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); } - #ifdef USES_MQTT + #if FEATURE_MQTT if (Protocol[ProtocolIndex].usesMQTT && Protocol[ProtocolIndex].usesTLS) { addControllerParameterForm(ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE); addFormNote(F("Default ports: MQTT: 1883 / MQTT TLS: 8883")); @@ -440,13 +440,13 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex } } { -#ifdef USES_MQTT +#if FEATURE_MQTT if (Protocol[ProtocolIndex].usesMQTT) { addFormSubHeader(F("Connection Status")); addRowLabel(F("MQTT Client Connected")); addEnabled(MQTTclient_connected); -#ifdef USE_MQTT_TLS +#if FEATURE_MQTT_TLS if (Protocol[ProtocolIndex].usesTLS) { addRowLabel(F("Last Error")); addHtmlInt(mqtt_tls_last_error); diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index 8ad0ef63c8..51c480738c 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -57,7 +57,7 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk - "-DUSE_MQTT_TLS", + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp82xx.py b/tools/pio/pre_custom_esp82xx.py index a404eb6a4b..1352a78f70 100644 --- a/tools/pio/pre_custom_esp82xx.py +++ b/tools/pio/pre_custom_esp82xx.py @@ -56,7 +56,7 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk - "-DUSE_MQTT_TLS", + "-DFEATURE_MQTT_TLS=0", # "-DFEATURE_MDNS=1", # "-DFEATURE_SD=1", "-DFEATURE_EXT_RTC=1", From 47233d55f8d34bb2eee6661c6c5a30faffd106ef Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 16 Oct 2022 18:08:58 +0200 Subject: [PATCH 047/367] [MQTT TLS] Resize certificate info textareas --- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 2 +- src/src/WebServer/ControllerPage.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp index 52e4065cb0..0388398295 100644 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp @@ -425,7 +425,7 @@ int ESPEasy_WiFiClientSecure::cert_to_pem(const mbedtls_x509_crt *crt, String& p 2* crt->raw.len; std::vector pem_buf; - pem_buf.resize(buffer_size); + pem_buf.resize(buffer_size, 0u); int ret = mbedtls_pem_write_buffer( pem_begin_crt.c_str(), pem_end_crt.c_str(), crt->raw.p, crt->raw.len, diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 31dce95178..3bbb03eb46 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -464,10 +464,14 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addFormSubHeader(F("Peer Certificate")); { - addRowLabel(F("Certificate Info")); - addHtml(F("")); + addFormTextArea( + F("Certificate Info"), + F("certinfo"), + mqtt_tls->getPeerCertificateInfo(), + 0, + 10, + 0, + true); } { String fingerprint; From 80c189d1df2f28ef06c17a6bb4f2955650fb2eb5 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 16 Oct 2022 23:05:52 +0200 Subject: [PATCH 048/367] [MQTT TLS] Fix layout controller page --- .../DataStructs/ControllerSettingsStruct.cpp | 8 ++--- src/src/Helpers/StringConverter.cpp | 11 ++++++ src/src/Helpers/StringConverter.h | 4 +++ src/src/WebServer/ControllerPage.cpp | 34 ++++++++++++------- src/src/WebServer/Markup.cpp | 14 ++++++-- 5 files changed, 50 insertions(+), 21 deletions(-) diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index b5b4ddbfd7..ce1d36884b 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -317,16 +317,12 @@ void ControllerSettingsStruct::useLocalSystemTime(bool value) TLS_types ControllerSettingsStruct::TLStype() const { // Store it in bits 12, 13, 14, 15 - const TLS_types tls_type = static_cast((VariousFlags >> 12) & 0xF); - return tls_type; + return static_cast(get4BitFromUL(VariousFlags, 12)); } void ControllerSettingsStruct::TLStype(TLS_types tls_type) { - const uint32_t mask = ~(0xF); - VariousFlags &= mask; // Clear the bits - const uint32_t tls_type_val = static_cast(tls_type) << 12; - VariousFlags |= tls_type_val; + set4BitToUL(VariousFlags, 12, static_cast(tls_type)); } String ControllerSettingsStruct::getCertificateFilename() const diff --git a/src/src/Helpers/StringConverter.cpp b/src/src/Helpers/StringConverter.cpp index 768fc07f91..aa7fdcd9b4 100644 --- a/src/src/Helpers/StringConverter.cpp +++ b/src/src/Helpers/StringConverter.cpp @@ -27,6 +27,17 @@ // -V::569 +unsigned int count_newlines(const String& str) +{ + unsigned int count = 0; + const size_t strlength = str.length(); + size_t pos = 0; + while (pos < strlength) { + if (str[pos] == '\n') ++count; + ++pos; + } + return count; +} /********************************************************************************************\ Convert a char string to integer diff --git a/src/src/Helpers/StringConverter.h b/src/src/Helpers/StringConverter.h index 2a9d1ce2c8..e0bea675a9 100644 --- a/src/src/Helpers/StringConverter.h +++ b/src/src/Helpers/StringConverter.h @@ -14,6 +14,10 @@ class IPAddress; // -V::569 + +unsigned int count_newlines(const String& str); + + /********************************************************************************************\ Concatenate using code which results in the smallest compiled code \*********************************************************************************************/ diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 3bbb03eb46..e24d234cde 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -465,12 +465,12 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex { addFormTextArea( - F("Certificate Info"), - F("certinfo"), + F("Certificate Info"), + F("certinfo"), mqtt_tls->getPeerCertificateInfo(), - 0, - 10, - 0, + -1, + -1, + -1, true); } { @@ -510,13 +510,23 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addRowLabel(label); } if (error == 0) { - addHtml(F("")); - - addHtml(F("")); + addTextArea( + F("peerCertInfo"), + mqtt_tls->getPeerCertificateInfo(chain), + -1, + -1, + -1, + true, + false); + + addTextArea( + F("pem"), + pem, + -1, + -1, + -1, + true, + false); } else { addHtmlInt(error); } diff --git a/src/src/WebServer/Markup.cpp b/src/src/WebServer/Markup.cpp index d26104ffca..5fbcebc54a 100644 --- a/src/src/WebServer/Markup.cpp +++ b/src/src/WebServer/Markup.cpp @@ -9,6 +9,7 @@ #include "../Helpers/Convert.h" #include "../Helpers/Hardware.h" +#include "../Helpers/StringConverter.h" #include "../Helpers/StringGenerator_GPIO.h" #include "../../ESPEasy_common.h" @@ -810,15 +811,22 @@ void addTextArea(const String & id, #endif // if FEATURE_TOOLTIPS ) { + if (rows < 0) { + rows = count_newlines(value) + 1; + } addHtml(F("",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0a;a++)for(i in o[a])n=o[a][i],o[a].hasOwnProperty(i)&&void 0!==n&&(e[i]=t.isPlainObject(n)?t.isPlainObject(e[i])?t.widget.extend({},e[i],n):t.widget.extend({},n):n);return e},t.widget.bridge=function(e,i){var n=i.prototype.widgetFullName||e;t.fn[e]=function(o){var a="string"==typeof o,r=s.call(arguments,1),h=this;return a?this.length||"instance"!==o?this.each(function(){var i,s=t.data(this,n);return"instance"===o?(h=s,!1):s?t.isFunction(s[o])&&"_"!==o.charAt(0)?(i=s[o].apply(s,r),i!==s&&void 0!==i?(h=i&&i.jquery?h.pushStack(i.get()):i,!1):void 0):t.error("no such method '"+o+"' for "+e+" widget instance"):t.error("cannot call methods on "+e+" prior to initialization; "+"attempted to call method '"+o+"'")}):h=void 0:(r.length&&(o=t.widget.extend.apply(null,[o].concat(r))),this.each(function(){var e=t.data(this,n);e?(e.option(o||{}),e._init&&e._init()):t.data(this,n,new i(o,this))})),h}},t.Widget=function(){},t.Widget._childConstructors=[],t.Widget.prototype={widgetName:"widget",widgetEventPrefix:"",defaultElement:"
",options:{classes:{},disabled:!1,create:null},_createWidget:function(e,s){s=t(s||this.defaultElement||this)[0],this.element=t(s),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=t(),this.hoverable=t(),this.focusable=t(),this.classesElementLookup={},s!==this&&(t.data(s,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===s&&this.destroy()}}),this.document=t(s.style?s.ownerDocument:s.document||s),this.window=t(this.document[0].defaultView||this.document[0].parentWindow)),this.options=t.widget.extend({},this.options,this._getCreateOptions(),e),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:t.noop,_create:t.noop,_init:t.noop,destroy:function(){var e=this;this._destroy(),t.each(this.classesElementLookup,function(t,i){e._removeClass(i,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:t.noop,widget:function(){return this.element},option:function(e,i){var s,n,o,a=e;if(0===arguments.length)return t.widget.extend({},this.options);if("string"==typeof e)if(a={},s=e.split("."),e=s.shift(),s.length){for(n=a[e]=t.widget.extend({},this.options[e]),o=0;s.length-1>o;o++)n[s[o]]=n[s[o]]||{},n=n[s[o]];if(e=s.pop(),1===arguments.length)return void 0===n[e]?null:n[e];n[e]=i}else{if(1===arguments.length)return void 0===this.options[e]?null:this.options[e];a[e]=i}return this._setOptions(a),this},_setOptions:function(t){var e;for(e in t)this._setOption(e,t[e]);return this},_setOption:function(t,e){return"classes"===t&&this._setOptionClasses(e),this.options[t]=e,"disabled"===t&&this._setOptionDisabled(e),this},_setOptionClasses:function(e){var i,s,n;for(i in e)n=this.classesElementLookup[i],e[i]!==this.options.classes[i]&&n&&n.length&&(s=t(n.get()),this._removeClass(n,i),s.addClass(this._classes({element:s,keys:i,classes:e,add:!0})))},_setOptionDisabled:function(t){this._toggleClass(this.widget(),this.widgetFullName+"-disabled",null,!!t),t&&(this._removeClass(this.hoverable,null,"ui-state-hover"),this._removeClass(this.focusable,null,"ui-state-focus"))},enable:function(){return this._setOptions({disabled:!1})},disable:function(){return this._setOptions({disabled:!0})},_classes:function(e){function i(i,o){var a,r;for(r=0;i.length>r;r++)a=n.classesElementLookup[i[r]]||t(),a=e.add?t(t.unique(a.get().concat(e.element.get()))):t(a.not(e.element).get()),n.classesElementLookup[i[r]]=a,s.push(i[r]),o&&e.classes[i[r]]&&s.push(e.classes[i[r]])}var s=[],n=this;return e=t.extend({element:this.element,classes:this.options.classes||{}},e),this._on(e.element,{remove:"_untrackClassesElement"}),e.keys&&i(e.keys.match(/\S+/g)||[],!0),e.extra&&i(e.extra.match(/\S+/g)||[]),s.join(" ")},_untrackClassesElement:function(e){var i=this;t.each(i.classesElementLookup,function(s,n){-1!==t.inArray(e.target,n)&&(i.classesElementLookup[s]=t(n.not(e.target).get()))})},_removeClass:function(t,e,i){return this._toggleClass(t,e,i,!1)},_addClass:function(t,e,i){return this._toggleClass(t,e,i,!0)},_toggleClass:function(t,e,i,s){s="boolean"==typeof s?s:i;var n="string"==typeof t||null===t,o={extra:n?e:i,keys:n?t:e,element:n?this.element:t,add:s};return o.element.toggleClass(this._classes(o),s),this},_on:function(e,i,s){var n,o=this;"boolean"!=typeof e&&(s=i,i=e,e=!1),s?(i=n=t(i),this.bindings=this.bindings.add(i)):(s=i,i=this.element,n=this.widget()),t.each(s,function(s,a){function r(){return e||o.options.disabled!==!0&&!t(this).hasClass("ui-state-disabled")?("string"==typeof a?o[a]:a).apply(o,arguments):void 0}"string"!=typeof a&&(r.guid=a.guid=a.guid||r.guid||t.guid++);var h=s.match(/^([\w:-]*)\s*(.*)$/),l=h[1]+o.eventNamespace,c=h[2];c?n.on(l,c,r):i.on(l,r)})},_off:function(e,i){i=(i||"").split(" ").join(this.eventNamespace+" ")+this.eventNamespace,e.off(i).off(i),this.bindings=t(this.bindings.not(e).get()),this.focusable=t(this.focusable.not(e).get()),this.hoverable=t(this.hoverable.not(e).get())},_delay:function(t,e){function i(){return("string"==typeof t?s[t]:t).apply(s,arguments)}var s=this;return setTimeout(i,e||0)},_hoverable:function(e){this.hoverable=this.hoverable.add(e),this._on(e,{mouseenter:function(e){this._addClass(t(e.currentTarget),null,"ui-state-hover")},mouseleave:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-hover")}})},_focusable:function(e){this.focusable=this.focusable.add(e),this._on(e,{focusin:function(e){this._addClass(t(e.currentTarget),null,"ui-state-focus")},focusout:function(e){this._removeClass(t(e.currentTarget),null,"ui-state-focus")}})},_trigger:function(e,i,s){var n,o,a=this.options[e];if(s=s||{},i=t.Event(i),i.type=(e===this.widgetEventPrefix?e:this.widgetEventPrefix+e).toLowerCase(),i.target=this.element[0],o=i.originalEvent)for(n in o)n in i||(i[n]=o[n]);return this.element.trigger(i,s),!(t.isFunction(a)&&a.apply(this.element[0],[i].concat(s))===!1||i.isDefaultPrevented())}},t.each({show:"fadeIn",hide:"fadeOut"},function(e,i){t.Widget.prototype["_"+e]=function(s,n,o){"string"==typeof n&&(n={effect:n});var a,r=n?n===!0||"number"==typeof n?i:n.effect||i:e;n=n||{},"number"==typeof n&&(n={duration:n}),a=!t.isEmptyObject(n),n.complete=o,n.delay&&s.delay(n.delay),a&&t.effects&&t.effects.effect[r]?s[e](n):r!==e&&s[r]?s[r](n.duration,n.easing,o):s.queue(function(i){t(this)[e](),o&&o.call(s[0]),i()})}}),t.widget,function(){function e(t,e,i){return[parseFloat(t[0])*(u.test(t[0])?e/100:1),parseFloat(t[1])*(u.test(t[1])?i/100:1)]}function i(e,i){return parseInt(t.css(e,i),10)||0}function s(e){var i=e[0];return 9===i.nodeType?{width:e.width(),height:e.height(),offset:{top:0,left:0}}:t.isWindow(i)?{width:e.width(),height:e.height(),offset:{top:e.scrollTop(),left:e.scrollLeft()}}:i.preventDefault?{width:0,height:0,offset:{top:i.pageY,left:i.pageX}}:{width:e.outerWidth(),height:e.outerHeight(),offset:e.offset()}}var n,o=Math.max,a=Math.abs,r=/left|center|right/,h=/top|center|bottom/,l=/[\+\-]\d+(\.[\d]+)?%?/,c=/^\w+/,u=/%$/,d=t.fn.position;t.position={scrollbarWidth:function(){if(void 0!==n)return n;var e,i,s=t("
"),o=s.children()[0];return t("body").append(s),e=o.offsetWidth,s.css("overflow","scroll"),i=o.offsetWidth,e===i&&(i=s[0].clientWidth),s.remove(),n=e-i},getScrollInfo:function(e){var i=e.isWindow||e.isDocument?"":e.element.css("overflow-x"),s=e.isWindow||e.isDocument?"":e.element.css("overflow-y"),n="scroll"===i||"auto"===i&&e.widthi?"left":e>0?"right":"center",vertical:0>r?"top":s>0?"bottom":"middle"};l>p&&p>a(e+i)&&(u.horizontal="center"),c>f&&f>a(s+r)&&(u.vertical="middle"),u.important=o(a(e),a(i))>o(a(s),a(r))?"horizontal":"vertical",n.using.call(this,t,u)}),h.offset(t.extend(D,{using:r}))})},t.ui.position={fit:{left:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollLeft:s.offset.left,a=s.width,r=t.left-e.collisionPosition.marginLeft,h=n-r,l=r+e.collisionWidth-a-n;e.collisionWidth>a?h>0&&0>=l?(i=t.left+h+e.collisionWidth-a-n,t.left+=h-i):t.left=l>0&&0>=h?n:h>l?n+a-e.collisionWidth:n:h>0?t.left+=h:l>0?t.left-=l:t.left=o(t.left-r,t.left)},top:function(t,e){var i,s=e.within,n=s.isWindow?s.scrollTop:s.offset.top,a=e.within.height,r=t.top-e.collisionPosition.marginTop,h=n-r,l=r+e.collisionHeight-a-n;e.collisionHeight>a?h>0&&0>=l?(i=t.top+h+e.collisionHeight-a-n,t.top+=h-i):t.top=l>0&&0>=h?n:h>l?n+a-e.collisionHeight:n:h>0?t.top+=h:l>0?t.top-=l:t.top=o(t.top-r,t.top)}},flip:{left:function(t,e){var i,s,n=e.within,o=n.offset.left+n.scrollLeft,r=n.width,h=n.isWindow?n.scrollLeft:n.offset.left,l=t.left-e.collisionPosition.marginLeft,c=l-h,u=l+e.collisionWidth-r-h,d="left"===e.my[0]?-e.elemWidth:"right"===e.my[0]?e.elemWidth:0,p="left"===e.at[0]?e.targetWidth:"right"===e.at[0]?-e.targetWidth:0,f=-2*e.offset[0];0>c?(i=t.left+d+p+f+e.collisionWidth-r-o,(0>i||a(c)>i)&&(t.left+=d+p+f)):u>0&&(s=t.left-e.collisionPosition.marginLeft+d+p+f-h,(s>0||u>a(s))&&(t.left+=d+p+f))},top:function(t,e){var i,s,n=e.within,o=n.offset.top+n.scrollTop,r=n.height,h=n.isWindow?n.scrollTop:n.offset.top,l=t.top-e.collisionPosition.marginTop,c=l-h,u=l+e.collisionHeight-r-h,d="top"===e.my[1],p=d?-e.elemHeight:"bottom"===e.my[1]?e.elemHeight:0,f="top"===e.at[1]?e.targetHeight:"bottom"===e.at[1]?-e.targetHeight:0,m=-2*e.offset[1];0>c?(s=t.top+p+f+m+e.collisionHeight-r-o,(0>s||a(c)>s)&&(t.top+=p+f+m)):u>0&&(i=t.top-e.collisionPosition.marginTop+p+f+m-h,(i>0||u>a(i))&&(t.top+=p+f+m))}},flipfit:{left:function(){t.ui.position.flip.left.apply(this,arguments),t.ui.position.fit.left.apply(this,arguments)},top:function(){t.ui.position.flip.top.apply(this,arguments),t.ui.position.fit.top.apply(this,arguments)}}}}(),t.ui.position,t.extend(t.expr[":"],{data:t.expr.createPseudo?t.expr.createPseudo(function(e){return function(i){return!!t.data(i,e)}}):function(e,i,s){return!!t.data(e,s[3])}}),t.fn.extend({disableSelection:function(){var t="onselectstart"in document.createElement("div")?"selectstart":"mousedown";return function(){return this.on(t+".ui-disableSelection",function(t){t.preventDefault()})}}(),enableSelection:function(){return this.off(".ui-disableSelection")}}),t.ui.focusable=function(i,s){var n,o,a,r,h,l=i.nodeName.toLowerCase();return"area"===l?(n=i.parentNode,o=n.name,i.href&&o&&"map"===n.nodeName.toLowerCase()?(a=t("img[usemap='#"+o+"']"),a.length>0&&a.is(":visible")):!1):(/^(input|select|textarea|button|object)$/.test(l)?(r=!i.disabled,r&&(h=t(i).closest("fieldset")[0],h&&(r=!h.disabled))):r="a"===l?i.href||s:s,r&&t(i).is(":visible")&&e(t(i)))},t.extend(t.expr[":"],{focusable:function(e){return t.ui.focusable(e,null!=t.attr(e,"tabindex"))}}),t.ui.focusable,t.fn.form=function(){return"string"==typeof this[0].form?this.closest("form"):t(this[0].form)},t.ui.formResetMixin={_formResetHandler:function(){var e=t(this);setTimeout(function(){var i=e.data("ui-form-reset-instances");t.each(i,function(){this.refresh()})})},_bindFormResetHandler:function(){if(this.form=this.element.form(),this.form.length){var t=this.form.data("ui-form-reset-instances")||[];t.length||this.form.on("reset.ui-form-reset",this._formResetHandler),t.push(this),this.form.data("ui-form-reset-instances",t)}},_unbindFormResetHandler:function(){if(this.form.length){var e=this.form.data("ui-form-reset-instances");e.splice(t.inArray(this,e),1),e.length?this.form.data("ui-form-reset-instances",e):this.form.removeData("ui-form-reset-instances").off("reset.ui-form-reset")}}},"1.7"===t.fn.jquery.substring(0,3)&&(t.each(["Width","Height"],function(e,i){function s(e,i,s,o){return t.each(n,function(){i-=parseFloat(t.css(e,"padding"+this))||0,s&&(i-=parseFloat(t.css(e,"border"+this+"Width"))||0),o&&(i-=parseFloat(t.css(e,"margin"+this))||0)}),i}var n="Width"===i?["Left","Right"]:["Top","Bottom"],o=i.toLowerCase(),a={innerWidth:t.fn.innerWidth,innerHeight:t.fn.innerHeight,outerWidth:t.fn.outerWidth,outerHeight:t.fn.outerHeight};t.fn["inner"+i]=function(e){return void 0===e?a["inner"+i].call(this):this.each(function(){t(this).css(o,s(this,e)+"px")})},t.fn["outer"+i]=function(e,n){return"number"!=typeof e?a["outer"+i].call(this,e):this.each(function(){t(this).css(o,s(this,e,!0,n)+"px")})}}),t.fn.addBack=function(t){return this.add(null==t?this.prevObject:this.prevObject.filter(t))}),t.ui.keyCode={BACKSPACE:8,COMMA:188,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,LEFT:37,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SPACE:32,TAB:9,UP:38},t.ui.escapeSelector=function(){var t=/([!"#$%&'()*+,./:;<=>?@[\]^`{|}~])/g;return function(e){return e.replace(t,"\\$1")}}(),t.fn.labels=function(){var e,i,s,n,o;return this[0].labels&&this[0].labels.length?this.pushStack(this[0].labels):(n=this.eq(0).parents("label"),s=this.attr("id"),s&&(e=this.eq(0).parents().last(),o=e.add(e.length?e.siblings():this.siblings()),i="label[for='"+t.ui.escapeSelector(s)+"']",n=n.add(o.find(i).addBack(i))),this.pushStack(n))},t.fn.scrollParent=function(e){var i=this.css("position"),s="absolute"===i,n=e?/(auto|scroll|hidden)/:/(auto|scroll)/,o=this.parents().filter(function(){var e=t(this);return s&&"static"===e.css("position")?!1:n.test(e.css("overflow")+e.css("overflow-y")+e.css("overflow-x"))}).eq(0);return"fixed"!==i&&o.length?o:t(this[0].ownerDocument||document)},t.extend(t.expr[":"],{tabbable:function(e){var i=t.attr(e,"tabindex"),s=null!=i;return(!s||i>=0)&&t.ui.focusable(e,s)}}),t.fn.extend({uniqueId:function(){var t=0;return function(){return this.each(function(){this.id||(this.id="ui-id-"+ ++t)})}}(),removeUniqueId:function(){return this.each(function(){/^ui-id-\d+$/.test(this.id)&&t(this).removeAttr("id")})}}),t.ui.ie=!!/msie [\w.]+/.exec(navigator.userAgent.toLowerCase());var n=!1;t(document).on("mouseup",function(){n=!1}),t.widget("ui.mouse",{version:"1.12.1",options:{cancel:"input, textarea, button, select, option",distance:1,delay:0},_mouseInit:function(){var e=this;this.element.on("mousedown."+this.widgetName,function(t){return e._mouseDown(t)}).on("click."+this.widgetName,function(i){return!0===t.data(i.target,e.widgetName+".preventClickEvent")?(t.removeData(i.target,e.widgetName+".preventClickEvent"),i.stopImmediatePropagation(),!1):void 0}),this.started=!1},_mouseDestroy:function(){this.element.off("."+this.widgetName),this._mouseMoveDelegate&&this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate)},_mouseDown:function(e){if(!n){this._mouseMoved=!1,this._mouseStarted&&this._mouseUp(e),this._mouseDownEvent=e;var i=this,s=1===e.which,o="string"==typeof this.options.cancel&&e.target.nodeName?t(e.target).closest(this.options.cancel).length:!1;return s&&!o&&this._mouseCapture(e)?(this.mouseDelayMet=!this.options.delay,this.mouseDelayMet||(this._mouseDelayTimer=setTimeout(function(){i.mouseDelayMet=!0},this.options.delay)),this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(e)!==!1,!this._mouseStarted)?(e.preventDefault(),!0):(!0===t.data(e.target,this.widgetName+".preventClickEvent")&&t.removeData(e.target,this.widgetName+".preventClickEvent"),this._mouseMoveDelegate=function(t){return i._mouseMove(t)},this._mouseUpDelegate=function(t){return i._mouseUp(t)},this.document.on("mousemove."+this.widgetName,this._mouseMoveDelegate).on("mouseup."+this.widgetName,this._mouseUpDelegate),e.preventDefault(),n=!0,!0)):!0}},_mouseMove:function(e){if(this._mouseMoved){if(t.ui.ie&&(!document.documentMode||9>document.documentMode)&&!e.button)return this._mouseUp(e);if(!e.which)if(e.originalEvent.altKey||e.originalEvent.ctrlKey||e.originalEvent.metaKey||e.originalEvent.shiftKey)this.ignoreMissingWhich=!0;else if(!this.ignoreMissingWhich)return this._mouseUp(e)}return(e.which||e.button)&&(this._mouseMoved=!0),this._mouseStarted?(this._mouseDrag(e),e.preventDefault()):(this._mouseDistanceMet(e)&&this._mouseDelayMet(e)&&(this._mouseStarted=this._mouseStart(this._mouseDownEvent,e)!==!1,this._mouseStarted?this._mouseDrag(e):this._mouseUp(e)),!this._mouseStarted)},_mouseUp:function(e){this.document.off("mousemove."+this.widgetName,this._mouseMoveDelegate).off("mouseup."+this.widgetName,this._mouseUpDelegate),this._mouseStarted&&(this._mouseStarted=!1,e.target===this._mouseDownEvent.target&&t.data(e.target,this.widgetName+".preventClickEvent",!0),this._mouseStop(e)),this._mouseDelayTimer&&(clearTimeout(this._mouseDelayTimer),delete this._mouseDelayTimer),this.ignoreMissingWhich=!1,n=!1,e.preventDefault()},_mouseDistanceMet:function(t){return Math.max(Math.abs(this._mouseDownEvent.pageX-t.pageX),Math.abs(this._mouseDownEvent.pageY-t.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return!0}}),t.ui.plugin={add:function(e,i,s){var n,o=t.ui[e].prototype;for(n in s)o.plugins[n]=o.plugins[n]||[],o.plugins[n].push([i,s[n]])},call:function(t,e,i,s){var n,o=t.plugins[e];if(o&&(s||t.element[0].parentNode&&11!==t.element[0].parentNode.nodeType))for(n=0;o.length>n;n++)t.options[o[n][0]]&&o[n][1].apply(t.element,i)}},t.widget("ui.resizable",t.ui.mouse,{version:"1.12.1",widgetEventPrefix:"resize",options:{alsoResize:!1,animate:!1,animateDuration:"slow",animateEasing:"swing",aspectRatio:!1,autoHide:!1,classes:{"ui-resizable-se":"ui-icon ui-icon-gripsmall-diagonal-se"},containment:!1,ghost:!1,grid:!1,handles:"e,s,se",helper:!1,maxHeight:null,maxWidth:null,minHeight:10,minWidth:10,zIndex:90,resize:null,start:null,stop:null},_num:function(t){return parseFloat(t)||0},_isNumber:function(t){return!isNaN(parseFloat(t))},_hasScroll:function(e,i){if("hidden"===t(e).css("overflow"))return!1;var s=i&&"left"===i?"scrollLeft":"scrollTop",n=!1;return e[s]>0?!0:(e[s]=1,n=e[s]>0,e[s]=0,n)},_create:function(){var e,i=this.options,s=this;this._addClass("ui-resizable"),t.extend(this,{_aspectRatio:!!i.aspectRatio,aspectRatio:i.aspectRatio,originalElement:this.element,_proportionallyResizeElements:[],_helper:i.helper||i.ghost||i.animate?i.helper||"ui-resizable-helper":null}),this.element[0].nodeName.match(/^(canvas|textarea|input|select|button|img)$/i)&&(this.element.wrap(t("
").css({position:this.element.css("position"),width:this.element.outerWidth(),height:this.element.outerHeight(),top:this.element.css("top"),left:this.element.css("left")})),this.element=this.element.parent().data("ui-resizable",this.element.resizable("instance")),this.elementIsWrapper=!0,e={marginTop:this.originalElement.css("marginTop"),marginRight:this.originalElement.css("marginRight"),marginBottom:this.originalElement.css("marginBottom"),marginLeft:this.originalElement.css("marginLeft")},this.element.css(e),this.originalElement.css("margin",0),this.originalResizeStyle=this.originalElement.css("resize"),this.originalElement.css("resize","none"),this._proportionallyResizeElements.push(this.originalElement.css({position:"static",zoom:1,display:"block"})),this.originalElement.css(e),this._proportionallyResize()),this._setupHandles(),i.autoHide&&t(this.element).on("mouseenter",function(){i.disabled||(s._removeClass("ui-resizable-autohide"),s._handles.show())}).on("mouseleave",function(){i.disabled||s.resizing||(s._addClass("ui-resizable-autohide"),s._handles.hide())}),this._mouseInit()},_destroy:function(){this._mouseDestroy();var e,i=function(e){t(e).removeData("resizable").removeData("ui-resizable").off(".resizable").find(".ui-resizable-handle").remove()};return this.elementIsWrapper&&(i(this.element),e=this.element,this.originalElement.css({position:e.css("position"),width:e.outerWidth(),height:e.outerHeight(),top:e.css("top"),left:e.css("left")}).insertAfter(e),e.remove()),this.originalElement.css("resize",this.originalResizeStyle),i(this.originalElement),this},_setOption:function(t,e){switch(this._super(t,e),t){case"handles":this._removeHandles(),this._setupHandles();break;default:}},_setupHandles:function(){var e,i,s,n,o,a=this.options,r=this;if(this.handles=a.handles||(t(".ui-resizable-handle",this.element).length?{n:".ui-resizable-n",e:".ui-resizable-e",s:".ui-resizable-s",w:".ui-resizable-w",se:".ui-resizable-se",sw:".ui-resizable-sw",ne:".ui-resizable-ne",nw:".ui-resizable-nw"}:"e,s,se"),this._handles=t(),this.handles.constructor===String)for("all"===this.handles&&(this.handles="n,e,s,w,se,sw,ne,nw"),s=this.handles.split(","),this.handles={},i=0;s.length>i;i++)e=t.trim(s[i]),n="ui-resizable-"+e,o=t("
"),this._addClass(o,"ui-resizable-handle "+n),o.css({zIndex:a.zIndex}),this.handles[e]=".ui-resizable-"+e,this.element.append(o);this._renderAxis=function(e){var i,s,n,o;e=e||this.element;for(i in this.handles)this.handles[i].constructor===String?this.handles[i]=this.element.children(this.handles[i]).first().show():(this.handles[i].jquery||this.handles[i].nodeType)&&(this.handles[i]=t(this.handles[i]),this._on(this.handles[i],{mousedown:r._mouseDown})),this.elementIsWrapper&&this.originalElement[0].nodeName.match(/^(textarea|input|select|button)$/i)&&(s=t(this.handles[i],this.element),o=/sw|ne|nw|se|n|s/.test(i)?s.outerHeight():s.outerWidth(),n=["padding",/ne|nw|n/.test(i)?"Top":/se|sw|s/.test(i)?"Bottom":/^e$/.test(i)?"Right":"Left"].join(""),e.css(n,o),this._proportionallyResize()),this._handles=this._handles.add(this.handles[i])},this._renderAxis(this.element),this._handles=this._handles.add(this.element.find(".ui-resizable-handle")),this._handles.disableSelection(),this._handles.on("mouseover",function(){r.resizing||(this.className&&(o=this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i)),r.axis=o&&o[1]?o[1]:"se")}),a.autoHide&&(this._handles.hide(),this._addClass("ui-resizable-autohide"))},_removeHandles:function(){this._handles.remove()},_mouseCapture:function(e){var i,s,n=!1;for(i in this.handles)s=t(this.handles[i])[0],(s===e.target||t.contains(s,e.target))&&(n=!0);return!this.options.disabled&&n},_mouseStart:function(e){var i,s,n,o=this.options,a=this.element;return this.resizing=!0,this._renderProxy(),i=this._num(this.helper.css("left")),s=this._num(this.helper.css("top")),o.containment&&(i+=t(o.containment).scrollLeft()||0,s+=t(o.containment).scrollTop()||0),this.offset=this.helper.offset(),this.position={left:i,top:s},this.size=this._helper?{width:this.helper.width(),height:this.helper.height()}:{width:a.width(),height:a.height()},this.originalSize=this._helper?{width:a.outerWidth(),height:a.outerHeight()}:{width:a.width(),height:a.height()},this.sizeDiff={width:a.outerWidth()-a.width(),height:a.outerHeight()-a.height()},this.originalPosition={left:i,top:s},this.originalMousePosition={left:e.pageX,top:e.pageY},this.aspectRatio="number"==typeof o.aspectRatio?o.aspectRatio:this.originalSize.width/this.originalSize.height||1,n=t(".ui-resizable-"+this.axis).css("cursor"),t("body").css("cursor","auto"===n?this.axis+"-resize":n),this._addClass("ui-resizable-resizing"),this._propagate("start",e),!0},_mouseDrag:function(e){var i,s,n=this.originalMousePosition,o=this.axis,a=e.pageX-n.left||0,r=e.pageY-n.top||0,h=this._change[o];return this._updatePrevProperties(),h?(i=h.apply(this,[e,a,r]),this._updateVirtualBoundaries(e.shiftKey),(this._aspectRatio||e.shiftKey)&&(i=this._updateRatio(i,e)),i=this._respectSize(i,e),this._updateCache(i),this._propagate("resize",e),s=this._applyChanges(),!this._helper&&this._proportionallyResizeElements.length&&this._proportionallyResize(),t.isEmptyObject(s)||(this._updatePrevProperties(),this._trigger("resize",e,this.ui()),this._applyChanges()),!1):!1},_mouseStop:function(e){this.resizing=!1;var i,s,n,o,a,r,h,l=this.options,c=this;return this._helper&&(i=this._proportionallyResizeElements,s=i.length&&/textarea/i.test(i[0].nodeName),n=s&&this._hasScroll(i[0],"left")?0:c.sizeDiff.height,o=s?0:c.sizeDiff.width,a={width:c.helper.width()-o,height:c.helper.height()-n},r=parseFloat(c.element.css("left"))+(c.position.left-c.originalPosition.left)||null,h=parseFloat(c.element.css("top"))+(c.position.top-c.originalPosition.top)||null,l.animate||this.element.css(t.extend(a,{top:h,left:r})),c.helper.height(c.size.height),c.helper.width(c.size.width),this._helper&&!l.animate&&this._proportionallyResize()),t("body").css("cursor","auto"),this._removeClass("ui-resizable-resizing"),this._propagate("stop",e),this._helper&&this.helper.remove(),!1},_updatePrevProperties:function(){this.prevPosition={top:this.position.top,left:this.position.left},this.prevSize={width:this.size.width,height:this.size.height}},_applyChanges:function(){var t={};return this.position.top!==this.prevPosition.top&&(t.top=this.position.top+"px"),this.position.left!==this.prevPosition.left&&(t.left=this.position.left+"px"),this.size.width!==this.prevSize.width&&(t.width=this.size.width+"px"),this.size.height!==this.prevSize.height&&(t.height=this.size.height+"px"),this.helper.css(t),t},_updateVirtualBoundaries:function(t){var e,i,s,n,o,a=this.options;o={minWidth:this._isNumber(a.minWidth)?a.minWidth:0,maxWidth:this._isNumber(a.maxWidth)?a.maxWidth:1/0,minHeight:this._isNumber(a.minHeight)?a.minHeight:0,maxHeight:this._isNumber(a.maxHeight)?a.maxHeight:1/0},(this._aspectRatio||t)&&(e=o.minHeight*this.aspectRatio,s=o.minWidth/this.aspectRatio,i=o.maxHeight*this.aspectRatio,n=o.maxWidth/this.aspectRatio,e>o.minWidth&&(o.minWidth=e),s>o.minHeight&&(o.minHeight=s),o.maxWidth>i&&(o.maxWidth=i),o.maxHeight>n&&(o.maxHeight=n)),this._vBoundaries=o},_updateCache:function(t){this.offset=this.helper.offset(),this._isNumber(t.left)&&(this.position.left=t.left),this._isNumber(t.top)&&(this.position.top=t.top),this._isNumber(t.height)&&(this.size.height=t.height),this._isNumber(t.width)&&(this.size.width=t.width)},_updateRatio:function(t){var e=this.position,i=this.size,s=this.axis;return this._isNumber(t.height)?t.width=t.height*this.aspectRatio:this._isNumber(t.width)&&(t.height=t.width/this.aspectRatio),"sw"===s&&(t.left=e.left+(i.width-t.width),t.top=null),"nw"===s&&(t.top=e.top+(i.height-t.height),t.left=e.left+(i.width-t.width)),t},_respectSize:function(t){var e=this._vBoundaries,i=this.axis,s=this._isNumber(t.width)&&e.maxWidth&&e.maxWidtht.width,a=this._isNumber(t.height)&&e.minHeight&&e.minHeight>t.height,r=this.originalPosition.left+this.originalSize.width,h=this.originalPosition.top+this.originalSize.height,l=/sw|nw|w/.test(i),c=/nw|ne|n/.test(i);return o&&(t.width=e.minWidth),a&&(t.height=e.minHeight),s&&(t.width=e.maxWidth),n&&(t.height=e.maxHeight),o&&l&&(t.left=r-e.minWidth),s&&l&&(t.left=r-e.maxWidth),a&&c&&(t.top=h-e.minHeight),n&&c&&(t.top=h-e.maxHeight),t.width||t.height||t.left||!t.top?t.width||t.height||t.top||!t.left||(t.left=null):t.top=null,t},_getPaddingPlusBorderDimensions:function(t){for(var e=0,i=[],s=[t.css("borderTopWidth"),t.css("borderRightWidth"),t.css("borderBottomWidth"),t.css("borderLeftWidth")],n=[t.css("paddingTop"),t.css("paddingRight"),t.css("paddingBottom"),t.css("paddingLeft")];4>e;e++)i[e]=parseFloat(s[e])||0,i[e]+=parseFloat(n[e])||0;return{height:i[0]+i[2],width:i[1]+i[3]}},_proportionallyResize:function(){if(this._proportionallyResizeElements.length)for(var t,e=0,i=this.helper||this.element;this._proportionallyResizeElements.length>e;e++)t=this._proportionallyResizeElements[e],this.outerDimensions||(this.outerDimensions=this._getPaddingPlusBorderDimensions(t)),t.css({height:i.height()-this.outerDimensions.height||0,width:i.width()-this.outerDimensions.width||0})},_renderProxy:function(){var e=this.element,i=this.options;this.elementOffset=e.offset(),this._helper?(this.helper=this.helper||t("
"),this._addClass(this.helper,this._helper),this.helper.css({width:this.element.outerWidth(),height:this.element.outerHeight(),position:"absolute",left:this.elementOffset.left+"px",top:this.elementOffset.top+"px",zIndex:++i.zIndex}),this.helper.appendTo("body").disableSelection()):this.helper=this.element +},_change:{e:function(t,e){return{width:this.originalSize.width+e}},w:function(t,e){var i=this.originalSize,s=this.originalPosition;return{left:s.left+e,width:i.width-e}},n:function(t,e,i){var s=this.originalSize,n=this.originalPosition;return{top:n.top+i,height:s.height-i}},s:function(t,e,i){return{height:this.originalSize.height+i}},se:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},sw:function(e,i,s){return t.extend(this._change.s.apply(this,arguments),this._change.w.apply(this,[e,i,s]))},ne:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.e.apply(this,[e,i,s]))},nw:function(e,i,s){return t.extend(this._change.n.apply(this,arguments),this._change.w.apply(this,[e,i,s]))}},_propagate:function(e,i){t.ui.plugin.call(this,e,[i,this.ui()]),"resize"!==e&&this._trigger(e,i,this.ui())},plugins:{},ui:function(){return{originalElement:this.originalElement,element:this.element,helper:this.helper,position:this.position,size:this.size,originalSize:this.originalSize,originalPosition:this.originalPosition}}}),t.ui.plugin.add("resizable","animate",{stop:function(e){var i=t(this).resizable("instance"),s=i.options,n=i._proportionallyResizeElements,o=n.length&&/textarea/i.test(n[0].nodeName),a=o&&i._hasScroll(n[0],"left")?0:i.sizeDiff.height,r=o?0:i.sizeDiff.width,h={width:i.size.width-r,height:i.size.height-a},l=parseFloat(i.element.css("left"))+(i.position.left-i.originalPosition.left)||null,c=parseFloat(i.element.css("top"))+(i.position.top-i.originalPosition.top)||null;i.element.animate(t.extend(h,c&&l?{top:c,left:l}:{}),{duration:s.animateDuration,easing:s.animateEasing,step:function(){var s={width:parseFloat(i.element.css("width")),height:parseFloat(i.element.css("height")),top:parseFloat(i.element.css("top")),left:parseFloat(i.element.css("left"))};n&&n.length&&t(n[0]).css({width:s.width,height:s.height}),i._updateCache(s),i._propagate("resize",e)}})}}),t.ui.plugin.add("resizable","containment",{start:function(){var e,i,s,n,o,a,r,h=t(this).resizable("instance"),l=h.options,c=h.element,u=l.containment,d=u instanceof t?u.get(0):/parent/.test(u)?c.parent().get(0):u;d&&(h.containerElement=t(d),/document/.test(u)||u===document?(h.containerOffset={left:0,top:0},h.containerPosition={left:0,top:0},h.parentData={element:t(document),left:0,top:0,width:t(document).width(),height:t(document).height()||document.body.parentNode.scrollHeight}):(e=t(d),i=[],t(["Top","Right","Left","Bottom"]).each(function(t,s){i[t]=h._num(e.css("padding"+s))}),h.containerOffset=e.offset(),h.containerPosition=e.position(),h.containerSize={height:e.innerHeight()-i[3],width:e.innerWidth()-i[1]},s=h.containerOffset,n=h.containerSize.height,o=h.containerSize.width,a=h._hasScroll(d,"left")?d.scrollWidth:o,r=h._hasScroll(d)?d.scrollHeight:n,h.parentData={element:d,left:s.left,top:s.top,width:a,height:r}))},resize:function(e){var i,s,n,o,a=t(this).resizable("instance"),r=a.options,h=a.containerOffset,l=a.position,c=a._aspectRatio||e.shiftKey,u={top:0,left:0},d=a.containerElement,p=!0;d[0]!==document&&/static/.test(d.css("position"))&&(u=h),l.left<(a._helper?h.left:0)&&(a.size.width=a.size.width+(a._helper?a.position.left-h.left:a.position.left-u.left),c&&(a.size.height=a.size.width/a.aspectRatio,p=!1),a.position.left=r.helper?h.left:0),l.top<(a._helper?h.top:0)&&(a.size.height=a.size.height+(a._helper?a.position.top-h.top:a.position.top),c&&(a.size.width=a.size.height*a.aspectRatio,p=!1),a.position.top=a._helper?h.top:0),n=a.containerElement.get(0)===a.element.parent().get(0),o=/relative|absolute/.test(a.containerElement.css("position")),n&&o?(a.offset.left=a.parentData.left+a.position.left,a.offset.top=a.parentData.top+a.position.top):(a.offset.left=a.element.offset().left,a.offset.top=a.element.offset().top),i=Math.abs(a.sizeDiff.width+(a._helper?a.offset.left-u.left:a.offset.left-h.left)),s=Math.abs(a.sizeDiff.height+(a._helper?a.offset.top-u.top:a.offset.top-h.top)),i+a.size.width>=a.parentData.width&&(a.size.width=a.parentData.width-i,c&&(a.size.height=a.size.width/a.aspectRatio,p=!1)),s+a.size.height>=a.parentData.height&&(a.size.height=a.parentData.height-s,c&&(a.size.width=a.size.height*a.aspectRatio,p=!1)),p||(a.position.left=a.prevPosition.left,a.position.top=a.prevPosition.top,a.size.width=a.prevSize.width,a.size.height=a.prevSize.height)},stop:function(){var e=t(this).resizable("instance"),i=e.options,s=e.containerOffset,n=e.containerPosition,o=e.containerElement,a=t(e.helper),r=a.offset(),h=a.outerWidth()-e.sizeDiff.width,l=a.outerHeight()-e.sizeDiff.height;e._helper&&!i.animate&&/relative/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l}),e._helper&&!i.animate&&/static/.test(o.css("position"))&&t(this).css({left:r.left-n.left-s.left,width:h,height:l})}}),t.ui.plugin.add("resizable","alsoResize",{start:function(){var e=t(this).resizable("instance"),i=e.options;t(i.alsoResize).each(function(){var e=t(this);e.data("ui-resizable-alsoresize",{width:parseFloat(e.width()),height:parseFloat(e.height()),left:parseFloat(e.css("left")),top:parseFloat(e.css("top"))})})},resize:function(e,i){var s=t(this).resizable("instance"),n=s.options,o=s.originalSize,a=s.originalPosition,r={height:s.size.height-o.height||0,width:s.size.width-o.width||0,top:s.position.top-a.top||0,left:s.position.left-a.left||0};t(n.alsoResize).each(function(){var e=t(this),s=t(this).data("ui-resizable-alsoresize"),n={},o=e.parents(i.originalElement[0]).length?["width","height"]:["width","height","top","left"];t.each(o,function(t,e){var i=(s[e]||0)+(r[e]||0);i&&i>=0&&(n[e]=i||null)}),e.css(n)})},stop:function(){t(this).removeData("ui-resizable-alsoresize")}}),t.ui.plugin.add("resizable","ghost",{start:function(){var e=t(this).resizable("instance"),i=e.size;e.ghost=e.originalElement.clone(),e.ghost.css({opacity:.25,display:"block",position:"relative",height:i.height,width:i.width,margin:0,left:0,top:0}),e._addClass(e.ghost,"ui-resizable-ghost"),t.uiBackCompat!==!1&&"string"==typeof e.options.ghost&&e.ghost.addClass(this.options.ghost),e.ghost.appendTo(e.helper)},resize:function(){var e=t(this).resizable("instance");e.ghost&&e.ghost.css({position:"relative",height:e.size.height,width:e.size.width})},stop:function(){var e=t(this).resizable("instance");e.ghost&&e.helper&&e.helper.get(0).removeChild(e.ghost.get(0))}}),t.ui.plugin.add("resizable","grid",{resize:function(){var e,i=t(this).resizable("instance"),s=i.options,n=i.size,o=i.originalSize,a=i.originalPosition,r=i.axis,h="number"==typeof s.grid?[s.grid,s.grid]:s.grid,l=h[0]||1,c=h[1]||1,u=Math.round((n.width-o.width)/l)*l,d=Math.round((n.height-o.height)/c)*c,p=o.width+u,f=o.height+d,m=s.maxWidth&&p>s.maxWidth,g=s.maxHeight&&f>s.maxHeight,_=s.minWidth&&s.minWidth>p,v=s.minHeight&&s.minHeight>f;s.grid=h,_&&(p+=l),v&&(f+=c),m&&(p-=l),g&&(f-=c),/^(se|s|e)$/.test(r)?(i.size.width=p,i.size.height=f):/^(ne)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.top=a.top-d):/^(sw)$/.test(r)?(i.size.width=p,i.size.height=f,i.position.left=a.left-u):((0>=f-c||0>=p-l)&&(e=i._getPaddingPlusBorderDimensions(this)),f-c>0?(i.size.height=f,i.position.top=a.top-d):(f=c-e.height,i.size.height=f,i.position.top=a.top+o.height-f),p-l>0?(i.size.width=p,i.position.left=a.left-u):(p=l-e.width,i.size.width=p,i.position.left=a.left+o.width-p))}}),t.ui.resizable});/** + * Copyright (c) 2007 Ariel Flesler - aflesler ○ gmail • com | https://github.com/flesler + * Licensed under MIT + * @author Ariel Flesler + * @version 2.1.2 + */ +;(function(f){"use strict";"function"===typeof define&&define.amd?define(["jquery"],f):"undefined"!==typeof module&&module.exports?module.exports=f(require("jquery")):f(jQuery)})(function($){"use strict";function n(a){return!a.nodeName||-1!==$.inArray(a.nodeName.toLowerCase(),["iframe","#document","html","body"])}function h(a){return $.isFunction(a)||$.isPlainObject(a)?a:{top:a,left:a}}var p=$.scrollTo=function(a,d,b){return $(window).scrollTo(a,d,b)};p.defaults={axis:"xy",duration:0,limit:!0};$.fn.scrollTo=function(a,d,b){"object"=== typeof d&&(b=d,d=0);"function"===typeof b&&(b={onAfter:b});"max"===a&&(a=9E9);b=$.extend({},p.defaults,b);d=d||b.duration;var u=b.queue&&1=f[g]?0:Math.min(f[g],n));!a&&1-1){targetElements.on(evt+EVENT_NAMESPACE,function elementToggle(event){$.powerTip.toggle(this,event)})}else{targetElements.on(evt+EVENT_NAMESPACE,function elementOpen(event){$.powerTip.show(this,event)})}});$.each(options.closeEvents,function(idx,evt){if($.inArray(evt,options.openEvents)<0){targetElements.on(evt+EVENT_NAMESPACE,function elementClose(event){$.powerTip.hide(this,!isMouseEvent(event))})}});targetElements.on("keydown"+EVENT_NAMESPACE,function elementKeyDown(event){if(event.keyCode===27){$.powerTip.hide(this,true)}})}return targetElements};$.fn.powerTip.defaults={fadeInTime:200,fadeOutTime:100,followMouse:false,popupId:"powerTip",popupClass:null,intentSensitivity:7,intentPollInterval:100,closeDelay:100,placement:"n",smartPlacement:false,offset:10,mouseOnToPopup:false,manual:false,openEvents:["mouseenter","focus"],closeEvents:["mouseleave","blur"]};$.fn.powerTip.smartPlacementLists={n:["n","ne","nw","s"],e:["e","ne","se","w","nw","sw","n","s","e"],s:["s","se","sw","n"],w:["w","nw","sw","e","ne","se","n","s","w"],nw:["nw","w","sw","n","s","se","nw"],ne:["ne","e","se","n","s","sw","ne"],sw:["sw","w","nw","s","n","ne","sw"],se:["se","e","ne","s","n","nw","se"],"nw-alt":["nw-alt","n","ne-alt","sw-alt","s","se-alt","w","e"],"ne-alt":["ne-alt","n","nw-alt","se-alt","s","sw-alt","e","w"],"sw-alt":["sw-alt","s","se-alt","nw-alt","n","ne-alt","w","e"],"se-alt":["se-alt","s","sw-alt","ne-alt","n","nw-alt","e","w"]};$.powerTip={show:function apiShowTip(element,event){if(isMouseEvent(event)){trackMouse(event);session.previousX=event.pageX;session.previousY=event.pageY;$(element).data(DATA_DISPLAYCONTROLLER).show()}else{$(element).first().data(DATA_DISPLAYCONTROLLER).show(true,true)}return element},reposition:function apiResetPosition(element){$(element).first().data(DATA_DISPLAYCONTROLLER).resetPosition();return element},hide:function apiCloseTip(element,immediate){var displayController;immediate=element?immediate:true;if(element){displayController=$(element).first().data(DATA_DISPLAYCONTROLLER)}else if(session.activeHover){displayController=session.activeHover.data(DATA_DISPLAYCONTROLLER)}if(displayController){displayController.hide(immediate)}return element},toggle:function apiToggle(element,event){if(session.activeHover&&session.activeHover.is(element)){$.powerTip.hide(element,!isMouseEvent(event))}else{$.powerTip.show(element,event)}return element}};$.powerTip.showTip=$.powerTip.show;$.powerTip.closeTip=$.powerTip.hide;function CSSCoordinates(){var me=this;me.top="auto";me.left="auto";me.right="auto";me.bottom="auto";me.set=function(property,value){if($.isNumeric(value)){me[property]=Math.round(value)}}}function DisplayController(element,options,tipController){var hoverTimer=null,myCloseDelay=null;function openTooltip(immediate,forceOpen){cancelTimer();if(!element.data(DATA_HASACTIVEHOVER)){if(!immediate){session.tipOpenImminent=true;hoverTimer=setTimeout(function intentDelay(){hoverTimer=null;checkForIntent()},options.intentPollInterval)}else{if(forceOpen){element.data(DATA_FORCEDOPEN,true)}closeAnyDelayed();tipController.showTip(element)}}else{cancelClose()}}function closeTooltip(disableDelay){if(myCloseDelay){myCloseDelay=session.closeDelayTimeout=clearTimeout(myCloseDelay);session.delayInProgress=false}cancelTimer();session.tipOpenImminent=false;if(element.data(DATA_HASACTIVEHOVER)){element.data(DATA_FORCEDOPEN,false);if(!disableDelay){session.delayInProgress=true;session.closeDelayTimeout=setTimeout(function closeDelay(){session.closeDelayTimeout=null;tipController.hideTip(element);session.delayInProgress=false;myCloseDelay=null},options.closeDelay);myCloseDelay=session.closeDelayTimeout}else{tipController.hideTip(element)}}}function checkForIntent(){var xDifference=Math.abs(session.previousX-session.currentX),yDifference=Math.abs(session.previousY-session.currentY),totalDifference=xDifference+yDifference;if(totalDifference",{id:options.popupId});if($body.length===0){$body=$("body")}$body.append(tipElement);session.tooltips=session.tooltips?session.tooltips.add(tipElement):tipElement}if(options.followMouse){if(!tipElement.data(DATA_HASMOUSEMOVE)){$document.on("mousemove"+EVENT_NAMESPACE,positionTipOnCursor);$window.on("scroll"+EVENT_NAMESPACE,positionTipOnCursor);tipElement.data(DATA_HASMOUSEMOVE,true)}}function beginShowTip(element){element.data(DATA_HASACTIVEHOVER,true);tipElement.queue(function queueTipInit(next){showTip(element);next()})}function showTip(element){var tipContent;if(!element.data(DATA_HASACTIVEHOVER)){return}if(session.isTipOpen){if(!session.isClosing){hideTip(session.activeHover)}tipElement.delay(100).queue(function queueTipAgain(next){showTip(element);next()});return}element.trigger("powerTipPreRender");tipContent=getTooltipContent(element);if(tipContent){tipElement.empty().append(tipContent)}else{return}element.trigger("powerTipRender");session.activeHover=element;session.isTipOpen=true;tipElement.data(DATA_MOUSEONTOTIP,options.mouseOnToPopup);tipElement.addClass(options.popupClass);if(!options.followMouse||element.data(DATA_FORCEDOPEN)){positionTipOnElement(element);session.isFixedTipOpen=true}else{positionTipOnCursor()}if(!element.data(DATA_FORCEDOPEN)&&!options.followMouse){$document.on("click"+EVENT_NAMESPACE,function documentClick(event){var target=event.target;if(target!==element[0]){if(options.mouseOnToPopup){if(target!==tipElement[0]&&!$.contains(tipElement[0],target)){$.powerTip.hide()}}else{$.powerTip.hide()}}})}if(options.mouseOnToPopup&&!options.manual){tipElement.on("mouseenter"+EVENT_NAMESPACE,function tipMouseEnter(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).cancel()}});tipElement.on("mouseleave"+EVENT_NAMESPACE,function tipMouseLeave(){if(session.activeHover){session.activeHover.data(DATA_DISPLAYCONTROLLER).hide()}})}tipElement.fadeIn(options.fadeInTime,function fadeInCallback(){if(!session.desyncTimeout){session.desyncTimeout=setInterval(closeDesyncedTip,500)}element.trigger("powerTipOpen")})}function hideTip(element){session.isClosing=true;session.isTipOpen=false;session.desyncTimeout=clearInterval(session.desyncTimeout);element.data(DATA_HASACTIVEHOVER,false);element.data(DATA_FORCEDOPEN,false);$document.off("click"+EVENT_NAMESPACE);tipElement.off(EVENT_NAMESPACE);tipElement.fadeOut(options.fadeOutTime,function fadeOutCallback(){var coords=new CSSCoordinates;session.activeHover=null;session.isClosing=false;session.isFixedTipOpen=false;tipElement.removeClass();coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);tipElement.css(coords);element.trigger("powerTipClose")})}function positionTipOnCursor(){var tipWidth,tipHeight,coords,collisions,collisionCount;if(!session.isFixedTipOpen&&(session.isTipOpen||session.tipOpenImminent&&tipElement.data(DATA_HASMOUSEMOVE))){tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=new CSSCoordinates;coords.set("top",session.currentY+options.offset);coords.set("left",session.currentX+options.offset);collisions=getViewportCollisions(coords,tipWidth,tipHeight);if(collisions!==Collision.none){collisionCount=countFlags(collisions);if(collisionCount===1){if(collisions===Collision.right){coords.set("left",session.scrollLeft+session.windowWidth-tipWidth)}else if(collisions===Collision.bottom){coords.set("top",session.scrollTop+session.windowHeight-tipHeight)}}else{coords.set("left",session.currentX-tipWidth-options.offset);coords.set("top",session.currentY-tipHeight-options.offset)}}tipElement.css(coords)}}function positionTipOnElement(element){var priorityList,finalPlacement;if(options.smartPlacement||options.followMouse&&element.data(DATA_FORCEDOPEN)){priorityList=$.fn.powerTip.smartPlacementLists[options.placement];$.each(priorityList,function(idx,pos){var collisions=getViewportCollisions(placeTooltip(element,pos),tipElement.outerWidth(),tipElement.outerHeight());finalPlacement=pos;return collisions!==Collision.none})}else{placeTooltip(element,options.placement);finalPlacement=options.placement}tipElement.removeClass("w nw sw e ne se n s w se-alt sw-alt ne-alt nw-alt");tipElement.addClass(finalPlacement)}function placeTooltip(element,placement){var iterationCount=0,tipWidth,tipHeight,coords=new CSSCoordinates;coords.set("top",0);coords.set("left",0);tipElement.css(coords);do{tipWidth=tipElement.outerWidth();tipHeight=tipElement.outerHeight();coords=placementCalculator.compute(element,placement,tipWidth,tipHeight,options.offset);tipElement.css(coords)}while(++iterationCount<=5&&(tipWidth!==tipElement.outerWidth()||tipHeight!==tipElement.outerHeight()));return coords}function closeDesyncedTip(){var isDesynced=false,hasDesyncableCloseEvent=$.grep(["mouseleave","mouseout","blur","focusout"],function(eventType){return $.inArray(eventType,options.closeEvents)!==-1}).length>0;if(session.isTipOpen&&!session.isClosing&&!session.delayInProgress&&hasDesyncableCloseEvent){if(session.activeHover.data(DATA_HASACTIVEHOVER)===false||session.activeHover.is(":disabled")){isDesynced=true}else if(!isMouseOver(session.activeHover)&&!session.activeHover.is(":focus")&&!session.activeHover.data(DATA_FORCEDOPEN)){if(tipElement.data(DATA_MOUSEONTOTIP)){if(!isMouseOver(tipElement)){isDesynced=true}}else{isDesynced=true}}if(isDesynced){hideTip(session.activeHover)}}}this.showTip=beginShowTip;this.hideTip=hideTip;this.resetPosition=positionTipOnElement}function isSvgElement(element){return Boolean(window.SVGElement&&element[0]instanceof SVGElement)}function isMouseEvent(event){return Boolean(event&&$.inArray(event.type,MOUSE_EVENTS)>-1&&typeof event.pageX==="number")}function initTracking(){if(!session.mouseTrackingActive){session.mouseTrackingActive=true;getViewportDimensions();$(getViewportDimensions);$document.on("mousemove"+EVENT_NAMESPACE,trackMouse);$window.on("resize"+EVENT_NAMESPACE,trackResize);$window.on("scroll"+EVENT_NAMESPACE,trackScroll)}}function getViewportDimensions(){session.scrollLeft=$window.scrollLeft();session.scrollTop=$window.scrollTop();session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackResize(){session.windowWidth=$window.width();session.windowHeight=$window.height()}function trackScroll(){var x=$window.scrollLeft(),y=$window.scrollTop();if(x!==session.scrollLeft){session.currentX+=x-session.scrollLeft;session.scrollLeft=x}if(y!==session.scrollTop){session.currentY+=y-session.scrollTop;session.scrollTop=y}}function trackMouse(event){session.currentX=event.pageX;session.currentY=event.pageY}function isMouseOver(element){var elementPosition=element.offset(),elementBox=element[0].getBoundingClientRect(),elementWidth=elementBox.right-elementBox.left,elementHeight=elementBox.bottom-elementBox.top;return session.currentX>=elementPosition.left&&session.currentX<=elementPosition.left+elementWidth&&session.currentY>=elementPosition.top&&session.currentY<=elementPosition.top+elementHeight}function getTooltipContent(element){var tipText=element.data(DATA_POWERTIP),tipObject=element.data(DATA_POWERTIPJQ),tipTarget=element.data(DATA_POWERTIPTARGET),targetElement,content;if(tipText){if($.isFunction(tipText)){tipText=tipText.call(element[0])}content=tipText}else if(tipObject){if($.isFunction(tipObject)){tipObject=tipObject.call(element[0])}if(tipObject.length>0){content=tipObject.clone(true,true)}}else if(tipTarget){targetElement=$("#"+tipTarget);if(targetElement.length>0){content=targetElement.html()}}return content}function getViewportCollisions(coords,elementWidth,elementHeight){var viewportTop=session.scrollTop,viewportLeft=session.scrollLeft,viewportBottom=viewportTop+session.windowHeight,viewportRight=viewportLeft+session.windowWidth,collisions=Collision.none;if(coords.topviewportBottom||Math.abs(coords.bottom-session.windowHeight)>viewportBottom){collisions|=Collision.bottom}if(coords.leftviewportRight){collisions|=Collision.left}if(coords.left+elementWidth>viewportRight||coords.right1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);/*! SmartMenus jQuery Plugin - v1.1.0 - September 17, 2017 + * http://www.smartmenus.org/ + * Copyright Vasil Dinkov, Vadikom Web Ltd. http://vadikom.com; Licensed MIT */(function(t){"function"==typeof define&&define.amd?define(["jquery"],t):"object"==typeof module&&"object"==typeof module.exports?module.exports=t(require("jquery")):t(jQuery)})(function($){function initMouseDetection(t){var e=".smartmenus_mouse";if(mouseDetectionEnabled||t)mouseDetectionEnabled&&t&&($(document).off(e),mouseDetectionEnabled=!1);else{var i=!0,s=null,o={mousemove:function(t){var e={x:t.pageX,y:t.pageY,timeStamp:(new Date).getTime()};if(s){var o=Math.abs(s.x-e.x),a=Math.abs(s.y-e.y);if((o>0||a>0)&&2>=o&&2>=a&&300>=e.timeStamp-s.timeStamp&&(mouse=!0,i)){var n=$(t.target).closest("a");n.is("a")&&$.each(menuTrees,function(){return $.contains(this.$root[0],n[0])?(this.itemEnter({currentTarget:n[0]}),!1):void 0}),i=!1}}s=e}};o[touchEvents?"touchstart":"pointerover pointermove pointerout MSPointerOver MSPointerMove MSPointerOut"]=function(t){isTouchEvent(t.originalEvent)&&(mouse=!1)},$(document).on(getEventsNS(o,e)),mouseDetectionEnabled=!0}}function isTouchEvent(t){return!/^(4|mouse)$/.test(t.pointerType)}function getEventsNS(t,e){e||(e="");var i={};for(var s in t)i[s.split(" ").join(e+" ")+e]=t[s];return i}var menuTrees=[],mouse=!1,touchEvents="ontouchstart"in window,mouseDetectionEnabled=!1,requestAnimationFrame=window.requestAnimationFrame||function(t){return setTimeout(t,1e3/60)},cancelAnimationFrame=window.cancelAnimationFrame||function(t){clearTimeout(t)},canAnimate=!!$.fn.animate;return $.SmartMenus=function(t,e){this.$root=$(t),this.opts=e,this.rootId="",this.accessIdPrefix="",this.$subArrow=null,this.activatedItems=[],this.visibleSubMenus=[],this.showTimeout=0,this.hideTimeout=0,this.scrollTimeout=0,this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.idInc=0,this.$firstLink=null,this.$firstSub=null,this.disabled=!1,this.$disableOverlay=null,this.$touchScrollingSub=null,this.cssTransforms3d="perspective"in t.style||"webkitPerspective"in t.style,this.wasCollapsible=!1,this.init()},$.extend($.SmartMenus,{hideAll:function(){$.each(menuTrees,function(){this.menuHideAll()})},destroy:function(){for(;menuTrees.length;)menuTrees[0].destroy();initMouseDetection(!0)},prototype:{init:function(t){var e=this;if(!t){menuTrees.push(this),this.rootId=((new Date).getTime()+Math.random()+"").replace(/\D/g,""),this.accessIdPrefix="sm-"+this.rootId+"-",this.$root.hasClass("sm-rtl")&&(this.opts.rightToLeftSubMenus=!0);var i=".smartmenus";this.$root.data("smartmenus",this).attr("data-smartmenus-id",this.rootId).dataSM("level",1).on(getEventsNS({"mouseover focusin":$.proxy(this.rootOver,this),"mouseout focusout":$.proxy(this.rootOut,this),keydown:$.proxy(this.rootKeyDown,this)},i)).on(getEventsNS({mouseenter:$.proxy(this.itemEnter,this),mouseleave:$.proxy(this.itemLeave,this),mousedown:$.proxy(this.itemDown,this),focus:$.proxy(this.itemFocus,this),blur:$.proxy(this.itemBlur,this),click:$.proxy(this.itemClick,this)},i),"a"),i+=this.rootId,this.opts.hideOnClick&&$(document).on(getEventsNS({touchstart:$.proxy(this.docTouchStart,this),touchmove:$.proxy(this.docTouchMove,this),touchend:$.proxy(this.docTouchEnd,this),click:$.proxy(this.docClick,this)},i)),$(window).on(getEventsNS({"resize orientationchange":$.proxy(this.winResize,this)},i)),this.opts.subIndicators&&(this.$subArrow=$("").addClass("sub-arrow"),this.opts.subIndicatorsText&&this.$subArrow.html(this.opts.subIndicatorsText)),initMouseDetection()}if(this.$firstSub=this.$root.find("ul").each(function(){e.menuInit($(this))}).eq(0),this.$firstLink=this.$root.find("a").eq(0),this.opts.markCurrentItem){var s=/(index|default)\.[^#\?\/]*/i,o=/#.*/,a=window.location.href.replace(s,""),n=a.replace(o,"");this.$root.find("a").each(function(){var t=this.href.replace(s,""),i=$(this);(t==a||t==n)&&(i.addClass("current"),e.opts.markCurrentTree&&i.parentsUntil("[data-smartmenus-id]","ul").each(function(){$(this).dataSM("parent-a").addClass("current")}))})}this.wasCollapsible=this.isCollapsible()},destroy:function(t){if(!t){var e=".smartmenus";this.$root.removeData("smartmenus").removeAttr("data-smartmenus-id").removeDataSM("level").off(e),e+=this.rootId,$(document).off(e),$(window).off(e),this.opts.subIndicators&&(this.$subArrow=null)}this.menuHideAll();var i=this;this.$root.find("ul").each(function(){var t=$(this);t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.dataSM("shown-before")&&((i.opts.subMenusMinWidth||i.opts.subMenusMaxWidth)&&t.css({width:"",minWidth:"",maxWidth:""}).removeClass("sm-nowrap"),t.dataSM("scroll-arrows")&&t.dataSM("scroll-arrows").remove(),t.css({zIndex:"",top:"",left:"",marginLeft:"",marginTop:"",display:""})),0==(t.attr("id")||"").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeDataSM("in-mega").removeDataSM("shown-before").removeDataSM("scroll-arrows").removeDataSM("parent-a").removeDataSM("level").removeDataSM("beforefirstshowfired").removeAttr("role").removeAttr("aria-hidden").removeAttr("aria-labelledby").removeAttr("aria-expanded"),this.$root.find("a.has-submenu").each(function(){var t=$(this);0==t.attr("id").indexOf(i.accessIdPrefix)&&t.removeAttr("id")}).removeClass("has-submenu").removeDataSM("sub").removeAttr("aria-haspopup").removeAttr("aria-controls").removeAttr("aria-expanded").closest("li").removeDataSM("sub"),this.opts.subIndicators&&this.$root.find("span.sub-arrow").remove(),this.opts.markCurrentItem&&this.$root.find("a.current").removeClass("current"),t||(this.$root=null,this.$firstLink=null,this.$firstSub=null,this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),menuTrees.splice($.inArray(this,menuTrees),1))},disable:function(t){if(!this.disabled){if(this.menuHideAll(),!t&&!this.opts.isPopup&&this.$root.is(":visible")){var e=this.$root.offset();this.$disableOverlay=$('
').css({position:"absolute",top:e.top,left:e.left,width:this.$root.outerWidth(),height:this.$root.outerHeight(),zIndex:this.getStartZIndex(!0),opacity:0}).appendTo(document.body)}this.disabled=!0}},docClick:function(t){return this.$touchScrollingSub?(this.$touchScrollingSub=null,void 0):((this.visibleSubMenus.length&&!$.contains(this.$root[0],t.target)||$(t.target).closest("a").length)&&this.menuHideAll(),void 0)},docTouchEnd:function(){if(this.lastTouch){if(!(!this.visibleSubMenus.length||void 0!==this.lastTouch.x2&&this.lastTouch.x1!=this.lastTouch.x2||void 0!==this.lastTouch.y2&&this.lastTouch.y1!=this.lastTouch.y2||this.lastTouch.target&&$.contains(this.$root[0],this.lastTouch.target))){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var t=this;this.hideTimeout=setTimeout(function(){t.menuHideAll()},350)}this.lastTouch=null}},docTouchMove:function(t){if(this.lastTouch){var e=t.originalEvent.touches[0];this.lastTouch.x2=e.pageX,this.lastTouch.y2=e.pageY}},docTouchStart:function(t){var e=t.originalEvent.touches[0];this.lastTouch={x1:e.pageX,y1:e.pageY,target:e.target}},enable:function(){this.disabled&&(this.$disableOverlay&&(this.$disableOverlay.remove(),this.$disableOverlay=null),this.disabled=!1)},getClosestMenu:function(t){for(var e=$(t).closest("ul");e.dataSM("in-mega");)e=e.parent().closest("ul");return e[0]||null},getHeight:function(t){return this.getOffset(t,!0)},getOffset:function(t,e){var i;"none"==t.css("display")&&(i={position:t[0].style.position,visibility:t[0].style.visibility},t.css({position:"absolute",visibility:"hidden"}).show());var s=t[0].getBoundingClientRect&&t[0].getBoundingClientRect(),o=s&&(e?s.height||s.bottom-s.top:s.width||s.right-s.left);return o||0===o||(o=e?t[0].offsetHeight:t[0].offsetWidth),i&&t.hide().css(i),o},getStartZIndex:function(t){var e=parseInt(this[t?"$root":"$firstSub"].css("z-index"));return!t&&isNaN(e)&&(e=parseInt(this.$root.css("z-index"))),isNaN(e)?1:e},getTouchPoint:function(t){return t.touches&&t.touches[0]||t.changedTouches&&t.changedTouches[0]||t},getViewport:function(t){var e=t?"Height":"Width",i=document.documentElement["client"+e],s=window["inner"+e];return s&&(i=Math.min(i,s)),i},getViewportHeight:function(){return this.getViewport(!0)},getViewportWidth:function(){return this.getViewport()},getWidth:function(t){return this.getOffset(t)},handleEvents:function(){return!this.disabled&&this.isCSSOn()},handleItemEvents:function(t){return this.handleEvents()&&!this.isLinkInMegaMenu(t)},isCollapsible:function(){return"static"==this.$firstSub.css("position")},isCSSOn:function(){return"inline"!=this.$firstLink.css("display")},isFixed:function(){var t="fixed"==this.$root.css("position");return t||this.$root.parentsUntil("body").each(function(){return"fixed"==$(this).css("position")?(t=!0,!1):void 0}),t},isLinkInMegaMenu:function(t){return $(this.getClosestMenu(t[0])).hasClass("mega-menu")},isTouchMode:function(){return!mouse||this.opts.noMouseOver||this.isCollapsible()},itemActivate:function(t,e){var i=t.closest("ul"),s=i.dataSM("level");if(s>1&&(!this.activatedItems[s-2]||this.activatedItems[s-2][0]!=i.dataSM("parent-a")[0])){var o=this;$(i.parentsUntil("[data-smartmenus-id]","ul").get().reverse()).add(i).each(function(){o.itemActivate($(this).dataSM("parent-a"))})}if((!this.isCollapsible()||e)&&this.menuHideSubMenus(this.activatedItems[s-1]&&this.activatedItems[s-1][0]==t[0]?s:s-1),this.activatedItems[s-1]=t,this.$root.triggerHandler("activate.smapi",t[0])!==!1){var a=t.dataSM("sub");a&&(this.isTouchMode()||!this.opts.showOnClick||this.clickActivated)&&this.menuShow(a)}},itemBlur:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&this.$root.triggerHandler("blur.smapi",e[0])},itemClick:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(this.$touchScrollingSub&&this.$touchScrollingSub[0]==e.closest("ul")[0])return this.$touchScrollingSub=null,t.stopPropagation(),!1;if(this.$root.triggerHandler("click.smapi",e[0])===!1)return!1;var i=$(t.target).is(".sub-arrow"),s=e.dataSM("sub"),o=s?2==s.dataSM("level"):!1,a=this.isCollapsible(),n=/toggle$/.test(this.opts.collapsibleBehavior),r=/link$/.test(this.opts.collapsibleBehavior),h=/^accordion/.test(this.opts.collapsibleBehavior);if(s&&!s.is(":visible")){if((!r||!a||i)&&(this.opts.showOnClick&&o&&(this.clickActivated=!0),this.itemActivate(e,h),s.is(":visible")))return this.focusActivated=!0,!1}else if(a&&(n||i))return this.itemActivate(e,h),this.menuHide(s),n&&(this.focusActivated=!1),!1;return this.opts.showOnClick&&o||e.hasClass("disabled")||this.$root.triggerHandler("select.smapi",e[0])===!1?!1:void 0}},itemDown:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&e.dataSM("mousedown",!0)},itemEnter:function(t){var e=$(t.currentTarget);if(this.handleItemEvents(e)){if(!this.isTouchMode()){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);var i=this;this.showTimeout=setTimeout(function(){i.itemActivate(e)},this.opts.showOnClick&&1==e.closest("ul").dataSM("level")?1:this.opts.showTimeout)}this.$root.triggerHandler("mouseenter.smapi",e[0])}},itemFocus:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(!this.focusActivated||this.isTouchMode()&&e.dataSM("mousedown")||this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0]==e[0]||this.itemActivate(e,!0),this.$root.triggerHandler("focus.smapi",e[0]))},itemLeave:function(t){var e=$(t.currentTarget);this.handleItemEvents(e)&&(this.isTouchMode()||(e[0].blur(),this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0)),e.removeDataSM("mousedown"),this.$root.triggerHandler("mouseleave.smapi",e[0]))},menuHide:function(t){if(this.$root.triggerHandler("beforehide.smapi",t[0])!==!1&&(canAnimate&&t.stop(!0,!0),"none"!=t.css("display"))){var e=function(){t.css("z-index","")};this.isCollapsible()?canAnimate&&this.opts.collapsibleHideFunction?this.opts.collapsibleHideFunction.call(this,t,e):t.hide(this.opts.collapsibleHideDuration,e):canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,t,e):t.hide(this.opts.hideDuration,e),t.dataSM("scroll")&&(this.menuScrollStop(t),t.css({"touch-action":"","-ms-touch-action":"","-webkit-transform":"",transform:""}).off(".smartmenus_scroll").removeDataSM("scroll").dataSM("scroll-arrows").hide()),t.dataSM("parent-a").removeClass("highlighted").attr("aria-expanded","false"),t.attr({"aria-expanded":"false","aria-hidden":"true"});var i=t.dataSM("level");this.activatedItems.splice(i-1,1),this.visibleSubMenus.splice($.inArray(t,this.visibleSubMenus),1),this.$root.triggerHandler("hide.smapi",t[0])}},menuHideAll:function(){this.showTimeout&&(clearTimeout(this.showTimeout),this.showTimeout=0);for(var t=this.opts.isPopup?1:0,e=this.visibleSubMenus.length-1;e>=t;e--)this.menuHide(this.visibleSubMenus[e]);this.opts.isPopup&&(canAnimate&&this.$root.stop(!0,!0),this.$root.is(":visible")&&(canAnimate&&this.opts.hideFunction?this.opts.hideFunction.call(this,this.$root):this.$root.hide(this.opts.hideDuration))),this.activatedItems=[],this.visibleSubMenus=[],this.clickActivated=!1,this.focusActivated=!1,this.zIndexInc=0,this.$root.triggerHandler("hideAll.smapi")},menuHideSubMenus:function(t){for(var e=this.activatedItems.length-1;e>=t;e--){var i=this.activatedItems[e].dataSM("sub");i&&this.menuHide(i)}},menuInit:function(t){if(!t.dataSM("in-mega")){t.hasClass("mega-menu")&&t.find("ul").dataSM("in-mega",!0);for(var e=2,i=t[0];(i=i.parentNode.parentNode)!=this.$root[0];)e++;var s=t.prevAll("a").eq(-1);s.length||(s=t.prevAll().find("a").eq(-1)),s.addClass("has-submenu").dataSM("sub",t),t.dataSM("parent-a",s).dataSM("level",e).parent().dataSM("sub",t);var o=s.attr("id")||this.accessIdPrefix+ ++this.idInc,a=t.attr("id")||this.accessIdPrefix+ ++this.idInc;s.attr({id:o,"aria-haspopup":"true","aria-controls":a,"aria-expanded":"false"}),t.attr({id:a,role:"group","aria-hidden":"true","aria-labelledby":o,"aria-expanded":"false"}),this.opts.subIndicators&&s[this.opts.subIndicatorsPos](this.$subArrow.clone())}},menuPosition:function(t){var e,i,s=t.dataSM("parent-a"),o=s.closest("li"),a=o.parent(),n=t.dataSM("level"),r=this.getWidth(t),h=this.getHeight(t),u=s.offset(),l=u.left,c=u.top,d=this.getWidth(s),m=this.getHeight(s),p=$(window),f=p.scrollLeft(),v=p.scrollTop(),b=this.getViewportWidth(),S=this.getViewportHeight(),g=a.parent().is("[data-sm-horizontal-sub]")||2==n&&!a.hasClass("sm-vertical"),M=this.opts.rightToLeftSubMenus&&!o.is("[data-sm-reverse]")||!this.opts.rightToLeftSubMenus&&o.is("[data-sm-reverse]"),w=2==n?this.opts.mainMenuSubOffsetX:this.opts.subMenusSubOffsetX,T=2==n?this.opts.mainMenuSubOffsetY:this.opts.subMenusSubOffsetY;if(g?(e=M?d-r-w:w,i=this.opts.bottomToTopSubMenus?-h-T:m+T):(e=M?w-r:d-w,i=this.opts.bottomToTopSubMenus?m-T-h:T),this.opts.keepInViewport){var y=l+e,I=c+i;if(M&&f>y?e=g?f-y+e:d-w:!M&&y+r>f+b&&(e=g?f+b-r-y+e:w-r),g||(S>h&&I+h>v+S?i+=v+S-h-I:(h>=S||v>I)&&(i+=v-I)),g&&(I+h>v+S+.49||v>I)||!g&&h>S+.49){var x=this;t.dataSM("scroll-arrows")||t.dataSM("scroll-arrows",$([$('')[0],$('')[0]]).on({mouseenter:function(){t.dataSM("scroll").up=$(this).hasClass("scroll-up"),x.menuScroll(t)},mouseleave:function(e){x.menuScrollStop(t),x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(t){t.preventDefault()}}).insertAfter(t));var A=".smartmenus_scroll";if(t.dataSM("scroll",{y:this.cssTransforms3d?0:i-m,step:1,itemH:m,subH:h,arrowDownH:this.getHeight(t.dataSM("scroll-arrows").eq(1))}).on(getEventsNS({mouseover:function(e){x.menuScrollOver(t,e)},mouseout:function(e){x.menuScrollOut(t,e)},"mousewheel DOMMouseScroll":function(e){x.menuScrollMousewheel(t,e)}},A)).dataSM("scroll-arrows").css({top:"auto",left:"0",marginLeft:e+(parseInt(t.css("border-left-width"))||0),width:r-(parseInt(t.css("border-left-width"))||0)-(parseInt(t.css("border-right-width"))||0),zIndex:t.css("z-index")}).eq(g&&this.opts.bottomToTopSubMenus?0:1).show(),this.isFixed()){var C={};C[touchEvents?"touchstart touchmove touchend":"pointerdown pointermove pointerup MSPointerDown MSPointerMove MSPointerUp"]=function(e){x.menuScrollTouch(t,e)},t.css({"touch-action":"none","-ms-touch-action":"none"}).on(getEventsNS(C,A))}}}t.css({top:"auto",left:"0",marginLeft:e,marginTop:i-m})},menuScroll:function(t,e,i){var s,o=t.dataSM("scroll"),a=t.dataSM("scroll-arrows"),n=o.up?o.upEnd:o.downEnd;if(!e&&o.momentum){if(o.momentum*=.92,s=o.momentum,.5>s)return this.menuScrollStop(t),void 0}else s=i||(e||!this.opts.scrollAccelerate?this.opts.scrollStep:Math.floor(o.step));var r=t.dataSM("level");if(this.activatedItems[r-1]&&this.activatedItems[r-1].dataSM("sub")&&this.activatedItems[r-1].dataSM("sub").is(":visible")&&this.menuHideSubMenus(r-1),o.y=o.up&&o.y>=n||!o.up&&n>=o.y?o.y:Math.abs(n-o.y)>s?o.y+(o.up?s:-s):n,t.css(this.cssTransforms3d?{"-webkit-transform":"translate3d(0, "+o.y+"px, 0)",transform:"translate3d(0, "+o.y+"px, 0)"}:{marginTop:o.y}),mouse&&(o.up&&o.y>o.downEnd||!o.up&&o.y0;t.dataSM("scroll-arrows").eq(i?0:1).is(":visible")&&(t.dataSM("scroll").up=i,this.menuScroll(t,!0))}e.preventDefault()},menuScrollOut:function(t,e){mouse&&(/^scroll-(up|down)/.test((e.relatedTarget||"").className)||(t[0]==e.relatedTarget||$.contains(t[0],e.relatedTarget))&&this.getClosestMenu(e.relatedTarget)==t[0]||t.dataSM("scroll-arrows").css("visibility","hidden"))},menuScrollOver:function(t,e){if(mouse&&!/^scroll-(up|down)/.test(e.target.className)&&this.getClosestMenu(e.target)==t[0]){this.menuScrollRefreshData(t);var i=t.dataSM("scroll"),s=$(window).scrollTop()-t.dataSM("parent-a").offset().top-i.itemH;t.dataSM("scroll-arrows").eq(0).css("margin-top",s).end().eq(1).css("margin-top",s+this.getViewportHeight()-i.arrowDownH).end().css("visibility","visible")}},menuScrollRefreshData:function(t){var e=t.dataSM("scroll"),i=$(window).scrollTop()-t.dataSM("parent-a").offset().top-e.itemH;this.cssTransforms3d&&(i=-(parseFloat(t.css("margin-top"))-i)),$.extend(e,{upEnd:i,downEnd:i+this.getViewportHeight()-e.subH})},menuScrollStop:function(t){return this.scrollTimeout?(cancelAnimationFrame(this.scrollTimeout),this.scrollTimeout=0,t.dataSM("scroll").step=1,!0):void 0},menuScrollTouch:function(t,e){if(e=e.originalEvent,isTouchEvent(e)){var i=this.getTouchPoint(e);if(this.getClosestMenu(i.target)==t[0]){var s=t.dataSM("scroll");if(/(start|down)$/i.test(e.type))this.menuScrollStop(t)?(e.preventDefault(),this.$touchScrollingSub=t):this.$touchScrollingSub=null,this.menuScrollRefreshData(t),$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp});else if(/move$/i.test(e.type)){var o=void 0!==s.touchY?s.touchY:s.touchStartY;if(void 0!==o&&o!=i.pageY){this.$touchScrollingSub=t;var a=i.pageY>o;void 0!==s.up&&s.up!=a&&$.extend(s,{touchStartY:i.pageY,touchStartTime:e.timeStamp}),$.extend(s,{up:a,touchY:i.pageY}),this.menuScroll(t,!0,Math.abs(i.pageY-o))}e.preventDefault()}else void 0!==s.touchY&&((s.momentum=15*Math.pow(Math.abs(i.pageY-s.touchStartY)/(e.timeStamp-s.touchStartTime),2))&&(this.menuScrollStop(t),this.menuScroll(t),e.preventDefault()),delete s.touchY)}}},menuShow:function(t){if((t.dataSM("beforefirstshowfired")||(t.dataSM("beforefirstshowfired",!0),this.$root.triggerHandler("beforefirstshow.smapi",t[0])!==!1))&&this.$root.triggerHandler("beforeshow.smapi",t[0])!==!1&&(t.dataSM("shown-before",!0),canAnimate&&t.stop(!0,!0),!t.is(":visible"))){var e=t.dataSM("parent-a"),i=this.isCollapsible();if((this.opts.keepHighlighted||i)&&e.addClass("highlighted"),i)t.removeClass("sm-nowrap").css({zIndex:"",width:"auto",minWidth:"",maxWidth:"",top:"",left:"",marginLeft:"",marginTop:""});else{if(t.css("z-index",this.zIndexInc=(this.zIndexInc||this.getStartZIndex())+1),(this.opts.subMenusMinWidth||this.opts.subMenusMaxWidth)&&(t.css({width:"auto",minWidth:"",maxWidth:""}).addClass("sm-nowrap"),this.opts.subMenusMinWidth&&t.css("min-width",this.opts.subMenusMinWidth),this.opts.subMenusMaxWidth)){var s=this.getWidth(t);t.css("max-width",this.opts.subMenusMaxWidth),s>this.getWidth(t)&&t.removeClass("sm-nowrap").css("width",this.opts.subMenusMaxWidth)}this.menuPosition(t)}var o=function(){t.css("overflow","")};i?canAnimate&&this.opts.collapsibleShowFunction?this.opts.collapsibleShowFunction.call(this,t,o):t.show(this.opts.collapsibleShowDuration,o):canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,t,o):t.show(this.opts.showDuration,o),e.attr("aria-expanded","true"),t.attr({"aria-expanded":"true","aria-hidden":"false"}),this.visibleSubMenus.push(t),this.$root.triggerHandler("show.smapi",t[0])}},popupHide:function(t){this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0);var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},t?1:this.opts.hideTimeout)},popupShow:function(t,e){if(!this.opts.isPopup)return alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.'),void 0;if(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),this.$root.dataSM("shown-before",!0),canAnimate&&this.$root.stop(!0,!0),!this.$root.is(":visible")){this.$root.css({left:t,top:e});var i=this,s=function(){i.$root.css("overflow","")};canAnimate&&this.opts.showFunction?this.opts.showFunction.call(this,this.$root,s):this.$root.show(this.opts.showDuration,s),this.visibleSubMenus[0]=this.$root}},refresh:function(){this.destroy(!0),this.init(!0)},rootKeyDown:function(t){if(this.handleEvents())switch(t.keyCode){case 27:var e=this.activatedItems[0];if(e){this.menuHideAll(),e[0].focus();var i=e.dataSM("sub");i&&this.menuHide(i)}break;case 32:var s=$(t.target);if(s.is("a")&&this.handleItemEvents(s)){var i=s.dataSM("sub");i&&!i.is(":visible")&&(this.itemClick({currentTarget:t.target}),t.preventDefault())}}},rootOut:function(t){if(this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&(this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0),!this.opts.showOnClick||!this.opts.hideOnClick)){var e=this;this.hideTimeout=setTimeout(function(){e.menuHideAll()},this.opts.hideTimeout)}},rootOver:function(t){this.handleEvents()&&!this.isTouchMode()&&t.target!=this.$root[0]&&this.hideTimeout&&(clearTimeout(this.hideTimeout),this.hideTimeout=0)},winResize:function(t){if(this.handleEvents()){if(!("onorientationchange"in window)||"orientationchange"==t.type){var e=this.isCollapsible();this.wasCollapsible&&e||(this.activatedItems.length&&this.activatedItems[this.activatedItems.length-1][0].blur(),this.menuHideAll()),this.wasCollapsible=e}}else if(this.$disableOverlay){var i=this.$root.offset();this.$disableOverlay.css({top:i.top,left:i.left,width:this.$root.outerWidth(),height:this.$root.outerHeight()})}}}}),$.fn.dataSM=function(t,e){return e?this.data(t+"_smartmenus",e):this.data(t+"_smartmenus")},$.fn.removeDataSM=function(t){return this.removeData(t+"_smartmenus")},$.fn.smartmenus=function(options){if("string"==typeof options){var args=arguments,method=options;return Array.prototype.shift.call(args),this.each(function(){var t=$(this).data("smartmenus");t&&t[method]&&t[method].apply(t,args)})}return this.each(function(){var dataOpts=$(this).data("sm-options")||null;if(dataOpts)try{dataOpts=eval("("+dataOpts+")")}catch(e){dataOpts=null,alert('ERROR\n\nSmartMenus jQuery init:\nInvalid "data-sm-options" attribute value syntax.')}new $.SmartMenus(this,$.extend({},$.fn.smartmenus.defaults,options,dataOpts))})},$.fn.smartmenus.defaults={isPopup:!1,mainMenuSubOffsetX:0,mainMenuSubOffsetY:0,subMenusSubOffsetX:0,subMenusSubOffsetY:0,subMenusMinWidth:"10em",subMenusMaxWidth:"20em",subIndicators:!0,subIndicatorsPos:"append",subIndicatorsText:"",scrollStep:30,scrollAccelerate:!0,showTimeout:250,hideTimeout:500,showDuration:0,showFunction:null,hideDuration:0,hideFunction:function(t,e){t.fadeOut(200,e)},collapsibleShowDuration:0,collapsibleShowFunction:function(t,e){t.slideDown(200,e)},collapsibleHideDuration:0,collapsibleHideFunction:function(t,e){t.slideUp(200,e)},showOnClick:!1,hideOnClick:!0,noMouseOver:!1,keepInViewport:!0,keepHighlighted:!0,markCurrentItem:!1,markCurrentTree:!0,rightToLeftSubMenus:!1,bottomToTopSubMenus:!1,collapsibleBehavior:"default"},$}); \ No newline at end of file diff --git a/lib/NoiascaNeopixelDisplay/docs/html/menu.js b/lib/NoiascaNeopixelDisplay/docs/html/menu.js new file mode 100644 index 0000000000..54e81cf15c --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/docs/html/menu.js @@ -0,0 +1,127 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file + */ +function initMenu(relPath,searchEnabled,serverSide,searchPage,search) { + function makeTree(data,relPath) { + var result=''; + if ('children' in data) { + result+=''; + } + return result; + } + var searchBox; + if (searchEnabled) { + if (serverSide) { + searchBox='
'+ + '
'+ + '
'+ + ''+ + '
'+ + '
'+ + '
'+ + '
'; + } else { + searchBox='
'+ + ''+ + ''+ + ''+ + ''+ + ''+ + '' + '' + '
'; + } + } + + $('#main-nav').before('
'+ + ''+ + ''+ + '
'); + $('#main-nav').append(makeTree(menudata,relPath)); + $('#main-nav').children(':first').addClass('sm sm-dox').attr('id','main-menu'); + if (searchBox) { + $('#main-menu').append('
  • '); + } + var $mainMenuState = $('#main-menu-state'); + var prevWidth = 0; + if ($mainMenuState.length) { + function initResizableIfExists() { + if (typeof initResizable==='function') initResizable(); + } + // animate mobile menu + $mainMenuState.change(function(e) { + var $menu = $('#main-menu'); + var options = { duration: 250, step: initResizableIfExists }; + if (this.checked) { + options['complete'] = function() { $menu.css('display', 'block') }; + $menu.hide().slideDown(options); + } else { + options['complete'] = function() { $menu.css('display', 'none') }; + $menu.show().slideUp(options); + } + }); + // set default menu visibility + function resetState() { + var $menu = $('#main-menu'); + var $mainMenuState = $('#main-menu-state'); + var newWidth = $(window).outerWidth(); + if (newWidth!=prevWidth) { + if ($(window).outerWidth()<768) { + $mainMenuState.prop('checked',false); $menu.hide(); + $('#searchBoxPos1').html(searchBox); + $('#searchBoxPos2').hide(); + } else { + $menu.show(); + $('#searchBoxPos1').empty(); + $('#searchBoxPos2').html(searchBox); + $('#searchBoxPos2').show(); + } + prevWidth = newWidth; + } + } + $(window).ready(function() { resetState(); initResizableIfExists(); }); + $(window).resize(resetState); + } + $('#main-menu').smartmenus(); +} +/* @license-end */ diff --git a/lib/NoiascaNeopixelDisplay/docs/html/menudata.js b/lib/NoiascaNeopixelDisplay/docs/html/menudata.js new file mode 100644 index 0000000000..4c2a8da6e1 --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/docs/html/menudata.js @@ -0,0 +1,31 @@ +/* + @licstart The following is the entire license notice for the JavaScript code in this file. + + The MIT License (MIT) + + Copyright (C) 1997-2020 by Dimitri van Heesch + + Permission is hereby granted, free of charge, to any person obtaining a copy of this software + and associated documentation files (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, publish, distribute, + sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all copies or + substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + @licend The above is the entire license notice for the JavaScript code in this file +*/ +var menudata={children:[ +{text:"Main Page",url:"index.html"}, +{text:"Classes",url:"annotated.html",children:[ +{text:"Class List",url:"annotated.html"}, +{text:"Class Members",url:"functions.html",children:[ +{text:"All",url:"functions.html"}, +{text:"Functions",url:"functions_func.html"}]}]}]} diff --git a/lib/NoiascaNeopixelDisplay/docs/html/nav_f.png b/lib/NoiascaNeopixelDisplay/docs/html/nav_f.png new file mode 100644 index 0000000000000000000000000000000000000000..72a58a529ed3a9ed6aa0c51a79cf207e026deee2 GIT binary patch literal 153 zcmeAS@N?(olHy`uVBq!ia0vp^j6iI`!2~2XGqLUlQVE_ejv*C{Z|{2ZH7M}7UYxc) zn!W8uqtnIQ>_z8U literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/nav_g.png b/lib/NoiascaNeopixelDisplay/docs/html/nav_g.png new file mode 100644 index 0000000000000000000000000000000000000000..2093a237a94f6c83e19ec6e5fd42f7ddabdafa81 GIT binary patch literal 95 zcmeAS@N?(olHy`uVBq!ia0vp^j6lrB!3HFm1ilyoDK$?Q$B+ufw|5PB85lU25BhtE tr?otc=hd~V+ws&_A@j8Fiv!KF$B+ufw|5=67#uj90@pIL wZ=Q8~_Ju`#59=RjDrmm`tMD@M=!-l18IR?&vFVdQ&MBb@0HFXL1|%O$WD@{VPM$7~Ar*{o?;hlAFyLXmaDC0y znK1_#cQqJWPES%4Uujug^TE?jMft$}Eq^WaR~)%f)vSNs&gek&x%A9X9sM-{AmhX=Jf(#6djGiuzAr*{o?=JLmPLyc> z_*`QK&+BH@jWrYJ7>r6%keRM@)Qyv8R=enp0jiI>aWlGyB58O zFVR20d+y`K7vDw(hJF3;>dD*3-?v=<8M)@x|EEGLnJsniYK!2U1 Y!`|5biEc?d1`HDhPgg&ebxsLQ02F6;9RL6T literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/sync_off.png b/lib/NoiascaNeopixelDisplay/docs/html/sync_off.png new file mode 100644 index 0000000000000000000000000000000000000000..3b443fc62892114406e3d399421b2a881b897acc GIT binary patch literal 853 zcmV-b1FHOqP)oT|#XixUYy%lpuf3i8{fX!o zUyDD0jOrAiT^tq>fLSOOABs-#u{dV^F$b{L9&!2=9&RmV;;8s^x&UqB$PCj4FdKbh zoB1WTskPUPu05XzFbA}=KZ-GP1fPpAfSs>6AHb12UlR%-i&uOlTpFNS7{jm@mkU1V zh`nrXr~+^lsV-s1dkZOaI|kYyVj3WBpPCY{n~yd%u%e+d=f%`N0FItMPtdgBb@py; zq@v6NVArhyTC7)ULw-Jy8y42S1~4n(3LkrW8mW(F-4oXUP3E`e#g**YyqI7h-J2zK zK{m9##m4ri!7N>CqQqCcnI3hqo1I;Yh&QLNY4T`*ptiQGozK>FF$!$+84Z`xwmeMh zJ0WT+OH$WYFALEaGj2_l+#DC3t7_S`vHpSivNeFbP6+r50cO8iu)`7i%Z4BTPh@_m3Tk!nAm^)5Bqnr%Ov|Baunj#&RPtRuK& z4RGz|D5HNrW83-#ydk}tVKJrNmyYt-sTxLGlJY5nc&Re zU4SgHNPx8~Yxwr$bsju?4q&%T1874xxzq+_%?h8_ofw~(bld=o3iC)LUNR*BY%c0y zWd_jX{Y8`l%z+ol1$@Qa?Cy!(0CVIEeYpKZ`(9{z>3$CIe;pJDQk$m3p}$>xBm4lb zKo{4S)`wdU9Ba9jJbVJ0C=SOefZe%d$8=2r={nu<_^a3~>c#t_U6dye5)JrR(_a^E f@}b6j1K9lwFJq@>o)+Ry00000NkvXXu0mjfWa5j* literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/sync_on.png b/lib/NoiascaNeopixelDisplay/docs/html/sync_on.png new file mode 100644 index 0000000000000000000000000000000000000000..e08320fb64e6fa33b573005ed6d8fe294e19db76 GIT binary patch literal 845 zcmV-T1G4;yP)Y;xxyHF2B5Wzm| zOOGupOTn@c(JmBOl)e;XMNnZuiTJP>rM8<|Q`7I_))aP?*T)ow&n59{}X4$3Goat zgjs?*aasfbrokzG5cT4K=uG`E14xZl@z)F={P0Y^?$4t z>v!teRnNZym<6h{7sLyF1V0HsfEl+l6TrZpsfr1}luH~F7L}ktXu|*uVX^RG$L0`K zWs3j|0tIvVe(N%_?2{(iCPFGf#B6Hjy6o&}D$A%W%jfO8_W%ZO#-mh}EM$LMn7joJ z05dHr!5Y92g+31l<%i1(=L1a1pXX+OYnalY>31V4K}BjyRe3)9n#;-cCVRD_IG1fT zOKGeNY8q;TL@K{dj@D^scf&VCs*-Jb>8b>|`b*osv52-!A?BpbYtTQBns5EAU**$m zSnVSm(teh>tQi*S*A>#ySc=n;`BHz`DuG4&g4Kf8lLhca+zvZ7t7RflD6-i-mcK=M z!=^P$*u2)bkY5asG4gsss!Hn%u~>}kIW`vMs%lJLH+u*9<4PaV_c6U`KqWXQH%+Nu zTv41O(^ZVi@qhjQdG!fbZw&y+2o!iYymO^?ud3{P*HdoX83YV*Uu_HB=?U&W9%AU# z80}k1SS-CXTU7dcQlsm<^oYLxVSseqY6NO}dc`Nj?8vrhNuCdm@^{a3AQ_>6myOj+ z`1RsLUXF|dm|3k7s2jD(B{rzE>WI2scH8i1;=O5Cc9xB3^aJk%fQjqsu+kH#0=_5a z0nCE8@dbQa-|YIuUVvG0L_IwHMEhOj$Mj4Uq05 X8=0q~qBNan00000NkvXXu0mjfptF>5 literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/tab_a.png b/lib/NoiascaNeopixelDisplay/docs/html/tab_a.png new file mode 100644 index 0000000000000000000000000000000000000000..3b725c41c5a527a3a3e40097077d0e206a681247 GIT binary patch literal 142 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QlXwMjv*C{Z|8b*H5dputLHD# z=<0|*y7z(Vor?d;H&?EG&cXR}?!j-Lm&u1OOI7AIF5&c)RFE;&p0MYK>*Kl@eiymD r@|NpwKX@^z+;{u_Z~trSBfrMKa%3`zocFjEXaR$#tDnm{r-UW|TZ1%4 literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/tab_b.png b/lib/NoiascaNeopixelDisplay/docs/html/tab_b.png new file mode 100644 index 0000000000000000000000000000000000000000..e2b4a8638cb3496a016eaed9e16ffc12846dea18 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^j6kfy!2~3aiye;!QU#tajv*C{Z}0l@H7kg?K0Lnr z!j&C6_(~HV9oQ0Pa6x{-v0AGV_E?vLn=ZI-;YrdjIl`U`uzuDWSP?o#Dmo{%SgM#oan kX~E1%D-|#H#QbHoIja2U-MgvsK&LQxy85}Sb4q9e0Efg%P5=M^ literal 0 HcmV?d00001 diff --git a/lib/NoiascaNeopixelDisplay/docs/html/tabs.css b/lib/NoiascaNeopixelDisplay/docs/html/tabs.css new file mode 100644 index 0000000000..00d1c60249 --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/docs/html/tabs.css @@ -0,0 +1 @@ +.sm{position:relative;z-index:9999}.sm,.sm ul,.sm li{display:block;list-style:none;margin:0;padding:0;line-height:normal;direction:ltr;text-align:left;-webkit-tap-highlight-color:rgba(0,0,0,0)}.sm-rtl,.sm-rtl ul,.sm-rtl li{direction:rtl;text-align:right}.sm>li>h1,.sm>li>h2,.sm>li>h3,.sm>li>h4,.sm>li>h5,.sm>li>h6{margin:0;padding:0}.sm ul{display:none}.sm li,.sm a{position:relative}.sm a{display:block}.sm a.disabled{cursor:not-allowed}.sm:after{content:"\00a0";display:block;height:0;font:0/0 serif;clear:both;visibility:hidden;overflow:hidden}.sm,.sm *,.sm *:before,.sm *:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}.main-menu-btn{position:relative;display:inline-block;width:36px;height:36px;text-indent:36px;margin-left:8px;white-space:nowrap;overflow:hidden;cursor:pointer;-webkit-tap-highlight-color:rgba(0,0,0,0)}.main-menu-btn-icon,.main-menu-btn-icon:before,.main-menu-btn-icon:after{position:absolute;top:50%;left:2px;height:2px;width:24px;background:#666;-webkit-transition:all .25s;transition:all .25s}.main-menu-btn-icon:before{content:'';top:-7px;left:0}.main-menu-btn-icon:after{content:'';top:7px;left:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon{height:0}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:before{top:0;-webkit-transform:rotate(-45deg);transform:rotate(-45deg)}#main-menu-state:checked ~ .main-menu-btn .main-menu-btn-icon:after{top:0;-webkit-transform:rotate(45deg);transform:rotate(45deg)}#main-menu-state{position:absolute;width:1px;height:1px;margin:-1px;border:0;padding:0;overflow:hidden;clip:rect(1px,1px,1px,1px)}#main-menu-state:not(:checked) ~ #main-menu{display:none}#main-menu-state:checked ~ #main-menu{display:block}@media(min-width:768px){.main-menu-btn{position:absolute;top:-99999px}#main-menu-state:not(:checked) ~ #main-menu{display:block}}.sm-dox{background-image:url("tab_b.png")}.sm-dox a,.sm-dox a:focus,.sm-dox a:hover,.sm-dox a:active{padding:0 12px;padding-right:43px;font-family:"Lucida Grande","Geneva","Helvetica",Arial,sans-serif;font-size:13px;font-weight:bold;line-height:36px;text-decoration:none;text-shadow:0 1px 1px rgba(255,255,255,0.9);color:#283a5d;outline:0}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox a.current{color:#d23600}.sm-dox a.disabled{color:#bbb}.sm-dox a span.sub-arrow{position:absolute;top:50%;margin-top:-14px;left:auto;right:3px;width:28px;height:28px;overflow:hidden;font:bold 12px/28px monospace !important;text-align:center;text-shadow:none;background:rgba(255,255,255,0.5);-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox a span.sub-arrow:before{display:block;content:'+'}.sm-dox a.highlighted span.sub-arrow:before{display:block;content:'-'}.sm-dox>li:first-child>a,.sm-dox>li:first-child>:not(ul) a{-moz-border-radius:5px 5px 0 0;-webkit-border-radius:5px;border-radius:5px 5px 0 0}.sm-dox>li:last-child>a,.sm-dox>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul{-moz-border-radius:0 0 5px 5px;-webkit-border-radius:0;border-radius:0 0 5px 5px}.sm-dox>li:last-child>a.highlighted,.sm-dox>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>a.highlighted,.sm-dox>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>ul>li:last-child>*:not(ul) a.highlighted{-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox ul{background:rgba(162,162,162,0.1)}.sm-dox ul a,.sm-dox ul a:focus,.sm-dox ul a:hover,.sm-dox ul a:active{font-size:12px;border-left:8px solid transparent;line-height:36px;text-shadow:none;background-color:white;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox ul ul a,.sm-dox ul ul a:hover,.sm-dox ul ul a:focus,.sm-dox ul ul a:active{border-left:16px solid transparent}.sm-dox ul ul ul a,.sm-dox ul ul ul a:hover,.sm-dox ul ul ul a:focus,.sm-dox ul ul ul a:active{border-left:24px solid transparent}.sm-dox ul ul ul ul a,.sm-dox ul ul ul ul a:hover,.sm-dox ul ul ul ul a:focus,.sm-dox ul ul ul ul a:active{border-left:32px solid transparent}.sm-dox ul ul ul ul ul a,.sm-dox ul ul ul ul ul a:hover,.sm-dox ul ul ul ul ul a:focus,.sm-dox ul ul ul ul ul a:active{border-left:40px solid transparent}@media(min-width:768px){.sm-dox ul{position:absolute;width:12em}.sm-dox li{float:left}.sm-dox.sm-rtl li{float:right}.sm-dox ul li,.sm-dox.sm-rtl ul li,.sm-dox.sm-vertical li{float:none}.sm-dox a{white-space:nowrap}.sm-dox ul a,.sm-dox.sm-vertical a{white-space:normal}.sm-dox .sm-nowrap>li>a,.sm-dox .sm-nowrap>li>:not(ul) a{white-space:nowrap}.sm-dox{padding:0 10px;background-image:url("tab_b.png");line-height:36px}.sm-dox a span.sub-arrow{top:50%;margin-top:-2px;right:12px;width:0;height:0;border-width:4px;border-style:solid dashed dashed dashed;border-color:#283a5d transparent transparent transparent;background:transparent;-moz-border-radius:0;-webkit-border-radius:0;border-radius:0}.sm-dox a,.sm-dox a:focus,.sm-dox a:active,.sm-dox a:hover,.sm-dox a.highlighted{padding:0 12px;background-image:url("tab_s.png");background-repeat:no-repeat;background-position:right;-moz-border-radius:0 !important;-webkit-border-radius:0;border-radius:0 !important}.sm-dox a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox a:hover span.sub-arrow{border-color:white transparent transparent transparent}.sm-dox a.has-submenu{padding-right:24px}.sm-dox li{border-top:0}.sm-dox>li>ul:before,.sm-dox>li>ul:after{content:'';position:absolute;top:-18px;left:30px;width:0;height:0;overflow:hidden;border-width:9px;border-style:dashed dashed solid dashed;border-color:transparent transparent #bbb transparent}.sm-dox>li>ul:after{top:-16px;left:31px;border-width:8px;border-color:transparent transparent #fff transparent}.sm-dox ul{border:1px solid #bbb;padding:5px 0;background:#fff;-moz-border-radius:5px !important;-webkit-border-radius:5px;border-radius:5px !important;-moz-box-shadow:0 5px 9px rgba(0,0,0,0.2);-webkit-box-shadow:0 5px 9px rgba(0,0,0,0.2);box-shadow:0 5px 9px rgba(0,0,0,0.2)}.sm-dox ul a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-color:transparent transparent transparent #555;border-style:dashed dashed dashed solid}.sm-dox ul a,.sm-dox ul a:hover,.sm-dox ul a:focus,.sm-dox ul a:active,.sm-dox ul a.highlighted{color:#555;background-image:none;border:0 !important;color:#555;background-image:none}.sm-dox ul a:hover{background-image:url("tab_a.png");background-repeat:repeat-x;color:white;text-shadow:0 1px 1px black}.sm-dox ul a:hover span.sub-arrow{border-color:transparent transparent transparent white}.sm-dox span.scroll-up,.sm-dox span.scroll-down{position:absolute;display:none;visibility:hidden;overflow:hidden;background:#fff;height:36px}.sm-dox span.scroll-up:hover,.sm-dox span.scroll-down:hover{background:#eee}.sm-dox span.scroll-up:hover span.scroll-up-arrow,.sm-dox span.scroll-up:hover span.scroll-down-arrow{border-color:transparent transparent #d23600 transparent}.sm-dox span.scroll-down:hover span.scroll-down-arrow{border-color:#d23600 transparent transparent transparent}.sm-dox span.scroll-up-arrow,.sm-dox span.scroll-down-arrow{position:absolute;top:0;left:50%;margin-left:-6px;width:0;height:0;overflow:hidden;border-width:6px;border-style:dashed dashed solid dashed;border-color:transparent transparent #555 transparent}.sm-dox span.scroll-down-arrow{top:8px;border-style:solid dashed dashed dashed;border-color:#555 transparent transparent transparent}.sm-dox.sm-rtl a.has-submenu{padding-right:12px;padding-left:24px}.sm-dox.sm-rtl a span.sub-arrow{right:auto;left:12px}.sm-dox.sm-rtl.sm-vertical a.has-submenu{padding:10px 20px}.sm-dox.sm-rtl.sm-vertical a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-rtl>li>ul:before{left:auto;right:30px}.sm-dox.sm-rtl>li>ul:after{left:auto;right:31px}.sm-dox.sm-rtl ul a.has-submenu{padding:10px 20px !important}.sm-dox.sm-rtl ul a span.sub-arrow{right:auto;left:8px;border-style:dashed solid dashed dashed;border-color:transparent #555 transparent transparent}.sm-dox.sm-vertical{padding:10px 0;-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.sm-dox.sm-vertical a{padding:10px 20px}.sm-dox.sm-vertical a:hover,.sm-dox.sm-vertical a:focus,.sm-dox.sm-vertical a:active,.sm-dox.sm-vertical a.highlighted{background:#fff}.sm-dox.sm-vertical a.disabled{background-image:url("tab_b.png")}.sm-dox.sm-vertical a span.sub-arrow{right:8px;top:50%;margin-top:-5px;border-width:5px;border-style:dashed dashed dashed solid;border-color:transparent transparent transparent #555}.sm-dox.sm-vertical>li>ul:before,.sm-dox.sm-vertical>li>ul:after{display:none}.sm-dox.sm-vertical ul a{padding:10px 20px}.sm-dox.sm-vertical ul a:hover,.sm-dox.sm-vertical ul a:focus,.sm-dox.sm-vertical ul a:active,.sm-dox.sm-vertical ul a.highlighted{background:#eee}.sm-dox.sm-vertical ul a.disabled{background:#fff}} \ No newline at end of file diff --git a/lib/NoiascaNeopixelDisplay/docs/info.txt b/lib/NoiascaNeopixelDisplay/docs/info.txt new file mode 100644 index 0000000000..9be2e1f1ee --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/docs/info.txt @@ -0,0 +1,20 @@ +Examples to come up with this libraries + +00 strand test shortest version +02 basic example some basic print write methods +03 segment check A - G , . 0 to 9 +05 all chars +04 print letters, dots, ... + +1x lot of pixels more pixels per Segment + +2x additional pixels + +3x clock 00:00 +32 clock 00:00:00 + +4x Scoreboard +4x Scoreboard mit Countdown Timter 02 02 02:59 +4x Scoreboard - Serial Interface + +8x ESP8266 - Webinterface \ No newline at end of file diff --git a/lib/NoiascaNeopixelDisplay/docs/more.html b/lib/NoiascaNeopixelDisplay/docs/more.html new file mode 100644 index 0000000000..05309ca94f --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/docs/more.html @@ -0,0 +1,11 @@ + + + +Noiasca Neopixel Display + + +

    Noiasca Neopixel Display

    +

    Full library documentation

    +

    Download latest Version

    + + diff --git a/lib/NoiascaNeopixelDisplay/examples/01_hello_world/01_hello_world.ino b/lib/NoiascaNeopixelDisplay/examples/01_hello_world/01_hello_world.ino new file mode 100644 index 0000000000..a1b613005b --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/01_hello_world/01_hello_world.ino @@ -0,0 +1,58 @@ +/******************************************************************************* + Noiasca Neopixel Display + 01 Hello World + + Simple Strandtest/Hello World program to check the wiring of your display + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-04 + *******************************************************************************/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 4; // How many digits (numbers) are available on your display? +const byte pixelPerDigit = 16; // all pixel, including decimal point pixels if available at each digit + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +const uint16_t ledCount(pixelPerDigit * numDigits); // keeps track of used pixels +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create Neopixel object like commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit); // create display object, handover the name of your strip as first parameter + +void setup() { + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + display.print(1234); // print 1234 to your display +} + +void loop() { + // put here other code which needs to run: +} diff --git a/lib/NoiascaNeopixelDisplay/examples/02_basic_example/02_basic_example.ino b/lib/NoiascaNeopixelDisplay/examples/02_basic_example/02_basic_example.ino new file mode 100644 index 0000000000..75f540785d --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/02_basic_example/02_basic_example.ino @@ -0,0 +1,137 @@ +/******************************************************************************* + Noiasca Neopixel Display + 02 basic example + + Shows the basic print functions + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-04 + *******************************************************************************/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 4; // How many digits (numbers) are available on your display +const byte pixelPerDigit = 16; // all Pixel, including decimal point pixels if available at each digit +const byte addPixels = 0; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34) + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +/* + typedef uint8_t segsize_t; // fit variable size to your needed pixels. uint8_t --> max 8 Pixel per digit + const segsize_t segment[8] { + 0b00000001, // SEG_A + 0b00000010, // SEG_B + 0b00000100, // SEG_C + 0b00001000, // SEG_D + 0b00010000, // SEG_E + 0b00100000, // SEG_F + 0b01000000, // SEG_G + 0b10000000 // SEG_DP if you don't have a decimal point, just leave it zero + }; +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +/* + // match LED Segment of Display to Hardware - Muss zur Pixel-Anordnung der Hardware passen + // 3 individual Pixel per Segment: + typedef uint32_t segsize_t; // fit variable size to your needed pixels. uint32_t --> max 32 Pixel per digit + const segsize_t segment[8] { + _BV(2) | _BV(1) | _BV(0), // SEG_A // n of maximal 32 bits pixels per Segment + _BV(5) | _BV(4) | _BV(3), // SEG_B + _BV(8) | _BV(7) | _BV(6), // SEG_C + _BV(11) | _BV(10) | _BV(9), // SEG_D + _BV(14) | _BV(13) | _BV(12), // SEG_E + _BV(17) | _BV(16) | _BV(15), // SEG_F + _BV(20) | _BV(19) | _BV(18), // SEG_G + _BV(21) // SEG_DP + }; +*/ + +const uint16_t ledCount(pixelPerDigit * numDigits + addPixels); // keeps track of used pixels +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // include the library after your segment definitions. download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, addPixels); // create display object, handover the name of your strip as first parameter! + +void setup() +{ + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n02 basic example")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + + Serial.println(F("print float")); + display.clear(); + display.print(12.3, 1); // print prints floats by default with two decimals. If you just need one decimal, use the print functionality to print only one decimal so if you receive + delay(1000); + + Serial.println(F("direct adress individual digits with low level method")); + display.writeLowLevel(0, 255); // switch on the first 8 segment of digit 0 + display.setColorFont(0x00FF00); + display.writeLowLevel(1, 0b10101010); // 4 pixels of digit 1 + display.setColorFont(0x0000088); + display.writeLowLevel(2, 0b01010101); // 4 pixels of digit 1 + display.show(); // send data to stripe - needed because writeLowLevel doesn' show data by default + delay(1000); + + Serial.println(F(".write() ASCII Codes (if printable)")); + display.clear(); // delete content on display + display.write(48); // 0 // basic write method, expects ASCII code, ends with an .show() by default + display.write(49); // 1 + display.write(50); // 2 + display.write(51); // 3 + delay(1000); + + Serial.println(F("set a new Color and use print method - easy way")); + display.clear(); + display.setColorFont(0x00FF00); // change color of font (active segments) + display.print(123); // print an integer on the display +} + +void loop() +{ + // put here other code which needs to run: + + display.clear(); + display.setColorFont(0x00AA00); + display.print(8888); + delay(2000); + + display.clear(); + display.setColorFont(0x00AA88); + display.print(1111); + delay(2000); + + display.clear(); + display.setColorFont(0xAAAA00); + display.print(random(10000)); + display.setPixelColor(random(40), 0x0000ff); // direct access a pixel + display.show(); + delay(2000); +} diff --git a/lib/NoiascaNeopixelDisplay/examples/03_counter/03_counter.ino b/lib/NoiascaNeopixelDisplay/examples/03_counter/03_counter.ino new file mode 100644 index 0000000000..6909dd213c --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/03_counter/03_counter.ino @@ -0,0 +1,68 @@ +/******************************************************************************* + Noiasca Neopixel Display + 03: Counter + + Just a simple counter, counting upwards + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-04 + *******************************************************************************/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 4; // How many digits (numbers) are available on your display +const byte pixelPerDigit = 16; // all Pixel, including decimal point pixels if available at each digit +const byte addPixels = 0; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34) + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +const uint16_t ledCount(pixelPerDigit * numDigits + addPixels); // keeps track of used pixels + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, addPixels); // create display object, handover the name of your strip as first parameter! + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n03 counter")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) +} + +void loop() { + // just counting upwards + for (uint16_t i = 0; i < pow(10, numDigits); i++) { + display.clear(); + display.print(i); + Serial.println(i); + delay(100); + } +} diff --git a/lib/NoiascaNeopixelDisplay/examples/20_additional_pixels/20_additional_pixels.ino b/lib/NoiascaNeopixelDisplay/examples/20_additional_pixels/20_additional_pixels.ino new file mode 100644 index 0000000000..c51e2756ab --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/20_additional_pixels/20_additional_pixels.ino @@ -0,0 +1,85 @@ +/* Noiasca Neopixel Display + 20: additional pixels + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-04 +*/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 4; // How many digits (numbers) are available on your display +const byte pixelPerDigit = 16; // all pixels, including decimal point pixels if available at each digit +const byte startPixel = 0; // start with this pixel on the strip +const byte addPixels = 2; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34) + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D + + in the following constants you have to define + which pixel number belongs to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +const uint16_t ledCount(pixelPerDigit * numDigits + addPixels); + +int offsetLogic_cb(uint16_t position) // your callback function to keep track of additional pixels +{ + uint16_t offset = 0; + if (position > 1 ) offset = addPixels; // example: the additional Pixels are between 2nd and 3rd digit + return offset; // you MUST return a value. It can be 0 +} + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, startPixel, addPixels, offsetLogic_cb); // create display object, handover the name of your strip as first parameter! + +void blinkSecond() { + static uint32_t previousTimestamp = 0; + static bool state = 0; + uint32_t actualTimestamp = millis() / 500; + if (actualTimestamp != previousTimestamp) { + previousTimestamp = actualTimestamp; + display.setPixelColor(pixelPerDigit * 2 + 0, 0x0000FF * state); // Assumption: the additional pixel is after two digits + display.setPixelColor(pixelPerDigit * 2 + 1, 0x0000FF * state); // Assumpiton: there are two additional pixels + display.show(); + state = !state; + } +} + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n20 additional pixels")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + + Serial.println(F("Print 1234 on your display")); + display.print("12"); + display.print("34"); +} + +void loop() { + // put here other code which needs to run: + blinkSecond(); +} diff --git a/lib/NoiascaNeopixelDisplay/examples/30_clock_basic/30_clock_basic.ino b/lib/NoiascaNeopixelDisplay/examples/30_clock_basic/30_clock_basic.ino new file mode 100644 index 0000000000..2faf38dddf --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/30_clock_basic/30_clock_basic.ino @@ -0,0 +1,131 @@ +/* Noiasca Neopixel Display + 30 clock two displays + handling HH MM SS as three separate displays + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2021-11-21 +*/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 2; // How many digits (numbers) are available on each display +const byte startPixelHH = 0; // the display starts with HH with the first pixel +const byte startPixelMM = 32; // the MM are in the middle (start at 0 + digits for 10H + digits for H + 2 for the colon) +const byte startPixelSS = 64; // seconds are on the far right, therefore we need the highest pixel offset +const byte pixelPerDigit = 15; // all pixels, including decimal point pixels if available at each digit +const byte addPixels = 4; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34:56) + +const uint16_t ledCount(pixelPerDigit * numDigits * 3 + addPixels); +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +// in this sketch we handle HH and MM as two individual displays: +Noiasca_NeopixelDisplay displayHH(strip, segment, numDigits, pixelPerDigit, startPixelHH); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay displayMM(strip, segment, numDigits, pixelPerDigit, startPixelMM); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay displaySS(strip, segment, numDigits, pixelPerDigit, startPixelSS); // create display object, handover the name of your strip as first parameter! + +void blinkColon() { + static uint32_t previousTimestamp = 0; + static bool state = 0; + uint32_t actualTimestamp = millis() / 500; + if (actualTimestamp != previousTimestamp) + { + previousTimestamp = actualTimestamp; + strip.setPixelColor(pixelPerDigit * 2 + 0, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 2 + 1, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 4 + 2, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 4 + 3, 0x0000FF * state); + strip.show(); + state = !state; + } +} + +void updateSecond() { + static uint32_t previousSecond = -1; + uint32_t actualSecond = (millis() / 1000UL) % 60 ; + if ( actualSecond != previousSecond) { + previousSecond = actualSecond; + Serial.println(actualSecond); + displaySS.setCursor(0); + if (actualSecond < 10) displaySS.print("0"); + displaySS.print(actualSecond); + } +} + +void updateMinute() { + static uint32_t previousMinute = -1; + uint32_t actualMinute = (millis() / 1000UL) / 60 % 60 ; + if ( actualMinute != previousMinute) { + previousMinute = actualMinute; + Serial.println(actualMinute); + displayMM.setCursor(0); + if (actualMinute < 10) displayMM.print("0"); + displayMM.print(actualMinute); + } +} + +void updateHour() { + static uint32_t previousHour = -1; + uint32_t actualHour = (millis() / 1000UL) / 3600 % 24 ; + if ( actualHour != previousHour) { + previousHour = actualHour; + Serial.println(actualHour); + displayHH.setCursor(0); + if (actualHour < 10) displayHH.print(" "); + displayHH.print(actualHour); + } +} + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n30 clock two displays")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + strip.clear(); // clears the full strip (all displays assigned to this strip!) + + displayHH.setColorFont(0xAAAA00); + displayMM.setColorFont(0x880044); + + Serial.println(F("Test: 888888 on display")); + displayHH.print("88"); + displayMM.print("88"); + displaySS.print("88"); + delay(1000); +} + +void loop() { + blinkColon(); + updateSecond(); + updateMinute(); + updateHour(); + // put here other code which needs to run: +} diff --git a/lib/NoiascaNeopixelDisplay/examples/31_clock_rtc/31_clock_rtc.ino b/lib/NoiascaNeopixelDisplay/examples/31_clock_rtc/31_clock_rtc.ino new file mode 100644 index 0000000000..c5f7a40a8b --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/31_clock_rtc/31_clock_rtc.ino @@ -0,0 +1,186 @@ +/* Noiasca Neopixel Display + 31 clock RTC + handling HH MM SS as three separate displays + reading RTC + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2021-11-09 +*/ + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 2; // How many digits (numbers) are available on each display +const byte startPixelHH = 0; // the display starts with HH with the first pixel +const byte startPixelMM = 16; // the MM are in the middle (start at 0 + digits for 10H + digits for H + 2 for the colon) 7+7+2 +const byte startPixelSS = 32; // seconds are on the far right, (7+7+2+7+7+2) +const byte pixelPerDigit = 7; // all pixels, including decimal point pixels if available at each digit +const byte addPixels = 4; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34:56) +const uint16_t ledCount(pixelPerDigit * numDigits * 3 + addPixels); + +typedef uint8_t segsize_t; // fit variable size to your needed pixels. uint8_t --> max 8 Pixel per digit +const segsize_t segment[8] { + 0b00000001, // SEG_A + 0b00000010, // SEG_B + 0b00000100, // SEG_C + 0b00001000, // SEG_D + 0b00010000, // SEG_E + 0b00100000, // SEG_F + 0b01000000, // SEG_G + 0b00000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +/* + +// Variant for 16 LEDs per digit +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 2; // How many digits (numbers) are available on each display +const byte startPixelHH = 0; // the display starts with HH with the first pixel +const byte startPixelMM = 32; // the MM are in the middle (start at 0 + digits for 10H + digits for H + 2 for the colon) +const byte startPixelSS = 64; // seconds are on the far right, therefore we need the highest pixel offset +const byte pixelPerDigit = 15; // all pixels, including decimal point pixels if available at each digit +const byte addPixels = 4; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34:56) +const uint16_t ledCount(pixelPerDigit * numDigits * 3 + addPixels); + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; +*/ + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +// in this sketch we handle HH and MM as two individual displays: +Noiasca_NeopixelDisplay displayHH(strip, segment, numDigits, pixelPerDigit, startPixelHH); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay displayMM(strip, segment, numDigits, pixelPerDigit, startPixelMM); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay displaySS(strip, segment, numDigits, pixelPerDigit, startPixelSS); // create display object, handover the name of your strip as first parameter! + +#include "RTClib.h" // by Adafruit tested with Version 1.12.5 +RTC_DS3231 rtc; + +void blinkColon() { + static uint32_t previousTimestamp = 0; + static bool state = 0; + uint32_t actualTimestamp = millis() / 500; + if (actualTimestamp != previousTimestamp) { + previousTimestamp = actualTimestamp; + strip.setPixelColor(pixelPerDigit * 2 + 0, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 2 + 1, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 4 + 2, 0x0000FF * state); + strip.setPixelColor(pixelPerDigit * 4 + 3, 0x0000FF * state); + strip.show(); + state = !state; + } +} + +void updateSecond() { + static uint32_t previousSecond = -1; + DateTime now = rtc.now(); + uint32_t actualSecond = now.second(); + if ( actualSecond != previousSecond) { + previousSecond = actualSecond; + Serial.print('s'); + Serial.println(actualSecond); + displaySS.setCursor(0); + if (actualSecond < 10) displaySS.print("0"); + displaySS.print(actualSecond); + } +} + +void updateMinute() { + static uint32_t previousMinute = -1; + DateTime now = rtc.now(); + uint32_t actualMinute = now.minute(); + if ( actualMinute != previousMinute) { + previousMinute = actualMinute; + Serial.print('m'); + Serial.println(actualMinute); + displayMM.setCursor(0); + if (actualMinute < 10) displayMM.print("0"); + displayMM.print(actualMinute); + } +} + +void updateHour() { + static uint32_t previousHour = -1; + DateTime now = rtc.now(); + uint32_t actualHour = now.hour(); + if ( actualHour != previousHour) { + previousHour = actualHour; + Serial.print('h'); + Serial.println(actualHour); + displayHH.setCursor(0); + if (actualHour < 10) displayHH.print(" "); + displayHH.print(actualHour); + } +} + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n31 clock two displays")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + strip.clear(); // clears the full strip (all displays assigned to this strip!) + + displayHH.setColorFont(0xAAAA00); + displayMM.setColorFont(0x880044); + + Serial.println(F("Print 8888 on display")); + displayHH.print("88"); + displayMM.print("88"); + displaySS.print("88"); + + if (! rtc.begin()) { + Serial.println(F("Couldn't find RTC")); + displayHH.print(" E"); + displayMM.print("rr"); + displaySS.print("or"); + delay(1000); // dirty delay to give time to read message + } + + if (rtc.lostPower()) { + Serial.println(F("RTC lost power, let's set the time!")); + // When time needs to be set on a new device, or after a power loss, the + // following line sets the RTC to the date & time this sketch was compiled + rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); + // This line sets the RTC with an explicit date & time + // YYYY MM DD HH MM SS + // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0)); + displayHH.print(" S"); + displayMM.print("ET"); + displaySS.print(" "); + delay(1000); // dirty delay to give time to read message + } +} + +void loop() { + blinkColon(); + updateSecond(); + updateMinute(); + updateHour(); + // put here other code which needs to run: +} diff --git a/lib/NoiascaNeopixelDisplay/examples/40_scoreboard/40_scoreboard.ino b/lib/NoiascaNeopixelDisplay/examples/40_scoreboard/40_scoreboard.ino new file mode 100644 index 0000000000..7099933323 --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/40_scoreboard/40_scoreboard.ino @@ -0,0 +1,123 @@ +/* Noiasca Neopixel Display + 40 scoreboard + a simple scoreboard for two players (A and B) + separate displays for A and B on the same strip pin + + Wire three buttons from the GPIO to GND. Input pullups are activated + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-04 +*/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 2; // How many digits (numbers) are available on each display +const byte pixelPerDigit = 14; // all pixels, including decimal point pixels if available at each digit +const byte addPixels = 4; // unregular additional pixels to be added to the strip + +const byte startPixelA = 0; // start pixel of display A +const byte startPixelB = 32; // start pixel of display B (assumption: 2 x 14 used by displayA + 4 additional Pixels) + +const byte buttonApin = A3; // button pin player A, connects to GND +const byte buttonBpin = A0; // button pin player B, connects to GND +const byte buttonResetPin = A1; // button to reset scores, connects to GND +byte counterA; // counts/goals/score for player A +byte counterB; // counts/goals/score for player B + +const uint16_t ledCount(pixelPerDigit * numDigits * 2 + addPixels); +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +// in this sketch we handle displayA and displayB as two individual displays: +Noiasca_NeopixelDisplay displayA(strip, segment, numDigits, pixelPerDigit, startPixelA); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay displayB(strip, segment, numDigits, pixelPerDigit, startPixelB); // create display object, handover the name of your strip as first parameter! + +#include // install OneButton library from library manager +OneButton buttonA(buttonApin, true); +OneButton buttonB(buttonBpin, true); +OneButton buttonReset(buttonResetPin, true); + +void clickA() { + counterA++; + Serial.print(F("PlayerA ")); Serial.println(counterA); + displayA.setCursor(0); + if (counterA < 10) displayA.print(" "); + displayA.print(counterA); +} + +void clickB() { + counterB++; + Serial.print(F("PlayerB ")); Serial.println(counterB); + displayB.setCursor(0); + if (counterB < 10) displayB.print(" "); + displayB.print(counterB); +} + +void resetScore() { + Serial.println(F("Reset Score")); + counterA = 0; + counterB = 0; + displayA.clear(); + displayA.print(" 0"); + displayB.clear(); + displayB.print(" 0"); +} + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n40 scoreboard two displays")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + strip.clear(); + + displayA.setColorFont(0xAA0000); // each display gets its own color, e.g. corresponding to the button color + displayB.setColorFont(0x00AA00); + + Serial.println(F("test display")); + for (byte i = 99; i > 0; i = i - 11) { + displayA.print(i); + displayB.print(i); + delay(200); + } + displayA.print(" 0"); + displayB.print(" 0"); + buttonA.attachClick(clickA); + buttonB.attachClick(clickB); + buttonReset.attachLongPressStart(resetScore); +} + +void loop() { + // put here other code which needs to run: + buttonA.tick(); + buttonB.tick(); + buttonReset.tick(); +} diff --git a/lib/NoiascaNeopixelDisplay/examples/64_large_display_64bit/64_large_display_64bit.ino b/lib/NoiascaNeopixelDisplay/examples/64_large_display_64bit/64_large_display_64bit.ino new file mode 100644 index 0000000000..fc10d67d9b --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/64_large_display_64bit/64_large_display_64bit.ino @@ -0,0 +1,115 @@ +/* + Noiasca Neopixel Display + 64 large display 64 bit - with lot of pixels per segment + + There was a user request, for a display with 7 pixels per segment, + which will give 35 pixels per digit. + obviously, 35 pixels don't fit in a 32bit bitmap, therefore you you must + use a unsigned 64bit variable for the segsize_t + + 64bit variables come with several restrictions. For example you will not be able to + print such a large variable to the serial, because it's not implemented in the print.h class. + + Theoretically 9pixels x 7segments = 63 would fit into a 64bit Variable, but be careful with + any bitshift you will need in your sketch. + + Warning: There a two default makros: + + _BV is part of Libc. The intended use is to create bitmasks for manipulating register values. + #define _BV(bit) (1 << (bit)) + + bit() is part of the Arduino implementation: + #define bit(b) (1UL << (b)) + + so non of them is good for 64bit. Therefore you must define our own makro if needed (see the code below) + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2021-05-44 4958/332 +*/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 2; // How many digits (numbers) are available on your display +const byte pixelPerDigit = 39; // all pixels, including decimal point pixels if available at each digit +const byte startPixel = 0; // start with this pixel on the strip +const byte addPixels = 4; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34) + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D + + in the following constants you have to define + which pixel number belongs to which segment +*/ + +typedef uint64_t segsize_t; // define the type of segsize_t to 64bit + +#define B64(b) (1ULL << (b)) // remark: don't use the _BV(n) nor bit() makro for 64 bit variables + +const segsize_t segment[8] { + B64(0) | B64(1) | B64(2) | B64(3) | B64(4), // SEG_A + B64(5) | B64(6) | B64(7) | B64(8) | B64(9), // SEG_B + B64(10) | B64(11) | B64(12) | B64(13) | B64(14), // SEG_C + B64(15) | B64(16) | B64(17) | B64(18) | B64(19), // SEG_D + B64(20) | B64(21) | B64(22) | B64(23) | B64(24), // SEG_E + B64(25) | B64(26) | B64(27) | B64(28) | B64(29), // SEG_F + B64(30) | B64(31) | B64(32) | B64(33) | B64(34), // SEG_G + B64(35) | B64(36) | B64(37) | B64(38), // SEG_DP +}; + +/* +// yes you can use the bit notation but it might becomes VERY LONG if you need 64 bits + +const segsize_t segment[8] { + 0b00000000000000000000000000000011111, // SEG_A + 0b00000000000000000000000001111100000, // SEG_B + 0b00000000000000000000111110000000000, // SEG_C + 0b00000000000000011111000000000000000, // SEG_D + 0b00000000001111100000000000000000000, // SEG_E + 0b00000111110000000000000000000000000, // SEG_F + 0b11111000000000000000000000000000000, // SEG_G + 0 // SEG_DP if you don't have a decimal point, just leave it zero +}; +*/ + +const uint16_t ledCount(pixelPerDigit * numDigits + addPixels); + +int offsetLogic_cb(uint16_t position) // your callback function to keep track of additional pixels +{ + uint16_t offset = 0; + if (position > 1 ) offset = addPixels; // example: the additional Pixels are between 2nd and 3rd digit + return offset; // you MUST return a value. It can be 0 +} + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create neopixel object like you commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, startPixel, addPixels, offsetLogic_cb); // create display object, handover the name of your strip as first parameter! + + +void setup() { + Serial.begin(115200); + Serial.println(F("\nNoiascaNeopixelDisplay\n")); + + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(30); // Set BRIGHTNESS (max = 255) +} + +void loop() { + display.clear(); + display.setColorFont(0x008800); + display.print("12"); + delay(3000); + + display.setColorFont(0x880000); + display.print("34"); + delay(3000); +} diff --git a/lib/NoiascaNeopixelDisplay/examples/90_development/90_development.ino b/lib/NoiascaNeopixelDisplay/examples/90_development/90_development.ino new file mode 100644 index 0000000000..571bc197fa --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/examples/90_development/90_development.ino @@ -0,0 +1,88 @@ +/******************************************************************************* + Noiasca Neopixel Display + 90 Development + + my sandbox to do some development tests + + http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + by noiasca + 2020-05-03 migration to segmentArray in user sketch + 2020-05-03 migration to offsetLogic_cb + *******************************************************************************/ + +const byte ledPin = 12; // Which pin on the Arduino is connected to the NeoPixels? +const byte numDigits = 4; // How many digits (numbers) are available on your display? +const byte pixelPerDigit = 16; // all pixel, including decimal point pixels if available at each digit +const byte startPixel = 0; // start with this pixel on the strip +const byte addPixels = 2; // unregular additional pixels to be added to the strip (e.g. a double point for a clock 12:34) + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + in the following constant array you have to define + which pixels belong to which segment +*/ + +typedef uint16_t segsize_t; // fit variable size to your needed pixels. uint16_t --> max 16 Pixel per digit +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP if you don't have a decimal point, just leave it zero +}; + +const uint16_t ledCount(pixelPerDigit * numDigits + addPixels); + +int offsetLogic_cb(uint16_t position) // your callback function to keep track of additional pixels +{ + uint16_t offset = 0; + if (position > 1 ) offset = addPixels; // example: the additional Pixels are between 2nd and 3rd digit + return offset; // you MUST return a value. It can be 0 +} + +#include // install Adafruit library from library manager +Adafruit_NeoPixel strip(ledCount, ledPin, NEO_GRB + NEO_KHZ800); // create Neopixel object like commonly used with Adafruit + +#include // download library from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + +//Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit); // create display object, handover the name of your strip as first parameter! +//Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, startPixel); // create display object, handover the name of your strip as first parameter! +//Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, startPixel, addPixels); // create display object, handover the name of your strip as first parameter! +Noiasca_NeopixelDisplay display(strip, segment, numDigits, pixelPerDigit, startPixel, addPixels, offsetLogic_cb); // create display object, handover the name of your strip as first parameter! + +void setup() { + Serial.begin(115200); + Serial.println(F("90 development only")); + strip.begin(); // INITIALIZE NeoPixel strip object (REQUIRED) + strip.show(); // Turn OFF all pixels ASAP + strip.setBrightness(50); // Set BRIGHTNESS to about 1/5 (max = 255) + + //display.setRightToLeft(); + //display.print("1.8.8.1."); + + //display.write(19); + display.print("88.88"); + //display.print(7); + //display.print(6); + //display.print(1); + + delay(2000); + display.setPixelColor(32 + 0, 0x0000FF); // Assumption: the additional pixel is after two digits + display.setPixelColor(32 + 1, 0x0000FF); // Assumption: there are two additional pixels + display.show(); +} + +void loop() { + // put here other code which needs to run: +} diff --git a/lib/NoiascaNeopixelDisplay/keywords.txt b/lib/NoiascaNeopixelDisplay/keywords.txt new file mode 100644 index 0000000000..501af48e24 --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/keywords.txt @@ -0,0 +1,30 @@ +####################################### +# Syntax Coloring Map For NoiascaLedControl +####################################### + +####################################### +# Datatypes (KEYWORD1) +####################################### + +Noiasca_NeopixelDisplay KEYWORD1 +segsize_t KEYWORD1 + +####################################### +# Methods and Functions (KEYWORD2) +####################################### + +begin KEYWORD2 +clear KEYWORD2 +setColorBack KEYWORD2 +setColorFont KEYWORD2 +setCursor KEYWORD2 +setPixelColor KEYWORD2 +setRightToLeft KEYWORD2 +#setEndOfDevice KEYWORD2 +show KEYWORD2 +writeLowLevel KEYWORD2 + +####################################### +# Constants (LITERAL1) +####################################### + diff --git a/lib/NoiascaNeopixelDisplay/library.properties b/lib/NoiascaNeopixelDisplay/library.properties new file mode 100644 index 0000000000..736c4bdf8e --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/library.properties @@ -0,0 +1,10 @@ +name=Noiasca NeopixelDisplay +version=1.0.1 +author=Werner Rothschopf +maintainer=Werner Rothschopf +sentence=A library for Neopixel based 7 Segment displays. +paragraph=The library supports displays based on WS2812/Neopixel. +category=Display +url=http://http://werner.rothschopf.net/ +architectures=* + diff --git a/lib/NoiascaNeopixelDisplay/src/Noiasca_NeopixelDisplay.h b/lib/NoiascaNeopixelDisplay/src/Noiasca_NeopixelDisplay.h new file mode 100644 index 0000000000..8952a33d20 --- /dev/null +++ b/lib/NoiascaNeopixelDisplay/src/Noiasca_NeopixelDisplay.h @@ -0,0 +1,503 @@ +/* NoaiscaNeopixelDisplay + Simulating a Seven Segment Display with Neopixels + + Download from: http://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + + open tasks + - + + copyright 2022 noiasca noiasca@yahoo.com + + Version + 2020-03-02 first version: https://forum.arduino.cc/index.php?topic=668096.0 + 2020-05-01 multiple pixel per segment idea: https://forum.arduino.cc/index.php?topic=681417 + 2020-05-02 reference to external created strip object (with inputs from https://forum.arduino.cc/index.php?topic=681647.0) + multiple displays on one strip + 2020-05-03 migration to segmentArray in user sketch, + 2020-05-04 callback function for additional pixels + 2021-05-24 1.0.1 fixed lowLevelWrite to leftshift according segsize + 2022-01-16 updated comments for doxygen + Some history seems to be missing... + 2024-05-12 tonhuisman: Use NeoPixelBus_wrapper instead of Adafruit_NeoPixel library (not supported on IDF 5.x) + 2024-08-05 tonhuisman: write() method: Disabled call to strip.show() to be handled externally + 2024-08-16 tonhuisman: writeLowLevel() method: Allow pixel stripes > 255 neopixels by increasing offset counter to uint16_t + 2024-08-20 tonhuisman: introduce NEOPIXEL_DISPLAY_USE_WRITE +*/ + +#pragma once +#define NOIASCA_NEOPIXEL_DISPLAY_VERSION "NoiascaNeopixelDisplay 1.0.1" // this library + +/** + \brief Neopixel Display base + + if you run out of memory and if you only need numbers, you can reduce the size of the sketch by setting this define + + * ******************************************************************/ +#define NEOPIXEL_DISPLAY_CHARSET_SIZE 2 // 2 full charset (default), 1 capital letters, 0 numbers and some symbols only +#define NEOPIXEL_DISPLAY_DEBUG 0 // Library debug messages: 0 off (default); 1 important/error; 2 warning; 3 info/debug; + +//#if defined(__AVR__) +//#include +//#elif defined(ESP8266) +//#include +//#endif + +#ifndef NEOPIXEL_DISPLAY_USE_WRITE +#define NEOPIXEL_DISPLAY_USE_WRITE 1 +#endif + +#include // install library from Library manager + +/* + Segments are named and orded like this + + SEG_A + SEG_F SEG_B + SEG_G + SEG_E SEG_C + SEG_D SEG_DP + + - + The mapping which pixels will be lighted for which character is basically done in 3 steps + + Step 1 + + The user has to assign the pixels to each segment in the user Sketch + example: + +typedef uint32_t segsize_t; +const segsize_t segment[8] { + 0b0000000000000011, // SEG_A + 0b0000000000001100, // SEG_B + 0b0000000000110000, // SEG_C + 0b0000000011000000, // SEG_D + 0b0000001100000000, // SEG_E + 0b0000110000000000, // SEG_F + 0b0011000000000000, // SEG_G + 0b1100000000000000 // SEG_DP +}; + + Step 2 + the library has a character table to map characters to the needed segments + + Step 3 + The write method combines the users segment definition with the character segments to optain + a pixel bitmap to be printed to the display +*/ + +#if NEOPIXEL_DISPLAY_USE_WRITE + +/* ******************************************************************* + character set for 7 segment displays + * ******************************************************************/ + +// each segment is assigned to one of 8 positions (bitwise) + +const byte SEG_A = 1; +const byte SEG_B = 2; +const byte SEG_C = 4; +const byte SEG_D = 8; +const byte SEG_E = 16; +const byte SEG_F = 32; +const byte SEG_G = 64; +const byte SEG_DP = 128; + +// several segments combined is one character +// The bitmap of the segment table only defines which segments belongs to which character + +const static byte charTable [] PROGMEM = { // if you run out of FLASH memory and you only need numbers, you can delete the characters after 57 + 0, // 32 space + SEG_B | SEG_C | SEG_DP, // ! 33 + SEG_B | SEG_F, // " 34 + 0, // # 35 + SEG_A | SEG_C | SEG_D | SEG_F | SEG_G, // $ 36 + SEG_A | SEG_B | SEG_F | SEG_G, // % 37 + 0, // & 38 + SEG_B, // ' 39 + SEG_A | SEG_D | SEG_E | SEG_F, // ( 40 + SEG_A | SEG_B | SEG_C | SEG_D, // ) 41 + 0, // * 42 no character on 7segment + 0, // + 43 no character on 7segment + 0, // , 44 will be handled in the write methode + SEG_G, // - 45 + 0, // . 46 will be handled in the write methode + SEG_B | SEG_E | SEG_G , // / 47 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // 0 48 + SEG_B | SEG_C, // 1 49 + SEG_A | SEG_B | SEG_D | SEG_E | SEG_G, // 2 50 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_G, // 3 51 + SEG_B | SEG_C | SEG_F | SEG_G, // 4 52 + SEG_A | SEG_C | SEG_D | SEG_F | SEG_G, // 5 53 + SEG_A | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // 6 54 + SEG_A | SEG_B | SEG_C, // 7 55 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // 8 56 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_G, // 9 57 +#if NEOPIXEL_DISPLAY_CHARSET_SIZE >= 1 + 0, // : 58 could be handled in the write methode + 0, // ; 59 could be handled in the write methode + SEG_D | SEG_E | SEG_G, // < 60 + SEG_G, // = 61 + SEG_C | SEG_D | SEG_G, // > 62 + SEG_A | SEG_B | SEG_E | SEG_G, // ? 63 + 0, // @ 64 + SEG_A | SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // A 65 + SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // B 66 + SEG_A | SEG_D | SEG_E | SEG_F, // C 67 + SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, // D 68 + SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, // E 69 + SEG_A | SEG_E | SEG_F | SEG_G, // F 70 + SEG_A | SEG_C | SEG_D | SEG_E | SEG_F, // G 71 + SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // H 72 + SEG_B | SEG_C, // I 73 + SEG_B | SEG_C | SEG_D | SEG_E, // J 74 + SEG_A | SEG_C | SEG_E | SEG_F | SEG_G, // K 75 + SEG_D | SEG_E | SEG_F, // L 76 + SEG_A | SEG_C | SEG_E, // M 77 + SEG_C | SEG_E | SEG_G, // N 78 + SEG_C | SEG_D | SEG_E | SEG_G, // O 79 + SEG_A | SEG_B | SEG_E | SEG_F | SEG_G, // P 80 + SEG_A | SEG_B | SEG_C | SEG_F | SEG_G, // Q 81 + SEG_E | SEG_G, // R 82 + SEG_A | SEG_C | SEG_D | SEG_F | SEG_G, // S 83 + SEG_D | SEG_E | SEG_F | SEG_G, // T 84 + SEG_B | SEG_C | SEG_D | SEG_E | SEG_F, // U 85 + SEG_C | SEG_D | SEG_E, // V 86 + SEG_B | SEG_D | SEG_F, // W 87 + SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // X 88 + SEG_B | SEG_C | SEG_D | SEG_F | SEG_G, // Y 89 + SEG_A | SEG_B | SEG_D | SEG_E | SEG_G, // Z 90 + SEG_A | SEG_D | SEG_E | SEG_F, // [ 91 + SEG_C | SEG_F | SEG_G, /* \ 92 backslash*/ + SEG_A | SEG_B | SEG_C | SEG_D, // ] 93 + SEG_A, // ^ 94 + SEG_D, // _ 95 underscore + SEG_B, // ` 96 +#endif +#if NEOPIXEL_DISPLAY_CHARSET_SIZE >= 2 + SEG_C | SEG_D | SEG_E | SEG_G | SEG_DP, // a 97 + SEG_C | SEG_D | SEG_E | SEG_F | SEG_G, // b 98 + SEG_D | SEG_E | SEG_G, // c 99 + SEG_B | SEG_C | SEG_D | SEG_E | SEG_G, // d 100 + SEG_A | SEG_D | SEG_E | SEG_F | SEG_G, // e 101 + SEG_A | SEG_E | SEG_F | SEG_G, // f 102 + SEG_A | SEG_C | SEG_D | SEG_E | SEG_F, // g G 103 capital letter will be used + SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // h 104 + SEG_C, // i 105 + SEG_C | SEG_D, // j 106 + SEG_A | SEG_C | SEG_E | SEG_F | SEG_G, // k 107 + SEG_E | SEG_F, // l 108 + SEG_A | SEG_C | SEG_E, // m n 109 n will be used + SEG_C | SEG_E | SEG_G, // n 110 + SEG_C | SEG_D | SEG_E | SEG_G, // o 111 + SEG_A | SEG_B | SEG_E | SEG_F | SEG_G, // p P 112 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_F | SEG_DP, // q Q 113 + SEG_E | SEG_G, // r 114 + SEG_A | SEG_C | SEG_D | SEG_F | SEG_G, // s S 115 + SEG_D | SEG_E | SEG_F | SEG_G, // t 116 + SEG_C | SEG_D | SEG_E, // u 117 + SEG_C | SEG_D | SEG_E, // v u 118 u will be used + SEG_C | SEG_D | SEG_E, // w u 119 u will be used + SEG_B | SEG_C | SEG_E | SEG_F | SEG_G, // x 120 + SEG_B | SEG_C | SEG_D | SEG_F | SEG_G, // y Y 121 + SEG_A | SEG_B | SEG_D | SEG_E | SEG_G, // z Z 122 + SEG_A | SEG_D | SEG_E | SEG_F, // { 123 + SEG_B | SEG_C, // | 124 + SEG_A | SEG_B | SEG_C | SEG_D, // } 125 + SEG_G, // ~ 126 + SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F | SEG_G | SEG_DP // 127 all segments +#endif +}; + +const byte lastCharacter = sizeof(charTable)/sizeof(charTable[0]) + 32 - 1; // if you use the full charset the lastCharacter will be 127 + +#endif // if NEOPIXEL_DISPLAY_USE_WRITE + +/** ******************************************************************* + \brief Neopixel Display base + + The Noiasca Neopixel Display class can be used for 7 segment displays + made of neopixel / WS2812 LEDs. + + * ******************************************************************/ + +class Noiasca_NeopixelDisplay : public Print { + private: + NeoPixelBus_wrapper& strip; + const uint16_t startPixel; // the first pixel to be used for this display + const segsize_t *segment; + const byte numDigits; // digits per device + const byte pixelPerDigit; // all Pixel, include double point pixels if they are available at each digit + //const byte segPerDigit = 7; // How many segments per digit (not implemented, has to be 7) + const byte addPixels; // unregular additional pixels to be added to the strip + using CallBack = int (*)(uint16_t value); + CallBack funcPtr; + const uint16_t ledCount; // How many NeoPixels are attached to the Arduino inkluding additional digits + + uint16_t currentPosition; // current position of cursor, the positions are order from LEFT (0) to RIGHT (highest) + uint16_t lastPosition; // last position of cursor - needed for dot and comma + uint32_t colorFont = 0xFF0000; // default color of visible segment + uint32_t colorBack = 0x000000; // default background color 0=black + //segsize_t lastBitmap; // stores the last printed segments - for future use + int8_t order = 1; // leftToRight = 1, rightToLeft=-1 + + public: +/** + \brief Constructor with 4 parameters + + The Noiasca Neopixel Display class can be used for 7 segment displays + made of neopixel / WS2812 LEDs. + @param strip a reference to your strip. + @param segment an array with the needed pixels for each segment. + @param numDigits how many digits you have on your display. + @param pixelPerDigit number of used pixels per digit. +*/ + Noiasca_NeopixelDisplay(NeoPixelBus_wrapper& strip, const segsize_t segment[8], byte numDigits, byte pixelPerDigit): + strip(strip), + startPixel(0), + segment{segment}, + numDigits(numDigits), + pixelPerDigit(pixelPerDigit), + addPixels(0), + funcPtr(nullptr), + ledCount(pixelPerDigit * numDigits + addPixels) + {} + +/** + \brief Constructor with 5 parameters + + @param strip a reference to your strip. + @param segment an array with the needed pixels for each segment. + @param numDigits how many digits you have on your display. + @param pixelPerDigit number of used pixels per digit. + @param startPixel if don't start with pixel 0 on the display use the 5th parameter to define the first pixel on the display. +*/ + Noiasca_NeopixelDisplay(NeoPixelBus_wrapper& strip, const segsize_t segment[8], byte numDigits, byte pixelPerDigit, uint16_t startPixel): + strip(strip), + startPixel(startPixel), + segment{segment}, + numDigits(numDigits), + pixelPerDigit(pixelPerDigit), + addPixels(0), + funcPtr(nullptr), + ledCount(pixelPerDigit * numDigits + addPixels) + {} + +/** + \brief Constructor with 7 parameters + + @param strip a reference to your strip. + @param segment an array with the needed pixels for each segment. + @param numDigits how many digits you have on your display. + @param pixelPerDigit number of used pixels per digit. + @param startPixel if don't start with pixel 0 on the display use the 5th parameter to define the first pixel on the display. + @param addPixels if there are more pixels in the chain than used for numbers on the display use this parameter to inform the library about the additional pixels. + @param funcPtr a callback function to calculate the start pixel for each digit. +*/ + Noiasca_NeopixelDisplay(NeoPixelBus_wrapper& strip, segsize_t segment[8], byte numDigits, byte pixelPerDigit, uint16_t startPixel, byte addPixels, CallBack funcPtr): + strip(strip), + startPixel(startPixel), + segment{segment}, + numDigits(numDigits), + pixelPerDigit(pixelPerDigit), + addPixels(addPixels), + funcPtr(funcPtr), + ledCount(pixelPerDigit * numDigits + addPixels) + {} + + virtual ~Noiasca_NeopixelDisplay() = default; + +/** + @brief clear all pixels of the NeoPixel display. + + This method uses Adafruits fill method to clear the definied + pixels of the display. + @note Clearing is done by overwriting all pixels (startPixel to length) with the current background color. +*/ + void clear() + { + //strip.clear(); // library clear uses the fill method + strip.fill(colorBack, startPixel, ledCount); + currentPosition = 0; + } + +/*! + @brief set the background color + + The background color is used if a pixel is "off" according to the character bitmap. + @param newColor the new color to be used for the next write +*/ + void setColorBack(uint32_t newColor) + { + colorBack = newColor; + } + +/*! + @brief set the font color + + The font color is used if a pixel is definied in the bitmap. + @param newColor the new color to be used for the next write. +*/ + void setColorFont(uint32_t newColor) + { + colorFont = newColor; + } + +/*! + @brief set cursor to the specifed cursor + + The order of cursor positions is from LEFT to RIGHT, starting with 0 on the LEFT. + @param newPosition the new cursor position for the display +*/ + void setCursor(uint8_t newPosition) + { + currentPosition = newPosition; + } + +/*! + @brief set a pixel to a specific color + + This gives you direct access to the strip object. + It is a imple passthrough method to the Neopixel strip + @param pixel the pixel you want to change + @param color the new color for this pixel +*/ + void setPixelColor(uint16_t pixel, uint32_t color) + { + strip.setPixelColor(pixel, color); + } + +/*! + @brief reverses the numbering of digits + + If the pixels are wired from RIGHT to LEFT + use this command to reverse the display +*/ + void setRightToLeft() + { + order = -1; + } + +/*! + @brief show the current buffer on the display + + It is a simple passthrough method to the Neopixel strip. +*/ + void show() + { + strip.show(); + } + +/*! + @brief write a bitmap to the display + + This low level write will write a bitmap to the given position of your display. + @param position the position where the bitmap should be printed + @param bitmask the bitmask to be printed + @param addOnly if set to true, the bitmap will be added to the position. By default (false) only the new bitmap will be shown. +*/ + void writeLowLevel(uint8_t position, segsize_t bitmask, bool addOnly = false) { + uint16_t offset = 0; // 2024-08-16 tonhuisman: Changed byte to uint16_t so we can have > 255 pixels on a strip + if (order == 1) // ascending order of digits + { + offset = position * pixelPerDigit + startPixel; // pixel offset = first pixel of this digit + if (funcPtr) // only if available (adress doesn't point to NULL) + offset = offset + funcPtr(position); // the user can define his own ruleset for offset calculation due to additional pixels + } + else // descending order of digits + { + uint16_t additional = 0; + if (funcPtr) + additional = funcPtr(position); + offset = ledCount - pixelPerDigit - position * pixelPerDigit - additional + startPixel; + } +#if NEOPIXEL_DISPLAY_DEBUG >= 3 + Serial.print("p="); + Serial.print(position); + Serial.print(" o="); + Serial.print(offset); + Serial.print(" "); + Serial.println(bitmask, BIN); +#endif + for (uint16_t i = 0; i < pixelPerDigit; i++) // 2024-08-16 tonhuisman: Changed byte to uint16_t + { + if (bitmask & ((segsize_t)1 << i)) // was if (bitmask & (1UL << i)) till 1.0.0 + strip.setPixelColor(i + offset, colorFont); + else + if (!addOnly) strip.setPixelColor(i + offset, colorBack); + } + } + +#if NEOPIXEL_DISPLAY_USE_WRITE + + size_t write(uint8_t value) + { + if (value == '.' || value == ',') + { + writeLowLevel(lastPosition, segment[7], true); // add decimal point to the last printed digit + } + else if (value > 31 && value <= lastCharacter) // write printable ASCII characters to display + { + segsize_t currentBitmap = 0; // the current bitmap + byte segments = 0; // all segments for this character + segments = pgm_read_byte_near(charTable + value - 32); // the table starts with the first printable character at 32 + // step 3: combine the segmentCharacterMapping with the pixels from the users pixelSegmentMapping + for (byte i = 0; i < 8; i++) { + if (segments & (1UL << i)) // UL not necessary, but uses less Flash than if you leave it away + currentBitmap |= segment[i]; + } + writeLowLevel(currentPosition, currentBitmap); + lastPosition = currentPosition; // remember this position just in case next print would be a (decimal) point or comma + currentPosition++; + if (currentPosition >= numDigits) currentPosition = 0; // wrap around + } + // 2024-08-05 tonhuisman: Disabled strip.show() to do it after writing several digits + // strip.show(); // force strip.show() after each single write MISSING tbd if that's really a good idea, alternative: do it manually after the last print + return 1; // assume sucess + } + +#else // if NEOPIXEL_DISPLAY_USE_WRITE + + size_t write(uint8_t value) { +#if NEOPIXEL_DISPLAY_DEBUG >= 3 + Serial.println("write() not implemented..."); +#endif + return 0; + } + +#endif // if NEOPIXEL_DISPLAY_USE_WRITE + +}; + + +/*! \mainpage Some words to the Noiasca Neopixel Display library + + \section intro_sec Introduction + + The "Noiasca Neopixel Display" library is an addon to Adafruits Neopixel library. + It uses the functionalities from Adafruit and makes handling of large displays very easy. + Currently the main focus is the simulation of "seven segment displays". + You can use this library for big clocks, scoreboards and similar use cases. + + The API/the interface uses the official print.h library. Additional comands are roughly based on LCD API 1.0. + For example you can use clear() to clear the display or setCursor() to write on a specific position. + + \section purpose_sec Mapping of Pixels to Segments + To use this library you must assign the used pixels for each segment and define a bitmap array. + This is explaind in detail on the homepage of the Noiasca Neopixel Display. + + \section install_sec Installation + + \subsection step0 Download the library + The newest version of this library can be downloaded from https://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm . + + \section example_sec Examples + + There are several examples please use the hello world for the beginning. + + \subsection step1 Install the library + + In the Arduino IDE use the Menu
    + Sketch / Include Library / Add .ZIP Library
    + to install the library. + */ + \ No newline at end of file diff --git a/src/_P165_7SegNeopixel.ino b/src/_P165_7SegNeopixel.ino new file mode 100644 index 0000000000..f671ea18f9 --- /dev/null +++ b/src/_P165_7SegNeopixel.ino @@ -0,0 +1,129 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P165 + +// ####################################################################################################### +// ################################## Plugin-165: Display - 7-Segment NeoPixel ########################### +// ####################################################################################################### + +/** Changelog: + * 2024-08-22 tonhuisman: Increased segment pixels for height to 5, equal to width + * Increased decimal point digits to 7 (max) and extra pixels to 12 + * When font color is either white or black, uses default color to stay visible in both light and dark Web-UI mode. + * 2024-08-20 tonhuisman: Draws either pixeled digits (edit mode) or the number plan (non-edit mode) + * Commands mostly similar to P073, including standard date/time content, text scrolling and binary content + * Additional RGB(W) colors can be set globally, per group, and per digit, both fontcolor and backcolor + * 2024-08-03 tonhuisman: Allow 1 to 4 groups of 1 to 4 digits, with decimal point (0..4 px) and extra pixels (0..10) per group + * Max pixels: horizontal segments 5/7, vertical segments 4/6. (7/6 with corner-overlap) Optional start-offset. + * Default content: Manual, Date/Time (12/24h), similar to P073 + * Has javascript code to show selected configuration-example + * 2024-05-12 tonhuisman: Initial start of plugin, + * based on Noiasca library from https://werner.rothschopf.net/202005_arduino_neopixel_display_en.htm + */ + +# include "src/PluginStructs/P165_data_struct.h" + +# define PLUGIN_165 +# define PLUGIN_ID_165 165 +# define PLUGIN_NAME_165 "Display - NeoPixel (7-Segment)" + +boolean Plugin_165(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_165; + Device[deviceCount].Type = DEVICE_TYPE_SINGLE; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_165); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P165_CONFIG_GROUPCOUNT = 1; + P165_CONFIG_DEF_BRIGHT = 40; + P165_CONFIG_MAX_BRIGHT = 255; + P165_CONFIG_FG_COLOR = ADAGFX_RED; + + for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { + P165_data_struct::initDigitGroup(event, grp); + } + + success = true; + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_output(F("Strip")); + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P165_data_struct(event)); + + P165_data_struct *P165_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P165_data && P165_data->isInitialized()); + break; + } + + case PLUGIN_READ: + { + // Nothing to see here + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + success = P165_data_struct::plugin_webform_load(event); + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + success = P165_data_struct::plugin_webform_save(event); + break; + } + + case PLUGIN_ONCE_A_SECOND: + { + P165_data_struct *P165_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P165_data && P165_data->plugin_once_a_second(event)); + break; + } + + case PLUGIN_TEN_PER_SECOND: + { + P165_data_struct *P165_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P165_data && P165_data->plugin_ten_per_second(event)); + break; + } + case PLUGIN_WRITE: + { + P165_data_struct *P165_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P165_data && P165_data->plugin_write(event, string)); + break; + } + } + return success; +} + +#endif // ifdef USES_P165 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 8af1f883f4..24666bcadc 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1523,7 +1523,7 @@ To create/register a plugin, you have to : #if !defined(USES_P138) && defined(ESP32) #define USES_P138 // IP5306 #endif -#endif +#endif // ifdef PLUGIN_SET_COLLECTION #ifdef PLUGIN_SET_COLLECTION_A @@ -1549,7 +1549,7 @@ To create/register a plugin, you have to : //#define USES_P099 // XPT2046 Touchscreen #define USES_P105 // AHT10/20/21 #define USES_P134 // A02YYUW -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_A #ifdef PLUGIN_SET_COLLECTION_B #define USES_P069 // LM75A @@ -1564,7 +1564,7 @@ To create/register a plugin, you have to : //#define USES_P109 // ThermoOLED #define USES_P110 // VL53L0X Time of Flight sensor #define USES_P113 // VL53L1X ToF -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_B #ifdef PLUGIN_SET_COLLECTION_C #define USES_P085 // AcuDC24x @@ -1575,7 +1575,7 @@ To create/register a plugin, you have to : #define USES_P111 // RC522 RFID reader #define USES_P143 // I2C Rotary encoders -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_C #ifdef PLUGIN_SET_COLLECTION_D #define USES_P093 // Mitsubishi Heat Pump @@ -1590,7 +1590,7 @@ To create/register a plugin, you have to : #define USES_P117 // SCD30 #define USES_P124 // I2C MultiRelay #define USES_P127 // CDM7160 -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_D #ifdef PLUGIN_SET_COLLECTION_E #define USES_P119 // ITG3205 Gyro @@ -1602,7 +1602,7 @@ To create/register a plugin, you have to : #define USES_P135 // SCD4x #define USES_P144 // Dust - PM1006(K) (Vindriktning) #define USES_P133 // LTR390 UV -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_E #ifdef PLUGIN_SET_COLLECTION_F #ifndef USES_P112 @@ -1631,7 +1631,7 @@ To create/register a plugin, you have to : #define USES_P153 // Environment - SHT4x #endif -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_F #ifdef PLUGIN_SET_COLLECTION_G #ifndef USES_P142 @@ -1662,7 +1662,7 @@ To create/register a plugin, you have to : #define USES_P170 // Input - I2C Liquid level sensor #endif -#endif +#endif // ifdef PLUGIN_SET_COLLECTION_G // Collection of all energy related plugins. #ifdef PLUGIN_ENERGY_COLLECTION @@ -1723,7 +1723,7 @@ To create/register a plugin, you have to : #define USES_P148 // Sonoff POWR3xxD and THR3xxD display #endif -#endif +#endif // ifdef PLUGIN_ENERGY_COLLECTION // Collection of all display plugins. (also NeoPixel) #ifdef PLUGIN_DISPLAY_COLLECTION @@ -1825,7 +1825,10 @@ To create/register a plugin, you have to : #ifndef USES_P148 #define USES_P148 // Sonoff POWR3xxD and THR3xxD display #endif -#endif + #if !defined(USES_P165) && defined(ESP32) + #define USES_P165 // Display - NeoPixel (7-Segment) + #endif +#endif // ifdef PLUGIN_DISPLAY_COLLECTION // Collection of all climate plugins. #ifdef PLUGIN_CLIMATE_COLLECTION @@ -1986,7 +1989,7 @@ To create/register a plugin, you have to : #ifndef USES_C011 #define USES_C011 // HTTP Advanced #endif -#endif +#endif // ifdef PLUGIN_CLIMATE_COLLECTION // Collection of all NeoPixel plugins #ifdef PLUGIN_NEOPIXEL_COLLECTION @@ -2040,7 +2043,10 @@ To create/register a plugin, you have to : #if !defined(USES_P138) && defined(ESP32) #define USES_P138 // IP5306 #endif -#endif + #if !defined(USES_P165) // && defined(ESP32) + #define USES_P165 // Display - NeoPixel (7-Segment) + #endif +#endif // ifdef PLUGIN_NEOPIXEL_COLLECTION #ifdef CONTROLLER_SET_COLLECTION #ifndef USES_C011 @@ -2066,7 +2072,7 @@ To create/register a plugin, you have to : #ifndef USES_C019 // #define USES_C019 // ESPEasy-NOW #endif -#endif +#endif // ifdef CONTROLLER_SET_COLLECTION #ifdef NOTIFIER_SET_COLLECTION @@ -2405,6 +2411,9 @@ To create/register a plugin, you have to : #ifndef USES_P162 #define USES_P162 // Output - MCP42xxx Digipot #endif + #ifndef USES_P165 + #define USES_P165 // Display - NeoPixel (7-Segment) + #endif #ifndef USES_P166 #define USES_P166 // Output - GP8403 DAC 0-10V #endif @@ -2482,7 +2491,7 @@ To create/register a plugin, you have to : #define DISABLE_SOFTWARE_SERIAL #endif -#if defined(USES_P095) || defined(USES_P096) || defined(USES_P116) || defined(USES_P131) || defined(USES_P141) || defined(USES_P123) // Add any plugin that uses AdafruitGFX_Helper +#if defined(USES_P095) || defined(USES_P096) || defined(USES_P116) || defined(USES_P131) || defined(USES_P141) || defined(USES_P123) || defined(USES_P165) // Add any plugin that uses AdafruitGFX_Helper #ifndef PLUGIN_USES_ADAFRUITGFX #define PLUGIN_USES_ADAFRUITGFX // Ensure AdafruitGFX_helper is available for graphics displays (only) #endif @@ -3435,6 +3444,9 @@ To create/register a plugin, you have to : #ifdef USES_P131 #undef USES_P131 #endif + #ifdef USES_P165 + #undef USES_P165 + #endif #endif diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp new file mode 100644 index 0000000000..5fbe2df4a5 --- /dev/null +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -0,0 +1,2035 @@ +#include "../PluginStructs/P165_data_struct.h" + +#ifdef USES_P165 + +P165_data_struct::P165_data_struct(struct EventStruct *event) { + if (!validGpio(CONFIG_PIN1)) { + addLog(LOG_LEVEL_ERROR, F("NeoPixel7Segment: GPIO pin invalid.")); + return; + } + + _stripType = P165_CONFIG_STRIP_TYPE; + _pixelGroups = P165_CONFIG_GROUPCOUNT; + _defBrightness = P165_CONFIG_DEF_BRIGHT; + _maxBrightness = P165_CONFIG_MAX_BRIGHT; + _fgColor = P165_CONFIG_FG_COLOR; + _bgColor = P165_CONFIG_BG_COLOR; + # if P165_FEATURE_P073 && P165_EXTRA_FONTS + _fontset = P165_CONFIG_FONTSET; + # endif // if P165_FEATURE_P073 && P165_EXTRA_FONTS + _scrollSpeed = P165_CONFIG_SCROLLSPEED; + _suppressLeading0 = P165_GET_FLAG_SUPP0; + _txtScrolling = P165_GET_FLAG_SCROLL_TEXT; + _scrollFull = P165_GET_FLAG_SCROLL_FULL; + _stdOffset = P165_GET_FLAG_STD_OFFSET; + + for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { + memcpy(&_pixelGroupCfg[grp], &P165_GROUP_CFG(grp), sizeof(_pixelGroupCfg[grp])); + _pixelGroupCfg[grp].aoffs = 0; + } + + const uint16_t pxlCount = calculateDisplayPixels(); // Needs the _pixelGroupCfg filled + + # if P165_DEBUG_INFO + addLog(LOG_LEVEL_INFO, strformat(F("NeoPixel7Segment: Start stripe for %d pixels."), pxlCount)); + # endif // if P165_DEBUG_INFO + strip = new (std::nothrow) NeoPixelBus_wrapper(pxlCount, CONFIG_PIN1, P165_STRIP_TYPE_RGBW == _stripType + ? NEO_GRBW + NEO_KHZ800 + : NEO_GRB + NEO_KHZ800); + _initialized = (nullptr != strip); + uint16_t pxlOffset = 0; + + if (_initialized) { + strip->begin(); // Start the strip + strip->setBrightness(std::min(_maxBrightness, _defBrightness)); + + int8_t fromGrp = 0; + int8_t toGrp = _pixelGroups; + int8_t incGrp = 1; + bool allRTL = true; + + for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { + allRTL &= _pixelGroupCfg[grp].rtld; + } + + if (allRTL) { // Revert group order if all groups have RTL set + fromGrp = _pixelGroups - 1; + toGrp = -1; + incGrp = -1; + } + + for (int8_t grp = fromGrp; grp != toGrp; grp += incGrp) { + fillSegmentBitmap(grp, _pixelGroupCfg[grp]); + + pxlOffset += _pixelGroupCfg[grp].offs; + const uint8_t pxlDigit = calculateGroupPixels(1, // Count pixels for 1 digit only + _pixelGroupCfg[grp].wpix, + _pixelGroupCfg[grp].hpix, + _pixelGroupCfg[grp].crnr, + _pixelGroupCfg[grp].dotp, + 0); // Extra pixels passed separately + # if P165_DEBUG_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("P165 : group: %d -------- start-pixel: %d"), grp + 1, pxlOffset)); + + for (uint8_t seg = 0; seg < 8; ++seg) { + addLog(LOG_LEVEL_INFO, strformat(F("P165 : segment: %c, bits: 0b%s"), 'a' + seg, + ull2String(bitSetULL(segments[grp][seg], pxlDigit + 2), 2).substring(1).c_str())); + bitClearULL(segments[grp][seg], pxlDigit + 2); // Reset additional bit + } + # endif // if P165_DEBUG_DEBUG + + display[grp] = new (std::nothrow) Noiasca_NeopixelDisplay(*strip, + segments[grp], + _pixelGroupCfg[grp].dgts, + pxlDigit, + pxlOffset, + _pixelGroupCfg[grp].addn, + nullptr /*offsetLogic_callback*/); + pxlOffset += (pxlDigit * _pixelGroupCfg[grp].dgts); + + if (_pixelGroupCfg[grp].addn > 0) { + _pixelGroupCfg[grp].aoffs = pxlOffset; + # if P165_DEBUG_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("P165 : group: %d add-on offset: %d, length: %d"), + grp + 1, _pixelGroupCfg[grp].aoffs, _pixelGroupCfg[grp].addn)); + # endif // if P165_DEBUG_DEBUG + } + pxlOffset += _pixelGroupCfg[grp].addn; + + _initialized = (nullptr != display[grp]); + + if (_initialized) { + display[grp]->setColorFont(AdaGFXrgb565ToRgb888(_fgColor)); + display[grp]->setColorBack(AdaGFXrgb565ToRgb888(_bgColor)); + } + } + } + + if (!_initialized) { // Clean-up on fail + for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { + delete display[grp]; + } + delete strip; + addLog(LOG_LEVEL_ERROR, F("NeoPixel7Segment: Initialization failed.")); + } +} + +int P165_data_struct::offsetLogic_callback(uint16_t position) { + uint16_t offset = 0; + + // if (position > 1 ) offset = addPixels; + return offset; +} + +P165_data_struct::~P165_data_struct() { + for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { + delete display[grp]; + } + delete strip; +} + +/******************************************************************** +* Initialize a single digit group +********************************************************************/ +void P165_data_struct::initDigitGroup(struct EventStruct *event, + uint8_t grp) { + P165_SET_CONFIG_WPIXELS(grp, 2); // Width 2 pixels + P165_SET_CONFIG_HPIXELS(grp, 2); // Height 2 pixels + P165_SET_CONFIG_CORNER(grp, false); + P165_SET_CONFIG_DOT(grp, 1); // 1 pixel per decimal point + P165_SET_CONFIG_EXTRA(grp, 0); + P165_SET_CONFIG_OFFSET(grp, 0); + P165_SET_CONFIG_DIGITS(grp, 1); // 1 digit per group + P165_SET_CONFIG_START(grp, false); + P165_SET_CONFIG_DEND(grp, false); + P165_SET_CONFIG_RTLD(grp, false); +} + +/******************************************************************** +* Show the configuration Web UI +********************************************************************/ +bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { + addJavascript(); + + const uint8_t grpCount = P165_CONFIG_GROUPCOUNT; + + { + const __FlashStringHelper *stripOptions[] = { F("GRB"), F("GRBW") }; + const int stripOptionValues[] = { P165_STRIP_TYPE_RGB, P165_STRIP_TYPE_RGBW }; + addFormSelector(F("Strip Type"), F("stripe"), NR_ELEMENTS(stripOptionValues), stripOptions, stripOptionValues, P165_CONFIG_STRIP_TYPE); + } + + if ((0 == P165_CONFIG_DEF_BRIGHT) && (0 == P165_CONFIG_MAX_BRIGHT)) { + P165_CONFIG_DEF_BRIGHT = 40; + P165_CONFIG_MAX_BRIGHT = 255; + } + + addFormNumericBox(F("Initial brightness"), F("brght"), P165_CONFIG_DEF_BRIGHT, 0, 255); + addUnit(F("0..255")); + addFormNumericBox(F("Maximum allowed brightness"), F("maxbrght"), P165_CONFIG_MAX_BRIGHT, 1, 255); + addUnit(F("1..255")); + + { + # if P165_FEATURE_P073 + P073_display_output_selector(F("dspout"), P165_CONFIG_OUTPUTTYPE); + # else // if P165_FEATURE_P073 + const __FlashStringHelper *displout[] = { + F("Manual"), + F("Clock 24h - Blink"), + F("Clock 24h - No Blink"), + F("Clock 12h - Blink"), + F("Clock 12h - No Blink"), + F("Date"), + }; + const int disploutOptions[] = { + P165_DISP_MANUAL, + P165_DISP_CLOCK24BLNK, + P165_DISP_CLOCK24, + P165_DISP_CLOCK12BLNK, + P165_DISP_CLOCK12, + P165_DISP_DATE, + }; + addFormSelector(F("Display Output"), F("dspout"), NR_ELEMENTS(disploutOptions), + displout, disploutOptions, P165_CONFIG_OUTPUTTYPE); + # endif // if P165_FEATURE_P073 + + int dgtCount = 0; + + for (uint8_t grp = 0; grp < grpCount; ++grp) { + dgtCount += P165_GET_CONFIG_DIGITS(grp); + } + const int maxOffset = max(1, dgtCount + 1); // Minimum width for standard content is 4 digits + addFormNumericBox(F("Start at digit"), F("stdoff"), P165_GET_FLAG_STD_OFFSET + 1, 1, max(1, maxOffset - 4), + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + maxOffset <= 5); + addFormNote(F("Min. 4 digits used for Time/Date Display Output")); + } + + # if P165_FEATURE_P073 && P165_EXTRA_FONTS + P073_font_selector(F("fontset"), P165_CONFIG_FONTSET); + # endif // if P165_FEATURE_P073 && P165_EXTRA_FONTS + + { + addFormSubHeader(F("Options")); + + addFormCheckBox(F("Suppress leading 0 on day/hour"), F("supp0"), P165_GET_FLAG_SUPP0); + + addFormCheckBox(F("Scroll text > display width"), F("scrltxt"), P165_GET_FLAG_SCROLL_TEXT); + addFormCheckBox(F("Scroll text in from right"), F("scrlfll"), P165_GET_FLAG_SCROLL_FULL); + + if (P165_CONFIG_SCROLLSPEED == 0) { P165_CONFIG_SCROLLSPEED = 10; } + addFormNumericBox(F("Scroll speed (0.1 sec/step)"), F("scrlspd"), P165_CONFIG_SCROLLSPEED, 1, 600); + addUnit(F("1..600 = 0.1..60 sec/step")); + } + + addFormSubHeader(F("Display")); + + addFormNote(F("Fields with *: When changed will save and reload the page.")); + + const __FlashStringHelper *digitOptions[] = { F("1"), F("2"), F("3"), F("4") }; + const int digitOptionValues[] = { 1, 2, 3, 4 }; + const __FlashStringHelper *startPixelOptions[] = { F("Left-top"), F("Right-top") }; + + const String fgColor = ADAGFX_WHITE == P165_CONFIG_FG_COLOR || ADAGFX_BLACK == P165_CONFIG_FG_COLOR + ? EMPTY_STRING + : AdaGFXrgb565ToWebColor(P165_CONFIG_FG_COLOR); + + addFormSelector(F("Number of Groups *"), + F("grps"), + NR_ELEMENTS(digitOptionValues), + digitOptions, + digitOptionValues, + grpCount, + true); + + AdaGFXFormForeAndBackColors(F("fgcolor"), + P165_CONFIG_FG_COLOR, + F("bgcolor"), + P165_CONFIG_BG_COLOR); + + const bool numberPlan = P165_GET_FLAG_NUMBERPLAN > 0; + addFormSelector_YesNo(F("Show Pixel number-plan *"), F("nbrpln"), P165_GET_FLAG_NUMBERPLAN, true); + addFormNote(F("When set to Yes will block digit-changes.")); + + int totalPixels = 0; + int totalDigits = 0; + int8_t fromGrp = 0; + int8_t toGrp = grpCount; + int8_t incGrp = 1; + bool allRTL = true; + + for (uint8_t grp = 0; grp < grpCount; ++grp) { + allRTL &= P165_GET_CONFIG_RTLD(grp); + } + + addFormSubHeader(F("Groups and Digits")); + + if (allRTL) { // Revert group order if all groups have RTL set + fromGrp = grpCount - 1; + toGrp = -1; + incGrp = -1; + addFormNote(F("Attention, Groups and Digits are shown in reverse order!")); + } + + int16_t dgtOffset = 0; + + for (int8_t grp = fromGrp; grp != toGrp; grp += incGrp) { + const uint8_t grp10 = grp * 10; + const uint8_t grpDgts = P165_GET_CONFIG_DIGITS(grp); + const uint8_t grpWPxls = P165_GET_CONFIG_WPIXELS(grp); + const uint8_t grpHPxls = P165_GET_CONFIG_HPIXELS(grp); + const uint8_t grpCrnr = P165_GET_CONFIG_CORNER(grp); + const uint8_t grpDotP = P165_GET_CONFIG_DOT(grp); + const uint8_t grpAddN = P165_GET_CONFIG_EXTRA(grp); + const uint8_t grpOffs = P165_GET_CONFIG_OFFSET(grp); + const uint8_t grpRtld = P165_GET_CONFIG_RTLD(grp); + + if (grp != fromGrp) { + addFormSeparator(2); + } + + if (0 == grpWPxls) { // Check for invalid settings + initDigitGroup(event, grp); + } + + addRowLabel(concat(F("Group "), grp + 1)); + + { + html_table(F("")); + + addRowLabel(F("Number of Digits *")); + addSelector(concat(F("dgts"), grp10), + NR_ELEMENTS(digitOptionValues), + digitOptions, + digitOptionValues, nullptr, + grpDgts, + true, !numberPlan); // 1st and 2nd column + + for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { + html_TD(); addHtml(F("Digit ")); // 3rd column = "Digit " + addHtmlInt(static_cast(grpRtld ? (grpDgts - dgt) : (dgt + 1))); + } + } + + addFormNumericBox(F("Segment Width pixels"), concat(F("wdth"), grp10), + grpWPxls, 1, 5, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + + dgtOffset += grpOffs; + + const int16_t dgtPxls = calculateGroupPixels(1, // Count single digit without additional pixels + grpWPxls, + grpHPxls, + grpCrnr, + grpDotP, + 0); + + { + if (grpRtld) { // Take care of Right To Left configured groups + dgtOffset += (dgtPxls * (grpDgts - 1)); + } + const uint16_t grpAoffs = (grpRtld ? (grpDgts - 1) : 0) * dgtPxls + grpOffs; + + for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { + drawSevenSegment(dgt, grp10, // 3rd column = subtable with digit + grpWPxls, + grpHPxls, + grpCrnr, + grpDotP, + grpAddN, + grpDgts - 1, + dgtOffset, + P165_GET_CONFIG_START(grp), + P165_GET_CONFIG_DEND(grp), + fgColor, + numberPlan, + dgt < (grpDgts - 1) ? -1 : grpAoffs, + grpWPxls > P165_SEGMENT_G_SPLIT_SIZE); + dgtOffset += (dgtPxls * (grpRtld ? -1 : 1)); + } + + if (grpRtld) { + dgtOffset += (dgtPxls * (grpDgts + 1)); // Add 'm all back + } + dgtOffset += grpAddN; + } + + { + addFormNumericBox(F("Segment Height pixels"), concat(F("hght"), grp10), + grpHPxls, 1, 5, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + + addFormCheckBox(F("Segment Corners overlap"), concat(F("crnr"), grp10), + grpCrnr, numberPlan); + + addFormNumericBox(F("Decimal pixels"), concat(F("decp"), grp10), + grpDotP, 0, P165_SEGMENT_DOT_PIXELS, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + } + { + addFormNumericBox(F("Extra pixels after"), concat(F("addn"), grp10), + grpAddN, 0, P165_SEGMENT_ADDON_PIXELS, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + + const int grpPixels = calculateGroupPixels(grpDgts, + grpWPxls, + grpHPxls, + grpCrnr, + grpDotP, + grpAddN); + totalPixels += grpPixels; + totalDigits += grpDgts; + + addFormNumericBox(F("Pixels in group"), concat(F("totp"), grp10), + grpPixels, + 0, INT32_MAX, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + true); + } + addFormSeparator(2); + + { + addFormNumericBox(F("Pixel-offset before"), concat(F("offs"), grp10), + grpOffs, 0, 15, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + totalPixels += grpOffs; + + addRowLabel(F("Starting pixel")); + addSelector(concat(F("strt"), grp10), + NR_ELEMENTS(startPixelOptions), + startPixelOptions, + nullptr, nullptr, + P165_GET_CONFIG_START(grp), false, + !numberPlan); + + addFormCheckBox(F("Decimal pixels last segment"), concat(F("dend"), grp10), + P165_GET_CONFIG_DEND(grp), numberPlan); + + addFormCheckBox(F("Right to Left digits"), concat(F("rtld"), grp10), + grpRtld, numberPlan); + + if (grpRtld) { + html_TR_TD(); + addHtml(F("
    ")); + addHtml(F("Any 'Extra pixels after' will be
    positioned left of the group!")); + addHtml(F("
    ")); + } else { + for (uint8_t r = 12; r < 13; ++r) { + html_TR_TD(); + addHtml(F(" ")); // We need 13 rows for the digit table to work as intended + } + } + } + + html_end_table(); + + { + // Bind handlers on input fields to update the 7 segment simulation and digit counts + addHtml(F("\n")); + } + } + + addFormSeparator(2); + + addFormNumericBox(F("Available digits"), F("totdgt"), + totalDigits, + 0, INT32_MAX, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + true); + addFormNumericBox(F("Total pixels to connect"), F("totpx"), + totalPixels, + 0, INT32_MAX, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + true); + + return true; +} + +/******************************************************************** +* Save the updated settings +********************************************************************/ +bool P165_data_struct::plugin_webform_save(struct EventStruct *event) { + const int16_t grps = getFormItemInt(F("grps")); + + P165_CONFIG_GROUPCOUNT = grps; + P165_CONFIG_STRIP_TYPE = getFormItemInt(F("stripe")); + P165_CONFIG_OUTPUTTYPE = getFormItemInt(F("dspout")); + P165_CONFIG_DEF_BRIGHT = getFormItemInt(F("brght")); + P165_CONFIG_MAX_BRIGHT = getFormItemInt(F("maxbrght")); + # if P165_FEATURE_P073 && P165_EXTRA_FONTS + P165_CONFIG_FONTSET = getFormItemInt(F("fontset")); + # endif // if P165_FEATURE_P073 && P165_EXTRA_FONTS + P165_CONFIG_SCROLLSPEED = getFormItemInt(F("scrlspd")); + + P165_SET_FLAG_SUPP0(isFormItemChecked(F("supp0")) ? 1 : 0); + P165_SET_FLAG_SCROLL_TEXT(isFormItemChecked(F("scrltxt")) ? 1 : 0); + P165_SET_FLAG_SCROLL_FULL(isFormItemChecked(F("scrlfll")) ? 1 : 0); + int stdoff = 1; + + if (update_whenset_FormItemInt(F("stdoff"), stdoff)) { + P165_SET_FLAG_STD_OFFSET(stdoff - 1); // Show 1..16, used as offset 0..15 + } + + const bool prevNumberPlan = P165_GET_FLAG_NUMBERPLAN > 0; + + P165_SET_FLAG_NUMBERPLAN(getFormItemInt(F("nbrpln"))); + + String color = webArg(F("fgcolor")); + + P165_CONFIG_FG_COLOR = ADAGFX_RED; // Default to red when empty + + if (!color.isEmpty()) { + P165_CONFIG_FG_COLOR = AdaGFXparseColor(color); // Reduce to rgb565 + } + color = webArg(F("bgcolor")); + P165_CONFIG_BG_COLOR = AdaGFXparseColor(color); // Empty = black + + if (!prevNumberPlan) { // Don't save now as the read-only inputs will return empty values + for (int grp = 0; grp < grps; ++grp) { + const uint8_t grp10 = grp * 10; + P165_SET_CONFIG_WPIXELS(grp, getFormItemInt(concat(F("wdth"), grp10))); + P165_SET_CONFIG_HPIXELS(grp, getFormItemInt(concat(F("hght"), grp10))); + P165_SET_CONFIG_CORNER(grp, isFormItemChecked(concat(F("crnr"), grp10))); + P165_SET_CONFIG_DOT(grp, getFormItemInt(concat(F("decp"), grp10))); + P165_SET_CONFIG_EXTRA(grp, getFormItemInt(concat(F("addn"), grp10))); + P165_SET_CONFIG_OFFSET(grp, getFormItemInt(concat(F("offs"), grp10))); + P165_SET_CONFIG_DIGITS(grp, getFormItemInt(concat(F("dgts"), grp10))); + P165_SET_CONFIG_START(grp, getFormItemInt(concat(F("strt"), grp10))); + P165_SET_CONFIG_DEND(grp, isFormItemChecked(concat(F("dend"), grp10))); + P165_SET_CONFIG_RTLD(grp, isFormItemChecked(concat(F("rtld"), grp10))); + } + } + return true; +} + +/******************************************************************** +* Calculate the number of pixels in a group +********************************************************************/ +uint16_t P165_data_struct::calculateGroupPixels(const uint8_t count, + const uint8_t wpixels, + const uint8_t hpixels, + const bool overlap, + const uint8_t decPt, + const uint8_t addN) { + return count * (3 * wpixels + 4 * hpixels + (overlap ? 6 : 0) + decPt) + addN; +} + +/******************************************************************** +* Calculate the total number of pixels for the entire display +********************************************************************/ +uint16_t P165_data_struct::calculateDisplayPixels() { + uint16_t result = 0; + + for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { + result += _pixelGroupCfg[grp].offs; + result += calculateGroupPixels(_pixelGroupCfg[grp].dgts, + _pixelGroupCfg[grp].wpix, + _pixelGroupCfg[grp].hpix, + _pixelGroupCfg[grp].crnr, + _pixelGroupCfg[grp].dotp, + _pixelGroupCfg[grp].addn); + } + return result; +} + +/******************************************************************** +* Calculate the total number of digits configured +********************************************************************/ +uint16_t P165_data_struct::calculateDisplayDigits() { + uint16_t result = 0; + + for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { + result += _pixelGroupCfg[grp].dgts; + } + return result; +} + +// Draw a 7segment digit with optional decimal point and extra pixels, and max 5-wide segments and 5-high segments +constexpr uint16_t P165_digitMask[13] = { // Regular digit layout, max size + 0b011111000, + 0b100000100, + 0b100000100, + 0b100000100, + 0b100000100, + 0b100000100, + 0b011111001, // Extra pixels after + 0b100000100, + 0b100000100, + 0b100000100, + 0b100000100, + 0b100000100, + 0b011111010, // Decimal point pixels +}; + +constexpr uint16_t P165_digitOverlap[13] = { // Overlap enabled, overlay these pixels + 0b100000100, + 0b000000000, + 0b000000000, + 0b000000000, + 0b000000000, + 0b000000000, + 0b100000100, + 0b000000000, + 0b000000000, + 0b000000000, + 0b000000000, + 0b000000000, + 0b100000100, +}; + +/******************************************************************** +* Draw a 7-segment digit-group by creating a table +* with some rows and columns hidden when not max. size +********************************************************************/ +void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit + const uint8_t grp, // Group * 10 + const uint8_t wpixels, // width pixels + const uint8_t hpixels, // heoght pixels + const bool overlap, // corner overlap + const uint8_t decPt, // decimal point pixels + const uint8_t addN, // additional pixels + const uint8_t max, // max already has 1 subtracted + const uint16_t offset, // pre-offset + const bool strt, // start left-top or right-top + const bool dend, // decimal point at end + const String & fgColor, // foreground color + const bool dspPlan, // show number plan + const int16_t aOffs, // addon offset + const bool splitG) { // split segment G in 2 halves + addHtml(F("")); + addHtml(strformat(F(""), digit + grp)); // Group should be factor 10 + + uint8_t hor = 0; + int8_t ver = 0; + uint8_t seg = 0; + + for (uint8_t h = 0; h < NR_ELEMENTS(P165_digitMask); ++h) { + const bool showRow = !(((h < 6) && (6 - h >= hpixels) && (h > 1)) || + ((h > 6) && (h - 6 > hpixels) && (h < 12))); + addHtml(F("")); // Hide row + } else { + addHtml(F("block'>")); // Show row + + if ((h == 0) || (h == 6)) { + ver = 0; // Restart vertical counter + } else { + ver++; + } + } + + hor = 0; // Restart horizontal counter + + for (uint8_t w = 0; w < 9; ++w) { + String pIndex; + + const bool showCol = !(w > 0 && w >= wpixels && w < 5); + + html_TD(showCol ? F("width:30px;display:inline-block") : F("width:30px;display:none")); + + if (bitRead(P165_digitMask[h], 8 - w) || + (overlap && bitRead(P165_digitOverlap[h], 8 - w))) { + if (dspPlan && showRow && showCol) { // Determine segment for pixel-indexes: + if ((h > 0) && (h < 6)) { // b/f + if (w == 0) { + seg = 5; // f + } else { + seg = 1; // b + } + } else if ((h > 6) && (h < 12)) { // c/e + if (w == 0) { + seg = 4; // e + } else { + seg = 2; // c + } + } else if ((w > 6) && (h == 12)) { // Decimal point + seg = 7; + } else if (h == 6) { // g / Additional pixels + if (w > 6) { + seg = 8; // virtual 9th segment + } else { + seg = 6; // g + } + } else if ((w < 7) && (h == 12)) { + seg = 3; // d + } else { + seg = 0; // a + } + + pIndex = calculatePixelIndex(hor, + ver - 1, + seg, + offset, + wpixels, + hpixels, + overlap, + strt, + dend, + decPt, + addN, + aOffs, + splitG); + + // pIndex = strformat(F("%d/%d/%d"), hor, ver - 1, seg); // For debugging only + hor++; + } + + if (w < 7) { + if (dspPlan) { + addHtml(pIndex); + } else { + if (!fgColor.isEmpty()) { // Colored pixel + addHtml(strformat(F("" P165_PIXEL_CHARACTER ""), fgColor.c_str())); + } else { + addHtml(F(P165_PIXEL_CHARACTER)); // Pixel + } + } + } else if (h == 6) { // Extra pixels after last digit + if ((addN > 0) && (digit == max)) { + if (dspPlan) { + addHtml(pIndex); + } else { + addHtmlInt(addN); // Show number of pixels + } + } else { + addHtml(F(" ")); // None + } + } else if (h == 12) { // Decimal point + if (decPt > 0) { + if (dspPlan) { + addHtml(pIndex); + } else { + addHtmlInt(decPt); // Show number of pixels + } + } else { + addHtml(F(" ")); // None + } + } else { + addHtml('?'); // this shouldn't ever show up... ;-) + } + } else { + addHtml(F(" ")); // No pixel + } + } + } + html_end_table(); +} + +/************************************************************************ + * Segment order maps, depending on configuration settings: + * - top-left or top-right (0/2) + * - dot-after-c or dot-at-end (0/1) + * - g-segment as a single 'block' or split in 2 halves (0/4) + ***********************************************************************/ + +/* *INDENT-OFF* */ +const uint8_t P165_segmentMap[][9] PROGMEM = { // 72 bytes, values: 0..7 segments, 16/26 => 6, 255 = ignore + { 0, 1, 2, 7, 3, 4, 6, 5, 255 }, // top-left, dot after c, order: a, b, c, h, d, e, g, f, - + { 0, 1, 2, 3, 4, 6, 5, 7, 255 }, // top-left, dot as last, order: a, b, c, d, e, g, f, h, - + { 1, 2, 7, 3, 4, 6, 5, 0, 255 }, // top-right, dot after c, order: b, c, h, d, e, g, f, a, - + { 1, 2, 3, 4, 6, 5, 0, 7, 255 }, // top-right, dot as last, order: b, c, d, e, g, f, a, h, - + { 0, 1, 16, 2, 7, 3, 4, 26, 5 }, // top-left, dot after c, split g, order: a, b, g1, c, h, d, e, g2, f + { 0, 1, 16, 2, 3, 4, 26, 5, 7 }, // top-left, dot as last, split g, order: a, b, g1, c, d, e, g2, f, h + { 1, 16, 2, 7, 3, 4, 26, 5, 0 }, // top-right, dot after c, split g, order: b, g1, c, h, d, e, g2, f, a + { 1, 16, 2, 3, 4, 26, 5, 0, 7 }, // top-right, dot as last, split g, order: b, g1, c, d, e, g2, f, a, h +}; +/* *INDENT-ON* */ +constexpr uint8_t P165_segmentCnt = NR_ELEMENTS(P165_segmentMap[0]); + +/************************************************************************* + * Returns the pixel-index as a string to show in the UI for a pixel + ************************************************************************/ +String P165_data_struct::calculatePixelIndex(const uint8_t hor, // horizontal 'pixel' + const int8_t ver, // vertical 'pixel' + const uint8_t seg, // segment + const uint16_t offset, // pre-offset + const uint8_t wpixels, // width pixels + const uint8_t hpixels, // height pixels + const bool overlap, // corner overlap + const bool strt, // start left-top or right-top + const bool dend, // decimal point at end + const uint8_t decPt, // decimal point pixels + const uint8_t addN, // additional pixels + const int16_t aOffs, // additional pixels offset + const bool splitG) { // split G segment in 2 halves + int16_t result(offset); + const uint8_t hpx = wpixels + (overlap ? 2 : 0); // Overlapping pixels checked on horizontal segments + const uint8_t vpx = hpixels; // Vertical pixels + const uint8_t smap = (strt ? 2 : 0) + (dend ? 1 : 0) + (splitG ? 4 : 0); + const uint8_t rh = hpx / 2; // Horizontal half part, right + const uint8_t lh = hpx - rh; // Horizontal half part, left + + result++; // 1-based index + + for (uint8_t spoint = 0; spoint < P165_segmentCnt; ++spoint) { // loop over all segments a..i + const uint8_t segment = pgm_read_byte(&(P165_segmentMap[smap][spoint])); + + if ((0 == segment) || (6 == segment)) { // horizontal segments a/g + for (uint8_t h = 0; h < hpx; ++h) { + if ((seg == segment) && (hor == h)) { + return String(result); + } + result++; + } + } else if (16 == segment) { // horizontal segment g, right half + for (uint8_t h = rh; h < hpx; ++h) { + if ((6 == seg) && (hor == h)) { + return String(result); + } + result++; + } + } else if (26 == segment) { // horizontal segment g, left half + for (uint8_t h = 0; h < hpx - lh; ++h) { + if ((6 == seg) && (hor == h)) { + return String(result); + } + result++; + } + } else if (3 == segment) { // horizontal segment d reversed + for (int8_t h = hpx - 1; h >= 0; --h) { + if ((seg == segment) && (hor == h)) { + return String(result); + } + result++; + } + } else if (7 == segment) { // Decimal point segment + if (seg == segment) { + String res(result); + res.reserve(decPt * 3); + + for (uint8_t dp = 1; dp < decPt; ++dp) { + result++; + char sep = ','; + + if (dp == 2) { sep = ' '; } + res = strformat(F("%s%c%d"), res.c_str(), sep, result); + } + return res; + } else { + result += decPt; + } + } else if ((4 == segment) || (5 == segment)) { // Vertical segments e/f + for (int8_t v = vpx - 1; v >= 0; --v) { + if ((seg == segment) && (ver == v)) { + return String(result); + } + result++; + } + } else if (255 != segment) { // Vertical segments b/c + for (uint8_t v = 0; v < vpx; ++v) { + if ((seg == segment) && (ver == v)) { + return String(result); + } + result++; + } + } + } + + if ((seg == 8) && (addN > 0) && (aOffs >= 0)) { + result += aOffs; + String res(result); + + res.reserve(addN * 4); + + for (uint8_t a = 1; a < addN; ++a) { + result++; + char sep = ','; + + if ((addN > 5) && (a == addN / 2)) { sep = ' '; } + res = strformat(F("%s%c%d"), res.c_str(), sep, result); + } + return res; + } + return F("x"); // We shouldn't get here... +} + +/************************************************************************** + * Setup the pixels to be lit per segment for a digit + *************************************************************************/ +void P165_data_struct::fillSegmentBitmap(const uint8_t grp, + const PixelGroupCfg pixCfg) { + if (grp > _pixelGroups) { return; } + + segsize_t pbit = 0; + const uint8_t hpx = pixCfg.wpix + (pixCfg.crnr ? 2 : 0); // Overlapping pixels checked on horizontal segments + const uint8_t vpx = pixCfg.hpix; // Vertical pixels = height pixels + // smap: Determine the segment(part) order + const uint8_t smap = (pixCfg.strt ? 2 : 0) + (pixCfg.dend ? 1 : 0) + (pixCfg.wpix > P165_SEGMENT_G_SPLIT_SIZE ? 4 : 0); + const uint8_t rh = hpx / 2; // Horizontal half part, right + const uint8_t lh = hpx - rh; // Horizontal half part, left + + for (uint8_t spoint = 0; spoint < P165_segmentCnt; ++spoint) { // loop over all segments a..h + const uint8_t segment = pgm_read_byte(&(P165_segmentMap[smap][spoint])); + + if ((0 == segment) || (3 == segment) || (6 == segment)) { // horizontal segments a/d/g + if (pixCfg.crnr) { + for (uint8_t h = 0; h < hpx; ++h) { + bitSetULL(segments[grp][segment], pbit); + + if ((0 == segment) && (h == 0)) { // left pixel (a,f) + bitSetULL(segments[grp][5], pbit); + } else + if ((0 == segment) && (h == hpx - 1)) { // right pixel (a,b) + bitSetULL(segments[grp][1], pbit); + } else + if ((3 == segment) && (h == 0)) { // right pixel (d,c), (other direction) + bitSetULL(segments[grp][2], pbit); + } else + if ((3 == segment) && (h == hpx - 1)) { // left pixel (d,e), (other direction) + bitSetULL(segments[grp][4], pbit); + } else + if ((6 == segment) && (h == 0)) { // left pixel (g,e,f) + bitSetULL(segments[grp][4], pbit); + bitSetULL(segments[grp][5], pbit); + } else + if ((6 == segment) && (h == hpx - 1)) { // right pixel (g,b,c) + bitSetULL(segments[grp][1], pbit); + bitSetULL(segments[grp][2], pbit); + } + pbit++; // next pixel + } + } else { + for (uint8_t h = 0; h < hpx; ++h) { + bitSetULL(segments[grp][segment], pbit); + pbit++; + } + } + } else if (16 == segment) { // Horizontal segment g, right half + for (uint8_t h = rh; h < hpx; ++h) { + bitSetULL(segments[grp][6], pbit); + + if (pixCfg.crnr && (h == hpx - 1)) { // right pixel (g,b,c) + bitSetULL(segments[grp][1], pbit); + bitSetULL(segments[grp][2], pbit); + } + pbit++; + } + } else if (26 == segment) { // Horizontal segment g, left half + for (uint8_t h = 0; h < hpx - lh; ++h) { + bitSetULL(segments[grp][6], pbit); + + if (pixCfg.crnr && (h == 0)) { // left pixel (g,e,f) + bitSetULL(segments[grp][4], pbit); + bitSetULL(segments[grp][5], pbit); + } + pbit++; + } + } else if (7 == segment) { // Decimal point segment + for (uint8_t dp = 0; dp < pixCfg.dotp; ++dp) { + bitSetULL(segments[grp][segment], pbit); + pbit++; + } + } else if (255 != segment) { // Vertical segments b/c/e/f + for (uint8_t v = 0; v < vpx; ++v) { + bitSetULL(segments[grp][segment], pbit); + pbit++; + } + } + } +} + +/**************************************************************************************** + * Fetch from local file system, flash or CDN + ***************************************************************************************/ +void P165_data_struct::addJavascript() { + serve_JS(JSfiles_e::P165_digit); // Source in static/p165_digit.js, minified script source in src/src/Static/WebStaticData.h +} + +/*************************************************************************** + * Update display with standard content once a second + **************************************************************************/ +bool P165_data_struct::plugin_once_a_second(struct EventStruct *event) { + if (_output == P165_DISP_MANUAL) { + return false; + } + + if ((_output == P165_DISP_CLOCK24BLNK) || + (_output == P165_DISP_CLOCK12BLNK)) { + _timesep = !_timesep; + } else { + _timesep = true; // On + } + + if (_output == P165_DISP_DATE) { + fillBufferWithDate(true, 0, 0, 0, + _suppressLeading0, + _stdOffset); + } else { + fillBufferWithTime(true, 0, 0, 0, + !((_output == P165_DISP_CLOCK24BLNK) || + (_output == P165_DISP_CLOCK24)), + _suppressLeading0, + _stdOffset); + } + writeBufferToDisplay(_currentGroup); + + // FIXME Should probably determine what group(s) are used and set the extra pixels for those groups + extraPixelsState(_currentGroup, _timesep ? 1 : 0, AdaGFXrgb565ToRgb888(_timesep ? _fgColor : _bgColor)); + + return true; +} + +/*************************************************************************** + * Scroll text in 0.1 second steps + **************************************************************************/ +bool P165_data_struct::plugin_ten_per_second(struct EventStruct *event) { + if ((_output != P165_DISP_MANUAL) || !isScrollEnabled()) { + return false; + } + + if (nextScroll()) { + writeBufferToDisplay(_currentGroup); + } + return true; +} + +/************************************************************************* + * Handle commands, quite compatible with P073 + ************************************************************************/ +const char p165_commands[] PROGMEM = + "7dtext|" + # if P165_FEATURE_P073 + "7dfont|" + # endif // if P165_FEATURE_P073 + "7dbin|" + "7dextra|" + "7don|" + "7doff|" + "7db|" + "7output|" + "7dgroup|" + "7color|" + "7digit|" + # if P165_FEATURE_DIGITCOLOR + "7digitcolor|" + # endif // if P165_FEATURE_DIGITCOLOR + # if P165_FEATURE_GROUPCOLOR + "7groupcolor|" + # endif // if P165_FEATURE_GROUPCOLOR +; +enum class p165_commands_e : int8_t { + invalid = -1, + c7dtext, + # if P165_FEATURE_P073 + c7dfont, + # endif // if P165_FEATURE_P073 + c7dbin, + c7dextra, + c7don, + c7doff, + c7db, + c7output, + c7group, + c7color, + c7digit, + # if P165_FEATURE_DIGITCOLOR + c7digitcolor, + # endif // if P165_FEATURE_DIGITCOLOR + # if P165_FEATURE_GROUPCOLOR + c7groupcolor, + # endif // if P165_FEATURE_GROUPCOLOR +}; + +bool P165_data_struct::plugin_write(struct EventStruct *event, + const String & string) { + const String cmd_s = parseString(string, 1); + + const int cmd_i = GetCommandCode(cmd_s.c_str(), p165_commands); + + if (cmd_i < 0) { return false; } // Fail fast + + const bool currentScroll = isScrollEnabled(); // Save current state + bool newScroll = currentScroll; // keep scroll if command changes + + const p165_commands_e cmd = static_cast(cmd_i); + + const String text = parseStringToEndKeepCase(string, 2); + bool success = false; + bool displayon = false; + + switch (cmd) { + case p165_commands_e::c7dtext: // Set text to show + setScrollEnabled(0 == _currentGroup); // Scrolling allowed for 7dtext command on entire display only + return plugin_write_7dtext(text); + break; + # if P165_FEATURE_P073 + case p165_commands_e::c7dfont: // Select font + return plugin_write_7dfont(event, text); + break; + # endif // if P165_FEATURE_P073 + case p165_commands_e::c7dbin: // Send binary data + setScrollEnabled(0 == _currentGroup); // Scrolling allowed for 7dbin command on entire display only + return plugin_write_7dbin(text); + break; + case p165_commands_e::c7don: // Display on + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("Neo7Seg : Display ON")); + # endif // ifndef BUILD_NO_DEBUG + displayon = true; + success = true; + break; + case p165_commands_e::c7doff: // Display off + # ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, F("Neo7Seg : Display OFF")); + # endif // ifndef BUILD_NO_DEBUG + displayon = false; + success = true; + break; + case p165_commands_e::c7db: // Set brightness + + if ((event->Par1 >= 0) && (event->Par1 < 256)) { + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, concat(F("Neo7Seg : Brightness="), event->Par1)); + } + # endif // ifndef BUILD_NO_DEBUG + _defBrightness = event->Par1; + P165_CONFIG_DEF_BRIGHT = event->Par1; + displayon = true; + success = true; + } + break; + case p165_commands_e::c7output: // 7output, : Select display output + + if ((event->Par1 >= 0) && (event->Par1 < 6)) { // 0:"Manual",1:"Clock 24h - Blink",2:"Clock 24h - No Blink", + // 3:"Clock 12h - Blink",4:"Clock 12h - No Blink",5:"Date" + # if P165_DEBUG_INFO + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, concat(F("Neo7Seg : Display output="), event->Par1)); + } + # endif // if P165_DEBUG_INFO + _output = event->Par1; + P165_CONFIG_OUTPUTTYPE = event->Par1; + displayon = true; + success = true; + + if (event->Par1 != 0) { newScroll = false; } // Scrolling only for Manual + } + break; + case p165_commands_e::c7group: // 7group, : 0 = global, 1..configured groups + + if ((event->Par1 >= 0) && (event->Par1 <= _pixelGroups)) { + _currentGroup = event->Par1; + success = true; + } + break; + case p165_commands_e::c7color: // 7color,fg_r,fg_g,fg_b[,fg_w],bg_r,bg_g,bg_b[,bg_w] + { + const bool rgbW = P165_STRIP_TYPE_RGBW == _stripType; + uint32_t fgColor = 0; + uint32_t bgColor = 0; + bool fgSet = false; + bool bgSet = false; + + if (parseRGBWColors(parseStringToEnd(string, 2), rgbW, fgColor, bgColor, fgSet, bgSet)) { + uint8_t from = 0; + uint8_t to = _pixelGroups; + + if (_currentGroup > 0) { + from = _currentGroup - 1; + to = _currentGroup; + } else { + if (fgSet) { _fgColor = AdaGFXrgb888ToRgb565(fgColor); } + + if (bgSet) { _bgColor = AdaGFXrgb888ToRgb565(bgColor); } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("Neo7Dgt: Colors: FG: 0x%08x BG: 0x%08x"), fgColor, bgColor)); + } + } + + for (uint8_t i = from; i < to; ++i) { + if (fgSet) { + display[i]->setColorFont(fgColor); + success = true; + } + + if (bgSet) { + display[i]->setColorBack(bgColor); + success = true; + } + + if (success && (_currentGroup > 0) && loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("Neo7Dgt: Group: %d colors: FG: 0x%08x BG: 0x%08x"), i + 1, fgColor, bgColor)); + } + } + } + break; + } + case p165_commands_e::c7digit: // 7digit,,,[.] : write a single character to a digit, with optional period + // Scrolling not allowed for 7digit command + newScroll = false; + success = plugin_write_7digit(text); + break; + case p165_commands_e::c7dextra: // 7dextra,,<0|1|2|3|4|5>[,,,[,]] : Set the 'Extra pixels after' on/off + // with optional color, group 0 sets the color to all groups additional pixels + { + const uint8_t par1 = event->Par1; + + if (((0 == par1) || ((par1 > 0) && (par1 <= _pixelGroups) && + (_pixelGroupCfg[par1 - 1].addn > 0))) && // single group have extra pixels after? + !parseString(string, 3).isEmpty()) { // on/off is given + const bool rgbW = P165_STRIP_TYPE_RGBW == _stripType; + const bool onState = 1 == (event->Par2 & 0x01); // Check for On (1/3/5) or Off (0/2/4) + uint32_t fgColor = 0; + uint32_t bgColor = 0; // Ignored! + bool fgSet = false; + bool bgSet = false; // Ignored! + + parseRGBWColors(parseStringToEnd(string, 4), rgbW, fgColor, bgColor, fgSet, bgSet); + const uint32_t newColor = onState + ? (fgSet ? fgColor : AdaGFXrgb565ToRgb888(_fgColor)) + : (fgSet ? fgColor : AdaGFXrgb565ToRgb888(_bgColor)); + + extraPixelsState(par1, event->Par2, newColor); + success = true; + } + break; + } + # if P165_FEATURE_DIGITCOLOR + case p165_commands_e::c7digitcolor: // 7digitcolor,grp,dgt,[-|fg_r,fg_g,fg_b[,fg_w][,bg_r,bg_g,bg_b[,bg_w]]] + # endif // if P165_FEATURE_DIGITCOLOR + # if P165_FEATURE_GROUPCOLOR + case p165_commands_e::c7groupcolor: // 7digitcolor,grp,[-|fg_r,fg_g,fg_b[,fg_w][,bg_r,bg_g,bg_b[,bg_w]]] + # endif // if P165_FEATURE_GROUPCOLOR + # if P165_FEATURE_DIGITCOLOR || P165_FEATURE_GROUPCOLOR + { + const bool rgbW = P165_STRIP_TYPE_RGBW == _stripType; + uint8_t grp = 0; + uint8_t dgt = 0; + uint32_t fgColor = 0; + uint32_t bgColor = 0; + const bool grpColor = + # if P165_FEATURE_GROUPCOLOR + p165_commands_e::c7groupcolor == cmd; + # else // if P165_FEATURE_GROUPCOLOR + false; + # endif // if P165_FEATURE_GROUPCOLOR + bool fgSet = false; + bool bgSet = false; + + // First 2 arguments: group and digit + if ((event->Par1 > 0) && (event->Par1 <= _pixelGroups)) { + grp = event->Par1; + } + + if ((grp > 0) && (grpColor || ((event->Par2 > 0) && (event->Par2 <= _pixelGroupCfg[grp - 1].dgts)))) { + dgt = event->Par2; + } + + if (((0 == dgt) && !grpColor) || (grpColor && (0 == grp))) { + break; // Invalid grp or dgt arguments + } + + const uint16_t grp100dgt = 0x100 + (grp << 4) + (dgt - 1); + const uint16_t grp200dgt = 0x200 + (grp << 4) + (dgt - 1); + const uint16_t grp300 = 0x300 + (grp << 4); + const uint16_t grp400 = 0x400 + (grp << 4); + const String par4 = parseString(string, 4); + + // Next argument: delete digit/groupcolor? + if (equals(par4, F("-"))) { // use dash to remove a digit/group color setting + auto it = digitColors.find(grpColor ? grp300 : grp100dgt); // fg color + + if (it != digitColors.end()) { + digitColors.erase(it); + } + it = digitColors.find(grpColor ? grp400 : grp200dgt); // bg color + + if (it != digitColors.end()) { + digitColors.erase(it); + } + success = true; + } else + if (parseRGBWColors(parseStringToEnd(string, 4), rgbW, fgColor, bgColor, fgSet, bgSet)) { + if (fgSet) { + auto it = digitColors.find(grpColor ? grp300 : grp100dgt); // fg color + + if (it != digitColors.end()) { // Update fg color + it->second = fgColor; + } else { + digitColors.emplace(grpColor ? grp300 : grp100dgt, fgColor); // New fg color + } + success = true; + } + + if (bgSet) { + auto it = digitColors.find(grpColor ? grp400 : grp200dgt); // bg color + + if (it != digitColors.end()) { // Update bg color + it->second = bgColor; + } else { + digitColors.emplace(grpColor ? grp400 : grp200dgt, bgColor); // New bg color + } + success = true; + } + } + # if P165_DEBUG_DEBUG + + for (auto const& it : digitColors) { + addLog(LOG_LEVEL_INFO, strformat(F("P165 : 7digit/groupcolor key: 0x%03x color: 0x%08x"), it.first, it.second)); + } + # endif // if P165_DEBUG_DEBUG + break; + } + # endif // if P165_FEATURE_DIGITCOLOR || P165_FEATURE_GROUPCOLOR + case p165_commands_e::invalid: + break; + } + + if (success) { + if (displayon) { + // TODO (On) + } else { + // TODO (Off) + } + setScrollEnabled(newScroll); + + strip->setBrightness(std::min(_maxBrightness, _defBrightness)); + } + return success; +} + +/************************************************************************************** + * set the state (color) of the extra pixels of 1 or all(0) groups, + * state: 0/1: all off/on, 2/3: first half, 4/5: second half + *************************************************************************************/ +bool P165_data_struct::extraPixelsState(uint8_t group, uint8_t state, uint32_t color) { + const uint8_t pxPart = (state >> 1); // Check what part to switch, cut off lowest bit + const uint8_t fromGrp = 0 == group ? 0 : group - 1; // All or 1 group + const uint8_t toGrp = 0 == group ? _pixelGroups : group; + + for (uint8_t grp = fromGrp; grp < toGrp; ++grp) { + if (_pixelGroupCfg[grp].addn > 0) { + uint16_t pxFrom = 0; + uint16_t pxTo = _pixelGroupCfg[grp].addn; + + if ((_pixelGroupCfg[grp].addn > 1) && (pxPart > 0)) { // 0 = full block + if (1 == pxPart) { // 2/3 first half off/on + pxTo = _pixelGroupCfg[grp].addn / 2; + } else { // 4/5 second half off/on + pxFrom = _pixelGroupCfg[grp].addn / 2; + } + } + + for (uint16_t i = pxFrom; i < pxTo; ++i) { + display[grp]->setPixelColor(i + _pixelGroupCfg[grp].aoffs, color); + } + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, + strformat(F("Neo7Dgt: Set group %d extra pixels %d..%d to color 0x%08x"), + grp + 1, + _pixelGroupCfg[grp].aoffs + 1 + pxFrom, + _pixelGroupCfg[grp].aoffs + pxTo, + color)); + } + } + } + + strip->show(); // Show the output + return true; +} + +/********************************************************************************************** + * Parse a string to foreground and background RGB or RGBW values, each requires 3 or 4 values + *********************************************************************************************/ +bool P165_data_struct::parseRGBWColors(const String& string, + bool rgbW, + uint32_t & fgColor, + uint32_t & bgColor, + bool & fgSet, + bool & bgSet) { + uint32_t clr_r = 0; + uint32_t clr_g = 0; + uint32_t clr_b = 0; + uint32_t clr_w = 0; + uint8_t rgbI = 0; + + String par_r = parseString(string, 1); + String par_g = parseString(string, 2); + String par_b = parseString(string, 3); + String par_w; + + if (rgbW) { par_w = parseString(string, 4); } + + // Foreground color check + if (!par_r.isEmpty() && !par_g.isEmpty() && !par_b.isEmpty() && (!rgbW || !par_w.isEmpty())) { + validUIntFromString(par_r, clr_r); // No value check as value is &-limited + validUIntFromString(par_g, clr_g); + validUIntFromString(par_b, clr_b); + + if (rgbW) { validUIntFromString(par_w, clr_w); } + + fgColor = ((clr_r & 0xFF) << 16) + ((clr_g & 0xFF) << 8) + (clr_b & 0xFF) + ((clr_w & 0xFF) << 24); + fgSet = true; + } + + // Background color check + if (rgbW) { rgbI++; } // Extra offset needed + par_r = parseString(string, 4 + rgbI); + par_g = parseString(string, 5 + rgbI); + par_b = parseString(string, 6 + rgbI); + + if (rgbW) { par_w = parseString(string, 7 + rgbI); } + + if (!par_r.isEmpty() && !par_g.isEmpty() && !par_b.isEmpty() && (!rgbW || !par_w.isEmpty())) { + validUIntFromString(par_r, clr_r); // No value check as value is &-limited + validUIntFromString(par_g, clr_g); + validUIntFromString(par_b, clr_b); + + if (rgbW) { + clr_w = 0; + validUIntFromString(par_w, clr_w); + } + + bgColor = ((clr_r & 0xFF) << 16) + ((clr_g & 0xFF) << 8) + (clr_b & 0xFF) + ((clr_w & 0xFF) << 24); + bgSet = true; + } + return fgSet || bgSet; // Skipping all arguments is not a valid outcome +} + +/****************************************************************************************** + * Write some text to the buffer to display. Scroll if that's enabled and text doesn't fit + *****************************************************************************************/ +bool P165_data_struct::plugin_write_7dtext(const String& text) { + if (_output != P165_DISP_MANUAL) { + return false; + } + # ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLogMove(LOG_LEVEL_INFO, strformat(F("Neo7Seg : Show Text=%s, group %d"), text.c_str(), _currentGroup)); + } + # endif // ifndef BUILD_NO_DEBUG + setTextToScroll(EMPTY_STRING); + const uint8_t bufLen = calculateDisplayDigits(); + + if (isScrollEnabled() && (getEffectiveTextLength(text) > bufLen)) { + setTextToScroll(text); + } else { + fillBufferWithString(text); + + writeBufferToDisplay(_currentGroup); + } + return true; +} + +# if P165_FEATURE_P073 + +/*********************************************************************** + * Select a font by name or number + **********************************************************************/ +bool P165_data_struct::plugin_write_7dfont(struct EventStruct *event, + const String & text) { + if (!text.isEmpty()) { + const int32_t fontNr = P073_parse_7dfont(event, text); + # if P165_DEBUG_INFO + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("P165 7dfont,%s -> %d"), parseString(text, 1).c_str(), fontNr)); + } + # endif // if P165_DEBUG_INFO + + if ((fontNr >= 0) && (fontNr <= 3)) { + _fontset = fontNr; + P165_CONFIG_FONTSET = fontNr; + return true; + } + } + return false; +} + +# endif // if P165_FEATURE_P073 + +/********************************************************************************************** + * Write binary data into the buffer to display, provide bytes with data matching the segments + *********************************************************************************************/ +bool P165_data_struct::plugin_write_7dbin(const String& text) { + if (!text.isEmpty()) { + String data; + int32_t byteValue{}; + int arg = 1; + String argValue = parseString(text, arg); + + while (!argValue.isEmpty()) { + if (validIntFromString(argValue, byteValue) && (byteValue < 256) && (byteValue > -1)) { + data += static_cast(byteValue); + } + arg++; + argValue = parseString(text, arg); + } + # if P165_DEBUG_INFO + addLog(LOG_LEVEL_INFO, strformat(F("7dbin: text: %s, data len: %d"), text.c_str(), data.length())); + # endif // if P165_DEBUG_INFO + const uint8_t bufLen = calculateDisplayDigits(); + + if (!data.isEmpty()) { + setTextToScroll(EMPTY_STRING); // Clear any scrolling text + + if (isScrollEnabled() && (data.length() > bufLen)) { + setBinaryData(data); + } else { + fillBufferWithString(data, true); + + writeBufferToDisplay(_currentGroup); + } + return true; + } + } + return false; +} + +/**************************************************************************** + * Write a character to an explicit digit in a group + ***************************************************************************/ +bool P165_data_struct::plugin_write_7digit(const String& text) { + if (!text.isEmpty()) { + uint32_t grp = 0; + uint32_t dgt = 0; + const String data = parseStringKeepCase(text, 3); + + if (validUIntFromString(parseString(text, 1), grp) && (grp > 0) && (grp <= _pixelGroups) && + validUIntFromString(parseString(text, 2), dgt) && (dgt > 0) && (dgt <= _pixelGroupCfg[grp - 1].dgts) && + !data.isEmpty()) { + grp--; + dgt--; + # if P165_FEATURE_P073 + const uint8_t cdata = P073_mapCharToFontPosition(data.charAt(0), _fontset); + # else // if P165_FEATURE_P073 + const uint8_t cdata = data.charAt(0); + # endif // if P165_FEATURE_P073 + writeCharacterToDisplay(grp, dgt, cdata, data.length() > 1 && data.charAt(1) == '.'); + strip->show(); + return true; + } + } + return false; +} + +/********************************************************************************** + * Put a string into the buffer, periods are smashed into the previous digit + *********************************************************************************/ +void P165_data_struct::fillBufferWithString(const String& textToShow, + bool useBinaryData) { + _binaryData = useBinaryData; + clearBuffer(); + const int txtlength = textToShow.length(); + uint8_t bufLen = calculateDisplayDigits(); + + int p = 0; + + if (_currentGroup > 0) { + bufLen = _pixelGroupCfg[_currentGroup].dgts; // Adjust length + + for (uint8_t grp = 0; grp < _currentGroup - 1; ++grp) { // Find current group start digit + p += _pixelGroupCfg[grp].dgts; // Add offset + } + } + + for (int i = 0; i < txtlength && p <= bufLen; ++i) { // p <= bufLen to allow a period after last digit + if (_periods + && (textToShow.charAt(i) == '.') + && !_binaryData + ) { // If setting periods true + if (p == 0) { // Text starts with a period, becomes a space with a dot + showperiods[p] = true; + showbuffer[p] = + # if P165_FEATURE_P073 + 10 // space in 7dgt fonts + # else // if P165_FEATURE_P073 + ' ' // space + # endif // if P165_FEATURE_P073 + ; + p++; + } else { + // if (p > 0) { + showperiods[p - 1] = true; // The period displays as a dot on the previous digit! + } + + if ((i > 0) && (textToShow.charAt(i - 1) == '.')) { // Handle consecutive periods + p++; + + if ((p - 1) < bufLen) { + showperiods[p - 1] = true; // The period displays as a dot on the previous digit! + } + } + } else if (p < bufLen) { + # if P165_FEATURE_P073 + showbuffer[p] = useBinaryData + ? textToShow.charAt(i) + : P073_mapCharToFontPosition(textToShow.charAt(i), _fontset); + # else // if P165_FEATURE_P073 + showbuffer[p] = textToShow.charAt(i); + # endif // if P165_FEATURE_P073 + p++; + } + } + # if P165_DEBUG_INFO + logBufferContent(F("7dtext")); + # endif // if P165_DEBUG_INFO +} + +void P165_data_struct::setBinaryData(const String& data) { + _binaryData = true; + setTextToScroll(data); + _binaryData = true; // is reset in setTextToScroll + _scrollCount = _scrollSpeed; + _scrollPos = 0; +} + +void P165_data_struct::fillBufferWithTime(const bool sevendgt_now, + uint8_t sevendgt_hours, + uint8_t sevendgt_minutes, + uint8_t sevendgt_seconds, + const bool flag12h, + const bool suppressLeading0, + const uint8_t offset) { + clearBuffer(); + + if (sevendgt_now) { + sevendgt_hours = node_time.hour(); + sevendgt_minutes = node_time.minute(); + sevendgt_seconds = node_time.second(); + } + + if (flag12h && (sevendgt_hours > 12)) { + sevendgt_hours -= 12; // if flag 12h is TRUE and h>12 adjust subtracting 12 + } + + if (flag12h && (sevendgt_hours == 0)) { + sevendgt_hours = 12; // if flag 12h is TRUE and h=0 adjust to h=12 + } + put4NumbersInBuffer(sevendgt_hours, sevendgt_minutes, sevendgt_seconds, -1, suppressLeading0, offset); +} + +void P165_data_struct::fillBufferWithDate(const bool sevendgt_now, + uint8_t sevendgt_day, + uint8_t sevendgt_month, + const int sevendgt_year, + const bool suppressLeading0, + const uint8_t offset) { + clearBuffer(); + int sevendgt_year0 = sevendgt_year; + + if (sevendgt_now) { + sevendgt_day = node_time.day(); + sevendgt_month = node_time.month(); + sevendgt_year0 = node_time.year(); + } else if (sevendgt_year0 < 100) { + sevendgt_year0 += 2000; + } + const uint8_t sevendgt_year1 = static_cast(sevendgt_year0 / 100); + const uint8_t sevendgt_year2 = static_cast(sevendgt_year0 % 100); + + put4NumbersInBuffer(sevendgt_day, sevendgt_month, sevendgt_year1, sevendgt_year2, suppressLeading0, offset); +} + +void P165_data_struct::put4NumbersInBuffer(const uint8_t nr1, + const uint8_t nr2, + const uint8_t nr3, + const int8_t nr4, + const bool suppressLeading0, + const uint8_t offset) { + uint8_t cOffs = 0; + + # if !P165_FEATURE_P073 + cOffs = '0'; // Fallback to ASCII if P073 not available + # endif // if !P165_FEATURE_P073 + + # if P165_DEBUG_DEBUG + addLog(LOG_LEVEL_INFO, + strformat(F("P165 : put4NumbersInBuffer: a:%d b:%d c:%d d:%d cOffs:%d offset:%d"), + nr1, nr2, nr3, nr4, cOffs, offset)); + # endif // if P165_DEBUG_DEBUG + + // offset shouldn't ever be > buffer size - 4 + showbuffer[0 + offset] = static_cast((nr1 / 10) + cOffs); + showbuffer[1 + offset] = (nr1 % 10) + cOffs; + showbuffer[2 + offset] = static_cast((nr2 / 10) + cOffs); + showbuffer[3 + offset] = (nr2 % 10) + cOffs; + + if ((nr3 > -1) && ((5 + offset) < P165_SHOW_BUFFER_SIZE)) { + showbuffer[4 + offset] = static_cast((nr3 / 10) + cOffs); + showbuffer[5 + offset] = (nr3 % 10) + cOffs; + } + + if ((nr4 > -1) && ((7 + offset) < P165_SHOW_BUFFER_SIZE)) { + showbuffer[6 + offset] = static_cast((nr4 / 10) + cOffs); + showbuffer[7 + offset] = (nr4 % 10) + cOffs; + } + + if (suppressLeading0 && (showbuffer[0 + offset] == cOffs)) { + showbuffer[0 + offset] = + # if P165_FEATURE_P073 + 10 + # else // if P165_FEATURE_P073 + ' ' + # endif // if P165_FEATURE_P073 + ; // set to space + } +} + +/*********************************************** + * Nothing in the buffer + **********************************************/ +void P165_data_struct::clearBuffer() { + memset(showbuffer, + _binaryData ? 0 : + # if P165_FEATURE_P073 + 10, + # else // if P165_FEATURE_P073 + ' ', // space + # endif // if P165_FEATURE_P073 + sizeof(showbuffer)); + + for (uint8_t i = 0; i < P165_SHOW_BUFFER_SIZE; ++i) { + showperiods[i] = false; + } +} + +/******************************************************************************************** + * Write a single character-mask to a digit + *******************************************************************************************/ +void P165_data_struct::writeCharacterToDisplay(uint8_t group, uint8_t digit, uint8_t character, bool period) { + uint8_t data = + # if P165_FEATURE_P073 + _binaryData ? + # endif // if P165_FEATURE_P073 + character + # if P165_FEATURE_P073 // Use P073 7-segment fonts and buffer content when available + : P073_getFontChar(character, _fontset) + # endif // if P165_FEATURE_P073 + ; + + if (!_binaryData && _periods && period) { + data |= 0x80; // Decimal point On + } + + display[group]->setCursor(digit); // Have to move the cursor ourselves for writeLowLevel and at pos. 0 + # if P165_FEATURE_P073 + display[group]->writeLowLevel(digit, digit2SegmentMap(group, data)); + # else // if P165_FEATURE_P073 + + if (_binaryData) { + display[group]->writeLowLevel(digit, digit2SegmentMap(group, data)); + } else { + display[group]->write(data); // Fallback to ASCII in buffer and built-in font if P073 not available + } + # endif // if P165_FEATURE_P073 +} + +/************************************************************************************* + * Send current buffer content to the display + ************************************************************************************/ +void P165_data_struct::writeBufferToDisplay(uint8_t group) { + uint8_t i = 0; + const uint8_t from = (group == 0 ? 0 : group - 1); + const uint8_t to = (group == 0 ? _pixelGroups : std::min(group, _pixelGroups)); + + # if P165_DEBUG_DEBUG + String log; + # endif // if P165_DEBUG_DEBUG + + for (uint8_t grp = from; grp < to; ++grp) { + # if P165_FEATURE_GROUPCOLOR + bool fgColorGrp = false; + bool bgColorGrp = false; + const uint16_t grp300 = 0x300 + ((grp + 1) << 4); + const uint16_t grp400 = 0x400 + ((grp + 1) << 4); + auto itgrp = digitColors.find(grp300); // fg color + + if (itgrp != digitColors.end()) { + display[grp]->setColorFont(itgrp->second); + fgColorGrp = true; + } + itgrp = digitColors.find(grp400); // bg color + + if (itgrp != digitColors.end()) { + display[grp]->setColorBack(itgrp->second); + bgColorGrp = true; + } + # endif // if P165_FEATURE_GROUPCOLOR + + for (uint8_t dgt = 0; dgt < _pixelGroupCfg[grp].dgts; ++dgt) { + # if P165_DEBUG_DEBUG + + if ((grp == from) && (dgt == 0)) { + log += F("P165 : BufToDisp: "); + } + # endif // if P165_DEBUG_DEBUG + # if P165_FEATURE_DIGITCOLOR + + // apply per-digit colormap + bool fgColorSet = false; + bool bgColorSet = false; + const uint16_t grp100dgt = 0x100 + ((grp + 1) << 4) + dgt; + const uint16_t grp200dgt = 0x200 + ((grp + 1) << 4) + dgt; + auto it = digitColors.find(grp100dgt); // fg color + + if (it != digitColors.end()) { + display[grp]->setColorFont(it->second); + fgColorSet = true; + } + it = digitColors.find(grp200dgt); // bg color + + if (it != digitColors.end()) { + display[grp]->setColorBack(it->second); + bgColorSet = true; + } + # endif // if P165_FEATURE_DIGITCOLOR + + writeCharacterToDisplay(grp, dgt, showbuffer[i], showperiods[i]); + + # if P165_FEATURE_DIGITCOLOR + + // Restore global colors + if (fgColorSet) { + display[grp]->setColorFont(AdaGFXrgb565ToRgb888(_fgColor)); + } + + if (bgColorSet) { + display[grp]->setColorBack(AdaGFXrgb565ToRgb888(_bgColor)); + } + # endif // if P165_FEATURE_DIGITCOLOR + + # if P165_DEBUG_DEBUG + log += strformat(F(" g:%d d:%d 0x%02x"), grp, dgt, showbuffer[i]); + # endif // if P165_DEBUG_DEBUG + ++i; + } + # if P165_FEATURE_GROUPCOLOR + + // Restore global colors + if (fgColorGrp) { + display[grp]->setColorFont(AdaGFXrgb565ToRgb888(_fgColor)); + } + + if (bgColorGrp) { + display[grp]->setColorBack(AdaGFXrgb565ToRgb888(_bgColor)); + } + # endif // if P165_FEATURE_GROUPCOLOR + } + # if P165_DEBUG_DEBUG + addLogMove(LOG_LEVEL_INFO, log); + # endif // if P165_DEBUG_DEBUG + strip->show(); // Show all content at once +} + +/************************************************************************************* + * fetch the pixels to turn on for a digit + ************************************************************************************/ +segsize_t P165_data_struct::digit2SegmentMap(uint8_t grp, uint8_t digit) { + segsize_t currentBitmap = 0; // clear the current bitmap + + for (byte i = 0; i < 8; i++) { + if (digit & (1UL << i)) { // UL not necessary, but uses less Flash than if you leave it away + currentBitmap |= segments[grp][i]; + } + } + + # if P165_DEBUG_DEBUG + + const uint8_t pxlDigit = calculateGroupPixels(1, // Count pixels for 1 digit only + _pixelGroupCfg[grp].wpix, + _pixelGroupCfg[grp].hpix, + _pixelGroupCfg[grp].crnr, + _pixelGroupCfg[grp].dotp, + 0); + + // Used for debugging + addLog(LOG_LEVEL_INFO, strformat(F("digit2SegmentMap: digit: 0x%02x, bits: 0b%s"), digit, + ull2String(bitSetULL(currentBitmap, pxlDigit + 2), 2).substring(1).c_str())); + bitClearULL(currentBitmap, pxlDigit + 2); + # endif // if P165_DEBUG_DEBUG + return currentBitmap; +} + +/******************************************************************** + * Skip periods as they are shown on previous digit + *******************************************************************/ +int P165_data_struct::getEffectiveTextLength(const String& text) { + const int textLength = text.length(); + int p = 0; + + for (int i = 0; i < textLength; ++i) { + if (_periods && (text.charAt(i) == '.')) { // If setting periods true + if (p == 0) { // Text starts with a period, becomes a space with a dot + p++; + } + + if ((i > 0) && (text.charAt(i - 1) == '.')) { // Handle consecutive periods + p++; + } + } else { + p++; + } + } + return p; +} + +/************************************************************************************** + * Scroll current buffer with text/binary data to display (copied/adjusted from P073) + *************************************************************************************/ +bool P165_data_struct::nextScroll() { + bool result = false; + + if (isScrollEnabled() && (!_textToScroll.isEmpty())) { + if ((_scrollCount > 0) && (_scrollCount < 0xFFFF)) { _scrollCount--; } + + if (_scrollCount == 0) { + _scrollCount = 0xFFFF; // Max value to avoid interference when scrolling long texts + result = true; + const uint8_t bufToFill = calculateDisplayDigits(); + const uint16_t txtlength = _textToScroll.length(); + clearBuffer(); + + uint8_t p = 0; + + for (int i = _scrollPos; i < txtlength && p <= bufToFill; ++i) { // p <= bufToFill to allow a period after last digit + if (_periods + && (_textToScroll.charAt(i) == '.') + && !_binaryData + ) { // If setting periods true + if (p == 0) { // Text starts with a period, becomes a space with a dot + showperiods[p] = true; + p++; + } else { + showperiods[p - 1] = true; // The period displays as a dot on the previous digit! + } + + if ((i > _scrollPos) && (_textToScroll.charAt(i - 1) == '.')) { // Handle consecutive periods + showperiods[p - 1] = true; // The period displays as a dot on the previous digit! + p++; + } + } else if (p < bufToFill) { + showbuffer[p] = + # if P165_FEATURE_P073 + _binaryData ? + # endif // if P165_FEATURE_P073 + _textToScroll.charAt(i) + # if P165_FEATURE_P073 + : P073_mapCharToFontPosition(_textToScroll.charAt(i), _fontset) + # endif // if P165_FEATURE_P073 + ; + p++; + } + } + _scrollPos++; + + if (_scrollPos > _textToScroll.length() - bufToFill) { + _scrollPos = 0; // Restart when all text displayed + } + _scrollCount = _scrollSpeed; // Restart countdown + # if P165_DEBUG_DEBUG + logBufferContent(F("nextScroll")); + # endif // if P165_DEBUG_DEBUG + } + } + return result; +} + +/*********************************************************************************** + * Set up the string to scroll across the display, with optional prefixed spaces + **********************************************************************************/ +void P165_data_struct::setTextToScroll(const String& text) { + _textToScroll = String(); + + if (!text.isEmpty()) { + const int bufToFill = calculateDisplayDigits(); + _textToScroll.reserve(text.length() + bufToFill + (_scrollFull ? bufToFill : 0)); + + for (int i = 0; _scrollFull && i < bufToFill; ++i) { // Scroll text in from the right, so start with all blancs + _textToScroll += _binaryData ? (char)0x00 : ' '; + } + _textToScroll += text; + + for (int i = 0; i < bufToFill; ++i) { // Scroll text off completely before restarting + _textToScroll += _binaryData ? (char)0x00 : ' '; + } + } + _scrollCount = _scrollSpeed; + _scrollPos = 0; + _binaryData = false; +} + +void P165_data_struct::setScrollSpeed(uint8_t speed) { + _scrollSpeed = speed; + _scrollCount = _scrollSpeed; + _scrollPos = 0; +} + +bool P165_data_struct::isScrollEnabled() { + return _txtScrolling && _scrollAllowed; +} + +void P165_data_struct::setScrollEnabled(bool scroll) { + _scrollAllowed = scroll; +} + +# if P165_DEBUG_INFO || P165_DEBUG_DEBUG + +/********************************************************************** + * Log the current buffer content with a prefix + *********************************************************************/ +void P165_data_struct::logBufferContent(String prefix) { + String log; + + if (loglevelActiveFor(LOG_LEVEL_INFO) && + log.reserve(26 + 4 * P165_SHOW_BUFFER_SIZE)) { + log = strformat(F("%s buffer: periods: %c"), prefix.c_str(), _periods ? 't' : 'f'); + + for (uint8_t i = 0; i < P165_SHOW_BUFFER_SIZE; i++) { + log += strformat(F("%c0x%X,%c"), i > 0 ? ',' : ' ', showbuffer[i], showperiods[i] ? '.' : ' '); + } + addLogMove(LOG_LEVEL_INFO, log); + } +} + +# endif // if P165_DEBUG_INFO || P165_DEBUG_DEBUG + +#endif // ifdef USES_P165 diff --git a/src/src/PluginStructs/P165_data_struct.h b/src/src/PluginStructs/P165_data_struct.h new file mode 100644 index 0000000000..29ab1325f4 --- /dev/null +++ b/src/src/PluginStructs/P165_data_struct.h @@ -0,0 +1,325 @@ +/////////////////////////////////////////////////////////////////////////////////////////////////// +// Plugin data structure for P165 Display - 7-Segment Neopixel +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifndef PLUGINSTRUCTS_P165_DATA_STRUCT_H +#define PLUGINSTRUCTS_P165_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P165 + +# include +# include "../Helpers/AdafruitGFX_helper.h" // Use Adafruit graphics helper object +# include "../Static/WebStaticData.h" // Javascript and support functions + +# define P165_DEBUG_INFO 1 // set 1 to enable some extra debug logging +# define P165_DEBUG_DEBUG 0 // set 1 to enable some extra development debug logging + +# ifdef USES_P073 +# define P165_FEATURE_P073 1 // Use P073 shared functions when available +# else // ifdef USES_P073 +# define P165_FEATURE_P073 0 +# endif // ifdef USES_P073 + +# if P165_FEATURE_P073 // Use shared fonts and functions from P073 Display - 7-segment display when available +# include "../PluginStructs/P073_data_struct.h" +# endif // if P165_FEATURE_P073 + +# if P165_FEATURE_P073 +# ifdef P073_EXTRA_FONTS // FIXME to use #if instead of #ifdef after P073 improvements from PR #5091 are merged +# define P165_EXTRA_FONTS 1 +# else // ifdef P073_EXTRA_FONTS +# define P165_EXTRA_FONTS 0 +# endif // ifdef P073_EXTRA_FONTS +# endif // if P165_FEATURE_P073 + +# ifndef P165_FEATURE_GROUPCOLOR +# ifndef LIMIT_BUILD_SIZE +# define P165_FEATURE_GROUPCOLOR 1 +# else // ifndef LIMIT_BUILD_SIZE +# define P165_FEATURE_GROUPCOLOR 0 +# endif // ifndef LIMIT_BUILD_SIZE +# endif // ifndef P165_FEATURE_GROUPCOLOR + +# ifndef P165_FEATURE_DIGITCOLOR +# ifndef LIMIT_BUILD_SIZE +# define P165_FEATURE_DIGITCOLOR 1 +# else // ifndef LIMIT_BUILD_SIZE +# define P165_FEATURE_DIGITCOLOR 0 +# endif // ifndef LIMIT_BUILD_SIZE +# endif // ifndef P165_FEATURE_DIGITCOLOR + +# define P165_PIXEL_CHARACTER "☸" // The character to draw for a pixel. When changing, also update cHcrnr() in p165_digit.js + +# define P165_CONFIG_GROUPCOUNT PCONFIG(0) +# define P165_CONFIG_STRIP_TYPE PCONFIG(1) +# define P165_CONFIG_OUTPUTTYPE PCONFIG(2) +# define P165_CONFIG_DEF_BRIGHT PCONFIG(3) +# define P165_CONFIG_MAX_BRIGHT PCONFIG(4) +# define P165_CONFIG_FONTSET PCONFIG(5) +# define P165_CONFIG_SCROLLSPEED PCONFIG(6) +# define P165_CONFIG_FG_COLOR PCONFIG_FLOAT(0) +# define P165_CONFIG_BG_COLOR PCONFIG_FLOAT(1) + +// Settings flags +# define P165_FLAGS PCONFIG(7) // 15 bits + +# define P165_FLAG_SUPPRESS_0 0 // 1 bit +# define P165_FLAG_NUMBERPLAN 1 // 1 bit +# define P165_FLAG_SCROLL_TEXT 2 // 1 bit +# define P165_FLAG_SCROLL_FULL 3 // 1 bit +# define P165_FLAG_STD_OFFSET 4 // 4 bit + +# define P165_GET_FLAG_SUPP0 (bitRead(P165_FLAGS, P165_FLAG_SUPPRESS_0)) +# define P165_GET_FLAG_NUMBERPLAN (bitRead(P165_FLAGS, P165_FLAG_NUMBERPLAN)) +# define P165_GET_FLAG_SCROLL_TEXT (bitRead(P165_FLAGS, P165_FLAG_SCROLL_TEXT)) +# define P165_GET_FLAG_SCROLL_FULL (bitRead(P165_FLAGS, P165_FLAG_SCROLL_FULL)) +# define P165_GET_FLAG_STD_OFFSET (get4BitFromUL(P165_FLAGS, P165_FLAG_STD_OFFSET)) + +# define P165_SET_FLAG_SUPP0(V) (bitWrite(P165_FLAGS, P165_FLAG_SUPPRESS_0, V)) +# define P165_SET_FLAG_NUMBERPLAN(V) (bitWrite(P165_FLAGS, P165_FLAG_NUMBERPLAN, V)) +# define P165_SET_FLAG_SCROLL_TEXT(V) (bitWrite(P165_FLAGS, P165_FLAG_SCROLL_TEXT, V)) +# define P165_SET_FLAG_SCROLL_FULL(V) (bitWrite(P165_FLAGS, P165_FLAG_SCROLL_FULL, V)) +# define P165_SET_FLAG_STD_OFFSET(V) (set4BitToUL(P165_FLAGS, P165_FLAG_STD_OFFSET, V)) + +// Config per display group, all 4 PCONFIG_(U)LONG variables used +# define P165_GROUP_CFG(N) PCONFIG_ULONG(N) + +# define P165_CONFIG_IDX_WPIXELS 0u // 3 bits +# define P165_CONFIG_IDX_HPIXELS 3u // 3 bits +# define P165_CONFIG_IDX_CORNER 6u // 1 bit +# define P165_CONFIG_IDX_DOT 7u // 3 bits +# define P165_CONFIG_IDX_EXTRA 10u // 4 bits +# define P165_CONFIG_IDX_OFFSET 14u // 4 bits +# define P165_CONFIG_IDX_DIGITS 18u // 3 bits +# define P165_CONFIG_IDX_START 21u // 1 bit +# define P165_CONFIG_IDX_DEND 22u // 1 bit +# define P165_CONFIG_IDX_RTLD 23u // 1 bit + +# define P165_GET_CONFIG_WPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS)) +# define P165_GET_CONFIG_HPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS)) +# define P165_GET_CONFIG_CORNER(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_CORNER)) +# define P165_GET_CONFIG_DOT(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT)) +# define P165_GET_CONFIG_EXTRA(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_EXTRA)) +# define P165_GET_CONFIG_OFFSET(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_OFFSET)) +# define P165_GET_CONFIG_DIGITS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DIGITS)) +# define P165_GET_CONFIG_START(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_START)) +# define P165_GET_CONFIG_DEND(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_DEND)) +# define P165_GET_CONFIG_RTLD(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_RTLD)) + +# define P165_SET_CONFIG_WPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS, V)) +# define P165_SET_CONFIG_HPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS, V)) +# define P165_SET_CONFIG_CORNER(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_CORNER, V)) +# define P165_SET_CONFIG_DOT(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT, V)) +# define P165_SET_CONFIG_EXTRA(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_EXTRA, V)) +# define P165_SET_CONFIG_OFFSET(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_OFFSET, V)) +# define P165_SET_CONFIG_DIGITS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DIGITS, V)) +# define P165_SET_CONFIG_START(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_START, V)) +# define P165_SET_CONFIG_DEND(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_DEND, V)) +# define P165_SET_CONFIG_RTLD(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_RTLD, V)) + +# define P165_SHOW_BUFFER_SIZE 16 // Max number of characters in the buffer to show (1..4 groups of 1..4 digits) + +# define P165_DISP_MANUAL 0 +# define P165_DISP_CLOCK24BLNK 1 +# define P165_DISP_CLOCK24 2 +# define P165_DISP_CLOCK12BLNK 3 +# define P165_DISP_CLOCK12 4 +# define P165_DISP_DATE 5 + +# define P165_STRIP_TYPE_RGB 0 +# define P165_STRIP_TYPE_RGBW 1 + +# define P165_SEGMENT_DOT_PIXELS 7 // Max: 7 (3 bits) +# define P165_SEGMENT_ADDON_PIXELS 12 // Max: 15 (4 bits) +# define P165_SEGMENT_G_SPLIT_SIZE 2 + +// Set typedef before include +typedef uint64_t segsize_t; // largest storage size available, allows for up to 64 pixels per digit, 41 used + +# include +# if P165_FEATURE_P073 +# define NEOPIXEL_DISPLAY_USE_WRITE 0 // Leave out character table and write() method +# endif // if P165_FEATURE_P073 +# include + +struct P165_data_struct : public PluginTaskData_base { +private: + + struct PixelGroupCfg { // Bit-mapping must match with P165_GET/SET_CONFIG_*() / P165_GROUP_CFG() macros + uint32_t wpix : 3; // widht (1..5) + uint32_t hpix : 3; // height (1..5) + uint32_t crnr : 1; // corners off/on + uint32_t dotp : 3; // dot pixels (0..7) + uint32_t addn : 4; // extra pixels (0..12) + uint32_t offs : 4; // offset before (0..15) + uint32_t dgts : 3; // digits in group (1..4) + uint32_t strt : 1; // start segment 0 = left-top/a, 1 = right-top/b + uint32_t dend : 1; // dot at: 1: end of digit 0: between c/d segments + uint32_t rtld : 1; // right to left display + uint32_t unused : 8; + uint32_t aoffs : 16; // Add-on pixels offset (use uin32_t for better memory alignment) + uint32_t unused2 : 16; + }; + +public: + + P165_data_struct(struct EventStruct *event); + virtual ~P165_data_struct(); + + static bool plugin_webform_load(struct EventStruct *event); + static bool plugin_webform_save(struct EventStruct *event); + static void initDigitGroup(struct EventStruct *event, + uint8_t grp); + + bool isInitialized() { + return _initialized; + } + + bool plugin_once_a_second(struct EventStruct *event); + bool plugin_ten_per_second(struct EventStruct *event); + bool plugin_write(struct EventStruct *event, + const String & string); + +private: + + static uint16_t calculateGroupPixels(const uint8_t count, + const uint8_t wpixels, + const uint8_t hpixels, + const bool overlap, + const uint8_t decPt, + const uint8_t addN); + static void drawSevenSegment(const uint8_t digit, + const uint8_t grp, + const uint8_t wpixels, + const uint8_t hpixels, + const bool overlap, + const uint8_t decPt, + const uint8_t addN, + const uint8_t max, + const uint16_t offset, + const bool strt, + const bool dend, + const String & fgColor, + const bool dspPlan, + const int16_t aOffs, + const bool splitG); + static String calculatePixelIndex(const uint8_t hor, + const int8_t ver, + const uint8_t seg, + const uint16_t offset, + const uint8_t wpixels, + const uint8_t hpixels, + const bool overlap, + const bool strt, + const bool dend, + const uint8_t decPt, + const uint8_t addN, + const int16_t aOffs, + const bool splitG); + static void addJavascript(); + uint16_t calculateDisplayPixels(); + uint16_t calculateDisplayDigits(); + void fillSegmentBitmap(const uint8_t grp, + const PixelGroupCfg pixCfg); + + static int offsetLogic_callback(uint16_t position); + + void fillBufferWithTime(const bool sevendgt_now, + uint8_t sevendgt_hours, + uint8_t sevendgt_minutes, + uint8_t sevendgt_seconds, + const bool flag12h, + const bool suppressLeading0, + const uint8_t offset = 0); + void fillBufferWithDate(const bool sevendgt_now, + uint8_t sevendgt_day, + uint8_t sevendgt_month, + const int sevendgt_year, + const bool suppressLeading0, + const uint8_t offset = 0); + void put4NumbersInBuffer(const uint8_t nr1, + const uint8_t nr2, + const uint8_t nr3, + const int8_t nr4, + const bool suppressLeading0, + const uint8_t offset = 0); + void clearBuffer(); + + void writeCharacterToDisplay(uint8_t group, + uint8_t digit, + uint8_t character, + bool period); + void writeBufferToDisplay(uint8_t group = 0); + segsize_t digit2SegmentMap(uint8_t grp, + uint8_t segments); + + + int getEffectiveTextLength(const String& text); + bool nextScroll(); + void setTextToScroll(const String& text); + void setScrollSpeed(uint8_t speed); + bool isScrollEnabled(); + void setScrollEnabled(bool scroll); + bool plugin_write_7dtext(const String& text); + # if P165_FEATURE_P073 + bool plugin_write_7dfont(struct EventStruct *event, + const String & text); + # endif // if P165_FEATURE_P073 + bool plugin_write_7dbin(const String& text); + bool plugin_write_7digit(const String& text); + void fillBufferWithString(const String& textToShow, + bool useBinaryData = false); + void setBinaryData(const String& data); + bool extraPixelsState(uint8_t grp, + uint8_t state, + uint32_t color); + # if P165_DEBUG_INFO || P165_DEBUG_DEBUG + void logBufferContent(String prefix); + # endif // if P165_DEBUG_INFO || P165_DEBUG_DEBUG + bool parseRGBWColors(const String& string, + bool rgbW, + uint32_t & fgColor, + uint32_t & bgColor, + bool & fgSet, + bool & bgSet); + + NeoPixelBus_wrapper *strip = nullptr; + Noiasca_NeopixelDisplay *display[PLUGIN_CONFIGLONGVAR_MAX]{ nullptr }; + + segsize_t segments[PLUGIN_CONFIGLONGVAR_MAX][8]{}; // 4*8*uint64_t = 256 bytes... + PixelGroupCfg _pixelGroupCfg[PLUGIN_CONFIGLONGVAR_MAX]{}; + + uint16_t _fgColor = ADAGFX_RED; + uint16_t _bgColor = ADAGFX_BLACK; + uint8_t _stripType = 0; + uint8_t _pixelGroups = 0; + uint8_t _defBrightness = 0; + uint8_t _maxBrightness = 0; + uint8_t _output = 0; + uint8_t _fontset = 0; + uint8_t _stdOffset = 0; + bool _initialized = false; + bool _timesep = false; + bool _suppressLeading0 = false; + + String _textToScroll; + bool _txtScrolling = false; + bool _scrollAllowed = false; + uint16_t _scrollSpeed = 0; + uint16_t _scrollCount = 0; + uint16_t _scrollPos = 0; + bool _scrollFull = false; + bool _binaryData = false; + bool _periods = true; + uint8_t _currentGroup = 0; + + uint8_t showbuffer[P165_SHOW_BUFFER_SIZE] = { 0 }; + bool showperiods[P165_SHOW_BUFFER_SIZE] = { 0 }; + + # if P165_FEATURE_DIGITCOLOR || P165_FEATURE_GROUPCOLOR + std::mapdigitColors; + # endif // if P165_FEATURE_DIGITCOLOR || P165_FEATURE_GROUPCOLOR +}; +#endif // ifdef USES_P165 +#endif // ifndef PLUGINSTRUCTS_P165_DATA_STRUCT_H diff --git a/src/src/Static/WebStaticData.cpp b/src/src/Static/WebStaticData.cpp index 8d5012131f..885ba9e57d 100644 --- a/src/src/Static/WebStaticData.cpp +++ b/src/src/Static/WebStaticData.cpp @@ -168,6 +168,11 @@ void serve_JS(JSfiles_e JSfile) { #endif break; #endif +#ifdef USES_P165 + case JSfiles_e::P165_digit: + url = F("p165_digit.js"); + break; +#endif // ifdef USES_P165 } @@ -223,6 +228,11 @@ void serve_JS(JSfiles_e JSfile) { case JSfiles_e::EasyColorCode_cm_plugins: break; #endif +#ifdef USES_P165 + case JSfiles_e::P165_digit: + TXBuffer.addFlashString((PGM_P)FPSTR(p165_digit_script)); + break; +#endif // ifdef USES_P165 } html_add_script_end(); return; diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index b2dbd5bf04..c994b19ff9 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -24,6 +24,9 @@ enum class JSfiles_e { EasyColorCode_espeasy, EasyColorCode_cm_plugins, #endif +#ifdef USES_P165 + P165_digit, +#endif // ifdef USES_P165 }; enum class CSSfiles_e { @@ -711,4 +714,23 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = {0x66,0x7 static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = {0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x67,0x65,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x28,0x29,0x7b,0x76,0x61,0x72,0x20,0x65,0x2c,0x6f,0x3d,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x75,0x73,0x65,0x72,0x41,0x67,0x65,0x6e,0x74,0x2c,0x74,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x28,0x6f,0x70,0x65,0x72,0x61,0x7c,0x63,0x68,0x72,0x6f,0x6d,0x65,0x7c,0x73,0x61,0x66,0x61,0x72,0x69,0x7c,0x66,0x69,0x72,0x65,0x66,0x6f,0x78,0x7c,0x6d,0x73,0x69,0x65,0x7c,0x74,0x72,0x69,0x64,0x65,0x6e,0x74,0x28,0x3f,0x3d,0x5c,0x2f,0x29,0x29,0x5c,0x2f,0x3f,0x5c,0x73,0x2a,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x69,0x29,0x7c,0x7c,0x5b,0x5d,0x3b,0x72,0x65,0x74,0x75,0x72,0x6e,0x2f,0x74,0x72,0x69,0x64,0x65,0x6e,0x74,0x2f,0x69,0x2e,0x74,0x65,0x73,0x74,0x28,0x74,0x5b,0x31,0x5d,0x29,0x3f,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x22,0x49,0x45,0x22,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x28,0x65,0x3d,0x2f,0x5c,0x62,0x72,0x76,0x5b,0x20,0x3a,0x5d,0x2b,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x67,0x2e,0x65,0x78,0x65,0x63,0x28,0x6f,0x29,0x7c,0x7c,0x5b,0x5d,0x29,0x5b,0x31,0x5d,0x7c,0x7c,0x22,0x22,0x7d,0x3a,0x22,0x43,0x68,0x72,0x6f,0x6d,0x65,0x22,0x3d,0x3d,0x3d,0x74,0x5b,0x31,0x5d,0x26,0x26,0x6e,0x75,0x6c,0x6c,0x21,0x3d,0x28,0x65,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x5c,0x62,0x4f,0x50,0x52,0x7c,0x45,0x64,0x67,0x65,0x5c,0x2f,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x29,0x29,0x3f,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x22,0x4f,0x70,0x65,0x72,0x61,0x22,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x65,0x5b,0x31,0x5d,0x7d,0x3a,0x28,0x74,0x3d,0x74,0x5b,0x32,0x5d,0x3f,0x5b,0x74,0x5b,0x31,0x5d,0x2c,0x74,0x5b,0x32,0x5d,0x5d,0x3a,0x5b,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x61,0x70,0x70,0x4e,0x61,0x6d,0x65,0x2c,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x61,0x70,0x70,0x56,0x65,0x72,0x73,0x69,0x6f,0x6e,0x2c,0x22,0x2d,0x3f,0x22,0x5d,0x2c,0x6e,0x75,0x6c,0x6c,0x21,0x3d,0x28,0x65,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x5c,0x2f,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x69,0x29,0x29,0x26,0x26,0x74,0x2e,0x73,0x70,0x6c,0x69,0x63,0x65,0x28,0x31,0x2c,0x31,0x2c,0x65,0x5b,0x31,0x5d,0x29,0x2c,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x74,0x5b,0x30,0x5d,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x74,0x5b,0x31,0x5d,0x7d,0x29,0x7d,0x76,0x61,0x72,0x20,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x3d,0x67,0x65,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x28,0x29,0x2c,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x3d,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x6e,0x61,0x6d,0x65,0x2b,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3b,0x28,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x6e,0x61,0x6d,0x65,0x3d,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3c,0x31,0x32,0x29,0x3f,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x3d,0x22,0x45,0x72,0x72,0x6f,0x72,0x3a,0x20,0x22,0x2b,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x2b,0x22,0x20,0x69,0x73,0x20,0x6e,0x6f,0x74,0x20,0x73,0x75,0x70,0x70,0x6f,0x72,0x74,0x65,0x64,0x21,0x20,0x50,0x6c,0x65,0x61,0x73,0x65,0x20,0x74,0x72,0x79,0x20,0x61,0x20,0x6d,0x6f,0x64,0x65,0x72,0x6e,0x20,0x77,0x65,0x62,0x20,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x22,0x3a,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x3d,0x22,0x46,0x65,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x6c,0x6f,0x67,0x20,0x65,0x6e,0x74,0x72,0x69,0x65,0x73,0x2e,0x2e,0x2e,0x22,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x31,0x65,0x33,0x2c,0x30,0x29,0x3b,0x76,0x61,0x72,0x20,0x6c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x3d,0x6e,0x65,0x77,0x20,0x41,0x72,0x72,0x61,0x79,0x28,0x22,0x55,0x6e,0x75,0x73,0x65,0x64,0x22,0x2c,0x22,0x45,0x72,0x72,0x6f,0x72,0x22,0x2c,0x22,0x49,0x6e,0x66,0x6f,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x20,0x4d,0x6f,0x72,0x65,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x20,0x44,0x65,0x76,0x22,0x29,0x3b,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x6f,0x29,0x7b,0x76,0x61,0x72,0x20,0x74,0x2c,0x6e,0x3b,0x69,0x73,0x4e,0x61,0x4e,0x28,0x6f,0x29,0x26,0x26,0x28,0x6f,0x3d,0x31,0x29,0x2c,0x6e,0x75,0x6c,0x6c,0x3d,0x3d,0x65,0x26,0x26,0x28,0x65,0x3d,0x31,0x65,0x33,0x29,0x2c,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x69,0x6e,0x67,0x5f,0x74,0x79,0x70,0x65,0x3d,0x65,0x3c,0x3d,0x35,0x30,0x30,0x3f,0x22,0x61,0x75,0x74,0x6f,0x22,0x3a,0x22,0x73,0x6d,0x6f,0x6f,0x74,0x68,0x22,0x3b,0x76,0x61,0x72,0x20,0x72,0x3d,0x22,0x22,0x2c,0x6c,0x3d,0x30,0x2c,0x73,0x3d,0x73,0x65,0x74,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x29,0x7b,0x6c,0x3e,0x30,0x3f,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x3a,0x28,0x2b,0x2b,0x6f,0x3e,0x31,0x3f,0x6c,0x3d,0x31,0x3a,0x66,0x65,0x74,0x63,0x68,0x28,0x22,0x2f,0x6c,0x6f,0x67,0x6a,0x73,0x6f,0x6e,0x22,0x29,0x2e,0x74,0x68,0x65,0x6e,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x32,0x30,0x30,0x3d,0x3d,0x3d,0x6f,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x3f,0x6f,0x2e,0x6a,0x73,0x6f,0x6e,0x28,0x29,0x2e,0x74,0x68,0x65,0x6e,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x76,0x61,0x72,0x20,0x6c,0x3b,0x66,0x6f,0x72,0x28,0x6e,0x75,0x6c,0x6c,0x3d,0x3d,0x6e,0x26,0x26,0x28,0x6e,0x3d,0x22,0x22,0x29,0x2c,0x74,0x3d,0x30,0x3b,0x74,0x3c,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x6e,0x72,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x3b,0x2b,0x2b,0x74,0x29,0x74,0x72,0x79,0x7b,0x6c,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x7d,0x63,0x61,0x74,0x63,0x68,0x28,0x65,0x29,0x7b,0x6c,0x3d,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x7d,0x66,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x7b,0x22,0x54,0x79,0x70,0x65,0x45,0x72,0x72,0x6f,0x72,0x22,0x21,0x3d,0x3d,0x6c,0x26,0x26,0x28,0x72,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x2c,0x6e,0x2b,0x3d,0x22,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x2b,0x22,0x20,0x69,0x64,0x3d,0x22,0x2b,0x72,0x2b,0x27,0x3e,0x3c,0x66,0x6f,0x6e,0x74,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x67,0x72,0x61,0x79,0x22,0x3e,0x27,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x2b,0x22,0x3a,0x3c,0x2f,0x66,0x6f,0x6e,0x74,0x3e,0x20,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x65,0x78,0x74,0x2b,0x22,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x22,0x29,0x7d,0x65,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x54,0x54,0x4c,0x2c,0x22,0x22,0x21,0x3d,0x3d,0x6e,0x26,0x26,0x28,0x22,0x46,0x65,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x6c,0x6f,0x67,0x20,0x65,0x6e,0x74,0x72,0x69,0x65,0x73,0x2e,0x2e,0x2e,0x22,0x3d,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x26,0x26,0x28,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x22,0x22,0x29,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x2b,0x3d,0x6e,0x29,0x2c,0x6e,0x3d,0x22,0x22,0x2c,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x22,0x29,0x2e,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x2c,0x31,0x3d,0x3d,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x26,0x26,0x22,0x22,0x21,0x3d,0x3d,0x72,0x26,0x26,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x72,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x49,0x6e,0x74,0x6f,0x56,0x69,0x65,0x77,0x28,0x7b,0x62,0x65,0x68,0x61,0x76,0x69,0x6f,0x72,0x3a,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x69,0x6e,0x67,0x5f,0x74,0x79,0x70,0x65,0x7d,0x29,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x5f,0x6c,0x6f,0x67,0x6c,0x65,0x76,0x65,0x6c,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x22,0x4c,0x6f,0x67,0x67,0x69,0x6e,0x67,0x3a,0x20,0x22,0x2b,0x6c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x5b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x53,0x65,0x74,0x74,0x69,0x6e,0x67,0x73,0x57,0x65,0x62,0x4c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x5d,0x2b,0x22,0x20,0x28,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x53,0x65,0x74,0x74,0x69,0x6e,0x67,0x73,0x57,0x65,0x62,0x4c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x2b,0x22,0x29,0x22,0x2c,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x30,0x29,0x7d,0x29,0x3a,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0x4c,0x6f,0x6f,0x6b,0x73,0x20,0x6c,0x69,0x6b,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x77,0x61,0x73,0x20,0x61,0x20,0x70,0x72,0x6f,0x62,0x6c,0x65,0x6d,0x2e,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x43,0x6f,0x64,0x65,0x3a,0x20,0x22,0x2b,0x6f,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x29,0x7d,0x29,0x2e,0x63,0x61,0x74,0x63,0x68,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x2b,0x3d,0x22,0x3c,0x64,0x69,0x76,0x3e,0x3e,0x3e,0x20,0x22,0x2b,0x6f,0x2e,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2b,0x22,0x20,0x3c,0x3c,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x22,0x2c,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x22,0x29,0x2e,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x54,0x6f,0x70,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x48,0x65,0x69,0x67,0x68,0x74,0x2c,0x65,0x3d,0x35,0x65,0x33,0x2c,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x30,0x29,0x7d,0x29,0x2c,0x6c,0x3d,0x31,0x29,0x7d,0x2c,0x65,0x29,0x7d,0}; #endif // WEBSERVER_INCLUDE_JS +#ifdef WEBSERVER_INCLUDE_JS +static const char p165_digit_script[] PROGMEM = { + "function sh_col(a,b,d){s=document.getElementById(a).getElementsByTagName('TR');for(r of s)" + " {c=r?.children[b];if(c?.tagName=='TD'){c.style.width=20;c.style.display=d?'inline-block':'none'}}};" + "function sh_row(a,b,c){r=document.getElementById(a).getElementsByTagName('TR')[b];if(r)r.style.display=c?'block':'none'};" + "function set_td(a,b,d,e,f){c=document.getElementById(a).getElementsByTagName('TR')[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" + "function chWdth(a,b,c,d,e){for(t=b;t☸';else z='☸';" + " for(t=c;t0,12,7,''+a)};" + "function chAddn(a,b,c,d,e){for(t=b;t0&&d&&t===b+c-1,6,8,''+a)};" + "function dgts(a,b){z=0;for(q=0;q content if fill is true, else non-breaking space, minimal checks + c = document.getElementById(tbl).getElementsByTagName('TR')[row].children[col]; + if (c?.tagName == 'TD') c.innerHTML = fill ? td : ' '; +}; +// Next functions: 4th & 5th parameters are used only in 1 function, but to keep the calling logic simple, leave it there +function chWdth(value, base, count, max, colr) { // Change width + for (t = base; t < base + count; t++) + for (i = 1; i <= 5; i++) + sh_col('dgtbl' + t, i, i <= value); +}; +function chHght(value, base, count, max, colr) { // Change height + for (t = base; t < base + count; t++) { + b = 'dgtbl' + t; // Table + for (i = 1; i < 6; i++) { + sh_row(b, 6 - i, i <= value); + sh_row(b, 6 + i, i <= value); + }; + } +}; +function chCrnr(checked, base, count, max, colr) { // Change Corner overlap + if (colr) z = ''; else z = '☸'; + for (t = base; t < base + count; t++) { + b = 'dgtbl' + t; // Table + for (i = 0; i < 14; i += 6) { + set_td(b, checked, i, 0, z); // P165_PIXEL_CHARACTER + set_td(b, checked, i, 6, z); + } + } +}; +function chDecp(value, base, count, max, colr) { // Change Decimal point pixels + for (t = base; t < base + count; t++) + set_td('dgtbl' + t, value > 0, 12, 7, '' + value); +}; +function chAddn(value, base, count, max, colr) { // Change Additional pixels + for (t = base; t < base + count; t++) + set_td('dgtbl' + t, value > 0 && max && t === base + count - 1, 6, 8, '' + value); +}; +function dgts(gps, flds) { // Update pixel counts per group and total pixels + z = 0; + for (q = 0; q < gps; q++) { + v = []; + for (f of flds) { + v.push(parseInt(document.getElementById(f + q * 10).value)); + } + c = document.getElementById('crnr' + q * 10).checked; + x = v[5] * (3 * v[0] + 4 * v[1] + (c ? 6 : 0) + v[2]) + v[3]; + z += x + v[4]; + document.getElementById('totp' + q * 10).value = x; + } + document.getElementById('totpx').value = z; +}; From 9adea949809b03a063ddda1cfe4cf0c0a62b10a9 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Aug 2024 21:46:19 +0200 Subject: [PATCH 200/367] [P165] Add code from #4977 --- src/include/ESPEasy_config.h | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/include/ESPEasy_config.h b/src/include/ESPEasy_config.h index bc2f8207b9..7aecb45a54 100644 --- a/src/include/ESPEasy_config.h +++ b/src/include/ESPEasy_config.h @@ -142,6 +142,27 @@ constexpr unsigned CEIL_LOG2(unsigned x) # endif // ifdef ESP8266 +// Start: Copied from PR #4977 (P139) +# ifdef ESP8266 + + // (ESP8266) FsP: FlashstringHelper to String-Pointer + # define FsP(F) String(F).c_str() +# endif // ifdef ESP8266 + +# ifdef ESP32 + # if defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C6) + + // (ESP32) FsP: FlashstringHelper to String-Pointer + # define FsP + # endif // if defined(ESP32C2) || defined(ESP32C3) || defined(ESP32C6) + # if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) + + // (ESP32) FsP: FlashstringHelper to String-Pointer + # define FsP + # endif // if defined(ESP32_CLASSIC) || defined(ESP32S2) || defined(ESP32S3) +# endif // ifdef ESP32 +// End: Copied from PR #4977 (P139) + // User configuration // Include Custom.h before ESPEasyDefaults.h. # ifdef USE_CUSTOM_H From f8f006d8e00bfb906f2c6c44718944d48bdacf73 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Aug 2024 21:46:59 +0200 Subject: [PATCH 201/367] [P165] Add code from #5091 --- src/src/PluginStructs/P073_data_struct.cpp | 134 +++++++++++++++++++++ src/src/PluginStructs/P073_data_struct.h | 24 +++- 2 files changed, 154 insertions(+), 4 deletions(-) diff --git a/src/src/PluginStructs/P073_data_struct.cpp b/src/src/PluginStructs/P073_data_struct.cpp index 75771b3c31..bb1138ff91 100644 --- a/src/src/PluginStructs/P073_data_struct.cpp +++ b/src/src/PluginStructs/P073_data_struct.cpp @@ -2,6 +2,140 @@ #ifdef USES_P073 +// FIXME Next part should be merged from PR #5091 for use of #if vs. #ifdef !!! +uint8_t P073_mapCharToFontPosition(char character, + uint8_t fontset) { + uint8_t position = 10; + + # ifdef P073_EXTRA_FONTS + const String specialChars = F(" -^=/_%@.,;:+*#!?'\"<>\\()|"); + const String chnorux = F("CHNORUX"); + + switch (fontset) { + case 1: // Siekoo + case 2: // Siekoo with uppercase 'CHNORUX' + + if ((fontset == 2) && (chnorux.indexOf(character) > -1)) { + position = chnorux.indexOf(character) + 35; + } else if (isDigit(character)) { + position = character - '0'; + } else if (isAlpha(character)) { + position = character - (isLowerCase(character) ? 'a' : 'A') + 42; + } else { + const int idx = specialChars.indexOf(character); + + if (idx > -1) { + position = idx + 10; // Space is still 10 + } + } + break; + case 3: // dSEG7 (same table size as 7Dgt) + default: // Original fontset (7Dgt) + # endif // ifdef P073_EXTRA_FONTS + + if (isDigit(character)) { + position = character - '0'; + } else if (isAlpha(character)) { + position = character - (isLowerCase(character) ? 'a' : 'A') + 16; + } else { + switch (character) { + case ' ': position = 10; break; + case '-': position = 11; break; + case '^': position = 12; break; // degree + case '=': position = 13; break; + case '/': position = 14; break; + case '_': position = 15; break; + } + } + # ifdef P073_EXTRA_FONTS +} + + # endif // ifdef P073_EXTRA_FONTS + return position; +} + +uint8_t P073_getFontChar(uint8_t index, + uint8_t fontset) { + # ifdef P073_EXTRA_FONTS + + switch (fontset) { + case 1: // Siekoo + case 2: // Siekoo uppercase CHNORUX + return pgm_read_byte(&(SiekooCharTable[index])); + case 3: // dSEG7 + return pgm_read_byte(&(Dseg7CharTable[index])); + default: // Standard fontset + return pgm_read_byte(&(DefaultCharTable[index])); + } + # else // ifdef P073_EXTRA_FONTS + return pgm_read_byte(&(DefaultCharTable[index])); + # endif // ifdef P073_EXTRA_FONTS +} + +int32_t P073_parse_7dfont(struct EventStruct *event, + const String & text) { + int32_t fontNr = 0; + + # ifdef P073_EXTRA_FONTS + + if (!text.isEmpty()) { + const String fontArg = parseString(text, 1); + fontNr = -1; + + if ((equals(fontArg, F("default"))) || (equals(fontArg, F("7dgt")))) { + fontNr = 0; + } else if (equals(fontArg, F("siekoo"))) { + fontNr = 1; + } else if (equals(fontArg, F("siekoo_upper"))) { + fontNr = 2; + } else if (equals(fontArg, F("dseg7"))) { + fontNr = 3; + } else if (!validIntFromString(text, fontNr)) { + fontNr = -1; // reset if invalid + } + } + # endif // ifdef P073_EXTRA_FONTS + return fontNr; +} + +void P073_display_output_selector(const __FlashStringHelper *id, int16_t value) { + const __FlashStringHelper *displout[] = { + F("Manual"), + F("Clock 24h - Blink"), + F("Clock 24h - No Blink"), + F("Clock 12h - Blink"), + F("Clock 12h - No Blink"), + F("Date"), + }; + const int disploutOptions[] = { + P073_DISP_MANUAL, + P073_DISP_CLOCK24BLNK, + P073_DISP_CLOCK24, + P073_DISP_CLOCK12BLNK, + P073_DISP_CLOCK12, + P073_DISP_DATE, + }; + + addFormSelector(F("Display Output"), id, NR_ELEMENTS(disploutOptions), displout, disploutOptions, value); +} + +# ifdef P073_EXTRA_FONTS +void P073_font_selector(const __FlashStringHelper *id, int16_t value) { + const __FlashStringHelper *fontset[] = { + F("Default"), + F("Siekoo"), + F("Siekoo with uppercase 'CHNORUX'"), + F("dSEG7"), + }; + + addFormSelector(F("Font set"), id, NR_ELEMENTS(fontset), fontset, nullptr, value); + addFormNote(F("Check documentation for examples of the font sets.")); +} + +# endif // ifdef P073_EXTRA_FONTS + +// FIXME End of part to merge from PR #5091 + void P073_data_struct::init(struct EventStruct *event) { ClearBuffer(); diff --git a/src/src/PluginStructs/P073_data_struct.h b/src/src/PluginStructs/P073_data_struct.h index 868e43da11..7afcb263c7 100644 --- a/src/src/PluginStructs/P073_data_struct.h +++ b/src/src/PluginStructs/P073_data_struct.h @@ -29,16 +29,17 @@ # define P073_7DBIN_COMMAND // Enable input of binary data via 7dbin,uint8_t,... command # define P073_SUPPRESS_ZERO // Enable Suppress leading zero on day/hour -# ifndef PLUGIN_SET_COLLECTION +# if defined(PLUGIN_SET_COLLECTION) && defined(ESP8266) -// # define P073_DEBUG // Leave out some debugging on demand, activates extra log info in the debug -# else // ifndef PLUGIN_SET_COLLECTION # undef P073_7DDT_COMMAND // Optionally activate if .bin file space is really problematic, to remove the 7ddt command # undef P073_EXTRA_FONTS // Optionally activate if .bin file space is really problematic, to remove the font selection and 7dfont command # undef P073_SCROLL_TEXT // Optionally activate if .bin file space is really problematic, to remove the scrolling text feature # undef P073_7DBIN_COMMAND // Optionally activate if .bin file space is really problematic, to remove the 7dbin command # undef P073_SUPPRESS_ZERO // Optionally activate if .bin file space is really problematic, to remove the Suppress leading zero feature -# endif // ifndef PLUGIN_SET_COLLECTION +# else // if defined(PLUGIN_SET_COLLECTION) && defined(ESP8266) + +// # define P073_DEBUG // Leave out some debugging on demand, activates extra log info in the debug +# endif // if defined(PLUGIN_SET_COLLECTION) && defined(ESP8266) # define TM1637_POWER_ON 0b10001000 # define TM1637_POWER_OFF 0b10000000 @@ -140,6 +141,21 @@ static const uint8_t Dseg7CharTable[42] PROGMEM = { # endif // P073_EXTRA_FONTS +// FIXME Next part should be merged from PR #5091 for use of #if vs. #ifdef !!! +uint8_t P073_mapCharToFontPosition(char character, + uint8_t fontset); +uint8_t P073_getFontChar(uint8_t index, + uint8_t fontset); +int32_t P073_parse_7dfont(struct EventStruct *event, + const String & text); +void P073_display_output_selector(const __FlashStringHelper *id, + int16_t value); +# ifdef P073_EXTRA_FONTS +void P073_font_selector(const __FlashStringHelper *id, + int16_t value); +# endif // ifdef P073_EXTRA_FONTS +// FIXME End of part to merge from PR #5091 + struct P073_data_struct : public PluginTaskData_base { public: From 6f741ce792d8a02b265286d07954e7318551512f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 25 Aug 2024 21:47:59 +0200 Subject: [PATCH 202/367] [AdaGFX] Add rgb565/rgb888 color conversions --- src/src/Helpers/AdafruitGFX_helper.cpp | 32 +++++++++++++++++++++++++- src/src/Helpers/AdafruitGFX_helper.h | 4 ++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/src/Helpers/AdafruitGFX_helper.cpp b/src/src/Helpers/AdafruitGFX_helper.cpp index 4bab47c637..1c63f5292e 100644 --- a/src/src/Helpers/AdafruitGFX_helper.cpp +++ b/src/src/Helpers/AdafruitGFX_helper.cpp @@ -2176,7 +2176,7 @@ bool AdafruitGFX_helper::processCommand(const String& string) { # if ADAGFX_ARGUMENT_VALIDATION const int16_t curWin = getWindow(); - if (curWin != 0) { selectWindow(0); } // Validate against raw window coordinates + if (curWin != 0) { selectWindow(0); } // Validate against raw window coordinates if (argCount == 6) { setRotation(nParams[5]); } // Use requested rotation @@ -2961,6 +2961,36 @@ uint16_t AdaGFXrgb565ToColor7(const uint16_t& color) { # endif // if ADAGFX_SUPPORT_7COLOR +/***************************************************************************** + * Convert rgb565 color to the closest matching rgb888 (RRGGBB) color + * (some approximation because of loss of resolution in rgb565) + ****************************************************************************/ +uint32_t AdaGFXrgb565ToRgb888(uint16_t rgb565) { + const uint16_t r = (((rgb565 & 0xF800) >> 11) * 255) / 31; + const uint16_t g = (((rgb565 & 0x07E0) >> 5) * 255) / 63; + const uint16_t b = ((rgb565 & 0x001F) * 255) / 31; + + return (r << 16) + (g << 8) + b; +} + +/**************************************************************************** + * Convert rgb565 color to web-usable color (# prefix with 6 digit hex value) + ***************************************************************************/ +String AdaGFXrgb565ToWebColor(uint16_t rgb565) { + return strformat(F("#%06X"), AdaGFXrgb565ToRgb888(rgb565)); +} + +/**************************************************************************** + * Convert a 24 bit RRGGBB color to rgb565 format. Some bits are lost + ***************************************************************************/ +uint16_t AdaGFXrgb888ToRgb565(uint32_t rgb888) { + const uint8_t r = (rgb888 & 0xFF0000) >> 16; + const uint8_t g = (rgb888 & 0xFF00) >> 8; + const uint8_t b = (rgb888 & 0xFF); + + return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3); +} + /**************************************************************************** * getTextMetrics: Returns the metrics related to current font ***************************************************************************/ diff --git a/src/src/Helpers/AdafruitGFX_helper.h b/src/src/Helpers/AdafruitGFX_helper.h index d0ebb9f032..d26bca0320 100644 --- a/src/src/Helpers/AdafruitGFX_helper.h +++ b/src/src/Helpers/AdafruitGFX_helper.h @@ -440,6 +440,10 @@ String AdaGFXcolorToString(const uint16_t & color, # if ADAGFX_SUPPORT_7COLOR uint16_t AdaGFXrgb565ToColor7(const uint16_t& color); // Convert rgb565 color to 7-color # endif // if ADAGFX_SUPPORT_7COLOR +uint32_t AdaGFXrgb565ToRgb888(uint16_t rgb565); +String AdaGFXrgb565ToWebColor(uint16_t rgb565); +uint16_t AdaGFXrgb888ToRgb565(uint32_t rgb888); + void AdaGFXFormLineSpacing(const __FlashStringHelper *id, uint8_t selectedIndex); String AdaGFXgetFontName(uint8_t fontId, From e0f6b2916dcebdd3c37e0a4dcccde129ff2d6a30 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 26 Aug 2024 12:06:28 +0200 Subject: [PATCH 203/367] [ESP-IDF 5.x] Fix detection of ESP32-rev3 --- platformio_special_envs.ini | 36 ------------------------ src/src/Helpers/Hardware_device_info.cpp | 35 ++++++++++++++++++----- 2 files changed, 28 insertions(+), 43 deletions(-) diff --git a/platformio_special_envs.ini b/platformio_special_envs.ini index ec6315afeb..6b2c28ebd0 100644 --- a/platformio_special_envs.ini +++ b/platformio_special_envs.ini @@ -28,29 +28,6 @@ lib_ignore = ${regular_platform.lib_ignore} LittleFS(esp8266) extra_scripts = ${extra_scripts_esp8266.extra_scripts} -[env:spec_debug_custom_IR_ESP8266_4M1M] -extends = esp8266_4M1M -build_type = ${debug_pio.build_type} -check_tool = ${debug_pio.check_tool} -check_flags = ${debug_pio.check_flags} -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -build_flags = ${regular_platform.build_flags} - ${debug_pio.build_flags} - ${esp8266_4M1M.build_flags} - -DPLUGIN_BUILD_CUSTOM -lib_ignore = ESP32_ping - ESP32WebServer - ServoESP32 - ESP32HTTPUpdateServer - adafruit/Adafruit GFX Library@^1.11.1 - LOLIN_EPD - Adafruit ILI9341 - adafruit/Adafruit BusIO - Adafruit NeoPixel - Adafruit Motor Shield V2 Library -extra_scripts = ${extra_scripts_esp8266.extra_scripts} - [env:spec_debug_beta_custom_ESP8266_4M1M] extends = esp8266_4M1M @@ -167,16 +144,3 @@ board_build.filesystem = littlefs ;board = lolin_d32_pro board = esp32_16M8M - - -; Special env for memory analysis -; This may generate builds which cannot be run, so do not upload to a node. -; Has the same lib_ignore as the IR builds, or else those cannot be built for testing -[env:spec_memanalyze_ESP8266] -extends = esp8266_4M1M -platform = ${regular_platform.platform} -platform_packages = ${regular_platform.platform_packages} -lib_ignore = ${regular_platform.lib_ignore} -build_flags = ${esp8266_4M1M.build_flags} -DMEMORY_ANALYSIS -DPLUGIN_BUILD_CUSTOM -w -DFEATURE_NON_STANDARD_24_TASKS=1 -DTASKS_MAX=24 -extra_scripts = pre:tools/pio/pre_memanalyze.py - pre:tools/pio/generate-compiletime-defines.py diff --git a/src/src/Helpers/Hardware_device_info.cpp b/src/src/Helpers/Hardware_device_info.cpp index 890a3c5456..2c21db8967 100644 --- a/src/src/Helpers/Hardware_device_info.cpp +++ b/src/src/Helpers/Hardware_device_info.cpp @@ -441,8 +441,11 @@ const __FlashStringHelper* getChipModel() { CHIP_ESP32S3 = 9, //!< ESP32-S3 CHIP_ESP32C3 = 5, //!< ESP32-C3 CHIP_ESP32C2 = 12, //!< ESP32-C2 + CHIP_ESP32C5 = 17, //!< ESP32-C5 beta3 (MPW) + CHIP_ESP32C5 = 23, //!< ESP32-C5 MP CHIP_ESP32C6_b = 7, //!< ESP32-C6(beta) CHIP_ESP32C6 = 13, //!< ESP32-C6 + CHIP_ESP32C61 = 20, //!< ESP32-C61 CHIP_ESP32H2_b1 = 10, //!< ESP32-H2(beta1) CHIP_ESP32H2_b2 = 14, //!< ESP32-H2(beta2) CHIP_ESP32H2 = 16, //!< ESP32-H2 @@ -470,7 +473,12 @@ const __FlashStringHelper* getChipModel() { esp_chip_info(&chip_info); uint32_t chip_model = chip_info.model; - uint32_t chip_revision = chip_info.revision; + const uint32_t chip_revision = +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + chip_info.revision / 100; +#else + chip_info.revision; +#endif uint32_t pkg_version = 0; # if (ESP_IDF_VERSION_MAJOR >= 5) @@ -530,6 +538,8 @@ const __FlashStringHelper* getChipModel() { case EFUSE_RD_CHIP_VER_PKG_ESP32PICOV302: return F("ESP32-PICO-V3-02"); // Max 240MHz, Dual core, LGA 7*7, 8MB embedded flash, 2MB embedded PSRAM, // ESP32-PICO-MINI-02, ESP32-PICO-DevKitM-2 + case EFUSE_RD_CHIP_VER_PKG_ESP32D0WDR2V3: + return F("ESP32-D0WDR2-V3"); } # endif // if CONFIG_IDF_TARGET_ESP32 return F("ESP32"); @@ -723,6 +733,15 @@ const __FlashStringHelper* getChipModel() { # endif // CONFIG_IDF_TARGET_ESP32P4 return F("ESP32-P4"); } + else if (17 == chip_model) { // ESP32-C5 beta3 (MPW) + return F("ESP32-C5 beta3"); + } + else if (23 == chip_model) { // ESP32-C5 MP + return F("ESP32-C5"); + } + else if (20 == chip_model) { // ESP32-C61 + return F("ESP32-C61"); + } return F("ESP32"); #elif defined(ESP8266) @@ -823,11 +842,7 @@ String getChipRevision() { # endif // if ESP_IDF_VERSION_MAJOR < 5 } #endif // ifdef ESP32 - String res; - res += rev / 100; - res += '.'; - res += rev % 100; - return res; + return strformat(F("%d.%02d"), rev / 100, rev % 100); } uint32_t getSketchSize() { @@ -893,7 +908,13 @@ bool CanUsePSRAM() { esp_chip_info_t chip_info; esp_chip_info(&chip_info); - if ((CHIP_ESP32 == chip_info.model) && (chip_info.revision < 3)) { + if ((CHIP_ESP32 == chip_info.model) && +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) + (chip_info.revision < 300) +#else + (chip_info.revision < 3) +#endif + ) { return false; } # if ESP_IDF_VERSION_MAJOR < 4 From f296d62fdeaef44b16b5da97c936d3b312b904d3 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Mon, 26 Aug 2024 22:17:56 +0200 Subject: [PATCH 204/367] [P165] Add Clear display on exit, add manual g-segment split, rework UI for better Extra pixel display, many bugfixes --- src/_P165_7SegNeopixel.ino | 4 + src/src/PluginStructs/P165_data_struct.cpp | 403 ++++++++++++--------- src/src/PluginStructs/P165_data_struct.h | 22 +- src/src/Static/WebStaticData.h | 4 +- static/p165_digit.js | 4 +- 5 files changed, 258 insertions(+), 179 deletions(-) diff --git a/src/_P165_7SegNeopixel.ino b/src/_P165_7SegNeopixel.ino index f671ea18f9..9f73ea99e3 100644 --- a/src/_P165_7SegNeopixel.ino +++ b/src/_P165_7SegNeopixel.ino @@ -6,6 +6,10 @@ // ####################################################################################################### /** Changelog: + * 2024-08-26 tonhuisman: Rework the UI somewhat to improve the display of the Extra pixels per group + * Add option for Clear on exit + * Add option to split the g-segment in 2 halves when assigning the number plan (should best be set from 3 pixels) + * this option was enabled by default from 3 horizontal pixels, can now be set manually. * 2024-08-22 tonhuisman: Increased segment pixels for height to 5, equal to width * Increased decimal point digits to 7 (max) and extra pixels to 12 * When font color is either white or black, uses default color to stay visible in both light and dark Web-UI mode. diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 5fbe2df4a5..d876ad52e3 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -19,19 +19,21 @@ P165_data_struct::P165_data_struct(struct EventStruct *event) { # endif // if P165_FEATURE_P073 && P165_EXTRA_FONTS _scrollSpeed = P165_CONFIG_SCROLLSPEED; _suppressLeading0 = P165_GET_FLAG_SUPP0; + _clearOnExit = P165_GET_FLAG_CLEAR_EXIT; _txtScrolling = P165_GET_FLAG_SCROLL_TEXT; _scrollFull = P165_GET_FLAG_SCROLL_FULL; _stdOffset = P165_GET_FLAG_STD_OFFSET; - for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { - memcpy(&_pixelGroupCfg[grp], &P165_GROUP_CFG(grp), sizeof(_pixelGroupCfg[grp])); + for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { + memcpy(&_pixelGroupCfg[grp], &P165_GROUP_CFG(grp), sizeof(P165_GROUP_CFG(grp))); _pixelGroupCfg[grp].aoffs = 0; + _totalDigits += _pixelGroupCfg[grp].dgts; } const uint16_t pxlCount = calculateDisplayPixels(); // Needs the _pixelGroupCfg filled # if P165_DEBUG_INFO - addLog(LOG_LEVEL_INFO, strformat(F("NeoPixel7Segment: Start stripe for %d pixels."), pxlCount)); + addLog(LOG_LEVEL_INFO, strformat(F("NeoPixel7Segment: Start stripe for %d pixels, %d digits."), pxlCount, _totalDigits)); # endif // if P165_DEBUG_INFO strip = new (std::nothrow) NeoPixelBus_wrapper(pxlCount, CONFIG_PIN1, P165_STRIP_TYPE_RGBW == _stripType ? NEO_GRBW + NEO_KHZ800 @@ -122,6 +124,15 @@ int P165_data_struct::offsetLogic_callback(uint16_t position) { } P165_data_struct::~P165_data_struct() { + if (_clearOnExit && (nullptr != strip)) { + const uint16_t pxlCount = calculateDisplayPixels(); + + for (uint16_t pxl = 0; pxl < pxlCount; ++pxl) { + strip->setPixelColor(pxl, 0); + } + strip->show(); + } + for (uint8_t grp = 0; grp < PLUGIN_CONFIGLONGVAR_MAX; ++grp) { delete display[grp]; } @@ -143,6 +154,7 @@ void P165_data_struct::initDigitGroup(struct EventStruct *event, P165_SET_CONFIG_START(grp, false); P165_SET_CONFIG_DEND(grp, false); P165_SET_CONFIG_RTLD(grp, false); + P165_SET_CONFIG_SPLTG(grp, false); } /******************************************************************** @@ -222,6 +234,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { if (P165_CONFIG_SCROLLSPEED == 0) { P165_CONFIG_SCROLLSPEED = 10; } addFormNumericBox(F("Scroll speed (0.1 sec/step)"), F("scrlspd"), P165_CONFIG_SCROLLSPEED, 1, 600); addUnit(F("1..600 = 0.1..60 sec/step")); + addFormCheckBox(F("Clear display on exit"), F("clrexit"), P165_GET_FLAG_CLEAR_EXIT); } addFormSubHeader(F("Display")); @@ -270,13 +283,18 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { fromGrp = grpCount - 1; toGrp = -1; incGrp = -1; - addFormNote(F("Attention, Groups and Digits are shown in reverse order!")); + addFormNote(F("Attention, Groups are shown in reverse order!")); } int16_t dgtOffset = 0; for (int8_t grp = fromGrp; grp != toGrp; grp += incGrp) { - const uint8_t grp10 = grp * 10; + const uint8_t grp10 = grp * 10; + + if (0 == P165_GET_CONFIG_WPIXELS(grp)) { // Check for invalid settings + initDigitGroup(event, grp); + } + const uint8_t grpDgts = P165_GET_CONFIG_DIGITS(grp); const uint8_t grpWPxls = P165_GET_CONFIG_WPIXELS(grp); const uint8_t grpHPxls = P165_GET_CONFIG_HPIXELS(grp); @@ -290,10 +308,6 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { addFormSeparator(2); } - if (0 == grpWPxls) { // Check for invalid settings - initDigitGroup(event, grp); - } - addRowLabel(concat(F("Group "), grp + 1)); { @@ -305,12 +319,15 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { digitOptions, digitOptionValues, nullptr, grpDgts, - true, !numberPlan); // 1st and 2nd column + true, !numberPlan); // 1st and 2nd column for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { + if ((0 == dgt) && grpRtld) { html_TD(); addHtml(F("(Extra)")); } html_TD(); addHtml(F("Digit ")); // 3rd column = "Digit " - addHtmlInt(static_cast(grpRtld ? (grpDgts - dgt) : (dgt + 1))); + addHtmlInt(static_cast(dgt + 1)); } + + if (!grpRtld) { html_TD(); addHtml(F("(Extra)")); } } addFormNumericBox(F("Segment Width pixels"), concat(F("wdth"), grp10), @@ -333,7 +350,6 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { if (grpRtld) { // Take care of Right To Left configured groups dgtOffset += (dgtPxls * (grpDgts - 1)); } - const uint16_t grpAoffs = (grpRtld ? (grpDgts - 1) : 0) * dgtPxls + grpOffs; for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { drawSevenSegment(dgt, grp10, // 3rd column = subtable with digit @@ -348,8 +364,9 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { P165_GET_CONFIG_DEND(grp), fgColor, numberPlan, - dgt < (grpDgts - 1) ? -1 : grpAoffs, - grpWPxls > P165_SEGMENT_G_SPLIT_SIZE); + grpOffs, + P165_GET_CONFIG_SPLTG(grp), + grpRtld); dgtOffset += (dgtPxls * (grpRtld ? -1 : 1)); } @@ -432,7 +449,11 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { addHtml(F("
    ")); addHtml(F("Any 'Extra pixels after' will be
    positioned left of the group!")); addHtml(F("
    ")); - } else { + } + addFormCheckBox(F("Split g-segment pixels"), concat(F("spltg"), grp10), + P165_GET_CONFIG_SPLTG(grp), numberPlan); + + if (!grpRtld) { for (uint8_t r = 12; r < 13; ++r) { html_TR_TD(); addHtml(F(" ")); // We need 13 rows for the digit table to work as intended @@ -504,6 +525,7 @@ bool P165_data_struct::plugin_webform_save(struct EventStruct *event) { P165_SET_FLAG_SUPP0(isFormItemChecked(F("supp0")) ? 1 : 0); P165_SET_FLAG_SCROLL_TEXT(isFormItemChecked(F("scrltxt")) ? 1 : 0); P165_SET_FLAG_SCROLL_FULL(isFormItemChecked(F("scrlfll")) ? 1 : 0); + P165_SET_FLAG_CLEAR_EXIT(isFormItemChecked(F("clrexit")) ? 1 : 0); int stdoff = 1; if (update_whenset_FormItemInt(F("stdoff"), stdoff)) { @@ -537,6 +559,7 @@ bool P165_data_struct::plugin_webform_save(struct EventStruct *event) { P165_SET_CONFIG_START(grp, getFormItemInt(concat(F("strt"), grp10))); P165_SET_CONFIG_DEND(grp, isFormItemChecked(concat(F("dend"), grp10))); P165_SET_CONFIG_RTLD(grp, isFormItemChecked(concat(F("rtld"), grp10))); + P165_SET_CONFIG_SPLTG(grp, isFormItemChecked(concat(F("spltg"), grp10))); } } return true; @@ -586,175 +609,217 @@ uint16_t P165_data_struct::calculateDisplayDigits() { // Draw a 7segment digit with optional decimal point and extra pixels, and max 5-wide segments and 5-high segments constexpr uint16_t P165_digitMask[13] = { // Regular digit layout, max size - 0b011111000, - 0b100000100, - 0b100000100, - 0b100000100, - 0b100000100, - 0b100000100, - 0b011111001, // Extra pixels after - 0b100000100, - 0b100000100, - 0b100000100, - 0b100000100, - 0b100000100, - 0b011111010, // Decimal point pixels + 0b01111100, + 0b10000010, + 0b10000010, + 0b10000010, + 0b10000010, + 0b10000010, + 0b01111100, + 0b10000010, + 0b10000010, + 0b10000010, + 0b10000010, + 0b10000010, + 0b01111101, // Decimal point pixels +}; + +constexpr uint8_t P165_extraMask[13] = { // Extra pixel table mask + 0b0, + 0b0, + 0b0, + 0b0, + 0b0, + 0b0, + 0b1, // Extra pixels after + 0b0, + 0b0, + 0b0, + 0b0, + 0b0, + 0b0, }; constexpr uint16_t P165_digitOverlap[13] = { // Overlap enabled, overlay these pixels - 0b100000100, - 0b000000000, - 0b000000000, - 0b000000000, - 0b000000000, - 0b000000000, - 0b100000100, - 0b000000000, - 0b000000000, - 0b000000000, - 0b000000000, - 0b000000000, - 0b100000100, + 0b10000010, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b10000010, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b00000000, + 0b10000010, }; /******************************************************************** * Draw a 7-segment digit-group by creating a table * with some rows and columns hidden when not max. size ********************************************************************/ -void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit - const uint8_t grp, // Group * 10 - const uint8_t wpixels, // width pixels - const uint8_t hpixels, // heoght pixels - const bool overlap, // corner overlap - const uint8_t decPt, // decimal point pixels - const uint8_t addN, // additional pixels - const uint8_t max, // max already has 1 subtracted - const uint16_t offset, // pre-offset - const bool strt, // start left-top or right-top - const bool dend, // decimal point at end - const String & fgColor, // foreground color - const bool dspPlan, // show number plan - const int16_t aOffs, // addon offset - const bool splitG) { // split segment G in 2 halves - addHtml(F("
    ")); - addHtml(strformat(F(""), digit + grp)); // Group should be factor 10 - - uint8_t hor = 0; - int8_t ver = 0; - uint8_t seg = 0; - - for (uint8_t h = 0; h < NR_ELEMENTS(P165_digitMask); ++h) { - const bool showRow = !(((h < 6) && (6 - h >= hpixels) && (h > 1)) || - ((h > 6) && (h - 6 > hpixels) && (h < 12))); - addHtml(F("")); // Hide row +void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit + const uint8_t grp10, // Group * 10 + const uint8_t wpixels, // width pixels + const uint8_t hpixels, // heoght pixels + const bool overlap, // corner overlap + const uint8_t decPt, // decimal point pixels + const uint8_t addN, // additional pixels + const uint8_t max, // max already has 1 subtracted + const uint16_t offset, // pre-offset + const bool strt, // start left-top or right-top + const bool dend, // decimal point at end + const String & fgColor, // foreground color + const bool dspPlan, // show number plan + const int16_t aOffs, // addon offset + const bool splitG, // split segment G in 2 halves + const bool rtld) { // direction ltr or rtl + int8_t tblFrom = 0; + int8_t tblTo = 1; + int8_t tblInc = 1; + + if ((aOffs >= 0) && (digit == (rtld ? 0 : max))) { + if (rtld) { + tblFrom = 1; + tblTo = -1; + tblInc = -1; } else { - addHtml(F("block'>")); // Show row - - if ((h == 0) || (h == 6)) { - ver = 0; // Restart vertical counter - } else { - ver++; - } + tblTo = 2; } + } - hor = 0; // Restart horizontal counter + for (int8_t tbl = tblFrom; tbl != tblTo; tbl += tblInc) { + const bool drawDigit = 0 == tbl; + const uint8_t maskBits = drawDigit ? 8 : 1; - for (uint8_t w = 0; w < 9; ++w) { - String pIndex; + addHtml(F("
    ")); + addHtml(strformat(F(""), drawDigit ? 'g' : 'x', digit + grp10)); // Group is factor 10 - const bool showCol = !(w > 0 && w >= wpixels && w < 5); + uint8_t hor = 0; + int8_t ver = 0; + uint8_t seg = 0; - html_TD(showCol ? F("width:30px;display:inline-block") : F("width:30px;display:none")); + for (uint8_t h = 0; h < NR_ELEMENTS(P165_digitMask); ++h) { + const bool showRow = !(((h < 6) && (6 - h >= hpixels) && (h > 1)) || + ((h > 6) && (h - 6 > hpixels) && (h < 12))); + addHtml(F("")); // Hide row + } else { + addHtml(F("block'>")); // Show row - pIndex = calculatePixelIndex(hor, - ver - 1, - seg, - offset, - wpixels, - hpixels, - overlap, - strt, - dend, - decPt, - addN, - aOffs, - splitG); - - // pIndex = strformat(F("%d/%d/%d"), hor, ver - 1, seg); // For debugging only - hor++; + if ((h == 0) || (h == 6)) { + ver = 0; // Restart vertical counter + } else { + ver++; } + } - if (w < 7) { - if (dspPlan) { - addHtml(pIndex); - } else { - if (!fgColor.isEmpty()) { // Colored pixel - addHtml(strformat(F("" P165_PIXEL_CHARACTER ""), fgColor.c_str())); + hor = 0; // Restart horizontal counter + + for (uint8_t w = 0; w < maskBits; ++w) { + String pIndex; + + const bool showCol = !(w > 0 && w >= wpixels && w < 5); + + html_TD(showCol ? (dspPlan && (w == maskBits - 1) + ? F("min-width:30px;width:75px;display:inline-block") + : F("width:30px;display:inline-block")) + : F("width:30px;display:none")); + + const bool showBit = drawDigit + ? (bitRead(P165_digitMask[h], (maskBits - 1) - w) || + (overlap && bitRead(P165_digitOverlap[h], (maskBits - 1) - w))) + : bitRead(P165_extraMask[h], (maskBits - 1) - w); + + if (showBit) { + if (dspPlan && showRow && showCol) { // Determine segment for pixel-indexes: + if ((h > 0) && (h < 6)) { // b/f + if (w == 0) { + seg = 5; // f + } else { + seg = 1; // b + } + } else if ((h > 6) && (h < 12)) { // c/e + if (w == 0) { + seg = 4; // e + } else { + seg = 2; // c + } + } else if ((w > 6) && (h == 12)) { // Decimal point + seg = 7; + } else if (h == 6) { // g / Additional pixels + if ((w > 6) || !drawDigit) { + seg = 8; // virtual 9th segment + } else { + seg = 6; // g + } + } else if ((w < 7) && (h == 12)) { + seg = 3; // d } else { - addHtml(F(P165_PIXEL_CHARACTER)); // Pixel + seg = 0; // a } + + pIndex = calculatePixelIndex(hor, + ver - 1, + seg, + offset, + wpixels, + hpixels, + overlap, + strt, + dend, + decPt, + addN, + splitG); + + // pIndex = strformat(F("%d/%d/%d"), hor, ver - 1, seg); // For debugging only + hor++; } - } else if (h == 6) { // Extra pixels after last digit - if ((addN > 0) && (digit == max)) { + + if ((w < 7) && drawDigit) { if (dspPlan) { addHtml(pIndex); } else { - addHtmlInt(addN); // Show number of pixels + if (!fgColor.isEmpty()) { // Colored pixel + addHtml(strformat(F("" P165_PIXEL_CHARACTER ""), fgColor.c_str())); + } else { + addHtml(F(P165_PIXEL_CHARACTER)); // Pixel + } } - } else { - addHtml(F(" ")); // None - } - } else if (h == 12) { // Decimal point - if (decPt > 0) { - if (dspPlan) { - addHtml(pIndex); + } else if (h == 6) { // Extra pixels after last digit + if ((addN > 0) && ((digit == max) || !drawDigit)) { + if (dspPlan) { + addHtml(pIndex); + } else { + addHtmlInt(addN); // Show number of pixels + } + } else { + addHtml(F(" ")); // None + } + } else if (h == 12) { // Decimal point + if (decPt > 0) { + if (dspPlan) { + addHtml(pIndex); + } else { + addHtmlInt(decPt); // Show number of pixels + } } else { - addHtmlInt(decPt); // Show number of pixels + addHtml(F(" ")); // None } } else { - addHtml(F(" ")); // None + addHtml('?'); // this shouldn't ever show up... ;-) } } else { - addHtml('?'); // this shouldn't ever show up... ;-) + addHtml(F(" ")); // No pixel } - } else { - addHtml(F(" ")); // No pixel } } + html_end_table(); } - html_end_table(); } /************************************************************************ @@ -792,7 +857,6 @@ String P165_data_struct::calculatePixelIndex(const uint8_t hor, // horizon const bool dend, // decimal point at end const uint8_t decPt, // decimal point pixels const uint8_t addN, // additional pixels - const int16_t aOffs, // additional pixels offset const bool splitG) { // split G segment in 2 halves int16_t result(offset); const uint8_t hpx = wpixels + (overlap ? 2 : 0); // Overlapping pixels checked on horizontal segments @@ -841,10 +905,7 @@ String P165_data_struct::calculatePixelIndex(const uint8_t hor, // horizon for (uint8_t dp = 1; dp < decPt; ++dp) { result++; - char sep = ','; - - if (dp == 2) { sep = ' '; } - res = strformat(F("%s%c%d"), res.c_str(), sep, result); + res = strformat(F("%s %d"), res.c_str(), result); } return res; } else { @@ -867,18 +928,14 @@ String P165_data_struct::calculatePixelIndex(const uint8_t hor, // horizon } } - if ((seg == 8) && (addN > 0) && (aOffs >= 0)) { - result += aOffs; + if ((seg == 8) && (addN > 0)) { String res(result); res.reserve(addN * 4); for (uint8_t a = 1; a < addN; ++a) { result++; - char sep = ','; - - if ((addN > 5) && (a == addN / 2)) { sep = ' '; } - res = strformat(F("%s%c%d"), res.c_str(), sep, result); + res = strformat(F("%s %d"), res.c_str(), result); } return res; } @@ -896,7 +953,7 @@ void P165_data_struct::fillSegmentBitmap(const uint8_t grp, const uint8_t hpx = pixCfg.wpix + (pixCfg.crnr ? 2 : 0); // Overlapping pixels checked on horizontal segments const uint8_t vpx = pixCfg.hpix; // Vertical pixels = height pixels // smap: Determine the segment(part) order - const uint8_t smap = (pixCfg.strt ? 2 : 0) + (pixCfg.dend ? 1 : 0) + (pixCfg.wpix > P165_SEGMENT_G_SPLIT_SIZE ? 4 : 0); + const uint8_t smap = (pixCfg.strt ? 2 : 0) + (pixCfg.dend ? 1 : 0) + (pixCfg.wpix > pixCfg.splt ? 4 : 0); const uint8_t rh = hpx / 2; // Horizontal half part, right const uint8_t lh = hpx - rh; // Horizontal half part, left @@ -1003,10 +1060,20 @@ bool P165_data_struct::plugin_once_a_second(struct EventStruct *event) { _suppressLeading0, _stdOffset); } - writeBufferToDisplay(_currentGroup); + writeBufferToDisplay(); - // FIXME Should probably determine what group(s) are used and set the extra pixels for those groups - extraPixelsState(_currentGroup, _timesep ? 1 : 0, AdaGFXrgb565ToRgb888(_timesep ? _fgColor : _bgColor)); + // FIXME Determine what group(s) are used and set the extra pixels for those groups + uint8_t dgts = 0; + const uint8_t dgtsNeeded = std::min(_totalDigits, static_cast(P165_DISP_DATE == _output ? 8 : 6)); + + for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { + dgts += _pixelGroupCfg[grp].dgts; + addLog(LOG_LEVEL_INFO, strformat(F("P165 : seconds marker %d Group %d, digits %d offset %d"), _timesep, grp, dgts, _stdOffset)); + + if ((_stdOffset <= dgts) && (dgtsNeeded - _stdOffset > dgts)) { + extraPixelsState(grp + 1, _timesep ? 1 : 0, AdaGFXrgb565ToRgb888(_timesep ? _fgColor : _bgColor)); + } + } return true; } @@ -1020,7 +1087,7 @@ bool P165_data_struct::plugin_ten_per_second(struct EventStruct *event) { } if (nextScroll()) { - writeBufferToDisplay(_currentGroup); + writeBufferToDisplay(); } return true; } @@ -1119,7 +1186,7 @@ bool P165_data_struct::plugin_write(struct EventStruct *event, break; case p165_commands_e::c7db: // Set brightness - if ((event->Par1 >= 0) && (event->Par1 < 256)) { + if ((event->Par1 >= 0) && (event->Par1 <= _maxBrightness)) { # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_INFO)) { @@ -1693,12 +1760,12 @@ void P165_data_struct::put4NumbersInBuffer(const uint8_t nr1, showbuffer[2 + offset] = static_cast((nr2 / 10) + cOffs); showbuffer[3 + offset] = (nr2 % 10) + cOffs; - if ((nr3 > -1) && ((5 + offset) < P165_SHOW_BUFFER_SIZE)) { + if ((nr3 > -1) && ((5 + offset) < _totalDigits)) { showbuffer[4 + offset] = static_cast((nr3 / 10) + cOffs); showbuffer[5 + offset] = (nr3 % 10) + cOffs; } - if ((nr4 > -1) && ((7 + offset) < P165_SHOW_BUFFER_SIZE)) { + if ((nr4 > -1) && ((7 + offset) < _totalDigits)) { showbuffer[6 + offset] = static_cast((nr4 / 10) + cOffs); showbuffer[7 + offset] = (nr4 % 10) + cOffs; } diff --git a/src/src/PluginStructs/P165_data_struct.h b/src/src/PluginStructs/P165_data_struct.h index 29ab1325f4..9584ee70c6 100644 --- a/src/src/PluginStructs/P165_data_struct.h +++ b/src/src/PluginStructs/P165_data_struct.h @@ -13,7 +13,7 @@ # include "../Static/WebStaticData.h" // Javascript and support functions # define P165_DEBUG_INFO 1 // set 1 to enable some extra debug logging -# define P165_DEBUG_DEBUG 0 // set 1 to enable some extra development debug logging +# define P165_DEBUG_DEBUG 1 // set 1 to enable some extra development debug logging # ifdef USES_P073 # define P165_FEATURE_P073 1 // Use P073 shared functions when available @@ -69,18 +69,21 @@ # define P165_FLAG_SCROLL_TEXT 2 // 1 bit # define P165_FLAG_SCROLL_FULL 3 // 1 bit # define P165_FLAG_STD_OFFSET 4 // 4 bit +# define P165_FLAG_CLEAR_EXIT 8 // 1 bit # define P165_GET_FLAG_SUPP0 (bitRead(P165_FLAGS, P165_FLAG_SUPPRESS_0)) # define P165_GET_FLAG_NUMBERPLAN (bitRead(P165_FLAGS, P165_FLAG_NUMBERPLAN)) # define P165_GET_FLAG_SCROLL_TEXT (bitRead(P165_FLAGS, P165_FLAG_SCROLL_TEXT)) # define P165_GET_FLAG_SCROLL_FULL (bitRead(P165_FLAGS, P165_FLAG_SCROLL_FULL)) # define P165_GET_FLAG_STD_OFFSET (get4BitFromUL(P165_FLAGS, P165_FLAG_STD_OFFSET)) +# define P165_GET_FLAG_CLEAR_EXIT (bitRead(P165_FLAGS, P165_FLAG_CLEAR_EXIT)) # define P165_SET_FLAG_SUPP0(V) (bitWrite(P165_FLAGS, P165_FLAG_SUPPRESS_0, V)) # define P165_SET_FLAG_NUMBERPLAN(V) (bitWrite(P165_FLAGS, P165_FLAG_NUMBERPLAN, V)) # define P165_SET_FLAG_SCROLL_TEXT(V) (bitWrite(P165_FLAGS, P165_FLAG_SCROLL_TEXT, V)) # define P165_SET_FLAG_SCROLL_FULL(V) (bitWrite(P165_FLAGS, P165_FLAG_SCROLL_FULL, V)) # define P165_SET_FLAG_STD_OFFSET(V) (set4BitToUL(P165_FLAGS, P165_FLAG_STD_OFFSET, V)) +# define P165_SET_FLAG_CLEAR_EXIT(V) (bitWrite(P165_FLAGS, P165_FLAG_CLEAR_EXIT, V)) // Config per display group, all 4 PCONFIG_(U)LONG variables used # define P165_GROUP_CFG(N) PCONFIG_ULONG(N) @@ -95,6 +98,7 @@ # define P165_CONFIG_IDX_START 21u // 1 bit # define P165_CONFIG_IDX_DEND 22u // 1 bit # define P165_CONFIG_IDX_RTLD 23u // 1 bit +# define P165_CONFIG_IDX_SPLTG 24u // 1 bit # define P165_GET_CONFIG_WPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS)) # define P165_GET_CONFIG_HPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS)) @@ -106,6 +110,7 @@ # define P165_GET_CONFIG_START(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_START)) # define P165_GET_CONFIG_DEND(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_DEND)) # define P165_GET_CONFIG_RTLD(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_RTLD)) +# define P165_GET_CONFIG_SPLTG(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_SPLTG)) # define P165_SET_CONFIG_WPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS, V)) # define P165_SET_CONFIG_HPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS, V)) @@ -117,6 +122,7 @@ # define P165_SET_CONFIG_START(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_START, V)) # define P165_SET_CONFIG_DEND(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_DEND, V)) # define P165_SET_CONFIG_RTLD(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_RTLD, V)) +# define P165_SET_CONFIG_SPLTG(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_SPLTG, V)) # define P165_SHOW_BUFFER_SIZE 16 // Max number of characters in the buffer to show (1..4 groups of 1..4 digits) @@ -132,10 +138,9 @@ # define P165_SEGMENT_DOT_PIXELS 7 // Max: 7 (3 bits) # define P165_SEGMENT_ADDON_PIXELS 12 // Max: 15 (4 bits) -# define P165_SEGMENT_G_SPLIT_SIZE 2 // Set typedef before include -typedef uint64_t segsize_t; // largest storage size available, allows for up to 64 pixels per digit, 41 used +typedef uint64_t segsize_t; // largest storage size available, allows for up to 64 pixels per digit # include # if P165_FEATURE_P073 @@ -157,7 +162,8 @@ struct P165_data_struct : public PluginTaskData_base { uint32_t strt : 1; // start segment 0 = left-top/a, 1 = right-top/b uint32_t dend : 1; // dot at: 1: end of digit 0: between c/d segments uint32_t rtld : 1; // right to left display - uint32_t unused : 8; + uint32_t splt : 1; // split g-segment (best enabled > 3 horizontal pixels) + uint32_t unused : 7; uint32_t aoffs : 16; // Add-on pixels offset (use uin32_t for better memory alignment) uint32_t unused2 : 16; }; @@ -190,7 +196,7 @@ struct P165_data_struct : public PluginTaskData_base { const uint8_t decPt, const uint8_t addN); static void drawSevenSegment(const uint8_t digit, - const uint8_t grp, + const uint8_t grp10, const uint8_t wpixels, const uint8_t hpixels, const bool overlap, @@ -203,7 +209,8 @@ struct P165_data_struct : public PluginTaskData_base { const String & fgColor, const bool dspPlan, const int16_t aOffs, - const bool splitG); + const bool splitG, + const bool rtld); static String calculatePixelIndex(const uint8_t hor, const int8_t ver, const uint8_t seg, @@ -215,7 +222,6 @@ struct P165_data_struct : public PluginTaskData_base { const bool dend, const uint8_t decPt, const uint8_t addN, - const int16_t aOffs, const bool splitG); static void addJavascript(); uint16_t calculateDisplayPixels(); @@ -299,9 +305,11 @@ struct P165_data_struct : public PluginTaskData_base { uint8_t _output = 0; uint8_t _fontset = 0; uint8_t _stdOffset = 0; + uint8_t _totalDigits = 0; bool _initialized = false; bool _timesep = false; bool _suppressLeading0 = false; + bool _clearOnExit = false; String _textToScroll; bool _txtScrolling = false; diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index c994b19ff9..53f0edf5fe 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -719,13 +719,13 @@ static const char p165_digit_script[] PROGMEM = { "function sh_col(a,b,d){s=document.getElementById(a).getElementsByTagName('TR');for(r of s)" " {c=r?.children[b];if(c?.tagName=='TD'){c.style.width=20;c.style.display=d?'inline-block':'none'}}};" "function sh_row(a,b,c){r=document.getElementById(a).getElementsByTagName('TR')[b];if(r)r.style.display=c?'block':'none'};" - "function set_td(a,b,d,e,f){c=document.getElementById(a).getElementsByTagName('TR')[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" + "function set_td(a,b,d,e,f){c=document.getElementById(a)?.getElementsByTagName('TR')[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" "function chWdth(a,b,c,d,e){for(t=b;t☸';else z='☸';" " for(t=c;t0,12,7,''+a)};" - "function chAddn(a,b,c,d,e){for(t=b;t0&&d&&t===b+c-1,6,8,''+a)};" + "function chAddn(a,b,c,d,e){for(t=b;t0&&d,6,0,''+a)};" "function dgts(a,b){z=0;for(q=0;q content if fill is true, else non-breaking space, minimal checks - c = document.getElementById(tbl).getElementsByTagName('TR')[row].children[col]; + c = document.getElementById(tbl)?.getElementsByTagName('TR')[row].children[col]; if (c?.tagName == 'TD') c.innerHTML = fill ? td : ' '; }; // Next functions: 4th & 5th parameters are used only in 1 function, but to keep the calling logic simple, leave it there @@ -50,7 +50,7 @@ function chDecp(value, base, count, max, colr) { // Change Decimal point pixels }; function chAddn(value, base, count, max, colr) { // Change Additional pixels for (t = base; t < base + count; t++) - set_td('dgtbl' + t, value > 0 && max && t === base + count - 1, 6, 8, '' + value); + set_td('dxtbl' + t, value > 0 && max /*&& t === base + count - 1*/, 6, 0, '' + value); }; function dgts(gps, flds) { // Update pixel counts per group and total pixels z = 0; From 913f54e699def88b7d1c849612f8c16b98845294 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 27 Aug 2024 14:54:03 +0200 Subject: [PATCH 205/367] [P165] Breaking Settings change! (delete & re-add) Allow 7 pixels/segment for w/h/dot, add 7dbefore command --- src/_P165_7SegNeopixel.ino | 5 + src/src/PluginStructs/P165_data_struct.cpp | 334 ++++++++++++--------- src/src/PluginStructs/P165_data_struct.h | 87 +++--- src/src/Static/WebStaticData.h | 10 +- static/p165_digit.js | 16 +- 5 files changed, 262 insertions(+), 190 deletions(-) diff --git a/src/_P165_7SegNeopixel.ino b/src/_P165_7SegNeopixel.ino index 9f73ea99e3..b0377af96e 100644 --- a/src/_P165_7SegNeopixel.ino +++ b/src/_P165_7SegNeopixel.ino @@ -6,6 +6,11 @@ // ####################################################################################################### /** Changelog: + * 2024-08-27 tonhuisman: Changed max. number of pixels per segment to 7 (from 5), changed settings storage from 3 to 4 bits for + * height, width and dot pixels per segment (not fully used yet, just preparing for future expansion). + * Add checks for not having > 64 bits per digit, as that's technically not supported. + * Reduce Digit display to ca. 80% size to save some screen real-estate. + * Add command 7dbefore, like 7dextra, to set the Pixels-offset before pixels on/off, left half, right half. * 2024-08-26 tonhuisman: Rework the UI somewhat to improve the display of the Extra pixels per group * Add option for Clear on exit * Add option to split the g-segment in 2 halves when assigning the number plan (should best be set from 3 pixels) diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index d876ad52e3..20fa042445 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -27,6 +27,7 @@ P165_data_struct::P165_data_struct(struct EventStruct *event) { for (uint8_t grp = 0; grp < _pixelGroups; ++grp) { memcpy(&_pixelGroupCfg[grp], &P165_GROUP_CFG(grp), sizeof(P165_GROUP_CFG(grp))); _pixelGroupCfg[grp].aoffs = 0; + _pixelGroupCfg[grp].boffs = 0; _totalDigits += _pixelGroupCfg[grp].dgts; } @@ -63,20 +64,31 @@ P165_data_struct::P165_data_struct(struct EventStruct *event) { for (int8_t grp = fromGrp; grp != toGrp; grp += incGrp) { fillSegmentBitmap(grp, _pixelGroupCfg[grp]); + if (_pixelGroupCfg[grp].offs > 0) { + _pixelGroupCfg[grp].boffs = pxlOffset; + # if P165_DEBUG_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("P165 : group: %d before offset: %d, length: %d"), + grp + 1, _pixelGroupCfg[grp].boffs, _pixelGroupCfg[grp].offs)); + # endif // if P165_DEBUG_DEBUG + } pxlOffset += _pixelGroupCfg[grp].offs; - const uint8_t pxlDigit = calculateGroupPixels(1, // Count pixels for 1 digit only - _pixelGroupCfg[grp].wpix, - _pixelGroupCfg[grp].hpix, - _pixelGroupCfg[grp].crnr, - _pixelGroupCfg[grp].dotp, - 0); // Extra pixels passed separately + const uint8_t pxlDigit = min(static_cast(63), + calculateGroupPixels(1, // Count pixels for 1 digit only + _pixelGroupCfg[grp].wpix, + _pixelGroupCfg[grp].hpix, + _pixelGroupCfg[grp].crnr, + _pixelGroupCfg[grp].dotp, + 0)); // Extra pixels passed separately # if P165_DEBUG_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("P165 : group: %d -------- start-pixel: %d"), grp + 1, pxlOffset)); for (uint8_t seg = 0; seg < 8; ++seg) { addLog(LOG_LEVEL_INFO, strformat(F("P165 : segment: %c, bits: 0b%s"), 'a' + seg, - ull2String(bitSetULL(segments[grp][seg], pxlDigit + 2), 2).substring(1).c_str())); - bitClearULL(segments[grp][seg], pxlDigit + 2); // Reset additional bit + ull2String(pxlDigit < 63 ? bitSetULL(segments[grp][seg], pxlDigit + 1) + : segments[grp][seg], 2).substring(1).c_str())); + + if (pxlDigit < 63) { bitClearULL(segments[grp][seg], pxlDigit + 1); // Reset additional bit + } } # endif // if P165_DEBUG_DEBUG @@ -313,6 +325,20 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { html_table(F("")); + { // Headers on a separate line + html_TR_TD(); addHtml(F(" ")); // 2 blank columns + html_TD(); addHtml(F(" ")); + + // 3rd column = "Digit " / "(Extra)" + for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { + if ((0 == dgt) && grpRtld) { html_TD(); addHtml(F("(Extra)")); } + html_TD(); addHtml(F("Digit ")); + addHtmlInt(static_cast(dgt + 1)); + } + + if (!grpRtld) { html_TD(); addHtml(F("(Extra)")); } + } + addRowLabel(F("Number of Digits *")); addSelector(concat(F("dgts"), grp10), NR_ELEMENTS(digitOptionValues), @@ -320,23 +346,8 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { digitOptionValues, nullptr, grpDgts, true, !numberPlan); // 1st and 2nd column - - for (uint8_t dgt = 0; dgt < grpDgts; ++dgt) { - if ((0 == dgt) && grpRtld) { html_TD(); addHtml(F("(Extra)")); } - html_TD(); addHtml(F("Digit ")); // 3rd column = "Digit " - addHtmlInt(static_cast(dgt + 1)); - } - - if (!grpRtld) { html_TD(); addHtml(F("(Extra)")); } } - addFormNumericBox(F("Segment Width pixels"), concat(F("wdth"), grp10), - grpWPxls, 1, 5, - # if FEATURE_TOOLTIPS - EMPTY_STRING, - # endif // if FEATURE_TOOLTIPS - numberPlan); - dgtOffset += grpOffs; const int16_t dgtPxls = calculateGroupPixels(1, // Count single digit without additional pixels @@ -377,8 +388,15 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { } { + addFormNumericBox(F("Segment Width pixels"), concat(F("wdth"), grp10), + grpWPxls, 1, P165_SEGMENT_WIDTH_PIXELS, + # if FEATURE_TOOLTIPS + EMPTY_STRING, + # endif // if FEATURE_TOOLTIPS + numberPlan); + addFormNumericBox(F("Segment Height pixels"), concat(F("hght"), grp10), - grpHPxls, 1, 5, + grpHPxls, 1, P165_SEGMENT_HEIGHT_PIXELS, # if FEATURE_TOOLTIPS EMPTY_STRING, # endif // if FEATURE_TOOLTIPS @@ -423,7 +441,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { addFormNumericBox(F("Pixel-offset before"), concat(F("offs"), grp10), - grpOffs, 0, 15, + grpOffs, 0, P165_SEGMENT_EXTRA_PIXELS, # if FEATURE_TOOLTIPS EMPTY_STRING, # endif // if FEATURE_TOOLTIPS @@ -441,24 +459,20 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { addFormCheckBox(F("Decimal pixels last segment"), concat(F("dend"), grp10), P165_GET_CONFIG_DEND(grp), numberPlan); - addFormCheckBox(F("Right to Left digits"), concat(F("rtld"), grp10), + addFormCheckBox(F("Right to Left digits"), concat(F("rtld"), grp10), grpRtld, numberPlan); - if (grpRtld) { - html_TR_TD(); - addHtml(F("
    ")); - addHtml(F("Any 'Extra pixels after' will be
    positioned left of the group!")); - addHtml(F("
    ")); - } addFormCheckBox(F("Split g-segment pixels"), concat(F("spltg"), grp10), P165_GET_CONFIG_SPLTG(grp), numberPlan); - if (!grpRtld) { - for (uint8_t r = 12; r < 13; ++r) { - html_TR_TD(); - addHtml(F(" ")); // We need 13 rows for the digit table to work as intended - } + # if P165_DIGIT_TABLE_H_INT > 17 + int rws = 17; // Above should be fixed number of rows, matching with ~80% digit table size + + for (; rws < P165_DIGIT_TABLE_H_INT; ++rws) { + html_TR_TD(); + addHtml(F(" ")); // We need P165_digit_table_h_int rows for the digit table to work as intended } + # endif // if P165_DIGIT_TABLE_H_INT > 17 } html_end_table(); @@ -560,6 +574,18 @@ bool P165_data_struct::plugin_webform_save(struct EventStruct *event) { P165_SET_CONFIG_DEND(grp, isFormItemChecked(concat(F("dend"), grp10))); P165_SET_CONFIG_RTLD(grp, isFormItemChecked(concat(F("rtld"), grp10))); P165_SET_CONFIG_SPLTG(grp, isFormItemChecked(concat(F("spltg"), grp10))); + # if P165_SEGMENT_WIDTH_PIXELS > 7 || P165_SEGMENT_HEIGHT_PIXELS > 7 + const uint8_t grpPixels = calculateGroupPixels(1, + P165_GET_CONFIG_WPIXELS(grp), + P165_GET_CONFIG_HPIXELS(grp), + P165_GET_CONFIG_CORNER(grp), + P165_GET_CONFIG_DOT(grp), + 0); + + if (grpPixels > 64) { + addHtmlError(strformat(F("Error: Group %d > 64 pixels configured (%d)"), grp + 1, grpPixels)); + } + # endif // if P165_SEGMENT_WIDTH_PIXELS > 7 || P165_SEGMENT_HEIGHT_PIXELS > 7 } } return true; @@ -608,23 +634,29 @@ uint16_t P165_data_struct::calculateDisplayDigits() { } // Draw a 7segment digit with optional decimal point and extra pixels, and max 5-wide segments and 5-high segments -constexpr uint16_t P165_digitMask[13] = { // Regular digit layout, max size - 0b01111100, - 0b10000010, - 0b10000010, - 0b10000010, - 0b10000010, - 0b10000010, - 0b01111100, - 0b10000010, - 0b10000010, - 0b10000010, - 0b10000010, - 0b10000010, - 0b01111101, // Decimal point pixels +constexpr uint16_t P165_digitMask[P165_DIGIT_TABLE_H_INT] = { // Regular digit layout, max size + 0b0111111100, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b0111111100, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b1000000010, + 0b0111111101, // Decimal point pixels }; -constexpr uint8_t P165_extraMask[13] = { // Extra pixel table mask +constexpr uint8_t P165_extraMask[P165_DIGIT_TABLE_H_INT] = { // Extra pixel table mask + 0b0, + 0b0, 0b0, 0b0, 0b0, @@ -638,22 +670,28 @@ constexpr uint8_t P165_extraMask[13] = { // Extra pixel table mask 0b0, 0b0, 0b0, + 0b0, + 0b0, }; -constexpr uint16_t P165_digitOverlap[13] = { // Overlap enabled, overlay these pixels - 0b10000010, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b10000010, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b00000000, - 0b10000010, +constexpr uint16_t P165_digitOverlap[P165_DIGIT_TABLE_H_INT] = { // Overlap enabled, overlay these 6 pixels + 0b1000000010, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b1000000010, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b0000000000, + 0b1000000010, }; /******************************************************************** @@ -676,6 +714,12 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit const int16_t aOffs, // addon offset const bool splitG, // split segment G in 2 halves const bool rtld) { // direction ltr or rtl + constexpr uint8_t hrMax = NR_ELEMENTS(P165_digitMask) - 1; // Height row max + constexpr uint8_t hcMask = hrMax / 2; // Height center + const uint8_t wrMask = 7; // Width right + const uint8_t wdMask = 8; // Width dot pos + const uint8_t wcBits = 10; // Width column bits + int8_t tblFrom = 0; int8_t tblTo = 1; int8_t tblInc = 1; @@ -692,9 +736,9 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit for (int8_t tbl = tblFrom; tbl != tblTo; tbl += tblInc) { const bool drawDigit = 0 == tbl; - const uint8_t maskBits = drawDigit ? 8 : 1; + const uint8_t maskBits = drawDigit ? wcBits : 1; - addHtml(F("
    ")); + addHtml(F("")); addHtml(strformat(F(""), drawDigit ? 'g' : 'x', digit + grp10)); // Group is factor 10 uint8_t hor = 0; @@ -702,16 +746,16 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit uint8_t seg = 0; for (uint8_t h = 0; h < NR_ELEMENTS(P165_digitMask); ++h) { - const bool showRow = !(((h < 6) && (6 - h >= hpixels) && (h > 1)) || - ((h > 6) && (h - 6 > hpixels) && (h < 12))); - addHtml(F("")); // Hide row } else { addHtml(F("block'>")); // Show row - if ((h == 0) || (h == 6)) { + if ((h == 0) || (h == hcMask)) { ver = 0; // Restart vertical counter } else { ver++; @@ -723,12 +767,12 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit for (uint8_t w = 0; w < maskBits; ++w) { String pIndex; - const bool showCol = !(w > 0 && w >= wpixels && w < 5); + const bool showCol = !(w > 0 && w >= wpixels && w < wrMask); html_TD(showCol ? (dspPlan && (w == maskBits - 1) - ? F("min-width:30px;width:75px;display:inline-block") - : F("width:30px;display:inline-block")) - : F("width:30px;display:none")); + ? F("min-width:" P165_TD_SIZE ";width:4rem;font-size:80%;display:inline-block") + : F("width:" P165_TD_SIZE ";font-size:80%;display:inline-block")) + : F("width:" P165_TD_SIZE ";display:none")); const bool showBit = drawDigit ? (bitRead(P165_digitMask[h], (maskBits - 1) - w) || @@ -736,31 +780,31 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit : bitRead(P165_extraMask[h], (maskBits - 1) - w); if (showBit) { - if (dspPlan && showRow && showCol) { // Determine segment for pixel-indexes: - if ((h > 0) && (h < 6)) { // b/f + if (dspPlan && showRow && showCol) { // Determine segment for pixel-indexes: + if ((h > 0) && (h < hcMask)) { // b/f if (w == 0) { - seg = 5; // f + seg = 5; // f } else { - seg = 1; // b + seg = 1; // b } - } else if ((h > 6) && (h < 12)) { // c/e + } else if ((h > hcMask) && (h < hrMax)) { // c/e if (w == 0) { - seg = 4; // e + seg = 4; // e } else { - seg = 2; // c + seg = 2; // c } - } else if ((w > 6) && (h == 12)) { // Decimal point + } else if ((w > wdMask) && (h == hrMax)) { // Decimal point seg = 7; - } else if (h == 6) { // g / Additional pixels - if ((w > 6) || !drawDigit) { - seg = 8; // virtual 9th segment + } else if (h == hcMask) { // g / Additional pixels + if ((w > wdMask) || !drawDigit) { + seg = 8; // virtual 9th segment } else { - seg = 6; // g + seg = 6; // g } - } else if ((w < 7) && (h == 12)) { - seg = 3; // d + } else if ((w <= wdMask) && (h == hrMax)) { + seg = 3; // d } else { - seg = 0; // a + seg = 0; // a } pIndex = calculatePixelIndex(hor, @@ -780,7 +824,7 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit hor++; } - if ((w < 7) && drawDigit) { + if ((w <= wdMask) && drawDigit) { if (dspPlan) { addHtml(pIndex); } else { @@ -790,7 +834,7 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit addHtml(F(P165_PIXEL_CHARACTER)); // Pixel } } - } else if (h == 6) { // Extra pixels after last digit + } else if (h == hcMask) { // Extra pixels after last digit if ((addN > 0) && ((digit == max) || !drawDigit)) { if (dspPlan) { addHtml(pIndex); @@ -800,7 +844,7 @@ void P165_data_struct::drawSevenSegment(const uint8_t digit, // Digit } else { addHtml(F(" ")); // None } - } else if (h == 12) { // Decimal point + } else if (h == hrMax) { // Decimal point if (decPt > 0) { if (dspPlan) { addHtml(pIndex); @@ -950,19 +994,20 @@ void P165_data_struct::fillSegmentBitmap(const uint8_t grp, if (grp > _pixelGroups) { return; } segsize_t pbit = 0; - const uint8_t hpx = pixCfg.wpix + (pixCfg.crnr ? 2 : 0); // Overlapping pixels checked on horizontal segments - const uint8_t vpx = pixCfg.hpix; // Vertical pixels = height pixels + const uint8_t hpx = pixCfg.wpix + (pixCfg.crnr ? 2 : 0); // Overlapping pixels checked on horizontal segments + const uint8_t vpx = pixCfg.hpix; // Vertical pixels = height pixels // smap: Determine the segment(part) order const uint8_t smap = (pixCfg.strt ? 2 : 0) + (pixCfg.dend ? 1 : 0) + (pixCfg.wpix > pixCfg.splt ? 4 : 0); - const uint8_t rh = hpx / 2; // Horizontal half part, right - const uint8_t lh = hpx - rh; // Horizontal half part, left + const uint8_t rh = hpx / 2; // Horizontal half part, right + const uint8_t lh = hpx - rh; // Horizontal half part, left - for (uint8_t spoint = 0; spoint < P165_segmentCnt; ++spoint) { // loop over all segments a..h + // loop over all segments a..h but don't exceed 64 bits + for (uint8_t spoint = 0; spoint < P165_segmentCnt && pbit < 64; ++spoint) { const uint8_t segment = pgm_read_byte(&(P165_segmentMap[smap][spoint])); - if ((0 == segment) || (3 == segment) || (6 == segment)) { // horizontal segments a/d/g + if ((0 == segment) || (3 == segment) || (6 == segment)) { // horizontal segments a/d/g if (pixCfg.crnr) { - for (uint8_t h = 0; h < hpx; ++h) { + for (uint8_t h = 0; h < hpx && pbit < 64; ++h) { bitSetULL(segments[grp][segment], pbit); if ((0 == segment) && (h == 0)) { // left pixel (a,f) @@ -988,13 +1033,13 @@ void P165_data_struct::fillSegmentBitmap(const uint8_t grp, pbit++; // next pixel } } else { - for (uint8_t h = 0; h < hpx; ++h) { + for (uint8_t h = 0; h < hpx && pbit < 64; ++h) { bitSetULL(segments[grp][segment], pbit); pbit++; } } } else if (16 == segment) { // Horizontal segment g, right half - for (uint8_t h = rh; h < hpx; ++h) { + for (uint8_t h = rh; h < hpx && pbit < 64; ++h) { bitSetULL(segments[grp][6], pbit); if (pixCfg.crnr && (h == hpx - 1)) { // right pixel (g,b,c) @@ -1004,7 +1049,7 @@ void P165_data_struct::fillSegmentBitmap(const uint8_t grp, pbit++; } } else if (26 == segment) { // Horizontal segment g, left half - for (uint8_t h = 0; h < hpx - lh; ++h) { + for (uint8_t h = 0; h < hpx - lh && pbit < 64; ++h) { bitSetULL(segments[grp][6], pbit); if (pixCfg.crnr && (h == 0)) { // left pixel (g,e,f) @@ -1014,12 +1059,12 @@ void P165_data_struct::fillSegmentBitmap(const uint8_t grp, pbit++; } } else if (7 == segment) { // Decimal point segment - for (uint8_t dp = 0; dp < pixCfg.dotp; ++dp) { + for (uint8_t dp = 0; dp < pixCfg.dotp && pbit < 64; ++dp) { bitSetULL(segments[grp][segment], pbit); pbit++; } } else if (255 != segment) { // Vertical segments b/c/e/f - for (uint8_t v = 0; v < vpx; ++v) { + for (uint8_t v = 0; v < vpx && pbit < 64; ++v) { bitSetULL(segments[grp][segment], pbit); pbit++; } @@ -1102,6 +1147,7 @@ const char p165_commands[] PROGMEM = # endif // if P165_FEATURE_P073 "7dbin|" "7dextra|" + "7dbefore|" "7don|" "7doff|" "7db|" @@ -1124,6 +1170,7 @@ enum class p165_commands_e : int8_t { # endif // if P165_FEATURE_P073 c7dbin, c7dextra, + c7dbefore, c7don, c7doff, c7db, @@ -1272,27 +1319,31 @@ bool P165_data_struct::plugin_write(struct EventStruct *event, newScroll = false; success = plugin_write_7digit(text); break; - case p165_commands_e::c7dextra: // 7dextra,,<0|1|2|3|4|5>[,,,[,]] : Set the 'Extra pixels after' on/off - // with optional color, group 0 sets the color to all groups additional pixels + case p165_commands_e::c7dextra: // 7dextra,,<0|1|2|3|4|5>[,,,[,]] : Set the 'Extra pixels after' on/off + // with optional color, group 0 sets the color to all groups additional pixels + case p165_commands_e::c7dbefore: // 7dbefore,,<0|1|2|3|4|5>[,,,[,]] : Set the 'Pixel-offset before' on/off { - const uint8_t par1 = event->Par1; + const uint8_t par1 = event->Par1; + const bool pxlExtra = p165_commands_e::c7dextra == cmd; if (((0 == par1) || ((par1 > 0) && (par1 <= _pixelGroups) && - (_pixelGroupCfg[par1 - 1].addn > 0))) && // single group have extra pixels after? - !parseString(string, 3).isEmpty()) { // on/off is given + pxlExtra ? (_pixelGroupCfg[par1 - 1].addn > 0) + : (_pixelGroupCfg[par1 - 1].offs > 0) + )) && // single group have extra pixels after? + !parseString(string, 3).isEmpty()) { // on/off is given const bool rgbW = P165_STRIP_TYPE_RGBW == _stripType; - const bool onState = 1 == (event->Par2 & 0x01); // Check for On (1/3/5) or Off (0/2/4) + const bool onState = 1 == (event->Par2 & 0x01); // Check for On (1/3/5) or Off (0/2/4) uint32_t fgColor = 0; - uint32_t bgColor = 0; // Ignored! + uint32_t bgColor = 0; // Ignored! bool fgSet = false; - bool bgSet = false; // Ignored! + bool bgSet = false; // Ignored! parseRGBWColors(parseStringToEnd(string, 4), rgbW, fgColor, bgColor, fgSet, bgSet); const uint32_t newColor = onState ? (fgSet ? fgColor : AdaGFXrgb565ToRgb888(_fgColor)) : (fgSet ? fgColor : AdaGFXrgb565ToRgb888(_bgColor)); - extraPixelsState(par1, event->Par2, newColor); + extraPixelsState(par1, event->Par2, newColor, pxlExtra); success = true; } break; @@ -1402,37 +1453,41 @@ bool P165_data_struct::plugin_write(struct EventStruct *event, } /************************************************************************************** - * set the state (color) of the extra pixels of 1 or all(0) groups, + * set the state (color) of the extra befoer/after pixels of 1 or all(0) groups, * state: 0/1: all off/on, 2/3: first half, 4/5: second half *************************************************************************************/ -bool P165_data_struct::extraPixelsState(uint8_t group, uint8_t state, uint32_t color) { +bool P165_data_struct::extraPixelsState(uint8_t group, uint8_t state, uint32_t color, bool pxlExtra) { const uint8_t pxPart = (state >> 1); // Check what part to switch, cut off lowest bit const uint8_t fromGrp = 0 == group ? 0 : group - 1; // All or 1 group const uint8_t toGrp = 0 == group ? _pixelGroups : group; for (uint8_t grp = fromGrp; grp < toGrp; ++grp) { - if (_pixelGroupCfg[grp].addn > 0) { + const uint8_t xPixels = pxlExtra ? _pixelGroupCfg[grp].addn : _pixelGroupCfg[grp].offs; + + if (xPixels > 0) { uint16_t pxFrom = 0; - uint16_t pxTo = _pixelGroupCfg[grp].addn; + uint16_t pxTo = xPixels; - if ((_pixelGroupCfg[grp].addn > 1) && (pxPart > 0)) { // 0 = full block - if (1 == pxPart) { // 2/3 first half off/on - pxTo = _pixelGroupCfg[grp].addn / 2; - } else { // 4/5 second half off/on - pxFrom = _pixelGroupCfg[grp].addn / 2; + if ((xPixels > 1) && (pxPart > 0)) { // 0 = full block + if (1 == pxPart) { // 2/3 first half off/on + pxTo = xPixels / 2; + } else { // 4/5 second half off/on + pxFrom = xPixels / 2; } } + const uint16_t xOffset = pxlExtra ? _pixelGroupCfg[grp].aoffs : _pixelGroupCfg[grp].boffs; for (uint16_t i = pxFrom; i < pxTo; ++i) { - display[grp]->setPixelColor(i + _pixelGroupCfg[grp].aoffs, color); + display[grp]->setPixelColor(i + xOffset, color); } if (loglevelActiveFor(LOG_LEVEL_INFO)) { addLog(LOG_LEVEL_INFO, - strformat(F("Neo7Dgt: Set group %d extra pixels %d..%d to color 0x%08x"), + strformat(F("Neo7Dgt: Set group %d extra %c pixels %d..%d to color 0x%08x"), grp + 1, - _pixelGroupCfg[grp].aoffs + 1 + pxFrom, - _pixelGroupCfg[grp].aoffs + pxTo, + pxlExtra ? 'a' : 'b', + xOffset + 1 + pxFrom, + xOffset + pxTo, color)); } } @@ -1931,27 +1986,30 @@ void P165_data_struct::writeBufferToDisplay(uint8_t group) { * fetch the pixels to turn on for a digit ************************************************************************************/ segsize_t P165_data_struct::digit2SegmentMap(uint8_t grp, uint8_t digit) { - segsize_t currentBitmap = 0; // clear the current bitmap + segsize_t currentBitmap = 0u; // clear the current bitmap for (byte i = 0; i < 8; i++) { - if (digit & (1UL << i)) { // UL not necessary, but uses less Flash than if you leave it away + if (digit & (1UL << i)) { // UL not necessary, but uses less Flash than if you leave it away currentBitmap |= segments[grp][i]; } } # if P165_DEBUG_DEBUG - const uint8_t pxlDigit = calculateGroupPixels(1, // Count pixels for 1 digit only - _pixelGroupCfg[grp].wpix, - _pixelGroupCfg[grp].hpix, - _pixelGroupCfg[grp].crnr, - _pixelGroupCfg[grp].dotp, - 0); + const uint8_t pxlDigit = min(static_cast(63), + calculateGroupPixels(1, // Count pixels for 1 digit only + _pixelGroupCfg[grp].wpix, + _pixelGroupCfg[grp].hpix, + _pixelGroupCfg[grp].crnr, + _pixelGroupCfg[grp].dotp, + 0)); // Used for debugging addLog(LOG_LEVEL_INFO, strformat(F("digit2SegmentMap: digit: 0x%02x, bits: 0b%s"), digit, - ull2String(bitSetULL(currentBitmap, pxlDigit + 2), 2).substring(1).c_str())); - bitClearULL(currentBitmap, pxlDigit + 2); + ull2String(pxlDigit < 63 ? bitSetULL(currentBitmap, pxlDigit + 1) + : currentBitmap, 2).substring(1).c_str())); + + if (pxlDigit < 63) { bitClearULL(currentBitmap, pxlDigit + 1); } # endif // if P165_DEBUG_DEBUG return currentBitmap; } diff --git a/src/src/PluginStructs/P165_data_struct.h b/src/src/PluginStructs/P165_data_struct.h index 9584ee70c6..da0d086c39 100644 --- a/src/src/PluginStructs/P165_data_struct.h +++ b/src/src/PluginStructs/P165_data_struct.h @@ -13,7 +13,7 @@ # include "../Static/WebStaticData.h" // Javascript and support functions # define P165_DEBUG_INFO 1 // set 1 to enable some extra debug logging -# define P165_DEBUG_DEBUG 1 // set 1 to enable some extra development debug logging +# define P165_DEBUG_DEBUG 0 // set 1 to enable some extra development debug logging # ifdef USES_P073 # define P165_FEATURE_P073 1 // Use P073 shared functions when available @@ -88,22 +88,22 @@ // Config per display group, all 4 PCONFIG_(U)LONG variables used # define P165_GROUP_CFG(N) PCONFIG_ULONG(N) -# define P165_CONFIG_IDX_WPIXELS 0u // 3 bits -# define P165_CONFIG_IDX_HPIXELS 3u // 3 bits -# define P165_CONFIG_IDX_CORNER 6u // 1 bit -# define P165_CONFIG_IDX_DOT 7u // 3 bits -# define P165_CONFIG_IDX_EXTRA 10u // 4 bits -# define P165_CONFIG_IDX_OFFSET 14u // 4 bits -# define P165_CONFIG_IDX_DIGITS 18u // 3 bits -# define P165_CONFIG_IDX_START 21u // 1 bit -# define P165_CONFIG_IDX_DEND 22u // 1 bit -# define P165_CONFIG_IDX_RTLD 23u // 1 bit -# define P165_CONFIG_IDX_SPLTG 24u // 1 bit - -# define P165_GET_CONFIG_WPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS)) -# define P165_GET_CONFIG_HPIXELS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS)) +# define P165_CONFIG_IDX_WPIXELS 0u // 4 bits +# define P165_CONFIG_IDX_HPIXELS 4u // 4 bits +# define P165_CONFIG_IDX_CORNER 8u // 1 bit +# define P165_CONFIG_IDX_DOT 9u // 4 bits +# define P165_CONFIG_IDX_EXTRA 13u // 4 bits +# define P165_CONFIG_IDX_OFFSET 17u // 4 bits +# define P165_CONFIG_IDX_DIGITS 21u // 3 bits +# define P165_CONFIG_IDX_START 24u // 1 bit +# define P165_CONFIG_IDX_DEND 25u // 1 bit +# define P165_CONFIG_IDX_RTLD 26u // 1 bit +# define P165_CONFIG_IDX_SPLTG 27u // 1 bit + +# define P165_GET_CONFIG_WPIXELS(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS)) +# define P165_GET_CONFIG_HPIXELS(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS)) # define P165_GET_CONFIG_CORNER(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_CORNER)) -# define P165_GET_CONFIG_DOT(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT)) +# define P165_GET_CONFIG_DOT(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT)) # define P165_GET_CONFIG_EXTRA(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_EXTRA)) # define P165_GET_CONFIG_OFFSET(D) (get4BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_OFFSET)) # define P165_GET_CONFIG_DIGITS(D) (get3BitFromUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DIGITS)) @@ -112,10 +112,10 @@ # define P165_GET_CONFIG_RTLD(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_RTLD)) # define P165_GET_CONFIG_SPLTG(D) (bitRead(P165_GROUP_CFG(D), P165_CONFIG_IDX_SPLTG)) -# define P165_SET_CONFIG_WPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS, V)) -# define P165_SET_CONFIG_HPIXELS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS, V)) +# define P165_SET_CONFIG_WPIXELS(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_WPIXELS, V)) +# define P165_SET_CONFIG_HPIXELS(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_HPIXELS, V)) # define P165_SET_CONFIG_CORNER(D, V) (bitWrite(P165_GROUP_CFG(D), P165_CONFIG_IDX_CORNER, V)) -# define P165_SET_CONFIG_DOT(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT, V)) +# define P165_SET_CONFIG_DOT(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DOT, V)) # define P165_SET_CONFIG_EXTRA(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_EXTRA, V)) # define P165_SET_CONFIG_OFFSET(D, V) (set4BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_OFFSET, V)) # define P165_SET_CONFIG_DIGITS(D, V) (set3BitToUL(P165_GROUP_CFG(D), P165_CONFIG_IDX_DIGITS, V)) @@ -133,14 +133,22 @@ # define P165_DISP_CLOCK12 4 # define P165_DISP_DATE 5 -# define P165_STRIP_TYPE_RGB 0 -# define P165_STRIP_TYPE_RGBW 1 +# define P165_STRIP_TYPE_RGB 0 +# define P165_STRIP_TYPE_RGBW 1 -# define P165_SEGMENT_DOT_PIXELS 7 // Max: 7 (3 bits) +# define P165_SEGMENT_WIDTH_PIXELS 7 // Max: 15 (4 bits), also needs digit table and javascript changes! +# define P165_SEGMENT_HEIGHT_PIXELS 7 // Max: 15 (4 bits) +# define P165_SEGMENT_DOT_PIXELS 7 // Max: 15 (4 bits) # define P165_SEGMENT_ADDON_PIXELS 12 // Max: 15 (4 bits) +# define P165_SEGMENT_EXTRA_PIXELS 15 // Max: 15 (4 bits) + +# define P165_DIGIT_TABLE_H_INT 17 +# define P165_DIGIT_TABLE_HEIGHT "17" // Used in F() string + +# define P165_TD_SIZE "1.6rem" // For use in the Digit display, size (width & height) of the table TD's // Set typedef before include -typedef uint64_t segsize_t; // largest storage size available, allows for up to 64 pixels per digit +typedef uint64_t segsize_t; // largest storage size available, allows for up to 64 pixels per digit # include # if P165_FEATURE_P073 @@ -151,21 +159,21 @@ typedef uint64_t segsize_t; // largest storage size available, allows for up to struct P165_data_struct : public PluginTaskData_base { private: - struct PixelGroupCfg { // Bit-mapping must match with P165_GET/SET_CONFIG_*() / P165_GROUP_CFG() macros - uint32_t wpix : 3; // widht (1..5) - uint32_t hpix : 3; // height (1..5) - uint32_t crnr : 1; // corners off/on - uint32_t dotp : 3; // dot pixels (0..7) - uint32_t addn : 4; // extra pixels (0..12) - uint32_t offs : 4; // offset before (0..15) - uint32_t dgts : 3; // digits in group (1..4) - uint32_t strt : 1; // start segment 0 = left-top/a, 1 = right-top/b - uint32_t dend : 1; // dot at: 1: end of digit 0: between c/d segments - uint32_t rtld : 1; // right to left display - uint32_t splt : 1; // split g-segment (best enabled > 3 horizontal pixels) - uint32_t unused : 7; - uint32_t aoffs : 16; // Add-on pixels offset (use uin32_t for better memory alignment) - uint32_t unused2 : 16; + struct PixelGroupCfg { // Bit-mapping must match with P165_GET/SET_CONFIG_*() / P165_GROUP_CFG() macros + uint32_t wpix : 4; // widht (1..15) (7) + uint32_t hpix : 4; // height (1..15) (7) + uint32_t crnr : 1; // corners off/on + uint32_t dotp : 4; // dot pixels (0..15) (7) + uint32_t addn : 4; // extra pixels (0..15) (12) + uint32_t offs : 4; // offset before (0..15) (15) + uint32_t dgts : 3; // digits in group (1..7) (4) + uint32_t strt : 1; // start segment 0 = left-top/a, 1 = right-top/b + uint32_t dend : 1; // dot at: 1: end of digit 0: between c/d segments + uint32_t rtld : 1; // right to left display + uint32_t splt : 1; // split g-segment (best enabled > 3 horizontal pixels) + uint32_t unused : 4; + uint32_t aoffs : 16; // Add-on pixels offset (use uin32_t for better memory alignment) + uint32_t boffs : 16; // Before pixels offset }; public: @@ -279,7 +287,8 @@ struct P165_data_struct : public PluginTaskData_base { void setBinaryData(const String& data); bool extraPixelsState(uint8_t grp, uint8_t state, - uint32_t color); + uint32_t color, + bool pxlExtra = true); # if P165_DEBUG_INFO || P165_DEBUG_DEBUG void logBufferContent(String prefix); # endif // if P165_DEBUG_INFO || P165_DEBUG_DEBUG diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index 53f0edf5fe..75e58700aa 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -720,12 +720,12 @@ static const char p165_digit_script[] PROGMEM = { " {c=r?.children[b];if(c?.tagName=='TD'){c.style.width=20;c.style.display=d?'inline-block':'none'}}};" "function sh_row(a,b,c){r=document.getElementById(a).getElementsByTagName('TR')[b];if(r)r.style.display=c?'block':'none'};" "function set_td(a,b,d,e,f){c=document.getElementById(a)?.getElementsByTagName('TR')[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" - "function chWdth(a,b,c,d,e){for(t=b;t☸';else z='☸';" - " for(t=c;t0,12,7,''+a)};" - "function chAddn(a,b,c,d,e){for(t=b;t0&&d,6,0,''+a)};" + " for(t=c;t0,16,9,''+a)};" + "function chAddn(a,b,c,d,e){for(t=b;t0&&d,8,0,''+a)};" "function dgts(a,b){z=0;for(q=0;q content if fill is true, // Next functions: 4th & 5th parameters are used only in 1 function, but to keep the calling logic simple, leave it there function chWdth(value, base, count, max, colr) { // Change width for (t = base; t < base + count; t++) - for (i = 1; i <= 5; i++) + for (i = 1; i <= 7; i++) sh_col('dgtbl' + t, i, i <= value); }; function chHght(value, base, count, max, colr) { // Change height for (t = base; t < base + count; t++) { b = 'dgtbl' + t; // Table - for (i = 1; i < 6; i++) { - sh_row(b, 6 - i, i <= value); - sh_row(b, 6 + i, i <= value); + for (i = 1; i < 8; i++) { + sh_row(b, 8 - i, i <= value); + sh_row(b, 8 + i, i <= value); }; } }; @@ -38,19 +38,19 @@ function chCrnr(checked, base, count, max, colr) { // Change Corner overlap if (colr) z = ''; else z = '☸'; for (t = base; t < base + count; t++) { b = 'dgtbl' + t; // Table - for (i = 0; i < 14; i += 6) { + for (i = 0; i < 18; i += 8) { set_td(b, checked, i, 0, z); // P165_PIXEL_CHARACTER - set_td(b, checked, i, 6, z); + set_td(b, checked, i, 8, z); } } }; function chDecp(value, base, count, max, colr) { // Change Decimal point pixels for (t = base; t < base + count; t++) - set_td('dgtbl' + t, value > 0, 12, 7, '' + value); + set_td('dgtbl' + t, value > 0, 16, 9, '' + value); }; function chAddn(value, base, count, max, colr) { // Change Additional pixels for (t = base; t < base + count; t++) - set_td('dxtbl' + t, value > 0 && max /*&& t === base + count - 1*/, 6, 0, '' + value); + set_td('dxtbl' + t, value > 0 && max, 8, 0, '' + value); }; function dgts(gps, flds) { // Update pixel counts per group and total pixels z = 0; From f0b82befaffac4b5602ac70956238ca36b844187 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 27 Aug 2024 21:15:23 +0200 Subject: [PATCH 206/367] [P173] Add plugin Environment - SHTC3 --- src/_P173_SHTC3.ino | 115 ++++++++++++++++ src/src/CustomBuild/define_plugin_sets.h | 9 ++ src/src/PluginStructs/P173_data_struct.cpp | 144 +++++++++++++++++++++ src/src/PluginStructs/P173_data_struct.h | 63 +++++++++ 4 files changed, 331 insertions(+) create mode 100644 src/_P173_SHTC3.ino create mode 100644 src/src/PluginStructs/P173_data_struct.cpp create mode 100644 src/src/PluginStructs/P173_data_struct.h diff --git a/src/_P173_SHTC3.ino b/src/_P173_SHTC3.ino new file mode 100644 index 0000000000..318eb073d1 --- /dev/null +++ b/src/_P173_SHTC3.ino @@ -0,0 +1,115 @@ +#include "_Plugin_Helper.h" +#ifdef USES_P173 + +// ####################################################################################################### +// ######################### Plugin 173: Environment - SHTC3 Temperature, Humidity ####################### +// ####################################################################################################### + +/** + * 2024-08-27 tonhuisman: Start plugin for SHTC3 I2C Temperature and Humidity sensor + * Using direct I2C communication, based of this library: https://github.com/cdjq/DFRobot_SHTC3 + **/ + +# define PLUGIN_173 +# define PLUGIN_ID_173 173 +# define PLUGIN_NAME_173 "Environment - SHTC3" +# define PLUGIN_VALUENAME1_173 "Temperature" +# define PLUGIN_VALUENAME2_173 "Humidity" + +# include "./src/PluginStructs/P173_data_struct.h" + +boolean Plugin_173(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_173; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TEMP_HUM; + Device[deviceCount].Ports = 0; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 2; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].PluginStats = true; + + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_173); + + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_173)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_173)); + + break; + } + + case PLUGIN_I2C_HAS_ADDRESS: + { + success = P173_I2C_ADDRESS == event->Par1; + + break; + } + + # if FEATURE_I2C_GET_ADDRESS + case PLUGIN_I2C_GET_ADDRESS: + { + event->Par1 = P173_I2C_ADDRESS; + success = true; + break; + } + # endif // if FEATURE_I2C_GET_ADDRESS + + case PLUGIN_WEBFORM_LOAD: + { + addFormTextBox(F("Temperature offset"), F("toffs"), toString(P173_TEMPERATURE_OFFSET, 2), 5); + addUnit(F("°C")); + + addFormCheckBox(F("Read in Low-Power mode"), F("lpmode"), P173_CONFIG_LOW_POWER == 1); + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P173_TEMPERATURE_OFFSET = getFormItemFloat(F("toffs")); + P173_CONFIG_LOW_POWER = isFormItemChecked(F("lpmode")) ? 1 : 0; + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P173_data_struct(P173_TEMPERATURE_OFFSET, P173_CONFIG_LOW_POWER)); + P173_data_struct *P173_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P173_data) && P173_data->init(); + + break; + } + + case PLUGIN_READ: + { + P173_data_struct *P173_data = static_cast(getPluginTaskData(event->TaskIndex)); + + success = (nullptr != P173_data) && P173_data->plugin_read(event); + + break; + } + } + return success; +} + +#endif // USES_P173 diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 8af1f883f4..9c430ee7b9 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1661,6 +1661,9 @@ To create/register a plugin, you have to : #ifndef USES_P170 #define USES_P170 // Input - I2C Liquid level sensor #endif + #if !defined(USES_P173) && defined(ESP32) + #define USES_P173 // Environment - SHTC3 + #endif #endif @@ -1981,6 +1984,9 @@ To create/register a plugin, you have to : #ifndef USES_P169 #define USES_P169 // Environment - AS3935 Lightning Detector #endif + #if !defined(USES_P173) // && defined(ESP32) + #define USES_P173 // Environment - SHTC3 + #endif // Controllers #ifndef USES_C011 @@ -2421,6 +2427,9 @@ To create/register a plugin, you have to : #ifndef USES_P169 #define USES_P169 // Environment - AS3935 Lightning Detector #endif + #ifndef USES_P173 + #define USES_P173 // Environment - SHTC3 + #endif // Controllers #ifndef USES_C015 diff --git a/src/src/PluginStructs/P173_data_struct.cpp b/src/src/PluginStructs/P173_data_struct.cpp new file mode 100644 index 0000000000..f102f6fdd1 --- /dev/null +++ b/src/src/PluginStructs/P173_data_struct.cpp @@ -0,0 +1,144 @@ +#include "../PluginStructs/P173_data_struct.h" + +#ifdef USES_P173 + +# include "../Helpers/CRC_functions.h" + +/************************************************************************** +* Constructor +**************************************************************************/ +P173_data_struct::P173_data_struct(float tempOffset, bool lowPower) : + _tempOffset(tempOffset), _lowPower(lowPower) +{} + +bool P173_data_struct::init() { + wakeup(); + softwareReset(); + delay(1); + + // Read sensor device id + if (checkDeviceID()) { + sleep(); + initialized = true; + } else { + addLog(LOG_LEVEL_ERROR, F("SHTC3: Sensor not recognized.")); + } + + return isInitialized(); +} + +/***************************************************** +* plugin_read +*****************************************************/ +bool P173_data_struct::plugin_read(struct EventStruct *event) { + bool success = false; + + if (isInitialized()) { + // Wake up the sensor + wakeup(); + + // Read the sensor data + uint8_t data[6]{}; + const uint16_t command = _lowPower ? P173_SHTC3_READ_RH_T_LP : P173_SHTC3_READ_RH_T; + + if (!readValue(command, 6, data)) { + addLog(LOG_LEVEL_ERROR, strformat(F("%s: Error reading sensor"), getTaskDeviceName(event->TaskIndex).c_str())); + } else + + // Read succesful, data valid? + if (calc_CRC8(data[0], data[1], data[2]) && calc_CRC8(data[3], data[4], data[5])) { + const float temp = static_cast(((uint16_t)data[0] << 8) | (uint16_t)data[1]); + const float hum = static_cast(((uint16_t)data[3] << 8) | (uint16_t)data[4]); + temperature = -45.0f + 175.0f * temp / 65535.0f; + humidity = 100.0f * hum / 65535.0f; + + if (definitelyLessThan(humidity, 0.0f)) { humidity = 0.0f; } + + if (definitelyGreaterThan(humidity, 100.0f)) { humidity = 100.0f; } + + UserVar.setFloat(event->TaskIndex, 0, temperature + _tempOffset); // Apply offset + UserVar.setFloat(event->TaskIndex, 1, humidity); + + success = true; + errorCount = 0; + + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, strformat(F("%s: Temperature: %s, Humidity: %s"), + getTaskDeviceName(event->TaskIndex).c_str(), + formatUserVarNoCheck(event, 0).c_str(), + formatUserVarNoCheck(event, 1).c_str())); + } + } else { + addLog(LOG_LEVEL_ERROR, concat(F("SHTC3: READ CRC Error, data: 0x"), formatToHex_array(data, 6))); + errorCount++; + + if (errorCount > P173_MAX_ERRORCOUNT) { + softwareReset(); + delay(1); + addLog(LOG_LEVEL_ERROR, F("SHTC3: READ Error count reached, sensor reset.")); + } + } + + if (!success) { + UserVar.setFloat(event->TaskIndex, 0, NAN); // Read error or I/O error + UserVar.setFloat(event->TaskIndex, 1, NAN); + } + + // Send sensor to sleep mode for least power consumption + sleep(); + } + + return success; +} + +/******************************** + * Low level functions + *******************************/ +void P173_data_struct::softwareReset() { + writeCommand(P173_SHTC3_RESET); + delayMicroseconds(173); +} + +void P173_data_struct::wakeup() { + writeCommand(P173_SHTC3_WAKEUP); + delayMicroseconds(500); +} + +void P173_data_struct::sleep() { + writeCommand(P173_SHTC3_SLEEP); + delayMicroseconds(230); +} + +bool P173_data_struct::checkDeviceID() { + uint8_t idArray[3]; + + readValue(P173_SHTC3_READ_DEVICEID, 3, idArray); + + if (calc_CRC8(idArray[0], idArray[1], idArray[2])) { + const uint16_t id = (idArray[0] << 8) | idArray[1]; + + if ((id & 0x807) == 0x807) { + return true; + } + } + return false; +} + +void P173_data_struct::writeCommand(uint16_t command) { + I2C_write16(_address, command); +} + +bool P173_data_struct::readValue(uint16_t command, uint8_t readnum, uint8_t *readArray) { + I2C_write16(_address, command); + delayMicroseconds(12000); + + if (Wire.requestFrom(_address, readnum) == readnum) { + for (uint8_t i = 0; i < readnum; ++i) { + readArray[i] = Wire.read(); + } + return true; + } + return false; +} + +#endif // ifdef USES_P173 diff --git a/src/src/PluginStructs/P173_data_struct.h b/src/src/PluginStructs/P173_data_struct.h new file mode 100644 index 0000000000..63980962e0 --- /dev/null +++ b/src/src/PluginStructs/P173_data_struct.h @@ -0,0 +1,63 @@ +#ifndef PLUGINSTRUCTS_P173_DATA_STRUCT_H +#define PLUGINSTRUCTS_P173_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P173 + +# define P173_I2C_ADDRESS 0x70 + +# define P173_CONFIG_LOW_POWER PCONFIG(0) +# define P173_TEMPERATURE_OFFSET PCONFIG_FLOAT(0) + +# define P173_SHTC3_READ_DEVICEID 0xEFC8 +# define P173_SHTC3_RESET 0x805D +# define P173_SHTC3_WAKEUP 0x3517 +# define P173_SHTC3_SLEEP 0xB098 +# define P173_SHTC3_READ_RH_T 0x7866 +# define P173_SHTC3_READ_RH_T_LP 0x609C + +// FIXME Not sure if we should support Clock stretching mode, commands kept for documentation +// # define P173_SHTC3_READ_RH_T_CLKSTR 0x7CA2 +// # define P173_SHTC3_READ_RH_T_CLKSTR_LP 0x6458 + +# define P173_MAX_ERRORCOUNT 5 + +struct P173_data_struct : public PluginTaskData_base { +public: + + P173_data_struct(float tempOffset, + bool lowPower); + + P173_data_struct() = delete; + virtual ~P173_data_struct() {} + + bool init(); + + bool plugin_read(struct EventStruct *event); + bool isInitialized() const { + return initialized; + } + +private: + + void softwareReset(); + void wakeup(); + void sleep(); + bool checkDeviceID(); + void writeCommand(uint16_t command); + bool readValue(uint16_t command, + uint8_t readnum, + uint8_t *readArray); + + uint8_t _address = P173_I2C_ADDRESS; + float _tempOffset = 0.0f; + bool _lowPower = false; + + float temperature = 0.0f; + float humidity = 0.0f; + int errorCount = 0; + bool initialized = false; +}; + +#endif // ifdef USES_P173 +#endif // ifndef PLUGINSTRUCTS_P173_DATA_STRUCT_H From 830f2cf11ac429ec80ef2a1a0e5f80d4c9b5b7e1 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Tue, 27 Aug 2024 21:16:21 +0200 Subject: [PATCH 207/367] [I2C] Add functions I2C_write16() and I2C_write16_LE() --- src/src/Helpers/I2C_access.cpp | 17 +++++++++++++++++ src/src/Helpers/I2C_access.h | 12 ++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/src/Helpers/I2C_access.cpp b/src/src/Helpers/I2C_access.cpp index ef1cecbff6..89d4be9fac 100644 --- a/src/src/Helpers/I2C_access.cpp +++ b/src/src/Helpers/I2C_access.cpp @@ -189,6 +189,23 @@ bool I2C_write8_reg(uint8_t i2caddr, uint8_t reg, uint8_t value) { return Wire.endTransmission() == 0; } +// **************************************************************************/ +// Writes an 16 bit value over I2C +// **************************************************************************/ +bool I2C_write16(uint8_t i2caddr, uint16_t value) { + Wire.beginTransmission(i2caddr); + Wire.write((uint8_t)(value >> 8)); + Wire.write((uint8_t)value); + return Wire.endTransmission() == 0; +} + +// **************************************************************************/ +// Writes an 16 bit LE value over I2C +// **************************************************************************/ +bool I2C_write16_LE(uint8_t i2caddr, uint16_t value) { + return I2C_write16(i2caddr, (value << 8) | (value >> 8)); +} + // **************************************************************************/ // Writes an 16 bit value over I2C to a register // **************************************************************************/ diff --git a/src/src/Helpers/I2C_access.h b/src/src/Helpers/I2C_access.h index f115eb0d34..bc7ea5a42e 100644 --- a/src/src/Helpers/I2C_access.h +++ b/src/src/Helpers/I2C_access.h @@ -41,6 +41,18 @@ bool I2C_write8_reg(uint8_t i2caddr, uint8_t reg, uint8_t value); +// **************************************************************************/ +// Writes an 16 bit value over I2C +// **************************************************************************/ +bool I2C_write16(uint8_t i2caddr, + uint16_t value); + +// **************************************************************************/ +// Writes an 16 bit LE value over I2C +// **************************************************************************/ +bool I2C_write16_LE(uint8_t i2caddr, + uint16_t value); + // **************************************************************************/ // Writes an 16 bit value over I2C to a register // **************************************************************************/ From f2db069e39193e8ce006e4f7baffd1f17f81c4fc Mon Sep 17 00:00:00 2001 From: Hans-Rune Bue Date: Tue, 27 Aug 2024 21:24:11 +0200 Subject: [PATCH 208/367] Initial ESPeasy tools add --- tools/ESPEasy-tools.md | 158 +++++++++++++++++++++++++++++++++++ tools/espeasybackup | 183 +++++++++++++++++++++++++++++++++++++++++ tools/espeasyconfig | 71 ++++++++++++++++ tools/espeasyupdate | 53 ++++++++++++ 4 files changed, 465 insertions(+) create mode 100644 tools/ESPEasy-tools.md create mode 100755 tools/espeasybackup create mode 100755 tools/espeasyconfig create mode 100755 tools/espeasyupdate diff --git a/tools/ESPEasy-tools.md b/tools/ESPEasy-tools.md new file mode 100644 index 0000000000..54602e24c5 --- /dev/null +++ b/tools/ESPEasy-tools.md @@ -0,0 +1,158 @@ +|Latest Nightly | Build Status | Downloads | Docs | Patreon | Ko-Fi | PayPal | +|-------|-------|-------|-------|-------|-------|-------| +| [![GitHub version](https://img.shields.io/github/release/letscontrolit/ESPEasy/all.svg)](https://github.com/letscontrolit/ESPEasy/releases/latest) | ![Build status](https://github.com/letscontrolit/ESPEasy/actions/workflows/build.yml/badge.svg) | [![Downloads](https://img.shields.io/github/downloads/letscontrolit/ESPEasy/total.svg)](https://github.com/letscontrolit/ESPEasy/releases) | [![Documentation Status](https://readthedocs.org/projects/espeasy/badge/?version=latest)](https://espeasy.readthedocs.io/en/latest/?badge=latest) | [![donate](https://img.shields.io/badge/donate-Patreon-blue.svg)](https://www.patreon.com/GrovkillenTDer) | [![donate](https://img.shields.io/badge/donate-KoFi-blue.svg)](https://ko-fi.com/grovkillentder) | [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://www.paypal.me/espeasy) | + +For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above. + +# ESPEasy (development branch) + + +Introduction https://espeasy.readthedocs.io/en/latest/ (and, mostly outdated, wiki: https://www.letscontrolit.com/wiki/index.php/ESPEasy#Introduction) + +**MEGA** +This is the development branch of ESPEasy. All new features go into this branch, and it has become the current stable branch. If you want to do a bugfix, do it on this branch. + + +Check here to learn how to use this branch and help us improving ESPEasy: [Starter guide for (local) development on ESPEasy](https://espeasy.readthedocs.io/en/latest/Participate/PlatformIO.html#starter-guide-for-local-development-on-espeasy) + +## Web based flasher (experimental) + +To make it easier to get started, one may flash a build directly to the ESP from your browser. +Currently only Chrome and Edge are supported. + +See [this flash page](https://td-er.nl/ESPEasy/) to try the new web flash feature. + +The web flasher is using [ESP Web Tools](https://esphome.github.io/esp-web-tools/) made by the people behind ESPHome and Home Assistant. + + +## Binary releases + +On demand, controlled by the repo owner, our build-bot will build a new binary release: https://github.com/letscontrolit/ESPEasy/releases + +The releases are named something like 'mega-20220626' (last number is the build date) + +Depending on your needs, we release different types of files: + +The name is built up from a few key parts: + +ESPEasy_mega\_*[releasedate]*\_*[build-type]*\_*[opt-arduino-library]*\_*[hardware-type]*\_*[flash-size][filesystem-size]*\_*[opt-build-features]*.bin + +*[build-type]* can be any of: +Build type | Description | included plugins | +-------------|-------------------------------------------|----------------------------------| +climate | All plugins related to climate measurement| Stable + Climate | +custom | Custom predefined set/Defined in Custom.h | Specific | +normal | Standard plugins | Stable | +collection_A | Normal + plugin collection A | Stable + Collection base + set A | +collection_B | Normal + plugin collection B | Stable + Collection base + set B | +collection_C | Normal + plugin collection C | Stable + Collection base + set C | +collection_D | Normal + plugin collection D | Stable + Collection base + set D | +collection_E | Normal + plugin collection E | Stable + Collection base + set E | +collection_F | Normal + plugin collection F | Stable + Collection base + set F | +collection_G | Normal + plugin collection G | Stable + Collection base + set G | +max | All available plugins | All available | +energy | All plugins related to energy measurement | Stable + Energy measurement | +display | All plugins related to displays | Stable + Displays | +neopixel | All plugins related to neopixel | Stable + Neopixel | +hard | hardware specific builds | Minimal | +minimal | minimal plugins for specific use-cases | Switch and Controller | +spec_* | specialized technical builds | Not intended for regular use | +IRext | Infra-red hardware specific | Sending and receiving IR cmd | +safeboot | (Experimental) `safeboot` build to enable
    most/all plugins on 4MB Flash boards | None | + + +*[opt-arduino-library]* (optional) can be any of: +Arduino library | Description | +----------------|------------------------------------| +alt_wifi | Alternative WiFi configuration | +beta | Arduino Beta release | +sdk3 | Arduino SDK v.3 | +core_274 | Arduino Core 2.7.4 release | +core_302 | Arduino Core 3.0.2 release | +core_274_sdk3 | Arduino Core 2.7.4 SDK v.3 release | + + +*[hardware-type]* can be any of: +Hardware type | Description | +-----------------|---------------------------------------------| +ESP8266 | Espressif ESP8266/ESP8285 generic boards | +WROOM02 | Espressif ESP8266 WRoom02 boards | +ESP32 | Espressif ESP32 generic boards | +ESP32solo1 | Espressif ESP32-Solo1 generic boards | +ESP32s2 | Espressif ESP32-S2 generic boards | +ESP32c3 | Espressif ESP32-C3 generic boards | +ESP32s3 | Espressif ESP32-S3 generic boards | +ESP32c2 | Espressif ESP32-C2 generic boards | +ESP32c6 | Espressif ESP32-C6 generic boards | +ESP32-wrover-kit | Espressif ESP32 wrover-kit boards | +SONOFF | Sonoff hardware specific | +other_POW | Switch with power measurement | +Shelly_1 | Shelly 1 switch | +Shelly_PLUG_S | Shelly plug S switch with power measurement | +Ventus | Ventus W266 weather station | +LCtech_relay | LC-tech serial switch | + +N.B. Starting 2022/07/23, 1M ESP8266 builds can also be used on ESP8285 units and thus there is no longer a specific ESP8285 build anymore. + + +*[flash-size]* can be any of: +Flash size | Description | +-----------|-----------------------------| +1M | 1 MB with 128 kB filesystem | +2M | 2 MB with 128 kB filesystem | +2M256 | 2 MB with 256 kB filesystem | +2M320k | 2 MB with 320 kB filesystem | +4M1M | 4 MB with 1 MB filesystem | +4M2M | 4 MB with 2 MB filesystem | +16M | 16 MB with 14 MB filesystem | +4M316k | 4 MB with 316 kB filesystem | +8M1M | 8 MB with 1 MB filesystem | +16M1M | 16 MB with 1 MB filesystem | +16M8M | 16 MB with 8 MB filesystem | + +N.B. Starting with release 2023/12/25, All ESP32 LittleFS builds use IDF 5.1, to support newer ESP32 chips like ESP32-C2 and ESP32-C6. Other SPIFFS based ESP32 builds will be migrated to LittleFS as SPIFFS is no longer available in IDF 5 and later. A migration plan will be made available in 2024. + +*[opt-build-features]* can be any of: +Build features | Description | +----------------|-----------------------------------------------------------------------------------------------------------| +LittleFS | Use LittleFS instead of SPIFFS filesystem (SPIFFS is unstable \> 2 MB, and no longer supported in IDF \> 5) | +VCC | Analog input configured to measure VCC voltage (ESP8266 only) | +OTA | Arduino OTA (Over The Air) update feature enabled | +Domoticz | Only Domoticz controllers (HTTP) and plugins included | +Domoticz_MQTT | Only Domoticz controllers (MQTT) and plugins included | +FHEM_HA | Only FHEM/OpenHAB/Home Assistant (MQTT) controllers and plugins included | +ETH | Ethernet interface enabled (ESP32-classic only) | +OPI_PSRAM | Specific configuration to enable PSRAM detection, ESP32-S3 only | +CDC | Support USBCDC/HWCDC-serial console on ESP32-C3, ESP32-S2, ESP32-S3 and ESP32-C6 | +noOTA/NO_OTA | Does not support OTA (Over The Air-updating of the firmware) Use [the flash page](https://td-er.nl/ESPEasy/) or ESPTool via USB Serial | + +Some example firmware names: +Firmware name | Hardware | Included plugins | +------------------------------------------------------------------|---------------------------------------|----------------------------------| +ESPEasy_mega-20230822_normal_ESP8266_1M.bin | ESP8266/ESP8285 with 1MB flash | Stable | +ESPEasy_mega-20230822_normal_ESP8266_4M1M.bin | ESP8266 with 4MB flash | Stable | +ESPEasy_mega-20230822_collection_A_ESP8266_4M1M.bin | ESP8266 with 4MB flash | Stable + Collection base + set A | +ESPEasy_mega-20230822_normal_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable | +ESPEasy_mega-20230822_collection_A_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable + Collection base + set A | +ESPEasy_mega-20230822_collection_B_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable + Collection base + set B | +ESPEasy_mega-20230822_max_ESP32s3_8M1M_LittleFS_CDC.bin | ESP32-S3 with 8MB flash, CDC-serial | All available plugins | +ESPEasy_mega-20230822_max_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC.bin | ESP32-S3 8MB flash, PSRAM, CDC-serial | All available plugins | +ESPEasy_mega-20230822_max_ESP32_16M1M.bin | ESP32 with 16MB flash | All available plugins | +ESPEasy_mega-20230822_max_ESP32_16M8M_LittleFS.bin | ESP32 with 16MB flash | All available plugins | + +The binary files for the different ESP32 variants (S2, C3, S3, C2, C6, 'Classic') are available in separate archives. + +To see what plugins are included in which collection set, you can find that on the [ESPEasy Plugin overview page](https://espeasy.readthedocs.io/en/latest/Plugin/_Plugin.html) + +## Documentation & more info + +Our new, in-depth documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/). Automatically built, so always up-to-date according to the contributed contents. The old Wiki documentation can be found at [letscontrolit.com/wiki](https://www.letscontrolit.com/wiki/index.php?title=ESPEasy). + +Additional details and discussion are on the "Experimental" section of the forum: https://www.letscontrolit.com/forum/viewforum.php?f=18 + +[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/letscontrolit/ESPEasy) + + +## Icons used + +Icons on courtesy of [ICONS8](https://icons8.com/). diff --git a/tools/espeasybackup b/tools/espeasybackup new file mode 100755 index 0000000000..d781935f00 --- /dev/null +++ b/tools/espeasybackup @@ -0,0 +1,183 @@ +#!/bin/bash +PROG=$( basename $0 ) +DATETIME=$( date "+%Y%m%d-%H%M%S-" ) +PREREQS="curl jq" +MAXTIME=30 +JSONTIME=6 +HWPROVIDERRE="5c:cf:7f:|18:fe:34:|60:01:94:|a0:20:a6:|b4:e6:2d:|68:c6:3a:" + +IPLIST=() + +function dbgprint { + [[ -n "${DBGPRINT}" ]] && echo "$PROG debug: $*" >&2 +} + +function peerlist { + local H="$1" + local O="${DATETIME}${H}.json" + + if [[ -s "${O}" ]] + then + dbgprint "PROG: Reading json file from $O ..." + else + dbgprint "PROG: Downloading json file from $H ..." + curl -s -m ${JSONTIME} "http://${H}/json" > "${O}" + fi + if [[ -s "${O}" ]] + then + jq '.nodes[]|{ IPaddr: .ip}' "${O}" | tr -d '"' | awk '{ if ( $1 == "IPaddr:" ) print $2 }' + else + rm "${O}" + echo "$PROG: Warning: Empty response from http://${H}/json" >&2 + fi +} + +function collectip { + local H="$1" + local IP I NIDX + for IP in $( peerlist ${H} ) + do + NIDX=${#IPLIST[*]} + for ((I=0; I < ${NIDX}; I++)) + do + [[ ${IPLIST[$I]} == ${IP} ]] && break + done + # if we made it to end of list without matches + if [[ ${I} == ${NIDX} ]] + then + IPLIST[$NIDX]="${IP}" + dbgprint "Found new $IP (index $NIDX ) ..." + fi + done +} + + +if [[ "$1" == "-h" ]] || [[ -z "$1" ]] +then + echo "Usage: $PROG [-a][-z zipfilefullpath|-Z] IPorHostname ..." >&2 + exit 1 +fi + +# +# put CURLARGS='-u username:password' in ~/.espeasy , then chmod 600 ~/.espeasy +# +if [[ -r "${HOME}/.espeasy" ]] +then + dbgprint "PROG: sourcing ${HOME}/.espeasy" + source "${HOME}/.espeasy" +else + echo "$PROG: Cannot read ${HOME}/.espeasy" >&2 + exit 1 +fi + + +if [[ "$1" == "-a" ]] +then + shift + ARPADDR=$( /usr/sbin/arp -an | awk "/${HWPROVIDERRE}/ { print \$2 }" | tr -d '()' | tr '\n' ' ' ) + dbgprint "PROG: arp address list: $ARPADDR" +fi + +if [[ "$1" == "-z" ]] +then + ZIP="$2" ; shift ; shift + DATETIME="" + PREPREQS="${PREREQS} zip" + mkdir "/tmp/${PROG}-$$" && cd "/tmp/${PROG}-$$" +elif [[ "$1" == "-Z" ]] +then + shift + ZIP="/tmp/config-$(hostname -s)-$( date +%F )" + DATETIME="" + PREPREQS="${PREREQS} zip" + mkdir "/tmp/${PROG}-$$" && cd "/tmp/${PROG}-$$" +fi + +[[ -n "${MAILTO}" ]] && PREPREQS="${PREREQS} mail" + +for CMD in ${PREREQS} +do + type $CMD > /dev/null 2>&1 && continue + echo "$PROG: Please install all prereqs: ${PREREQS}" + exit 2 +done + + +if [[ $# -gt 1 ]] || [[ -n "${ARPADDR}" ]] +then + echo "$PROG: Initial list $* $ARPADDR ..." + for H in $* ${ARPADDR} + do + collectip "${H}" + done + + echo "$PROG: Secondary list: ${IPLIST[*]} ..." + for H in ${IPLIST} + do + collectip "${H}" + done + +else + collectip "$1" + IPLIST=( $1 ) +fi + +echo "PROG: Backup list has ${#IPLIST[*]} nodes: ${IPLIST[*]} " + +for H in ${IPLIST[*]} +do + O="${DATETIME}${H}.json" + + if [[ ! -s "${O}" ]] + then + dbgprint "PROG: Downloading missing json file from $H ..." + curl -s -m ${JSONTIME} "http://${H}/json" > "${O}" + fi + + NAME=$( jq '.WiFi.Hostname' "${O}" | tr -d '"') + NAME="${NAME:-$H}" + for F in config.dat security.dat notification.dat rules1.txt rules2.txt rules3.txt rules4.txt esp.css + do + O="${DATETIME}${NAME}-${F}" + echo -n "$PROG: Backup $H $F --> ${O} ..." + curl -s -m ${MAXTIME} ${CURLARGS} "http://${H}/${F}" > "${O}" + if [[ -s "${O}" ]] + then + if grep -q "URI: /${F}" "${O}" + then + echo "Header only - removed" + rm "${O}" + else + echo " OK" + fi + else + echo "empty - removed" + rm "${O}" + fi + done + # + # We're done with this IP address. Rename to hostname if relevant ... + # + [[ ${NAME} != ${H} ]] && mv "${DATETIME}${H}.json" "${DATETIME}${NAME}.json" +done + +if [[ -n "${ZIP}" ]] +then + echo "$PROG: Sending ZIP file $ZIP to $MAILTO ..." + dbgprint "PROG: Creating zip-file ${ZIP}.zip from ${PWD}" + zip -rq9 "${ZIP}" . + NFILES=$( ls -1 | wc -l ) + cd "${HOME}" + rm -rf "/tmp/${PROG}-$$" + if [[ -n "${MAILTO}" ]] + then + dbgprint "Email to ${MAILTO}" + SUB="$PROG backup from $( hostname -s ) on $( date +%F ) (${#IPLIST[*]} nodes, ${NFILES} files)" + #mpack -s "${SUB}" "${ZIP}.zip" ${MAILTO} + echo -e "Backup file attached from ${#IPLIST[*]} nodes, total of ${NFILES} files\n\n" | \ + mail -s "${SUB}" --content-type=application/zip -A "${ZIP}.zip" ${MAILTO} + fi +fi + +# :tabSize=4:indentSize=4:noTabs=true +# vim:ts=4:sw=4 diff --git a/tools/espeasyconfig b/tools/espeasyconfig new file mode 100755 index 0000000000..d58f4b8e49 --- /dev/null +++ b/tools/espeasyconfig @@ -0,0 +1,71 @@ +#!/bin/bash +PROG=$( basename $0 .sh ) +TMP="/tmp/${PROG}-$$/" + +[[ "$1" == "-r" ]] && REBOOTESP="Y" && shift +H="$1"; shift +FILE="$1" + +if [[ $# -lt 1 ]] || [[ ! -r "${FILE}" ]] +then + echo "Usage: $PROG [-r] NameOrIP file [ file ... ]" >&2 + exit 2 +fi + +# +# put CURLARGS='-u username:password' in ~/.espeasy +# +if [[ -r "${HOME}/.espeasy" ]] +then + source "${HOME}/.espeasy" +else + echo "$PROG: Cannot read ${HOME}/.espeasy" >&2 + exit 1 +fi + +mkdir "${TMP}" || exit 2 + +for FILE in $* +do + for NAME in notification.dat security.dat config.dat rules1.txt rules2.txt rules3.txt rules4.txt esp.css + do + if [[ ${FILE} =~ "${NAME}" ]] + then + MATCH="${NAME}" + echo -n "$PROG: File name ${NAME}: " + cp -v "${FILE}" "${TMP}/${MATCH}" + break; + fi + done + [[ -z "${MATCH}" ]] && echo "$PROG: Cannot determine name of $FILE ... Skipping" +done + +cd "${TMP}" || exit 2 + +for FILE in * +do + echo -n "Upload $FILE: " + if curl ${CURLARGS} --progress-bar -X POST -F "name=@${FILE}" -F "type=file" "http://${H}/upload" | grep -q "Upload OK" + then + echo "$PROG: $FILE upload OK " + else + echo "$PROG: $FILE upload FAILED" >&2 + fi +done +if [[ -n "${REBOOTESP}" ]] +then + echo -n "Reboot: " + if curl ${CURLARGS} --progress-bar "http://${H}/?cmd=reboot" | grep -q '>OK' + then + echo "$PROG: Reboot OK " + else + echo "$PROG: Reboot FAILED" >&2 + fi +fi +cd +#echo "$PROG: Please do a manual rm -rf $TMP" + +rm -rf "${TMP}" + +# vim:ts=4:sw=4 + diff --git a/tools/espeasyupdate b/tools/espeasyupdate new file mode 100755 index 0000000000..55824a5d0e --- /dev/null +++ b/tools/espeasyupdate @@ -0,0 +1,53 @@ +#!/bin/bash +PROG=$( basename $0 .sh ) + +[[ "$1" == "-f" ]] && FORCEUPDATE=1 && shift +[[ "$1" == "-n" ]] && NOUPDATE=1 && shift +[[ "$1" == "-f" ]] && FORCEUPDATE=1 && shift + +FILE="$1" +FILEFW=$( echo ${FILE%.bi*} | sed 's/_20[[:digit:]]*//' ) +FILEFW=$( basename ${FILEFW} ) + +if [[ $# -lt 1 ]] || [[ ! -r "${FILE}" ]] +then + echo "Usage: $PROG [-n|-f] filename NameOrIP [ NameOrIP ... ]" >&2 + exit 2 +fi +shift + +# +# put CURLARGS='-u username:password' in ~/.espeasy - file readable only by user +# +if [[ -r "${HOME}/.espeasy" ]] +then + source "${HOME}/.espeasy" +fi + +for H in $* +do + echo "$PROG: Updating $H ..." + VER=$( curl -s "http://${H}/json"| jq -r '.System."Git Build"' ) + if [[ "${VER#Custom}" != "${VER}" ]] + then + if strings "${FILE}" | grep -q "^${VER}" + then + echo "$PROG: ${H} already has '${VER}'" + [ -z "${FORCEUPDATE}" ] && continue + fi + fi + DEVFW=$( curl -s "http://${H}/json"| jq -r '.System."Binary Filename"' ) + [[ -n "${DEVFW}" ]] && DEVFW=$( sed 's/_20[[:digit:]]*//' <<< ${DEVFW} ) + if [[ "${DEVFW}" != "${FILEFW}" ]] + then + echo "$PROG: Firmware on file (${FILEFW}) different from device (${DEVFW}). Use -f to force update." + [ -z "${FORCEUPDATE}" ] && continue + fi + if [ -n "${NOUPDATE}" ] + then + echo "curl ${CURLARGS} -X POST -F \"name=@${FILE}\" -F \"value=Update\" \"http://${H}/update\"" + continue + fi + curl ${CURLARGS} -X POST -F "name=@${FILE}" -F "value=Update" "http://${H}/update" +done +# vim:ts=4:sw=4 From 17a6add596e7bcf7ac9eb8bba0640cb915c36ed7 Mon Sep 17 00:00:00 2001 From: Hans-Rune Bue Date: Tue, 27 Aug 2024 21:28:41 +0200 Subject: [PATCH 209/367] ESPeasy tools readme --- tools/ESPEasy-tools.md | 300 ++++++++++++++++++++--------------------- 1 file changed, 146 insertions(+), 154 deletions(-) diff --git a/tools/ESPEasy-tools.md b/tools/ESPEasy-tools.md index 54602e24c5..49d72420fa 100644 --- a/tools/ESPEasy-tools.md +++ b/tools/ESPEasy-tools.md @@ -1,158 +1,150 @@ -|Latest Nightly | Build Status | Downloads | Docs | Patreon | Ko-Fi | PayPal | -|-------|-------|-------|-------|-------|-------|-------| -| [![GitHub version](https://img.shields.io/github/release/letscontrolit/ESPEasy/all.svg)](https://github.com/letscontrolit/ESPEasy/releases/latest) | ![Build status](https://github.com/letscontrolit/ESPEasy/actions/workflows/build.yml/badge.svg) | [![Downloads](https://img.shields.io/github/downloads/letscontrolit/ESPEasy/total.svg)](https://github.com/letscontrolit/ESPEasy/releases) | [![Documentation Status](https://readthedocs.org/projects/espeasy/badge/?version=latest)](https://espeasy.readthedocs.io/en/latest/?badge=latest) | [![donate](https://img.shields.io/badge/donate-Patreon-blue.svg)](https://www.patreon.com/GrovkillenTDer) | [![donate](https://img.shields.io/badge/donate-KoFi-blue.svg)](https://ko-fi.com/grovkillentder) | [![donate](https://img.shields.io/badge/donate-PayPal-blue.svg)](https://www.paypal.me/espeasy) | -For ways to *support* us, see [this announcement on the forum](https://www.letscontrolit.com/forum/viewtopic.php?f=14&t=5787), or have a look at the [Patreon](https://www.patreon.com/GrovkillenTDer), [Ko-Fi](https://ko-fi.com/grovkillentder) or [PayPal](https://www.paypal.me/espeasy) links above. +# ESPEasy tools -# ESPEasy (development branch) - - -Introduction https://espeasy.readthedocs.io/en/latest/ (and, mostly outdated, wiki: https://www.letscontrolit.com/wiki/index.php/ESPEasy#Introduction) - -**MEGA** -This is the development branch of ESPEasy. All new features go into this branch, and it has become the current stable branch. If you want to do a bugfix, do it on this branch. +This is a set of simple but powerful command line tools for scripting updates, and for backup and restore of configuration settings and ESPeasy rule files +Be careful not to use these tools in a multiuser environment as the login password may display with something like `ps f -ef` -Check here to learn how to use this branch and help us improving ESPEasy: [Starter guide for (local) development on ESPEasy](https://espeasy.readthedocs.io/en/latest/Participate/PlatformIO.html#starter-guide-for-local-development-on-espeasy) - -## Web based flasher (experimental) - -To make it easier to get started, one may flash a build directly to the ESP from your browser. -Currently only Chrome and Edge are supported. - -See [this flash page](https://td-er.nl/ESPEasy/) to try the new web flash feature. - -The web flasher is using [ESP Web Tools](https://esphome.github.io/esp-web-tools/) made by the people behind ESPHome and Home Assistant. - - -## Binary releases - -On demand, controlled by the repo owner, our build-bot will build a new binary release: https://github.com/letscontrolit/ESPEasy/releases - -The releases are named something like 'mega-20220626' (last number is the build date) - -Depending on your needs, we release different types of files: - -The name is built up from a few key parts: - -ESPEasy_mega\_*[releasedate]*\_*[build-type]*\_*[opt-arduino-library]*\_*[hardware-type]*\_*[flash-size][filesystem-size]*\_*[opt-build-features]*.bin - -*[build-type]* can be any of: -Build type | Description | included plugins | --------------|-------------------------------------------|----------------------------------| -climate | All plugins related to climate measurement| Stable + Climate | -custom | Custom predefined set/Defined in Custom.h | Specific | -normal | Standard plugins | Stable | -collection_A | Normal + plugin collection A | Stable + Collection base + set A | -collection_B | Normal + plugin collection B | Stable + Collection base + set B | -collection_C | Normal + plugin collection C | Stable + Collection base + set C | -collection_D | Normal + plugin collection D | Stable + Collection base + set D | -collection_E | Normal + plugin collection E | Stable + Collection base + set E | -collection_F | Normal + plugin collection F | Stable + Collection base + set F | -collection_G | Normal + plugin collection G | Stable + Collection base + set G | -max | All available plugins | All available | -energy | All plugins related to energy measurement | Stable + Energy measurement | -display | All plugins related to displays | Stable + Displays | -neopixel | All plugins related to neopixel | Stable + Neopixel | -hard | hardware specific builds | Minimal | -minimal | minimal plugins for specific use-cases | Switch and Controller | -spec_* | specialized technical builds | Not intended for regular use | -IRext | Infra-red hardware specific | Sending and receiving IR cmd | -safeboot | (Experimental) `safeboot` build to enable
    most/all plugins on 4MB Flash boards | None | - - -*[opt-arduino-library]* (optional) can be any of: -Arduino library | Description | -----------------|------------------------------------| -alt_wifi | Alternative WiFi configuration | -beta | Arduino Beta release | -sdk3 | Arduino SDK v.3 | -core_274 | Arduino Core 2.7.4 release | -core_302 | Arduino Core 3.0.2 release | -core_274_sdk3 | Arduino Core 2.7.4 SDK v.3 release | - - -*[hardware-type]* can be any of: -Hardware type | Description | ------------------|---------------------------------------------| -ESP8266 | Espressif ESP8266/ESP8285 generic boards | -WROOM02 | Espressif ESP8266 WRoom02 boards | -ESP32 | Espressif ESP32 generic boards | -ESP32solo1 | Espressif ESP32-Solo1 generic boards | -ESP32s2 | Espressif ESP32-S2 generic boards | -ESP32c3 | Espressif ESP32-C3 generic boards | -ESP32s3 | Espressif ESP32-S3 generic boards | -ESP32c2 | Espressif ESP32-C2 generic boards | -ESP32c6 | Espressif ESP32-C6 generic boards | -ESP32-wrover-kit | Espressif ESP32 wrover-kit boards | -SONOFF | Sonoff hardware specific | -other_POW | Switch with power measurement | -Shelly_1 | Shelly 1 switch | -Shelly_PLUG_S | Shelly plug S switch with power measurement | -Ventus | Ventus W266 weather station | -LCtech_relay | LC-tech serial switch | - -N.B. Starting 2022/07/23, 1M ESP8266 builds can also be used on ESP8285 units and thus there is no longer a specific ESP8285 build anymore. - - -*[flash-size]* can be any of: -Flash size | Description | ------------|-----------------------------| -1M | 1 MB with 128 kB filesystem | -2M | 2 MB with 128 kB filesystem | -2M256 | 2 MB with 256 kB filesystem | -2M320k | 2 MB with 320 kB filesystem | -4M1M | 4 MB with 1 MB filesystem | -4M2M | 4 MB with 2 MB filesystem | -16M | 16 MB with 14 MB filesystem | -4M316k | 4 MB with 316 kB filesystem | -8M1M | 8 MB with 1 MB filesystem | -16M1M | 16 MB with 1 MB filesystem | -16M8M | 16 MB with 8 MB filesystem | - -N.B. Starting with release 2023/12/25, All ESP32 LittleFS builds use IDF 5.1, to support newer ESP32 chips like ESP32-C2 and ESP32-C6. Other SPIFFS based ESP32 builds will be migrated to LittleFS as SPIFFS is no longer available in IDF 5 and later. A migration plan will be made available in 2024. - -*[opt-build-features]* can be any of: -Build features | Description | -----------------|-----------------------------------------------------------------------------------------------------------| -LittleFS | Use LittleFS instead of SPIFFS filesystem (SPIFFS is unstable \> 2 MB, and no longer supported in IDF \> 5) | -VCC | Analog input configured to measure VCC voltage (ESP8266 only) | -OTA | Arduino OTA (Over The Air) update feature enabled | -Domoticz | Only Domoticz controllers (HTTP) and plugins included | -Domoticz_MQTT | Only Domoticz controllers (MQTT) and plugins included | -FHEM_HA | Only FHEM/OpenHAB/Home Assistant (MQTT) controllers and plugins included | -ETH | Ethernet interface enabled (ESP32-classic only) | -OPI_PSRAM | Specific configuration to enable PSRAM detection, ESP32-S3 only | -CDC | Support USBCDC/HWCDC-serial console on ESP32-C3, ESP32-S2, ESP32-S3 and ESP32-C6 | -noOTA/NO_OTA | Does not support OTA (Over The Air-updating of the firmware) Use [the flash page](https://td-er.nl/ESPEasy/) or ESPTool via USB Serial | - -Some example firmware names: -Firmware name | Hardware | Included plugins | -------------------------------------------------------------------|---------------------------------------|----------------------------------| -ESPEasy_mega-20230822_normal_ESP8266_1M.bin | ESP8266/ESP8285 with 1MB flash | Stable | -ESPEasy_mega-20230822_normal_ESP8266_4M1M.bin | ESP8266 with 4MB flash | Stable | -ESPEasy_mega-20230822_collection_A_ESP8266_4M1M.bin | ESP8266 with 4MB flash | Stable + Collection base + set A | -ESPEasy_mega-20230822_normal_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable | -ESPEasy_mega-20230822_collection_A_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable + Collection base + set A | -ESPEasy_mega-20230822_collection_B_ESP32_4M316k.bin | ESP32 with 4MB flash | Stable + Collection base + set B | -ESPEasy_mega-20230822_max_ESP32s3_8M1M_LittleFS_CDC.bin | ESP32-S3 with 8MB flash, CDC-serial | All available plugins | -ESPEasy_mega-20230822_max_ESP32s3_8M1M_LittleFS_OPI_PSRAM_CDC.bin | ESP32-S3 8MB flash, PSRAM, CDC-serial | All available plugins | -ESPEasy_mega-20230822_max_ESP32_16M1M.bin | ESP32 with 16MB flash | All available plugins | -ESPEasy_mega-20230822_max_ESP32_16M8M_LittleFS.bin | ESP32 with 16MB flash | All available plugins | - -The binary files for the different ESP32 variants (S2, C3, S3, C2, C6, 'Classic') are available in separate archives. - -To see what plugins are included in which collection set, you can find that on the [ESPEasy Plugin overview page](https://espeasy.readthedocs.io/en/latest/Plugin/_Plugin.html) - -## Documentation & more info - -Our new, in-depth documentation can be found at [ESPEasy.readthedocs.io](https://espeasy.readthedocs.io/en/latest/). Automatically built, so always up-to-date according to the contributed contents. The old Wiki documentation can be found at [letscontrolit.com/wiki](https://www.letscontrolit.com/wiki/index.php?title=ESPEasy). - -Additional details and discussion are on the "Experimental" section of the forum: https://www.letscontrolit.com/forum/viewforum.php?f=18 - -[![Gitpod Ready-to-Code](https://img.shields.io/badge/Gitpod-Ready--to--Code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/letscontrolit/ESPEasy) - - -## Icons used - -Icons on courtesy of [ICONS8](https://icons8.com/). + +## Installation and setup + +### Prereqs + +You will need: + +- `curl`, `jq` and `gzip` must be available +- A working `mail` command is needed if you want backups sent somewhere else by email + +### Copy scripts + +The `espeasy*` scripts will normally be copied somewhere in your `$PATH`, for example: + +```bash +chmod a+rx espeasy* +cp -v espeasy* /usr/local/bin/ +``` + +### Credentials + +You will need to set up a `$HOME/.espeasy` file as follows: + +```bash +# +# espeasy logins +# +CURLFLAGS="" +CURLARGS=${CURLARGS:-"$CURLFLAGS -u admin:password"} # change password to yours +``` + +Then make sure this file is readable by your user only (`chmod 600 $HOME/.espeasy`) + + +## Backup / restore + +### Single device + +ESP device backup for a single device can be done to your current directory as follows: + +```bash +$ espeasybackup 192.168.202.62 +PROG: Backup list has 1 nodes: 192.168.202.62 +espeasybackup: Backup 192.168.202.62 config.dat --> 20240827-205701-MitsubishiIR-12-config.dat ... OK +espeasybackup: Backup 192.168.202.62 security.dat --> 20240827-205701-MitsubishiIR-12-security.dat ... OK +espeasybackup: Backup 192.168.202.62 notification.dat --> 20240827-205701-MitsubishiIR-12-notification.dat ... OK +espeasybackup: Backup 192.168.202.62 rules1.txt --> 20240827-205701-MitsubishiIR-12-rules1.txt ... OK +espeasybackup: Backup 192.168.202.62 rules2.txt --> 20240827-205701-MitsubishiIR-12-rules2.txt ... OK +espeasybackup: Backup 192.168.202.62 rules3.txt --> 20240827-205701-MitsubishiIR-12-rules3.txt ... OK +espeasybackup: Backup 192.168.202.62 rules4.txt --> 20240827-205701-MitsubishiIR-12-rules4.txt ... OK +espeasybackup: Backup 192.168.202.62 esp.css --> 20240827-205701-MitsubishiIR-12-esp.css ... OK +``` + +... and restored as follows: + +```bash +$ rm *.css # sometimes causes problems +$ espeasyconfig 192.168.202.62 20240827-205701-MitsubishiIR-12* +espeasyconfig: File name config.dat: '20240827-205701-MitsubishiIR-12-config.dat' -> '/tmp/espeasyconfig-1185//config.dat' +espeasyconfig: File name notification.dat: '20240827-205701-MitsubishiIR-12-notification.dat' -> '/tmp/espeasyconfig-1185//notification.dat' +espeasyconfig: File name rules1.txt: '20240827-205701-MitsubishiIR-12-rules1.txt' -> '/tmp/espeasyconfig-1185//rules1.txt' +espeasyconfig: File name rules2.txt: '20240827-205701-MitsubishiIR-12-rules2.txt' -> '/tmp/espeasyconfig-1185//rules2.txt' +espeasyconfig: File name rules3.txt: '20240827-205701-MitsubishiIR-12-rules3.txt' -> '/tmp/espeasyconfig-1185//rules3.txt' +espeasyconfig: File name rules4.txt: '20240827-205701-MitsubishiIR-12-rules4.txt' -> '/tmp/espeasyconfig-1185//rules4.txt' +espeasyconfig: File name security.dat: '20240827-205701-MitsubishiIR-12-security.dat' -> '/tmp/espeasyconfig-1185//security.dat' +Upload config.dat: ########### 100.0% +espeasyconfig: config.dat upload OK +########### 100.0% +espeasyconfig: notification.dat upload OK +########### 100.0% +espeasyconfig: rules1.txt upload OK +########### 100.0% +espeasyconfig: rules2.txt upload OK +########### 100.0% +espeasyconfig: rules3.txt upload OK +########### 100.0% +espeasyconfig: rules4.txt upload OK +########### 100.0% +espeasyconfig: security.dat upload OK +``` + +The restore destination must be same ESP device type + +### Multiple devices + +If you add the `-a` option, the peer device list will be used to find your ESP devices, and also add any well known ESP MAC addresses found in the local arp table (`arp -a`) + + +```bash +$ espeasybackup -a 192.168.202.62 +espeasybackup: Initial list 192.168.202.62 192.168.202.63 ... +espeasybackup: Secondary list: 192.168.202.242 192.168.202.52 192.168.202.61 192.168.202.62 192.168.202.63 192.168.202.65 192.168.202.66 192.168.202.67 ... +PROG: Backup list has 8 nodes: 192.168.202.242 192.168.202.52 192.168.202.61 192.168.202.62 192.168.202.63 192.168.202.65 192.168.202.66 192.168.202.67 +espeasybackup: Backup 192.168.202.242 config.dat --> 20240827-210214-TestBallValve-2-config.dat ... OK +espeasybackup: Backup 192.168.202.242 security.dat --> 20240827-210214-TestBallValve-2-security.dat ... OK +espeasybackup: Backup 192.168.202.242 notification.dat --> 20240827-210214-TestBallValve-2-notification.dat ... OK +espeasybackup: Backup 192.168.202.242 rules1.txt --> 20240827-210214-TestBallValve-2-rules1.txt ... OK +espeasybackup: Backup 192.168.202.242 rules2.txt --> 20240827-210214-TestBallValve-2-rules2.txt ... OK +espeasybackup: Backup 192.168.202.242 rules3.txt --> 20240827-210214-TestBallValve-2-rules3.txt ... OK +espeasybackup: Backup 192.168.202.242 rules4.txt --> 20240827-210214-TestBallValve-2-rules4.txt ... OK +espeasybackup: Backup 192.168.202.62 config.dat --> 20240827-210214-MitsubishiIR-12-config.dat ... OK +espeasybackup: Backup 192.168.202.62 security.dat --> 20240827-210214-MitsubishiIR-12-security.dat ... OK +espeasybackup: Backup 192.168.202.62 notification.dat --> 20240827-210214-MitsubishiIR-12-notification.dat ... OK +espeasybackup: Backup 192.168.202.62 rules1.txt --> 20240827-210214-MitsubishiIR-12-rules1.txt ... OK +espeasybackup: Backup 192.168.202.62 rules2.txt --> 20240827-210214-MitsubishiIR-12-rules2.txt ... OK +espeasybackup: Backup 192.168.202.62 rules3.txt --> 20240827-210214-MitsubishiIR-12-rules3.txt ... OK +espeasybackup: Backup 192.168.202.62 rules4.txt --> 20240827-210214-MitsubishiIR-12-rules4.txt ... OK +espeasybackup: Backup 192.168.202.62 esp.css --> 20240827-210214-MitsubishiIR-12-esp.css ... OK +. +. +. +``` + +### cron jobs for backup + +You may want to back up your ESP devices regularly to a remote place. This is a way to find all ESP devices from the peer list of some og them (`-a` option), compress into a `.tar.gz` file (`-Z` option), and email that to a mail account (when `MAILTO` defined in the environment): + +```bash +20 06 * * 6 env MAILTO=admin@mydomain.net /usr/local/bin/espeasybackup -a -Z 192.168.202.52 192.168.202.62 192.168.202.64 > /tmp/espeasybackup.log 2>&1 +``` + + +## New firmware deployment + +To upload new firmware, you specify a firmware file and as many IP addresses / hostnames that you want to update with that same hardware + +```bash +$ espeasyupdate -f ESP_Easy_mega_20240826_IR_ESP32c3_4M316k_LittleFS_CDC.bin 192.168.202.242 +espeasyupdate: Updating 192.168.202.242 ... +espeasyupdate: 192.168.202.242 already has 'Custom IR for AC - 20240822-1' +espeasyupdate: Firmware on file (ESP_Easy_mega_IR_ESP32c3_4M316k_LittleFS_CDC) different from device (ESP_Easy_mega_public_IR_ESP32c3_4M316k_LittleFS_CDC). Use -f to force update. +Update Success! Rebooting... +``` + +The `-f` option is needed if you want to switch from one firmware file to another, or repeat a git build already deployed. + +This is tested as follows: + +```bash +# IP is hoatname or IP address +curl -s "http://${IP}/json"| jq -r '.System."Git Build"' # normally skipped if already deployed +curl -s "http://${IP}/json"| jq -r '.System."Binary Filename"' # normally skipped if binary file is different +``` From c657c1816e32a5a5fa6ed90655b7927ea37797ae Mon Sep 17 00:00:00 2001 From: Hans-Rune Bue Date: Tue, 27 Aug 2024 21:33:55 +0200 Subject: [PATCH 210/367] Missing $ in --- tools/ESPEasy-tools.md | 2 +- tools/espeasybackup | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/ESPEasy-tools.md b/tools/ESPEasy-tools.md index 49d72420fa..91a3bb728c 100644 --- a/tools/ESPEasy-tools.md +++ b/tools/ESPEasy-tools.md @@ -97,7 +97,7 @@ If you add the `-a` option, the peer device list will be used to find your ESP d $ espeasybackup -a 192.168.202.62 espeasybackup: Initial list 192.168.202.62 192.168.202.63 ... espeasybackup: Secondary list: 192.168.202.242 192.168.202.52 192.168.202.61 192.168.202.62 192.168.202.63 192.168.202.65 192.168.202.66 192.168.202.67 ... -PROG: Backup list has 8 nodes: 192.168.202.242 192.168.202.52 192.168.202.61 192.168.202.62 192.168.202.63 192.168.202.65 192.168.202.66 192.168.202.67 +espeasybackup: Backup list has 8 nodes: 192.168.202.242 192.168.202.52 192.168.202.61 192.168.202.62 192.168.202.63 192.168.202.65 192.168.202.66 192.168.202.67 espeasybackup: Backup 192.168.202.242 config.dat --> 20240827-210214-TestBallValve-2-config.dat ... OK espeasybackup: Backup 192.168.202.242 security.dat --> 20240827-210214-TestBallValve-2-security.dat ... OK espeasybackup: Backup 192.168.202.242 notification.dat --> 20240827-210214-TestBallValve-2-notification.dat ... OK diff --git a/tools/espeasybackup b/tools/espeasybackup index d781935f00..2cb8bb3891 100755 --- a/tools/espeasybackup +++ b/tools/espeasybackup @@ -122,7 +122,7 @@ else IPLIST=( $1 ) fi -echo "PROG: Backup list has ${#IPLIST[*]} nodes: ${IPLIST[*]} " +echo "$PROG: Backup list has ${#IPLIST[*]} nodes: ${IPLIST[*]} " for H in ${IPLIST[*]} do From e23df8129aa1702aa617e7087db95b69e284280b Mon Sep 17 00:00:00 2001 From: Hans-Rune Bue Date: Wed, 28 Aug 2024 07:41:09 +0200 Subject: [PATCH 211/367] Document enhancements on requirements, security + linting --- tools/ESPEasy-tools.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tools/ESPEasy-tools.md b/tools/ESPEasy-tools.md index 91a3bb728c..fbb1e44bb0 100644 --- a/tools/ESPEasy-tools.md +++ b/tools/ESPEasy-tools.md @@ -3,8 +3,13 @@ This is a set of simple but powerful command line tools for scripting updates, and for backup and restore of configuration settings and ESPeasy rule files -Be careful not to use these tools in a multiuser environment as the login password may display with something like `ps f -ef` +## Security +Some warnings apply: + +- Be careful not to use these tools in a multiuser environment. The ESPEasy device password may display with something like `ps f -ef` while commands are in progress. +- Protect your `$HOME/.espeasy` credential files as well as your home directory from access for other users. +- Make sure you have physical access / can powercycle your ESPEasy devices. In rare cases a power cycle is needed for updates to work, or if a formware upload does not complete ## Installation and setup @@ -12,6 +17,7 @@ Be careful not to use these tools in a multiuser environment as the login passwo You will need: +- A `bash` shell environment (Linux, MacOS, WSL does not really matter) - `curl`, `jq` and `gzip` must be available - A working `mail` command is needed if you want backups sent somewhere else by email @@ -20,13 +26,14 @@ You will need: The `espeasy*` scripts will normally be copied somewhere in your `$PATH`, for example: ```bash +cd tools chmod a+rx espeasy* -cp -v espeasy* /usr/local/bin/ +cp -v espeasy* /usr/local/bin/ # you may need sudo command in front ``` ### Credentials -You will need to set up a `$HOME/.espeasy` file as follows: +You will normally set up a `$HOME/.espeasy` file as follows for the ESPEasy password. This is not needed if the `CURLARGS` variable is set up in your shell environment from somewhere else. In that case, create an empty file. ```bash # @@ -38,8 +45,9 @@ CURLARGS=${CURLARGS:-"$CURLFLAGS -u admin:password"} # change password to yours Then make sure this file is readable by your user only (`chmod 600 $HOME/.espeasy`) +This assumes that the same ESPEasy passwords apply on all devices, but you can also do something like `env CURLARGS="-u admin:otherpasswd" espeasy....` if some devices need another password. -## Backup / restore +## Backup / restore ### Single device @@ -92,7 +100,6 @@ The restore destination must be same ESP device type If you add the `-a` option, the peer device list will be used to find your ESP devices, and also add any well known ESP MAC addresses found in the local arp table (`arp -a`) - ```bash $ espeasybackup -a 192.168.202.62 espeasybackup: Initial list 192.168.202.62 192.168.202.63 ... @@ -126,7 +133,6 @@ You may want to back up your ESP devices regularly to a remote place. This is a 20 06 * * 6 env MAILTO=admin@mydomain.net /usr/local/bin/espeasybackup -a -Z 192.168.202.52 192.168.202.62 192.168.202.64 > /tmp/espeasybackup.log 2>&1 ``` - ## New firmware deployment To upload new firmware, you specify a firmware file and as many IP addresses / hostnames that you want to update with that same hardware @@ -139,12 +145,12 @@ espeasyupdate: Firmware on file (ESP_Easy_mega_IR_ESP32c3_4M316k_LittleFS_CDC) d Update Success! Rebooting... ``` -The `-f` option is needed if you want to switch from one firmware file to another, or repeat a git build already deployed. +The `-f` option is needed if you want to switch from one firmware file to another, or repeat installing a git build already deployed. -This is tested as follows: +These facts are tested from the ESPEasy device as follows: ```bash -# IP is hoatname or IP address +# IP is hostname or IP address curl -s "http://${IP}/json"| jq -r '.System."Git Build"' # normally skipped if already deployed curl -s "http://${IP}/json"| jq -r '.System."Binary Filename"' # normally skipped if binary file is different ``` From 95f0137af9c47aacfc03ae792ef37e1430d5fa53 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Wed, 28 Aug 2024 14:05:00 +0200 Subject: [PATCH 212/367] [P165] Add digitmapping for having all RTL groups, UI improvements and fixes --- src/_P165_7SegNeopixel.ino | 2 + src/src/PluginStructs/P165_data_struct.cpp | 88 ++++++++++++++-------- src/src/PluginStructs/P165_data_struct.h | 13 +++- 3 files changed, 69 insertions(+), 34 deletions(-) diff --git a/src/_P165_7SegNeopixel.ino b/src/_P165_7SegNeopixel.ino index b0377af96e..0c7326030e 100644 --- a/src/_P165_7SegNeopixel.ino +++ b/src/_P165_7SegNeopixel.ino @@ -6,6 +6,8 @@ // ####################################################################################################### /** Changelog: + * 2024-08-28 tonhuisman: Fix digit mapping when using Right to Left digit ordering + * Moved around a few UI options, and changed some captions, fix editing digit options by keyboard. * 2024-08-27 tonhuisman: Changed max. number of pixels per segment to 7 (from 5), changed settings storage from 3 to 4 bits for * height, width and dot pixels per segment (not fully used yet, just preparing for future expansion). * Add checks for not having > 64 bits per digit, as that's technically not supported. diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 20fa042445..3499b2a7d0 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -60,10 +60,26 @@ P165_data_struct::P165_data_struct(struct EventStruct *event) { toGrp = -1; incGrp = -1; } + uint8_t dgtMap = 0; // starting digit for (int8_t grp = fromGrp; grp != toGrp; grp += incGrp) { fillSegmentBitmap(grp, _pixelGroupCfg[grp]); + // Set up digit mapping + uint8_t gOffs = 0; + + for (uint8_t g = 0; g < grp; ++g) { // Determine digit offset + gOffs += _pixelGroupCfg[g].dgts; + } + + for (uint8_t d = 0; d < _pixelGroupCfg[grp].dgts; ++d) { + showmap[dgtMap] = gOffs + d; + # if P165_DEBUG_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("P165 : showmap digit: %d, to group %d digit: %d"), dgtMap + 1, grp + 1, gOffs + d + 1)); + # endif // if P165_DEBUG_DEBUG + dgtMap++; + } + if (_pixelGroupCfg[grp].offs > 0) { _pixelGroupCfg[grp].boffs = pxlOffset; # if P165_DEBUG_DEBUG @@ -405,7 +421,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { addFormCheckBox(F("Segment Corners overlap"), concat(F("crnr"), grp10), grpCrnr, numberPlan); - addFormNumericBox(F("Decimal pixels"), concat(F("decp"), grp10), + addFormNumericBox(F("Decimal dot pixels"), concat(F("decp"), grp10), grpDotP, 0, P165_SEGMENT_DOT_PIXELS, # if FEATURE_TOOLTIPS EMPTY_STRING, @@ -456,15 +472,15 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { P165_GET_CONFIG_START(grp), false, !numberPlan); - addFormCheckBox(F("Decimal pixels last segment"), concat(F("dend"), grp10), + addFormCheckBox(F("Split g-segment pixels"), concat(F("spltg"), grp10), + P165_GET_CONFIG_SPLTG(grp), numberPlan); + + addFormCheckBox(F("Decimal dot last segment"), concat(F("dend"), grp10), P165_GET_CONFIG_DEND(grp), numberPlan); - addFormCheckBox(F("Right to Left digits"), concat(F("rtld"), grp10), + addFormCheckBox(F("Right to Left digits"), concat(F("rtld"), grp10), grpRtld, numberPlan); - addFormCheckBox(F("Split g-segment pixels"), concat(F("spltg"), grp10), - P165_GET_CONFIG_SPLTG(grp), numberPlan); - # if P165_DIGIT_TABLE_H_INT > 17 int rws = 17; // Above should be fixed number of rows, matching with ~80% digit table size @@ -480,7 +496,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { // Bind handlers on input fields to update the 7 segment simulation and digit counts addHtml(F("\n")); } From 3fc40874bd4d28d404962ba68acfe7943d7f2352 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Fri, 13 Sep 2024 23:38:48 +0200 Subject: [PATCH 239/367] [P113] Add UI selection of ROI area (single area selection only) --- src/_P113_VL53L1X.ino | 67 ++++-- src/src/PluginStructs/P113_data_struct.cpp | 142 +++++++++++ src/src/PluginStructs/P113_data_struct.h | 25 +- static/p113.css | 57 +++++ static/p113_script.js | 262 +++++++++++++++++++++ 5 files changed, 531 insertions(+), 22 deletions(-) create mode 100644 static/p113.css create mode 100644 static/p113_script.js diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index f29435798d..e6b64fff79 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -6,6 +6,8 @@ // ####################################################################################################### /** Changelog: + * 2024-09-13 tonhuisman: ROI settings can be selected by dragging the Optical Center Index matrix and the O.C. can be selected + * by double-clicking or alt-clicking an index. With validation. * 2024-07-29 tonhuisman: Add Region of Interest (ROI) settings for reducing the Field of View (FoV) of the sensor * 2024-04-25 tonhuisman: Add Direction value (1/0/-1), code improvements * 2023-08-11 tonhuisman: Fix issue not surfacing before, that the library right-shifts the I2C address when that is set... @@ -36,19 +38,16 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) { case PLUGIN_DEVICE_ADD: { - Device[++deviceCount].Number = PLUGIN_ID_113; - Device[deviceCount].Type = DEVICE_TYPE_I2C; - Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; - Device[deviceCount].Ports = 0; - Device[deviceCount].PullUpOption = false; - Device[deviceCount].InverseLogicOption = false; - Device[deviceCount].FormulaOption = true; - Device[deviceCount].ValueCount = 3; - Device[deviceCount].SendDataOption = true; - Device[deviceCount].TimerOption = true; - Device[deviceCount].TimerOptional = true; - Device[deviceCount].GlobalSyncOption = true; - Device[deviceCount].PluginStats = true; + Device[++deviceCount].Number = PLUGIN_ID_113; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; + Device[deviceCount].Ports = 0; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 3; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].PluginStats = true; break; } @@ -130,16 +129,52 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) # endif // ifndef LIMIT_BUILD_SIZE # if P113_USE_ROI + addFormSubHeader(F("Region Of Interest (ROI)")); + addRowLabel(F("Configure ROI")); + P113_data_struct::loadJavascript(); + P113_data_struct::loadCss(); P113_CheckMinMaxValues(event); + + html_table(F("")); + + html_TR_TD(); + html_TD(2); + addHtml(F("Select ROI area, min. 4 x 4 SPADs")); + addFormNumericBox(F("ROI 'x' SPADs"), F("roix"), P113_ROI_X, 4, 16); addUnit(F("4..16")); + + P113_data_struct::drawSelectionArea(P113_ROI_X, P113_ROI_Y, P113_OPT_CENTER); + addFormNumericBox(F("ROI 'y' SPADs"), F("roiy"), P113_ROI_Y, 4, 16); addUnit(F("4..16")); - addFormNumericBox(F("Optical Center index for ROI"), F("optcent"), P113_OPT_CENTER, 0, 255); - addFormNote(F("Default: 199 = sensor-center, please check the documentation.")); + addFormNumericBox(F("Optical Center index for ROI"), F("optc"), P113_OPT_CENTER, 0, 255); + + html_TR_TD(); + addHtmlDiv(F("note"), F("Default: 199 = sensor-center.")); + html_TR_TD(); + addHtmlDiv(F("note"), F("Click & Drag to select ROI.")); + html_TR_TD(); + addHtmlDiv(F("note"), F("Alt-Click/Dbl-Click to select Optical Center.")); + + int rws = 7; // Above should be fixed number of rows, matching with ~80% selection area + + for (; rws < 16; ++rws) { + html_TR_TD(); + addHtml(F(" ")); + } + + html_end_table(); + html_add_script(false); + addHtml(F("document.addEventListener('DOMContentLoaded', p113_main);")); + const __FlashStringHelper *_fmt = F("document.getElementById('%s').onchange=function(){p113_main.upDsp()};"); + addHtml(strformat(_fmt, String(F("roix")).c_str())); + addHtml(strformat(_fmt, String(F("roiy")).c_str())); + addHtml(strformat(_fmt, String(F("optc")).c_str())); + html_add_script_end(); # endif // if P113_USE_ROI success = true; @@ -156,7 +191,7 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) # if P113_USE_ROI P113_ROI_X = getFormItemInt(F("roix")); P113_ROI_Y = getFormItemInt(F("roiy")); - P113_OPT_CENTER = getFormItemInt(F("optcent")); + P113_OPT_CENTER = getFormItemInt(F("optc")); P113_CheckMinMaxValues(event); # endif // if P113_USE_ROI diff --git a/src/src/PluginStructs/P113_data_struct.cpp b/src/src/PluginStructs/P113_data_struct.cpp index c5985bd5c1..30c07cbc86 100644 --- a/src/src/PluginStructs/P113_data_struct.cpp +++ b/src/src/PluginStructs/P113_data_struct.cpp @@ -123,4 +123,146 @@ bool P113_data_struct::isReadSuccessful() { return success; } +# if P113_USE_ROI +uint8_t P113_data_struct::getSPAD(uint8_t y, uint8_t x) { + const uint8_t offset = ((x & 0x0F) << 3) + (y & 0x07); + + return (y > 7) ? 127 - offset : 128 + offset; +} + +void P113_data_struct::opticalIndexToXy(uint8_t oi, uint8_t& y, uint8_t& x) { + x = 0; + y = 0; + + if (oi < 128) { + y += 8; + oi = 127 - oi; + } else { + oi -= 128; + } + x = oi >> 3; + y += oi & 0x7; +} + +void P113_data_struct::drawSelectionArea(uint8_t roix, uint8_t roiy, uint8_t opticalCenter) { + addHtml(F("
    ")); + addHtml(F("
    " + "
    ")); + uint8_t lx = 0; + uint8_t ly = 0; + uint8_t rx = 15; + uint8_t ry = 15; + uint8_t ocx = 0; + uint8_t ocy = 0; + + P113_data_struct::opticalIndexToXy(opticalCenter, ocy, ocx); + const uint8_t hx = roix / 2; + const uint8_t hy = roiy / 2; + const uint8_t dx = roix % 2 != 0 ? 0 : 1; + const uint8_t dy = roiy % 2 != 0 ? 0 : 1; + + if ((roix < 16) || (roiy < 16)) { + lx = ocx - hx; + rx = ocx + hx - dx; + ly = ocy - hy + dy; + ry = ocy + hy; + + if ((rx + 1) - (lx + 1) < 4) { // check width >= 4 SPADs + const uint8_t drx = 3 - ((rx + 1) - (lx + 1)); + + if (rx < (15 - drx)) { + rx += drx; + } else { + lx -= drx; + ocx -= drx / 2; + } + } + + if (rx > 15) { // do we fall off from the right? + const uint8_t drx = rx - 15; + rx -= drx; + lx -= drx; + ocx -= drx; + } + + if ((ry + 1) - (ly + 1) < 4) { // check height >= 4 SPADs + const uint8_t dry = 3 - ((ry + 1) - (ly + 1)); + + if (ry < ((15 - dry))) { + ry += dry; + } else { + ly -= dry; + ocy -= dry / 2; + } + } + + if (ry > 15) { // do we fall off from the bottom? + const uint8_t dry = ry - 15; + ry -= dry; + ly -= dry; + ocy -= dry; + } + } + + // addLog(LOG_LEVEL_INFO, strformat(F("drawSelectionArea E lx:%d ly:%d rx:%d ry:%d opt.center:%d ocx:%d ocy:%d hx:%d hy:%d dx:%d dy:%d"), + // lx, ly, rx, ry, opticalCenter, ocx, ocy, hx, hy, dx, dy)); + + for (uint8_t y = 0; y < 16; ++y) { + for (uint8_t x = 0; x < 16; ++x) { + const bool sel = x >= lx && x <= rx && y >= ly && y <= ry; + const bool oc = x == ocx && y == ocy; + + addHtml(F("
    '); + addHtmlInt(getSPAD(y, x)); + addHtml(F("
    ")); + } + } + + addHtml(F("
    ")); +} + +void P113_data_struct::loadJavascript() { + html_add_script(false); + addHtml(F( // TODO Move to WebStaticData.h + "function p113_main(){" + "function elId(e){return document.getElementById(e)}" + "function elVal(e){return elId(e).value}" + "function cntr(e){if(elVal('roix')>10||elVal('roiy')>10)return;let sc=[];const cs=vi.querySelectorAll('.cel');cs.forEach((c)=>{const b=c.getBoundingClientRect();if(e.x>=b.left&&e.y>=b.top&&e.x-scx<=b.right&&e.y-scy<=b.bottom)sc.push(c)});if(sc.length===1){elId('optc').value=parseInt(sc[0].textContent);upDsp()}}" + "const vi=elId('vi');const sl=elId('sL');let imd=false;let ims=false;let st={},se={};let sr={};let oC=199;let xr=16;let yr=16;let ptg=0;let scx=vi.offsetWidth-vi.clientWidth;let scy=vi.offsetHeight-vi.clientHeight;let sm={w:96,h:96,};" + "vi.addEventListener('mousedown',(e)=>{if(e.altKey){cntr(e)}else{imd=true;if(!ims){se={x:null,y:null};clr()}st={x:e.x+window.scrollX-vi.offsetLeft,y:e.y+window.scrollY-vi.offsetTop}}});" + "vi.addEventListener('mouseup',(e)=>{if(!imd)return;imd=false;const vc=vi.getBoundingClientRect();se={x:e.x+window.scrollX-vi.offsetLeft,y:e.y+window.scrollY-vi.offsetTop};sl.style.top=`0px`;sl.style.left=`0px`;sl.style.bottom='unset';sl.style.right='unset';sl.style.visibility='hidden';if(Math.abs(se.x-st.x){cntr(e)});" + "vi.addEventListener('mousemove',(e)=>{if(!imd)return;se={x:e.x+window.scrollX,y:e.y+window.scrollY};sr={x1:Math.min(st.x,se.x),y1:Math.min(st.y,se.y),x2:Math.max(st.x,se.x)+scx,y2:Math.max(st.y,se.y)+scy};sl.style.top=`${scx+Math.min(sr.y1,sr.y2)}px`;sl.style.left=`${scy+Math.min(sr.x1,sr.x2)}px`;sl.style.bottom=`${vi.offsetHeight+vi.offsetTop-Math.max(sr.y1,sr.y2)}px`;sl.style.right=`${vi.offsetWidth+vi.offsetLeft-Math.max(sr.x1,sr.x2)}px`;sl.style.visibility='visible';if(!ims)clr()});" + "function clr(){vi.querySelectorAll('.cel').forEach((c)=>{c.classList.remove('sel');c.classList.remove('oc')})}" + "function gSelCls(){let sc=[];const cl=vi.querySelectorAll('.cel');sl.style.visibility='visible';cl.forEach((c)=>{const b=c.getBoundingClientRect();wx=window.scrollX;wy=window.scrollY;if(sr.x1-wx<=((b.left)-vi.offsetLeft+16)&&sr.y1-wy<=((b.top)-vi.offsetTop+16)&&(sr.x2-wx-scx+16)>=(b.right)&&(sr.y2-wy-scy+16)>=b.bottom)sc.push(c)});if(sc.length>0){[ly,lx]=oiToXy(sc[0].textContent);[ry,rx]=oiToXy(sc[sc.length-1].textContent);nx=lx+Math.floor((rx-lx)/2+0.5);ny=ly+Math.floor((ry-ly)/2);oC=gSP(ny,nx);xr=1+rx-lx;yr=1+ry-ly;ptg=1;elId('roix').value=xr;elId('roiy').value=yr;elId('optc').value=oC;ptg=0}return sc}" + "function oiToXy(oidx){x=0;y=0;o=parseInt(oidx);if(o<128){y+=8;o=127-o}else{o-=128}x=o>>3;y+=o&7;return[y,x]}" + "function pntSel(roix,roiy,oCen){if(!ptg){lx=0;ly=0;rx=15;ry=15;oo=oCen;if(roix>10||roiy>10){oCen=199}[ocy,ocx]=oiToXy(oCen);hx=Math.floor(roix/2);hy=Math.floor(roiy/2);dx=roix%2!=0?0:1;dy=roiy%2!=0?0:1;if((roix<16)||(roiy<16)){lx=ocx-hx;rx=ocx+hx-dx;ly=ocy-hy+dy;ry=ocy+hy;if((rx+1)-(lx+1)<4){d=3-((rx+1)-(lx+1));if(rx<(15-d)){rx+=d}else{lx-=d;ocx-=d/2}}if(rx>15||lx<0){d=lx<0?lx:rx-15;rx-=d;lx-=d;ocx-=d}if((ry+1)-(ly+1)<4){d=3-((ry+1)-(ly+1));if(ry<((15-d))){ry+=d}else{ly-=d;ocy-=d/2}}if(ry>15||ly<0){d=ly<0?ly:ry-15;ry-=d;ly-=d;ocy-=d}}cl=vi.querySelectorAll('.cel');for(y=0;y<16;++y){for(x=0;x<16;++x){cc=cl[y*16+x].classList;if(x>=lx&&x<=rx&&y>=ly&&y<=ry)cc.add('sel');else cc.remove('sel');if(x==ocx&&y==ocy)cc.add('oc');else cc.remove('oc')}}oc=gSP(ocy,ocx);if(oc!=oo){ptg=1;elId('optc').value=oc;ptg=0}}}" + "function upDsp(){xr=elVal('roix');yr=elVal('roiy');optc=elVal('optc');pntSel(xr,yr,optc)}p113_main.upDsp=upDsp;" + "function gSP(y,x){o=((x&15)<<3)+(y&7);return(y>7)?127-o:128+o}document.addEventListener('resize',()=>{scx=vi.offsetWidth-vi.clientWidth;scy=vi.offsetHeight-vi.clientHeight})}" + + )); + html_add_script_end(); +} + +void P113_data_struct::loadCss() { + addHtml(F("")); +} + +# endif // if P113_USE_ROI + #endif // ifdef USES_P113 diff --git a/src/src/PluginStructs/P113_data_struct.h b/src/src/PluginStructs/P113_data_struct.h index cd89188807..70129d408c 100644 --- a/src/src/PluginStructs/P113_data_struct.h +++ b/src/src/PluginStructs/P113_data_struct.h @@ -43,12 +43,25 @@ struct P113_data_struct : public PluginTaskData_base { P113_data_struct() = delete; virtual ~P113_data_struct(); - bool begin(struct EventStruct *event); - bool startRead(); - bool readAvailable(); - uint16_t readDistance(); - uint16_t readAmbient(); - bool isReadSuccessful(); + bool begin(struct EventStruct *event); + bool startRead(); + bool readAvailable(); + uint16_t readDistance(); + uint16_t readAmbient(); + bool isReadSuccessful(); + + # if P113_USE_ROI + static uint8_t getSPAD(uint8_t x, + uint8_t y); + static void opticalIndexToXy(uint8_t oi, + uint8_t& y, + uint8_t& x); + static void drawSelectionArea(uint8_t roix, + uint8_t roiy, + uint8_t opticalCenter); + static void loadJavascript(); + static void loadCss(); + # endif // if P113_USE_ROI private: diff --git a/static/p113.css b/static/p113.css new file mode 100644 index 0000000000..ef3836346d --- /dev/null +++ b/static/p113.css @@ -0,0 +1,57 @@ +:root { + --bg113b: #bdaeeb45; +} + +[data-theme='dark'] { + --bg113b: #55304445; +} + +@media (prefers-color-scheme:dark) { + [data-theme='auto'] { + --bg113b: #55304445; + } +} + +#vi { + border: 1px solid var(--c4); + padding: 0; + overflow: auto; + display: grid; + grid-template-columns: repeat(16, 2rem); + gap: 0; + position: relative; + -moz-user-select: -moz-none; + -khtml-user-select: none; + -webkit-user-select: none; + -ms-user-select: none; + user-select: none; +} + +.cel { + width: 2rem; + height: 2rem; + border-right: 1px solid var(--c4); + border-bottom: 1px solid var(--c4); + text-align: center; + line-height: 2rem; + vertical-align: middle; + display: inline-block; +} + +.cel.sel { + background-color: var(--bg5); +} + +.cel.oc { + font-style: italic; + text-decoration: underline; +} + +#sL { + position: absolute; + top: 0; + left: 0; + border: 1px solid var(--bg5); + background-color: var(--bg113b); + z-index: 99999; +} \ No newline at end of file diff --git a/static/p113_script.js b/static/p113_script.js new file mode 100644 index 0000000000..f6663fedf5 --- /dev/null +++ b/static/p113_script.js @@ -0,0 +1,262 @@ +function p113_main() { + function elId(e) { + return document.getElementById(e); + } + function elVal(e) { + return elId(e).value; + } + function cntr(e) { + if (elVal('roix') > 10 || elVal('roiy') > 10) return; + let sc = []; + const cs = vi.querySelectorAll('.cel'); + cs.forEach((c) => { + const b = c.getBoundingClientRect(); + if ( + e.x >= b.left && + e.y >= b.top && + e.x - scx <= b.right && + e.y - scy <= b.bottom + ) sc.push(c); + }); + if (sc.length === 1) { elId('optc').value = parseInt(sc[0].textContent); upDsp(); } + } + const vi = elId('vi'); + const sl = elId('sL'); + let imd = false; + let ims = false; + let st = {}, + se = {}; + let sr = {}; + let oC = 199; + let xr = 16; + let yr = 16; + let ptg = 0; + let scx = vi.offsetWidth - vi.clientWidth; + let scy = vi.offsetHeight - vi.clientHeight; + let sm = { + w: 96, + h: 96, + }; + // console.log('p113_main', vi, sl); + + vi.addEventListener('mousedown', (e) => { + if (e.altKey) { + cntr(e); + } else { + imd = true; + if (!ims) { + se = { + x: null, + y: null + }; + clr(); + } + st = { + x: e.x + window.scrollX - vi.offsetLeft, + y: e.y + window.scrollY - vi.offsetTop + }; + // console.log('mousedown', e.x, e.y, st); + } + }); + vi.addEventListener('mouseup', (e) => { + if (!imd) return; + imd = false; + const vc = vi.getBoundingClientRect(); + se = { + x: e.x + window.scrollX - vi.offsetLeft, + y: e.y + window.scrollY - vi.offsetTop + }; + // console.log('mouseup', sr, se, vc.left, vc.top, window.scrollY); + sl.style.top = `0px`; + sl.style.left = `0px`; + sl.style.bottom = 'unset'; + sl.style.right = 'unset'; + sl.style.visibility = 'hidden'; + if ( + Math.abs(se.x - st.x) < sm.w || + Math.abs(se.y - st.y) < sm.h + ) { upDsp(); return; } + gSelCls(); + pntSel(xr, yr, oC); + }); + vi.addEventListener('dblclick', (e) => { + cntr(e); + // console.log('dblclick', e.x, e.y); + }); + vi.addEventListener('mousemove', (e) => { + if (!imd) return; + se = { + x: e.x + window.scrollX, + y: e.y + window.scrollY + }; + sr = { + x1: Math.min(st.x, se.x), + y1: Math.min(st.y, se.y), + x2: Math.max(st.x, se.x) + scx, + y2: Math.max(st.y, se.y) + scy + }; + sl.style.top = `${scx + Math.min( + sr.y1, + sr.y2 + )}px`; + sl.style.left = `${scy + Math.min( + sr.x1, + sr.x2 + )}px`; + sl.style.bottom = `${vi.offsetHeight + + vi.offsetTop - + Math.max(sr.y1, sr.y2) + }px`; + sl.style.right = `${vi.offsetWidth + + vi.offsetLeft - + Math.max(sr.x1, sr.x2) + }px`; + sl.style.visibility = 'visible'; + if (!ims) + clr(); + }); + function clr() { + vi.querySelectorAll('.cel') + .forEach((c) => { c.classList.remove('sel'); c.classList.remove('oc') }); + } + function gSelCls() { + let sc = []; + const cl = vi.querySelectorAll('.cel'); + sl.style.visibility = 'visible'; + cl.forEach((c) => { + const b = c.getBoundingClientRect(); + wx=window.scrollX; + wy=window.scrollY; + if ( + sr.x1 - wx <= ((b.left) - vi.offsetLeft + 16) && + sr.y1 - wy <= ((b.top) - vi.offsetTop + 16) && + (sr.x2 - wx - scx + 16) >= (b.right) && + (sr.y2 - wy - scy + 16) >= b.bottom + ) sc.push(c); + }); + if (sc.length > 0) { + [ly, lx] = oiToXy(sc[0].textContent); + [ry, rx] = oiToXy(sc[sc.length - 1].textContent); + nx = lx + Math.floor((rx - lx) / 2 + 0.5); + ny = ly + Math.floor((ry - ly) / 2); + oC = gSP(ny, nx); + xr = 1 + rx - lx; + yr = 1 + ry - ly; + // console.log('getSelectedCells', sc[0], sc[sc.length - 1], lx, ly, rx, ry, nx, ny, 'optc:', oC); + ptg = 1; + elId('roix').value = xr; + elId('roiy').value = yr; + elId('optc').value = oC; + ptg = 0; + } + return sc; + } + function oiToXy(oidx) { + x = 0; + y = 0; + o = parseInt(oidx); + if (o < 128) { + y += 8; + o = 127 - o; + } else { + o -= 128; + } + x = o >> 3; + y += o & 7; + return [y, x]; + } + function pntSel(roix, roiy, oCen) { + if (!ptg) { + lx = 0; + ly = 0; + rx = 15; + ry = 15; + oo = oCen; + if (roix > 10 || roiy > 10) { oCen = 199; } + [ocy, ocx] = oiToXy(oCen); + hx = Math.floor(roix / 2); + hy = Math.floor(roiy / 2); + dx = roix % 2 != 0 ? 0 : 1; + dy = roiy % 2 != 0 ? 0 : 1; + if ((roix < 16) || (roiy < 16)) { + lx = ocx - hx; + rx = ocx + hx - dx; + ly = ocy - hy + dy; + ry = ocy + hy; + if ((rx + 1) - (lx + 1) < 4) { + d = 3 - ((rx + 1) - (lx + 1)); + if (rx < (15 - d)) { + rx += d; + } else { + lx -= d; + ocx -= d / 2; + } + } + if (rx > 15 || lx < 0) { + d = lx < 0 ? lx : rx - 15; + rx -= d; + lx -= d; + ocx -= d; + } + if ((ry + 1) - (ly + 1) < 4) { + d = 3 - ((ry + 1) - (ly + 1)); + if (ry < ((15 - d))) { + ry += d; + } else { + ly -= d; + ocy -= d / 2; + } + } + if (ry > 15 || ly < 0) { + d = ly < 0 ? ly : ry - 15; + ry -= d; + ly -= d; + ocy -= d; + } + } + // console.log('dsp oCen:', getSPAD(ocy, ocx), oCen); + clr = vi.querySelectorAll('.cel'); + for (y = 0; y < 16; ++y) { + for (x = 0; x < 16; ++x) { + cc = clr[y * 16 + x].classList; + if (x >= lx && x <= rx && y >= ly && y <= ry) + cc.add('sel'); + else + cc.remove('sel'); + if (x == ocx && y == ocy) + cc.add('oc'); + else + cc.remove('oc'); + } + } + oc = gSP(ocy, ocx); + if (oc != oo) { + ptg = 1; + elId('optc').value = oc; + ptg = 0; + } + } + } + function upDsp() { + xr = elVal('roix'); + yr = elVal('roiy'); + optc = elVal('optc'); + pntSel(xr, yr, optc); + } + p113_main.upDsp = upDsp; + function gSP(y, x) { + o = ((x & 15) << 3) + (y & 7); + return (y > 7) ? 127 - o : 128 + o; + } + // Needed for multi-select + // document.addEventListener('keydown', (e) => { + // if (e.ctrlKey) isMultiSelectionOn = true; + // }); + // document.addEventListener('keyup', (e) => { + // if (!e.ctrlKey) isMultiSelectionOn = false; + // }); + document.addEventListener('resize', () => { + scx = vi.offsetWidth - vi.clientWidth; + scy = vi.offsetHeight - vi.clientHeight; + }); +} From b2fc44465a4c2aeed3db1c44ba23c1561752f489 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 14 Sep 2024 23:43:54 +0200 Subject: [PATCH 240/367] [ESP-IDF5.3] Switch back to IDF5.3 for LittleFS builds --- platformio_core_defs.ini | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 8d41c43c1b..8c589364e7 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -194,12 +194,8 @@ lib_ignore = ; ESP_IDF 5.1 [core_esp32_IDF5_3__3_0_4_LittleFS] -;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2861/framework-arduinoespressif32-all-release_v5.3-40822c72.zip -;platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10+rc1/platform-espressif32.zip -;platform_packages = -platform = https://github.com/Jason2866/platform-espressif32.git -platform_packages = framework-arduinoespressif32 @https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2858/framework-arduinoespressif32-all-release_v5.1-147836c.zip +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2953/framework-arduinoespressif32-all-release_v5.3-b2dba612.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 From 88cb1bcfbfb376306d03617b964da55806b68ed9 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sat, 14 Sep 2024 23:45:05 +0200 Subject: [PATCH 241/367] [N001_Email] Increase parse buffer size + cleanup --- src/_N001_Email.cpp | 161 +++++++++++++++++++++++++------------------- 1 file changed, 90 insertions(+), 71 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index bebb3b4d29..3b63be66b2 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -47,7 +47,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, bool NPlugin_001_Auth(WiFiClient & client, const String& user, const String& pass, - uint16_t timeout); + uint16_t timeout); bool NPlugin_001_MTA(WiFiClient & client, const String& aStr, uint16_t aWaitForPattern, @@ -142,6 +142,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat( F("Email: Connecting to %s:%d"), @@ -172,7 +173,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co ); uint16_t clientTimeout = notificationsettings.Timeout * 1000; // Convert to mS. - if (clientTimeout < NPLUGIN_001_MIN_TM || clientTimeout > NPLUGIN_001_MAX_TM) { + + if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; } @@ -193,39 +195,44 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co // Use Notify Command's destination email address(s) if provided in Command rules. // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
    How are you?
    Have a good day.
    " - String subAddr = ""; - String tmp_ato = ""; - int pos_brace1 = aMesg.indexOf('{'); - int pos_amper = aMesg.indexOf('@'); - int pos_brace2 = aMesg.indexOf('}'); - if(pos_brace1 == 0 && pos_amper > pos_brace1 && pos_brace2 > pos_amper) { - subAddr = aMesg.substring(pos_brace1+1, pos_brace2); - subAddr.trim(); - tmp_ato = subAddr; + String subAddr; + String tmp_ato; + int pos_brace1 = aMesg.indexOf('{'); + int pos_amper = aMesg.indexOf('@'); + int pos_brace2 = aMesg.indexOf('}'); + + if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { + subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); + subAddr.trim(); + tmp_ato = subAddr; # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); - } - # endif - String subMsg = aMesg.substring(pos_brace2+1); // Remove substitute email address from subject line. + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + + String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. + subMsg.trim(); + + if (subMsg.indexOf(',') == 0) { + subMsg = subMsg.substring(1); // Remove leading comma. subMsg.trim(); - if(subMsg.indexOf(',') == 0) { - subMsg = subMsg.substring(1); // Remove leading comma. - subMsg.trim(); - } - if(!subMsg.length()) { - subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; - } + } + + if (!subMsg.length()) { + subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; + } # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); - } - # endif - aMesg = subMsg; + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); + } + # endif // ifndef BUILD_NO_DEBUG + aMesg = subMsg; } else { - tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. + tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. } // Clean up receiver address. @@ -252,73 +259,83 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout/1000)); - # endif + addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); + # endif // ifndef BUILD_NO_DEBUG while (true) { if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); } - # endif + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } if (!failFlag) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); - } - # endif - if (!NPlugin_001_MTA(client, strformat(F("EHLO %s"), notificationsettings.Domain), 250, clientTimeout)) { + addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); + + if (!NPlugin_001_MTA( + client, + astr, + 250, + clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; } } - // Must retrieve SMTP Reply Packet. Data not used, ignored. + // Must retrieve SMTP Reply Packet. Data not used, ignored. if (!failFlag) { - unsigned long timeout = millis(); + const unsigned long timer = millis() + clientTimeout; String replyStr; - String catStr = ""; - while (client.available()) { - if (millis() > timeout + clientTimeout) { + String catStr; + + bool done = false; + + while (client.available() && !done) { + if (timeOutReached(timer)) { failFlag = true; break; } - safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ, clientTimeout); + done = safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ); catStr += replyStr; } - if(!catStr.length()) { - catStr = "Empty!"; + if (!catStr.length()) { + catStr = F("Empty!"); } # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Packet Rcvd is: > %s <"),catStr.c_str())); + String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); + addLogMove(LOG_LEVEL_DEBUG, log); } - # endif + # endif // ifndef BUILD_NO_DEBUG } if (!failFlag) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); } - # endif + # endif // ifndef BUILD_NO_DEBUG + if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); - } - # endif + + addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } @@ -327,25 +344,26 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if (!failFlag) { # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); - # endif - if (!NPlugin_001_MTA(client, strformat(F("MAIL FROM:<%s>"), email_address.c_str()), 250, clientTimeout)) { + # endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); + + if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { # ifndef BUILD_NO_DEBUG - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); - } - # endif + addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } } if (!failFlag) { - bool nextAddressAvailable = true; - int i = 0; + bool nextAddressAvailable = true; + int i = 0; String emailTo; const String receiver(tmp_ato); - addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"),receiver.c_str())); + addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"), receiver.c_str())); if (!getNextMailAddress(receiver, emailTo, i)) { addLog(LOG_LEVEL_ERROR, F("Email: Receiver missing!")); @@ -353,9 +371,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } while (nextAddressAvailable) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLogMove(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); + addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); } if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } @@ -416,13 +433,13 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass if (mta1 && mta2 && mta3) { addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); } - return (mta1 && mta2 && mta3); - + return mta1 && mta2 && mta3; } bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) { # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, aStr); } @@ -438,6 +455,7 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa backgroundtasks(); const String aWaitForPattern_str = strformat(F("%d "), aWaitForPattern); + while (true) { if (timeOutReached(timer)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { @@ -450,13 +468,14 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa delay(0); String line; - safeReadStringUntil(client, line, '\n', NPLUGIN_001_PKT_SZ, timeout); + safeReadStringUntil(client, line, '\n', 1024, timeout); - line.replace("-", " "); // Must Remove optional dash from MTA response code. + line.replace("-", " "); // Must Remove optional dash from MTA response code. const bool patternFound = line.indexOf(aWaitForPattern_str) >= 0; # ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, line); } From 8af2923d171794476878971a3e22431b5d4032b8 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 11:16:00 +0200 Subject: [PATCH 242/367] [ESP-IDF5.3] Fix build solo1 builds --- platformio_esp32_solo1.ini | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index 3e88830187..3e1a1f2e17 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -18,10 +18,8 @@ build_unflags = ${esp32_base.build_unflags} ; IDF 5.1.2 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 -;platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -;platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2835/framework-arduinoespressif32-solo1-release_v5.3-fe1954e5.zip -platform = https://github.com/Jason2866/platform-espressif32.git -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2859/framework-arduinoespressif32-solo1-release_v5.1-147836c.zip +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2954/framework-arduinoespressif32-solo1-release_v5.3-b2dba612.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 85942025659e16fcf4ff6e7a51f8c6e11a06a234 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 15 Sep 2024 14:05:19 +0200 Subject: [PATCH 243/367] [Build] Possible fix for unstable builds --- src/src/DataTypes/TaskValues_Data.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/DataTypes/TaskValues_Data.h b/src/src/DataTypes/TaskValues_Data.h index 9d46fac4ea..97cb7a5820 100644 --- a/src/src/DataTypes/TaskValues_Data.h +++ b/src/src/DataTypes/TaskValues_Data.h @@ -5,7 +5,7 @@ #include "../DataTypes/SensorVType.h" -struct __attribute__((__packed__)) TaskValues_Data_t { +struct TaskValues_Data_t { TaskValues_Data_t(); TaskValues_Data_t(const TaskValues_Data_t& other); From 40760e09b45acbc43ac77b97db04dea8e7952ff0 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 15 Sep 2024 16:13:27 +0200 Subject: [PATCH 244/367] [Build] Possible fix, part 2, for unstable builds --- src/src/DataTypes/TaskValues_Data.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/src/DataTypes/TaskValues_Data.h b/src/src/DataTypes/TaskValues_Data.h index 97cb7a5820..78e5fc6603 100644 --- a/src/src/DataTypes/TaskValues_Data.h +++ b/src/src/DataTypes/TaskValues_Data.h @@ -5,7 +5,7 @@ #include "../DataTypes/SensorVType.h" -struct TaskValues_Data_t { +struct alignas(uint32_t) TaskValues_Data_t { TaskValues_Data_t(); TaskValues_Data_t(const TaskValues_Data_t& other); From 6c55b8ff5dc8f3ed56900559c023e2c5ca126224 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 15 Sep 2024 16:41:48 +0200 Subject: [PATCH 245/367] [P165] Bugfixes and javascript optimizations --- src/src/PluginStructs/P165_data_struct.cpp | 2 +- src/src/Static/WebStaticData.h | 22 +++++++++++---------- static/p165_digit.js | 23 +++++++++++++--------- 3 files changed, 27 insertions(+), 20 deletions(-) diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index d2165e5b77..15fa758dd3 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -518,7 +518,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { grpCount)); addHtml(strformat(_fmt, FsP(F("decp")), grp10, FsP(F("chDecp")), FsP(F("value")), grp10, grpDgts, false, "", grpCount)); - addHtml(strformat(_fmt, FsP(F("addn")), grp10, FsP(F("chAddn")), FsP(F("value")), grp10, grpDgts, true, "", + addHtml(strformat(_fmt, FsP(F("addn")), grp10, FsP(F("chAddn")), FsP(F("value")), grp10, grpRtld ? 0 : (grpDgts - 1), true, "", grpCount)); addHtml(strformat(F("document.getElementById('offs%d').onchange=function(){dgts(%d,['wdth','hght','decp','addn','offs','dgts'])};"), grp10, grpCount)); diff --git a/src/src/Static/WebStaticData.h b/src/src/Static/WebStaticData.h index 75e58700aa..b7e1ac3ff1 100644 --- a/src/src/Static/WebStaticData.h +++ b/src/src/Static/WebStaticData.h @@ -714,23 +714,25 @@ static const char DATA_UPDATE_SENSOR_VALUES_DEVICE_PAGE_JS[] PROGMEM = {0x66,0x7 static const char DATA_FETCH_AND_PARSE_LOG_JS[] PROGMEM = {0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x67,0x65,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x28,0x29,0x7b,0x76,0x61,0x72,0x20,0x65,0x2c,0x6f,0x3d,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x75,0x73,0x65,0x72,0x41,0x67,0x65,0x6e,0x74,0x2c,0x74,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x28,0x6f,0x70,0x65,0x72,0x61,0x7c,0x63,0x68,0x72,0x6f,0x6d,0x65,0x7c,0x73,0x61,0x66,0x61,0x72,0x69,0x7c,0x66,0x69,0x72,0x65,0x66,0x6f,0x78,0x7c,0x6d,0x73,0x69,0x65,0x7c,0x74,0x72,0x69,0x64,0x65,0x6e,0x74,0x28,0x3f,0x3d,0x5c,0x2f,0x29,0x29,0x5c,0x2f,0x3f,0x5c,0x73,0x2a,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x69,0x29,0x7c,0x7c,0x5b,0x5d,0x3b,0x72,0x65,0x74,0x75,0x72,0x6e,0x2f,0x74,0x72,0x69,0x64,0x65,0x6e,0x74,0x2f,0x69,0x2e,0x74,0x65,0x73,0x74,0x28,0x74,0x5b,0x31,0x5d,0x29,0x3f,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x22,0x49,0x45,0x22,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x28,0x65,0x3d,0x2f,0x5c,0x62,0x72,0x76,0x5b,0x20,0x3a,0x5d,0x2b,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x67,0x2e,0x65,0x78,0x65,0x63,0x28,0x6f,0x29,0x7c,0x7c,0x5b,0x5d,0x29,0x5b,0x31,0x5d,0x7c,0x7c,0x22,0x22,0x7d,0x3a,0x22,0x43,0x68,0x72,0x6f,0x6d,0x65,0x22,0x3d,0x3d,0x3d,0x74,0x5b,0x31,0x5d,0x26,0x26,0x6e,0x75,0x6c,0x6c,0x21,0x3d,0x28,0x65,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x5c,0x62,0x4f,0x50,0x52,0x7c,0x45,0x64,0x67,0x65,0x5c,0x2f,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x29,0x29,0x3f,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x22,0x4f,0x70,0x65,0x72,0x61,0x22,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x65,0x5b,0x31,0x5d,0x7d,0x3a,0x28,0x74,0x3d,0x74,0x5b,0x32,0x5d,0x3f,0x5b,0x74,0x5b,0x31,0x5d,0x2c,0x74,0x5b,0x32,0x5d,0x5d,0x3a,0x5b,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x61,0x70,0x70,0x4e,0x61,0x6d,0x65,0x2c,0x6e,0x61,0x76,0x69,0x67,0x61,0x74,0x6f,0x72,0x2e,0x61,0x70,0x70,0x56,0x65,0x72,0x73,0x69,0x6f,0x6e,0x2c,0x22,0x2d,0x3f,0x22,0x5d,0x2c,0x6e,0x75,0x6c,0x6c,0x21,0x3d,0x28,0x65,0x3d,0x6f,0x2e,0x6d,0x61,0x74,0x63,0x68,0x28,0x2f,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x5c,0x2f,0x28,0x5c,0x64,0x2b,0x29,0x2f,0x69,0x29,0x29,0x26,0x26,0x74,0x2e,0x73,0x70,0x6c,0x69,0x63,0x65,0x28,0x31,0x2c,0x31,0x2c,0x65,0x5b,0x31,0x5d,0x29,0x2c,0x7b,0x6e,0x61,0x6d,0x65,0x3a,0x74,0x5b,0x30,0x5d,0x2c,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3a,0x74,0x5b,0x31,0x5d,0x7d,0x29,0x7d,0x76,0x61,0x72,0x20,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x3d,0x67,0x65,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x28,0x29,0x2c,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x3d,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x6e,0x61,0x6d,0x65,0x2b,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3b,0x28,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x6e,0x61,0x6d,0x65,0x3d,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x3c,0x31,0x32,0x29,0x3f,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x3d,0x22,0x45,0x72,0x72,0x6f,0x72,0x3a,0x20,0x22,0x2b,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x42,0x72,0x6f,0x77,0x73,0x65,0x72,0x2b,0x22,0x20,0x69,0x73,0x20,0x6e,0x6f,0x74,0x20,0x73,0x75,0x70,0x70,0x6f,0x72,0x74,0x65,0x64,0x21,0x20,0x50,0x6c,0x65,0x61,0x73,0x65,0x20,0x74,0x72,0x79,0x20,0x61,0x20,0x6d,0x6f,0x64,0x65,0x72,0x6e,0x20,0x77,0x65,0x62,0x20,0x62,0x72,0x6f,0x77,0x73,0x65,0x72,0x2e,0x22,0x3a,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x3d,0x22,0x46,0x65,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x6c,0x6f,0x67,0x20,0x65,0x6e,0x74,0x72,0x69,0x65,0x73,0x2e,0x2e,0x2e,0x22,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x74,0x65,0x78,0x74,0x54,0x6f,0x44,0x69,0x73,0x70,0x6c,0x61,0x79,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x31,0x65,0x33,0x2c,0x30,0x29,0x3b,0x76,0x61,0x72,0x20,0x6c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x3d,0x6e,0x65,0x77,0x20,0x41,0x72,0x72,0x61,0x79,0x28,0x22,0x55,0x6e,0x75,0x73,0x65,0x64,0x22,0x2c,0x22,0x45,0x72,0x72,0x6f,0x72,0x22,0x2c,0x22,0x49,0x6e,0x66,0x6f,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x20,0x4d,0x6f,0x72,0x65,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x55,0x6e,0x64,0x65,0x66,0x69,0x6e,0x65,0x64,0x22,0x2c,0x22,0x44,0x65,0x62,0x75,0x67,0x20,0x44,0x65,0x76,0x22,0x29,0x3b,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x20,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x6f,0x29,0x7b,0x76,0x61,0x72,0x20,0x74,0x2c,0x6e,0x3b,0x69,0x73,0x4e,0x61,0x4e,0x28,0x6f,0x29,0x26,0x26,0x28,0x6f,0x3d,0x31,0x29,0x2c,0x6e,0x75,0x6c,0x6c,0x3d,0x3d,0x65,0x26,0x26,0x28,0x65,0x3d,0x31,0x65,0x33,0x29,0x2c,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x69,0x6e,0x67,0x5f,0x74,0x79,0x70,0x65,0x3d,0x65,0x3c,0x3d,0x35,0x30,0x30,0x3f,0x22,0x61,0x75,0x74,0x6f,0x22,0x3a,0x22,0x73,0x6d,0x6f,0x6f,0x74,0x68,0x22,0x3b,0x76,0x61,0x72,0x20,0x72,0x3d,0x22,0x22,0x2c,0x6c,0x3d,0x30,0x2c,0x73,0x3d,0x73,0x65,0x74,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x29,0x7b,0x6c,0x3e,0x30,0x3f,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x3a,0x28,0x2b,0x2b,0x6f,0x3e,0x31,0x3f,0x6c,0x3d,0x31,0x3a,0x66,0x65,0x74,0x63,0x68,0x28,0x22,0x2f,0x6c,0x6f,0x67,0x6a,0x73,0x6f,0x6e,0x22,0x29,0x2e,0x74,0x68,0x65,0x6e,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x32,0x30,0x30,0x3d,0x3d,0x3d,0x6f,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x3f,0x6f,0x2e,0x6a,0x73,0x6f,0x6e,0x28,0x29,0x2e,0x74,0x68,0x65,0x6e,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x76,0x61,0x72,0x20,0x6c,0x3b,0x66,0x6f,0x72,0x28,0x6e,0x75,0x6c,0x6c,0x3d,0x3d,0x6e,0x26,0x26,0x28,0x6e,0x3d,0x22,0x22,0x29,0x2c,0x74,0x3d,0x30,0x3b,0x74,0x3c,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x6e,0x72,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x3b,0x2b,0x2b,0x74,0x29,0x74,0x72,0x79,0x7b,0x6c,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x7d,0x63,0x61,0x74,0x63,0x68,0x28,0x65,0x29,0x7b,0x6c,0x3d,0x65,0x2e,0x6e,0x61,0x6d,0x65,0x7d,0x66,0x69,0x6e,0x61,0x6c,0x6c,0x79,0x7b,0x22,0x54,0x79,0x70,0x65,0x45,0x72,0x72,0x6f,0x72,0x22,0x21,0x3d,0x3d,0x6c,0x26,0x26,0x28,0x72,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x2c,0x6e,0x2b,0x3d,0x22,0x3c,0x64,0x69,0x76,0x20,0x63,0x6c,0x61,0x73,0x73,0x3d,0x6c,0x65,0x76,0x65,0x6c,0x5f,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x6c,0x65,0x76,0x65,0x6c,0x2b,0x22,0x20,0x69,0x64,0x3d,0x22,0x2b,0x72,0x2b,0x27,0x3e,0x3c,0x66,0x6f,0x6e,0x74,0x20,0x63,0x6f,0x6c,0x6f,0x72,0x3d,0x22,0x67,0x72,0x61,0x79,0x22,0x3e,0x27,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x2b,0x22,0x3a,0x3c,0x2f,0x66,0x6f,0x6e,0x74,0x3e,0x20,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x45,0x6e,0x74,0x72,0x69,0x65,0x73,0x5b,0x74,0x5d,0x2e,0x74,0x65,0x78,0x74,0x2b,0x22,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x22,0x29,0x7d,0x65,0x3d,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x54,0x54,0x4c,0x2c,0x22,0x22,0x21,0x3d,0x3d,0x6e,0x26,0x26,0x28,0x22,0x46,0x65,0x74,0x63,0x68,0x69,0x6e,0x67,0x20,0x6c,0x6f,0x67,0x20,0x65,0x6e,0x74,0x72,0x69,0x65,0x73,0x2e,0x2e,0x2e,0x22,0x3d,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x26,0x26,0x28,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x22,0x22,0x29,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x2b,0x3d,0x6e,0x29,0x2c,0x6e,0x3d,0x22,0x22,0x2c,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x22,0x29,0x2e,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x2c,0x31,0x3d,0x3d,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x26,0x26,0x22,0x22,0x21,0x3d,0x3d,0x72,0x26,0x26,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x72,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x49,0x6e,0x74,0x6f,0x56,0x69,0x65,0x77,0x28,0x7b,0x62,0x65,0x68,0x61,0x76,0x69,0x6f,0x72,0x3a,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x69,0x6e,0x67,0x5f,0x74,0x79,0x70,0x65,0x7d,0x29,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x75,0x72,0x72,0x65,0x6e,0x74,0x5f,0x6c,0x6f,0x67,0x6c,0x65,0x76,0x65,0x6c,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x3d,0x22,0x4c,0x6f,0x67,0x67,0x69,0x6e,0x67,0x3a,0x20,0x22,0x2b,0x6c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x5b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x53,0x65,0x74,0x74,0x69,0x6e,0x67,0x73,0x57,0x65,0x62,0x4c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x5d,0x2b,0x22,0x20,0x28,0x22,0x2b,0x6f,0x2e,0x4c,0x6f,0x67,0x2e,0x53,0x65,0x74,0x74,0x69,0x6e,0x67,0x73,0x57,0x65,0x62,0x4c,0x6f,0x67,0x4c,0x65,0x76,0x65,0x6c,0x2b,0x22,0x29,0x22,0x2c,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x30,0x29,0x7d,0x29,0x3a,0x63,0x6f,0x6e,0x73,0x6f,0x6c,0x65,0x2e,0x6c,0x6f,0x67,0x28,0x22,0x4c,0x6f,0x6f,0x6b,0x73,0x20,0x6c,0x69,0x6b,0x65,0x20,0x74,0x68,0x65,0x72,0x65,0x20,0x77,0x61,0x73,0x20,0x61,0x20,0x70,0x72,0x6f,0x62,0x6c,0x65,0x6d,0x2e,0x20,0x53,0x74,0x61,0x74,0x75,0x73,0x20,0x43,0x6f,0x64,0x65,0x3a,0x20,0x22,0x2b,0x6f,0x2e,0x73,0x74,0x61,0x74,0x75,0x73,0x29,0x7d,0x29,0x2e,0x63,0x61,0x74,0x63,0x68,0x28,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x28,0x6f,0x29,0x7b,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x69,0x6e,0x6e,0x65,0x72,0x48,0x54,0x4d,0x4c,0x2b,0x3d,0x22,0x3c,0x64,0x69,0x76,0x3e,0x3e,0x3e,0x20,0x22,0x2b,0x6f,0x2e,0x6d,0x65,0x73,0x73,0x61,0x67,0x65,0x2b,0x22,0x20,0x3c,0x3c,0x3c,0x2f,0x64,0x69,0x76,0x3e,0x22,0x2c,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x5f,0x6f,0x6e,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x61,0x75,0x74,0x6f,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x22,0x29,0x2e,0x63,0x68,0x65,0x63,0x6b,0x65,0x64,0x2c,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x54,0x6f,0x70,0x3d,0x64,0x6f,0x63,0x75,0x6d,0x65,0x6e,0x74,0x2e,0x67,0x65,0x74,0x45,0x6c,0x65,0x6d,0x65,0x6e,0x74,0x42,0x79,0x49,0x64,0x28,0x22,0x63,0x6f,0x70,0x79,0x54,0x65,0x78,0x74,0x5f,0x31,0x22,0x29,0x2e,0x73,0x63,0x72,0x6f,0x6c,0x6c,0x48,0x65,0x69,0x67,0x68,0x74,0x2c,0x65,0x3d,0x35,0x65,0x33,0x2c,0x63,0x6c,0x65,0x61,0x72,0x49,0x6e,0x74,0x65,0x72,0x76,0x61,0x6c,0x28,0x73,0x29,0x2c,0x6c,0x6f,0x6f,0x70,0x44,0x65,0x4c,0x6f,0x6f,0x70,0x28,0x65,0x2c,0x30,0x29,0x7d,0x29,0x2c,0x6c,0x3d,0x31,0x29,0x7d,0x2c,0x65,0x29,0x7d,0}; #endif // WEBSERVER_INCLUDE_JS -#ifdef WEBSERVER_INCLUDE_JS +#if defined(WEBSERVER_INCLUDE_JS) && defined(USES_P165) static const char p165_digit_script[] PROGMEM = { - "function sh_col(a,b,d){s=document.getElementById(a).getElementsByTagName('TR');for(r of s)" + "function elId(e){return document.getElementById(e)}" + "function elTR(e){return elId(e)?.getElementsByTagName('TR')}" + "function sh_col(a,b,d){s=elTR(a);for(r of s)" " {c=r?.children[b];if(c?.tagName=='TD'){c.style.width=20;c.style.display=d?'inline-block':'none'}}};" - "function sh_row(a,b,c){r=document.getElementById(a).getElementsByTagName('TR')[b];if(r)r.style.display=c?'block':'none'};" - "function set_td(a,b,d,e,f){c=document.getElementById(a)?.getElementsByTagName('TR')[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" + "function sh_row(a,b,c){r=elTR(a)[b];if(r)r.style.display=c?'block':'none'};" + "function set_td(a,b,d,e,f){c=elTR(a)[d].children[e];if(c?.tagName=='TD')c.innerHTML=b?f:' '};" "function chWdth(a,b,c,d,e){for(t=b;t☸';else z='☸';" " for(t=c;t0,16,9,''+a)};" - "function chAddn(a,b,c,d,e){for(t=b;t0&&d,8,0,''+a)};" - "function dgts(a,b){z=0;for(q=0;q0,8,0,''+a)};" + "function dgts(a,b){z=0;for(q=0;q content if fill is true, else non-breaking space, minimal checks - c = document.getElementById(tbl)?.getElementsByTagName('TR')[row].children[col]; + c = elTR(tbl)[row].children[col]; if (c?.tagName == 'TD') c.innerHTML = fill ? td : ' '; }; // Next functions: 4th & 5th parameters are used only in 1 function, but to keep the calling logic simple, leave it there @@ -49,20 +55,19 @@ function chDecp(value, base, count, max, colr) { // Change Decimal point pixels set_td('dgtbl' + t, value > 0, 16, 9, '' + value); }; function chAddn(value, base, count, max, colr) { // Change Additional pixels - for (t = base; t < base + count; t++) - set_td('dxtbl' + t, value > 0 && max, 8, 0, '' + value); + set_td('dxtbl' + base + count, value > 0 && max, 8, 0, '' + value); }; function dgts(gps, flds) { // Update pixel counts per group and total pixels z = 0; for (q = 0; q < gps; q++) { v = []; for (f of flds) { - v.push(parseInt(document.getElementById(f + q * 10).value)); + v.push(parseInt(elId(f + q * 10).value)); } - c = document.getElementById('crnr' + q * 10).checked; + c = elId('crnr' + q * 10).checked; x = v[5] * (3 * v[0] + 4 * v[1] + (c ? 6 : 0) + v[2]) + v[3]; z += x + v[4]; - document.getElementById('totp' + q * 10).value = x; + elId('totp' + q * 10).value = x; } - document.getElementById('totpx').value = z; + elId('totpx').value = z; }; From 044dd434ad821661037a080d649510dce0f5328f Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 15 Sep 2024 16:54:02 +0200 Subject: [PATCH 246/367] [P165] Bugfixes and javascript optimizations --- src/src/PluginStructs/P165_data_struct.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 15fa758dd3..3d6c98fb19 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -506,7 +506,7 @@ bool P165_data_struct::plugin_webform_load(struct EventStruct *event) { { // Bind handlers on input fields to update the 7 segment simulation and digit counts addHtml(F("\n")); } From 9b0fa9e53c73efc8044cb4ff654e2bd4cdd4aa1a Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 17:23:19 +0200 Subject: [PATCH 247/367] [Email] Set timeout on the connection itself --- src/_N001_Email.cpp | 8 +- .../DataStructs/NotificationSettingsStruct.h | 2 +- src/src/WebServer/NotificationPage.cpp | 89 ++++++++++++------- 3 files changed, 63 insertions(+), 36 deletions(-) diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 3b63be66b2..f23e53433f 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -134,11 +134,11 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 - client.setTimeout((CONTROLLER_CLIENTTIMEOUT_MAX + 500) / 1000); // in seconds!!!! + client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! Client *pClient = &client; - pClient->setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); + pClient->setTimeout(notificationsettings.Timeout_ms); # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - client.setTimeout(CONTROLLER_CLIENTTIMEOUT_MAX); // in msec as it should be! + client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS # ifndef BUILD_NO_DEBUG @@ -172,7 +172,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" ); - uint16_t clientTimeout = notificationsettings.Timeout * 1000; // Convert to mS. + uint16_t clientTimeout = notificationsettings.Timeout_ms; if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; diff --git a/src/src/DataStructs/NotificationSettingsStruct.h b/src/src/DataStructs/NotificationSettingsStruct.h index f4f23fc852..58827fcc70 100644 --- a/src/src/DataStructs/NotificationSettingsStruct.h +++ b/src/src/DataStructs/NotificationSettingsStruct.h @@ -31,7 +31,7 @@ struct NotificationSettingsStruct int8_t Pin2; char User[49]; char Pass[33]; - unsigned int Timeout; + unsigned int Timeout_ms; //its safe to extend this struct, up to 4096 bytes, default values in config are 0 }; diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index aa04973dc7..e76cc74948 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -12,20 +12,19 @@ * 2024-07-01 ThomasB : Start of changelog, older changes not logged. */ -#include "../WebServer/ESPEasy_WebServer.h" -#include "../WebServer/HTML_wrappers.h" -#include "../WebServer/Markup.h" -#include "../WebServer/Markup_Buttons.h" -#include "../WebServer/Markup_Forms.h" +# include "../WebServer/ESPEasy_WebServer.h" +# include "../WebServer/HTML_wrappers.h" +# include "../WebServer/Markup.h" +# include "../WebServer/Markup_Buttons.h" +# include "../WebServer/Markup_Forms.h" -#include "../DataStructs/ESPEasy_EventStruct.h" -#include "../DataStructs/NotificationSettingsStruct.h" +# include "../DataStructs/ESPEasy_EventStruct.h" +# include "../DataStructs/NotificationSettingsStruct.h" -#include "../Helpers/ESPEasy_Storage.h" - -#include "../Globals/ESPEasy_Scheduler.h" -#include "../Globals/Settings.h" +# include "../Helpers/ESPEasy_Storage.h" +# include "../Globals/ESPEasy_Scheduler.h" +# include "../Globals/Settings.h" // ******************************************************************************** @@ -33,13 +32,13 @@ // ******************************************************************************** -#include "../Globals/NPlugins.h" +# include "../Globals/NPlugins.h" void handle_notifications() { - #ifndef BUILD_NO_RAM_TRACKER + # ifndef BUILD_NO_RAM_TRACKER checkRAM(F("handle_notifications")); - #endif + # endif // ifndef BUILD_NO_RAM_TRACKER if (!isLoggedIn()) { return; } navMenuIndex = MENU_INDEX_NOTIFICATIONS; @@ -52,19 +51,19 @@ void handle_notifications() { // 'index' value in the URL - uint8_t notificationindex = getFormItemInt(F("index"), 0); + uint8_t notificationindex = getFormItemInt(F("index"), 0); boolean notificationindexNotSet = notificationindex == 0; --notificationindex; const int notification_webarg_value = getFormItemInt(F("notification"), -1); - if (!notificationindexNotSet && notification_webarg_value != -1) + if (!notificationindexNotSet && (notification_webarg_value != -1)) { const npluginID_t notification = npluginID_t::toPluginID(notification_webarg_value); + if (notification == INVALID_N_PLUGIN_ID) { - Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; + Settings.Notification[notificationindex] = INVALID_N_PLUGIN_ID.value; Settings.NotificationEnabled[notificationindex] = false; - } else { MakeNotificationSettings(NotificationSettings); @@ -82,8 +81,10 @@ void handle_notifications() { String dummyString; NPlugin_ptr[NotificationProtocolIndex](NPlugin::Function::NPLUGIN_WEBFORM_SAVE, 0, dummyString); } - NotificationSettings.Port = getFormItemInt(F("port"), 0); - NotificationSettings.Timeout = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM/1000); + NotificationSettings.Port = getFormItemInt(F("port"), 0); + + // FIXME TD-er: Must convert this to msec in the user interface as every other timeout in ESPEasy is in msec. + NotificationSettings.Timeout_ms = 1000 * getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM / 1000); NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1); NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1); Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled")); @@ -97,7 +98,8 @@ void handle_notifications() { strncpy_webserver_arg(NotificationSettings.Body, F("body")); } } - addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), sizeof(NotificationSettingsStruct))); + addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), + sizeof(NotificationSettingsStruct))); } // Save the settings. @@ -123,7 +125,7 @@ void handle_notifications() { { html_table_class_multirow(); html_TR(); - html_table_header(F(""), 70); + html_table_header(F(""), 70); html_table_header(F("Nr"), 50); html_table_header(F("Enabled"), 100); html_table_header(F("Service")); @@ -150,8 +152,8 @@ void handle_notifications() { addEnabled(Settings.NotificationEnabled[x]); html_TD(); - uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); - String NotificationName = F("(plugin not found?)"); + uint8_t NotificationProtocolIndex = getNProtocolIndex(npluginID_t::toPluginID(Settings.Notification[x])); + String NotificationName = F("(plugin not found?)"); if (validNProtocolIndex(NotificationProtocolIndex)) { @@ -161,20 +163,21 @@ void handle_notifications() { html_TD(); addHtml(NotificationSettings.Server); html_TD(); - if (NotificationSettings.Port){ + + if (NotificationSettings.Port) { addHtmlInt(NotificationSettings.Port); } else { - //MFD: we display the GPIO + // MFD: we display the GPIO addGpioHtml(NotificationSettings.Pin1); - if (NotificationSettings.Pin2>=0) + if (NotificationSettings.Pin2 >= 0) { html_BR(); addGpioHtml(NotificationSettings.Pin2); } } } - else{ + else { html_TD(3); } } @@ -217,9 +220,33 @@ void handle_notifications() { { addFormTextBox(F("Domain"), F("domain"), NotificationSettings.Domain, sizeof(NotificationSettings.Domain) - 1); addFormTextBox(F("Server"), F("server"), NotificationSettings.Server, sizeof(NotificationSettings.Server) - 1); - addFormNumericBox(F("Port"), F("port"), NotificationSettings.Port, 1, 65535, F("NOTE: SSL/TLS servers NOT supported!")); - if (NotificationSettings.TimeoutNPLUGIN_001_MAX_TM/1000) {NotificationSettings.Timeout=NPLUGIN_001_DEF_TM/1000;} - addFormNumericBox(F("Timeout"), F("timeout"), NotificationSettings.Timeout, NPLUGIN_001_MIN_TM/1000, NPLUGIN_001_MAX_TM/1000, F("Maximum Server Response Time)")); + addFormNumericBox( + F("Port"), F("port"), + NotificationSettings.Port, + 1, + 65535 +# if FEATURE_TOOLTIPS + , F("NOTE: SSL/TLS servers NOT supported!") +# endif // if FEATURE_TOOLTIPS + ); + + if ((NotificationSettings.Timeout_ms < NPLUGIN_001_MIN_TM) || + (NotificationSettings.Timeout_ms > NPLUGIN_001_MAX_TM)) + { + NotificationSettings.Timeout_ms = NPLUGIN_001_DEF_TM; + } + + // FIXME TD-er: Must convert to msec as every other timeout used/configured in ESPEasy is in msec + addFormNumericBox( + F("Timeout"), F("timeout"), + NotificationSettings.Timeout_ms / 1000, + NPLUGIN_001_MIN_TM / 1000, + NPLUGIN_001_MAX_TM / 1000 +# if FEATURE_TOOLTIPS + , F("Maximum Server Response Time") +# endif // if FEATURE_TOOLTIPS + ); + addUnit(F("Seconds")); addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1); From 0d81a41dfc894e1a7a7b17347a25ca5656839faf Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 17:24:41 +0200 Subject: [PATCH 248/367] Do not apply nr. of decimals before calculating formula (#5123) Fixes: #5123 --- src/src/DataStructs/UserVarStruct.cpp | 9 +++++++-- src/src/DataTypes/TaskValues_Data.cpp | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/src/DataStructs/UserVarStruct.cpp b/src/src/DataStructs/UserVarStruct.cpp index 1c97650ada..671dfd5181 100644 --- a/src/src/DataStructs/UserVarStruct.cpp +++ b/src/src/DataStructs/UserVarStruct.cpp @@ -379,7 +379,10 @@ const TaskValues_Data_t * UserVarStruct::getRawOrComputed( if ((it == _computed.end()) || !it->second.isSet(varNr)) { // Try to compute values which do have a formula but not yet a 'computed' value cached. // FIXME TD-er: This may yield unexpected results when formula contains references to %pvalue% - const int nrDecimals = Cache.getTaskDeviceValueDecimals(taskIndex, varNr); + + + // Should not apply set nr. of decimals when calculating a formula + const uint8_t nrDecimals = 255; const String value = getAsString(taskIndex, varNr, sensorType, nrDecimals, true); constexpr bool applyNow = true; @@ -477,7 +480,9 @@ bool UserVarStruct::applyFormulaAndSet(taskIndex_t taskIndex TaskValues_Data_t tmp; tmp.set(varNr, value, sensorType); - const uint8_t nrDecimals = Cache.getTaskDeviceValueDecimals(taskIndex, varNr); + + // Should not apply set nr. of decimals when calculating a formula + const uint8_t nrDecimals = 255; const String value_str = tmp.getAsString(varNr, sensorType, nrDecimals); constexpr bool applyNow = false; diff --git a/src/src/DataTypes/TaskValues_Data.cpp b/src/src/DataTypes/TaskValues_Data.cpp index 961b630b40..ae0a6c7b91 100644 --- a/src/src/DataTypes/TaskValues_Data.cpp +++ b/src/src/DataTypes/TaskValues_Data.cpp @@ -253,11 +253,19 @@ String TaskValues_Data_t::getAsString(uint8_t varNr, Sensor_VType sensorType, u String result; if (isFloatOutputDataType(sensorType)) { - result = toString(getFloat(varNr), nrDecimals); + const float value = getFloat(varNr); + if (nrDecimals = 255) { + nrDecimals = maxNrDecimals_fpType(value); + } + result = toString(value, nrDecimals); #if FEATURE_EXTENDED_TASK_VALUE_TYPES #if FEATURE_USE_DOUBLE_AS_ESPEASY_RULES_FLOAT_TYPE } else if (isDoubleOutputDataType(sensorType)) { - result = doubleToString(getDouble(varNr), nrDecimals); + const double value = getDouble(varNr); + if (nrDecimals = 255) { + nrDecimals = maxNrDecimals_fpType(value); + } + result = doubleToString(value, nrDecimals); #endif #endif } else if (sensorType == Sensor_VType::SENSOR_TYPE_ULONG) { From cffe50091341f4c1a905026054063460d1e58175 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 19:40:50 +0200 Subject: [PATCH 249/367] [GPIO] Revert some changes regarding send bootstate on P001 --- src/src/Helpers/_Plugin_Helper_GPIO.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/src/Helpers/_Plugin_Helper_GPIO.cpp b/src/src/Helpers/_Plugin_Helper_GPIO.cpp index 7624ed121f..2093b1903f 100644 --- a/src/src/Helpers/_Plugin_Helper_GPIO.cpp +++ b/src/src/Helpers/_Plugin_Helper_GPIO.cpp @@ -68,14 +68,6 @@ bool GPIO_plugin_helper_data_t::init( newStatus.task++; } - - // @giig1967g-20181022: set initial UserVar of the switch - if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { - UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); - } else { - UserVar.setFloat(event->TaskIndex, 0, newStatus.state); - } - // if boot pinState must be send, inverse default pinState // this is done to force the trigger in PLUGIN_TEN_PER_SECOND if (_sendBootState) { @@ -90,6 +82,14 @@ bool GPIO_plugin_helper_data_t::init( // setPinState(PLUGIN_ID_009, _mcpPin, PIN_MODE_INPUT, switchstate[event->TaskIndex]); savePortStatus(_portStatus_key, newStatus); + + // @giig1967g-20181022: set initial UserVar of the switch + if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { + UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); + } else { + UserVar.setFloat(event->TaskIndex, 0, newStatus.state); + } + return true; } From 6bbc83066837268ab0a8d7947b0d8e368b0fb5d0 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 20:35:56 +0200 Subject: [PATCH 250/367] Add .platformio to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 3965436a7e..a7d3d15ffb 100644 --- a/.gitignore +++ b/.gitignore @@ -69,3 +69,5 @@ src/CustomIR.h docs/source/Plugin/_plugin_sets_overview.repl *.PVS-Studio.stacktrace.txt + +.platformio/ From b5a7c901f6edae106b52fe41acd447419a958a53 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 15 Sep 2024 19:40:50 +0200 Subject: [PATCH 251/367] [GPIO] Revert some changes regarding send bootstate on P001 --- src/src/Helpers/_Plugin_Helper_GPIO.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/src/Helpers/_Plugin_Helper_GPIO.cpp b/src/src/Helpers/_Plugin_Helper_GPIO.cpp index 7624ed121f..2093b1903f 100644 --- a/src/src/Helpers/_Plugin_Helper_GPIO.cpp +++ b/src/src/Helpers/_Plugin_Helper_GPIO.cpp @@ -68,14 +68,6 @@ bool GPIO_plugin_helper_data_t::init( newStatus.task++; } - - // @giig1967g-20181022: set initial UserVar of the switch - if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { - UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); - } else { - UserVar.setFloat(event->TaskIndex, 0, newStatus.state); - } - // if boot pinState must be send, inverse default pinState // this is done to force the trigger in PLUGIN_TEN_PER_SECOND if (_sendBootState) { @@ -90,6 +82,14 @@ bool GPIO_plugin_helper_data_t::init( // setPinState(PLUGIN_ID_009, _mcpPin, PIN_MODE_INPUT, switchstate[event->TaskIndex]); savePortStatus(_portStatus_key, newStatus); + + // @giig1967g-20181022: set initial UserVar of the switch + if ((newStatus.state != -1) && Settings.TaskDevicePin1Inversed[event->TaskIndex]) { + UserVar.setFloat(event->TaskIndex, 0, !newStatus.state); + } else { + UserVar.setFloat(event->TaskIndex, 0, newStatus.state); + } + return true; } From 34ab470bbaa0d9a5246b5a962ff745842c1fc88a Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 15 Sep 2024 21:36:46 +0200 Subject: [PATCH 252/367] [P113] Improve UI selection of ROI area for touch devices, code improvements --- src/_P113_VL53L1X.ino | 17 ++++++++++--- src/src/PluginStructs/P113_data_struct.cpp | 29 ++++------------------ src/src/PluginStructs/P113_data_struct.h | 4 +++ src/src/Static/WebStaticData.cpp | 10 ++++++++ src/src/Static/WebStaticData.h | 27 ++++++++++++++++++++ 5 files changed, 59 insertions(+), 28 deletions(-) diff --git a/src/_P113_VL53L1X.ino b/src/_P113_VL53L1X.ino index e6b64fff79..6186a56980 100644 --- a/src/_P113_VL53L1X.ino +++ b/src/_P113_VL53L1X.ino @@ -6,6 +6,8 @@ // ####################################################################################################### /** Changelog: + * 2024-09-15 tonhuisman: Fix ROI selection for tablet/mobile devices (touch-only) as click&drag doesn't work there. + * Fix some bugs, and optimize the js code a bit. Move static minified js to WebStaticData.h * 2024-09-13 tonhuisman: ROI settings can be selected by dragging the Optical Center Index matrix and the O.C. can be selected * by double-clicking or alt-clicking an index. With validation. * 2024-07-29 tonhuisman: Add Region of Interest (ROI) settings for reducing the Field of View (FoV) of the sensor @@ -160,7 +162,14 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) html_TR_TD(); addHtmlDiv(F("note"), F("Alt-Click/Dbl-Click to select Optical Center.")); - int rws = 7; // Above should be fixed number of rows, matching with ~80% selection area + html_TR_TD(); + addHtml(F(" ")); + + addFormCheckBox(F("Use click-lock"), F("lck"), false); + html_TR_TD(); + addHtmlDiv(F("note"), F("(For touch devices, not saved.)")); + + int rws = 10; // Above should be fixed number of rows, matching with ~80% selection area for (; rws < 16; ++rws) { html_TR_TD(); @@ -258,11 +267,11 @@ boolean Plugin_113(uint8_t function, struct EventStruct *event, String& string) # if P113_USE_ROI void P113_CheckMinMaxValues(struct EventStruct *event) { - if (0 == P113_ROI_X) { P113_ROI_X = 16; } // Default + if (0 == P113_ROI_X) { P113_ROI_X = 16; } // Default - if (0 == P113_ROI_Y) { P113_ROI_Y = 16; } // Default + if (0 == P113_ROI_Y) { P113_ROI_Y = 16; } // Default - if (0 == P113_OPT_CENTER) { P113_OPT_CENTER = 199; } // Optical Center @ Center of sensor. See matrix in documentation + if (0 == P113_OPT_CENTER) { P113_OPT_CENTER = 199; } // Optical Center @ Center of sensor. See matrix in documentation if ((P113_ROI_X > 10) || (P113_ROI_Y > 10)) { P113_OPT_CENTER = 199; } // Driver behavior } diff --git a/src/src/PluginStructs/P113_data_struct.cpp b/src/src/PluginStructs/P113_data_struct.cpp index 30c07cbc86..43bc3eb966 100644 --- a/src/src/PluginStructs/P113_data_struct.cpp +++ b/src/src/PluginStructs/P113_data_struct.cpp @@ -228,34 +228,15 @@ void P113_data_struct::drawSelectionArea(uint8_t roix, uint8_t roiy, uint8_t opt } void P113_data_struct::loadJavascript() { - html_add_script(false); - addHtml(F( // TODO Move to WebStaticData.h - "function p113_main(){" - "function elId(e){return document.getElementById(e)}" - "function elVal(e){return elId(e).value}" - "function cntr(e){if(elVal('roix')>10||elVal('roiy')>10)return;let sc=[];const cs=vi.querySelectorAll('.cel');cs.forEach((c)=>{const b=c.getBoundingClientRect();if(e.x>=b.left&&e.y>=b.top&&e.x-scx<=b.right&&e.y-scy<=b.bottom)sc.push(c)});if(sc.length===1){elId('optc').value=parseInt(sc[0].textContent);upDsp()}}" - "const vi=elId('vi');const sl=elId('sL');let imd=false;let ims=false;let st={},se={};let sr={};let oC=199;let xr=16;let yr=16;let ptg=0;let scx=vi.offsetWidth-vi.clientWidth;let scy=vi.offsetHeight-vi.clientHeight;let sm={w:96,h:96,};" - "vi.addEventListener('mousedown',(e)=>{if(e.altKey){cntr(e)}else{imd=true;if(!ims){se={x:null,y:null};clr()}st={x:e.x+window.scrollX-vi.offsetLeft,y:e.y+window.scrollY-vi.offsetTop}}});" - "vi.addEventListener('mouseup',(e)=>{if(!imd)return;imd=false;const vc=vi.getBoundingClientRect();se={x:e.x+window.scrollX-vi.offsetLeft,y:e.y+window.scrollY-vi.offsetTop};sl.style.top=`0px`;sl.style.left=`0px`;sl.style.bottom='unset';sl.style.right='unset';sl.style.visibility='hidden';if(Math.abs(se.x-st.x){cntr(e)});" - "vi.addEventListener('mousemove',(e)=>{if(!imd)return;se={x:e.x+window.scrollX,y:e.y+window.scrollY};sr={x1:Math.min(st.x,se.x),y1:Math.min(st.y,se.y),x2:Math.max(st.x,se.x)+scx,y2:Math.max(st.y,se.y)+scy};sl.style.top=`${scx+Math.min(sr.y1,sr.y2)}px`;sl.style.left=`${scy+Math.min(sr.x1,sr.x2)}px`;sl.style.bottom=`${vi.offsetHeight+vi.offsetTop-Math.max(sr.y1,sr.y2)}px`;sl.style.right=`${vi.offsetWidth+vi.offsetLeft-Math.max(sr.x1,sr.x2)}px`;sl.style.visibility='visible';if(!ims)clr()});" - "function clr(){vi.querySelectorAll('.cel').forEach((c)=>{c.classList.remove('sel');c.classList.remove('oc')})}" - "function gSelCls(){let sc=[];const cl=vi.querySelectorAll('.cel');sl.style.visibility='visible';cl.forEach((c)=>{const b=c.getBoundingClientRect();wx=window.scrollX;wy=window.scrollY;if(sr.x1-wx<=((b.left)-vi.offsetLeft+16)&&sr.y1-wy<=((b.top)-vi.offsetTop+16)&&(sr.x2-wx-scx+16)>=(b.right)&&(sr.y2-wy-scy+16)>=b.bottom)sc.push(c)});if(sc.length>0){[ly,lx]=oiToXy(sc[0].textContent);[ry,rx]=oiToXy(sc[sc.length-1].textContent);nx=lx+Math.floor((rx-lx)/2+0.5);ny=ly+Math.floor((ry-ly)/2);oC=gSP(ny,nx);xr=1+rx-lx;yr=1+ry-ly;ptg=1;elId('roix').value=xr;elId('roiy').value=yr;elId('optc').value=oC;ptg=0}return sc}" - "function oiToXy(oidx){x=0;y=0;o=parseInt(oidx);if(o<128){y+=8;o=127-o}else{o-=128}x=o>>3;y+=o&7;return[y,x]}" - "function pntSel(roix,roiy,oCen){if(!ptg){lx=0;ly=0;rx=15;ry=15;oo=oCen;if(roix>10||roiy>10){oCen=199}[ocy,ocx]=oiToXy(oCen);hx=Math.floor(roix/2);hy=Math.floor(roiy/2);dx=roix%2!=0?0:1;dy=roiy%2!=0?0:1;if((roix<16)||(roiy<16)){lx=ocx-hx;rx=ocx+hx-dx;ly=ocy-hy+dy;ry=ocy+hy;if((rx+1)-(lx+1)<4){d=3-((rx+1)-(lx+1));if(rx<(15-d)){rx+=d}else{lx-=d;ocx-=d/2}}if(rx>15||lx<0){d=lx<0?lx:rx-15;rx-=d;lx-=d;ocx-=d}if((ry+1)-(ly+1)<4){d=3-((ry+1)-(ly+1));if(ry<((15-d))){ry+=d}else{ly-=d;ocy-=d/2}}if(ry>15||ly<0){d=ly<0?ly:ry-15;ry-=d;ly-=d;ocy-=d}}cl=vi.querySelectorAll('.cel');for(y=0;y<16;++y){for(x=0;x<16;++x){cc=cl[y*16+x].classList;if(x>=lx&&x<=rx&&y>=ly&&y<=ry)cc.add('sel');else cc.remove('sel');if(x==ocx&&y==ocy)cc.add('oc');else cc.remove('oc')}}oc=gSP(ocy,ocx);if(oc!=oo){ptg=1;elId('optc').value=oc;ptg=0}}}" - "function upDsp(){xr=elVal('roix');yr=elVal('roiy');optc=elVal('optc');pntSel(xr,yr,optc)}p113_main.upDsp=upDsp;" - "function gSP(y,x){o=((x&15)<<3)+(y&7);return(y>7)?127-o:128+o}document.addEventListener('resize',()=>{scx=vi.offsetWidth-vi.clientWidth;scy=vi.offsetHeight-vi.clientHeight})}" - - )); - html_add_script_end(); + serve_JS(JSfiles_e::P113_script); // Source in static/p113_script.js, minified script source in src/src/Static/WebStaticData.h } void P113_data_struct::loadCss() { - addHtml(F("")); } @@ -955,17 +977,24 @@ void getWiFi_RSSI_icon(int rssi, int width_pixels) int white_between_bar = (static_cast(width_pixels) / nbars) * 0.2f; if (white_between_bar < 1) { white_between_bar = 1; } - const int barWidth = (width_pixels - (nbars - 1) * white_between_bar) / nbars; + const int barWidth = (width_pixels - (nbars - 1) * white_between_bar) / nbars; const int svg_width_pixels = nbars * barWidth + (nbars - 1) * white_between_bar; write_SVG_image_header(svg_width_pixels, svg_width_pixels, true); - const float scale = 100.0f / svg_width_pixels; - const int bar_height_step = 100 / nbars; + const float scale = 100.0f / svg_width_pixels; + const int bar_height_step = 100 / nbars; for (int i = 0; i < nbars; ++i) { const unsigned int color = i < nbars_filled ? 0x07d : 0xBFa1a1a1; // Blue/Grey75% const int barHeight = (i + 1) * bar_height_step; - createSvgRect_noStroke(i < nbars_filled ? F("bar_highlight") : F("bar_dimmed"), color, i * (barWidth + white_between_bar) * scale, 100 - barHeight, barWidth, barHeight, 0, 0); + createSvgRect_noStroke(i < nbars_filled ? F("bar_highlight") : F("bar_dimmed"), + color, + i * (barWidth + white_between_bar) * scale, + 100 - barHeight, + barWidth, + barHeight, + 0, + 0); } addHtml(F("\n")); } @@ -990,6 +1019,7 @@ void getConfig_dat_file_layout() { if (SettingsType::getSettingsFile(settingsType) == SettingsType::SettingsFileEnum::FILE_CONFIG_type) { unsigned int color = SettingsType::getSVGcolor(settingsType); + if (SettingsType::getSettingsParameters(settingsType, 0, max_index, offset, max_size, struct_size)) { for (int i = 0; i < max_index; ++i) { @@ -1004,7 +1034,7 @@ void getConfig_dat_file_layout() { // Text labels constexpr float textXoffset = SVG_BAR_WIDTH + 2; - float textYoffset = yOffset + 0.9f * SVG_BAR_HEIGHT; + float textYoffset = yOffset + 0.9f * SVG_BAR_HEIGHT; createSvgTextElement(SettingsType::getSettingsFileName(SettingsType::Enum::TaskSettings_Type), textXoffset, textYoffset); addHtml(F("\n")); @@ -1076,28 +1106,28 @@ void getStorageTableSVG(SettingsType::Enum settingsType) { } void drawPartitionChartSVG( - float yOffset, - uint32_t realSize, - uint32_t partitionAddress, - uint32_t partitionSize, - unsigned int partitionColor, - const String& label, - const String& name) + float yOffset, + uint32_t realSize, + uint32_t partitionAddress, + uint32_t partitionSize, + unsigned int partitionColor, + const String& label, + const String& name) { createSvgHorRectPath(0xcdcdcd, 0, yOffset, realSize, SVG_BAR_HEIGHT - 2, realSize, SVG_BAR_WIDTH); createSvgHorRectPath(partitionColor, partitionAddress, yOffset, partitionSize, SVG_BAR_HEIGHT - 2, realSize, SVG_BAR_WIDTH); - float textXoffset = SVG_BAR_WIDTH + 2; + float textXoffset = SVG_BAR_WIDTH + 2; const float textYoffset = yOffset + 0.9f * SVG_BAR_HEIGHT; - createSvgTextElement(formatHumanReadable(partitionSize, 1024), textXoffset, textYoffset); + createSvgTextElement(formatHumanReadable(partitionSize, 1024), textXoffset, textYoffset); textXoffset = SVG_BAR_WIDTH + 60; - createSvgTextElement(label, textXoffset, textYoffset); + createSvgTextElement(label, textXoffset, textYoffset); textXoffset = SVG_BAR_WIDTH + 130; - createSvgTextElement(name, textXoffset, textYoffset); + createSvgTextElement(name, textXoffset, textYoffset); } -#ifdef ESP32 +# ifdef ESP32 -# include +# include void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor) { @@ -1118,11 +1148,11 @@ void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor) { do { _mypart = esp_partition_get(_mypartiterator); drawPartitionChartSVG( - yOffset, - realSize, - _mypart->address, - _mypart->size, - partitionColor, + yOffset, + realSize, + _mypart->address, + _mypart->size, + partitionColor, _mypart->label, getPartitionType(_mypart->type, _mypart->subtype)); yOffset += SVG_BAR_HEIGHT; @@ -1132,85 +1162,87 @@ void getPartitionTableSVG(uint8_t pType, unsigned int partitionColor) { esp_partition_iterator_release(_mypartiterator); } -#endif // ifdef ESP32 +# endif // ifdef ESP32 -#ifdef ESP8266 +# ifdef ESP8266 void getPartitionTableSVG() { // sketch / OTA / FS / EEPROM / RFcal / wifi const int nrPartitions = 6; - const int shiftY = 2; + const int shiftY = 2; + write_SVG_image_header(SVG_BAR_WIDTH + 250, nrPartitions * SVG_BAR_HEIGHT + shiftY); float yOffset = shiftY; for (int i = 0; i < nrPartitions; ++i) { const ESP8266_partition_type ptype = static_cast(i); - uint32_t partitionAddress = 0; - int32_t partitionSize = 0; - const int32_t partitionSector = getPartitionInfo(ptype, partitionAddress, partitionSize); + uint32_t partitionAddress = 0; + int32_t partitionSize = 0; + const int32_t partitionSector = getPartitionInfo(ptype, partitionAddress, partitionSize); - const __FlashStringHelper * label = F(""); + const __FlashStringHelper *label = F(""); String descr; unsigned int partitionColor = 0xab56e6; + switch (ptype) { case ESP8266_partition_type::sketch: - label = F("sketch"); + label = F("sketch"); partitionColor = 0xab56e6; break; case ESP8266_partition_type::ota: - label = F("ota"); + label = F("ota"); partitionColor = 0x5856e6; break; case ESP8266_partition_type::fs: - label = F("fs"); + label = F("fs"); partitionColor = 0xff7f00; - #ifdef USE_LITTLEFS + # ifdef USE_LITTLEFS descr = F("LittleFS"); - #else + # else // ifdef USE_LITTLEFS descr = F("SPIFFS"); - #endif + # endif // ifdef USE_LITTLEFS break; case ESP8266_partition_type::eeprom: - label = F("eeprom"); - descr = concat(F("sector:"), partitionSector); + label = F("eeprom"); + descr = concat(F("sector:"), partitionSector); partitionColor = 0x7fff00; break; case ESP8266_partition_type::rf_cal: - label = F("RFcal"); + label = F("RFcal"); partitionColor = 0xff007f; break; case ESP8266_partition_type::wifi: - label = F("WiFi"); + label = F("WiFi"); partitionColor = 0xff00ff; break; - } drawPartitionChartSVG( - yOffset, - getFlashRealSizeInBytes(), - partitionAddress, - partitionSize, - partitionColor, - label, - descr); + yOffset, + getFlashRealSizeInBytes(), + partitionAddress, + partitionSize, + partitionColor, + label, + descr); yOffset += SVG_BAR_HEIGHT; -/* - String debuglog = concat(F("partition: "), (i+1)); - debuglog += concat(F(" FS_st: "), formatToHex((uint32_t)&_FS_start)); - debuglog += concat(F(" FS_end: "), formatToHex((uint32_t)&_FS_end)); - debuglog += concat(F(" EEPROM: "), formatToHex((uint32_t)&_EEPROM_start)); - debuglog += concat(F(" addr: "), formatToHex(partitionAddress, 8)); - debuglog += concat(F(" part.size: "), partitionSize); - debuglog += concat(F(" label: "), label); - addLog(LOG_LEVEL_INFO, debuglog); -*/ + /* + String debuglog = concat(F("partition: "), (i+1)); + debuglog += concat(F(" FS_st: "), formatToHex((uint32_t)&_FS_start)); + debuglog += concat(F(" FS_end: "), formatToHex((uint32_t)&_FS_end)); + debuglog += concat(F(" EEPROM: "), formatToHex((uint32_t)&_EEPROM_start)); + debuglog += concat(F(" addr: "), formatToHex(partitionAddress, 8)); + debuglog += concat(F(" part.size: "), partitionSize); + debuglog += concat(F(" label: "), label); + addLog(LOG_LEVEL_INFO, debuglog); + */ } addHtml(F("\n")); } -#endif -#endif -bool webArg2ip(const __FlashStringHelper * arg, uint8_t *IP) { +# endif // ifdef ESP8266 +#endif // if FEATURE_CHART_STORAGE_LAYOUT + +bool webArg2ip(const __FlashStringHelper *arg, uint8_t *IP) { return str2ip(webArg(arg), IP); } diff --git a/src/src/WebServer/WebTemplateParser.cpp b/src/src/WebServer/WebTemplateParser.cpp index 942f70a66c..3bc338c55a 100644 --- a/src/src/WebServer/WebTemplateParser.cpp +++ b/src/src/WebServer/WebTemplateParser.cpp @@ -253,7 +253,7 @@ void WebTemplateParser::getErrorNotifications() { const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(x); if (Settings.ControllerEnabled[x] && - validProtocolIndex(ProtocolIndex) && + validProtocolIndex(ProtocolIndex) && getProtocolStruct(ProtocolIndex).usesMQTT) { ++nrMQTTenabled; } @@ -295,24 +295,27 @@ void WebTemplateParser::getWebPageTemplateVar(const String& varName) if (equals(varName, F("css"))) { serve_favicon(); -/* - bool defaultCssServed = false; - if (MENU_INDEX_SETUP == navMenuIndex) { - // Serve embedded CSS - defaultCssServed = serve_CSS_inline(); - } - if (!defaultCssServed) { -*/ - serve_CSS(CSSfiles_e::ESPEasy_default); -// } + /* + bool defaultCssServed = false; + + if (MENU_INDEX_SETUP == navMenuIndex) { + // Serve embedded CSS + defaultCssServed = serve_CSS_inline(); + } + if (!defaultCssServed) { + */ + serve_CSS(CSSfiles_e::ESPEasy_default); + + // } #if FEATURE_RULES_EASY_COLOR_CODE + if (!Settings.DisableRulesCodeCompletion() && - (MENU_INDEX_RULES == navMenuIndex || - MENU_INDEX_CUSTOM_PAGE == navMenuIndex)) { + ((MENU_INDEX_RULES == navMenuIndex) || + (MENU_INDEX_CUSTOM_PAGE == navMenuIndex))) { serve_CSS(CSSfiles_e::EasyColorCode_codemirror); } - #endif + #endif // if FEATURE_RULES_EASY_COLOR_CODE return; } break; @@ -353,9 +356,10 @@ void WebTemplateParser::getWebPageTemplateVar(const String& varName) #endif // if FEATURE_CHART_JS #if FEATURE_RULES_EASY_COLOR_CODE + if (!Settings.DisableRulesCodeCompletion() && - (MENU_INDEX_RULES == navMenuIndex || - MENU_INDEX_CUSTOM_PAGE == navMenuIndex)) { + ((MENU_INDEX_RULES == navMenuIndex) || + (MENU_INDEX_CUSTOM_PAGE == navMenuIndex))) { html_add_Easy_color_code_script(); } #endif // if FEATURE_RULES_EASY_COLOR_CODE From 9f60b5efbf66be1946f192fe0da134e8a4b05859 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 26 Sep 2024 11:02:22 +0200 Subject: [PATCH 283/367] [ESP-IDF5.3] Update to SDK IDF5.3.1/Arduino3.0.5 20240924 --- platformio_core_defs.ini | 8 ++++---- platformio_esp32_envs.ini | 14 +++++++------- platformio_esp32_solo1.ini | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 5200a45e78..7226073582 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -167,7 +167,7 @@ extra_scripts = ${esp82xx_common.extra_scripts} ; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4 ; Just for those who lost track of the extremely confusing numbering schema. ; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 -[core_esp32_IDF5_1__3_0_2_SPIFFS] +[core_esp32_IDF5_1_4__3_0_5_SPIFFS] platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2962/framework-arduinoespressif32-all-release_v5.1-33fbade.zip build_flags = -DESP32_STAGE @@ -192,10 +192,10 @@ build_flags = -DESP32_STAGE -Wnull-dereference lib_ignore = -; ESP_IDF 5.1 -[core_esp32_IDF5_3__3_0_4_LittleFS] +; ESP_IDF 5.3.1 +[core_esp32_IDF5_3_1__3_0_5_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2980/framework-arduinoespressif32-all-release_v5.3-3a06c6fe.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2990/framework-arduinoespressif32-all-release_v5.3-7c1246ae.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 13a27af924..00f2ecd9cc 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -6,7 +6,7 @@ [esp32_base] -extends = common, core_esp32_IDF5_1__3_0_2_SPIFFS +extends = common, core_esp32_IDF5_1_4__3_0_5_SPIFFS upload_speed = 460800 upload_before_reset = default_reset upload_after_reset = hard_reset @@ -19,7 +19,7 @@ extra_scripts = post:tools/pio/post_esp32.py ; pre:tools/pio/apply_patches.py build_unflags = -Wall -fno-lto -build_flags = ${core_esp32_IDF5_1__3_0_2_SPIFFS.build_flags} +build_flags = ${core_esp32_IDF5_1_4__3_0_5_SPIFFS.build_flags} ; ${mqtt_flags.build_flags} -DMQTT_MAX_PACKET_SIZE=2048 -DCONFIG_FREERTOS_ASSERT_DISABLE @@ -30,11 +30,11 @@ build_flags = ${core_esp32_IDF5_1__3_0_2_SPIFFS.build_flags} -DCORE_DEBUG_LEVEL=ARDUHAL_LOG_LEVEL_NONE monitor_filters = esp32_exception_decoder lib_ignore = - ${core_esp32_IDF5_1__3_0_2_SPIFFS.lib_ignore} + ${core_esp32_IDF5_1_4__3_0_5_SPIFFS.lib_ignore} [esp32_base_idf5] -extends = common, core_esp32_IDF5_3__3_0_4_LittleFS +extends = common, core_esp32_IDF5_3_1__3_0_5_LittleFS upload_speed = 460800 upload_before_reset = default_reset upload_after_reset = hard_reset @@ -51,7 +51,7 @@ extra_scripts = post:tools/pio/post_esp32.py ; However LTO cannot optimize builds with text section literals and thus will result in quite a lot larger builds (80k - 140k larger) build_unflags = -Wall -fno-lto -build_flags = ${core_esp32_IDF5_3__3_0_4_LittleFS.build_flags} +build_flags = ${core_esp32_IDF5_3_1__3_0_5_LittleFS.build_flags} ; ${mqtt_flags.build_flags} -DMQTT_MAX_PACKET_SIZE=2048 -DCONFIG_FREERTOS_ASSERT_DISABLE @@ -65,7 +65,7 @@ build_flags = ${core_esp32_IDF5_3__3_0_4_LittleFS.build_flags} -DLWIP_IPV6=1 monitor_filters = esp32_exception_decoder lib_ignore = - ${core_esp32_IDF5_3__3_0_4_LittleFS.lib_ignore} + ${core_esp32_IDF5_3_1__3_0_5_LittleFS.lib_ignore} ; -flto cannot be used for ESP32 C3! @@ -116,7 +116,7 @@ board_build.filesystem = littlefs lib_ignore = ${esp32_always.lib_ignore} ESP32_ping ESP32 BLE Arduino - ${core_esp32_IDF5_3__3_0_4_LittleFS.lib_ignore} + ${core_esp32_IDF5_3_1__3_0_5_LittleFS.lib_ignore} [esp32_IRExt] diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index 111adf9300..a510eedd27 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -1,9 +1,9 @@ -; IDF 5.1.2 +; IDF 5.3.1 [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2981/framework-arduinoespressif32-solo1-release_v5.3-3a06c6fe.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2987/framework-arduinoespressif32-solo1-release_v5.3-7c1246ae.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 34d434efd9e7e70fcdfbe8983897ec0bd9c2f42e Mon Sep 17 00:00:00 2001 From: uwekaditz Date: Thu, 26 Sep 2024 12:07:45 +0200 Subject: [PATCH 284/367] Removed unneeded string init, code simplified --- src/src/ESPEasyCore/ESPEasyRules.cpp | 2 +- src/src/Helpers/StringConverter.cpp | 2 +- src/src/Helpers/StringParser.cpp | 19 +++++++++---------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasyRules.cpp b/src/src/ESPEasyCore/ESPEasyRules.cpp index 901853d086..a1792b37fa 100644 --- a/src/src/ESPEasyCore/ESPEasyRules.cpp +++ b/src/src/ESPEasyCore/ESPEasyRules.cpp @@ -465,7 +465,7 @@ void parse_string_commands(String& line) { bool mustReplaceMaskedChars = false; bool mustReplaceEscapedBracket = false; bool mustReplaceEscapedCurlyBracket = false; - String MaskEscapedBracket = ""; + String MaskEscapedBracket; if (hasEscapedCharacter(line,'(') || hasEscapedCharacter(line,')')) { // replace the \( and \) with other characters to mask the escaped brackets so we can continue parsing. diff --git a/src/src/Helpers/StringConverter.cpp b/src/src/Helpers/StringConverter.cpp index 54b8589e09..791bd14de9 100644 --- a/src/src/Helpers/StringConverter.cpp +++ b/src/src/Helpers/StringConverter.cpp @@ -1317,7 +1317,7 @@ void parseSingleControllerVariable(String & s, void parseSystemVariables(String& s, bool useURLencode) { - String MaskEscapedPercent = ""; + String MaskEscapedPercent; bool mustReplaceEscapedPercent = hasEscapedCharacter(s, '%'); if (mustReplaceEscapedPercent) { diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index df9601146c..f23d2f98e0 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -28,20 +28,19 @@ \*********************************************************************************************/ bool hasEscapedCharacter(String& str, const char EscapeChar) { - String EscStr = F("\\"); - EscStr += EscapeChar; + const String EscStr = concat(F("\\"), EscapeChar); return (str.indexOf(EscStr)>=0); } void stripEscapeCharacters(String& str) { - str.replace(F("\\%"), "%"); // Allow system vars to be passed in by using \% instead of % - str.replace(F("\\["), "["); // Allow task values to be passed in by using \[ instead of [ - str.replace(F("\\]"), "]"); // Allow task values to be passed in by using \] instead of ] - str.replace(F("\\{"), "{"); // Allow commands to be passed in by using \{ instead of { - str.replace(F("\\}"), "}"); // Allow commands to be passed in by using \} instead of } - str.replace(F("\\("), "("); // Allow commands to be passed in by using \( instead of ( - str.replace(F("\\)"), ")"); // Allow commands to be passed in by using \) instead of ) + const uint8_t braces[] = { '%', '[', ']', '{', '}', '(', ')' }; + constexpr uint8_t nrbraces = NR_ELEMENTS(braces); + + for (uint8_t i = 0; i < nrbraces; ++i) { + const String s(concat(F("\\"), braces[i])); + str.replace(s, s.substring(2)); + } } String parseTemplate(String& tmpString) @@ -80,7 +79,7 @@ String parseTemplate_padded(String& tmpString, uint8_t minimal_lineSize, bool us int lastStartpos = 0; int endpos = 0; bool mustReplaceEscapedSquareBracket = false; - String MaskEscapedBracket = ""; + String MaskEscapedBracket; if (hasEscapedCharacter(tmpString, '[') || hasEscapedCharacter(tmpString, ']')) { // replace the \[ and \] with other characters to mask the escaped square brackets so we can continue parsing. From 1cafaab48d1f990e34fa715850218996ecd46cb1 Mon Sep 17 00:00:00 2001 From: uwekaditz Date: Thu, 26 Sep 2024 13:36:26 +0200 Subject: [PATCH 285/367] BUG: simplified stripEscapeCharacters() did not work --- src/src/Helpers/StringParser.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/Helpers/StringParser.cpp b/src/src/Helpers/StringParser.cpp index f23d2f98e0..6f1eff7631 100644 --- a/src/src/Helpers/StringParser.cpp +++ b/src/src/Helpers/StringParser.cpp @@ -34,12 +34,12 @@ bool hasEscapedCharacter(String& str, const char EscapeChar) void stripEscapeCharacters(String& str) { - const uint8_t braces[] = { '%', '[', ']', '{', '}', '(', ')' }; + const char braces[] = { '%', '[', ']', '{', '}', '(', ')' }; constexpr uint8_t nrbraces = NR_ELEMENTS(braces); for (uint8_t i = 0; i < nrbraces; ++i) { const String s(concat(F("\\"), braces[i])); - str.replace(s, s.substring(2)); + str.replace(s, s.substring(1)); } } From 4ae6c40b0fb6485cb74cfbe4a7ea235f2228ea14 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 27 Sep 2024 08:45:34 +0200 Subject: [PATCH 286/367] [ESP-IDF5.3] Update to 20240926 --- platformio_core_defs.ini | 2 +- platformio_esp32_solo1.ini | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index 7226073582..65d2038076 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -195,7 +195,7 @@ lib_ignore = ; ESP_IDF 5.3.1 [core_esp32_IDF5_3_1__3_0_5_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2990/framework-arduinoespressif32-all-release_v5.3-7c1246ae.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2996/framework-arduinoespressif32-all-release_v5.3-22a3b096.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index a510eedd27..4250b93411 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -3,7 +3,7 @@ [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2987/framework-arduinoespressif32-solo1-release_v5.3-7c1246ae.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2997/framework-arduinoespressif32-solo1-release_v5.3-22a3b096.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS From 4daaf59e07b42e02eb5f0629bf6b85fedfe8ae78 Mon Sep 17 00:00:00 2001 From: uwekaditz Date: Fri, 27 Sep 2024 17:17:59 +0200 Subject: [PATCH 287/367] Disable P040 (RFID - ID12LA/RDM6300) for PLUGIN_BUILD_IR_EXTENDED_NO_RX due to limited size --- src/src/CustomBuild/define_plugin_sets.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index ef174d37fc..c5b11c0543 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2078,6 +2078,9 @@ To create/register a plugin, you have to : #ifdef USES_P039 #undef USES_P039 // Environment - Thermocouple #endif + #ifdef USES_P040 + #undef USES_P040 // RFID - ID12LA/RDM6300 + #endif #endif // ifdef PLUGIN_BUILD_IR_EXTENDED_NO_RX // EXPERIMENTAL (playground) ####################### From 4a1a46a3aaf2edf35f27becf389b284d328585f2 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 28 Sep 2024 17:01:28 +0200 Subject: [PATCH 288/367] [P165] Small bugfix --- src/src/PluginStructs/P165_data_struct.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/src/PluginStructs/P165_data_struct.cpp b/src/src/PluginStructs/P165_data_struct.cpp index 238732dbea..48d4eebda8 100644 --- a/src/src/PluginStructs/P165_data_struct.cpp +++ b/src/src/PluginStructs/P165_data_struct.cpp @@ -1195,8 +1195,8 @@ bool P165_data_struct::plugin_once_a_second(struct EventStruct *event) { } if (_blinkDot && - (_output == P165_DISP_CLOCK24BLNK) || - (_output == P165_DISP_CLOCK12BLNK)) { + ((_output == P165_DISP_CLOCK24BLNK) || + (_output == P165_DISP_CLOCK12BLNK))) { showperiods[showmap[1 + _stdOffset]] = _timesep; // Blink dot on second digit } From 72618565f99632466f151b4113953b7b145411f8 Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sat, 28 Sep 2024 20:12:57 +0200 Subject: [PATCH 289/367] [P165] Update documentation --- docs/source/Plugin/P165.rst | 18 ++++++++++++++++-- .../Plugin/P165_DeviceConfiguration.png | Bin 93812 -> 90002 bytes .../Plugin/P165_StartingSegmentOption-g.png | Bin 0 -> 7827 bytes .../Plugin/P165_StartingSegmentOptions.png | Bin 0 -> 26754 bytes 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 docs/source/Plugin/P165_StartingSegmentOption-g.png create mode 100644 docs/source/Plugin/P165_StartingSegmentOptions.png diff --git a/docs/source/Plugin/P165.rst b/docs/source/Plugin/P165.rst index dc32fc183e..9495162cb9 100644 --- a/docs/source/Plugin/P165.rst +++ b/docs/source/Plugin/P165.rst @@ -126,6 +126,8 @@ Options * **Suppress leading 0 on day/hour**: When enabled, will show the hours of the time and days of the date without a leading 0 when < 10. +* **Use decimal dot for blink**: When enabled will use the decimal dot on the second digit (with offset accounted for) for blinking, when no extra pixels are available. + * **Scroll text > display width**: Normally the ``7dtext,`` command only show the left n characters the display can hold. This option enables the Scroll Text feature, that will scroll text sent using the ``7dtext`` or ``7dbin`` commands from right to left when the content is longer than the display can show at once. * **Scroll text in from right**: Normally the Scroll Text feature starts with the display filled with the left part of the text to scroll, with this option enabled, the display starts empty and the text is scrolled in from the right side of the display to the left, until all text is scrolled off. Then the scrolling restarts. @@ -181,13 +183,25 @@ These options determine the number-plan of the pixels in this group: * **Pixel-offset before**: The number of pixels to skip before the first digit is planned. Sometimes used to 'fix' the signal level for the pixels if the strip is connected to the ESP without a level converter, but that's an unreliable fix, so a level converter should best be used when powering the pixels with the 'suggested' (strongly advised) 5V. These pixels can also be used as an extra indicator, as the ``7dbefore`` command can be used to set these pixels to a color. -* **Starting pixel**: By default the number-plan starts at the left-top pixel of a digit, and flows clock-wise along all pixels for a single digit. With this setting you can select to start at the right-top of the digit, for assigning the number-plan. (See remark when using **counter-clockwise** option, below.) +* **Starting segment**: By default the number-plan starts at the left pixel of the top segment (a) of a digit, and flows clock-wise along all pixels for a single digit. With this setting you can select to start at the top of the top-right segment (b) or the left of the center-segment (g), when assigning the number-plan. (See remark when using **counter-clockwise** option, below.) + +.. image:: P165_StartingSegmentOptions.png + +* *a (top)*: The default starting segment. + +* *b/f (right/left)*: For the default clockwise numbering, the b-segment is the first segment to be numbered. Starting point is the f-segment when **Numbering counter-clockwise** is enabled. + +* *g (center)*: This is a very optimal numbering plan, as the segment-order is g, c, dp, d, e, f, a, b, and as it ends at the b-segment the next digit can easily be coupled to this one. When this option is selected, the **Split g-segment pixels** and **Decimal dot last segment** options are disabled (and ignored), as they don't make sense to be used. + +.. image:: P165_StartingSegmentOption-g.png + +| * **Split g-segment pixels**: The default number-plan assigns pixel-indexes to the g-segment between the e and f segment. When using a larger number of Width pixels, wiring the g-segment can be challenging. When enabled, this option assigns pixel-indexes to the right-half set of pixels between the b and c segment, and the remaining pixels between the e and f segment. * **Decimal dot last segment**: Similar to the g-segment, also the dp-segment can be challening to wire, as by default the f-segment is assigned the last pixel-index. So the default here is to assign pixel-indexes to the dp-segment between the c and d-segments. If you explicitly want to have these dp-pixels assigned as the last pixel-indexes, this option can be enabled. -* **Numbering counter-clockwise**: By default the pixel-indexes are assigned in a clock-wise order, but if your wiring-plan works better when numbering counter-clockwise, this is the setting to enable. This also changes how the **Starting pixel** is interpreted: Top-left will start from the f-segment, and end with the a-segment, and top-right will start assigning pixel-indexes from the right side of the a-segment, and work down the f, g, e, d, dp, c and b segments. +* **Numbering counter-clockwise**: By default the pixel-indexes are assigned in a clock-wise order, but if your wiring-plan works better when numbering counter-clockwise, this is the setting to enable. This also changes how the **Starting pixel** is interpreted: Top-left will start from the f-segment, and end with the a-segment, and top-right will start assigning pixel-indexes from the right side of the a-segment, and work down the f, g, e, d, dp, c and b segments. When this option is used with the **Starting segment** set to **g (center)**, also the **Right to left digits** option should best be enabled too, for most optimal result * **Right to Left digits**: When creating larger displays, or building a 2 1/2 or 3 1/2 digit display for displaying f.e. numbers up to 100 or 1000, starting the digits at the left is not efficient, as the entire left digit will get pixel-indexes assigned. When using the Right to Left digits option, the digits will be virtually reverted, and pixel-indexes assigned from the right digit to the left digit. The extra pixels, not needed to show anything else than ``1`` can be left out of the display. An example config is included below, showing this. diff --git a/docs/source/Plugin/P165_DeviceConfiguration.png b/docs/source/Plugin/P165_DeviceConfiguration.png index 6aa652a886a6215df5fb25ec889b99819b6821c7..0542c933fa0a38d80fcda7642e3d0bdd644b0383 100644 GIT binary patch literal 90002 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

    V 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 literal 93812 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�%-?F#_ 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 JWpexezX2||ik<)f diff --git a/docs/source/Plugin/P165_StartingSegmentOption-g.png b/docs/source/Plugin/P165_StartingSegmentOption-g.png new file mode 100644 index 0000000000000000000000000000000000000000..09f8b86303122d7c0ba7245b5302fbb01461e756 GIT binary patch literal 7827 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%0AM4BdEQm}DSb;s*Ee6RaQu=^1-=DWaf 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 Date: Sun, 29 Sep 2024 12:52:44 +0200 Subject: [PATCH 290/367] [GPIO] Fix build warnings on ESP8266 for GPIO direction --- src/_P004_Dallas.ino | 2 +- src/_P013_HCSR04.ino | 2 +- src/_P018_Dust.ino | 2 +- src/_P021_Level.ino | 2 +- src/_P029_Output.ino | 2 +- src/_P031_SHT1X.ino | 2 +- src/_P035_IRTX.ino | 2 +- src/_P038_NeoPixel.ino | 2 +- src/_P041_NeoClock.ino | 2 +- src/_P042_Candle.ino | 2 +- src/_P043_ClkOutput.ino | 2 +- src/_P054_DMX512.ino | 2 +- src/_P055_Chiming.ino | 6 +++--- src/_P063_TTP229_KeyPad.ino | 2 +- src/_P067_HX711_Load_Cell.ino | 2 +- src/_P070_NeoPixel_Clock.ino | 2 +- src/_P073_7DGT.ino | 6 +++--- src/_P076_HLW8012.ino | 2 +- src/_P088_HeatpumpIR.ino | 2 +- src/_P126_74HC595.ino | 6 +++--- src/_P128_NeoPixelBusFX.ino | 2 +- src/_P129_74HC165.ino | 4 ++-- src/_P131_NeoPixelMatrix.ino | 2 +- src/_P165_7SegNeopixel.ino | 2 +- src/src/DataStructs/DeviceStruct.cpp | 23 ++++++++++++++++++----- src/src/DataStructs/DeviceStruct.h | 12 ++++++++---- src/src/Helpers/ESPEasy_checks.cpp | 6 ++++++ src/src/Helpers/StringGenerator_GPIO.h | 2 +- 28 files changed, 64 insertions(+), 41 deletions(-) diff --git a/src/_P004_Dallas.ino b/src/_P004_Dallas.ino index 60e2e89f81..72aac9325e 100644 --- a/src/_P004_Dallas.ino +++ b/src/_P004_Dallas.ino @@ -62,7 +62,7 @@ boolean Plugin_004(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].GlobalSyncOption = true; Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P013_HCSR04.ino b/src/_P013_HCSR04.ino index 0c56c9bf9d..e6f151a73b 100644 --- a/src/_P013_HCSR04.ino +++ b/src/_P013_HCSR04.ino @@ -58,7 +58,7 @@ boolean Plugin_013(uint8_t function, struct EventStruct *even Device[deviceCount].TimerOptional = true; Device[deviceCount].GlobalSyncOption = true; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P018_Dust.ino b/src/_P018_Dust.ino index 12b1a611f6..173f0be65f 100644 --- a/src/_P018_Dust.ino +++ b/src/_P018_Dust.ino @@ -33,7 +33,7 @@ boolean Plugin_018(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].GlobalSyncOption = true; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P021_Level.ino b/src/_P021_Level.ino index 07942aaded..0635d6926d 100644 --- a/src/_P021_Level.ino +++ b/src/_P021_Level.ino @@ -47,7 +47,7 @@ boolean Plugin_021(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 1; Device[deviceCount].SendDataOption = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P029_Output.ino b/src/_P029_Output.ino index 423597a2a8..7e788a4bf6 100644 --- a/src/_P029_Output.ino +++ b/src/_P029_Output.ino @@ -30,7 +30,7 @@ boolean Plugin_029(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SWITCH; Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 1; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P031_SHT1X.ino b/src/_P031_SHT1X.ino index eff2e58f28..0ed34f2791 100644 --- a/src/_P031_SHT1X.ino +++ b/src/_P031_SHT1X.ino @@ -32,7 +32,7 @@ boolean Plugin_031(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P035_IRTX.ino b/src/_P035_IRTX.ino index 7b6215de53..456e84eb2f 100644 --- a/src/_P035_IRTX.ino +++ b/src/_P035_IRTX.ino @@ -62,7 +62,7 @@ boolean Plugin_035(uint8_t function, struct EventStruct *event, String& string) Device[++deviceCount].Number = PLUGIN_ID_035; Device[deviceCount].Type = DEVICE_TYPE_SINGLE; Device[deviceCount].SendDataOption = false; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P038_NeoPixel.ino b/src/_P038_NeoPixel.ino index b1b3b96f98..294e053c52 100644 --- a/src/_P038_NeoPixel.ino +++ b/src/_P038_NeoPixel.ino @@ -60,7 +60,7 @@ boolean Plugin_038(uint8_t function, struct EventStruct *event, String& string) Device[++deviceCount].Number = PLUGIN_ID_038; Device[deviceCount].Type = DEVICE_TYPE_SINGLE; Device[deviceCount].TimerOption = false; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P041_NeoClock.ino b/src/_P041_NeoClock.ino index 273f72fa86..2813907fc4 100644 --- a/src/_P041_NeoClock.ino +++ b/src/_P041_NeoClock.ino @@ -41,7 +41,7 @@ boolean Plugin_041(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 0; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P042_Candle.ino b/src/_P042_Candle.ino index c9e0f0bb6f..6c0d4083a7 100644 --- a/src/_P042_Candle.ino +++ b/src/_P042_Candle.ino @@ -99,7 +99,7 @@ boolean Plugin_042(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].ValueCount = 3; Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P043_ClkOutput.ino b/src/_P043_ClkOutput.ino index f0138baff8..44ee37e40e 100644 --- a/src/_P043_ClkOutput.ino +++ b/src/_P043_ClkOutput.ino @@ -75,7 +75,7 @@ boolean Plugin_043(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].ValueCount = 2; Device[deviceCount].SendDataOption = true; Device[deviceCount].OutputDataType = Output_Data_type_t::Simple; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P054_DMX512.ino b/src/_P054_DMX512.ino index 715cdf10b6..a7f34ee18c 100644 --- a/src/_P054_DMX512.ino +++ b/src/_P054_DMX512.ino @@ -84,7 +84,7 @@ boolean Plugin_054(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].Ports = 0; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; Device[deviceCount].ValueCount = 0; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P055_Chiming.ino b/src/_P055_Chiming.ino index 828fe03dda..b589b065be 100644 --- a/src/_P055_Chiming.ino +++ b/src/_P055_Chiming.ino @@ -96,9 +96,9 @@ boolean Plugin_055(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].InverseLogicOption = true; Device[deviceCount].ValueCount = 0; Device[deviceCount].GlobalSyncOption = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin3Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin3Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P063_TTP229_KeyPad.ino b/src/_P063_TTP229_KeyPad.ino index b33599a3ab..2bd04914eb 100644 --- a/src/_P063_TTP229_KeyPad.ino +++ b/src/_P063_TTP229_KeyPad.ino @@ -80,7 +80,7 @@ boolean Plugin_063(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = true; Device[deviceCount].GlobalSyncOption = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P067_HX711_Load_Cell.ino b/src/_P067_HX711_Load_Cell.ino index 9452f1efdd..7a75d7b0aa 100644 --- a/src/_P067_HX711_Load_Cell.ino +++ b/src/_P067_HX711_Load_Cell.ino @@ -51,7 +51,7 @@ boolean Plugin_067(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P070_NeoPixel_Clock.ino b/src/_P070_NeoPixel_Clock.ino index 2de1610378..bff2699b3a 100644 --- a/src/_P070_NeoPixel_Clock.ino +++ b/src/_P070_NeoPixel_Clock.ino @@ -41,7 +41,7 @@ boolean Plugin_070(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TRIPLE; Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 3; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); // FIXME TD-er: Not sure if access to any existing task data is needed when saving Device[deviceCount].ExitTaskBeforeSave = false; diff --git a/src/_P073_7DGT.ino b/src/_P073_7DGT.ino index d0f07b42de..84ca01e3c8 100644 --- a/src/_P073_7DGT.ino +++ b/src/_P073_7DGT.ino @@ -92,9 +92,9 @@ boolean Plugin_073(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 0; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin3Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin3Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P076_HLW8012.ino b/src/_P076_HLW8012.ino index 58c90e0c55..eacb3bd6d4 100644 --- a/src/_P076_HLW8012.ino +++ b/src/_P076_HLW8012.ino @@ -118,7 +118,7 @@ boolean Plugin_076(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].PluginStats = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P088_HeatpumpIR.ino b/src/_P088_HeatpumpIR.ino index 0696c9cef9..938d2d84ce 100644 --- a/src/_P088_HeatpumpIR.ino +++ b/src/_P088_HeatpumpIR.ino @@ -66,7 +66,7 @@ boolean Plugin_088(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; Device[deviceCount].Ports = 0; Device[deviceCount].ValueCount = 0; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P126_74HC595.ino b/src/_P126_74HC595.ino index 8178ce0934..b7ef09d8f8 100644 --- a/src/_P126_74HC595.ino +++ b/src/_P126_74HC595.ino @@ -98,9 +98,9 @@ boolean Plugin_126(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = true; Device[deviceCount].HasFormatUserVar = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin3Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin3Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P128_NeoPixelBusFX.ino b/src/_P128_NeoPixelBusFX.ino index 89c31bbd81..5f1829a878 100644 --- a/src/_P128_NeoPixelBusFX.ino +++ b/src/_P128_NeoPixelBusFX.ino @@ -161,7 +161,7 @@ boolean Plugin_128(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].SendDataOption = true; Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P129_74HC165.ino b/src/_P129_74HC165.ino index 3be2e36005..be2213ee70 100644 --- a/src/_P129_74HC165.ino +++ b/src/_P129_74HC165.ino @@ -81,8 +81,8 @@ boolean Plugin_129(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].TimerOption = true; // Used to update the Devices page Device[deviceCount].TimerOptional = true; Device[deviceCount].HasFormatUserVar = true; - Device[deviceCount].Pin2Direction = gpio_direction::gpio_output; - Device[deviceCount].Pin3Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin2Direction(gpio_direction::gpio_output); + Device[deviceCount].setPin3Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P131_NeoPixelMatrix.ino b/src/_P131_NeoPixelMatrix.ino index 6b6b564f2d..7bea0062db 100644 --- a/src/_P131_NeoPixelMatrix.ino +++ b/src/_P131_NeoPixelMatrix.ino @@ -49,7 +49,7 @@ boolean Plugin_131(uint8_t function, struct EventStruct *event, String& string) Device[deviceCount].ValueCount = 0; Device[deviceCount].TimerOption = true; Device[deviceCount].TimerOptional = true; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/_P165_7SegNeopixel.ino b/src/_P165_7SegNeopixel.ino index 949e39b057..094cc1cdd2 100644 --- a/src/_P165_7SegNeopixel.ino +++ b/src/_P165_7SegNeopixel.ino @@ -57,7 +57,7 @@ boolean Plugin_165(uint8_t function, struct EventStruct *event, String& string) Device[++deviceCount].Number = PLUGIN_ID_165; Device[deviceCount].Type = DEVICE_TYPE_SINGLE; Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_NONE; - Device[deviceCount].Pin1Direction = gpio_direction::gpio_output; + Device[deviceCount].setPin1Direction(gpio_direction::gpio_output); break; } diff --git a/src/src/DataStructs/DeviceStruct.cpp b/src/src/DataStructs/DeviceStruct.cpp index 569f88f796..96eafb470c 100644 --- a/src/src/DataStructs/DeviceStruct.cpp +++ b/src/src/DataStructs/DeviceStruct.cpp @@ -5,14 +5,17 @@ DeviceStruct::DeviceStruct() : Number(0), Type(0), VType(Sensor_VType::SENSOR_TYPE_NONE), Ports(0), ValueCount(0), OutputDataType(Output_Data_type_t::Default), + Pin1Direction(static_cast(gpio_direction::gpio_direction_MAX)), + Pin2Direction(static_cast(gpio_direction::gpio_direction_MAX)), + Pin3Direction(static_cast(gpio_direction::gpio_direction_MAX)), PullUpOption(false), InverseLogicOption(false), FormulaOption(false), Custom(false), SendDataOption(false), GlobalSyncOption(false), TimerOption(false), TimerOptional(false), DecimalsOnly(false), DuplicateDetection(false), ExitTaskBeforeSave(true), ErrorStateValues(false), PluginStats(false), PluginLogsPeaks(false), PowerManager(false), TaskLogsOwnPeaks(false), I2CNoDeviceCheck(false), - I2CMax100kHz(false), HasFormatUserVar(false), Pin1Direction(gpio_direction::gpio_direction_MAX), - Pin2Direction(gpio_direction::gpio_direction_MAX), Pin3Direction(gpio_direction::gpio_direction_MAX) {} + I2CMax100kHz(false), HasFormatUserVar(false) +{} bool DeviceStruct::connectedToGPIOpins() const { switch(Type) { @@ -69,14 +72,24 @@ bool DeviceStruct::isCustom() const { (Type == DEVICE_TYPE_CUSTOM3); } +void DeviceStruct::setPinDirection(int pin, gpio_direction direction) +{ + const uint8_t val = static_cast(direction) & ((1 << GPIO_DIRECTION_NR_BITS) - 1); + switch (pin) { + case 1: Pin1Direction = val; break; + case 2: Pin2Direction = val; break; + case 3: Pin3Direction = val; break; + } +} + gpio_direction DeviceStruct::getPinDirection(int pin) const { switch (pin) { case 1: - return Pin1Direction; + return static_cast(Pin1Direction); case 2: - return Pin2Direction; + return static_cast(Pin2Direction); case 3: - return Pin3Direction; + return static_cast(Pin3Direction); } return gpio_direction::gpio_direction_MAX; } diff --git a/src/src/DataStructs/DeviceStruct.h b/src/src/DataStructs/DeviceStruct.h index f5dccc73ba..7b3b81c74e 100644 --- a/src/src/DataStructs/DeviceStruct.h +++ b/src/src/DataStructs/DeviceStruct.h @@ -69,6 +69,11 @@ struct __attribute__((__packed__)) DeviceStruct return pluginID_t::toPluginID(Number); } + void setPinDirection(int pin, gpio_direction direction); + void setPin1Direction(gpio_direction direction) { setPinDirection(1, direction); } + void setPin2Direction(gpio_direction direction) { setPinDirection(2, direction); } + void setPin3Direction(gpio_direction direction) { setPinDirection(3, direction); } + gpio_direction getPinDirection(int pin) const; PinSelectPurpose pinDirectionToPurpose(gpio_direction direction) const; PinSelectPurpose getPinSelectPurpose(int pin) const; @@ -80,10 +85,9 @@ struct __attribute__((__packed__)) DeviceStruct uint8_t Ports; // Port to use when device has multiple I/O pins (N.B. not used much) uint8_t ValueCount; // The number of output values of a plugin. The value should match the number of keys PLUGIN_VALUENAME1_xxx Output_Data_type_t OutputDataType; // Subset of selectable output data types (Default = no selection) - - gpio_direction Pin1Direction : NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)); - gpio_direction Pin2Direction : NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)); - gpio_direction Pin3Direction : NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)); + uint8_t Pin1Direction : GPIO_DIRECTION_NR_BITS; + uint8_t Pin2Direction : GPIO_DIRECTION_NR_BITS; + uint8_t Pin3Direction : GPIO_DIRECTION_NR_BITS; bool PullUpOption : 1; // Allow to set internal pull-up resistors. bool InverseLogicOption : 1; // Allow to invert the boolean state (e.g. a switch) diff --git a/src/src/Helpers/ESPEasy_checks.cpp b/src/src/Helpers/ESPEasy_checks.cpp index 87a0986450..9a83f51b98 100644 --- a/src/src/Helpers/ESPEasy_checks.cpp +++ b/src/src/Helpers/ESPEasy_checks.cpp @@ -181,6 +181,12 @@ void run_compiletime_checks() { //const size_t offset = offsetof(SettingsStruct, ControllerEnabled); //check_size(); + + // ESP8266 toolchain does not support constexpr macros in struct defines + // to determine nr of bits in a struct. + static_assert(GPIO_DIRECTION_NR_BITS== NR_BITS(static_cast(gpio_direction::gpio_direction_MAX)), "Correct GPIO_DIRECTION_NR_BITS"); + + #endif } diff --git a/src/src/Helpers/StringGenerator_GPIO.h b/src/src/Helpers/StringGenerator_GPIO.h index 6ead026e51..80ab040ecc 100644 --- a/src/src/Helpers/StringGenerator_GPIO.h +++ b/src/src/Helpers/StringGenerator_GPIO.h @@ -9,7 +9,7 @@ #define HTML_SYMBOL_OUTPUT "⇒" #define HTML_SYMBOL_I_O "⇔" - +#define GPIO_DIRECTION_NR_BITS 2 enum class gpio_direction : uint8_t { gpio_input, gpio_output, From 4b2143d96d08a51a5653254d547a2a824d1310b1 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 29 Sep 2024 12:56:39 +0200 Subject: [PATCH 291/367] [ESPEasy p2p] Fix sending task value data on ESP8266 TaskValueData_t is now forced 32-bit aligned to fix other issues, so we must copy it byte aligned to/from the p2p struct. --- src/_C013.cpp | 11 +++-------- src/src/DataStructs/C013_p2p_SensorDataStruct.h | 2 +- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/src/_C013.cpp b/src/_C013.cpp index 56243a0819..3e33bb2f8b 100644 --- a/src/_C013.cpp +++ b/src/_C013.cpp @@ -157,11 +157,9 @@ void C013_SendUDPTaskData(struct EventStruct *event, uint8_t destUnit, uint8_t d const TaskValues_Data_t *taskValues = UserVar.getRawTaskValues_Data(event->TaskIndex); + if (taskValues != nullptr) { - for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x) - { - dataReply.values.copyValue(*taskValues, x, dataReply.sensorType); - } + memcpy(dataReply.taskValues_Data, taskValues->binary, sizeof(dataReply.taskValues_Data)); } dataReply.destUnit = destUnit; @@ -388,10 +386,7 @@ void C013_Receive(struct EventStruct *event) { TaskValues_Data_t *taskValues = UserVar.getRawTaskValues_Data(dataReply.destTaskIndex); if (taskValues != nullptr) { - for (taskVarIndex_t x = 0; x < VARS_PER_TASK; ++x) - { - taskValues->copyValue(dataReply.values, x, sensorType); - } + memcpy(taskValues->binary, dataReply.taskValues_Data, sizeof(dataReply.taskValues_Data)); } STOP_TIMER(C013_RECEIVE_SENSOR_DATA); diff --git a/src/src/DataStructs/C013_p2p_SensorDataStruct.h b/src/src/DataStructs/C013_p2p_SensorDataStruct.h index 8306d2d0bc..6c7ffebe6e 100644 --- a/src/src/DataStructs/C013_p2p_SensorDataStruct.h +++ b/src/src/DataStructs/C013_p2p_SensorDataStruct.h @@ -41,7 +41,7 @@ struct __attribute__((__packed__)) C013_SensorDataStruct // Thus should not be checked pluginID_t deviceNumber = INVALID_PLUGIN_ID; Sensor_VType sensorType = Sensor_VType::SENSOR_TYPE_NONE; - TaskValues_Data_t values{}; + uint8_t taskValues_Data[sizeof(TaskValues_Data_t)]{}; // Extra info added on 20240619 (build ID 20871) ShortChecksumType checksum; From 2db4231e2993b01098ce2d046eff1f5233a42b1b Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 29 Sep 2024 13:15:33 +0200 Subject: [PATCH 292/367] [LibSSL] Add LightBearSSL lib made by Tasmota --- .../bearssl_esp8266-customized.txt | 13 + lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk | 21 + .../bearssl-esp8266/library.properties | 9 + lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c | 346 ++ lib/lib_ssl/bearssl-esp8266/src/aead/eax.c | 525 ++ lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c | 318 ++ lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c | 44 + .../bearssl-esp8266/src/codec/dec16be.c | 38 + .../bearssl-esp8266/src/codec/dec16le.c | 38 + .../bearssl-esp8266/src/codec/dec32be.c | 38 + .../bearssl-esp8266/src/codec/dec32le.c | 38 + .../bearssl-esp8266/src/codec/dec64be.c | 38 + .../bearssl-esp8266/src/codec/dec64le.c | 38 + .../bearssl-esp8266/src/codec/enc16be.c | 38 + .../bearssl-esp8266/src/codec/enc16le.c | 38 + .../bearssl-esp8266/src/codec/enc32be.c | 38 + .../bearssl-esp8266/src/codec/enc32le.c | 38 + .../bearssl-esp8266/src/codec/enc64be.c | 38 + .../bearssl-esp8266/src/codec/enc64le.c | 38 + .../bearssl-esp8266/src/codec/pemdec.c | 536 ++ .../bearssl-esp8266/src/codec/pemenc.c | 173 + .../bearssl-esp8266/src/ec/ec_all_m15.c | 121 + .../bearssl-esp8266/src/ec/ec_c25519_m15.c | 1478 ++++++ .../bearssl-esp8266/src/ec/ec_curve25519.c | 46 + .../bearssl-esp8266/src/ec/ec_default.c | 36 + .../bearssl-esp8266/src/ec/ec_keygen.c | 86 + .../bearssl-esp8266/src/ec/ec_p256_m15.c | 2111 ++++++++ .../bearssl-esp8266/src/ec/ec_prime_i15.c | 826 ++++ .../bearssl-esp8266/src/ec/ec_pubkey.c | 85 + .../bearssl-esp8266/src/ec/ec_secp256r1.c | 51 + .../bearssl-esp8266/src/ec/ec_secp384r1.c | 57 + .../bearssl-esp8266/src/ec/ec_secp521r1.c | 64 + .../bearssl-esp8266/src/ec/ecdsa_atr.c | 134 + .../src/ec/ecdsa_default_sign_asn1.c | 36 + .../src/ec/ecdsa_default_sign_raw.c | 36 + .../src/ec/ecdsa_default_vrfy_asn1.c | 36 + .../src/ec/ecdsa_default_vrfy_raw.c | 36 + .../bearssl-esp8266/src/ec/ecdsa_i15_bits.c | 47 + .../src/ec/ecdsa_i15_sign_asn1.c | 45 + .../src/ec/ecdsa_i15_sign_raw.c | 174 + .../src/ec/ecdsa_i15_vrfy_asn1.c | 48 + .../src/ec/ecdsa_i15_vrfy_raw.c | 166 + .../bearssl-esp8266/src/ec/ecdsa_rta.c | 121 + .../bearssl-esp8266/src/hash/dig_oid.c | 84 + .../bearssl-esp8266/src/hash/dig_size.c | 50 + .../bearssl-esp8266/src/hash/ghash_ctmul.c | 345 ++ .../bearssl-esp8266/src/hash/ghash_ctmul32.c | 251 + .../bearssl-esp8266/src/hash/ghash_ctmul64.c | 154 + .../bearssl-esp8266/src/hash/ghash_pclmul.c | 389 ++ lib/lib_ssl/bearssl-esp8266/src/hash/md5.c | 210 + .../bearssl-esp8266/src/hash/md5sha1.c | 141 + lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c | 56 + .../bearssl-esp8266/src/hash/multihash.c | 166 + lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c | 191 + .../bearssl-esp8266/src/hash/sha2big.c | 285 ++ .../bearssl-esp8266/src/hash/sha2small.c | 341 ++ lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c | 46 + .../bearssl-esp8266/src/int/i15_bitlen.c | 44 + .../bearssl-esp8266/src/int/i15_decmod.c | 124 + .../bearssl-esp8266/src/int/i15_decode.c | 56 + .../bearssl-esp8266/src/int/i15_decred.c | 100 + .../bearssl-esp8266/src/int/i15_encode.c | 56 + .../bearssl-esp8266/src/int/i15_fmont.c | 59 + .../bearssl-esp8266/src/int/i15_iszero.c | 39 + .../bearssl-esp8266/src/int/i15_moddiv.c | 465 ++ .../bearssl-esp8266/src/int/i15_modpow.c | 50 + .../bearssl-esp8266/src/int/i15_modpow2.c | 160 + .../bearssl-esp8266/src/int/i15_montmul.c | 321 ++ .../bearssl-esp8266/src/int/i15_mulacc.c | 61 + .../bearssl-esp8266/src/int/i15_muladd.c | 173 + .../bearssl-esp8266/src/int/i15_ninv15.c | 38 + .../bearssl-esp8266/src/int/i15_reduce.c | 66 + .../bearssl-esp8266/src/int/i15_rshift.c | 47 + lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c | 46 + .../bearssl-esp8266/src/int/i15_tmont.c | 36 + lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c | 107 + lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c | 590 +++ lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c | 122 + lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c | 193 + .../bearssl-esp8266/src/pgmspace_bearssl.h | 64 + .../bearssl-esp8266/src/rand/aesctr_drbg.c | 206 + .../bearssl-esp8266/src/rand/hmac_drbg.c | 157 + lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c | 214 + .../src/rsa/rsa_default_keygen.c | 38 + .../src/rsa/rsa_default_modulus.c | 36 + .../src/rsa/rsa_default_oaep_decrypt.c | 38 + .../src/rsa/rsa_default_oaep_encrypt.c | 38 + .../src/rsa/rsa_default_pkcs1_sign.c | 38 + .../src/rsa/rsa_default_pkcs1_vrfy.c | 38 + .../src/rsa/rsa_default_priv.c | 38 + .../src/rsa/rsa_default_privexp.c | 36 + .../src/rsa/rsa_default_pss_sign.c | 38 + .../src/rsa/rsa_default_pss_vrfy.c | 38 + .../bearssl-esp8266/src/rsa/rsa_default_pub.c | 38 + .../src/rsa/rsa_default_pubexp.c | 36 + .../bearssl-esp8266/src/rsa/rsa_i15_keygen.c | 585 +++ .../bearssl-esp8266/src/rsa/rsa_i15_modulus.c | 99 + .../src/rsa/rsa_i15_oaep_decrypt.c | 41 + .../src/rsa/rsa_i15_oaep_encrypt.c | 44 + .../src/rsa/rsa_i15_pkcs1_sign.c | 37 + .../src/rsa/rsa_i15_pkcs1_vrfy.c | 43 + .../bearssl-esp8266/src/rsa/rsa_i15_priv.c | 215 + .../bearssl-esp8266/src/rsa/rsa_i15_privexp.c | 320 ++ .../src/rsa/rsa_i15_pss_sign.c | 40 + .../src/rsa/rsa_i15_pss_vrfy.c | 44 + .../bearssl-esp8266/src/rsa/rsa_i15_pub.c | 113 + .../bearssl-esp8266/src/rsa/rsa_i15_pubexp.c | 152 + .../bearssl-esp8266/src/rsa/rsa_oaep_pad.c | 112 + .../bearssl-esp8266/src/rsa/rsa_oaep_unpad.c | 145 + .../src/rsa/rsa_pkcs1_sig_pad.c | 100 + .../src/rsa/rsa_pkcs1_sig_unpad.c | 121 + .../bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c | 106 + .../src/rsa/rsa_pss_sig_unpad.c | 121 + .../bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c | 52 + lib/lib_ssl/bearssl-esp8266/src/settings.c | 313 ++ lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c | 73 + .../bearssl-esp8266/src/ssl/prf_md5sha1.c | 43 + .../bearssl-esp8266/src/ssl/prf_sha256.c | 36 + .../bearssl-esp8266/src/ssl/prf_sha384.c | 36 + .../src/ssl/ssl_ccert_single_ec.c | 156 + .../src/ssl/ssl_ccert_single_rsa.c | 157 + .../bearssl-esp8266/src/ssl/ssl_client.c | 78 + .../src/ssl/ssl_client_default_rsapub.c | 32 + .../bearssl-esp8266/src/ssl/ssl_client_full.c | 179 + .../bearssl-esp8266/src/ssl/ssl_engine.c | 1584 ++++++ .../src/ssl/ssl_engine_default_aescbc.c | 64 + .../src/ssl/ssl_engine_default_aesccm.c | 67 + .../src/ssl/ssl_engine_default_aesgcm.c | 89 + .../src/ssl/ssl_engine_default_chapol.c | 65 + .../src/ssl/ssl_engine_default_descbc.c | 37 + .../src/ssl/ssl_engine_default_ec.c | 36 + .../src/ssl/ssl_engine_default_ecdsa.c | 38 + .../src/ssl/ssl_engine_default_rsavrfy.c | 32 + .../bearssl-esp8266/src/ssl/ssl_hashes.c | 46 + .../bearssl-esp8266/src/ssl/ssl_hs_client.c | 1927 ++++++++ .../bearssl-esp8266/src/ssl/ssl_hs_server.c | 1995 ++++++++ lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c | 261 + .../bearssl-esp8266/src/ssl/ssl_keyexport.c | 83 + lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c | 537 ++ .../bearssl-esp8266/src/ssl/ssl_rec_cbc.c | 440 ++ .../bearssl-esp8266/src/ssl/ssl_rec_ccm.c | 213 + .../bearssl-esp8266/src/ssl/ssl_rec_chapol.c | 177 + .../bearssl-esp8266/src/ssl/ssl_rec_gcm.c | 235 + .../src/ssl/ssl_scert_single_ec.c | 142 + .../src/ssl/ssl_scert_single_rsa.c | 162 + .../src/symcipher/aes_big_cbcdec.c | 69 + .../src/symcipher/aes_big_cbcenc.c | 67 + .../src/symcipher/aes_big_ctr.c | 84 + .../src/symcipher/aes_big_ctrcbc.c | 142 + .../src/symcipher/aes_big_dec.c | 254 + .../src/symcipher/aes_big_enc.c | 157 + .../src/symcipher/aes_common.c | 112 + .../bearssl-esp8266/src/symcipher/aes_ct.c | 328 ++ .../bearssl-esp8266/src/symcipher/aes_ct64.c | 398 ++ .../src/symcipher/aes_ct64_cbcdec.c | 104 + .../src/symcipher/aes_ct64_cbcenc.c | 81 + .../src/symcipher/aes_ct64_ctr.c | 114 + .../src/symcipher/aes_ct64_ctrcbc.c | 433 ++ .../src/symcipher/aes_ct64_dec.c | 159 + .../src/symcipher/aes_ct64_enc.c | 115 + .../src/symcipher/aes_ct_cbcdec.c | 111 + .../src/symcipher/aes_ct_cbcenc.c | 91 + .../src/symcipher/aes_ct_ctr.c | 116 + .../src/symcipher/aes_ct_ctrcbc.c | 422 ++ .../src/symcipher/aes_ct_dec.c | 170 + .../src/symcipher/aes_ct_enc.c | 112 + .../src/symcipher/aes_small_cbcdec.c | 69 + .../src/symcipher/aes_small_cbcenc.c | 67 + .../src/symcipher/aes_small_ctr.c | 84 + .../src/symcipher/aes_small_ctrcbc.c | 142 + .../src/symcipher/aes_small_dec.c | 176 + .../src/symcipher/aes_small_enc.c | 129 + .../src/symcipher/chacha20_ct.c | 110 + .../src/symcipher/chacha20_sse2.c | 237 + .../bearssl-esp8266/src/symcipher/des_ct.c | 411 ++ .../src/symcipher/des_ct_cbcdec.c | 87 + .../src/symcipher/des_ct_cbcenc.c | 69 + .../src/symcipher/des_support.c | 166 + .../bearssl-esp8266/src/symcipher/des_tab.c | 310 ++ .../src/symcipher/des_tab_cbcdec.c | 85 + .../src/symcipher/des_tab_cbcenc.c | 67 + .../src/symcipher/poly1305_ctmul.c | 260 + .../src/symcipher/poly1305_ctmul32.c | 297 ++ .../src/symcipher/poly1305_ctmulq.c | 475 ++ .../src/symcipher/poly1305_i15.c | 221 + lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h | 183 + .../bearssl-esp8266/src/t_bearssl_aead.h | 1059 ++++ .../bearssl-esp8266/src/t_bearssl_block.h | 2618 ++++++++++ .../bearssl-esp8266/src/t_bearssl_ec.h | 967 ++++ .../bearssl-esp8266/src/t_bearssl_hash.h | 1346 +++++ .../bearssl-esp8266/src/t_bearssl_hmac.h | 241 + .../bearssl-esp8266/src/t_bearssl_kdf.h | 284 ++ .../bearssl-esp8266/src/t_bearssl_pem.h | 294 ++ .../bearssl-esp8266/src/t_bearssl_prf.h | 150 + .../bearssl-esp8266/src/t_bearssl_rand.h | 397 ++ .../bearssl-esp8266/src/t_bearssl_rsa.h | 1655 +++++++ .../bearssl-esp8266/src/t_bearssl_ssl.h | 4308 +++++++++++++++++ .../src/t_bearssl_tasmota_config.h | 40 + .../bearssl-esp8266/src/t_bearssl_x509.h | 1595 ++++++ lib/lib_ssl/bearssl-esp8266/src/t_config.h | 238 + lib/lib_ssl/bearssl-esp8266/src/t_inner.h | 2619 ++++++++++ .../bearssl-esp8266/src/x509/asn1enc.c | 93 + .../src/x509/encode_ec_pk8der.c | 110 + .../src/x509/encode_ec_rawder.c | 161 + .../src/x509/encode_rsa_pk8der.c | 97 + .../src/x509/encode_rsa_rawder.c | 96 + .../bearssl-esp8266/src/x509/pkey_decoder.c | 587 +++ .../bearssl-esp8266/src/x509/skey_decoder.c | 654 +++ .../bearssl-esp8266/src/x509/x509_decoder.c | 790 +++ .../bearssl-esp8266/src/x509/x509_knownkey.c | 105 + .../bearssl-esp8266/src/x509/x509_minimal.c | 1777 +++++++ .../src/x509/x509_minimal_full.c | 59 + lib/lib_ssl/tls_mini/library.json | 17 + lib/lib_ssl/tls_mini/src/StackThunk_light.cpp | 169 + lib/lib_ssl/tls_mini/src/StackThunk_light.h | 95 + .../src/WiFiClientSecureLightBearSSL.cpp | 1017 ++++ .../src/WiFiClientSecureLightBearSSL.h | 300 ++ 217 files changed, 58079 insertions(+) create mode 100644 lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt create mode 100644 lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk create mode 100644 lib/lib_ssl/bearssl-esp8266/library.properties create mode 100644 lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/aead/eax.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_m15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/md5.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/settings.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_config.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/t_inner.h create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c create mode 100644 lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c create mode 100644 lib/lib_ssl/tls_mini/library.json create mode 100644 lib/lib_ssl/tls_mini/src/StackThunk_light.cpp create mode 100644 lib/lib_ssl/tls_mini/src/StackThunk_light.h create mode 100644 lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp create mode 100644 lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h diff --git a/lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt b/lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt new file mode 100644 index 0000000000..2652045582 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/bearssl_esp8266-customized.txt @@ -0,0 +1,13 @@ +This library is adapted from bearssl-esp8266 to avoid conflict with the +BearSSL headers in Arduino Core. + +To recreate, copy all original 'src/' and 'inc/' into 'src/' lib. + +Then rename the following: + - "bearssl with "t_bearssl + - "inner with "t_inner + - "config with "t_config + +Add the customized files in src/: + - t_bearssl_tasmota_config.h + - pgmspace_bearssl.h diff --git a/lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk b/lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk new file mode 100644 index 0000000000..5c409b8be7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/conf/esp8266.mk @@ -0,0 +1,21 @@ +# Configuration for compiling to an ESP8266 from a UNIX system + +# We are on a Unix system so we assume a Single Unix compatible 'make' +# utility, and Unix defaults. +include conf/Unix.mk + +# We override the build directory. +BUILD = esp8266 + +# C compiler, linker, and static library builder. +TOOLCHAIN_PREFIX := xtensa-lx106-elf- +CC := $(TOOLCHAIN_PREFIX)gcc +CFLAGS = -W -Wall -g -O2 -Wpointer-arith -Wl,-EL -nostdlib -mlongcalls -mno-text-section-literals -ffunction-sections -fdata-sections -Werror +CFLAGS += -D__ets__ -DICACHE_FLASH -DESP8266 -DBR_SLOW_MUL15=1 +LD := $(TOOLCHAIN_PREFIX)ld +AR := $(TOOLCHAIN_PREFIX)ar + +# We compile only the static library. +DLL = no +TOOLS = no +TESTS = no diff --git a/lib/lib_ssl/bearssl-esp8266/library.properties b/lib/lib_ssl/bearssl-esp8266/library.properties new file mode 100644 index 0000000000..7728af80cb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/library.properties @@ -0,0 +1,9 @@ +name=BearSSL +version=0.6 +author=Thomas Pornin +maintainer=Earle F. Philhower, III +sentence=BearSSL implementation of the SSL/TLS protocol optimized for ESP8266 by Earle F. Philhower, optimized for Tasmota by Stephan Hadinger +paragraph= +category=Other +url=https://github.com/earlephilhower/bearssl-esp8266.git +architectures=esp8266,esp32 diff --git a/lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c b/lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c new file mode 100644 index 0000000000..427d1becc7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/aead/ccm.c @@ -0,0 +1,346 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * The combined CTR + CBC-MAC functions can only handle full blocks, + * so some buffering is necessary. + * + * - 'ptr' contains a value from 0 to 15, which is the number of bytes + * accumulated in buf[] that still needs to be processed with the + * current CBC-MAC computation. + * + * - When processing the message itself, CTR encryption/decryption is + * also done at the same time. The first 'ptr' bytes of buf[] then + * contains the plaintext bytes, while the last '16 - ptr' bytes of + * buf[] are the remnants of the stream block, to be used against + * the next input bytes, when available. When 'ptr' is 0, the + * contents of buf[] are to be ignored. + * + * - The current counter and running CBC-MAC values are kept in 'ctr' + * and 'cbcmac', respectively. + */ + +/* see bearssl_block.h */ +void +br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx) +{ + ctx->bctx = bctx; +} + +/* see bearssl_block.h */ +int +br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len, + uint64_t aad_len, uint64_t data_len, size_t tag_len) +{ + unsigned char tmp[16]; + unsigned u, q; + + if (nonce_len < 7 || nonce_len > 13) { + return 0; + } + if (tag_len < 4 || tag_len > 16 || (tag_len & 1) != 0) { + return 0; + } + q = 15 - (unsigned)nonce_len; + ctx->tag_len = tag_len; + + /* + * Block B0, to start CBC-MAC. + */ + tmp[0] = (aad_len > 0 ? 0x40 : 0x00) + | (((unsigned)tag_len - 2) << 2) + | (q - 1); + memcpy(tmp + 1, nonce, nonce_len); + for (u = 0; u < q; u ++) { + tmp[15 - u] = (unsigned char)data_len; + data_len >>= 8; + } + if (data_len != 0) { + /* + * If the data length was not entirely consumed in the + * loop above, then it exceeds the maximum limit of + * q bytes (when encoded). + */ + return 0; + } + + /* + * Start CBC-MAC. + */ + memset(ctx->cbcmac, 0, sizeof ctx->cbcmac); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, tmp, sizeof tmp); + + /* + * Assemble AAD length header. + */ + if ((aad_len >> 32) != 0) { + ctx->buf[0] = 0xFF; + ctx->buf[1] = 0xFF; + br_enc64be(ctx->buf + 2, aad_len); + ctx->ptr = 10; + } else if (aad_len >= 0xFF00) { + ctx->buf[0] = 0xFF; + ctx->buf[1] = 0xFE; + br_enc32be(ctx->buf + 2, (uint32_t)aad_len); + ctx->ptr = 6; + } else if (aad_len > 0) { + br_enc16be(ctx->buf, (unsigned)aad_len); + ctx->ptr = 2; + } else { + ctx->ptr = 0; + } + + /* + * Make initial counter value and compute tag mask. + */ + ctx->ctr[0] = q - 1; + memcpy(ctx->ctr + 1, nonce, nonce_len); + memset(ctx->ctr + 1 + nonce_len, 0, q); + memset(ctx->tagmask, 0, sizeof ctx->tagmask); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, + ctx->tagmask, sizeof ctx->tagmask); + + return 1; +} + +/* see bearssl_block.h */ +void +br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len) +{ + const unsigned char *dbuf; + size_t ptr; + + dbuf = data; + + /* + * Complete partial block, if needed. + */ + ptr = ctx->ptr; + if (ptr != 0) { + size_t clen; + + clen = (sizeof ctx->buf) - ptr; + if (clen > len) { + memcpy(ctx->buf + ptr, dbuf, len); + ctx->ptr = ptr + len; + return; + } + memcpy(ctx->buf + ptr, dbuf, clen); + dbuf += clen; + len -= clen; + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * Process complete blocks. + */ + ptr = len & 15; + len -= ptr; + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, dbuf, len); + dbuf += len; + + /* + * Copy last partial block in the context buffer. + */ + memcpy(ctx->buf, dbuf, ptr); + ctx->ptr = ptr; +} + +/* see bearssl_block.h */ +void +br_ccm_flip(br_ccm_context *ctx) +{ + size_t ptr; + + /* + * Complete AAD partial block with zeros, if necessary. + */ + ptr = ctx->ptr; + if (ptr != 0) { + memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + ctx->ptr = 0; + } + + /* + * Counter was already set by br_ccm_reset(). + */ +} + +/* see bearssl_block.h */ +void +br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *dbuf; + size_t ptr; + + dbuf = data; + + /* + * Complete a partial block, if any: ctx->buf[] contains + * ctx->ptr plaintext bytes (already reported), and the other + * bytes are CTR stream output. + */ + ptr = ctx->ptr; + if (ptr != 0) { + size_t clen; + size_t u; + + clen = (sizeof ctx->buf) - ptr; + if (clen > len) { + clen = len; + } + if (encrypt) { + for (u = 0; u < clen; u ++) { + unsigned w, x; + + w = ctx->buf[ptr + u]; + x = dbuf[u]; + ctx->buf[ptr + u] = x; + dbuf[u] = w ^ x; + } + } else { + for (u = 0; u < clen; u ++) { + unsigned w; + + w = ctx->buf[ptr + u] ^ dbuf[u]; + dbuf[u] = w; + ctx->buf[ptr + u] = w; + } + } + dbuf += clen; + len -= clen; + ptr += clen; + if (ptr < sizeof ctx->buf) { + ctx->ptr = ptr; + return; + } + (*ctx->bctx)->mac(ctx->bctx, + ctx->cbcmac, ctx->buf, sizeof ctx->buf); + } + + /* + * Process all complete blocks. Note that the ctrcbc API is for + * encrypt-then-MAC (CBC-MAC is computed over the encrypted + * blocks) while CCM uses MAC-and-encrypt (CBC-MAC is computed + * over the plaintext blocks). Therefore, we need to use the + * _decryption_ function for encryption, and the encryption + * function for decryption (this works because CTR encryption + * and decryption are identical, so the choice really is about + * computing the CBC-MAC before or after XORing with the CTR + * stream). + */ + ptr = len & 15; + len -= ptr; + if (encrypt) { + (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } else { + (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } + dbuf += len; + + /* + * If there is some remaining data, then we need to compute an + * extra block of CTR stream. + */ + if (ptr != 0) { + size_t u; + + memset(ctx->buf, 0, sizeof ctx->buf); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, + ctx->buf, sizeof ctx->buf); + if (encrypt) { + for (u = 0; u < ptr; u ++) { + unsigned w, x; + + w = ctx->buf[u]; + x = dbuf[u]; + ctx->buf[u] = x; + dbuf[u] = w ^ x; + } + } else { + for (u = 0; u < ptr; u ++) { + unsigned w; + + w = ctx->buf[u] ^ dbuf[u]; + dbuf[u] = w; + ctx->buf[u] = w; + } + } + } + ctx->ptr = ptr; +} + +/* see bearssl_block.h */ +size_t +br_ccm_get_tag(br_ccm_context *ctx, void *tag) +{ + size_t ptr; + size_t u; + + /* + * If there is some buffered data, then we need to pad it with + * zeros and finish up CBC-MAC. + */ + ptr = ctx->ptr; + if (ptr != 0) { + memset(ctx->buf + ptr, 0, (sizeof ctx->buf) - ptr); + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * XOR the tag mask into the CBC-MAC output. + */ + for (u = 0; u < ctx->tag_len; u ++) { + ctx->cbcmac[u] ^= ctx->tagmask[u]; + } + memcpy(tag, ctx->cbcmac, ctx->tag_len); + return ctx->tag_len; +} + +/* see bearssl_block.h */ +uint32_t +br_ccm_check_tag(br_ccm_context *ctx, const void *tag) +{ + unsigned char tmp[16]; + size_t u, tag_len; + uint32_t z; + + tag_len = br_ccm_get_tag(ctx, tmp); + z = 0; + for (u = 0; u < tag_len; u ++) { + z |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(z); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/aead/eax.c b/lib/lib_ssl/bearssl-esp8266/src/aead/eax.c new file mode 100644 index 0000000000..9e0b3230ba --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/aead/eax.c @@ -0,0 +1,525 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * The combined CTR + CBC-MAC functions can only handle full blocks, + * so some buffering is necessary. Moreover, EAX has a special padding + * rule for CBC-MAC, which implies that we cannot compute the MAC over + * the last received full block until we know whether we are at the + * end of the data or not. + * + * - 'ptr' contains a value from 1 to 16, which is the number of bytes + * accumulated in buf[] that still needs to be processed with the + * current OMAC computation. Beware that this can go to 16: a + * complete block cannot be processed until it is known whether it + * is the last block or not. However, it can never be 0, because + * OMAC^t works on an input that is at least one-block long. + * + * - When processing the message itself, CTR encryption/decryption is + * also done at the same time. The first 'ptr' bytes of buf[] then + * contains the encrypted bytes, while the last '16 - ptr' bytes of + * buf[] are the remnants of the stream block, to be used against + * the next input bytes, when available. + * + * - The current counter and running CBC-MAC values are kept in 'ctr' + * and 'cbcmac', respectively. + * + * - The derived keys for padding are kept in L2 and L4 (double and + * quadruple of Enc_K(0^n), in GF(2^128), respectively). + */ + +/* + * Start an OMAC computation; the first block is the big-endian + * representation of the provided value ('val' must fit on one byte). + * We make it a delayed block because it may also be the last one, + */ +static void +omac_start(br_eax_context *ctx, unsigned val) +{ + memset(ctx->cbcmac, 0, sizeof ctx->cbcmac); + memset(ctx->buf, 0, sizeof ctx->buf); + ctx->buf[15] = val; + ctx->ptr = 16; +} + +/* + * Double a value in finite field GF(2^128), defined with modulus + * X^128+X^7+X^2+X+1. + */ +static void +double_gf128(unsigned char *dst, const unsigned char *src) +{ + unsigned cc; + int i; + + cc = 0x87 & -((unsigned)src[0] >> 7); + for (i = 15; i >= 0; i --) { + unsigned z; + + z = (src[i] << 1) ^ cc; + cc = z >> 8; + dst[i] = (unsigned char)z; + } +} + +/* + * Apply padding to the last block, currently in ctx->buf (with + * ctx->ptr bytes), and finalize OMAC computation. + */ +static void +do_pad(br_eax_context *ctx) +{ + unsigned char *pad; + size_t ptr, u; + + ptr = ctx->ptr; + if (ptr == 16) { + pad = ctx->L2; + } else { + ctx->buf[ptr ++] = 0x80; + memset(ctx->buf + ptr, 0x00, 16 - ptr); + pad = ctx->L4; + } + for (u = 0; u < sizeof ctx->buf; u ++) { + ctx->buf[u] ^= pad[u]; + } + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, ctx->buf, sizeof ctx->buf); +} + +/* + * Apply CBC-MAC on the provided data, with buffering management. + * + * Upon entry, two situations are acceptable: + * + * ctx->ptr == 0: there is no data to process in ctx->buf + * ctx->ptr == 16: there is a full block of unprocessed data in ctx->buf + * + * Upon exit, ctx->ptr may be zero only if it was already zero on entry, + * and len == 0. In all other situations, ctx->ptr will be non-zero on + * exit (and may have value 16). + */ +static void +do_cbcmac_chunk(br_eax_context *ctx, const void *data, size_t len) +{ + size_t ptr; + + if (len == 0) { + return; + } + ptr = len & (size_t)15; + if (ptr == 0) { + len -= 16; + ptr = 16; + } else { + len -= ptr; + } + if (ctx->ptr == 16) { + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, data, len); + memcpy(ctx->buf, (const unsigned char *)data + len, ptr); + ctx->ptr = ptr; +} + +/* see bearssl_aead.h */ +void +br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx) +{ + unsigned char tmp[16], iv[16]; + + ctx->vtable = &br_eax_vtable; + ctx->bctx = bctx; + + /* + * Encrypt a whole-zero block to compute L2 and L4. + */ + memset(tmp, 0, sizeof tmp); + memset(iv, 0, sizeof iv); + (*bctx)->ctr(bctx, iv, tmp, sizeof tmp); + double_gf128(ctx->L2, tmp); + double_gf128(ctx->L4, ctx->L2); +} + +/* see bearssl_aead.h */ +void +br_eax_capture(const br_eax_context *ctx, br_eax_state *st) +{ + /* + * We capture the three OMAC* states _after_ processing the + * initial block (assuming that nonce, message and AAD are + * all non-empty). + */ + int i; + + memset(st->st, 0, sizeof st->st); + for (i = 0; i < 3; i ++) { + unsigned char tmp[16]; + + memset(tmp, 0, sizeof tmp); + tmp[15] = (unsigned char)i; + (*ctx->bctx)->mac(ctx->bctx, st->st[i], tmp, sizeof tmp); + } +} + +/* see bearssl_aead.h */ +void +br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len) +{ + /* + * Process nonce with OMAC^0. + */ + omac_start(ctx, 0); + do_cbcmac_chunk(ctx, nonce, len); + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + + /* + * Start OMAC^1 for the AAD ("header" in the EAX specification). + */ + omac_start(ctx, 1); + + /* + * We use ctx->head[0] as temporary flag to mark that we are + * using a "normal" reset(). + */ + ctx->head[0] = 0; +} + +/* see bearssl_aead.h */ +void +br_eax_reset_pre_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len) +{ + if (len == 0) { + omac_start(ctx, 0); + } else { + memcpy(ctx->cbcmac, st->st[0], sizeof ctx->cbcmac); + ctx->ptr = 0; + do_cbcmac_chunk(ctx, nonce, len); + } + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + + memcpy(ctx->cbcmac, st->st[1], sizeof ctx->cbcmac); + ctx->ptr = 0; + + memcpy(ctx->ctr, st->st[2], sizeof ctx->ctr); + + /* + * We use ctx->head[0] as a flag to indicate that we use a + * a recorded state, with ctx->ctr containing the preprocessed + * first block for OMAC^2. + */ + ctx->head[0] = 1; +} + +/* see bearssl_aead.h */ +void +br_eax_reset_post_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len) +{ + if (len == 0) { + omac_start(ctx, 0); + } else { + memcpy(ctx->cbcmac, st->st[0], sizeof ctx->cbcmac); + ctx->ptr = 0; + do_cbcmac_chunk(ctx, nonce, len); + } + do_pad(ctx); + memcpy(ctx->nonce, ctx->cbcmac, sizeof ctx->cbcmac); + memcpy(ctx->ctr, ctx->nonce, sizeof ctx->nonce); + + memcpy(ctx->head, st->st[1], sizeof ctx->head); + + memcpy(ctx->cbcmac, st->st[2], sizeof ctx->cbcmac); + ctx->ptr = 0; +} + +/* see bearssl_aead.h */ +void +br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len) +{ + size_t ptr; + + ptr = ctx->ptr; + + /* + * If there is a partial block, first complete it. + */ + if (ptr < 16) { + size_t clen; + + clen = 16 - ptr; + if (len <= clen) { + memcpy(ctx->buf + ptr, data, len); + ctx->ptr = ptr + len; + return; + } + memcpy(ctx->buf + ptr, data, clen); + data = (const unsigned char *)data + clen; + len -= clen; + } + + /* + * We now have a full block in buf[], and this is not the last + * block. + */ + do_cbcmac_chunk(ctx, data, len); +} + +/* see bearssl_aead.h */ +void +br_eax_flip(br_eax_context *ctx) +{ + int from_capture; + + /* + * ctx->head[0] may be non-zero if the context was reset with + * a pre-AAD captured state. In that case, ctx->ctr[] contains + * the state for OMAC^2 _after_ processing the first block. + */ + from_capture = ctx->head[0]; + + /* + * Complete the OMAC computation on the AAD. + */ + do_pad(ctx); + memcpy(ctx->head, ctx->cbcmac, sizeof ctx->cbcmac); + + /* + * Start OMAC^2 for the encrypted data. + * If the context was initialized from a captured state, then + * the OMAC^2 value is in the ctr[] array. + */ + if (from_capture) { + memcpy(ctx->cbcmac, ctx->ctr, sizeof ctx->cbcmac); + ctx->ptr = 0; + } else { + omac_start(ctx, 2); + } + + /* + * Initial counter value for CTR is the processed nonce. + */ + memcpy(ctx->ctr, ctx->nonce, sizeof ctx->nonce); +} + +/* see bearssl_aead.h */ +void +br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *dbuf; + size_t ptr; + + /* + * Ensure that there is actual data to process. + */ + if (len == 0) { + return; + } + + dbuf = data; + ptr = ctx->ptr; + + /* + * We may have ptr == 0 here if we initialized from a captured + * state. In that case, there is no partially consumed block + * or unprocessed data. + */ + if (ptr != 0 && ptr != 16) { + /* + * We have a partially consumed block. + */ + size_t u, clen; + + clen = 16 - ptr; + if (len <= clen) { + clen = len; + } + if (encrypt) { + for (u = 0; u < clen; u ++) { + ctx->buf[ptr + u] ^= dbuf[u]; + } + memcpy(dbuf, ctx->buf + ptr, clen); + } else { + for (u = 0; u < clen; u ++) { + unsigned dx, sx; + + sx = ctx->buf[ptr + u]; + dx = dbuf[u]; + ctx->buf[ptr + u] = dx; + dbuf[u] = sx ^ dx; + } + } + + if (len <= clen) { + ctx->ptr = ptr + clen; + return; + } + dbuf += clen; + len -= clen; + } + + /* + * We now have a complete encrypted block in buf[] that must still + * be processed with OMAC, and this is not the final buf. + * Exception: when ptr == 0, no block has been produced yet. + */ + if (ptr != 0) { + (*ctx->bctx)->mac(ctx->bctx, ctx->cbcmac, + ctx->buf, sizeof ctx->buf); + } + + /* + * Do CTR encryption or decryption and CBC-MAC for all full blocks + * except the last. + */ + ptr = len & (size_t)15; + if (ptr == 0) { + len -= 16; + ptr = 16; + } else { + len -= ptr; + } + if (encrypt) { + (*ctx->bctx)->encrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } else { + (*ctx->bctx)->decrypt(ctx->bctx, ctx->ctr, ctx->cbcmac, + dbuf, len); + } + dbuf += len; + + /* + * Compute next block of CTR stream, and use it to finish + * encrypting or decrypting the data. + */ + memset(ctx->buf, 0, sizeof ctx->buf); + (*ctx->bctx)->ctr(ctx->bctx, ctx->ctr, ctx->buf, sizeof ctx->buf); + if (encrypt) { + size_t u; + + for (u = 0; u < ptr; u ++) { + ctx->buf[u] ^= dbuf[u]; + } + memcpy(dbuf, ctx->buf, ptr); + } else { + size_t u; + + for (u = 0; u < ptr; u ++) { + unsigned dx, sx; + + sx = ctx->buf[u]; + dx = dbuf[u]; + ctx->buf[u] = dx; + dbuf[u] = sx ^ dx; + } + } + ctx->ptr = ptr; +} + +/* + * Complete tag computation. The final tag is written in ctx->cbcmac. + */ +static void +do_final(br_eax_context *ctx) +{ + size_t u; + + do_pad(ctx); + + /* + * Authentication tag is the XOR of the three OMAC outputs for + * the nonce, AAD and encrypted data. + */ + for (u = 0; u < 16; u ++) { + ctx->cbcmac[u] ^= ctx->nonce[u] ^ ctx->head[u]; + } +} + +/* see bearssl_aead.h */ +void +br_eax_get_tag(br_eax_context *ctx, void *tag) +{ + do_final(ctx); + memcpy(tag, ctx->cbcmac, sizeof ctx->cbcmac); +} + +/* see bearssl_aead.h */ +void +br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len) +{ + do_final(ctx); + memcpy(tag, ctx->cbcmac, len); +} + +/* see bearssl_aead.h */ +uint32_t +br_eax_check_tag_trunc(br_eax_context *ctx, const void *tag, size_t len) +{ + unsigned char tmp[16]; + size_t u; + int x; + + br_eax_get_tag(ctx, tmp); + x = 0; + for (u = 0; u < len; u ++) { + x |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(x); +} + +/* see bearssl_aead.h */ +uint32_t +br_eax_check_tag(br_eax_context *ctx, const void *tag) +{ + return br_eax_check_tag_trunc(ctx, tag, 16); +} + +/* see bearssl_aead.h */ +const br_aead_class br_eax_vtable PROGMEM = { + 16, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_eax_reset, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_eax_aad_inject, + (void (*)(const br_aead_class **)) + &br_eax_flip, + (void (*)(const br_aead_class **, int, void *, size_t)) + &br_eax_run, + (void (*)(const br_aead_class **, void *)) + &br_eax_get_tag, + (uint32_t (*)(const br_aead_class **, const void *)) + &br_eax_check_tag, + (void (*)(const br_aead_class **, void *, size_t)) + &br_eax_get_tag_trunc, + (uint32_t (*)(const br_aead_class **, const void *, size_t)) + &br_eax_check_tag_trunc +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c b/lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c new file mode 100644 index 0000000000..40084ed651 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/aead/gcm.c @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Implementation Notes + * ==================== + * + * Since CTR and GHASH implementations can handle only full blocks, a + * 16-byte buffer (buf[]) is maintained in the context: + * + * - When processing AAD, buf[] contains the 0-15 unprocessed bytes. + * + * - When doing CTR encryption / decryption, buf[] contains the AES output + * for the last partial block, to be used with the next few bytes of + * data, as well as the already encrypted bytes. For instance, if the + * processed data length so far is 21 bytes, then buf[0..4] contains + * the five last encrypted bytes, and buf[5..15] contains the next 11 + * AES output bytes to be XORed with the next 11 bytes of input. + * + * The recorded AES output bytes are used to complete the block when + * the corresponding bytes are obtained. Note that buf[] always + * contains the _encrypted_ bytes, whether we apply encryption or + * decryption: these bytes are used as input to GHASH when the block + * is complete. + * + * In both cases, the low bits of the data length counters (count_aad, + * count_ctr) are used to work out the current situation. + */ + +/* see bearssl_aead.h */ +void +br_gcm_init(br_gcm_context *ctx, const br_block_ctr_class **bctx, br_ghash gh) +{ + unsigned char iv[12]; + + ctx->vtable = &br_gcm_vtable; + ctx->bctx = bctx; + ctx->gh = gh; + + /* + * The GHASH key h[] is the raw encryption of the all-zero + * block. Since we only have a CTR implementation, we use it + * with an all-zero IV and a zero counter, to CTR-encrypt an + * all-zero block. + */ + memset(ctx->h, 0, sizeof ctx->h); + memset(iv, 0, sizeof iv); + (*bctx)->run(bctx, iv, 0, ctx->h, sizeof ctx->h); +} + +/* see bearssl_aead.h */ +void +br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len) +{ + /* + * If the provided nonce is 12 bytes, then this is the initial + * IV for CTR mode; it will be used with a counter that starts + * at 2 (value 1 is for encrypting the GHASH output into the tag). + * + * If the provided nonce has any other length, then it is hashed + * (with GHASH) into a 16-byte value that will be the IV for CTR + * (both 12-byte IV and 32-bit counter). + */ + if (len == 12) { + memcpy(ctx->j0_1, iv, 12); + ctx->j0_2 = 1; + } else { + unsigned char ty[16], tmp[16]; + + memset(ty, 0, sizeof ty); + ctx->gh(ty, ctx->h, iv, len); + memset(tmp, 0, 8); + br_enc64be(tmp + 8, (uint64_t)len << 3); + ctx->gh(ty, ctx->h, tmp, 16); + memcpy(ctx->j0_1, ty, 12); + ctx->j0_2 = br_dec32be(ty + 12); + } + ctx->jc = ctx->j0_2 + 1; + memset(ctx->y, 0, sizeof ctx->y); + ctx->count_aad = 0; + ctx->count_ctr = 0; +} + +/* see bearssl_aead.h */ +void +br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len) +{ + size_t ptr, dlen; + + ptr = (size_t)ctx->count_aad & (size_t)15; + if (ptr != 0) { + /* + * If there is a partial block, then we first try to + * complete it. + */ + size_t clen; + + clen = 16 - ptr; + if (len < clen) { + memcpy(ctx->buf + ptr, data, len); + ctx->count_aad += (uint64_t)len; + return; + } + memcpy(ctx->buf + ptr, data, clen); + ctx->gh(ctx->y, ctx->h, ctx->buf, 16); + data = (const unsigned char *)data + clen; + len -= clen; + ctx->count_aad += (uint64_t)clen; + } + + /* + * Now AAD is aligned on a 16-byte block (with regards to GHASH). + * We process all complete blocks, and save the last partial + * block. + */ + dlen = len & ~(size_t)15; + ctx->gh(ctx->y, ctx->h, data, dlen); + memcpy(ctx->buf, (const unsigned char *)data + dlen, len - dlen); + ctx->count_aad += (uint64_t)len; +} + +/* see bearssl_aead.h */ +void +br_gcm_flip(br_gcm_context *ctx) +{ + /* + * We complete the GHASH computation if there is a partial block. + * The GHASH implementation automatically applies padding with + * zeros. + */ + size_t ptr; + + ptr = (size_t)ctx->count_aad & (size_t)15; + if (ptr != 0) { + ctx->gh(ctx->y, ctx->h, ctx->buf, ptr); + } +} + +/* see bearssl_aead.h */ +void +br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len) +{ + unsigned char *buf; + size_t ptr, dlen; + + buf = data; + ptr = (size_t)ctx->count_ctr & (size_t)15; + if (ptr != 0) { + /* + * If we have a partial block, then we try to complete it. + */ + size_t u, clen; + + clen = 16 - ptr; + if (len < clen) { + clen = len; + } + for (u = 0; u < clen; u ++) { + unsigned x, y; + + x = buf[u]; + y = x ^ ctx->buf[ptr + u]; + ctx->buf[ptr + u] = encrypt ? y : x; + buf[u] = y; + } + ctx->count_ctr += (uint64_t)clen; + buf += clen; + len -= clen; + if (ptr + clen < 16) { + return; + } + ctx->gh(ctx->y, ctx->h, ctx->buf, 16); + } + + /* + * Process full blocks. + */ + dlen = len & ~(size_t)15; + if (!encrypt) { + ctx->gh(ctx->y, ctx->h, buf, dlen); + } + ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->jc, buf, dlen); + if (encrypt) { + ctx->gh(ctx->y, ctx->h, buf, dlen); + } + buf += dlen; + len -= dlen; + ctx->count_ctr += (uint64_t)dlen; + + if (len > 0) { + /* + * There is a partial block. + */ + size_t u; + + memset(ctx->buf, 0, sizeof ctx->buf); + ctx->jc = (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, + ctx->jc, ctx->buf, 16); + for (u = 0; u < len; u ++) { + unsigned x, y; + + x = buf[u]; + y = x ^ ctx->buf[u]; + ctx->buf[u] = encrypt ? y : x; + buf[u] = y; + } + ctx->count_ctr += (uint64_t)len; + } +} + +/* see bearssl_aead.h */ +void +br_gcm_get_tag(br_gcm_context *ctx, void *tag) +{ + size_t ptr; + unsigned char tmp[16]; + + ptr = (size_t)ctx->count_ctr & (size_t)15; + if (ptr > 0) { + /* + * There is a partial block: encrypted/decrypted data has + * been produced, but the encrypted bytes must still be + * processed by GHASH. + */ + ctx->gh(ctx->y, ctx->h, ctx->buf, ptr); + } + + /* + * Final block for GHASH: the AAD and plaintext lengths (in bits). + */ + br_enc64be(tmp, ctx->count_aad << 3); + br_enc64be(tmp + 8, ctx->count_ctr << 3); + ctx->gh(ctx->y, ctx->h, tmp, 16); + + /* + * Tag is the GHASH output XORed with the encryption of the + * nonce with the initial counter value. + */ + memcpy(tag, ctx->y, 16); + (*ctx->bctx)->run(ctx->bctx, ctx->j0_1, ctx->j0_2, tag, 16); +} + +/* see bearssl_aead.h */ +void +br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len) +{ + unsigned char tmp[16]; + + br_gcm_get_tag(ctx, tmp); + memcpy(tag, tmp, len); +} + +/* see bearssl_aead.h */ +uint32_t +br_gcm_check_tag_trunc(br_gcm_context *ctx, const void *tag, size_t len) +{ + unsigned char tmp[16]; + size_t u; + int x; + + br_gcm_get_tag(ctx, tmp); + x = 0; + for (u = 0; u < len; u ++) { + x |= tmp[u] ^ ((const unsigned char *)tag)[u]; + } + return EQ0(x); +} + +/* see bearssl_aead.h */ +uint32_t +br_gcm_check_tag(br_gcm_context *ctx, const void *tag) +{ + return br_gcm_check_tag_trunc(ctx, tag, 16); +} + +/* see bearssl_aead.h */ +const br_aead_class br_gcm_vtable PROGMEM = { + 16, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_reset, + (void (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_aad_inject, + (void (*)(const br_aead_class **)) + &br_gcm_flip, + (void (*)(const br_aead_class **, int, void *, size_t)) + &br_gcm_run, + (void (*)(const br_aead_class **, void *)) + &br_gcm_get_tag, + (uint32_t (*)(const br_aead_class **, const void *)) + &br_gcm_check_tag, + (void (*)(const br_aead_class **, void *, size_t)) + &br_gcm_get_tag_trunc, + (uint32_t (*)(const br_aead_class **, const void *, size_t)) + &br_gcm_check_tag_trunc +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c b/lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c new file mode 100644 index 0000000000..33f4b1a49f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/ccopy.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + uint32_t x, y; + + x = *s ++; + y = *d; + *d = MUX(ctl, x, y); + d ++; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c new file mode 100644 index 0000000000..af7b48074a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec16be(uint16_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec16be(buf); + buf += 2; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c new file mode 100644 index 0000000000..4bf0174b91 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec16le(uint16_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec16le(buf); + buf += 2; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c new file mode 100644 index 0000000000..51016e5b97 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec32be(uint32_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec32be(buf); + buf += 4; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c new file mode 100644 index 0000000000..22b2c01a8e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec32le(uint32_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec32le(buf); + buf += 4; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c new file mode 100644 index 0000000000..55632e4a5b --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec64be(uint64_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec64be(buf); + buf += 8; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c new file mode 100644 index 0000000000..e77614a5cc --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/dec64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_dec64le(uint64_t *v, size_t num, const void *src) +{ + const unsigned char *buf; + + buf = src; + while (num -- > 0) { + *v ++ = br_dec64le(buf); + buf += 8; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c new file mode 100644 index 0000000000..be29cd6e9e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc16be(void *dst, const uint16_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc16be(buf, *v ++); + buf += 2; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c new file mode 100644 index 0000000000..d5f6f7ea0e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc16le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc16le(void *dst, const uint16_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc16le(buf, *v ++); + buf += 2; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c new file mode 100644 index 0000000000..89fad30d66 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc32be(void *dst, const uint32_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc32be(buf, *v ++); + buf += 4; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c new file mode 100644 index 0000000000..4fae447745 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc32le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc32le(void *dst, const uint32_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc32le(buf, *v ++); + buf += 4; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c new file mode 100644 index 0000000000..d06ffebbd8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64be.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc64be(void *dst, const uint64_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc64be(buf, *v ++); + buf += 8; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c new file mode 100644 index 0000000000..8e2f8e8877 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/enc64le.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_range_enc64le(void *dst, const uint64_t *v, size_t num) +{ + unsigned char *buf; + + buf = dst; + while (num -- > 0) { + br_enc64le(buf, *v ++); + buf += 8; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c b/lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c new file mode 100644 index 0000000000..0de8d34148 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/pemdec.c @@ -0,0 +1,536 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_pem_decoder_init_main(void *t0ctx); + +void br_pem_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + +#define CTX ((br_pem_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_pem_decoder_context, cpu))) + +/* see bearssl_pem.h */ +void +br_pem_decoder_init(br_pem_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_pem_decoder_init_main(&ctx->cpu); + br_pem_decoder_run(&ctx->cpu); +} + +/* see bearssl_pem.h */ +size_t +br_pem_decoder_push(br_pem_decoder_context *ctx, + const void *data, size_t len) +{ + if (ctx->event) { + return 0; + } + ctx->hbuf = data; + ctx->hlen = len; + br_pem_decoder_run(&ctx->cpu); + return len - ctx->hlen; +} + +/* see bearssl_pem.h */ +int +br_pem_decoder_event(br_pem_decoder_context *ctx) +{ + int event; + + event = ctx->event; + ctx->event = 0; + return event; +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, + 0x00, 0x2D, 0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x09, 0x00, 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, + 0x01, 0x08, 0x00, 0x00, 0x13, 0x13, 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_pem_decoder_context, event)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_pem_decoder_context, name)), 0x00, 0x00, 0x05, + 0x14, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x03, 0x13, 0x04, 0x76, 0x01, + 0x2D, 0x0C, 0x06, 0x05, 0x2E, 0x01, 0x03, 0x2D, 0x00, 0x01, 0x0D, 0x27, + 0x05, 0x04, 0x01, 0x03, 0x2D, 0x00, 0x15, 0x2E, 0x01, 0x02, 0x2D, 0x00, + 0x01, 0x01, 0x7F, 0x03, 0x00, 0x25, 0x01, 0x00, 0x18, 0x0D, 0x06, 0x03, + 0x13, 0x04, 0x3C, 0x01, 0x7F, 0x18, 0x0D, 0x06, 0x13, 0x13, 0x02, 0x00, + 0x05, 0x06, 0x2E, 0x01, 0x03, 0x2D, 0x04, 0x03, 0x01, 0x7F, 0x23, 0x01, + 0x00, 0x00, 0x04, 0x23, 0x01, 0x01, 0x18, 0x0D, 0x06, 0x09, 0x13, 0x01, + 0x00, 0x23, 0x01, 0x00, 0x00, 0x04, 0x14, 0x01, 0x02, 0x18, 0x0D, 0x06, + 0x06, 0x13, 0x01, 0x7F, 0x00, 0x04, 0x08, 0x13, 0x01, 0x03, 0x2D, 0x01, + 0x00, 0x00, 0x13, 0x01, 0x00, 0x03, 0x00, 0x04, 0xFF, 0x33, 0x01, 0x2C, + 0x14, 0x01, 0x2D, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x7F, 0x00, 0x14, 0x31, + 0x06, 0x02, 0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, + 0x02, 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, + 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03, + 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02, + 0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, + 0x06, 0x04, 0x13, 0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x2E, + 0x13, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, 0x01, 0x03, 0x00, + 0x2F, 0x05, 0x04, 0x13, 0x01, 0x03, 0x00, 0x01, 0x3D, 0x0C, 0x06, 0x03, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x0F, 0x10, 0x06, 0x03, 0x01, 0x03, + 0x00, 0x02, 0x00, 0x01, 0x04, 0x0F, 0x1C, 0x01, 0x01, 0x00, 0x16, 0x14, + 0x1D, 0x06, 0x05, 0x13, 0x2E, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06, + 0x0A, 0x07, 0x03, 0x00, 0x29, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x04, 0x13, + 0x01, 0x03, 0x00, 0x14, 0x01, 0x3D, 0x0D, 0x06, 0x20, 0x13, 0x2F, 0x05, + 0x03, 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x03, 0x10, 0x06, 0x03, 0x01, + 0x03, 0x00, 0x02, 0x00, 0x01, 0x0A, 0x0F, 0x1C, 0x02, 0x00, 0x01, 0x02, + 0x0F, 0x1C, 0x01, 0x01, 0x00, 0x16, 0x14, 0x1D, 0x06, 0x05, 0x13, 0x2E, + 0x01, 0x03, 0x00, 0x02, 0x00, 0x01, 0x06, 0x0A, 0x07, 0x03, 0x00, 0x02, + 0x00, 0x01, 0x10, 0x0F, 0x1C, 0x02, 0x00, 0x01, 0x08, 0x0F, 0x1C, 0x02, + 0x00, 0x1C, 0x01, 0x00, 0x00, 0x00, 0x28, 0x01, 0x01, 0x2D, 0x24, 0x06, + 0x02, 0x04, 0x7B, 0x04, 0x75, 0x00, 0x14, 0x12, 0x2A, 0x14, 0x05, 0x04, + 0x20, 0x01, 0x7F, 0x00, 0x2C, 0x2A, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x05, + 0x13, 0x20, 0x01, 0x00, 0x00, 0x0D, 0x05, 0x05, 0x13, 0x2E, 0x01, 0x00, + 0x00, 0x1E, 0x04, 0x5E, 0x00, 0x01, 0x01, 0x27, 0x06, 0x0B, 0x22, 0x01, + 0x80, 0x7F, 0x2B, 0x14, 0x06, 0x02, 0x30, 0x00, 0x13, 0x04, 0x6E, 0x00, + 0x2C, 0x14, 0x31, 0x05, 0x01, 0x00, 0x13, 0x04, 0x77, 0x00, 0x14, 0x14, + 0x01, 0x80, 0x61, 0x0E, 0x1B, 0x01, 0x80, 0x7A, 0x0B, 0x10, 0x06, 0x03, + 0x01, 0x20, 0x08, 0x00, 0x01, 0x14, 0x03, 0x00, 0x1B, 0x18, 0x05, 0x05, + 0x20, 0x2E, 0x01, 0x00, 0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0D, 0x06, 0x06, + 0x20, 0x02, 0x00, 0x1B, 0x08, 0x00, 0x14, 0x01, 0x0D, 0x0D, 0x06, 0x03, + 0x13, 0x04, 0x03, 0x2A, 0x18, 0x1A, 0x1E, 0x1B, 0x1F, 0x1B, 0x04, 0x59, + 0x00, 0x19, 0x14, 0x1D, 0x05, 0x01, 0x00, 0x13, 0x11, 0x04, 0x76, 0x00, + 0x21, 0x1A, 0x11, 0x00, 0x00, 0x2C, 0x01, 0x0A, 0x0C, 0x06, 0x02, 0x04, + 0x78, 0x00, 0x01, 0x01, 0x7F, 0x03, 0x00, 0x2C, 0x14, 0x01, 0x0A, 0x0C, + 0x06, 0x09, 0x31, 0x05, 0x04, 0x01, 0x00, 0x03, 0x00, 0x04, 0x70, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x14, 0x06, 0x14, 0x1F, 0x14, 0x22, 0x07, 0x17, + 0x01, 0x2D, 0x0C, 0x06, 0x08, 0x22, 0x07, 0x1E, 0x01, 0x00, 0x1B, 0x1A, + 0x00, 0x04, 0x69, 0x22, 0x1A, 0x00, 0x00, 0x14, 0x01, 0x0A, 0x0C, 0x1B, + 0x01, 0x20, 0x0B, 0x10, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 19, + 24, + 29, + 67, + 149, + 384, + 396, + 431, + 450, + 460, + 479, + 523, + 534, + 539, + 549, + 574, + 601 +}; + +#define T0_INTERPRETED 29 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_pem_decoder_init_main, 38) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_pem_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 10: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 11: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 12: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 13: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 14: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 15: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 16: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 17: { + /* co */ + T0_CO(); + } + break; + case 18: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 19: { + /* drop */ + (void)T0_POP(); + } + break; + case 20: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 21: { + /* flush-buf */ + + if (CTX->ptr > 0) { + if (CTX->dest) { + CTX->dest(CTX->dest_ctx, CTX->buf, CTX->ptr); + } + CTX->ptr = 0; + } + + } + break; + case 22: { + /* from-base64 */ + + uint32_t c = T0_POP(); + uint32_t p, q, r, z; + p = c - 0x41; + q = c - 0x61; + r = c - 0x30; + + z = ((p + 2) & -LT(p, 26)) + | ((q + 28) & -LT(q, 26)) + | ((r + 54) & -LT(r, 10)) + | (64 & -EQ(c, 0x2B)) + | (65 & -EQ(c, 0x2F)) + | EQ(c, 0x3D); + T0_PUSHi((int32_t)z - 2); + + } + break; + case 23: { + /* get8 */ + + size_t addr = T0_POP(); + T0_PUSH(*((unsigned char *)CTX + addr)); + + } + break; + case 24: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 25: { + /* read8-native */ + + do { + if (CTX->hlen > 0) { + uint8_t ch = pgm_read_byte(CTX->hbuf ++); + CTX->hlen --; + if (ch == '\r') continue; // skip \rs + T0_PUSH(ch); + break; + } else { + T0_PUSHi(-1); + break; + } + } while (1); + + } + break; + case 26: { + /* set8 */ + + size_t addr = T0_POP(); + unsigned x = T0_POP(); + *((unsigned char *)CTX + addr) = x; + + } + break; + case 27: { + /* swap */ + T0_SWAP(); + } + break; + case 28: { + /* write8 */ + + unsigned char x = (unsigned char)T0_POP(); + CTX->buf[CTX->ptr ++] = x; + if (CTX->ptr == sizeof CTX->buf) { + if (CTX->dest) { + CTX->dest(CTX->dest_ctx, CTX->buf, sizeof CTX->buf); + } + CTX->ptr = 0; + } + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c b/lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c new file mode 100644 index 0000000000..b6475cd683 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/codec/pemenc.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Get the appropriate Base64 character for a numeric value in the + * 0..63 range. This is constant-time. + */ +static char +b64char(uint32_t x) +{ + /* + * Values 0 to 25 map to 0x41..0x5A ('A' to 'Z') + * Values 26 to 51 map to 0x61..0x7A ('a' to 'z') + * Values 52 to 61 map to 0x30..0x39 ('0' to '9') + * Value 62 maps to 0x2B ('+') + * Value 63 maps to 0x2F ('/') + */ + uint32_t a, b, c; + + a = x - 26; + b = x - 52; + c = x - 62; + + /* + * Looking at bits 8..15 of values a, b and c: + * + * x a b c + * --------------------- + * 0..25 FF FF FF + * 26..51 00 FF FF + * 52..61 00 00 FF + * 62..63 00 00 00 + */ + return (char)(((x + 0x41) & ((a & b & c) >> 8)) + | ((x + (0x61 - 26)) & ((~a & b & c) >> 8)) + | ((x - (52 - 0x30)) & ((~a & ~b & c) >> 8)) + | ((0x2B + ((x & 1) << 2)) & (~(a | b | c) >> 8))); +} + +/* see bearssl_pem.h */ +size_t +br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags) +{ + size_t dlen, banner_len, lines; + char *d; + unsigned char *buf; + size_t u; + int off, lim; + + banner_len = strlen(banner); + /* FIXME: try to avoid divisions here, as they may pull + an extra libc function. */ + if ((flags & BR_PEM_LINE64) != 0) { + lines = (len + 47) / 48; + } else { + lines = (len + 56) / 57; + } + dlen = (banner_len << 1) + 30 + (((len + 2) / 3) << 2) + + lines + 2; + if ((flags & BR_PEM_CRLF) != 0) { + dlen += lines + 2; + } + + if (dest == NULL) { + return dlen; + } + + d = dest; + + /* + * We always move the source data to the end of output buffer; + * the encoding process never "catches up" except at the very + * end. This also handles all conditions of partial or total + * overlap. + */ + buf = (unsigned char *)d + dlen - len; + memmove(buf, data, len); + + memcpy(d, "-----BEGIN ", 11); + d += 11; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + off = 0; + lim = (flags & BR_PEM_LINE64) != 0 ? 16 : 19; + for (u = 0; (u + 2) < len; u += 3) { + uint32_t w; + + w = ((uint32_t)buf[u] << 16) + | ((uint32_t)buf[u + 1] << 8) + | (uint32_t)buf[u + 2]; + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + *d ++ = b64char((w >> 6) & 0x3F); + *d ++ = b64char(w & 0x3F); + if (++ off == lim) { + off = 0; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + } + if (u < len) { + uint32_t w; + + w = (uint32_t)buf[u] << 16; + if (u + 1 < len) { + w |= (uint32_t)buf[u + 1] << 8; + } + *d ++ = b64char(w >> 18); + *d ++ = b64char((w >> 12) & 0x3F); + if (u + 1 < len) { + *d ++ = b64char((w >> 6) & 0x3F); + } else { + *d ++ = 0x3D; + } + *d ++ = 0x3D; + off ++; + } + if (off != 0) { + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + } + + memcpy(d, "-----END ", 9); + d += 9; + memcpy(d, banner, banner_len); + d += banner_len; + memcpy(d, "-----", 5); + d += 5; + if ((flags & BR_PEM_CRLF) != 0) { + *d ++ = 0x0D; + } + *d ++ = 0x0A; + + /* Final zero, not counted in returned length. */ + *d ++ = 0x00; + + return dlen; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c new file mode 100644 index 0000000000..a8708dd912 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_all_m15.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.generator(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.generator(curve, len); + default: + return br_ec_prime_i15.generator(curve, len); + } +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.order(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.order(curve, len); + default: + return br_ec_prime_i15.order(curve, len); + } +} + +static size_t +api_xoff(int curve, size_t *len) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.xoff(curve, len); + case BR_EC_curve25519: + return br_ec_c25519_m15.xoff(curve, len); + default: + return br_ec_prime_i15.xoff(curve, len); + } +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *kb, size_t kblen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.mul(G, Glen, kb, kblen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.mul(G, Glen, kb, kblen, curve); + default: + return br_ec_prime_i15.mul(G, Glen, kb, kblen, curve); + } +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.mulgen(R, x, xlen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.mulgen(R, x, xlen, curve); + default: + return br_ec_prime_i15.mulgen(R, x, xlen, curve); + } +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return br_ec_p256_m15.muladd(A, B, len, + x, xlen, y, ylen, curve); + case BR_EC_curve25519: + return br_ec_c25519_m15.muladd(A, B, len, + x, xlen, y, ylen, curve); + default: + return br_ec_prime_i15.muladd(A, B, len, + x, xlen, y, ylen, curve); + } +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_all_m15 PROGMEM = { + (uint32_t)0x23800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_m15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_m15.c new file mode 100644 index 0000000000..115d51a63e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_c25519_m15.c @@ -0,0 +1,1478 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* obsolete +#include +#include +static void +print_int(const char *name, const uint32_t *x) +{ + size_t u; + unsigned char tmp[36]; + + printf("%s = ", name); + for (u = 0; u < 20; u ++) { + if (x[u] > 0x1FFF) { + printf("INVALID:"); + for (u = 0; u < 20; u ++) { + printf(" %04X", x[u]); + } + printf("\n"); + return; + } + } + memset(tmp, 0, sizeof tmp); + for (u = 0; u < 20; u ++) { + uint32_t w; + int j, k; + + w = x[u]; + j = 13 * (int)u; + k = j & 7; + if (k != 0) { + w <<= k; + j -= k; + } + k = j >> 3; + tmp[35 - k] |= (unsigned char)w; + tmp[34 - k] |= (unsigned char)(w >> 8); + tmp[33 - k] |= (unsigned char)(w >> 16); + tmp[32 - k] |= (unsigned char)(w >> 24); + } + for (u = 4; u < 36; u ++) { + printf("%02X", tmp[u]); + } + printf("\n"); +} +*/ + +/* + * If BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_ + * that right-shifting a signed negative integer copies the sign bit + * (arithmetic right-shift). This is "implementation-defined behaviour", + * i.e. it is not undefined, but it may differ between compilers. Each + * compiler is supposed to document its behaviour in that respect. GCC + * explicitly defines that an arithmetic right shift is used. We expect + * all other compilers to do the same, because underlying CPU offer an + * arithmetic right shift opcode that could not be used otherwise. + */ +#if BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#else +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n)) +#endif + +/* + * Convert an integer from unsigned little-endian encoding to a sequence of + * 13-bit words in little-endian order. The final "partial" word is + * returned. + */ +static uint32_t +le8_to_le13(uint32_t *dst, const unsigned char *src, size_t len) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + acc |= (uint32_t)(*src ++) << acc_len; + acc_len += 8; + if (acc_len >= 13) { + *dst ++ = acc & 0x1FFF; + acc >>= 13; + acc_len -= 13; + } + } + return acc; +} + +/* + * Convert an integer (13-bit words, little-endian) to unsigned + * little-endian encoding. The total encoding length is provided; all + * the destination bytes will be filled. + */ +static void +le13_to_le8(unsigned char *dst, size_t len, const uint32_t *src) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + if (acc_len < 8) { + acc |= (*src ++) << acc_len; + acc_len += 13; + } + *dst ++ = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } +} + +/* + * Normalise an array of words to a strict 13 bits per word. Returned + * value is the resulting carry. The source (w) and destination (d) + * arrays may be identical, but shall not overlap partially. + */ +static inline uint32_t +norm13(uint32_t *d, const uint32_t *w, size_t len) +{ + size_t u; + uint32_t cc; + + cc = 0; + for (u = 0; u < len; u ++) { + int32_t z; + + z = w[u] + cc; + d[u] = z & 0x1FFF; + cc = ARSH(z, 13); + } + return cc; +} + +/* + * mul20() multiplies two 260-bit integers together. Each word must fit + * on 13 bits; source operands use 20 words, destination operand + * receives 40 words. All overlaps allowed. + * + * square20() computes the square of a 260-bit integer. Each word must + * fit on 13 bits; source operand uses 20 words, destination operand + * receives 40 words. All overlaps allowed. + */ + +#if BR_SLOW_MUL15 + +static void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * Two-level Karatsuba: turns a 20x20 multiplication into + * nine 5x5 multiplications. We use 13-bit words but do not + * propagate carries immediately, so words may expand: + * + * - First Karatsuba decomposition turns the 20x20 mul on + * 13-bit words into three 10x10 muls, two on 13-bit words + * and one on 14-bit words. + * + * - Second Karatsuba decomposition further splits these into: + * + * * four 5x5 muls on 13-bit words + * * four 5x5 muls on 14-bit words + * * one 5x5 mul on 15-bit words + * + * Highest word value is 8191, 16382 or 32764, for 13-bit, 14-bit + * or 15-bit words, respectively. + */ + uint32_t u[45], v[45], w[90]; + uint32_t cc; + int i; + +#define ZADD(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] = (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] = (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] = (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] = (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] = (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define ZADDT(dw, d_off, sw, s_off) do { \ + (dw)[5 * (d_off) + 0] += (sw)[5 * (s_off) + 0]; \ + (dw)[5 * (d_off) + 1] += (sw)[5 * (s_off) + 1]; \ + (dw)[5 * (d_off) + 2] += (sw)[5 * (s_off) + 2]; \ + (dw)[5 * (d_off) + 3] += (sw)[5 * (s_off) + 3]; \ + (dw)[5 * (d_off) + 4] += (sw)[5 * (s_off) + 4]; \ + } while (0) + +#define ZSUB2F(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] -= (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] -= (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] -= (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] -= (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] -= (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define CPR1(w, cprcc) do { \ + uint32_t cprz = (w) + cprcc; \ + (w) = cprz & 0x1FFF; \ + cprcc = cprz >> 13; \ + } while (0) + +#define CPR(dw, d_off) do { \ + uint32_t cprcc; \ + cprcc = 0; \ + CPR1((dw)[(d_off) + 0], cprcc); \ + CPR1((dw)[(d_off) + 1], cprcc); \ + CPR1((dw)[(d_off) + 2], cprcc); \ + CPR1((dw)[(d_off) + 3], cprcc); \ + CPR1((dw)[(d_off) + 4], cprcc); \ + CPR1((dw)[(d_off) + 5], cprcc); \ + CPR1((dw)[(d_off) + 6], cprcc); \ + CPR1((dw)[(d_off) + 7], cprcc); \ + CPR1((dw)[(d_off) + 8], cprcc); \ + (dw)[(d_off) + 9] = cprcc; \ + } while (0) + + memcpy_P(u, a, 20 * sizeof *a); + ZADD(u, 4, a, 0, a, 1); + ZADD(u, 5, a, 2, a, 3); + ZADD(u, 6, a, 0, a, 2); + ZADD(u, 7, a, 1, a, 3); + ZADD(u, 8, u, 6, u, 7); + + memcpy_P(v, b, 20 * sizeof *b); + ZADD(v, 4, b, 0, b, 1); + ZADD(v, 5, b, 2, b, 3); + ZADD(v, 6, b, 0, b, 2); + ZADD(v, 7, b, 1, b, 3); + ZADD(v, 8, v, 6, v, 7); + + /* + * Do the eight first 8x8 muls. Source words are at most 16382 + * each, so we can add product results together "as is" in 32-bit + * words. + */ + for (i = 0; i < 40; i += 5) { + w[(i << 1) + 0] = MUL15(u[i + 0], v[i + 0]); + w[(i << 1) + 1] = MUL15(u[i + 0], v[i + 1]) + + MUL15(u[i + 1], v[i + 0]); + w[(i << 1) + 2] = MUL15(u[i + 0], v[i + 2]) + + MUL15(u[i + 1], v[i + 1]) + + MUL15(u[i + 2], v[i + 0]); + w[(i << 1) + 3] = MUL15(u[i + 0], v[i + 3]) + + MUL15(u[i + 1], v[i + 2]) + + MUL15(u[i + 2], v[i + 1]) + + MUL15(u[i + 3], v[i + 0]); + w[(i << 1) + 4] = MUL15(u[i + 0], v[i + 4]) + + MUL15(u[i + 1], v[i + 3]) + + MUL15(u[i + 2], v[i + 2]) + + MUL15(u[i + 3], v[i + 1]) + + MUL15(u[i + 4], v[i + 0]); + w[(i << 1) + 5] = MUL15(u[i + 1], v[i + 4]) + + MUL15(u[i + 2], v[i + 3]) + + MUL15(u[i + 3], v[i + 2]) + + MUL15(u[i + 4], v[i + 1]); + w[(i << 1) + 6] = MUL15(u[i + 2], v[i + 4]) + + MUL15(u[i + 3], v[i + 3]) + + MUL15(u[i + 4], v[i + 2]); + w[(i << 1) + 7] = MUL15(u[i + 3], v[i + 4]) + + MUL15(u[i + 4], v[i + 3]); + w[(i << 1) + 8] = MUL15(u[i + 4], v[i + 4]); + w[(i << 1) + 9] = 0; + } + + /* + * For the 9th multiplication, source words are up to 32764, + * so we must do some carry propagation. If we add up to + * 4 products and the carry is no more than 524224, then the + * result fits in 32 bits, and the next carry will be no more + * than 524224 (because 4*(32764^2)+524224 < 8192*524225). + * + * We thus just skip one of the products in the middle word, + * then do a carry propagation (this reduces words to 13 bits + * each, except possibly the last, which may use up to 17 bits + * or so), then add the missing product. + */ + w[80 + 0] = MUL15(u[40 + 0], v[40 + 0]); + w[80 + 1] = MUL15(u[40 + 0], v[40 + 1]) + + MUL15(u[40 + 1], v[40 + 0]); + w[80 + 2] = MUL15(u[40 + 0], v[40 + 2]) + + MUL15(u[40 + 1], v[40 + 1]) + + MUL15(u[40 + 2], v[40 + 0]); + w[80 + 3] = MUL15(u[40 + 0], v[40 + 3]) + + MUL15(u[40 + 1], v[40 + 2]) + + MUL15(u[40 + 2], v[40 + 1]) + + MUL15(u[40 + 3], v[40 + 0]); + w[80 + 4] = MUL15(u[40 + 0], v[40 + 4]) + + MUL15(u[40 + 1], v[40 + 3]) + + MUL15(u[40 + 2], v[40 + 2]) + + MUL15(u[40 + 3], v[40 + 1]); + /* + MUL15(u[40 + 4], v[40 + 0]) */ + w[80 + 5] = MUL15(u[40 + 1], v[40 + 4]) + + MUL15(u[40 + 2], v[40 + 3]) + + MUL15(u[40 + 3], v[40 + 2]) + + MUL15(u[40 + 4], v[40 + 1]); + w[80 + 6] = MUL15(u[40 + 2], v[40 + 4]) + + MUL15(u[40 + 3], v[40 + 3]) + + MUL15(u[40 + 4], v[40 + 2]); + w[80 + 7] = MUL15(u[40 + 3], v[40 + 4]) + + MUL15(u[40 + 4], v[40 + 3]); + w[80 + 8] = MUL15(u[40 + 4], v[40 + 4]); + + CPR(w, 80); + + w[80 + 4] += MUL15(u[40 + 4], v[40 + 0]); + + /* + * The products on 14-bit words in slots 6 and 7 yield values + * up to 5*(16382^2) each, and we need to subtract two such + * values from the higher word. We need the subtraction to fit + * in a _signed_ 32-bit integer, i.e. 31 bits + a sign bit. + * However, 10*(16382^2) does not fit. So we must perform a + * bit of reduction here. + */ + CPR(w, 60); + CPR(w, 70); + + /* + * Recompose results. + */ + + /* 0..1*0..1 into 0..3 */ + ZSUB2F(w, 8, w, 0, w, 2); + ZSUB2F(w, 9, w, 1, w, 3); + ZADDT(w, 1, w, 8); + ZADDT(w, 2, w, 9); + + /* 2..3*2..3 into 4..7 */ + ZSUB2F(w, 10, w, 4, w, 6); + ZSUB2F(w, 11, w, 5, w, 7); + ZADDT(w, 5, w, 10); + ZADDT(w, 6, w, 11); + + /* (0..1+2..3)*(0..1+2..3) into 12..15 */ + ZSUB2F(w, 16, w, 12, w, 14); + ZSUB2F(w, 17, w, 13, w, 15); + ZADDT(w, 13, w, 16); + ZADDT(w, 14, w, 17); + + /* first-level recomposition */ + ZSUB2F(w, 12, w, 0, w, 4); + ZSUB2F(w, 13, w, 1, w, 5); + ZSUB2F(w, 14, w, 2, w, 6); + ZSUB2F(w, 15, w, 3, w, 7); + ZADDT(w, 2, w, 12); + ZADDT(w, 3, w, 13); + ZADDT(w, 4, w, 14); + ZADDT(w, 5, w, 15); + + /* + * Perform carry propagation to bring all words down to 13 bits. + */ + cc = norm13(d, w, 40); + d[39] += (cc << 13); + +#undef ZADD +#undef ZADDT +#undef ZSUB2F +#undef CPR1 +#undef CPR +} + +static inline void +square20(uint32_t *d, const uint32_t *a) +{ + mul20(d, a, a); +} + +#else + +/*static*/ void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], b[ 0]); + t[ 1] = MUL15(a[ 0], b[ 1]) + + MUL15(a[ 1], b[ 0]); + t[ 2] = MUL15(a[ 0], b[ 2]) + + MUL15(a[ 1], b[ 1]) + + MUL15(a[ 2], b[ 0]); + t[ 3] = MUL15(a[ 0], b[ 3]) + + MUL15(a[ 1], b[ 2]) + + MUL15(a[ 2], b[ 1]) + + MUL15(a[ 3], b[ 0]); + t[ 4] = MUL15(a[ 0], b[ 4]) + + MUL15(a[ 1], b[ 3]) + + MUL15(a[ 2], b[ 2]) + + MUL15(a[ 3], b[ 1]) + + MUL15(a[ 4], b[ 0]); + t[ 5] = MUL15(a[ 0], b[ 5]) + + MUL15(a[ 1], b[ 4]) + + MUL15(a[ 2], b[ 3]) + + MUL15(a[ 3], b[ 2]) + + MUL15(a[ 4], b[ 1]) + + MUL15(a[ 5], b[ 0]); + t[ 6] = MUL15(a[ 0], b[ 6]) + + MUL15(a[ 1], b[ 5]) + + MUL15(a[ 2], b[ 4]) + + MUL15(a[ 3], b[ 3]) + + MUL15(a[ 4], b[ 2]) + + MUL15(a[ 5], b[ 1]) + + MUL15(a[ 6], b[ 0]); + t[ 7] = MUL15(a[ 0], b[ 7]) + + MUL15(a[ 1], b[ 6]) + + MUL15(a[ 2], b[ 5]) + + MUL15(a[ 3], b[ 4]) + + MUL15(a[ 4], b[ 3]) + + MUL15(a[ 5], b[ 2]) + + MUL15(a[ 6], b[ 1]) + + MUL15(a[ 7], b[ 0]); + t[ 8] = MUL15(a[ 0], b[ 8]) + + MUL15(a[ 1], b[ 7]) + + MUL15(a[ 2], b[ 6]) + + MUL15(a[ 3], b[ 5]) + + MUL15(a[ 4], b[ 4]) + + MUL15(a[ 5], b[ 3]) + + MUL15(a[ 6], b[ 2]) + + MUL15(a[ 7], b[ 1]) + + MUL15(a[ 8], b[ 0]); + t[ 9] = MUL15(a[ 0], b[ 9]) + + MUL15(a[ 1], b[ 8]) + + MUL15(a[ 2], b[ 7]) + + MUL15(a[ 3], b[ 6]) + + MUL15(a[ 4], b[ 5]) + + MUL15(a[ 5], b[ 4]) + + MUL15(a[ 6], b[ 3]) + + MUL15(a[ 7], b[ 2]) + + MUL15(a[ 8], b[ 1]) + + MUL15(a[ 9], b[ 0]); + t[10] = MUL15(a[ 0], b[10]) + + MUL15(a[ 1], b[ 9]) + + MUL15(a[ 2], b[ 8]) + + MUL15(a[ 3], b[ 7]) + + MUL15(a[ 4], b[ 6]) + + MUL15(a[ 5], b[ 5]) + + MUL15(a[ 6], b[ 4]) + + MUL15(a[ 7], b[ 3]) + + MUL15(a[ 8], b[ 2]) + + MUL15(a[ 9], b[ 1]) + + MUL15(a[10], b[ 0]); + t[11] = MUL15(a[ 0], b[11]) + + MUL15(a[ 1], b[10]) + + MUL15(a[ 2], b[ 9]) + + MUL15(a[ 3], b[ 8]) + + MUL15(a[ 4], b[ 7]) + + MUL15(a[ 5], b[ 6]) + + MUL15(a[ 6], b[ 5]) + + MUL15(a[ 7], b[ 4]) + + MUL15(a[ 8], b[ 3]) + + MUL15(a[ 9], b[ 2]) + + MUL15(a[10], b[ 1]) + + MUL15(a[11], b[ 0]); + t[12] = MUL15(a[ 0], b[12]) + + MUL15(a[ 1], b[11]) + + MUL15(a[ 2], b[10]) + + MUL15(a[ 3], b[ 9]) + + MUL15(a[ 4], b[ 8]) + + MUL15(a[ 5], b[ 7]) + + MUL15(a[ 6], b[ 6]) + + MUL15(a[ 7], b[ 5]) + + MUL15(a[ 8], b[ 4]) + + MUL15(a[ 9], b[ 3]) + + MUL15(a[10], b[ 2]) + + MUL15(a[11], b[ 1]) + + MUL15(a[12], b[ 0]); + t[13] = MUL15(a[ 0], b[13]) + + MUL15(a[ 1], b[12]) + + MUL15(a[ 2], b[11]) + + MUL15(a[ 3], b[10]) + + MUL15(a[ 4], b[ 9]) + + MUL15(a[ 5], b[ 8]) + + MUL15(a[ 6], b[ 7]) + + MUL15(a[ 7], b[ 6]) + + MUL15(a[ 8], b[ 5]) + + MUL15(a[ 9], b[ 4]) + + MUL15(a[10], b[ 3]) + + MUL15(a[11], b[ 2]) + + MUL15(a[12], b[ 1]) + + MUL15(a[13], b[ 0]); + t[14] = MUL15(a[ 0], b[14]) + + MUL15(a[ 1], b[13]) + + MUL15(a[ 2], b[12]) + + MUL15(a[ 3], b[11]) + + MUL15(a[ 4], b[10]) + + MUL15(a[ 5], b[ 9]) + + MUL15(a[ 6], b[ 8]) + + MUL15(a[ 7], b[ 7]) + + MUL15(a[ 8], b[ 6]) + + MUL15(a[ 9], b[ 5]) + + MUL15(a[10], b[ 4]) + + MUL15(a[11], b[ 3]) + + MUL15(a[12], b[ 2]) + + MUL15(a[13], b[ 1]) + + MUL15(a[14], b[ 0]); + t[15] = MUL15(a[ 0], b[15]) + + MUL15(a[ 1], b[14]) + + MUL15(a[ 2], b[13]) + + MUL15(a[ 3], b[12]) + + MUL15(a[ 4], b[11]) + + MUL15(a[ 5], b[10]) + + MUL15(a[ 6], b[ 9]) + + MUL15(a[ 7], b[ 8]) + + MUL15(a[ 8], b[ 7]) + + MUL15(a[ 9], b[ 6]) + + MUL15(a[10], b[ 5]) + + MUL15(a[11], b[ 4]) + + MUL15(a[12], b[ 3]) + + MUL15(a[13], b[ 2]) + + MUL15(a[14], b[ 1]) + + MUL15(a[15], b[ 0]); + t[16] = MUL15(a[ 0], b[16]) + + MUL15(a[ 1], b[15]) + + MUL15(a[ 2], b[14]) + + MUL15(a[ 3], b[13]) + + MUL15(a[ 4], b[12]) + + MUL15(a[ 5], b[11]) + + MUL15(a[ 6], b[10]) + + MUL15(a[ 7], b[ 9]) + + MUL15(a[ 8], b[ 8]) + + MUL15(a[ 9], b[ 7]) + + MUL15(a[10], b[ 6]) + + MUL15(a[11], b[ 5]) + + MUL15(a[12], b[ 4]) + + MUL15(a[13], b[ 3]) + + MUL15(a[14], b[ 2]) + + MUL15(a[15], b[ 1]) + + MUL15(a[16], b[ 0]); + t[17] = MUL15(a[ 0], b[17]) + + MUL15(a[ 1], b[16]) + + MUL15(a[ 2], b[15]) + + MUL15(a[ 3], b[14]) + + MUL15(a[ 4], b[13]) + + MUL15(a[ 5], b[12]) + + MUL15(a[ 6], b[11]) + + MUL15(a[ 7], b[10]) + + MUL15(a[ 8], b[ 9]) + + MUL15(a[ 9], b[ 8]) + + MUL15(a[10], b[ 7]) + + MUL15(a[11], b[ 6]) + + MUL15(a[12], b[ 5]) + + MUL15(a[13], b[ 4]) + + MUL15(a[14], b[ 3]) + + MUL15(a[15], b[ 2]) + + MUL15(a[16], b[ 1]) + + MUL15(a[17], b[ 0]); + t[18] = MUL15(a[ 0], b[18]) + + MUL15(a[ 1], b[17]) + + MUL15(a[ 2], b[16]) + + MUL15(a[ 3], b[15]) + + MUL15(a[ 4], b[14]) + + MUL15(a[ 5], b[13]) + + MUL15(a[ 6], b[12]) + + MUL15(a[ 7], b[11]) + + MUL15(a[ 8], b[10]) + + MUL15(a[ 9], b[ 9]) + + MUL15(a[10], b[ 8]) + + MUL15(a[11], b[ 7]) + + MUL15(a[12], b[ 6]) + + MUL15(a[13], b[ 5]) + + MUL15(a[14], b[ 4]) + + MUL15(a[15], b[ 3]) + + MUL15(a[16], b[ 2]) + + MUL15(a[17], b[ 1]) + + MUL15(a[18], b[ 0]); + t[19] = MUL15(a[ 0], b[19]) + + MUL15(a[ 1], b[18]) + + MUL15(a[ 2], b[17]) + + MUL15(a[ 3], b[16]) + + MUL15(a[ 4], b[15]) + + MUL15(a[ 5], b[14]) + + MUL15(a[ 6], b[13]) + + MUL15(a[ 7], b[12]) + + MUL15(a[ 8], b[11]) + + MUL15(a[ 9], b[10]) + + MUL15(a[10], b[ 9]) + + MUL15(a[11], b[ 8]) + + MUL15(a[12], b[ 7]) + + MUL15(a[13], b[ 6]) + + MUL15(a[14], b[ 5]) + + MUL15(a[15], b[ 4]) + + MUL15(a[16], b[ 3]) + + MUL15(a[17], b[ 2]) + + MUL15(a[18], b[ 1]) + + MUL15(a[19], b[ 0]); + t[20] = MUL15(a[ 1], b[19]) + + MUL15(a[ 2], b[18]) + + MUL15(a[ 3], b[17]) + + MUL15(a[ 4], b[16]) + + MUL15(a[ 5], b[15]) + + MUL15(a[ 6], b[14]) + + MUL15(a[ 7], b[13]) + + MUL15(a[ 8], b[12]) + + MUL15(a[ 9], b[11]) + + MUL15(a[10], b[10]) + + MUL15(a[11], b[ 9]) + + MUL15(a[12], b[ 8]) + + MUL15(a[13], b[ 7]) + + MUL15(a[14], b[ 6]) + + MUL15(a[15], b[ 5]) + + MUL15(a[16], b[ 4]) + + MUL15(a[17], b[ 3]) + + MUL15(a[18], b[ 2]) + + MUL15(a[19], b[ 1]); + t[21] = MUL15(a[ 2], b[19]) + + MUL15(a[ 3], b[18]) + + MUL15(a[ 4], b[17]) + + MUL15(a[ 5], b[16]) + + MUL15(a[ 6], b[15]) + + MUL15(a[ 7], b[14]) + + MUL15(a[ 8], b[13]) + + MUL15(a[ 9], b[12]) + + MUL15(a[10], b[11]) + + MUL15(a[11], b[10]) + + MUL15(a[12], b[ 9]) + + MUL15(a[13], b[ 8]) + + MUL15(a[14], b[ 7]) + + MUL15(a[15], b[ 6]) + + MUL15(a[16], b[ 5]) + + MUL15(a[17], b[ 4]) + + MUL15(a[18], b[ 3]) + + MUL15(a[19], b[ 2]); + t[22] = MUL15(a[ 3], b[19]) + + MUL15(a[ 4], b[18]) + + MUL15(a[ 5], b[17]) + + MUL15(a[ 6], b[16]) + + MUL15(a[ 7], b[15]) + + MUL15(a[ 8], b[14]) + + MUL15(a[ 9], b[13]) + + MUL15(a[10], b[12]) + + MUL15(a[11], b[11]) + + MUL15(a[12], b[10]) + + MUL15(a[13], b[ 9]) + + MUL15(a[14], b[ 8]) + + MUL15(a[15], b[ 7]) + + MUL15(a[16], b[ 6]) + + MUL15(a[17], b[ 5]) + + MUL15(a[18], b[ 4]) + + MUL15(a[19], b[ 3]); + t[23] = MUL15(a[ 4], b[19]) + + MUL15(a[ 5], b[18]) + + MUL15(a[ 6], b[17]) + + MUL15(a[ 7], b[16]) + + MUL15(a[ 8], b[15]) + + MUL15(a[ 9], b[14]) + + MUL15(a[10], b[13]) + + MUL15(a[11], b[12]) + + MUL15(a[12], b[11]) + + MUL15(a[13], b[10]) + + MUL15(a[14], b[ 9]) + + MUL15(a[15], b[ 8]) + + MUL15(a[16], b[ 7]) + + MUL15(a[17], b[ 6]) + + MUL15(a[18], b[ 5]) + + MUL15(a[19], b[ 4]); + t[24] = MUL15(a[ 5], b[19]) + + MUL15(a[ 6], b[18]) + + MUL15(a[ 7], b[17]) + + MUL15(a[ 8], b[16]) + + MUL15(a[ 9], b[15]) + + MUL15(a[10], b[14]) + + MUL15(a[11], b[13]) + + MUL15(a[12], b[12]) + + MUL15(a[13], b[11]) + + MUL15(a[14], b[10]) + + MUL15(a[15], b[ 9]) + + MUL15(a[16], b[ 8]) + + MUL15(a[17], b[ 7]) + + MUL15(a[18], b[ 6]) + + MUL15(a[19], b[ 5]); + t[25] = MUL15(a[ 6], b[19]) + + MUL15(a[ 7], b[18]) + + MUL15(a[ 8], b[17]) + + MUL15(a[ 9], b[16]) + + MUL15(a[10], b[15]) + + MUL15(a[11], b[14]) + + MUL15(a[12], b[13]) + + MUL15(a[13], b[12]) + + MUL15(a[14], b[11]) + + MUL15(a[15], b[10]) + + MUL15(a[16], b[ 9]) + + MUL15(a[17], b[ 8]) + + MUL15(a[18], b[ 7]) + + MUL15(a[19], b[ 6]); + t[26] = MUL15(a[ 7], b[19]) + + MUL15(a[ 8], b[18]) + + MUL15(a[ 9], b[17]) + + MUL15(a[10], b[16]) + + MUL15(a[11], b[15]) + + MUL15(a[12], b[14]) + + MUL15(a[13], b[13]) + + MUL15(a[14], b[12]) + + MUL15(a[15], b[11]) + + MUL15(a[16], b[10]) + + MUL15(a[17], b[ 9]) + + MUL15(a[18], b[ 8]) + + MUL15(a[19], b[ 7]); + t[27] = MUL15(a[ 8], b[19]) + + MUL15(a[ 9], b[18]) + + MUL15(a[10], b[17]) + + MUL15(a[11], b[16]) + + MUL15(a[12], b[15]) + + MUL15(a[13], b[14]) + + MUL15(a[14], b[13]) + + MUL15(a[15], b[12]) + + MUL15(a[16], b[11]) + + MUL15(a[17], b[10]) + + MUL15(a[18], b[ 9]) + + MUL15(a[19], b[ 8]); + t[28] = MUL15(a[ 9], b[19]) + + MUL15(a[10], b[18]) + + MUL15(a[11], b[17]) + + MUL15(a[12], b[16]) + + MUL15(a[13], b[15]) + + MUL15(a[14], b[14]) + + MUL15(a[15], b[13]) + + MUL15(a[16], b[12]) + + MUL15(a[17], b[11]) + + MUL15(a[18], b[10]) + + MUL15(a[19], b[ 9]); + t[29] = MUL15(a[10], b[19]) + + MUL15(a[11], b[18]) + + MUL15(a[12], b[17]) + + MUL15(a[13], b[16]) + + MUL15(a[14], b[15]) + + MUL15(a[15], b[14]) + + MUL15(a[16], b[13]) + + MUL15(a[17], b[12]) + + MUL15(a[18], b[11]) + + MUL15(a[19], b[10]); + t[30] = MUL15(a[11], b[19]) + + MUL15(a[12], b[18]) + + MUL15(a[13], b[17]) + + MUL15(a[14], b[16]) + + MUL15(a[15], b[15]) + + MUL15(a[16], b[14]) + + MUL15(a[17], b[13]) + + MUL15(a[18], b[12]) + + MUL15(a[19], b[11]); + t[31] = MUL15(a[12], b[19]) + + MUL15(a[13], b[18]) + + MUL15(a[14], b[17]) + + MUL15(a[15], b[16]) + + MUL15(a[16], b[15]) + + MUL15(a[17], b[14]) + + MUL15(a[18], b[13]) + + MUL15(a[19], b[12]); + t[32] = MUL15(a[13], b[19]) + + MUL15(a[14], b[18]) + + MUL15(a[15], b[17]) + + MUL15(a[16], b[16]) + + MUL15(a[17], b[15]) + + MUL15(a[18], b[14]) + + MUL15(a[19], b[13]); + t[33] = MUL15(a[14], b[19]) + + MUL15(a[15], b[18]) + + MUL15(a[16], b[17]) + + MUL15(a[17], b[16]) + + MUL15(a[18], b[15]) + + MUL15(a[19], b[14]); + t[34] = MUL15(a[15], b[19]) + + MUL15(a[16], b[18]) + + MUL15(a[17], b[17]) + + MUL15(a[18], b[16]) + + MUL15(a[19], b[15]); + t[35] = MUL15(a[16], b[19]) + + MUL15(a[17], b[18]) + + MUL15(a[18], b[17]) + + MUL15(a[19], b[16]); + t[36] = MUL15(a[17], b[19]) + + MUL15(a[18], b[18]) + + MUL15(a[19], b[17]); + t[37] = MUL15(a[18], b[19]) + + MUL15(a[19], b[18]); + t[38] = MUL15(a[19], b[19]); + + d[39] = norm13(d, t, 39); +} + +/*static*/ void +square20(uint32_t *d, const uint32_t *a) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], a[ 0]); + t[ 1] = ((MUL15(a[ 0], a[ 1])) << 1); + t[ 2] = MUL15(a[ 1], a[ 1]) + + ((MUL15(a[ 0], a[ 2])) << 1); + t[ 3] = ((MUL15(a[ 0], a[ 3]) + + MUL15(a[ 1], a[ 2])) << 1); + t[ 4] = MUL15(a[ 2], a[ 2]) + + ((MUL15(a[ 0], a[ 4]) + + MUL15(a[ 1], a[ 3])) << 1); + t[ 5] = ((MUL15(a[ 0], a[ 5]) + + MUL15(a[ 1], a[ 4]) + + MUL15(a[ 2], a[ 3])) << 1); + t[ 6] = MUL15(a[ 3], a[ 3]) + + ((MUL15(a[ 0], a[ 6]) + + MUL15(a[ 1], a[ 5]) + + MUL15(a[ 2], a[ 4])) << 1); + t[ 7] = ((MUL15(a[ 0], a[ 7]) + + MUL15(a[ 1], a[ 6]) + + MUL15(a[ 2], a[ 5]) + + MUL15(a[ 3], a[ 4])) << 1); + t[ 8] = MUL15(a[ 4], a[ 4]) + + ((MUL15(a[ 0], a[ 8]) + + MUL15(a[ 1], a[ 7]) + + MUL15(a[ 2], a[ 6]) + + MUL15(a[ 3], a[ 5])) << 1); + t[ 9] = ((MUL15(a[ 0], a[ 9]) + + MUL15(a[ 1], a[ 8]) + + MUL15(a[ 2], a[ 7]) + + MUL15(a[ 3], a[ 6]) + + MUL15(a[ 4], a[ 5])) << 1); + t[10] = MUL15(a[ 5], a[ 5]) + + ((MUL15(a[ 0], a[10]) + + MUL15(a[ 1], a[ 9]) + + MUL15(a[ 2], a[ 8]) + + MUL15(a[ 3], a[ 7]) + + MUL15(a[ 4], a[ 6])) << 1); + t[11] = ((MUL15(a[ 0], a[11]) + + MUL15(a[ 1], a[10]) + + MUL15(a[ 2], a[ 9]) + + MUL15(a[ 3], a[ 8]) + + MUL15(a[ 4], a[ 7]) + + MUL15(a[ 5], a[ 6])) << 1); + t[12] = MUL15(a[ 6], a[ 6]) + + ((MUL15(a[ 0], a[12]) + + MUL15(a[ 1], a[11]) + + MUL15(a[ 2], a[10]) + + MUL15(a[ 3], a[ 9]) + + MUL15(a[ 4], a[ 8]) + + MUL15(a[ 5], a[ 7])) << 1); + t[13] = ((MUL15(a[ 0], a[13]) + + MUL15(a[ 1], a[12]) + + MUL15(a[ 2], a[11]) + + MUL15(a[ 3], a[10]) + + MUL15(a[ 4], a[ 9]) + + MUL15(a[ 5], a[ 8]) + + MUL15(a[ 6], a[ 7])) << 1); + t[14] = MUL15(a[ 7], a[ 7]) + + ((MUL15(a[ 0], a[14]) + + MUL15(a[ 1], a[13]) + + MUL15(a[ 2], a[12]) + + MUL15(a[ 3], a[11]) + + MUL15(a[ 4], a[10]) + + MUL15(a[ 5], a[ 9]) + + MUL15(a[ 6], a[ 8])) << 1); + t[15] = ((MUL15(a[ 0], a[15]) + + MUL15(a[ 1], a[14]) + + MUL15(a[ 2], a[13]) + + MUL15(a[ 3], a[12]) + + MUL15(a[ 4], a[11]) + + MUL15(a[ 5], a[10]) + + MUL15(a[ 6], a[ 9]) + + MUL15(a[ 7], a[ 8])) << 1); + t[16] = MUL15(a[ 8], a[ 8]) + + ((MUL15(a[ 0], a[16]) + + MUL15(a[ 1], a[15]) + + MUL15(a[ 2], a[14]) + + MUL15(a[ 3], a[13]) + + MUL15(a[ 4], a[12]) + + MUL15(a[ 5], a[11]) + + MUL15(a[ 6], a[10]) + + MUL15(a[ 7], a[ 9])) << 1); + t[17] = ((MUL15(a[ 0], a[17]) + + MUL15(a[ 1], a[16]) + + MUL15(a[ 2], a[15]) + + MUL15(a[ 3], a[14]) + + MUL15(a[ 4], a[13]) + + MUL15(a[ 5], a[12]) + + MUL15(a[ 6], a[11]) + + MUL15(a[ 7], a[10]) + + MUL15(a[ 8], a[ 9])) << 1); + t[18] = MUL15(a[ 9], a[ 9]) + + ((MUL15(a[ 0], a[18]) + + MUL15(a[ 1], a[17]) + + MUL15(a[ 2], a[16]) + + MUL15(a[ 3], a[15]) + + MUL15(a[ 4], a[14]) + + MUL15(a[ 5], a[13]) + + MUL15(a[ 6], a[12]) + + MUL15(a[ 7], a[11]) + + MUL15(a[ 8], a[10])) << 1); + t[19] = ((MUL15(a[ 0], a[19]) + + MUL15(a[ 1], a[18]) + + MUL15(a[ 2], a[17]) + + MUL15(a[ 3], a[16]) + + MUL15(a[ 4], a[15]) + + MUL15(a[ 5], a[14]) + + MUL15(a[ 6], a[13]) + + MUL15(a[ 7], a[12]) + + MUL15(a[ 8], a[11]) + + MUL15(a[ 9], a[10])) << 1); + t[20] = MUL15(a[10], a[10]) + + ((MUL15(a[ 1], a[19]) + + MUL15(a[ 2], a[18]) + + MUL15(a[ 3], a[17]) + + MUL15(a[ 4], a[16]) + + MUL15(a[ 5], a[15]) + + MUL15(a[ 6], a[14]) + + MUL15(a[ 7], a[13]) + + MUL15(a[ 8], a[12]) + + MUL15(a[ 9], a[11])) << 1); + t[21] = ((MUL15(a[ 2], a[19]) + + MUL15(a[ 3], a[18]) + + MUL15(a[ 4], a[17]) + + MUL15(a[ 5], a[16]) + + MUL15(a[ 6], a[15]) + + MUL15(a[ 7], a[14]) + + MUL15(a[ 8], a[13]) + + MUL15(a[ 9], a[12]) + + MUL15(a[10], a[11])) << 1); + t[22] = MUL15(a[11], a[11]) + + ((MUL15(a[ 3], a[19]) + + MUL15(a[ 4], a[18]) + + MUL15(a[ 5], a[17]) + + MUL15(a[ 6], a[16]) + + MUL15(a[ 7], a[15]) + + MUL15(a[ 8], a[14]) + + MUL15(a[ 9], a[13]) + + MUL15(a[10], a[12])) << 1); + t[23] = ((MUL15(a[ 4], a[19]) + + MUL15(a[ 5], a[18]) + + MUL15(a[ 6], a[17]) + + MUL15(a[ 7], a[16]) + + MUL15(a[ 8], a[15]) + + MUL15(a[ 9], a[14]) + + MUL15(a[10], a[13]) + + MUL15(a[11], a[12])) << 1); + t[24] = MUL15(a[12], a[12]) + + ((MUL15(a[ 5], a[19]) + + MUL15(a[ 6], a[18]) + + MUL15(a[ 7], a[17]) + + MUL15(a[ 8], a[16]) + + MUL15(a[ 9], a[15]) + + MUL15(a[10], a[14]) + + MUL15(a[11], a[13])) << 1); + t[25] = ((MUL15(a[ 6], a[19]) + + MUL15(a[ 7], a[18]) + + MUL15(a[ 8], a[17]) + + MUL15(a[ 9], a[16]) + + MUL15(a[10], a[15]) + + MUL15(a[11], a[14]) + + MUL15(a[12], a[13])) << 1); + t[26] = MUL15(a[13], a[13]) + + ((MUL15(a[ 7], a[19]) + + MUL15(a[ 8], a[18]) + + MUL15(a[ 9], a[17]) + + MUL15(a[10], a[16]) + + MUL15(a[11], a[15]) + + MUL15(a[12], a[14])) << 1); + t[27] = ((MUL15(a[ 8], a[19]) + + MUL15(a[ 9], a[18]) + + MUL15(a[10], a[17]) + + MUL15(a[11], a[16]) + + MUL15(a[12], a[15]) + + MUL15(a[13], a[14])) << 1); + t[28] = MUL15(a[14], a[14]) + + ((MUL15(a[ 9], a[19]) + + MUL15(a[10], a[18]) + + MUL15(a[11], a[17]) + + MUL15(a[12], a[16]) + + MUL15(a[13], a[15])) << 1); + t[29] = ((MUL15(a[10], a[19]) + + MUL15(a[11], a[18]) + + MUL15(a[12], a[17]) + + MUL15(a[13], a[16]) + + MUL15(a[14], a[15])) << 1); + t[30] = MUL15(a[15], a[15]) + + ((MUL15(a[11], a[19]) + + MUL15(a[12], a[18]) + + MUL15(a[13], a[17]) + + MUL15(a[14], a[16])) << 1); + t[31] = ((MUL15(a[12], a[19]) + + MUL15(a[13], a[18]) + + MUL15(a[14], a[17]) + + MUL15(a[15], a[16])) << 1); + t[32] = MUL15(a[16], a[16]) + + ((MUL15(a[13], a[19]) + + MUL15(a[14], a[18]) + + MUL15(a[15], a[17])) << 1); + t[33] = ((MUL15(a[14], a[19]) + + MUL15(a[15], a[18]) + + MUL15(a[16], a[17])) << 1); + t[34] = MUL15(a[17], a[17]) + + ((MUL15(a[15], a[19]) + + MUL15(a[16], a[18])) << 1); + t[35] = ((MUL15(a[16], a[19]) + + MUL15(a[17], a[18])) << 1); + t[36] = MUL15(a[18], a[18]) + + ((MUL15(a[17], a[19])) << 1); + t[37] = ((MUL15(a[18], a[19])) << 1); + t[38] = MUL15(a[19], a[19]); + + d[39] = norm13(d, t, 39); +} + +#endif + +/* + * Perform a "final reduction" in field F255 (field for Curve25519) + * The source value must be less than twice the modulus. If the value + * is not lower than the modulus, then the modulus is subtracted and + * this function returns 1; otherwise, it leaves it untouched and it + * returns 0. + */ +static uint32_t +reduce_final_f255(uint32_t *d) +{ + uint32_t t[20]; + uint32_t cc; + int i; + + memcpy(t, d, sizeof t); + cc = 19; + for (i = 0; i < 20; i ++) { + uint32_t w; + + w = t[i] + cc; + cc = w >> 13; + t[i] = w & 0x1FFF; + } + cc = t[19] >> 8; + t[19] &= 0xFF; + CCOPY(cc, d, t, sizeof t); + return cc; +} + +static void +f255_mulgen(uint32_t *d, const uint32_t *a, const uint32_t *b, int square) +{ + uint32_t t[40], cc, w; + + /* + * Compute raw multiplication. All result words fit in 13 bits + * each; upper word (t[39]) must fit on 5 bits, since the product + * of two 256-bit integers must fit on 512 bits. + */ + if (square) { + square20(t, a); + } else { + mul20(t, a, b); + } + + /* + * Modular reduction: each high word is added where necessary. + * Since the modulus is 2^255-19 and word 20 corresponds to + * offset 20*13 = 260, word 20+k must be added to word k with + * a factor of 19*2^5 = 608. The extra bits in word 19 are also + * added that way. + */ + cc = MUL15(t[19] >> 8, 19); + t[19] &= 0xFF; + +#define MM1(x) do { \ + w = t[x] + cc + MUL15(t[(x) + 20], 608); \ + t[x] = w & 0x1FFF; \ + cc = w >> 13; \ + } while (0) + + MM1( 0); + MM1( 1); + MM1( 2); + MM1( 3); + MM1( 4); + MM1( 5); + MM1( 6); + MM1( 7); + MM1( 8); + MM1( 9); + MM1(10); + MM1(11); + MM1(12); + MM1(13); + MM1(14); + MM1(15); + MM1(16); + MM1(17); + MM1(18); + MM1(19); + +#undef MM1 + + cc = MUL15(w >> 8, 19); + t[19] &= 0xFF; + +#define MM2(x) do { \ + w = t[x] + cc; \ + d[x] = w & 0x1FFF; \ + cc = w >> 13; \ + } while (0) + + MM2( 0); + MM2( 1); + MM2( 2); + MM2( 3); + MM2( 4); + MM2( 5); + MM2( 6); + MM2( 7); + MM2( 8); + MM2( 9); + MM2(10); + MM2(11); + MM2(12); + MM2(13); + MM2(14); + MM2(15); + MM2(16); + MM2(17); + MM2(18); + MM2(19); + +#undef MM2 +} + +/* + * Perform a multiplication of two integers modulo 2^255-19. + * Operands are arrays of 20 words, each containing 13 bits of data, in + * little-endian order. Input value may be up to 2^256-1; on output, value + * fits on 256 bits and is lower than twice the modulus. + * + * f255_mul() is the general multiplication, f255_square() is specialised + * for squarings. + */ +#define f255_mul(d, a, b) f255_mulgen(d, a, b, 0) +#define f255_square(d, a) f255_mulgen(d, a, a, 1) + +/* + * Add two values in F255. Partial reduction is performed (down to less + * than twice the modulus). + */ +static void +f255_add(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + int i; + uint32_t cc, w; + + cc = 0; + for (i = 0; i < 20; i ++) { + w = a[i] + b[i] + cc; + d[i] = w & 0x1FFF; + cc = w >> 13; + } + cc = MUL15(w >> 8, 19); + d[19] &= 0xFF; + for (i = 0; i < 20; i ++) { + w = d[i] + cc; + d[i] = w & 0x1FFF; + cc = w >> 13; + } +} + +/* + * Subtract one value from another in F255. Partial reduction is + * performed (down to less than twice the modulus). + */ +static void +f255_sub(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * We actually compute a - b + 2*p, so that the final value is + * necessarily positive. + */ + int i; + uint32_t cc, w; + + cc = (uint32_t)-38; + for (i = 0; i < 20; i ++) { + w = a[i] - b[i] + cc; + d[i] = w & 0x1FFF; + cc = ARSH(w, 13); + } + cc = MUL15((w + 0x200) >> 8, 19); + d[19] &= 0xFF; + for (i = 0; i < 20; i ++) { + w = d[i] + cc; + d[i] = w & 0x1FFF; + cc = w >> 13; + } +} + +/* + * Multiply an integer by the 'A24' constant (121665). Partial reduction + * is performed (down to less than twice the modulus). + */ +static void +f255_mul_a24(uint32_t *d, const uint32_t *a) +{ + int i; + uint32_t cc, w; + + cc = 0; + for (i = 0; i < 20; i ++) { + w = MUL15(a[i], 121665) + cc; + d[i] = w & 0x1FFF; + cc = w >> 13; + } + cc = MUL15(w >> 8, 19); + d[19] &= 0xFF; + for (i = 0; i < 20; i ++) { + w = d[i] + cc; + d[i] = w & 0x1FFF; + cc = w >> 13; + } +} + +static const unsigned char GEN[] PROGMEM = { + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char ORDER[] PROGMEM = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return GEN; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return ORDER; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 0; +} + +static void +cswap(uint32_t *a, uint32_t *b, uint32_t ctl) +{ + int i; + + ctl = -ctl; + for (i = 0; i < 20; i ++) { + uint32_t aw, bw, tw; + + aw = a[i]; + bw = b[i]; + tw = ctl & (aw ^ bw); + a[i] = aw ^ tw; + b[i] = bw ^ tw; + } +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *kb, size_t kblen, int curve) +{ + uint32_t x1[20], x2[20], x3[20], z2[20], z3[20]; + uint32_t a[20], aa[20], b[20], bb[20]; + uint32_t c[20], d[20], e[20], da[20], cb[20]; + unsigned char k[32]; + uint32_t swap; + int i; + + (void)curve; + + /* + * Points are encoded over exactly 32 bytes. Multipliers must fit + * in 32 bytes as well. + * RFC 7748 mandates that the high bit of the last point byte must + * be ignored/cleared. + */ + if (Glen != 32 || kblen > 32) { + return 0; + } + G[31] &= 0x7F; + + /* + * Initialise variables x1, x2, z2, x3 and z3. We set all of them + * into Montgomery representation. + */ + x1[19] = le8_to_le13(x1, G, 32); + memcpy(x3, x1, sizeof x1); + memset(z2, 0, sizeof z2); + memset(x2, 0, sizeof x2); + x2[0] = 1; + memset(z3, 0, sizeof z3); + z3[0] = 1; + + memset(k, 0, (sizeof k) - kblen); + memcpy(k + (sizeof k) - kblen, kb, kblen); + k[31] &= 0xF8; + k[0] &= 0x7F; + k[0] |= 0x40; + + /* obsolete + print_int("x1", x1); + */ + + swap = 0; + for (i = 254; i >= 0; i --) { + uint32_t kt; + + kt = (k[31 - (i >> 3)] >> (i & 7)) & 1; + swap ^= kt; + cswap(x2, x3, swap); + cswap(z2, z3, swap); + swap = kt; + + /* obsolete + print_int("x2", x2); + print_int("z2", z2); + print_int("x3", x3); + print_int("z3", z3); + */ + + f255_add(a, x2, z2); + f255_square(aa, a); + f255_sub(b, x2, z2); + f255_square(bb, b); + f255_sub(e, aa, bb); + f255_add(c, x3, z3); + f255_sub(d, x3, z3); + f255_mul(da, d, a); + f255_mul(cb, c, b); + + /* obsolete + print_int("a ", a); + print_int("aa", aa); + print_int("b ", b); + print_int("bb", bb); + print_int("e ", e); + print_int("c ", c); + print_int("d ", d); + print_int("da", da); + print_int("cb", cb); + */ + + f255_add(x3, da, cb); + f255_square(x3, x3); + f255_sub(z3, da, cb); + f255_square(z3, z3); + f255_mul(z3, z3, x1); + f255_mul(x2, aa, bb); + f255_mul_a24(z2, e); + f255_add(z2, z2, aa); + f255_mul(z2, e, z2); + + /* obsolete + print_int("x2", x2); + print_int("z2", z2); + print_int("x3", x3); + print_int("z3", z3); + */ + } + cswap(x2, x3, swap); + cswap(z2, z3, swap); + + /* + * Inverse z2 with a modular exponentiation. This is a simple + * square-and-multiply algorithm; we mutualise most non-squarings + * since the exponent contains almost only ones. + */ + memcpy(a, z2, sizeof z2); + for (i = 0; i < 15; i ++) { + f255_square(a, a); + f255_mul(a, a, z2); + } + memcpy(b, a, sizeof a); + for (i = 0; i < 14; i ++) { + int j; + + for (j = 0; j < 16; j ++) { + f255_square(b, b); + } + f255_mul(b, b, a); + } + for (i = 14; i >= 0; i --) { + f255_square(b, b); + if ((0xFFEB >> i) & 1) { + f255_mul(b, z2, b); + } + } + f255_mul(x2, x2, b); + reduce_final_f255(x2); + le13_to_le8(G, 32, x2); + return 1; +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = api_generator(curve, &Glen); + memcpy_P(R, G, Glen); + api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + /* + * We don't implement this method, since it is used for ECDSA + * only, and there is no ECDSA over Curve25519 (which instead + * uses EdDSA). + */ + (void)A; + (void)B; + (void)len; + (void)x; + (void)xlen; + (void)y; + (void)ylen; + (void)curve; + return 0; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_c25519_m15 PROGMEM = { + (uint32_t)0x20000000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; \ No newline at end of file diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c new file mode 100644 index 0000000000..a475035a1e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_curve25519.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char GEN[] PROGMEM = { + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +static const unsigned char ORDER[] PROGMEM = { + 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +/* see inner.h */ +const br_ec_curve_def br_curve25519 = { + BR_EC_curve25519, + ORDER, sizeof ORDER, + GEN, sizeof GEN +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c new file mode 100644 index 0000000000..b29d68e334 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_default.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +const br_ec_impl * +br_ec_get_default(void) +{ +#if BR_LOMUL + return &br_ec_all_m15; +#else + return &br_ec_all_m31; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c new file mode 100644 index 0000000000..2c9069d66f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_keygen.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +size_t +br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve) +{ + const unsigned char *order; + unsigned char *buf; + size_t len; + unsigned mask; + + if (curve < 0 || curve >= 32 + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + order = impl->order(curve, &len); + while (len > 0 && *order == 0) { + order ++; + len --; + } + if (kbuf == NULL || len == 0) { + return len; + } + mask = order[0]; + mask |= (mask >> 1); + mask |= (mask >> 2); + mask |= (mask >> 4); + + /* + * We generate sequences of random bits of the right size, until + * the value is strictly lower than the curve order (we also + * check for all-zero values, which are invalid). + */ + buf = kbuf; + for (;;) { + size_t u; + unsigned cc, zz; + + (*rng_ctx)->generate(rng_ctx, buf, len); + buf[0] &= mask; + cc = 0; + u = len; + zz = 0; + while (u -- > 0) { + cc = ((unsigned)(buf[u] - order[u] - cc) >> 8) & 1; + zz |= buf[u]; + } + if (cc != 0 && zz != 0) { + break; + } + } + + if (sk != NULL) { + sk->curve = curve; + sk->x = buf; + sk->xlen = len; + } + return len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c new file mode 100644 index 0000000000..7eb1c5b726 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_p256_m15.c @@ -0,0 +1,2111 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * If BR_NO_ARITH_SHIFT is undefined, or defined to 0, then we _assume_ + * that right-shifting a signed negative integer copies the sign bit + * (arithmetic right-shift). This is "implementation-defined behaviour", + * i.e. it is not undefined, but it may differ between compilers. Each + * compiler is supposed to document its behaviour in that respect. GCC + * explicitly defines that an arithmetic right shift is used. We expect + * all other compilers to do the same, because underlying CPU offer an + * arithmetic right shift opcode that could not be used otherwise. + */ +#if BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#else +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n)) +#endif + +/* + * Convert an integer from unsigned big-endian encoding to a sequence of + * 13-bit words in little-endian order. The final "partial" word is + * returned. + */ +static uint32_t +be8_to_le13(uint32_t *dst, const unsigned char *src, size_t len) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + acc |= (uint32_t)src[len] << acc_len; + acc_len += 8; + if (acc_len >= 13) { + *dst ++ = acc & 0x1FFF; + acc >>= 13; + acc_len -= 13; + } + } + return acc; +} + +/* + * Convert an integer (13-bit words, little-endian) to unsigned + * big-endian encoding. The total encoding length is provided; all + * the destination bytes will be filled. + */ +static void +le13_to_be8(unsigned char *dst, size_t len, const uint32_t *src) +{ + uint32_t acc; + int acc_len; + + acc = 0; + acc_len = 0; + while (len -- > 0) { + if (acc_len < 8) { + acc |= (*src ++) << acc_len; + acc_len += 13; + } + dst[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } +} + +/* + * Normalise an array of words to a strict 13 bits per word. Returned + * value is the resulting carry. The source (w) and destination (d) + * arrays may be identical, but shall not overlap partially. + */ +static inline uint32_t +norm13(uint32_t *d, const uint32_t *w, size_t len) +{ + size_t u; + uint32_t cc; + + cc = 0; + for (u = 0; u < len; u ++) { + int32_t z; + + z = w[u] + cc; + d[u] = z & 0x1FFF; + cc = ARSH(z, 13); + } + return cc; +} + +/* + * mul20() multiplies two 260-bit integers together. Each word must fit + * on 13 bits; source operands use 20 words, destination operand + * receives 40 words. All overlaps allowed. + * + * square20() computes the square of a 260-bit integer. Each word must + * fit on 13 bits; source operand uses 20 words, destination operand + * receives 40 words. All overlaps allowed. + */ + +#if BR_SLOW_MUL15 + +static void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + /* + * Two-level Karatsuba: turns a 20x20 multiplication into + * nine 5x5 multiplications. We use 13-bit words but do not + * propagate carries immediately, so words may expand: + * + * - First Karatsuba decomposition turns the 20x20 mul on + * 13-bit words into three 10x10 muls, two on 13-bit words + * and one on 14-bit words. + * + * - Second Karatsuba decomposition further splits these into: + * + * * four 5x5 muls on 13-bit words + * * four 5x5 muls on 14-bit words + * * one 5x5 mul on 15-bit words + * + * Highest word value is 8191, 16382 or 32764, for 13-bit, 14-bit + * or 15-bit words, respectively. + */ + uint32_t u[45], v[45], w[90]; + uint32_t cc; + int i; + +#define ZADD(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] = (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] = (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] = (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] = (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] = (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define ZADDT(dw, d_off, sw, s_off) do { \ + (dw)[5 * (d_off) + 0] += (sw)[5 * (s_off) + 0]; \ + (dw)[5 * (d_off) + 1] += (sw)[5 * (s_off) + 1]; \ + (dw)[5 * (d_off) + 2] += (sw)[5 * (s_off) + 2]; \ + (dw)[5 * (d_off) + 3] += (sw)[5 * (s_off) + 3]; \ + (dw)[5 * (d_off) + 4] += (sw)[5 * (s_off) + 4]; \ + } while (0) + +#define ZSUB2F(dw, d_off, s1w, s1_off, s2w, s2_off) do { \ + (dw)[5 * (d_off) + 0] -= (s1w)[5 * (s1_off) + 0] \ + + (s2w)[5 * (s2_off) + 0]; \ + (dw)[5 * (d_off) + 1] -= (s1w)[5 * (s1_off) + 1] \ + + (s2w)[5 * (s2_off) + 1]; \ + (dw)[5 * (d_off) + 2] -= (s1w)[5 * (s1_off) + 2] \ + + (s2w)[5 * (s2_off) + 2]; \ + (dw)[5 * (d_off) + 3] -= (s1w)[5 * (s1_off) + 3] \ + + (s2w)[5 * (s2_off) + 3]; \ + (dw)[5 * (d_off) + 4] -= (s1w)[5 * (s1_off) + 4] \ + + (s2w)[5 * (s2_off) + 4]; \ + } while (0) + +#define CPR1(w, cprcc) do { \ + uint32_t cprz = (w) + cprcc; \ + (w) = cprz & 0x1FFF; \ + cprcc = cprz >> 13; \ + } while (0) + +#define CPR(dw, d_off) do { \ + uint32_t cprcc; \ + cprcc = 0; \ + CPR1((dw)[(d_off) + 0], cprcc); \ + CPR1((dw)[(d_off) + 1], cprcc); \ + CPR1((dw)[(d_off) + 2], cprcc); \ + CPR1((dw)[(d_off) + 3], cprcc); \ + CPR1((dw)[(d_off) + 4], cprcc); \ + CPR1((dw)[(d_off) + 5], cprcc); \ + CPR1((dw)[(d_off) + 6], cprcc); \ + CPR1((dw)[(d_off) + 7], cprcc); \ + CPR1((dw)[(d_off) + 8], cprcc); \ + (dw)[(d_off) + 9] = cprcc; \ + } while (0) + + memcpy(u, a, 20 * sizeof *a); + ZADD(u, 4, a, 0, a, 1); + ZADD(u, 5, a, 2, a, 3); + ZADD(u, 6, a, 0, a, 2); + ZADD(u, 7, a, 1, a, 3); + ZADD(u, 8, u, 6, u, 7); + + memcpy(v, b, 20 * sizeof *b); + ZADD(v, 4, b, 0, b, 1); + ZADD(v, 5, b, 2, b, 3); + ZADD(v, 6, b, 0, b, 2); + ZADD(v, 7, b, 1, b, 3); + ZADD(v, 8, v, 6, v, 7); + + /* + * Do the eight first 8x8 muls. Source words are at most 16382 + * each, so we can add product results together "as is" in 32-bit + * words. + */ + for (i = 0; i < 40; i += 5) { + w[(i << 1) + 0] = MUL15(u[i + 0], v[i + 0]); + w[(i << 1) + 1] = MUL15(u[i + 0], v[i + 1]) + + MUL15(u[i + 1], v[i + 0]); + w[(i << 1) + 2] = MUL15(u[i + 0], v[i + 2]) + + MUL15(u[i + 1], v[i + 1]) + + MUL15(u[i + 2], v[i + 0]); + w[(i << 1) + 3] = MUL15(u[i + 0], v[i + 3]) + + MUL15(u[i + 1], v[i + 2]) + + MUL15(u[i + 2], v[i + 1]) + + MUL15(u[i + 3], v[i + 0]); + w[(i << 1) + 4] = MUL15(u[i + 0], v[i + 4]) + + MUL15(u[i + 1], v[i + 3]) + + MUL15(u[i + 2], v[i + 2]) + + MUL15(u[i + 3], v[i + 1]) + + MUL15(u[i + 4], v[i + 0]); + w[(i << 1) + 5] = MUL15(u[i + 1], v[i + 4]) + + MUL15(u[i + 2], v[i + 3]) + + MUL15(u[i + 3], v[i + 2]) + + MUL15(u[i + 4], v[i + 1]); + w[(i << 1) + 6] = MUL15(u[i + 2], v[i + 4]) + + MUL15(u[i + 3], v[i + 3]) + + MUL15(u[i + 4], v[i + 2]); + w[(i << 1) + 7] = MUL15(u[i + 3], v[i + 4]) + + MUL15(u[i + 4], v[i + 3]); + w[(i << 1) + 8] = MUL15(u[i + 4], v[i + 4]); + w[(i << 1) + 9] = 0; + } + + /* + * For the 9th multiplication, source words are up to 32764, + * so we must do some carry propagation. If we add up to + * 4 products and the carry is no more than 524224, then the + * result fits in 32 bits, and the next carry will be no more + * than 524224 (because 4*(32764^2)+524224 < 8192*524225). + * + * We thus just skip one of the products in the middle word, + * then do a carry propagation (this reduces words to 13 bits + * each, except possibly the last, which may use up to 17 bits + * or so), then add the missing product. + */ + w[80 + 0] = MUL15(u[40 + 0], v[40 + 0]); + w[80 + 1] = MUL15(u[40 + 0], v[40 + 1]) + + MUL15(u[40 + 1], v[40 + 0]); + w[80 + 2] = MUL15(u[40 + 0], v[40 + 2]) + + MUL15(u[40 + 1], v[40 + 1]) + + MUL15(u[40 + 2], v[40 + 0]); + w[80 + 3] = MUL15(u[40 + 0], v[40 + 3]) + + MUL15(u[40 + 1], v[40 + 2]) + + MUL15(u[40 + 2], v[40 + 1]) + + MUL15(u[40 + 3], v[40 + 0]); + w[80 + 4] = MUL15(u[40 + 0], v[40 + 4]) + + MUL15(u[40 + 1], v[40 + 3]) + + MUL15(u[40 + 2], v[40 + 2]) + + MUL15(u[40 + 3], v[40 + 1]); + /* + MUL15(u[40 + 4], v[40 + 0]) */ + w[80 + 5] = MUL15(u[40 + 1], v[40 + 4]) + + MUL15(u[40 + 2], v[40 + 3]) + + MUL15(u[40 + 3], v[40 + 2]) + + MUL15(u[40 + 4], v[40 + 1]); + w[80 + 6] = MUL15(u[40 + 2], v[40 + 4]) + + MUL15(u[40 + 3], v[40 + 3]) + + MUL15(u[40 + 4], v[40 + 2]); + w[80 + 7] = MUL15(u[40 + 3], v[40 + 4]) + + MUL15(u[40 + 4], v[40 + 3]); + w[80 + 8] = MUL15(u[40 + 4], v[40 + 4]); + + CPR(w, 80); + + w[80 + 4] += MUL15(u[40 + 4], v[40 + 0]); + + /* + * The products on 14-bit words in slots 6 and 7 yield values + * up to 5*(16382^2) each, and we need to subtract two such + * values from the higher word. We need the subtraction to fit + * in a _signed_ 32-bit integer, i.e. 31 bits + a sign bit. + * However, 10*(16382^2) does not fit. So we must perform a + * bit of reduction here. + */ + CPR(w, 60); + CPR(w, 70); + + /* + * Recompose results. + */ + + /* 0..1*0..1 into 0..3 */ + ZSUB2F(w, 8, w, 0, w, 2); + ZSUB2F(w, 9, w, 1, w, 3); + ZADDT(w, 1, w, 8); + ZADDT(w, 2, w, 9); + + /* 2..3*2..3 into 4..7 */ + ZSUB2F(w, 10, w, 4, w, 6); + ZSUB2F(w, 11, w, 5, w, 7); + ZADDT(w, 5, w, 10); + ZADDT(w, 6, w, 11); + + /* (0..1+2..3)*(0..1+2..3) into 12..15 */ + ZSUB2F(w, 16, w, 12, w, 14); + ZSUB2F(w, 17, w, 13, w, 15); + ZADDT(w, 13, w, 16); + ZADDT(w, 14, w, 17); + + /* first-level recomposition */ + ZSUB2F(w, 12, w, 0, w, 4); + ZSUB2F(w, 13, w, 1, w, 5); + ZSUB2F(w, 14, w, 2, w, 6); + ZSUB2F(w, 15, w, 3, w, 7); + ZADDT(w, 2, w, 12); + ZADDT(w, 3, w, 13); + ZADDT(w, 4, w, 14); + ZADDT(w, 5, w, 15); + + /* + * Perform carry propagation to bring all words down to 13 bits. + */ + cc = norm13(d, w, 40); + d[39] += (cc << 13); + +#undef ZADD +#undef ZADDT +#undef ZSUB2F +#undef CPR1 +#undef CPR +} + +static inline void +square20(uint32_t *d, const uint32_t *a) +{ + mul20(d, a, a); +} + +#else +extern void mul20(uint32_t *d, const uint32_t *a, const uint32_t *b); +extern void square20(uint32_t *d, const uint32_t *a); + +#if 0 +static void +mul20(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], b[ 0]); + t[ 1] = MUL15(a[ 0], b[ 1]) + + MUL15(a[ 1], b[ 0]); + t[ 2] = MUL15(a[ 0], b[ 2]) + + MUL15(a[ 1], b[ 1]) + + MUL15(a[ 2], b[ 0]); + t[ 3] = MUL15(a[ 0], b[ 3]) + + MUL15(a[ 1], b[ 2]) + + MUL15(a[ 2], b[ 1]) + + MUL15(a[ 3], b[ 0]); + t[ 4] = MUL15(a[ 0], b[ 4]) + + MUL15(a[ 1], b[ 3]) + + MUL15(a[ 2], b[ 2]) + + MUL15(a[ 3], b[ 1]) + + MUL15(a[ 4], b[ 0]); + t[ 5] = MUL15(a[ 0], b[ 5]) + + MUL15(a[ 1], b[ 4]) + + MUL15(a[ 2], b[ 3]) + + MUL15(a[ 3], b[ 2]) + + MUL15(a[ 4], b[ 1]) + + MUL15(a[ 5], b[ 0]); + t[ 6] = MUL15(a[ 0], b[ 6]) + + MUL15(a[ 1], b[ 5]) + + MUL15(a[ 2], b[ 4]) + + MUL15(a[ 3], b[ 3]) + + MUL15(a[ 4], b[ 2]) + + MUL15(a[ 5], b[ 1]) + + MUL15(a[ 6], b[ 0]); + t[ 7] = MUL15(a[ 0], b[ 7]) + + MUL15(a[ 1], b[ 6]) + + MUL15(a[ 2], b[ 5]) + + MUL15(a[ 3], b[ 4]) + + MUL15(a[ 4], b[ 3]) + + MUL15(a[ 5], b[ 2]) + + MUL15(a[ 6], b[ 1]) + + MUL15(a[ 7], b[ 0]); + t[ 8] = MUL15(a[ 0], b[ 8]) + + MUL15(a[ 1], b[ 7]) + + MUL15(a[ 2], b[ 6]) + + MUL15(a[ 3], b[ 5]) + + MUL15(a[ 4], b[ 4]) + + MUL15(a[ 5], b[ 3]) + + MUL15(a[ 6], b[ 2]) + + MUL15(a[ 7], b[ 1]) + + MUL15(a[ 8], b[ 0]); + t[ 9] = MUL15(a[ 0], b[ 9]) + + MUL15(a[ 1], b[ 8]) + + MUL15(a[ 2], b[ 7]) + + MUL15(a[ 3], b[ 6]) + + MUL15(a[ 4], b[ 5]) + + MUL15(a[ 5], b[ 4]) + + MUL15(a[ 6], b[ 3]) + + MUL15(a[ 7], b[ 2]) + + MUL15(a[ 8], b[ 1]) + + MUL15(a[ 9], b[ 0]); + t[10] = MUL15(a[ 0], b[10]) + + MUL15(a[ 1], b[ 9]) + + MUL15(a[ 2], b[ 8]) + + MUL15(a[ 3], b[ 7]) + + MUL15(a[ 4], b[ 6]) + + MUL15(a[ 5], b[ 5]) + + MUL15(a[ 6], b[ 4]) + + MUL15(a[ 7], b[ 3]) + + MUL15(a[ 8], b[ 2]) + + MUL15(a[ 9], b[ 1]) + + MUL15(a[10], b[ 0]); + t[11] = MUL15(a[ 0], b[11]) + + MUL15(a[ 1], b[10]) + + MUL15(a[ 2], b[ 9]) + + MUL15(a[ 3], b[ 8]) + + MUL15(a[ 4], b[ 7]) + + MUL15(a[ 5], b[ 6]) + + MUL15(a[ 6], b[ 5]) + + MUL15(a[ 7], b[ 4]) + + MUL15(a[ 8], b[ 3]) + + MUL15(a[ 9], b[ 2]) + + MUL15(a[10], b[ 1]) + + MUL15(a[11], b[ 0]); + t[12] = MUL15(a[ 0], b[12]) + + MUL15(a[ 1], b[11]) + + MUL15(a[ 2], b[10]) + + MUL15(a[ 3], b[ 9]) + + MUL15(a[ 4], b[ 8]) + + MUL15(a[ 5], b[ 7]) + + MUL15(a[ 6], b[ 6]) + + MUL15(a[ 7], b[ 5]) + + MUL15(a[ 8], b[ 4]) + + MUL15(a[ 9], b[ 3]) + + MUL15(a[10], b[ 2]) + + MUL15(a[11], b[ 1]) + + MUL15(a[12], b[ 0]); + t[13] = MUL15(a[ 0], b[13]) + + MUL15(a[ 1], b[12]) + + MUL15(a[ 2], b[11]) + + MUL15(a[ 3], b[10]) + + MUL15(a[ 4], b[ 9]) + + MUL15(a[ 5], b[ 8]) + + MUL15(a[ 6], b[ 7]) + + MUL15(a[ 7], b[ 6]) + + MUL15(a[ 8], b[ 5]) + + MUL15(a[ 9], b[ 4]) + + MUL15(a[10], b[ 3]) + + MUL15(a[11], b[ 2]) + + MUL15(a[12], b[ 1]) + + MUL15(a[13], b[ 0]); + t[14] = MUL15(a[ 0], b[14]) + + MUL15(a[ 1], b[13]) + + MUL15(a[ 2], b[12]) + + MUL15(a[ 3], b[11]) + + MUL15(a[ 4], b[10]) + + MUL15(a[ 5], b[ 9]) + + MUL15(a[ 6], b[ 8]) + + MUL15(a[ 7], b[ 7]) + + MUL15(a[ 8], b[ 6]) + + MUL15(a[ 9], b[ 5]) + + MUL15(a[10], b[ 4]) + + MUL15(a[11], b[ 3]) + + MUL15(a[12], b[ 2]) + + MUL15(a[13], b[ 1]) + + MUL15(a[14], b[ 0]); + t[15] = MUL15(a[ 0], b[15]) + + MUL15(a[ 1], b[14]) + + MUL15(a[ 2], b[13]) + + MUL15(a[ 3], b[12]) + + MUL15(a[ 4], b[11]) + + MUL15(a[ 5], b[10]) + + MUL15(a[ 6], b[ 9]) + + MUL15(a[ 7], b[ 8]) + + MUL15(a[ 8], b[ 7]) + + MUL15(a[ 9], b[ 6]) + + MUL15(a[10], b[ 5]) + + MUL15(a[11], b[ 4]) + + MUL15(a[12], b[ 3]) + + MUL15(a[13], b[ 2]) + + MUL15(a[14], b[ 1]) + + MUL15(a[15], b[ 0]); + t[16] = MUL15(a[ 0], b[16]) + + MUL15(a[ 1], b[15]) + + MUL15(a[ 2], b[14]) + + MUL15(a[ 3], b[13]) + + MUL15(a[ 4], b[12]) + + MUL15(a[ 5], b[11]) + + MUL15(a[ 6], b[10]) + + MUL15(a[ 7], b[ 9]) + + MUL15(a[ 8], b[ 8]) + + MUL15(a[ 9], b[ 7]) + + MUL15(a[10], b[ 6]) + + MUL15(a[11], b[ 5]) + + MUL15(a[12], b[ 4]) + + MUL15(a[13], b[ 3]) + + MUL15(a[14], b[ 2]) + + MUL15(a[15], b[ 1]) + + MUL15(a[16], b[ 0]); + t[17] = MUL15(a[ 0], b[17]) + + MUL15(a[ 1], b[16]) + + MUL15(a[ 2], b[15]) + + MUL15(a[ 3], b[14]) + + MUL15(a[ 4], b[13]) + + MUL15(a[ 5], b[12]) + + MUL15(a[ 6], b[11]) + + MUL15(a[ 7], b[10]) + + MUL15(a[ 8], b[ 9]) + + MUL15(a[ 9], b[ 8]) + + MUL15(a[10], b[ 7]) + + MUL15(a[11], b[ 6]) + + MUL15(a[12], b[ 5]) + + MUL15(a[13], b[ 4]) + + MUL15(a[14], b[ 3]) + + MUL15(a[15], b[ 2]) + + MUL15(a[16], b[ 1]) + + MUL15(a[17], b[ 0]); + t[18] = MUL15(a[ 0], b[18]) + + MUL15(a[ 1], b[17]) + + MUL15(a[ 2], b[16]) + + MUL15(a[ 3], b[15]) + + MUL15(a[ 4], b[14]) + + MUL15(a[ 5], b[13]) + + MUL15(a[ 6], b[12]) + + MUL15(a[ 7], b[11]) + + MUL15(a[ 8], b[10]) + + MUL15(a[ 9], b[ 9]) + + MUL15(a[10], b[ 8]) + + MUL15(a[11], b[ 7]) + + MUL15(a[12], b[ 6]) + + MUL15(a[13], b[ 5]) + + MUL15(a[14], b[ 4]) + + MUL15(a[15], b[ 3]) + + MUL15(a[16], b[ 2]) + + MUL15(a[17], b[ 1]) + + MUL15(a[18], b[ 0]); + t[19] = MUL15(a[ 0], b[19]) + + MUL15(a[ 1], b[18]) + + MUL15(a[ 2], b[17]) + + MUL15(a[ 3], b[16]) + + MUL15(a[ 4], b[15]) + + MUL15(a[ 5], b[14]) + + MUL15(a[ 6], b[13]) + + MUL15(a[ 7], b[12]) + + MUL15(a[ 8], b[11]) + + MUL15(a[ 9], b[10]) + + MUL15(a[10], b[ 9]) + + MUL15(a[11], b[ 8]) + + MUL15(a[12], b[ 7]) + + MUL15(a[13], b[ 6]) + + MUL15(a[14], b[ 5]) + + MUL15(a[15], b[ 4]) + + MUL15(a[16], b[ 3]) + + MUL15(a[17], b[ 2]) + + MUL15(a[18], b[ 1]) + + MUL15(a[19], b[ 0]); + t[20] = MUL15(a[ 1], b[19]) + + MUL15(a[ 2], b[18]) + + MUL15(a[ 3], b[17]) + + MUL15(a[ 4], b[16]) + + MUL15(a[ 5], b[15]) + + MUL15(a[ 6], b[14]) + + MUL15(a[ 7], b[13]) + + MUL15(a[ 8], b[12]) + + MUL15(a[ 9], b[11]) + + MUL15(a[10], b[10]) + + MUL15(a[11], b[ 9]) + + MUL15(a[12], b[ 8]) + + MUL15(a[13], b[ 7]) + + MUL15(a[14], b[ 6]) + + MUL15(a[15], b[ 5]) + + MUL15(a[16], b[ 4]) + + MUL15(a[17], b[ 3]) + + MUL15(a[18], b[ 2]) + + MUL15(a[19], b[ 1]); + t[21] = MUL15(a[ 2], b[19]) + + MUL15(a[ 3], b[18]) + + MUL15(a[ 4], b[17]) + + MUL15(a[ 5], b[16]) + + MUL15(a[ 6], b[15]) + + MUL15(a[ 7], b[14]) + + MUL15(a[ 8], b[13]) + + MUL15(a[ 9], b[12]) + + MUL15(a[10], b[11]) + + MUL15(a[11], b[10]) + + MUL15(a[12], b[ 9]) + + MUL15(a[13], b[ 8]) + + MUL15(a[14], b[ 7]) + + MUL15(a[15], b[ 6]) + + MUL15(a[16], b[ 5]) + + MUL15(a[17], b[ 4]) + + MUL15(a[18], b[ 3]) + + MUL15(a[19], b[ 2]); + t[22] = MUL15(a[ 3], b[19]) + + MUL15(a[ 4], b[18]) + + MUL15(a[ 5], b[17]) + + MUL15(a[ 6], b[16]) + + MUL15(a[ 7], b[15]) + + MUL15(a[ 8], b[14]) + + MUL15(a[ 9], b[13]) + + MUL15(a[10], b[12]) + + MUL15(a[11], b[11]) + + MUL15(a[12], b[10]) + + MUL15(a[13], b[ 9]) + + MUL15(a[14], b[ 8]) + + MUL15(a[15], b[ 7]) + + MUL15(a[16], b[ 6]) + + MUL15(a[17], b[ 5]) + + MUL15(a[18], b[ 4]) + + MUL15(a[19], b[ 3]); + t[23] = MUL15(a[ 4], b[19]) + + MUL15(a[ 5], b[18]) + + MUL15(a[ 6], b[17]) + + MUL15(a[ 7], b[16]) + + MUL15(a[ 8], b[15]) + + MUL15(a[ 9], b[14]) + + MUL15(a[10], b[13]) + + MUL15(a[11], b[12]) + + MUL15(a[12], b[11]) + + MUL15(a[13], b[10]) + + MUL15(a[14], b[ 9]) + + MUL15(a[15], b[ 8]) + + MUL15(a[16], b[ 7]) + + MUL15(a[17], b[ 6]) + + MUL15(a[18], b[ 5]) + + MUL15(a[19], b[ 4]); + t[24] = MUL15(a[ 5], b[19]) + + MUL15(a[ 6], b[18]) + + MUL15(a[ 7], b[17]) + + MUL15(a[ 8], b[16]) + + MUL15(a[ 9], b[15]) + + MUL15(a[10], b[14]) + + MUL15(a[11], b[13]) + + MUL15(a[12], b[12]) + + MUL15(a[13], b[11]) + + MUL15(a[14], b[10]) + + MUL15(a[15], b[ 9]) + + MUL15(a[16], b[ 8]) + + MUL15(a[17], b[ 7]) + + MUL15(a[18], b[ 6]) + + MUL15(a[19], b[ 5]); + t[25] = MUL15(a[ 6], b[19]) + + MUL15(a[ 7], b[18]) + + MUL15(a[ 8], b[17]) + + MUL15(a[ 9], b[16]) + + MUL15(a[10], b[15]) + + MUL15(a[11], b[14]) + + MUL15(a[12], b[13]) + + MUL15(a[13], b[12]) + + MUL15(a[14], b[11]) + + MUL15(a[15], b[10]) + + MUL15(a[16], b[ 9]) + + MUL15(a[17], b[ 8]) + + MUL15(a[18], b[ 7]) + + MUL15(a[19], b[ 6]); + t[26] = MUL15(a[ 7], b[19]) + + MUL15(a[ 8], b[18]) + + MUL15(a[ 9], b[17]) + + MUL15(a[10], b[16]) + + MUL15(a[11], b[15]) + + MUL15(a[12], b[14]) + + MUL15(a[13], b[13]) + + MUL15(a[14], b[12]) + + MUL15(a[15], b[11]) + + MUL15(a[16], b[10]) + + MUL15(a[17], b[ 9]) + + MUL15(a[18], b[ 8]) + + MUL15(a[19], b[ 7]); + t[27] = MUL15(a[ 8], b[19]) + + MUL15(a[ 9], b[18]) + + MUL15(a[10], b[17]) + + MUL15(a[11], b[16]) + + MUL15(a[12], b[15]) + + MUL15(a[13], b[14]) + + MUL15(a[14], b[13]) + + MUL15(a[15], b[12]) + + MUL15(a[16], b[11]) + + MUL15(a[17], b[10]) + + MUL15(a[18], b[ 9]) + + MUL15(a[19], b[ 8]); + t[28] = MUL15(a[ 9], b[19]) + + MUL15(a[10], b[18]) + + MUL15(a[11], b[17]) + + MUL15(a[12], b[16]) + + MUL15(a[13], b[15]) + + MUL15(a[14], b[14]) + + MUL15(a[15], b[13]) + + MUL15(a[16], b[12]) + + MUL15(a[17], b[11]) + + MUL15(a[18], b[10]) + + MUL15(a[19], b[ 9]); + t[29] = MUL15(a[10], b[19]) + + MUL15(a[11], b[18]) + + MUL15(a[12], b[17]) + + MUL15(a[13], b[16]) + + MUL15(a[14], b[15]) + + MUL15(a[15], b[14]) + + MUL15(a[16], b[13]) + + MUL15(a[17], b[12]) + + MUL15(a[18], b[11]) + + MUL15(a[19], b[10]); + t[30] = MUL15(a[11], b[19]) + + MUL15(a[12], b[18]) + + MUL15(a[13], b[17]) + + MUL15(a[14], b[16]) + + MUL15(a[15], b[15]) + + MUL15(a[16], b[14]) + + MUL15(a[17], b[13]) + + MUL15(a[18], b[12]) + + MUL15(a[19], b[11]); + t[31] = MUL15(a[12], b[19]) + + MUL15(a[13], b[18]) + + MUL15(a[14], b[17]) + + MUL15(a[15], b[16]) + + MUL15(a[16], b[15]) + + MUL15(a[17], b[14]) + + MUL15(a[18], b[13]) + + MUL15(a[19], b[12]); + t[32] = MUL15(a[13], b[19]) + + MUL15(a[14], b[18]) + + MUL15(a[15], b[17]) + + MUL15(a[16], b[16]) + + MUL15(a[17], b[15]) + + MUL15(a[18], b[14]) + + MUL15(a[19], b[13]); + t[33] = MUL15(a[14], b[19]) + + MUL15(a[15], b[18]) + + MUL15(a[16], b[17]) + + MUL15(a[17], b[16]) + + MUL15(a[18], b[15]) + + MUL15(a[19], b[14]); + t[34] = MUL15(a[15], b[19]) + + MUL15(a[16], b[18]) + + MUL15(a[17], b[17]) + + MUL15(a[18], b[16]) + + MUL15(a[19], b[15]); + t[35] = MUL15(a[16], b[19]) + + MUL15(a[17], b[18]) + + MUL15(a[18], b[17]) + + MUL15(a[19], b[16]); + t[36] = MUL15(a[17], b[19]) + + MUL15(a[18], b[18]) + + MUL15(a[19], b[17]); + t[37] = MUL15(a[18], b[19]) + + MUL15(a[19], b[18]); + t[38] = MUL15(a[19], b[19]); + d[39] = norm13(d, t, 39); +} + +static void +square20(uint32_t *d, const uint32_t *a) +{ + uint32_t t[39]; + + t[ 0] = MUL15(a[ 0], a[ 0]); + t[ 1] = ((MUL15(a[ 0], a[ 1])) << 1); + t[ 2] = MUL15(a[ 1], a[ 1]) + + ((MUL15(a[ 0], a[ 2])) << 1); + t[ 3] = ((MUL15(a[ 0], a[ 3]) + + MUL15(a[ 1], a[ 2])) << 1); + t[ 4] = MUL15(a[ 2], a[ 2]) + + ((MUL15(a[ 0], a[ 4]) + + MUL15(a[ 1], a[ 3])) << 1); + t[ 5] = ((MUL15(a[ 0], a[ 5]) + + MUL15(a[ 1], a[ 4]) + + MUL15(a[ 2], a[ 3])) << 1); + t[ 6] = MUL15(a[ 3], a[ 3]) + + ((MUL15(a[ 0], a[ 6]) + + MUL15(a[ 1], a[ 5]) + + MUL15(a[ 2], a[ 4])) << 1); + t[ 7] = ((MUL15(a[ 0], a[ 7]) + + MUL15(a[ 1], a[ 6]) + + MUL15(a[ 2], a[ 5]) + + MUL15(a[ 3], a[ 4])) << 1); + t[ 8] = MUL15(a[ 4], a[ 4]) + + ((MUL15(a[ 0], a[ 8]) + + MUL15(a[ 1], a[ 7]) + + MUL15(a[ 2], a[ 6]) + + MUL15(a[ 3], a[ 5])) << 1); + t[ 9] = ((MUL15(a[ 0], a[ 9]) + + MUL15(a[ 1], a[ 8]) + + MUL15(a[ 2], a[ 7]) + + MUL15(a[ 3], a[ 6]) + + MUL15(a[ 4], a[ 5])) << 1); + t[10] = MUL15(a[ 5], a[ 5]) + + ((MUL15(a[ 0], a[10]) + + MUL15(a[ 1], a[ 9]) + + MUL15(a[ 2], a[ 8]) + + MUL15(a[ 3], a[ 7]) + + MUL15(a[ 4], a[ 6])) << 1); + t[11] = ((MUL15(a[ 0], a[11]) + + MUL15(a[ 1], a[10]) + + MUL15(a[ 2], a[ 9]) + + MUL15(a[ 3], a[ 8]) + + MUL15(a[ 4], a[ 7]) + + MUL15(a[ 5], a[ 6])) << 1); + t[12] = MUL15(a[ 6], a[ 6]) + + ((MUL15(a[ 0], a[12]) + + MUL15(a[ 1], a[11]) + + MUL15(a[ 2], a[10]) + + MUL15(a[ 3], a[ 9]) + + MUL15(a[ 4], a[ 8]) + + MUL15(a[ 5], a[ 7])) << 1); + t[13] = ((MUL15(a[ 0], a[13]) + + MUL15(a[ 1], a[12]) + + MUL15(a[ 2], a[11]) + + MUL15(a[ 3], a[10]) + + MUL15(a[ 4], a[ 9]) + + MUL15(a[ 5], a[ 8]) + + MUL15(a[ 6], a[ 7])) << 1); + t[14] = MUL15(a[ 7], a[ 7]) + + ((MUL15(a[ 0], a[14]) + + MUL15(a[ 1], a[13]) + + MUL15(a[ 2], a[12]) + + MUL15(a[ 3], a[11]) + + MUL15(a[ 4], a[10]) + + MUL15(a[ 5], a[ 9]) + + MUL15(a[ 6], a[ 8])) << 1); + t[15] = ((MUL15(a[ 0], a[15]) + + MUL15(a[ 1], a[14]) + + MUL15(a[ 2], a[13]) + + MUL15(a[ 3], a[12]) + + MUL15(a[ 4], a[11]) + + MUL15(a[ 5], a[10]) + + MUL15(a[ 6], a[ 9]) + + MUL15(a[ 7], a[ 8])) << 1); + t[16] = MUL15(a[ 8], a[ 8]) + + ((MUL15(a[ 0], a[16]) + + MUL15(a[ 1], a[15]) + + MUL15(a[ 2], a[14]) + + MUL15(a[ 3], a[13]) + + MUL15(a[ 4], a[12]) + + MUL15(a[ 5], a[11]) + + MUL15(a[ 6], a[10]) + + MUL15(a[ 7], a[ 9])) << 1); + t[17] = ((MUL15(a[ 0], a[17]) + + MUL15(a[ 1], a[16]) + + MUL15(a[ 2], a[15]) + + MUL15(a[ 3], a[14]) + + MUL15(a[ 4], a[13]) + + MUL15(a[ 5], a[12]) + + MUL15(a[ 6], a[11]) + + MUL15(a[ 7], a[10]) + + MUL15(a[ 8], a[ 9])) << 1); + t[18] = MUL15(a[ 9], a[ 9]) + + ((MUL15(a[ 0], a[18]) + + MUL15(a[ 1], a[17]) + + MUL15(a[ 2], a[16]) + + MUL15(a[ 3], a[15]) + + MUL15(a[ 4], a[14]) + + MUL15(a[ 5], a[13]) + + MUL15(a[ 6], a[12]) + + MUL15(a[ 7], a[11]) + + MUL15(a[ 8], a[10])) << 1); + t[19] = ((MUL15(a[ 0], a[19]) + + MUL15(a[ 1], a[18]) + + MUL15(a[ 2], a[17]) + + MUL15(a[ 3], a[16]) + + MUL15(a[ 4], a[15]) + + MUL15(a[ 5], a[14]) + + MUL15(a[ 6], a[13]) + + MUL15(a[ 7], a[12]) + + MUL15(a[ 8], a[11]) + + MUL15(a[ 9], a[10])) << 1); + t[20] = MUL15(a[10], a[10]) + + ((MUL15(a[ 1], a[19]) + + MUL15(a[ 2], a[18]) + + MUL15(a[ 3], a[17]) + + MUL15(a[ 4], a[16]) + + MUL15(a[ 5], a[15]) + + MUL15(a[ 6], a[14]) + + MUL15(a[ 7], a[13]) + + MUL15(a[ 8], a[12]) + + MUL15(a[ 9], a[11])) << 1); + t[21] = ((MUL15(a[ 2], a[19]) + + MUL15(a[ 3], a[18]) + + MUL15(a[ 4], a[17]) + + MUL15(a[ 5], a[16]) + + MUL15(a[ 6], a[15]) + + MUL15(a[ 7], a[14]) + + MUL15(a[ 8], a[13]) + + MUL15(a[ 9], a[12]) + + MUL15(a[10], a[11])) << 1); + t[22] = MUL15(a[11], a[11]) + + ((MUL15(a[ 3], a[19]) + + MUL15(a[ 4], a[18]) + + MUL15(a[ 5], a[17]) + + MUL15(a[ 6], a[16]) + + MUL15(a[ 7], a[15]) + + MUL15(a[ 8], a[14]) + + MUL15(a[ 9], a[13]) + + MUL15(a[10], a[12])) << 1); + t[23] = ((MUL15(a[ 4], a[19]) + + MUL15(a[ 5], a[18]) + + MUL15(a[ 6], a[17]) + + MUL15(a[ 7], a[16]) + + MUL15(a[ 8], a[15]) + + MUL15(a[ 9], a[14]) + + MUL15(a[10], a[13]) + + MUL15(a[11], a[12])) << 1); + t[24] = MUL15(a[12], a[12]) + + ((MUL15(a[ 5], a[19]) + + MUL15(a[ 6], a[18]) + + MUL15(a[ 7], a[17]) + + MUL15(a[ 8], a[16]) + + MUL15(a[ 9], a[15]) + + MUL15(a[10], a[14]) + + MUL15(a[11], a[13])) << 1); + t[25] = ((MUL15(a[ 6], a[19]) + + MUL15(a[ 7], a[18]) + + MUL15(a[ 8], a[17]) + + MUL15(a[ 9], a[16]) + + MUL15(a[10], a[15]) + + MUL15(a[11], a[14]) + + MUL15(a[12], a[13])) << 1); + t[26] = MUL15(a[13], a[13]) + + ((MUL15(a[ 7], a[19]) + + MUL15(a[ 8], a[18]) + + MUL15(a[ 9], a[17]) + + MUL15(a[10], a[16]) + + MUL15(a[11], a[15]) + + MUL15(a[12], a[14])) << 1); + t[27] = ((MUL15(a[ 8], a[19]) + + MUL15(a[ 9], a[18]) + + MUL15(a[10], a[17]) + + MUL15(a[11], a[16]) + + MUL15(a[12], a[15]) + + MUL15(a[13], a[14])) << 1); + t[28] = MUL15(a[14], a[14]) + + ((MUL15(a[ 9], a[19]) + + MUL15(a[10], a[18]) + + MUL15(a[11], a[17]) + + MUL15(a[12], a[16]) + + MUL15(a[13], a[15])) << 1); + t[29] = ((MUL15(a[10], a[19]) + + MUL15(a[11], a[18]) + + MUL15(a[12], a[17]) + + MUL15(a[13], a[16]) + + MUL15(a[14], a[15])) << 1); + t[30] = MUL15(a[15], a[15]) + + ((MUL15(a[11], a[19]) + + MUL15(a[12], a[18]) + + MUL15(a[13], a[17]) + + MUL15(a[14], a[16])) << 1); + t[31] = ((MUL15(a[12], a[19]) + + MUL15(a[13], a[18]) + + MUL15(a[14], a[17]) + + MUL15(a[15], a[16])) << 1); + t[32] = MUL15(a[16], a[16]) + + ((MUL15(a[13], a[19]) + + MUL15(a[14], a[18]) + + MUL15(a[15], a[17])) << 1); + t[33] = ((MUL15(a[14], a[19]) + + MUL15(a[15], a[18]) + + MUL15(a[16], a[17])) << 1); + t[34] = MUL15(a[17], a[17]) + + ((MUL15(a[15], a[19]) + + MUL15(a[16], a[18])) << 1); + t[35] = ((MUL15(a[16], a[19]) + + MUL15(a[17], a[18])) << 1); + t[36] = MUL15(a[18], a[18]) + + ((MUL15(a[17], a[19])) << 1); + t[37] = ((MUL15(a[18], a[19])) << 1); + t[38] = MUL15(a[19], a[19]); + d[39] = norm13(d, t, 39); +} +#endif + +#endif + +/* + * Modulus for field F256 (field for point coordinates in curve P-256). + */ +static const uint32_t F256[] PROGMEM = { + 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x1FFF, 0x001F, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0400, 0x0000, + 0x0000, 0x1FF8, 0x1FFF, 0x01FF +}; + +/* + * The 'b' curve equation coefficient for P-256. + */ +static const uint32_t P256_B[] PROGMEM = { + 0x004B, 0x1E93, 0x0F89, 0x1C78, 0x03BC, 0x187B, 0x114E, 0x1619, + 0x1D06, 0x0328, 0x01AF, 0x0D31, 0x1557, 0x15DE, 0x1ECF, 0x127C, + 0x0A3A, 0x0EC5, 0x118D, 0x00B5 +}; + +/* + * Perform a "short reduction" in field F256 (field for curve P-256). + * The source value should be less than 262 bits; on output, it will + * be at most 257 bits, and less than twice the modulus. + */ +static void +reduce_f256(uint32_t *d) +{ + uint32_t x; + + x = d[19] >> 9; + d[19] &= 0x01FF; + d[17] += x << 3; + d[14] -= x << 10; + d[7] -= x << 5; + d[0] += x; + norm13(d, d, 20); +} + +/* + * Perform a "final reduction" in field F256 (field for curve P-256). + * The source value must be less than twice the modulus. If the value + * is not lower than the modulus, then the modulus is subtracted and + * this function returns 1; otherwise, it leaves it untouched and it + * returns 0. + */ +static uint32_t +reduce_final_f256(uint32_t *d) +{ + uint32_t t[20]; + uint32_t cc; + int i; + + memcpy(t, d, sizeof t); + cc = 0; + for (i = 0; i < 20; i ++) { + uint32_t w; + + w = t[i] - F256[i] - cc; + cc = w >> 31; + t[i] = w & 0x1FFF; + } + cc ^= 1; + CCOPY(cc, d, t, sizeof t); + return cc; +} + +/* + * Perform a multiplication of two integers modulo + * 2^256-2^224+2^192+2^96-1 (for NIST curve P-256). Operands are arrays + * of 20 words, each containing 13 bits of data, in little-endian order. + * On input, upper word may be up to 13 bits (hence value up to 2^260-1); + * on output, value fits on 257 bits and is lower than twice the modulus. + */ +static void +mul_f256(uint32_t *d, const uint32_t *a, const uint32_t *b) +{ + uint32_t t[40], cc; + int i; + + /* + * Compute raw multiplication. All result words fit in 13 bits + * each. + */ + mul20(t, a, b); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + */ + for (i = 39; i >= 20; i --) { + uint32_t x; + + x = t[i]; + t[i - 2] += ARSH(x, 6); + t[i - 3] += (x << 7) & 0x1FFF; + t[i - 4] -= ARSH(x, 12); + t[i - 5] -= (x << 1) & 0x1FFF; + t[i - 12] -= ARSH(x, 4); + t[i - 13] -= (x << 9) & 0x1FFF; + t[i - 19] += ARSH(x, 9); + t[i - 20] += (x << 4) & 0x1FFF; + } + + /* + * Propagate carries. This is a signed propagation, and the + * result may be negative. The loop above may enlarge values, + * but not two much: worst case is the chain involving t[i - 3], + * in which a value may be added to itself up to 7 times. Since + * starting values are 13-bit each, all words fit on 20 bits + * (21 to account for the sign bit). + */ + cc = norm13(t, t, 20); + + /* + * Perform modular reduction again for the bits beyond 256 (the carry + * and the bits 256..259). Since the largest shift below is by 10 + * bits, and the values fit on 21 bits, values fit in 32-bit words, + * thereby allowing injecting full word values. + */ + cc = (cc << 4) | (t[19] >> 9); + t[19] &= 0x01FF; + t[17] += cc << 3; + t[14] -= cc << 10; + t[7] -= cc << 5; + t[0] += cc; + + /* + * If the carry is negative, then after carry propagation, we may + * end up with a value which is negative, and we don't want that. + * Thus, in that case, we add the modulus. Note that the subtraction + * result, when the carry is negative, is always smaller than the + * modulus, so the extra addition will not make the value exceed + * twice the modulus. + */ + cc >>= 31; + t[0] -= cc; + t[7] += cc << 5; + t[14] += cc << 10; + t[17] -= cc << 3; + t[19] += cc << 9; + + norm13(d, t, 20); +} + +/* + * Square an integer modulo 2^256-2^224+2^192+2^96-1 (for NIST curve + * P-256). Operand is an array of 20 words, each containing 13 bits of + * data, in little-endian order. On input, upper word may be up to 13 + * bits (hence value up to 2^260-1); on output, value fits on 257 bits + * and is lower than twice the modulus. + */ +static void +square_f256(uint32_t *d, const uint32_t *a) +{ + uint32_t t[40], cc; + int i; + + /* + * Compute raw square. All result words fit in 13 bits each. + */ + square20(t, a); + + /* + * Modular reduction: each high word in added/subtracted where + * necessary. + * + * The modulus is: + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1 + * Therefore: + * 2^256 = 2^224 - 2^192 - 2^96 + 1 mod p + * + * For a word x at bit offset n (n >= 256), we have: + * x*2^n = x*2^(n-32) - x*2^(n-64) + * - x*2^(n - 160) + x*2^(n-256) mod p + * + * Thus, we can nullify the high word if we reinject it at some + * proper emplacements. + */ + for (i = 39; i >= 20; i --) { + uint32_t x; + + x = t[i]; + t[i - 2] += ARSH(x, 6); + t[i - 3] += (x << 7) & 0x1FFF; + t[i - 4] -= ARSH(x, 12); + t[i - 5] -= (x << 1) & 0x1FFF; + t[i - 12] -= ARSH(x, 4); + t[i - 13] -= (x << 9) & 0x1FFF; + t[i - 19] += ARSH(x, 9); + t[i - 20] += (x << 4) & 0x1FFF; + } + + /* + * Propagate carries. This is a signed propagation, and the + * result may be negative. The loop above may enlarge values, + * but not two much: worst case is the chain involving t[i - 3], + * in which a value may be added to itself up to 7 times. Since + * starting values are 13-bit each, all words fit on 20 bits + * (21 to account for the sign bit). + */ + cc = norm13(t, t, 20); + + /* + * Perform modular reduction again for the bits beyond 256 (the carry + * and the bits 256..259). Since the largest shift below is by 10 + * bits, and the values fit on 21 bits, values fit in 32-bit words, + * thereby allowing injecting full word values. + */ + cc = (cc << 4) | (t[19] >> 9); + t[19] &= 0x01FF; + t[17] += cc << 3; + t[14] -= cc << 10; + t[7] -= cc << 5; + t[0] += cc; + + /* + * If the carry is negative, then after carry propagation, we may + * end up with a value which is negative, and we don't want that. + * Thus, in that case, we add the modulus. Note that the subtraction + * result, when the carry is negative, is always smaller than the + * modulus, so the extra addition will not make the value exceed + * twice the modulus. + */ + cc >>= 31; + t[0] -= cc; + t[7] += cc << 5; + t[14] += cc << 10; + t[17] -= cc << 3; + t[19] += cc << 9; + + norm13(d, t, 20); +} + +/* + * Jacobian coordinates for a point in P-256: affine coordinates (X,Y) + * are such that: + * X = x / z^2 + * Y = y / z^3 + * For the point at infinity, z = 0. + * Each point thus admits many possible representations. + * + * Coordinates are represented in arrays of 32-bit integers, each holding + * 13 bits of data. Values may also be slightly greater than the modulus, + * but they will always be lower than twice the modulus. + */ +typedef struct { + uint32_t x[20]; + uint32_t y[20]; + uint32_t z[20]; +} p256_jacobian; + +/* + * Convert a point to affine coordinates: + * - If the point is the point at infinity, then all three coordinates + * are set to 0. + * - Otherwise, the 'z' coordinate is set to 1, and the 'x' and 'y' + * coordinates are the 'X' and 'Y' affine coordinates. + * The coordinates are guaranteed to be lower than the modulus. + */ +static void +p256_to_affine(p256_jacobian *P) +{ + uint32_t t1[20], t2[20]; + int i; + + /* + * Invert z with a modular exponentiation: the modulus is + * p = 2^256 - 2^224 + 2^192 + 2^96 - 1, and the exponent is + * p-2. Exponent bit pattern (from high to low) is: + * - 32 bits of value 1 + * - 31 bits of value 0 + * - 1 bit of value 1 + * - 96 bits of value 0 + * - 94 bits of value 1 + * - 1 bit of value 0 + * - 1 bit of value 1 + * Thus, we precompute z^(2^31-1) to speed things up. + * + * If z = 0 (point at infinity) then the modular exponentiation + * will yield 0, which leads to the expected result (all three + * coordinates set to 0). + */ + + /* + * A simple square-and-multiply for z^(2^31-1). We could save about + * two dozen multiplications here with an addition chain, but + * this would require a bit more code, and extra stack buffers. + */ + memcpy(t1, P->z, sizeof P->z); + for (i = 0; i < 30; i ++) { + square_f256(t1, t1); + mul_f256(t1, t1, P->z); + } + + /* + * Square-and-multiply. Apart from the squarings, we have a few + * multiplications to set bits to 1; we multiply by the original z + * for setting 1 bit, and by t1 for setting 31 bits. + */ + memcpy(t2, P->z, sizeof P->z); + for (i = 1; i < 256; i ++) { + square_f256(t2, t2); + switch (i) { + case 31: + case 190: + case 221: + case 252: + mul_f256(t2, t2, t1); + break; + case 63: + case 253: + case 255: + mul_f256(t2, t2, P->z); + break; + } + } + + /* + * Now that we have 1/z, multiply x by 1/z^2 and y by 1/z^3. + */ + mul_f256(t1, t2, t2); + mul_f256(P->x, t1, P->x); + mul_f256(t1, t1, t2); + mul_f256(P->y, t1, P->y); + reduce_final_f256(P->x); + reduce_final_f256(P->y); + + /* + * Multiply z by 1/z. If z = 0, then this will yield 0, otherwise + * this will set z to 1. + */ + mul_f256(P->z, P->z, t2); + reduce_final_f256(P->z); +} + +/* + * Double a point in P-256. This function works for all valid points, + * including the point at infinity. + */ +static void +p256_double(p256_jacobian *Q) +{ + /* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * These formulas work for all points, including points of order 2 + * and points at infinity: + * - If y = 0 then z' = 0. But there is no such point in P-256 + * anyway. + * - If z = 0 then z' = 0. + */ + uint32_t t1[20], t2[20], t3[20], t4[20]; + int i; + + /* + * Compute z^2 in t1. + */ + square_f256(t1, Q->z); + + /* + * Compute x-z^2 in t2 and x+z^2 in t1. + */ + for (i = 0; i < 20; i ++) { + t2[i] = (F256[i] << 1) + Q->x[i] - t1[i]; + t1[i] += Q->x[i]; + } + norm13(t1, t1, 20); + norm13(t2, t2, 20); + + /* + * Compute 3*(x+z^2)*(x-z^2) in t1. + */ + mul_f256(t3, t1, t2); + for (i = 0; i < 20; i ++) { + t1[i] = MUL15(3, t3[i]); + } + norm13(t1, t1, 20); + + /* + * Compute 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + square_f256(t3, Q->y); + for (i = 0; i < 20; i ++) { + t3[i] <<= 1; + } + norm13(t3, t3, 20); + mul_f256(t2, Q->x, t3); + for (i = 0; i < 20; i ++) { + t2[i] <<= 1; + } + norm13(t2, t2, 20); + reduce_f256(t2); + + /* + * Compute x' = m^2 - 2*s. + */ + square_f256(Q->x, t1); + for (i = 0; i < 20; i ++) { + Q->x[i] += (F256[i] << 2) - (t2[i] << 1); + } + norm13(Q->x, Q->x, 20); + reduce_f256(Q->x); + + /* + * Compute z' = 2*y*z. + */ + mul_f256(t4, Q->y, Q->z); + for (i = 0; i < 20; i ++) { + Q->z[i] = t4[i] << 1; + } + norm13(Q->z, Q->z, 20); + reduce_f256(Q->z); + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - Q->x[i]; + } + norm13(t2, t2, 20); + mul_f256(Q->y, t1, t2); + square_f256(t4, t3); + for (i = 0; i < 20; i ++) { + Q->y[i] += (F256[i] << 2) - (t4[i] << 1); + } + norm13(Q->y, Q->y, 20); + reduce_f256(Q->y); +} + +/* + * Add point P2 to point P1. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 but P2 != 0 + * - If P1 != 0 but P2 == 0 + * - If P1 == P2 + * + * In all three cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - P1 == 0 and P2 == 0 + * - The Y coordinate of one of the points is 0 and the other point is + * the point at infinity. + * + * The third case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 and P2 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add(p256_jacobian *P1, const p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + */ + uint32_t t1[20], t2[20], t3[20], t4[20], t5[20], t6[20], t7[20]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + square_f256(t3, P2->z); + mul_f256(t1, P1->x, t3); + mul_f256(t4, P2->z, t3); + mul_f256(t3, P1->y, t4); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - t1[i]; + t4[i] += (F256[i] << 1) - t3[i]; + } + norm13(t2, t2, 20); + norm13(t4, t4, 20); + reduce_f256(t4); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 20; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + for (i = 0; i < 20; i ++) { + P1->x[i] += (F256[i] << 3) - t5[i] - (t6[i] << 1); + } + norm13(P1->x, P1->x, 20); + reduce_f256(P1->x); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + for (i = 0; i < 20; i ++) { + t6[i] += (F256[i] << 1) - P1->x[i]; + } + norm13(t6, t6, 20); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + for (i = 0; i < 20; i ++) { + P1->y[i] += (F256[i] << 1) - t1[i]; + } + norm13(P1->y, P1->y, 20); + reduce_f256(P1->y); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(t1, P1->z, P2->z); + mul_f256(P1->z, t1, t2); + + return ret; +} + +/* + * Add point P2 to point P1. This is a specialised function for the + * case when P2 is a non-zero point in affine coordinate. + * + * This function computes the wrong result in the following cases: + * + * - If P1 == 0 + * - If P1 == P2 + * + * In both cases, P1 is set to the point at infinity. + * + * Returned value is 0 if one of the following occurs: + * + * - P1 and P2 have the same Y coordinate + * - The Y coordinate of P2 is 0 and P1 is the point at infinity. + * + * The second case cannot actually happen with valid points, since a point + * with Y == 0 is a point of order 2, and there is no point of order 2 on + * curve P-256. + * + * Therefore, assuming that P1 != 0 on input, then the caller + * can apply the following: + * + * - If the result is not the point at infinity, then it is correct. + * - Otherwise, if the returned value is 1, then this is a case of + * P1+P2 == 0, so the result is indeed the point at infinity. + * - Otherwise, P1 == P2, so a "double" operation should have been + * performed. + */ +static uint32_t +p256_add_mixed(p256_jacobian *P1, const p256_jacobian *P2) +{ + /* + * Addtions formulas are: + * + * u1 = x1 + * u2 = x2 * z1^2 + * s1 = y1 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 + */ + uint32_t t1[20], t2[20], t3[20], t4[20], t5[20], t6[20], t7[20]; + uint32_t ret; + int i; + + /* + * Compute u1 = x1 (in t1) and s1 = y1 (in t3). + */ + memcpy(t1, P1->x, sizeof t1); + memcpy(t3, P1->y, sizeof t3); + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + square_f256(t4, P1->z); + mul_f256(t2, P2->x, t4); + mul_f256(t5, P1->z, t4); + mul_f256(t4, P2->y, t5); + + /* + * Compute h = h2 - u1 (in t2) and r = s2 - s1 (in t4). + * We need to test whether r is zero, so we will do some extra + * reduce. + */ + for (i = 0; i < 20; i ++) { + t2[i] += (F256[i] << 1) - t1[i]; + t4[i] += (F256[i] << 1) - t3[i]; + } + norm13(t2, t2, 20); + norm13(t4, t4, 20); + reduce_f256(t4); + reduce_final_f256(t4); + ret = 0; + for (i = 0; i < 20; i ++) { + ret |= t4[i]; + } + ret = (ret | -ret) >> 31; + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5); + */ + square_f256(t7, t2); + mul_f256(t6, t1, t7); + mul_f256(t5, t7, t2); + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + */ + square_f256(P1->x, t4); + for (i = 0; i < 20; i ++) { + P1->x[i] += (F256[i] << 3) - t5[i] - (t6[i] << 1); + } + norm13(P1->x, P1->x, 20); + reduce_f256(P1->x); + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + for (i = 0; i < 20; i ++) { + t6[i] += (F256[i] << 1) - P1->x[i]; + } + norm13(t6, t6, 20); + mul_f256(P1->y, t4, t6); + mul_f256(t1, t5, t3); + for (i = 0; i < 20; i ++) { + P1->y[i] += (F256[i] << 1) - t1[i]; + } + norm13(P1->y, P1->y, 20); + reduce_f256(P1->y); + + /* + * Compute z3 = h*z1*z2. + */ + mul_f256(P1->z, P1->z, t2); + + return ret; +} + +/* + * Decode a P-256 point. This function does not support the point at + * infinity. Returned value is 0 if the point is invalid, 1 otherwise. + */ +static uint32_t +p256_decode(p256_jacobian *P, const void *src, size_t len) +{ + const unsigned char *buf; + uint32_t tx[20], ty[20], t1[20], t2[20]; + uint32_t bad; + int i; + + if (len != 65) { + return 0; + } + buf = src; + + /* + * First byte must be 0x04 (uncompressed format). We could support + * "hybrid format" (first byte is 0x06 or 0x07, and encodes the + * least significant bit of the Y coordinate), but it is explicitly + * forbidden by RFC 5480 (section 2.2). + */ + bad = NEQ(buf[0], 0x04); + + /* + * Decode the coordinates, and check that they are both lower + * than the modulus. + */ + tx[19] = be8_to_le13(tx, buf + 1, 32); + ty[19] = be8_to_le13(ty, buf + 33, 32); + bad |= reduce_final_f256(tx); + bad |= reduce_final_f256(ty); + + /* + * Check curve equation. + */ + square_f256(t1, tx); + mul_f256(t1, tx, t1); + square_f256(t2, ty); + for (i = 0; i < 20; i ++) { + t1[i] += (F256[i] << 3) - MUL15(3, tx[i]) + P256_B[i] - t2[i]; + } + norm13(t1, t1, 20); + reduce_f256(t1); + reduce_final_f256(t1); + for (i = 0; i < 20; i ++) { + bad |= t1[i]; + } + + /* + * Copy coordinates to the point structure. + */ + memcpy(P->x, tx, sizeof tx); + memcpy(P->y, ty, sizeof ty); + memset(P->z, 0, sizeof P->z); + P->z[0] = 1; + return EQ(bad, 0); +} + +/* + * Encode a point into a buffer. This function assumes that the point is + * valid, in affine coordinates, and not the point at infinity. + */ +static void +p256_encode(void *dst, const p256_jacobian *P) +{ + unsigned char *buf; + + buf = dst; + buf[0] = 0x04; + le13_to_be8(buf + 1, 32, P->x); + le13_to_be8(buf + 33, 32, P->y); +} + +/* + * Multiply a curve point by an integer. The integer is assumed to be + * lower than the curve order, and the base point must not be the point + * at infinity. + */ +static void +p256_mul(p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 2-bit window to handle multiplier bits by pairs. + * The precomputed window really is the points P2 and P3. + */ + uint32_t qz; + p256_jacobian P2, P3, Q, T, U; + + /* + * Compute window values. + */ + P2 = *P; + p256_double(&P2); + P3 = *P; + p256_add(&P3, &P2); + + /* + * We start with Q = 0. We process multiplier bits 2 by 2. + */ + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + p256_double(&Q); + p256_double(&Q); + T = *P; + U = Q; + bits = (*x >> k) & (uint32_t)3; + bnz = NEQ(bits, 0); + CCOPY(EQ(bits, 2), &T, &P2, sizeof T); + CCOPY(EQ(bits, 3), &T, &P3, sizeof T); + p256_add(&U, &T); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + *P = Q; +} + +/* + * Precomputed window: k*G points, where G is the curve generator, and k + * is an integer from 1 to 15 (inclusive). The X and Y coordinates of + * the point are encoded as 20 words of 13 bits each (little-endian + * order); 13-bit words are then grouped 2-by-2 into 32-bit words + * (little-endian order within each word). + */ +static const uint32_t Gwin[15][20] PROGMEM = { + + { 0x04C60296, 0x02721176, 0x19D00F4A, 0x102517AC, + 0x13B8037D, 0x0748103C, 0x1E730E56, 0x08481FE2, + 0x0F97012C, 0x00D605F4, 0x1DFA11F5, 0x0C801A0D, + 0x0F670CBB, 0x0AED0CC5, 0x115E0E33, 0x181F0785, + 0x13F514A7, 0x0FF30E3B, 0x17171E1A, 0x009F18D0 }, + + { 0x1B341978, 0x16911F11, 0x0D9A1A60, 0x1C4E1FC8, + 0x1E040969, 0x096A06B0, 0x091C0030, 0x09EF1A29, + 0x18C40D03, 0x00F91C9E, 0x13C313D1, 0x096F0748, + 0x011419E0, 0x1CC713A6, 0x1DD31DAD, 0x1EE80C36, + 0x1ECD0C69, 0x1A0800A4, 0x08861B8E, 0x000E1DD5 }, + + { 0x173F1D6C, 0x02CC06F1, 0x14C21FB4, 0x043D1EB6, + 0x0F3606B7, 0x1A971C59, 0x1BF71951, 0x01481323, + 0x068D0633, 0x00BD12F9, 0x13EA1032, 0x136209E8, + 0x1C1E19A7, 0x06C7013E, 0x06C10AB0, 0x14C908BB, + 0x05830CE1, 0x1FEF18DD, 0x00620998, 0x010E0D19 }, + + { 0x18180852, 0x0604111A, 0x0B771509, 0x1B6F0156, + 0x00181FE2, 0x1DCC0AF4, 0x16EF0659, 0x11F70E80, + 0x11A912D0, 0x01C414D2, 0x027618C6, 0x05840FC6, + 0x100215C4, 0x187E0C3B, 0x12771C96, 0x150C0B5D, + 0x0FF705FD, 0x07981C67, 0x1AD20C63, 0x01C11C55 }, + + { 0x1E8113ED, 0x0A940370, 0x12920215, 0x1FA31D6F, + 0x1F7C0C82, 0x10CD03F7, 0x02640560, 0x081A0B5E, + 0x1BD21151, 0x00A21642, 0x0D0B0DA4, 0x0176113F, + 0x04440D1D, 0x001A1360, 0x1068012F, 0x1F141E49, + 0x10DF136B, 0x0E4F162B, 0x0D44104A, 0x01C1105F }, + + { 0x011411A9, 0x01551A4F, 0x0ADA0C6B, 0x01BD0EC8, + 0x18120C74, 0x112F1778, 0x099202CB, 0x0C05124B, + 0x195316A4, 0x01600685, 0x1E3B1FE2, 0x189014E3, + 0x0B5E1FD7, 0x0E0311F8, 0x08E000F7, 0x174E00DE, + 0x160702DF, 0x1B5A15BF, 0x03A11237, 0x01D01704 }, + + { 0x0C3D12A3, 0x0C501C0C, 0x17AD1300, 0x1715003F, + 0x03F719F8, 0x18031ED8, 0x1D980667, 0x0F681896, + 0x1B7D00BF, 0x011C14CE, 0x0FA000B4, 0x1C3501B0, + 0x0D901C55, 0x06790C10, 0x029E0736, 0x0DEB0400, + 0x034F183A, 0x030619B4, 0x0DEF0033, 0x00E71AC7 }, + + { 0x1B7D1393, 0x1B3B1076, 0x0BED1B4D, 0x13011F3A, + 0x0E0E1238, 0x156A132B, 0x013A02D3, 0x160A0D01, + 0x1CED1EE9, 0x00C5165D, 0x184C157E, 0x08141A83, + 0x153C0DA5, 0x1ED70F9D, 0x05170D51, 0x02CF13B8, + 0x18AE1771, 0x1B04113F, 0x05EC11E9, 0x015A16B3 }, + + { 0x04A41EE0, 0x1D1412E4, 0x1C591D79, 0x118511B7, + 0x14F00ACB, 0x1AE31E1C, 0x049C0D51, 0x016E061E, + 0x1DB71EDF, 0x01D41A35, 0x0E8208FA, 0x14441293, + 0x011F1E85, 0x1D54137A, 0x026B114F, 0x151D0832, + 0x00A50964, 0x1F9C1E1C, 0x064B12C9, 0x005409D1 }, + + { 0x062B123F, 0x0C0D0501, 0x183704C3, 0x08E31120, + 0x0A2E0A6C, 0x14440FED, 0x090A0D1E, 0x13271964, + 0x0B590A3A, 0x019D1D9B, 0x05780773, 0x09770A91, + 0x0F770CA3, 0x053F19D4, 0x02C80DED, 0x1A761304, + 0x091E0DD9, 0x15D201B8, 0x151109AA, 0x010F0198 }, + + { 0x05E101D1, 0x072314DD, 0x045F1433, 0x1A041541, + 0x10B3142E, 0x01840736, 0x1C1B19DB, 0x098B0418, + 0x1DBC083B, 0x007D1444, 0x01511740, 0x11DD1F3A, + 0x04ED0E2F, 0x1B4B1A62, 0x10480D04, 0x09E911A2, + 0x04211AFA, 0x19140893, 0x04D60CC4, 0x01210648 }, + + { 0x112703C4, 0x018B1BA1, 0x164C1D50, 0x05160BE0, + 0x0BCC1830, 0x01CB1554, 0x13291732, 0x1B2B1918, + 0x0DED0817, 0x00E80775, 0x0A2401D3, 0x0BFE08B3, + 0x0E531199, 0x058616E9, 0x04770B91, 0x110F0C55, + 0x19C11554, 0x0BFB1159, 0x03541C38, 0x000E1C2D }, + + { 0x10390C01, 0x02BB0751, 0x0AC5098E, 0x096C17AB, + 0x03C90E28, 0x10BD18BF, 0x002E1F2D, 0x092B0986, + 0x1BD700AC, 0x002E1F20, 0x1E3D1FD8, 0x077718BB, + 0x06F919C4, 0x187407ED, 0x11370E14, 0x081E139C, + 0x00481ADB, 0x14AB0289, 0x066A0EBE, 0x00C70ED6 }, + + { 0x0694120B, 0x124E1CC9, 0x0E2F0570, 0x17CF081A, + 0x078906AC, 0x066D17CF, 0x1B3207F4, 0x0C5705E9, + 0x10001C38, 0x00A919DE, 0x06851375, 0x0F900BD8, + 0x080401BA, 0x0EEE0D42, 0x1B8B11EA, 0x0B4519F0, + 0x090F18C0, 0x062E1508, 0x0DD909F4, 0x01EB067C }, + + { 0x0CDC1D5F, 0x0D1818F9, 0x07781636, 0x125B18E8, + 0x0D7003AF, 0x13110099, 0x1D9B1899, 0x175C1EB7, + 0x0E34171A, 0x01E01153, 0x081A0F36, 0x0B391783, + 0x1D1F147E, 0x19CE16D7, 0x11511B21, 0x1F2C10F9, + 0x12CA0E51, 0x05A31D39, 0x171A192E, 0x016B0E4F } +}; + +/* + * Lookup one of the Gwin[] values, by index. This is constant-time. + */ +static void +lookup_Gwin(p256_jacobian *T, uint32_t idx) +{ + uint32_t xy[20]; + uint32_t k; + size_t u; + + memset(xy, 0, sizeof xy); + for (k = 0; k < 15; k ++) { + uint32_t m; + + m = -EQ(idx, k + 1); + for (u = 0; u < 20; u ++) { + xy[u] |= m & Gwin[k][u]; + } + } + for (u = 0; u < 10; u ++) { + T->x[(u << 1) + 0] = xy[u] & 0xFFFF; + T->x[(u << 1) + 1] = xy[u] >> 16; + T->y[(u << 1) + 0] = xy[u + 10] & 0xFFFF; + T->y[(u << 1) + 1] = xy[u + 10] >> 16; + } + memset(T->z, 0, sizeof T->z); + T->z[0] = 1; +} + +/* + * Multiply the generator by an integer. The integer is assumed non-zero + * and lower than the curve order. + */ +static void +p256_mulgen(p256_jacobian *P, const unsigned char *x, size_t xlen) +{ + /* + * qz is a flag that is initially 1, and remains equal to 1 + * as long as the point is the point at infinity. + * + * We use a 4-bit window to handle multiplier bits by groups + * of 4. The precomputed window is constant static data, with + * points in affine coordinates; we use a constant-time lookup. + */ + p256_jacobian Q; + uint32_t qz; + + memset(&Q, 0, sizeof Q); + qz = 1; + while (xlen -- > 0) { + int k; + unsigned bx; + + bx = *x ++; + for (k = 0; k < 2; k ++) { + uint32_t bits; + uint32_t bnz; + p256_jacobian T, U; + + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + p256_double(&Q); + bits = (bx >> 4) & 0x0F; + bnz = NEQ(bits, 0); + lookup_Gwin(&T, bits); + U = Q; + p256_add_mixed(&U, &T); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + bx <<= 4; + } + } + *P = Q; +} + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + (void)curve; + *len = br_secp256r1.generator_len; + return br_secp256r1.generator; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + (void)curve; + *len = br_secp256r1.order_len; + return br_secp256r1.order; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + (void)curve; + *len = 32; + return 1; +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + p256_jacobian P; + + (void)curve; + if (Glen != 65) { + return 0; + } + r = p256_decode(&P, G, Glen); + p256_mul(&P, x, xlen); + p256_to_affine(&P); + p256_encode(G, &P); + return r; +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + p256_jacobian P; + + (void)curve; + p256_mulgen(&P, x, xlen); + p256_to_affine(&P); + p256_encode(R, &P); + return 65; +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + p256_jacobian P, Q; + uint32_t r, t, z; + int i; + + (void)curve; + if (len != 65) { + return 0; + } + r = p256_decode(&P, A, len); + p256_mul(&P, x, xlen); + if (B == NULL) { + p256_mulgen(&Q, y, ylen); + } else { + r &= p256_decode(&Q, B, len); + p256_mul(&Q, y, ylen); + } + + /* + * The final addition may fail in case both points are equal. + */ + t = p256_add(&P, &Q); + reduce_final_f256(P.z); + z = 0; + for (i = 0; i < 20; i ++) { + z |= P.z[i]; + } + z = EQ(z, 0); + p256_double(&Q); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + CCOPY(z & ~t, &P, &Q, sizeof Q); + p256_to_affine(&P); + p256_encode(A, &P); + r &= ~(z & t); + return r; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_p256_m15 PROGMEM = { + (uint32_t)0x00800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c new file mode 100644 index 0000000000..62bf46d058 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_prime_i15.c @@ -0,0 +1,826 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Parameters for supported curves: + * - field modulus p + * - R^2 mod p (R = 2^(15k) for the smallest k such that R >= p) + * - b*R mod p (b is the second curve equation parameter) + */ + +static const uint16_t P256_P[] PROGMEM = { + 0x0111, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x003F, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x4000, 0x7FFF, + 0x7FFF, 0x0001 +}; + +static const uint16_t P256_R2[] PROGMEM = { + 0x0111, + 0x0000, 0x6000, 0x0000, 0x0000, 0x0000, 0x0000, 0x7FFC, 0x7FFF, + 0x7FBF, 0x7FFF, 0x7FBF, 0x7FFF, 0x7FFF, 0x7FFF, 0x77FF, 0x7FFF, + 0x4FFF, 0x0000 +}; + +static const uint16_t P256_B[] PROGMEM = { + 0x0111, + 0x770C, 0x5EEF, 0x29C4, 0x3EC4, 0x6273, 0x0486, 0x4543, 0x3993, + 0x3C01, 0x6B56, 0x212E, 0x57EE, 0x4882, 0x204B, 0x7483, 0x3C16, + 0x0187, 0x0000 +}; + +static const uint16_t P384_P[] PROGMEM = { + 0x0199, + 0x7FFF, 0x7FFF, 0x0003, 0x0000, 0x0000, 0x0000, 0x7FC0, 0x7FFF, + 0x7EFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x01FF +}; + +static const uint16_t P384_R2[] PROGMEM = { + 0x0199, + 0x1000, 0x0000, 0x0000, 0x7FFF, 0x7FFF, 0x0001, 0x0000, 0x0010, + 0x0000, 0x0000, 0x0000, 0x7F00, 0x7FFF, 0x01FF, 0x0000, 0x1000, + 0x0000, 0x2000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000 +}; + +static const uint16_t P384_B[] PROGMEM = { + 0x0199, + 0x7333, 0x2096, 0x70D1, 0x2310, 0x3020, 0x6197, 0x1464, 0x35BB, + 0x70CA, 0x0117, 0x1920, 0x4136, 0x5FC8, 0x5713, 0x4938, 0x7DD2, + 0x4DD2, 0x4A71, 0x0220, 0x683E, 0x2C87, 0x4DB1, 0x7BFF, 0x6C09, + 0x0452, 0x0084 +}; + +static const uint16_t P521_P[] PROGMEM = { + 0x022B, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, + 0x7FFF, 0x7FFF, 0x07FF +}; + +static const uint16_t P521_R2[] PROGMEM = { + 0x022B, + 0x0100, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, + 0x0000, 0x0000, 0x0000 +}; + +static const uint16_t P521_B[] PROGMEM = { + 0x022B, + 0x7002, 0x6A07, 0x751A, 0x228F, 0x71EF, 0x5869, 0x20F4, 0x1EFC, + 0x7357, 0x37E0, 0x4EEC, 0x605E, 0x1652, 0x26F6, 0x31FA, 0x4A8F, + 0x6193, 0x3C2A, 0x3C42, 0x48C7, 0x3489, 0x6771, 0x4C57, 0x5CCD, + 0x2725, 0x545B, 0x503B, 0x5B42, 0x21A0, 0x2534, 0x687E, 0x70E4, + 0x1618, 0x27D7, 0x0465 +}; + +typedef struct { + const uint16_t *p; + const uint16_t *b; + const uint16_t *R2; + uint16_t p0i; + size_t point_len; +} curve_params; + +static inline const curve_params * +id_to_curve(int curve) +{ + static const curve_params pp[] = { + { P256_P, P256_B, P256_R2, 0x0001, 65 }, + { P384_P, P384_B, P384_R2, 0x0001, 97 }, + { P521_P, P521_B, P521_R2, 0x0001, 133 } + }; + + return &pp[curve - BR_EC_secp256r1]; +} + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) + +/* + * Type for a point in Jacobian coordinates: + * -- three values, x, y and z, in Montgomery representation + * -- affine coordinates are X = x / z^2 and Y = y / z^3 + * -- for the point at infinity, z = 0 + */ +typedef struct { + uint16_t c[3][I15_LEN]; +} jacobian; + +/* + * We use a custom interpreter that uses a dozen registers, and + * only six operations: + * MSET(d, a) copy a into d + * MADD(d, a) d = d+a (modular) + * MSUB(d, a) d = d-a (modular) + * MMUL(d, a, b) d = a*b (Montgomery multiplication) + * MINV(d, a, b) invert d modulo p; a and b are used as scratch registers + * MTZ(d) clear return value if d = 0 + * Destination of MMUL (d) must be distinct from operands (a and b). + * There is no such constraint for MSUB and MADD. + * + * Registers include the operand coordinates, and temporaries. + */ +#define MSET(d, a) (0x0000 + ((d) << 8) + ((a) << 4)) +#define MADD(d, a) (0x1000 + ((d) << 8) + ((a) << 4)) +#define MSUB(d, a) (0x2000 + ((d) << 8) + ((a) << 4)) +#define MMUL(d, a, b) (0x3000 + ((d) << 8) + ((a) << 4) + (b)) +#define MINV(d, a, b) (0x4000 + ((d) << 8) + ((a) << 4) + (b)) +#define MTZ(d) (0x5000 + ((d) << 8)) +#define ENDCODE 0 + +/* + * Registers for the input operands. + */ +#define P1x 0 +#define P1y 1 +#define P1z 2 +#define P2x 3 +#define P2y 4 +#define P2z 5 + +/* + * Alternate names for the first input operand. + */ +#define Px 0 +#define Py 1 +#define Pz 2 + +/* + * Temporaries. + */ +#define t1 6 +#define t2 7 +#define t3 8 +#define t4 9 +#define t5 10 +#define t6 11 +#define t7 12 + +/* + * Extra scratch registers available when there is no second operand (e.g. + * for "double" and "affine"). + */ +#define t8 3 +#define t9 4 +#define t10 5 + +/* + * Doubling formulas are: + * + * s = 4*x*y^2 + * m = 3*(x + z^2)*(x - z^2) + * x' = m^2 - 2*s + * y' = m*(s - x') - 8*y^4 + * z' = 2*y*z + * + * If y = 0 (P has order 2) then this yields infinity (z' = 0), as it + * should. This case should not happen anyway, because our curves have + * prime order, and thus do not contain any point of order 2. + * + * If P is infinity (z = 0), then again the formulas yield infinity, + * which is correct. Thus, this code works for all points. + * + * Cost: 8 multiplications + */ +static const uint16_t code_double[] PROGMEM = { + /* + * Compute z^2 (in t1). + */ + MMUL(t1, Pz, Pz), + + /* + * Compute x-z^2 (in t2) and then x+z^2 (in t1). + */ + MSET(t2, Px), + MSUB(t2, t1), + MADD(t1, Px), + + /* + * Compute m = 3*(x+z^2)*(x-z^2) (in t1). + */ + MMUL(t3, t1, t2), + MSET(t1, t3), + MADD(t1, t3), + MADD(t1, t3), + + /* + * Compute s = 4*x*y^2 (in t2) and 2*y^2 (in t3). + */ + MMUL(t3, Py, Py), + MADD(t3, t3), + MMUL(t2, Px, t3), + MADD(t2, t2), + + /* + * Compute x' = m^2 - 2*s. + */ + MMUL(Px, t1, t1), + MSUB(Px, t2), + MSUB(Px, t2), + + /* + * Compute z' = 2*y*z. + */ + MMUL(t4, Py, Pz), + MSET(Pz, t4), + MADD(Pz, t4), + + /* + * Compute y' = m*(s - x') - 8*y^4. Note that we already have + * 2*y^2 in t3. + */ + MSUB(t2, Px), + MMUL(Py, t1, t2), + MMUL(t4, t3, t3), + MSUB(Py, t4), + MSUB(Py, t4), + + ENDCODE +}; + +/* + * Addtions formulas are: + * + * u1 = x1 * z2^2 + * u2 = x2 * z1^2 + * s1 = y1 * z2^3 + * s2 = y2 * z1^3 + * h = u2 - u1 + * r = s2 - s1 + * x3 = r^2 - h^3 - 2 * u1 * h^2 + * y3 = r * (u1 * h^2 - x3) - s1 * h^3 + * z3 = h * z1 * z2 + * + * If both P1 and P2 are infinity, then z1 == 0 and z2 == 0, implying that + * z3 == 0, so the result is correct. + * If either of P1 or P2 is infinity, but not both, then z3 == 0, which is + * not correct. + * h == 0 only if u1 == u2; this happens in two cases: + * -- if s1 == s2 then P1 and/or P2 is infinity, or P1 == P2 + * -- if s1 != s2 then P1 + P2 == infinity (but neither P1 or P2 is infinity) + * + * Thus, the following situations are not handled correctly: + * -- P1 = 0 and P2 != 0 + * -- P1 != 0 and P2 = 0 + * -- P1 = P2 + * All other cases are properly computed. However, even in "incorrect" + * situations, the three coordinates still are properly formed field + * elements. + * + * The returned flag is cleared if r == 0. This happens in the following + * cases: + * -- Both points are on the same horizontal line (same Y coordinate). + * -- Both points are infinity. + * -- One point is infinity and the other is on line Y = 0. + * The third case cannot happen with our curves (there is no valid point + * on line Y = 0 since that would be a point of order 2). If the two + * source points are non-infinity, then remains only the case where the + * two points are on the same horizontal line. + * + * This allows us to detect the "P1 == P2" case, assuming that P1 != 0 and + * P2 != 0: + * -- If the returned value is not the point at infinity, then it was properly + * computed. + * -- Otherwise, if the returned flag is 1, then P1+P2 = 0, and the result + * is indeed the point at infinity. + * -- Otherwise (result is infinity, flag is 0), then P1 = P2 and we should + * use the 'double' code. + * + * Cost: 16 multiplications + */ +static const uint16_t code_add[] PROGMEM = { + /* + * Compute u1 = x1*z2^2 (in t1) and s1 = y1*z2^3 (in t3). + */ + MMUL(t3, P2z, P2z), + MMUL(t1, P1x, t3), + MMUL(t4, P2z, t3), + MMUL(t3, P1y, t4), + + /* + * Compute u2 = x2*z1^2 (in t2) and s2 = y2*z1^3 (in t4). + */ + MMUL(t4, P1z, P1z), + MMUL(t2, P2x, t4), + MMUL(t5, P1z, t4), + MMUL(t4, P2y, t5), + + /* + * Compute h = u2 - u1 (in t2) and r = s2 - s1 (in t4). + */ + MSUB(t2, t1), + MSUB(t4, t3), + + /* + * Report cases where r = 0 through the returned flag. + */ + MTZ(t4), + + /* + * Compute u1*h^2 (in t6) and h^3 (in t5). + */ + MMUL(t7, t2, t2), + MMUL(t6, t1, t7), + MMUL(t5, t7, t2), + + /* + * Compute x3 = r^2 - h^3 - 2*u1*h^2. + * t1 and t7 can be used as scratch registers. + */ + MMUL(P1x, t4, t4), + MSUB(P1x, t5), + MSUB(P1x, t6), + MSUB(P1x, t6), + + /* + * Compute y3 = r*(u1*h^2 - x3) - s1*h^3. + */ + MSUB(t6, P1x), + MMUL(P1y, t4, t6), + MMUL(t1, t5, t3), + MSUB(P1y, t1), + + /* + * Compute z3 = h*z1*z2. + */ + MMUL(t1, P1z, P2z), + MMUL(P1z, t1, t2), + + ENDCODE +}; + +/* + * Check that the point is on the curve. This code snippet assumes the + * following conventions: + * -- Coordinates x and y have been freshly decoded in P1 (but not + * converted to Montgomery coordinates yet). + * -- P2x, P2y and P2z are set to, respectively, R^2, b*R and 1. + */ +static const uint16_t code_check[] PROGMEM = { + + /* Convert x and y to Montgomery representation. */ + MMUL(t1, P1x, P2x), + MMUL(t2, P1y, P2x), + MSET(P1x, t1), + MSET(P1y, t2), + + /* Compute x^3 in t1. */ + MMUL(t2, P1x, P1x), + MMUL(t1, P1x, t2), + + /* Subtract 3*x from t1. */ + MSUB(t1, P1x), + MSUB(t1, P1x), + MSUB(t1, P1x), + + /* Add b. */ + MADD(t1, P2y), + + /* Compute y^2 in t2. */ + MMUL(t2, P1y, P1y), + + /* Compare y^2 with x^3 - 3*x + b; they must match. */ + MSUB(t1, t2), + MTZ(t1), + + /* Set z to 1 (in Montgomery representation). */ + MMUL(P1z, P2x, P2z), + + ENDCODE +}; + +/* + * Conversion back to affine coordinates. This code snippet assumes that + * the z coordinate of P2 is set to 1 (not in Montgomery representation). + */ +static const uint16_t code_affine[] PROGMEM = { + + /* Save z*R in t1. */ + MSET(t1, P1z), + + /* Compute z^3 in t2. */ + MMUL(t2, P1z, P1z), + MMUL(t3, P1z, t2), + MMUL(t2, t3, P2z), + + /* Invert to (1/z^3) in t2. */ + MINV(t2, t3, t4), + + /* Compute y. */ + MSET(t3, P1y), + MMUL(P1y, t2, t3), + + /* Compute (1/z^2) in t3. */ + MMUL(t3, t2, t1), + + /* Compute x. */ + MSET(t2, P1x), + MMUL(P1x, t2, t3), + + ENDCODE +}; + +static uint32_t +run_code(jacobian *P1, const jacobian *P2, + const curve_params *cc, const uint16_t *code) +{ + uint32_t r; + uint16_t t[13][I15_LEN]; + size_t u; + + r = 1; + + /* + * Copy the two operands in the dedicated registers. + */ + memcpy(t[P1x], P1->c, 3 * I15_LEN * sizeof(uint16_t)); + memcpy(t[P2x], P2->c, 3 * I15_LEN * sizeof(uint16_t)); + + stack_thunk_yield(); + + /* + * Run formulas. + */ + for (u = 0;; u ++) { + unsigned op, d, a, b; + + op = pgm_read_word(&code[u]); + if (op == 0) { + break; + } + d = (op >> 8) & 0x0F; + a = (op >> 4) & 0x0F; + b = op & 0x0F; + op >>= 12; + switch (op) { + uint32_t ctl; + size_t plen; + unsigned char tp[(BR_MAX_EC_SIZE + 7) >> 3]; + + case 0: + memcpy(t[d], t[a], I15_LEN * sizeof(uint16_t)); + break; + case 1: + ctl = br_i15_add(t[d], t[a], 1); + ctl |= NOT(br_i15_sub(t[d], cc->p, 0)); + br_i15_sub(t[d], cc->p, ctl); + break; + case 2: + br_i15_add(t[d], cc->p, br_i15_sub(t[d], t[a], 1)); + break; + case 3: + br_i15_montymul(t[d], t[a], t[b], cc->p, cc->p0i); + break; + case 4: + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + br_i15_encode(tp, plen, cc->p); + tp[plen - 1] -= 2; + br_i15_modpow(t[d], tp, plen, + cc->p, cc->p0i, t[a], t[b]); + break; + default: + r &= ~br_i15_iszero(t[d]); + break; + } + } + + /* + * Copy back result. + */ + memcpy(P1->c, t[P1x], 3 * I15_LEN * sizeof(uint16_t)); + return r; +} + +static void +set_one(uint16_t *x, const uint16_t *p) +{ + size_t plen; + + plen = (pgm_read_word(&p[0]) + 31) >> 4; + memset(x, 0, plen * sizeof *x); + x[0] = pgm_read_word(&p[0]); + x[1] = 0x0001; +} + +static void +point_zero(jacobian *P, const curve_params *cc) +{ + memset(P, 0, sizeof *P); + P->c[0][0] = P->c[1][0] = P->c[2][0] = pgm_read_word(&cc->p[0]); +} + +static inline void +point_double(jacobian *P, const curve_params *cc) +{ + run_code(P, P, cc, code_double); +} + +static inline uint32_t +point_add(jacobian *P1, const jacobian *P2, const curve_params *cc) +{ + return run_code(P1, P2, cc, code_add); +} + +static void +point_mul(jacobian *P, const unsigned char *x, size_t xlen, + const curve_params *cc) +{ + /* + * We do a simple double-and-add ladder with a 2-bit window + * to make only one add every two doublings. We thus first + * precompute 2P and 3P in some local buffers. + * + * We always perform two doublings and one addition; the + * addition is with P, 2P and 3P and is done in a temporary + * array. + * + * The addition code cannot handle cases where one of the + * operands is infinity, which is the case at the start of the + * ladder. We therefore need to maintain a flag that controls + * this situation. + */ + uint32_t qz; + jacobian P2, P3, Q, T, U; + + memcpy(&P2, P, sizeof P2); + point_double(&P2, cc); + memcpy(&P3, P, sizeof P3); + point_add(&P3, &P2, cc); + + point_zero(&Q, cc); + qz = 1; + while (xlen -- > 0) { + int k; + + for (k = 6; k >= 0; k -= 2) { + uint32_t bits; + uint32_t bnz; + + point_double(&Q, cc); + point_double(&Q, cc); + memcpy(&T, P, sizeof T); + memcpy(&U, &Q, sizeof U); + bits = (pgm_read_byte(&*x) >> k) & (uint32_t)3; + bnz = NEQ(bits, 0); + CCOPY(EQ(bits, 2), &T, &P2, sizeof T); + CCOPY(EQ(bits, 3), &T, &P3, sizeof T); + point_add(&U, &T, cc); + CCOPY(bnz & qz, &Q, &T, sizeof Q); + CCOPY(bnz & ~qz, &Q, &U, sizeof Q); + qz &= ~bnz; + } + x ++; + } + memcpy(P, &Q, sizeof Q); +} + +/* + * Decode point into Jacobian coordinates. This function does not support + * the point at infinity. If the point is invalid then this returns 0, but + * the coordinates are still set to properly formed field elements. + */ +static uint32_t +point_decode(jacobian *P, const void *src, size_t len, const curve_params *cc) +{ + /* + * Points must use uncompressed format: + * -- first byte is 0x04; + * -- coordinates X and Y use unsigned big-endian, with the same + * length as the field modulus. + * + * We don't support hybrid format (uncompressed, but first byte + * has value 0x06 or 0x07, depending on the least significant bit + * of Y) because it is rather useless, and explicitly forbidden + * by PKIX (RFC 5480, section 2.2). + * + * We don't support compressed format either, because it is not + * much used in practice (there are or were patent-related + * concerns about point compression, which explains the lack of + * generalised support). Also, point compression support would + * need a bit more code. + */ + const unsigned char *buf; + size_t plen, zlen; + uint32_t r; + jacobian Q; + + buf = src; + point_zero(P, cc); + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + if (len != 1 + (plen << 1)) { + return 0; + } + r = br_i15_decode_mod(P->c[0], buf + 1, plen, cc->p); + r &= br_i15_decode_mod(P->c[1], buf + 1 + plen, plen, cc->p); + + /* + * Check first byte. + */ + r &= EQ(pgm_read_byte(&buf[0]), 0x04); + /* obsolete + r &= EQ(buf[0], 0x04) | (EQ(buf[0] & 0xFE, 0x06) + & ~(uint32_t)(buf[0] ^ buf[plen << 1])); + */ + + /* + * Convert coordinates and check that the point is valid. + */ + zlen = ((pgm_read_word(&cc->p[0]) + 31) >> 4) * sizeof(uint16_t); + memcpy_P(Q.c[0], cc->R2, zlen); + memcpy_P(Q.c[1], cc->b, zlen); + set_one(Q.c[2], cc->p); + r &= ~run_code(P, &Q, cc, code_check); + return r; +} + +/* + * Encode a point. This method assumes that the point is correct and is + * not the point at infinity. Encoded size is always 1+2*plen, where + * plen is the field modulus length, in bytes. + */ +static void +point_encode(void *dst, const jacobian *P, const curve_params *cc) +{ + unsigned char *buf; + size_t plen; + jacobian Q, T; + + buf = dst; + plen = (pgm_read_word(&cc->p[0]) - (pgm_read_word(&cc->p[0]) >> 4) + 7) >> 3; + buf[0] = 0x04; + memcpy(&Q, P, sizeof *P); + set_one(T.c[2], cc->p); + run_code(&Q, &T, cc, code_affine); + br_i15_encode(buf + 1, plen, Q.c[0]); + br_i15_encode(buf + 1 + plen, plen, Q.c[1]); +} + +static const br_ec_curve_def * +id_to_curve_def(int curve) +{ + switch (curve) { + case BR_EC_secp256r1: + return &br_secp256r1; + case BR_EC_secp384r1: + return &br_secp384r1; + case BR_EC_secp521r1: + return &br_secp521r1; + } + return NULL; +} + +static const unsigned char * +api_generator(int curve, size_t *len) +{ + const br_ec_curve_def *cd; + + cd = id_to_curve_def(curve); + *len = cd->generator_len; + return cd->generator; +} + +static const unsigned char * +api_order(int curve, size_t *len) +{ + const br_ec_curve_def *cd; + + cd = id_to_curve_def(curve); + *len = cd->order_len; + return cd->order; +} + +static size_t +api_xoff(int curve, size_t *len) +{ + api_generator(curve, len); + *len >>= 1; + return 1; +} + +static uint32_t +api_mul(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve) +{ + uint32_t r; + const curve_params *cc; + jacobian P; + + cc = id_to_curve(curve); + if (Glen != cc->point_len) { + return 0; + } + r = point_decode(&P, G, Glen, cc); + point_mul(&P, x, xlen, cc); + point_encode(G, &P, cc); + return r; +} + +static size_t +api_mulgen(unsigned char *R, + const unsigned char *x, size_t xlen, int curve) +{ + const unsigned char *G; + size_t Glen; + + G = api_generator(curve, &Glen); + memcpy_P(R, G, Glen); + api_mul(R, Glen, x, xlen, curve); + return Glen; +} + +static uint32_t +api_muladd(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve) +{ + uint32_t r, t, z; + const curve_params *cc; + jacobian P, Q; + + /* + * TODO: see about merging the two ladders. Right now, we do + * two independent point multiplications, which is a bit + * wasteful of CPU resources (but yields short code). + */ + + cc = id_to_curve(curve); + if (len != cc->point_len) { + return 0; + } + r = point_decode(&P, A, len, cc); + if (B == NULL) { + size_t Glen; + + B = api_generator(curve, &Glen); + } + r &= point_decode(&Q, B, len, cc); + point_mul(&P, x, xlen, cc); + point_mul(&Q, y, ylen, cc); + + /* + * We want to compute P+Q. Since the base points A and B are distinct + * from infinity, and the multipliers are non-zero and lower than the + * curve order, then we know that P and Q are non-infinity. This + * leaves two special situations to test for: + * -- If P = Q then we must use point_double(). + * -- If P+Q = 0 then we must report an error. + */ + t = point_add(&P, &Q, cc); + point_double(&Q, cc); + z = br_i15_iszero(P.c[2]); + + /* + * If z is 1 then either P+Q = 0 (t = 1) or P = Q (t = 0). So we + * have the following: + * + * z = 0, t = 0 return P (normal addition) + * z = 0, t = 1 return P (normal addition) + * z = 1, t = 0 return Q (a 'double' case) + * z = 1, t = 1 report an error (P+Q = 0) + */ + CCOPY(z & ~t, &P, &Q, sizeof Q); + point_encode(A, &P, cc); + r &= ~(z & t); + + return r; +} + +/* see bearssl_ec.h */ +const br_ec_impl br_ec_prime_i15 PROGMEM = { + (uint32_t)0x03800000, + &api_generator, + &api_order, + &api_xoff, + &api_mul, + &api_mulgen, + &api_muladd +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c new file mode 100644 index 0000000000..638937f84e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_pubkey.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char POINT_LEN[] PROGMEM = { + 0, /* 0: not a valid curve ID */ + 43, /* sect163k1 */ + 43, /* sect163r1 */ + 43, /* sect163r2 */ + 51, /* sect193r1 */ + 51, /* sect193r2 */ + 61, /* sect233k1 */ + 61, /* sect233r1 */ + 61, /* sect239k1 */ + 73, /* sect283k1 */ + 73, /* sect283r1 */ + 105, /* sect409k1 */ + 105, /* sect409r1 */ + 145, /* sect571k1 */ + 145, /* sect571r1 */ + 41, /* secp160k1 */ + 41, /* secp160r1 */ + 41, /* secp160r2 */ + 49, /* secp192k1 */ + 49, /* secp192r1 */ + 57, /* secp224k1 */ + 57, /* secp224r1 */ + 65, /* secp256k1 */ + 65, /* secp256r1 */ + 97, /* secp384r1 */ + 133, /* secp521r1 */ + 65, /* brainpoolP256r1 */ + 97, /* brainpoolP384r1 */ + 129, /* brainpoolP512r1 */ + 32, /* curve25519 */ + 56, /* curve448 */ +}; + +/* see bearssl_ec.h */ +size_t +br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk) +{ + int curve; + size_t len; + + curve = sk->curve; + if (curve < 0 || curve >= 32 || curve >= (int)(sizeof POINT_LEN) + || ((impl->supported_curves >> curve) & 1) == 0) + { + return 0; + } + if (kbuf == NULL) { + return pgm_read_byte(&POINT_LEN[curve]); + } + len = impl->mulgen(kbuf, sk->x, sk->xlen, curve); + if (pk != NULL) { + pk->curve = curve; + pk->q = kbuf; + pk->qlen = len; + } + return len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c new file mode 100644 index 0000000000..70c856a04b --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp256r1.c @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P256_N[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, + 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51 +}; + +static const unsigned char P256_G[] PROGMEM = { + 0x04, 0x6B, 0x17, 0xD1, 0xF2, 0xE1, 0x2C, 0x42, + 0x47, 0xF8, 0xBC, 0xE6, 0xE5, 0x63, 0xA4, 0x40, + 0xF2, 0x77, 0x03, 0x7D, 0x81, 0x2D, 0xEB, 0x33, + 0xA0, 0xF4, 0xA1, 0x39, 0x45, 0xD8, 0x98, 0xC2, + 0x96, 0x4F, 0xE3, 0x42, 0xE2, 0xFE, 0x1A, 0x7F, + 0x9B, 0x8E, 0xE7, 0xEB, 0x4A, 0x7C, 0x0F, 0x9E, + 0x16, 0x2B, 0xCE, 0x33, 0x57, 0x6B, 0x31, 0x5E, + 0xCE, 0xCB, 0xB6, 0x40, 0x68, 0x37, 0xBF, 0x51, + 0xF5 +}; + +/* see inner.h */ +const br_ec_curve_def br_secp256r1 PROGMEM = { + BR_EC_secp256r1, + P256_N, sizeof P256_N, + P256_G, sizeof P256_G +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c new file mode 100644 index 0000000000..2e6d637091 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp384r1.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P384_N[] PROGMEM = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xC7, 0x63, 0x4D, 0x81, 0xF4, 0x37, 0x2D, 0xDF, + 0x58, 0x1A, 0x0D, 0xB2, 0x48, 0xB0, 0xA7, 0x7A, + 0xEC, 0xEC, 0x19, 0x6A, 0xCC, 0xC5, 0x29, 0x73 +}; + +static const unsigned char P384_G[] PROGMEM = { + 0x04, 0xAA, 0x87, 0xCA, 0x22, 0xBE, 0x8B, 0x05, + 0x37, 0x8E, 0xB1, 0xC7, 0x1E, 0xF3, 0x20, 0xAD, + 0x74, 0x6E, 0x1D, 0x3B, 0x62, 0x8B, 0xA7, 0x9B, + 0x98, 0x59, 0xF7, 0x41, 0xE0, 0x82, 0x54, 0x2A, + 0x38, 0x55, 0x02, 0xF2, 0x5D, 0xBF, 0x55, 0x29, + 0x6C, 0x3A, 0x54, 0x5E, 0x38, 0x72, 0x76, 0x0A, + 0xB7, 0x36, 0x17, 0xDE, 0x4A, 0x96, 0x26, 0x2C, + 0x6F, 0x5D, 0x9E, 0x98, 0xBF, 0x92, 0x92, 0xDC, + 0x29, 0xF8, 0xF4, 0x1D, 0xBD, 0x28, 0x9A, 0x14, + 0x7C, 0xE9, 0xDA, 0x31, 0x13, 0xB5, 0xF0, 0xB8, + 0xC0, 0x0A, 0x60, 0xB1, 0xCE, 0x1D, 0x7E, 0x81, + 0x9D, 0x7A, 0x43, 0x1D, 0x7C, 0x90, 0xEA, 0x0E, + 0x5F +}; + +/* see inner.h */ +const br_ec_curve_def br_secp384r1 PROGMEM = { + BR_EC_secp384r1, + P384_N, sizeof P384_N, + P384_G, sizeof P384_G +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c new file mode 100644 index 0000000000..4c082481d0 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ec_secp521r1.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const unsigned char P521_N[] PROGMEM = { + 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFA, 0x51, 0x86, 0x87, 0x83, 0xBF, 0x2F, + 0x96, 0x6B, 0x7F, 0xCC, 0x01, 0x48, 0xF7, 0x09, + 0xA5, 0xD0, 0x3B, 0xB5, 0xC9, 0xB8, 0x89, 0x9C, + 0x47, 0xAE, 0xBB, 0x6F, 0xB7, 0x1E, 0x91, 0x38, + 0x64, 0x09 +}; + +static const unsigned char P521_G[] PROGMEM = { + 0x04, 0x00, 0xC6, 0x85, 0x8E, 0x06, 0xB7, 0x04, + 0x04, 0xE9, 0xCD, 0x9E, 0x3E, 0xCB, 0x66, 0x23, + 0x95, 0xB4, 0x42, 0x9C, 0x64, 0x81, 0x39, 0x05, + 0x3F, 0xB5, 0x21, 0xF8, 0x28, 0xAF, 0x60, 0x6B, + 0x4D, 0x3D, 0xBA, 0xA1, 0x4B, 0x5E, 0x77, 0xEF, + 0xE7, 0x59, 0x28, 0xFE, 0x1D, 0xC1, 0x27, 0xA2, + 0xFF, 0xA8, 0xDE, 0x33, 0x48, 0xB3, 0xC1, 0x85, + 0x6A, 0x42, 0x9B, 0xF9, 0x7E, 0x7E, 0x31, 0xC2, + 0xE5, 0xBD, 0x66, 0x01, 0x18, 0x39, 0x29, 0x6A, + 0x78, 0x9A, 0x3B, 0xC0, 0x04, 0x5C, 0x8A, 0x5F, + 0xB4, 0x2C, 0x7D, 0x1B, 0xD9, 0x98, 0xF5, 0x44, + 0x49, 0x57, 0x9B, 0x44, 0x68, 0x17, 0xAF, 0xBD, + 0x17, 0x27, 0x3E, 0x66, 0x2C, 0x97, 0xEE, 0x72, + 0x99, 0x5E, 0xF4, 0x26, 0x40, 0xC5, 0x50, 0xB9, + 0x01, 0x3F, 0xAD, 0x07, 0x61, 0x35, 0x3C, 0x70, + 0x86, 0xA2, 0x72, 0xC2, 0x40, 0x88, 0xBE, 0x94, + 0x76, 0x9F, 0xD1, 0x66, 0x50 +}; + +/* see inner.h */ +const br_ec_curve_def br_secp521r1 PROGMEM = { + BR_EC_secp521r1, + P521_N, sizeof P521_N, + P521_G, sizeof P521_G +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c new file mode 100644 index 0000000000..e5b61f05d8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_atr.c @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +size_t +br_ecdsa_asn1_to_raw(void *sig, size_t sig_len) +{ + /* + * Note: this code is a bit lenient in that it accepts a few + * deviations to DER with regards to minimality of encoding of + * lengths and integer values. These deviations are still + * unambiguous. + * + * Signature format is a SEQUENCE of two INTEGER values. We + * support only integers of less than 127 bytes each (signed + * encoding) so the resulting raw signature will have length + * at most 254 bytes. + */ + + unsigned char *buf, *r, *s; + size_t zlen, rlen, slen, off; + unsigned char tmp[254]; + + buf = sig; + if (sig_len < 8) { + return 0; + } + + /* + * First byte is SEQUENCE tag. + */ + if (buf[0] != 0x30) { + return 0; + } + + /* + * The SEQUENCE length will be encoded over one or two bytes. We + * limit the total SEQUENCE contents to 255 bytes, because it + * makes things simpler; this is enough for subgroup orders up + * to 999 bits. + */ + zlen = buf[1]; + if (zlen > 0x80) { + if (zlen != 0x81) { + return 0; + } + zlen = buf[2]; + if (zlen != sig_len - 3) { + return 0; + } + off = 3; + } else { + if (zlen != sig_len - 2) { + return 0; + } + off = 2; + } + + /* + * First INTEGER (r). + */ + if (buf[off ++] != 0x02) { + return 0; + } + rlen = buf[off ++]; + if (rlen >= 0x80) { + return 0; + } + r = buf + off; + off += rlen; + + /* + * Second INTEGER (s). + */ + if (off + 2 > sig_len) { + return 0; + } + if (buf[off ++] != 0x02) { + return 0; + } + slen = buf[off ++]; + if (slen >= 0x80 || slen != sig_len - off) { + return 0; + } + s = buf + off; + + /* + * Removing leading zeros from r and s. + */ + while (rlen > 0 && *r == 0) { + rlen --; + r ++; + } + while (slen > 0 && *s == 0) { + slen --; + s ++; + } + + /* + * Compute common length for the two integers, then copy integers + * into the temporary buffer, and finally copy it back over the + * signature buffer. + */ + zlen = rlen > slen ? rlen : slen; + sig_len = zlen << 1; + memset(tmp, 0, sig_len); + memcpy(tmp + zlen - rlen, r, rlen); + memcpy(tmp + sig_len - slen, s, slen); + memcpy(sig, tmp, sig_len); + return sig_len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c new file mode 100644 index 0000000000..c07ad2affe --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_asn1.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_sign +br_ecdsa_sign_asn1_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_sign_asn1; +#else + return &br_ecdsa_i31_sign_asn1; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c new file mode 100644 index 0000000000..cbaa9b80ff --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_sign_raw.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_sign +br_ecdsa_sign_raw_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_sign_raw; +#else + return &br_ecdsa_i31_sign_raw; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c new file mode 100644 index 0000000000..0a82bc4137 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_asn1.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_vrfy +br_ecdsa_vrfy_asn1_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_vrfy_asn1; +#else + return &br_ecdsa_i31_vrfy_asn1; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c new file mode 100644 index 0000000000..833e79f168 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_default_vrfy_raw.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ec.h */ +br_ecdsa_vrfy +br_ecdsa_vrfy_raw_get_default(void) +{ +#if BR_LOMUL + return &br_ecdsa_i15_vrfy_raw; +#else + return &br_ecdsa_i31_vrfy_raw; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c new file mode 100644 index 0000000000..e1e754d3f5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_bits.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_ecdsa_i15_bits2int(uint16_t *x, + const void *src, size_t len, uint32_t ebitlen) +{ + uint32_t bitlen, hbitlen; + int sc; + + bitlen = ebitlen - (ebitlen >> 4); + hbitlen = (uint32_t)len << 3; + if (hbitlen > bitlen) { + len = (bitlen + 7) >> 3; + sc = (int)((hbitlen - bitlen) & 7); + } else { + sc = 0; + } + br_i15_zero(x, ebitlen); + br_i15_decode(x, src, len); + br_i15_rshift(x, sc); + x[0] = ebitlen; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c new file mode 100644 index 0000000000..6770e7702e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_asn1.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define ORDER_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +size_t +br_ecdsa_i15_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig) +{ + unsigned char rsig[(ORDER_LEN << 1) + 12]; + size_t sig_len; + + sig_len = br_ecdsa_i15_sign_raw(impl, hf, hash_value, sk, rsig); + if (sig_len == 0) { + return 0; + } + sig_len = br_ecdsa_raw_to_asn1(rsig, sig_len); + memcpy(sig, rsig, sig_len); + return sig_len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c new file mode 100644 index 0000000000..83f2587418 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_sign_raw.c @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) +#define POINT_LEN (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1)) +#define ORDER_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +size_t +br_ecdsa_i15_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + * We also rely on the last byte of the curve order to be distinct + * from 0 and 1. + */ + const br_ec_curve_def *cd; + uint16_t n[I15_LEN], r[I15_LEN], s[I15_LEN], x[I15_LEN]; + uint16_t m[I15_LEN], k[I15_LEN], t1[I15_LEN], t2[I15_LEN]; + unsigned char tt[ORDER_LEN << 1]; + unsigned char eU[POINT_LEN]; + size_t hash_len, nlen, ulen; + uint16_t n0i; + uint32_t ctl; + br_hmac_drbg_context drbg; + + /* + * If the curve is not supported, then exit with an error. + */ + if (((impl->supported_curves >> sk->curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + switch (sk->curve) { + case BR_EC_secp256r1: + cd = &br_secp256r1; + break; + case BR_EC_secp384r1: + cd = &br_secp384r1; + break; + case BR_EC_secp521r1: + cd = &br_secp521r1; + break; + default: + return 0; + } + + /* + * Get modulus. + */ + nlen = cd->order_len; + br_i15_decode(n, cd->order, nlen); + n0i = br_i15_ninv15(n[1]); + + /* + * Get private key as an i15 integer. This also checks that the + * private key is well-defined (not zero, and less than the + * curve order). + */ + if (!br_i15_decode_mod(x, sk->x, sk->xlen, n)) { + return 0; + } + if (br_i15_iszero(x)) { + return 0; + } + + /* + * Get hash length. + */ + hash_len = (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + + /* + * Truncate and reduce the hash value modulo the curve order. + */ + br_ecdsa_i15_bits2int(m, hash_value, hash_len, n[0]); + br_i15_sub(m, n, br_i15_sub(m, n, 0) ^ 1); + + /* + * RFC 6979 generation of the "k" value. + * + * The process uses HMAC_DRBG (with the hash function used to + * process the message that is to be signed). The seed is the + * concatenation of the encodings of the private key and + * the hash value (after truncation and modular reduction). + */ + br_i15_encode(tt, nlen, x); + br_i15_encode(tt + nlen, nlen, m); + br_hmac_drbg_init(&drbg, hf, tt, nlen << 1); + for (;;) { + br_hmac_drbg_generate(&drbg, tt, nlen); + br_ecdsa_i15_bits2int(k, tt, nlen, n[0]); + if (br_i15_iszero(k)) { + continue; + } + if (br_i15_sub(k, n, 0)) { + break; + } + } + + /* + * Compute k*G and extract the X coordinate, then reduce it + * modulo the curve order. Since we support only curves with + * prime order, that reduction is only a matter of computing + * a subtraction. + */ + br_i15_encode(tt, nlen, k); + ulen = impl->mulgen(eU, tt, nlen, sk->curve); + br_i15_zero(r, n[0]); + br_i15_decode(r, &eU[1], ulen >> 1); + r[0] = n[0]; + br_i15_sub(r, n, br_i15_sub(r, n, 0) ^ 1); + + /* + * Compute 1/k in double-Montgomery representation. We do so by + * first converting _from_ Montgomery representation (twice), + * then using a modular exponentiation. + */ + br_i15_from_monty(k, n, n0i); + br_i15_from_monty(k, n, n0i); + memcpy_P(tt, cd->order, nlen); + tt[nlen - 1] -= 2; + br_i15_modpow(k, tt, nlen, n, n0i, t1, t2); + + /* + * Compute s = (m+xr)/k (mod n). + * The k[] array contains R^2/k (double-Montgomery representation); + * we thus can use direct Montgomery multiplications and conversions + * from Montgomery, avoiding any call to br_i15_to_monty() (which + * is slower). + */ + br_i15_from_monty(m, n, n0i); + br_i15_montymul(t1, x, r, n, n0i); + ctl = br_i15_add(t1, m, 1); + ctl |= br_i15_sub(t1, n, 0) ^ 1; + br_i15_sub(t1, n, ctl); + br_i15_montymul(s, t1, k, n, n0i); + + /* + * Encode r and s in the signature. + */ + br_i15_encode(sig, nlen, r); + br_i15_encode((unsigned char *)sig + nlen, nlen, s); + return nlen << 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c new file mode 100644 index 0000000000..f213331c58 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_asn1.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define FIELD_LEN ((BR_MAX_EC_SIZE + 7) >> 3) + +/* see bearssl_ec.h */ +uint32_t +br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, + const void *sig, size_t sig_len) +{ + /* + * We use a double-sized buffer because a malformed ASN.1 signature + * may trigger a size expansion when converting to "raw" format. + */ + unsigned char rsig[(FIELD_LEN << 2) + 24]; + + if (sig_len > ((sizeof rsig) >> 1)) { + return 0; + } + memcpy(rsig, sig, sig_len); + sig_len = br_ecdsa_asn1_to_raw(rsig, sig_len); + return br_ecdsa_i15_vrfy_raw(impl, hash, hash_len, pk, rsig, sig_len); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c new file mode 100644 index 0000000000..d2a16ac0e2 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_i15_vrfy_raw.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define I15_LEN ((BR_MAX_EC_SIZE + 29) / 15) +#define POINT_LEN (1 + (((BR_MAX_EC_SIZE + 7) >> 3) << 1)) + +/* see bearssl_ec.h */ +uint32_t +br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, + const void *sig, size_t sig_len) +{ + /* + * IMPORTANT: this code is fit only for curves with a prime + * order. This is needed so that modular reduction of the X + * coordinate of a point can be done with a simple subtraction. + */ + const br_ec_curve_def *cd; + uint16_t n[I15_LEN], r[I15_LEN], s[I15_LEN], t1[I15_LEN], t2[I15_LEN]; + unsigned char tx[(BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char ty[(BR_MAX_EC_SIZE + 7) >> 3]; + unsigned char eU[POINT_LEN]; + size_t nlen, rlen, ulen; + uint16_t n0i; + uint32_t res; + + /* + * If the curve is not supported, then report an error. + */ + if (((impl->supported_curves >> pk->curve) & 1) == 0) { + return 0; + } + + /* + * Get the curve parameters (generator and order). + */ + switch (pk->curve) { + case BR_EC_secp256r1: + cd = &br_secp256r1; + break; + case BR_EC_secp384r1: + cd = &br_secp384r1; + break; + case BR_EC_secp521r1: + cd = &br_secp521r1; + break; + default: + return 0; + } + + /* + * Signature length must be even. + */ + if (sig_len & 1) { + return 0; + } + rlen = sig_len >> 1; + + /* + * Public key point must have the proper size for this curve. + */ + if (pk->qlen != cd->generator_len) { + return 0; + } + + /* + * Get modulus; then decode the r and s values. They must be + * lower than the modulus, and s must not be null. + */ + nlen = cd->order_len; + br_i15_decode(n, cd->order, nlen); + n0i = br_i15_ninv15(n[1]); + if (!br_i15_decode_mod(r, sig, rlen, n)) { + return 0; + } + if (!br_i15_decode_mod(s, (const unsigned char *)sig + rlen, rlen, n)) { + return 0; + } + if (br_i15_iszero(s)) { + return 0; + } + + /* + * Invert s. We do that with a modular exponentiation; we use + * the fact that for all the curves we support, the least + * significant byte is not 0 or 1, so we can subtract 2 without + * any carry to process. + * We also want 1/s in Montgomery representation, which can be + * done by converting _from_ Montgomery representation before + * the inversion (because (1/s)*R = 1/(s/R)). + */ + br_i15_from_monty(s, n, n0i); + memcpy_P(tx, cd->order, nlen); + tx[nlen - 1] -= 2; + br_i15_modpow(s, tx, nlen, n, n0i, t1, t2); + + /* + * Truncate the hash to the modulus length (in bits) and reduce + * it modulo the curve order. The modular reduction can be done + * with a subtraction since the truncation already reduced the + * value to the modulus bit length. + */ + br_ecdsa_i15_bits2int(t1, hash, hash_len, n[0]); + br_i15_sub(t1, n, br_i15_sub(t1, n, 0) ^ 1); + + /* + * Multiply the (truncated, reduced) hash value with 1/s, result in + * t2, encoded in ty. + */ + br_i15_montymul(t2, t1, s, n, n0i); + br_i15_encode(ty, nlen, t2); + + /* + * Multiply r with 1/s, result in t1, encoded in tx. + */ + br_i15_montymul(t1, r, s, n, n0i); + br_i15_encode(tx, nlen, t1); + + /* + * Compute the point x*Q + y*G. + */ + ulen = cd->generator_len; + memcpy(eU, pk->q, ulen); + res = impl->muladd(eU, NULL, ulen, + tx, nlen, ty, nlen, cd->curve); + + /* + * Get the X coordinate, reduce modulo the curve order, and + * compare with the 'r' value. + * + * The modular reduction can be done with subtractions because + * we work with curves of prime order, so the curve order is + * close to the field order (Hasse's theorem). + */ + br_i15_zero(t1, n[0]); + br_i15_decode(t1, &eU[1], ulen >> 1); + t1[0] = n[0]; + br_i15_sub(t1, n, br_i15_sub(t1, n, 0) ^ 1); + res &= ~br_i15_sub(t1, r, 1); + res &= br_i15_iszero(t1); + return res; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c new file mode 100644 index 0000000000..6550dc7272 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ec/ecdsa_rta.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Compute ASN.1 encoded length for the provided integer. The ASN.1 + * encoding is signed, so its leading bit must have value 0; it must + * also be of minimal length (so leading bytes of value 0 must be + * removed, except if that would contradict the rule about the sign + * bit). + */ +static size_t +asn1_int_length(const unsigned char *x, size_t xlen) +{ + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + if (xlen == 0 || *x >= 0x80) { + xlen ++; + } + return xlen; +} + +/* see bearssl_ec.h */ +size_t +br_ecdsa_raw_to_asn1(void *sig, size_t sig_len) +{ + /* + * Internal buffer is large enough to accommodate a signature + * such that r and s fit on 125 bytes each (signed encoding), + * meaning a curve order of up to 999 bits. This is the limit + * that ensures "simple" length encodings. + */ + unsigned char *buf; + size_t hlen, rlen, slen, zlen, off; + unsigned char tmp[257]; + + buf = sig; + if ((sig_len & 1) != 0) { + return 0; + } + + /* + * Compute lengths for the two integers. + */ + hlen = sig_len >> 1; + rlen = asn1_int_length(buf, hlen); + slen = asn1_int_length(buf + hlen, hlen); + if (rlen > 125 || slen > 125) { + return 0; + } + + /* + * SEQUENCE header. + */ + tmp[0] = 0x30; + zlen = rlen + slen + 4; + if (zlen >= 0x80) { + tmp[1] = 0x81; + tmp[2] = zlen; + off = 3; + } else { + tmp[1] = zlen; + off = 2; + } + + /* + * First INTEGER (r). + */ + tmp[off ++] = 0x02; + tmp[off ++] = rlen; + if (rlen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf, hlen); + } else { + memcpy(tmp + off, buf + hlen - rlen, rlen); + } + off += rlen; + + /* + * Second INTEGER (s). + */ + tmp[off ++] = 0x02; + tmp[off ++] = slen; + if (slen > hlen) { + tmp[off] = 0x00; + memcpy(tmp + off + 1, buf + hlen, hlen); + } else { + memcpy(tmp + off, buf + sig_len - slen, slen); + } + off += slen; + + /* + * Return ASN.1 signature. + */ + memcpy(sig, tmp, off); + return off; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c new file mode 100644 index 0000000000..f8cfd9937e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_oid.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This file contains the encoded OID for the standard hash functions. + * Such OID appear in, for instance, the PKCS#1 v1.5 padding for RSA + * signatures. + */ + +static const unsigned char md5_OID[] = { + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x02, 0x05 +}; + +static const unsigned char sha1_OID[] = { + 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +static const unsigned char sha224_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +static const unsigned char sha256_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +static const unsigned char sha384_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +static const unsigned char sha512_OID[] = { + 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; + +/* see inner.h */ +const unsigned char * +br_digest_OID(int digest_id, size_t *len) +{ + switch (digest_id) { + case br_md5_ID: + *len = sizeof md5_OID; + return md5_OID; + case br_sha1_ID: + *len = sizeof sha1_OID; + return sha1_OID; + case br_sha224_ID: + *len = sizeof sha224_OID; + return sha224_OID; + case br_sha256_ID: + *len = sizeof sha256_OID; + return sha256_OID; + case br_sha384_ID: + *len = sizeof sha384_OID; + return sha384_OID; + case br_sha512_ID: + *len = sizeof sha512_OID; + return sha512_OID; + default: + *len = 0; + return NULL; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c new file mode 100644 index 0000000000..bd3c8dbf75 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/dig_size.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +size_t +br_digest_size_by_ID(int digest_id) +{ + switch (digest_id) { + case br_md5sha1_ID: + return br_md5_SIZE + br_sha1_SIZE; + case br_md5_ID: + return br_md5_SIZE; + case br_sha1_ID: + return br_sha1_SIZE; + case br_sha224_ID: + return br_sha224_SIZE; + case br_sha256_ID: + return br_sha256_SIZE; + case br_sha384_ID: + return br_sha384_SIZE; + case br_sha512_ID: + return br_sha512_SIZE; + default: + /* abort(); */ + return 0; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c new file mode 100644 index 0000000000..344830e3fd --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul.c @@ -0,0 +1,345 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * We compute "carryless multiplications" through normal integer + * multiplications, masking out enough bits to create "holes" in which + * carries may expand without altering our bits; we really use 8 data + * bits per 32-bit word, spaced every fourth bit. Accumulated carries + * may not exceed 8 in total, which fits in 4 bits. + * + * It would be possible to use a 3-bit spacing, allowing two operands, + * one with 7 non-zero data bits, the other one with 10 or 11 non-zero + * data bits; this asymmetric splitting makes the overall code more + * complex with thresholds and exceptions, and does not appear to be + * worth the effort. + */ + +/* + * We cannot really autodetect whether multiplications are "slow" or + * not. A typical example is the ARM Cortex M0+, which exists in two + * versions: one with a 1-cycle multiplication opcode, the other with + * a 32-cycle multiplication opcode. They both use exactly the same + * architecture and ABI, and cannot be distinguished from each other + * at compile-time. + * + * Since most modern CPU (even embedded CPU) still have fast + * multiplications, we use the "fast mul" code by default. + */ + +#if BR_SLOW_MUL + +/* + * This implementation uses Karatsuba-like reduction to make fewer + * integer multiplications (9 instead of 16), at the expense of extra + * logical operations (XOR, shifts...). On modern x86 CPU that offer + * fast, pipelined multiplications, this code is about twice slower than + * the simpler code with 16 multiplications. This tendency may be + * reversed on low-end platforms with expensive multiplications. + */ + +#define MUL32(h, l, x, y) do { \ + uint64_t mul32tmp = MUL(x, y); \ + (h) = (uint32_t)(mul32tmp >> 32); \ + (l) = (uint32_t)mul32tmp; \ + } while (0) + +static inline void +bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint32_t a0, a1, a2, a3, a4, a5, a6, a7, a8; + uint32_t b0, b1, b2, b3, b4, b5, b6, b7, b8; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + + /* + * (x0+W*x1)*(y0+W*y1) -> a0:b0 + * (x2+W*x3)*(y2+W*y3) -> a3:b3 + * ((x0+x2)+W*(x1+x3))*((y0+y2)+W*(y1+y3)) -> a6:b6 + */ + a0 = x0; + b0 = y0; + a1 = x1 >> 1; + b1 = y1 >> 1; + a2 = a0 ^ a1; + b2 = b0 ^ b1; + a3 = x2 >> 2; + b3 = y2 >> 2; + a4 = x3 >> 3; + b4 = y3 >> 3; + a5 = a3 ^ a4; + b5 = b3 ^ b4; + a6 = a0 ^ a3; + b6 = b0 ^ b3; + a7 = a1 ^ a4; + b7 = b1 ^ b4; + a8 = a6 ^ a7; + b8 = b6 ^ b7; + + MUL32(b0, a0, b0, a0); + MUL32(b1, a1, b1, a1); + MUL32(b2, a2, b2, a2); + MUL32(b3, a3, b3, a3); + MUL32(b4, a4, b4, a4); + MUL32(b5, a5, b5, a5); + MUL32(b6, a6, b6, a6); + MUL32(b7, a7, b7, a7); + MUL32(b8, a8, b8, a8); + + a0 &= (uint32_t)0x11111111; + a1 &= (uint32_t)0x11111111; + a2 &= (uint32_t)0x11111111; + a3 &= (uint32_t)0x11111111; + a4 &= (uint32_t)0x11111111; + a5 &= (uint32_t)0x11111111; + a6 &= (uint32_t)0x11111111; + a7 &= (uint32_t)0x11111111; + a8 &= (uint32_t)0x11111111; + b0 &= (uint32_t)0x11111111; + b1 &= (uint32_t)0x11111111; + b2 &= (uint32_t)0x11111111; + b3 &= (uint32_t)0x11111111; + b4 &= (uint32_t)0x11111111; + b5 &= (uint32_t)0x11111111; + b6 &= (uint32_t)0x11111111; + b7 &= (uint32_t)0x11111111; + b8 &= (uint32_t)0x11111111; + + a2 ^= a0 ^ a1; + b2 ^= b0 ^ b1; + a0 ^= (a2 << 1) ^ (a1 << 2); + b0 ^= (b2 << 1) ^ (b1 << 2); + a5 ^= a3 ^ a4; + b5 ^= b3 ^ b4; + a3 ^= (a5 << 1) ^ (a4 << 2); + b3 ^= (b5 << 1) ^ (b4 << 2); + a8 ^= a6 ^ a7; + b8 ^= b6 ^ b7; + a6 ^= (a8 << 1) ^ (a7 << 2); + b6 ^= (b8 << 1) ^ (b7 << 2); + a6 ^= a0 ^ a3; + b6 ^= b0 ^ b3; + *lo = a0 ^ (a6 << 2) ^ (a3 << 4); + *hi = b0 ^ (b6 << 2) ^ (b3 << 4) ^ (a6 >> 30) ^ (a3 >> 28); +} + +#else + +/* + * Simple multiplication in GF(2)[X], using 16 integer multiplications. + */ + +static inline void +bmul(uint32_t *hi, uint32_t *lo, uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint64_t z0, z1, z2, z3; + uint64_t z; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + z0 = MUL(x0, y0) ^ MUL(x1, y3) ^ MUL(x2, y2) ^ MUL(x3, y1); + z1 = MUL(x0, y1) ^ MUL(x1, y0) ^ MUL(x2, y3) ^ MUL(x3, y2); + z2 = MUL(x0, y2) ^ MUL(x1, y1) ^ MUL(x2, y0) ^ MUL(x3, y3); + z3 = MUL(x0, y3) ^ MUL(x1, y2) ^ MUL(x2, y1) ^ MUL(x3, y0); + z0 &= (uint64_t)0x1111111111111111; + z1 &= (uint64_t)0x2222222222222222; + z2 &= (uint64_t)0x4444444444444444; + z3 &= (uint64_t)0x8888888888888888; + z = z0 | z1 | z2 | z3; + *lo = (uint32_t)z; + *hi = (uint32_t)(z >> 32); +} + +#endif + +/* see bearssl_hash.h */ +void +br_ghash_ctmul(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf, *hb; + unsigned char *yb; + uint32_t yw[4]; + uint32_t hw[4]; + + /* + * Throughout the loop we handle the y and h values as arrays + * of 32-bit words. + */ + buf = data; + yb = y; + hb = h; + yw[3] = br_dec32be(yb); + yw[2] = br_dec32be(yb + 4); + yw[1] = br_dec32be(yb + 8); + yw[0] = br_dec32be(yb + 12); + hw[3] = br_dec32be(hb); + hw[2] = br_dec32be(hb + 4); + hw[1] = br_dec32be(hb + 8); + hw[0] = br_dec32be(hb + 12); + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + int i; + uint32_t a[9], b[9], zw[8]; + uint32_t c0, c1, c2, c3, d0, d1, d2, d3, e0, e1, e2, e3; + + /* + * Get the next 16-byte block (using zero-padding if + * necessary). + */ + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + + /* + * Decode the block. The GHASH standard mandates + * big-endian encoding. + */ + yw[3] ^= br_dec32be(src); + yw[2] ^= br_dec32be(src + 4); + yw[1] ^= br_dec32be(src + 8); + yw[0] ^= br_dec32be(src + 12); + + /* + * We multiply two 128-bit field elements. We use + * Karatsuba to turn that into three 64-bit + * multiplications, which are themselves done with a + * total of nine 32-bit multiplications. + */ + + /* + * y[0,1]*h[0,1] -> 0..2 + * y[2,3]*h[2,3] -> 3..5 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6..8 + */ + a[0] = yw[0]; + b[0] = hw[0]; + a[1] = yw[1]; + b[1] = hw[1]; + a[2] = a[0] ^ a[1]; + b[2] = b[0] ^ b[1]; + + a[3] = yw[2]; + b[3] = hw[2]; + a[4] = yw[3]; + b[4] = hw[3]; + a[5] = a[3] ^ a[4]; + b[5] = b[3] ^ b[4]; + + a[6] = a[0] ^ a[3]; + b[6] = b[0] ^ b[3]; + a[7] = a[1] ^ a[4]; + b[7] = b[1] ^ b[4]; + a[8] = a[6] ^ a[7]; + b[8] = b[6] ^ b[7]; + + for (i = 0; i < 9; i ++) { + bmul(&b[i], &a[i], b[i], a[i]); + } + + c0 = a[0]; + c1 = b[0] ^ a[2] ^ a[0] ^ a[1]; + c2 = a[1] ^ b[2] ^ b[0] ^ b[1]; + c3 = b[1]; + d0 = a[3]; + d1 = b[3] ^ a[5] ^ a[3] ^ a[4]; + d2 = a[4] ^ b[5] ^ b[3] ^ b[4]; + d3 = b[4]; + e0 = a[6]; + e1 = b[6] ^ a[8] ^ a[6] ^ a[7]; + e2 = a[7] ^ b[8] ^ b[6] ^ b[7]; + e3 = b[7]; + + e0 ^= c0 ^ d0; + e1 ^= c1 ^ d1; + e2 ^= c2 ^ d2; + e3 ^= c3 ^ d3; + c2 ^= e0; + c3 ^= e1; + d0 ^= e2; + d1 ^= e3; + + /* + * GHASH specification has the bits "reversed" (most + * significant is in fact least significant), which does + * not matter for a carryless multiplication, except that + * the 255-bit result must be shifted by 1 bit. + */ + zw[0] = c0 << 1; + zw[1] = (c1 << 1) | (c0 >> 31); + zw[2] = (c2 << 1) | (c1 >> 31); + zw[3] = (c3 << 1) | (c2 >> 31); + zw[4] = (d0 << 1) | (c3 >> 31); + zw[5] = (d1 << 1) | (d0 >> 31); + zw[6] = (d2 << 1) | (d1 >> 31); + zw[7] = (d3 << 1) | (d2 >> 31); + + /* + * We now do the reduction modulo the field polynomial + * to get back to 128 bits. + */ + for (i = 0; i < 4; i ++) { + uint32_t lw; + + lw = zw[i]; + zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7); + zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25); + } + memcpy(yw, zw + 4, sizeof yw); + } + + /* + * Encode back the result. + */ + br_enc32be(yb, yw[3]); + br_enc32be(yb + 4, yw[2]); + br_enc32be(yb + 8, yw[1]); + br_enc32be(yb + 12, yw[0]); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c new file mode 100644 index 0000000000..5f73c8160a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul32.c @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This implementation uses 32-bit multiplications, and only the low + * 32 bits for each multiplication result. This is meant primarily for + * the ARM Cortex M0 and M0+, whose multiplication opcode does not yield + * the upper 32 bits; but it might also be useful on architectures where + * access to the upper 32 bits requires use of specific registers that + * create contention (e.g. on i386, "mul" necessarily outputs the result + * in edx:eax, while "imul" can use any registers but is limited to the + * low 32 bits). + * + * The implementation trick that is used here is bit-reversing (bit 0 + * is swapped with bit 31, bit 1 with bit 30, and so on). In GF(2)[X], + * for all values x and y, we have: + * rev32(x) * rev32(y) = rev64(x * y) + * In other words, if we bit-reverse (over 32 bits) the operands, then we + * bit-reverse (over 64 bits) the result. + */ + +/* + * Multiplication in GF(2)[X], truncated to its low 32 bits. + */ +static inline uint32_t +bmul32(uint32_t x, uint32_t y) +{ + uint32_t x0, x1, x2, x3; + uint32_t y0, y1, y2, y3; + uint32_t z0, z1, z2, z3; + + x0 = x & (uint32_t)0x11111111; + x1 = x & (uint32_t)0x22222222; + x2 = x & (uint32_t)0x44444444; + x3 = x & (uint32_t)0x88888888; + y0 = y & (uint32_t)0x11111111; + y1 = y & (uint32_t)0x22222222; + y2 = y & (uint32_t)0x44444444; + y3 = y & (uint32_t)0x88888888; + z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1); + z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2); + z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3); + z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0); + z0 &= (uint32_t)0x11111111; + z1 &= (uint32_t)0x22222222; + z2 &= (uint32_t)0x44444444; + z3 &= (uint32_t)0x88888888; + return z0 | z1 | z2 | z3; +} + +/* + * Bit-reverse a 32-bit word. + */ +static uint32_t +rev32(uint32_t x) +{ +#define RMS(m, s) do { \ + x = ((x & (uint32_t)(m)) << (s)) \ + | ((x >> (s)) & (uint32_t)(m)); \ + } while (0) + + RMS(0x55555555, 1); + RMS(0x33333333, 2); + RMS(0x0F0F0F0F, 4); + RMS(0x00FF00FF, 8); + return (x << 16) | (x >> 16); + +#undef RMS +} + +/* see bearssl_hash.h */ +void +br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len) +{ + /* + * This implementation is similar to br_ghash_ctmul() except + * that we have to do the multiplication twice, with the + * "normal" and "bit reversed" operands. Hence we end up with + * eighteen 32-bit multiplications instead of nine. + */ + + const unsigned char *buf, *hb; + unsigned char *yb; + uint32_t yw[4]; + uint32_t hw[4], hwr[4]; + + buf = data; + yb = y; + hb = h; + yw[3] = br_dec32be(yb); + yw[2] = br_dec32be(yb + 4); + yw[1] = br_dec32be(yb + 8); + yw[0] = br_dec32be(yb + 12); + hw[3] = br_dec32be(hb); + hw[2] = br_dec32be(hb + 4); + hw[1] = br_dec32be(hb + 8); + hw[0] = br_dec32be(hb + 12); + hwr[3] = rev32(hw[3]); + hwr[2] = rev32(hw[2]); + hwr[1] = rev32(hw[1]); + hwr[0] = rev32(hw[0]); + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + int i; + uint32_t a[18], b[18], c[18]; + uint32_t d0, d1, d2, d3, d4, d5, d6, d7; + uint32_t zw[8]; + + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + yw[3] ^= br_dec32be(src); + yw[2] ^= br_dec32be(src + 4); + yw[1] ^= br_dec32be(src + 8); + yw[0] ^= br_dec32be(src + 12); + + /* + * We are using Karatsuba: the 128x128 multiplication is + * reduced to three 64x64 multiplications, hence nine + * 32x32 multiplications. With the bit-reversal trick, + * we have to perform 18 32x32 multiplications. + */ + + /* + * y[0,1]*h[0,1] -> 0,1,4 + * y[2,3]*h[2,3] -> 2,3,5 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,7,8 + */ + + a[0] = yw[0]; + a[1] = yw[1]; + a[2] = yw[2]; + a[3] = yw[3]; + a[4] = a[0] ^ a[1]; + a[5] = a[2] ^ a[3]; + a[6] = a[0] ^ a[2]; + a[7] = a[1] ^ a[3]; + a[8] = a[6] ^ a[7]; + + a[ 9] = rev32(yw[0]); + a[10] = rev32(yw[1]); + a[11] = rev32(yw[2]); + a[12] = rev32(yw[3]); + a[13] = a[ 9] ^ a[10]; + a[14] = a[11] ^ a[12]; + a[15] = a[ 9] ^ a[11]; + a[16] = a[10] ^ a[12]; + a[17] = a[15] ^ a[16]; + + b[0] = hw[0]; + b[1] = hw[1]; + b[2] = hw[2]; + b[3] = hw[3]; + b[4] = b[0] ^ b[1]; + b[5] = b[2] ^ b[3]; + b[6] = b[0] ^ b[2]; + b[7] = b[1] ^ b[3]; + b[8] = b[6] ^ b[7]; + + b[ 9] = hwr[0]; + b[10] = hwr[1]; + b[11] = hwr[2]; + b[12] = hwr[3]; + b[13] = b[ 9] ^ b[10]; + b[14] = b[11] ^ b[12]; + b[15] = b[ 9] ^ b[11]; + b[16] = b[10] ^ b[12]; + b[17] = b[15] ^ b[16]; + + for (i = 0; i < 18; i ++) { + c[i] = bmul32(a[i], b[i]); + } + + c[4] ^= c[0] ^ c[1]; + c[5] ^= c[2] ^ c[3]; + c[8] ^= c[6] ^ c[7]; + + c[13] ^= c[ 9] ^ c[10]; + c[14] ^= c[11] ^ c[12]; + c[17] ^= c[15] ^ c[16]; + + /* + * y[0,1]*h[0,1] -> 0,9^4,1^13,10 + * y[2,3]*h[2,3] -> 2,11^5,3^14,12 + * (y[0,1]+y[2,3])*(h[0,1]+h[2,3]) -> 6,15^8,7^17,16 + */ + d0 = c[0]; + d1 = c[4] ^ (rev32(c[9]) >> 1); + d2 = c[1] ^ c[0] ^ c[2] ^ c[6] ^ (rev32(c[13]) >> 1); + d3 = c[4] ^ c[5] ^ c[8] + ^ (rev32(c[10] ^ c[9] ^ c[11] ^ c[15]) >> 1); + d4 = c[2] ^ c[1] ^ c[3] ^ c[7] + ^ (rev32(c[13] ^ c[14] ^ c[17]) >> 1); + d5 = c[5] ^ (rev32(c[11] ^ c[10] ^ c[12] ^ c[16]) >> 1); + d6 = c[3] ^ (rev32(c[14]) >> 1); + d7 = rev32(c[12]) >> 1; + + zw[0] = d0 << 1; + zw[1] = (d1 << 1) | (d0 >> 31); + zw[2] = (d2 << 1) | (d1 >> 31); + zw[3] = (d3 << 1) | (d2 >> 31); + zw[4] = (d4 << 1) | (d3 >> 31); + zw[5] = (d5 << 1) | (d4 >> 31); + zw[6] = (d6 << 1) | (d5 >> 31); + zw[7] = (d7 << 1) | (d6 >> 31); + + for (i = 0; i < 4; i ++) { + uint32_t lw; + + lw = zw[i]; + zw[i + 4] ^= lw ^ (lw >> 1) ^ (lw >> 2) ^ (lw >> 7); + zw[i + 3] ^= (lw << 31) ^ (lw << 30) ^ (lw << 25); + } + memcpy(yw, zw + 4, sizeof yw); + } + br_enc32be(yb, yw[3]); + br_enc32be(yb + 4, yw[2]); + br_enc32be(yb + 8, yw[1]); + br_enc32be(yb + 12, yw[0]); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c new file mode 100644 index 0000000000..45604f7d7d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_ctmul64.c @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This is the 64-bit variant of br_ghash_ctmul32(), with 64-bit operands + * and bit reversal of 64-bit words. + */ + +static inline uint64_t +bmul64(uint64_t x, uint64_t y) +{ + uint64_t x0, x1, x2, x3; + uint64_t y0, y1, y2, y3; + uint64_t z0, z1, z2, z3; + + x0 = x & (uint64_t)0x1111111111111111; + x1 = x & (uint64_t)0x2222222222222222; + x2 = x & (uint64_t)0x4444444444444444; + x3 = x & (uint64_t)0x8888888888888888; + y0 = y & (uint64_t)0x1111111111111111; + y1 = y & (uint64_t)0x2222222222222222; + y2 = y & (uint64_t)0x4444444444444444; + y3 = y & (uint64_t)0x8888888888888888; + z0 = (x0 * y0) ^ (x1 * y3) ^ (x2 * y2) ^ (x3 * y1); + z1 = (x0 * y1) ^ (x1 * y0) ^ (x2 * y3) ^ (x3 * y2); + z2 = (x0 * y2) ^ (x1 * y1) ^ (x2 * y0) ^ (x3 * y3); + z3 = (x0 * y3) ^ (x1 * y2) ^ (x2 * y1) ^ (x3 * y0); + z0 &= (uint64_t)0x1111111111111111; + z1 &= (uint64_t)0x2222222222222222; + z2 &= (uint64_t)0x4444444444444444; + z3 &= (uint64_t)0x8888888888888888; + return z0 | z1 | z2 | z3; +} + +static uint64_t +rev64(uint64_t x) +{ +#define RMS(m, s) do { \ + x = ((x & (uint64_t)(m)) << (s)) \ + | ((x >> (s)) & (uint64_t)(m)); \ + } while (0) + + RMS(0x5555555555555555, 1); + RMS(0x3333333333333333, 2); + RMS(0x0F0F0F0F0F0F0F0F, 4); + RMS(0x00FF00FF00FF00FF, 8); + RMS(0x0000FFFF0000FFFF, 16); + return (x << 32) | (x >> 32); + +#undef RMS +} + +/* see bearssl_ghash.h */ +void +br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf, *hb; + unsigned char *yb; + uint64_t y0, y1; + uint64_t h0, h1, h2, h0r, h1r, h2r; + + buf = data; + yb = y; + hb = h; + y1 = br_dec64be(yb); + y0 = br_dec64be(yb + 8); + h1 = br_dec64be(hb); + h0 = br_dec64be(hb + 8); + h0r = rev64(h0); + h1r = rev64(h1); + h2 = h0 ^ h1; + h2r = h0r ^ h1r; + while (len > 0) { + const unsigned char *src; + unsigned char tmp[16]; + uint64_t y0r, y1r, y2, y2r; + uint64_t z0, z1, z2, z0h, z1h, z2h; + uint64_t v0, v1, v2, v3; + + if (len >= 16) { + src = buf; + buf += 16; + len -= 16; + } else { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + src = tmp; + len = 0; + } + y1 ^= br_dec64be(src); + y0 ^= br_dec64be(src + 8); + + y0r = rev64(y0); + y1r = rev64(y1); + y2 = y0 ^ y1; + y2r = y0r ^ y1r; + + z0 = bmul64(y0, h0); + z1 = bmul64(y1, h1); + z2 = bmul64(y2, h2); + z0h = bmul64(y0r, h0r); + z1h = bmul64(y1r, h1r); + z2h = bmul64(y2r, h2r); + z2 ^= z0 ^ z1; + z2h ^= z0h ^ z1h; + z0h = rev64(z0h) >> 1; + z1h = rev64(z1h) >> 1; + z2h = rev64(z2h) >> 1; + + v0 = z0; + v1 = z0h ^ z2; + v2 = z1 ^ z2h; + v3 = z1h; + + v3 = (v3 << 1) | (v2 >> 63); + v2 = (v2 << 1) | (v1 >> 63); + v1 = (v1 << 1) | (v0 >> 63); + v0 = (v0 << 1); + + v2 ^= v0 ^ (v0 >> 1) ^ (v0 >> 2) ^ (v0 >> 7); + v1 ^= (v0 << 63) ^ (v0 << 62) ^ (v0 << 57); + v3 ^= v1 ^ (v1 >> 1) ^ (v1 >> 2) ^ (v1 >> 7); + v2 ^= (v1 << 63) ^ (v1 << 62) ^ (v1 << 57); + + y0 = v2; + y1 = v3; + } + + br_enc64be(yb, y1); + br_enc64be(yb + 8, y0); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c new file mode 100644 index 0000000000..f8feaa5adb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/ghash_pclmul.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +/* + * This is the GHASH implementation that leverages the pclmulqdq opcode + * (from the AES-NI instructions). + */ + +#if BR_AES_X86NI + +/* + * Test CPU support for PCLMULQDQ. + */ +static inline int +pclmul_supported(void) +{ + /* + * Bit mask for features in ECX: + * 1 PCLMULQDQ support + */ + return br_cpuid(0, 0, 0x00000002, 0); +} + +/* see bearssl_hash.h */ +br_ghash +br_ghash_pclmul_get(void) +{ + return pclmul_supported() ? &br_ghash_pclmul : 0; +} + +BR_TARGETS_X86_UP + +/* + * GHASH is defined over elements of GF(2^128) with "full little-endian" + * representation: leftmost byte is least significant, and, within each + * byte, leftmost _bit_ is least significant. The natural ordering in + * x86 is "mixed little-endian": bytes are ordered from least to most + * significant, but bits within a byte are in most-to-least significant + * order. Going to full little-endian representation would require + * reversing bits within each byte, which is doable but expensive. + * + * Instead, we go to full big-endian representation, by swapping bytes + * around, which is done with a single _mm_shuffle_epi8() opcode (it + * comes with SSSE3; all CPU that offer pclmulqdq also have SSSE3). We + * can use a full big-endian representation because in a carryless + * multiplication, we have a nice bit reversal property: + * + * rev_128(x) * rev_128(y) = rev_255(x * y) + * + * So by using full big-endian, we still get the right result, except + * that it is right-shifted by 1 bit. The left-shift is relatively + * inexpensive, and it can be mutualised. + * + * + * Since SSE2 opcodes do not have facilities for shitfting full 128-bit + * values with bit precision, we have to break down values into 64-bit + * chunks. We number chunks from 0 to 3 in left to right order. + */ + +/* + * Byte-swap a complete 128-bit value. This normally uses + * _mm_shuffle_epi8(), which gets translated to pshufb (an SSSE3 opcode). + * However, this crashes old Clang versions, so, for Clang before 3.8, + * we use an alternate (and less efficient) version. + */ +#if BR_CLANG && !BR_CLANG_3_8 +#define BYTESWAP_DECL +#define BYTESWAP_PREP (void)0 +#define BYTESWAP(x) do { \ + __m128i byteswap1, byteswap2; \ + byteswap1 = (x); \ + byteswap2 = _mm_srli_epi16(byteswap1, 8); \ + byteswap1 = _mm_slli_epi16(byteswap1, 8); \ + byteswap1 = _mm_or_si128(byteswap1, byteswap2); \ + byteswap1 = _mm_shufflelo_epi16(byteswap1, 0x1B); \ + byteswap1 = _mm_shufflehi_epi16(byteswap1, 0x1B); \ + (x) = _mm_shuffle_epi32(byteswap1, 0x4E); \ + } while (0) +#else +#define BYTESWAP_DECL __m128i byteswap_index; +#define BYTESWAP_PREP do { \ + byteswap_index = _mm_set_epi8( \ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); \ + } while (0) +#define BYTESWAP(x) do { \ + (x) = _mm_shuffle_epi8((x), byteswap_index); \ + } while (0) +#endif + +/* + * Call pclmulqdq. Clang appears to have trouble with the intrinsic, so, + * for that compiler, we use inline assembly. Inline assembly is + * potentially a bit slower because the compiler does not understand + * what the opcode does, and thus cannot optimize instruction + * scheduling. + * + * We use a target of "sse2" only, so that Clang may still handle the + * '__m128i' type and allocate SSE2 registers. + */ +#if BR_CLANG +BR_TARGET("sse2") +static inline __m128i +pclmulqdq00(__m128i x, __m128i y) +{ + __asm__ ("pclmulqdq $0x00, %1, %0" : "+x" (x) : "x" (y)); + return x; +} +BR_TARGET("sse2") +static inline __m128i +pclmulqdq11(__m128i x, __m128i y) +{ + __asm__ ("pclmulqdq $0x11, %1, %0" : "+x" (x) : "x" (y)); + return x; +} +#else +#define pclmulqdq00(x, y) _mm_clmulepi64_si128(x, y, 0x00) +#define pclmulqdq11(x, y) _mm_clmulepi64_si128(x, y, 0x11) +#endif + +/* + * From a 128-bit value kw, compute kx as the XOR of the two 64-bit + * halves of kw (into the right half of kx; left half is unspecified). + */ +#define BK(kw, kx) do { \ + kx = _mm_xor_si128(kw, _mm_shuffle_epi32(kw, 0x0E)); \ + } while (0) + +/* + * Combine two 64-bit values (k0:k1) into a 128-bit (kw) value and + * the XOR of the two values (kx). + */ +#define PBK(k0, k1, kw, kx) do { \ + kw = _mm_unpacklo_epi64(k1, k0); \ + kx = _mm_xor_si128(k0, k1); \ + } while (0) + +/* + * Left-shift by 1 bit a 256-bit value (in four 64-bit words). + */ +#define SL_256(x0, x1, x2, x3) do { \ + x0 = _mm_or_si128( \ + _mm_slli_epi64(x0, 1), \ + _mm_srli_epi64(x1, 63)); \ + x1 = _mm_or_si128( \ + _mm_slli_epi64(x1, 1), \ + _mm_srli_epi64(x2, 63)); \ + x2 = _mm_or_si128( \ + _mm_slli_epi64(x2, 1), \ + _mm_srli_epi64(x3, 63)); \ + x3 = _mm_slli_epi64(x3, 1); \ + } while (0) + +/* + * Perform reduction in GF(2^128). The 256-bit value is in x0..x3; + * result is written in x0..x1. + */ +#define REDUCE_F128(x0, x1, x2, x3) do { \ + x1 = _mm_xor_si128( \ + x1, \ + _mm_xor_si128( \ + _mm_xor_si128( \ + x3, \ + _mm_srli_epi64(x3, 1)), \ + _mm_xor_si128( \ + _mm_srli_epi64(x3, 2), \ + _mm_srli_epi64(x3, 7)))); \ + x2 = _mm_xor_si128( \ + _mm_xor_si128( \ + x2, \ + _mm_slli_epi64(x3, 63)), \ + _mm_xor_si128( \ + _mm_slli_epi64(x3, 62), \ + _mm_slli_epi64(x3, 57))); \ + x0 = _mm_xor_si128( \ + x0, \ + _mm_xor_si128( \ + _mm_xor_si128( \ + x2, \ + _mm_srli_epi64(x2, 1)), \ + _mm_xor_si128( \ + _mm_srli_epi64(x2, 2), \ + _mm_srli_epi64(x2, 7)))); \ + x1 = _mm_xor_si128( \ + _mm_xor_si128( \ + x1, \ + _mm_slli_epi64(x2, 63)), \ + _mm_xor_si128( \ + _mm_slli_epi64(x2, 62), \ + _mm_slli_epi64(x2, 57))); \ + } while (0) + +/* + * Square value kw into (dw,dx). + */ +#define SQUARE_F128(kw, dw, dx) do { \ + __m128i z0, z1, z2, z3; \ + z1 = pclmulqdq11(kw, kw); \ + z3 = pclmulqdq00(kw, kw); \ + z0 = _mm_shuffle_epi32(z1, 0x0E); \ + z2 = _mm_shuffle_epi32(z3, 0x0E); \ + SL_256(z0, z1, z2, z3); \ + REDUCE_F128(z0, z1, z2, z3); \ + PBK(z0, z1, dw, dx); \ + } while (0) + +/* see bearssl_hash.h */ +BR_TARGET("ssse3,pclmul") +void +br_ghash_pclmul(void *y, const void *h, const void *data, size_t len) +{ + const unsigned char *buf1, *buf2; + unsigned char tmp[64]; + size_t num4, num1; + __m128i yw, h1w, h1x; + BYTESWAP_DECL + + /* + * We split data into two chunks. First chunk starts at buf1 + * and contains num4 blocks of 64-byte values. Second chunk + * starts at buf2 and contains num1 blocks of 16-byte values. + * We want the first chunk to be as large as possible. + */ + buf1 = data; + num4 = len >> 6; + len &= 63; + buf2 = buf1 + (num4 << 6); + num1 = (len + 15) >> 4; + if ((len & 15) != 0) { + memcpy(tmp, buf2, len); + memset(tmp + len, 0, (num1 << 4) - len); + buf2 = tmp; + } + + /* + * Preparatory step for endian conversions. + */ + BYTESWAP_PREP; + + /* + * Load y and h. + */ + yw = _mm_loadu_si128(y); + h1w = _mm_loadu_si128(h); + BYTESWAP(yw); + BYTESWAP(h1w); + BK(h1w, h1x); + + if (num4 > 0) { + __m128i h2w, h2x, h3w, h3x, h4w, h4x; + __m128i t0, t1, t2, t3; + + /* + * Compute h2 = h^2. + */ + SQUARE_F128(h1w, h2w, h2x); + + /* + * Compute h3 = h^3 = h*(h^2). + */ + t1 = pclmulqdq11(h1w, h2w); + t3 = pclmulqdq00(h1w, h2w); + t2 = _mm_xor_si128(pclmulqdq00(h1x, h2x), + _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + PBK(t0, t1, h3w, h3x); + + /* + * Compute h4 = h^4 = (h^2)^2. + */ + SQUARE_F128(h2w, h4w, h4x); + + while (num4 -- > 0) { + __m128i aw0, aw1, aw2, aw3; + __m128i ax0, ax1, ax2, ax3; + + aw0 = _mm_loadu_si128((void *)(buf1 + 0)); + aw1 = _mm_loadu_si128((void *)(buf1 + 16)); + aw2 = _mm_loadu_si128((void *)(buf1 + 32)); + aw3 = _mm_loadu_si128((void *)(buf1 + 48)); + BYTESWAP(aw0); + BYTESWAP(aw1); + BYTESWAP(aw2); + BYTESWAP(aw3); + buf1 += 64; + + aw0 = _mm_xor_si128(aw0, yw); + BK(aw1, ax1); + BK(aw2, ax2); + BK(aw3, ax3); + BK(aw0, ax0); + + t1 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq11(aw0, h4w), + pclmulqdq11(aw1, h3w)), + _mm_xor_si128( + pclmulqdq11(aw2, h2w), + pclmulqdq11(aw3, h1w))); + t3 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq00(aw0, h4w), + pclmulqdq00(aw1, h3w)), + _mm_xor_si128( + pclmulqdq00(aw2, h2w), + pclmulqdq00(aw3, h1w))); + t2 = _mm_xor_si128( + _mm_xor_si128( + pclmulqdq00(ax0, h4x), + pclmulqdq00(ax1, h3x)), + _mm_xor_si128( + pclmulqdq00(ax2, h2x), + pclmulqdq00(ax3, h1x))); + t2 = _mm_xor_si128(t2, _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + yw = _mm_unpacklo_epi64(t1, t0); + } + } + + while (num1 -- > 0) { + __m128i aw, ax; + __m128i t0, t1, t2, t3; + + aw = _mm_loadu_si128((void *)buf2); + BYTESWAP(aw); + buf2 += 16; + + aw = _mm_xor_si128(aw, yw); + BK(aw, ax); + + t1 = pclmulqdq11(aw, h1w); + t3 = pclmulqdq00(aw, h1w); + t2 = pclmulqdq00(ax, h1x); + t2 = _mm_xor_si128(t2, _mm_xor_si128(t1, t3)); + t0 = _mm_shuffle_epi32(t1, 0x0E); + t1 = _mm_xor_si128(t1, _mm_shuffle_epi32(t2, 0x0E)); + t2 = _mm_xor_si128(t2, _mm_shuffle_epi32(t3, 0x0E)); + SL_256(t0, t1, t2, t3); + REDUCE_F128(t0, t1, t2, t3); + yw = _mm_unpacklo_epi64(t1, t0); + } + + BYTESWAP(yw); + _mm_storeu_si128(y, yw); +} + +BR_TARGETS_X86_DOWN + +#else + +/* see bearssl_hash.h */ +br_ghash +br_ghash_pclmul_get(void) +{ + return 0; +} + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/md5.c b/lib/lib_ssl/bearssl-esp8266/src/hash/md5.c new file mode 100644 index 0000000000..f38ee6ccbf --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/md5.c @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define F(B, C, D) ((((C) ^ (D)) & (B)) ^ (D)) +#define G(B, C, D) ((((C) ^ (B)) & (D)) ^ (C)) +#define H(B, C, D) ((B) ^ (C) ^ (D)) +#define I(B, C, D) ((C) ^ ((B) | ~(D))) + +#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +/* see inner.h */ +const uint32_t br_md5_IV[4] PROGMEM = { + 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476 +}; + +static const uint32_t K[64] PROGMEM = { + 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, + 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, + 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, + 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821, + + 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, + 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, + 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, + 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A, + + 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, + 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, + 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, + 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665, + + 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, + 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, + 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, + 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391 +}; + +static const unsigned char MP_flash[48] PROGMEM = { + 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, + 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, + 0, 7, 14, 5, 12, 3, 10, 1, 8, 15, 6, 13, 4, 11, 2, 9 +}; + +/* see inner.h */ +void +br_md5_round(const unsigned char *buf, uint32_t *val) +{ + uint32_t m[16]; + uint32_t a, b, c, d; + int i; + uint8_t MP[48]; + memcpy_P(MP, MP_flash, 48); + + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + /* obsolete + for (i = 0; i < 16; i ++) { + m[i] = br_dec32le(buf + (i << 2)); + } + */ + br_range_dec32le(m, 16, buf); + + for (i = 0; i < 16; i += 4) { + a = b + ROTL(a + F(b, c, d) + m[i + 0] + K[i + 0], 7); + d = a + ROTL(d + F(a, b, c) + m[i + 1] + K[i + 1], 12); + c = d + ROTL(c + F(d, a, b) + m[i + 2] + K[i + 2], 17); + b = c + ROTL(b + F(c, d, a) + m[i + 3] + K[i + 3], 22); + } + for (i = 16; i < 32; i += 4) { + a = b + ROTL(a + G(b, c, d) + m[MP[i - 16]] + K[i + 0], 5); + d = a + ROTL(d + G(a, b, c) + m[MP[i - 15]] + K[i + 1], 9); + c = d + ROTL(c + G(d, a, b) + m[MP[i - 14]] + K[i + 2], 14); + b = c + ROTL(b + G(c, d, a) + m[MP[i - 13]] + K[i + 3], 20); + } + for (i = 32; i < 48; i += 4) { + a = b + ROTL(a + H(b, c, d) + m[MP[i - 16]] + K[i + 0], 4); + d = a + ROTL(d + H(a, b, c) + m[MP[i - 15]] + K[i + 1], 11); + c = d + ROTL(c + H(d, a, b) + m[MP[i - 14]] + K[i + 2], 16); + b = c + ROTL(b + H(c, d, a) + m[MP[i - 13]] + K[i + 3], 23); + } + for (i = 48; i < 64; i += 4) { + a = b + ROTL(a + I(b, c, d) + m[MP[i - 16]] + K[i + 0], 6); + d = a + ROTL(d + I(a, b, c) + m[MP[i - 15]] + K[i + 1], 10); + c = d + ROTL(c + I(d, a, b) + m[MP[i - 14]] + K[i + 2], 15); + b = c + ROTL(b + I(c, d, a) + m[MP[i - 13]] + K[i + 3], 21); + } + + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; +} + +/* see bearssl.h */ +void +br_md5_init(br_md5_context *cc) +{ + cc->vtable = &br_md5_vtable; + memcpy(cc->val, br_md5_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_md5_update(br_md5_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_md5_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_md5_out(const br_md5_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val[4]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_md5_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64le(buf + 56, cc->count << 3); + br_md5_round(buf, val); + br_range_enc32le(dst, val, 4); +} + +/* see bearssl.h */ +uint64_t +br_md5_state(const br_md5_context *cc, void *dst) +{ + br_range_enc32le(dst, cc->val, 4); + return cc->count; +} + +/* see bearssl.h */ +void +br_md5_set_state(br_md5_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32le(cc->val, 4, stb); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_md5_vtable PROGMEM = { + sizeof(br_md5_context), + BR_HASHDESC_ID(br_md5_ID) + | BR_HASHDESC_OUT(16) + | BR_HASHDESC_STATE(16) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING, + (void (*)(const br_hash_class **))&br_md5_init, + (void (*)(const br_hash_class **, const void *, size_t))&br_md5_update, + (void (*)(const br_hash_class *const *, void *))&br_md5_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_md5_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_md5_set_state +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c new file mode 100644 index 0000000000..b9553d685c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/md5sha1.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_md5sha1_init(br_md5sha1_context *cc) +{ + cc->vtable = &br_md5sha1_vtable; + memcpy(cc->val_md5, br_md5_IV, sizeof cc->val_md5); + memcpy(cc->val_sha1, br_sha1_IV, sizeof cc->val_sha1); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_md5sha1_update(br_md5sha1_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_md5_round(cc->buf, cc->val_md5); + br_sha1_round(cc->buf, cc->val_sha1); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_md5sha1_out(const br_md5sha1_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val_md5[4]; + uint32_t val_sha1[5]; + size_t ptr; + unsigned char *out; + uint64_t count; + + count = cc->count; + ptr = (size_t)count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val_md5, cc->val_md5, sizeof val_md5); + memcpy(val_sha1, cc->val_sha1, sizeof val_sha1); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_md5_round(buf, val_md5); + br_sha1_round(buf, val_sha1); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + count <<= 3; + br_enc64le(buf + 56, count); + br_md5_round(buf, val_md5); + br_enc64be(buf + 56, count); + br_sha1_round(buf, val_sha1); + out = dst; + br_range_enc32le(out, val_md5, 4); + br_range_enc32be(out + 16, val_sha1, 5); +} + +/* see bearssl.h */ +uint64_t +br_md5sha1_state(const br_md5sha1_context *cc, void *dst) +{ + unsigned char *out; + + out = dst; + br_range_enc32le(out, cc->val_md5, 4); + br_range_enc32be(out + 16, cc->val_sha1, 5); + return cc->count; +} + +/* see bearssl.h */ +void +br_md5sha1_set_state(br_md5sha1_context *cc, const void *stb, uint64_t count) +{ + const unsigned char *buf; + + buf = stb; + br_range_dec32le(cc->val_md5, 4, buf); + br_range_dec32be(cc->val_sha1, 5, buf + 16); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_md5sha1_vtable PROGMEM = { + sizeof(br_md5sha1_context), + BR_HASHDESC_ID(br_md5sha1_ID) + | BR_HASHDESC_OUT(36) + | BR_HASHDESC_STATE(36) + | BR_HASHDESC_LBLEN(6), + (void (*)(const br_hash_class **))&br_md5sha1_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_md5sha1_update, + (void (*)(const br_hash_class *const *, void *)) + &br_md5sha1_out, + (uint64_t (*)(const br_hash_class *const *, void *)) + &br_md5sha1_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_md5sha1_set_state +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c new file mode 100644 index 0000000000..63b42ff440 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/mgf1.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_mgf1_xor(void *data, size_t len, + const br_hash_class *dig, const void *seed, size_t seed_len) +{ + unsigned char *buf; + size_t u, hlen; + uint32_t c; + + buf = data; + hlen = br_digest_size(dig); + for (u = 0, c = 0; u < len; u += hlen, c ++) { + br_hash_compat_context hc; + unsigned char tmp[64]; + size_t v; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, seed, seed_len); + br_enc32be(tmp, c); + dig->update(&hc.vtable, tmp, 4); + dig->out(&hc.vtable, tmp); + for (v = 0; v < hlen; v ++) { + if ((u + v) >= len) { + break; + } + buf[u + v] ^= tmp[v]; + } + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c b/lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c new file mode 100644 index 0000000000..a039e2e71c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/multihash.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * An aggregate context that is large enough for all supported hash + * functions. + */ +typedef union { + const br_hash_class *vtable; + br_md5_context md5; + br_sha1_context sha1; + br_sha224_context sha224; + br_sha256_context sha256; + br_sha384_context sha384; + br_sha512_context sha512; +} gen_hash_context; + +/* + * Get the offset to the state for a specific hash function within the + * context structure. This shall be called only for the supported hash + * functions, + */ +static size_t +get_state_offset(int id) +{ + if (id >= 5) { + /* + * SHA-384 has id 5, and SHA-512 has id 6. Both use + * eight 64-bit words for their state. + */ + return offsetof(br_multihash_context, val_64) + + ((size_t)(id - 5) * (8 * sizeof(uint64_t))); + } else { + /* + * MD5 has id 1, SHA-1 has id 2, SHA-224 has id 3 and + * SHA-256 has id 4. They use 32-bit words for their + * states (4 words for MD5, 5 for SHA-1, 8 for SHA-224 + * and 8 for SHA-256). + */ + unsigned x; + + x = id - 1; + x = ((x + (x & (x >> 1))) << 2) + (x >> 1); + return offsetof(br_multihash_context, val_32) + + x * sizeof(uint32_t); + } +} + +/* see bearssl_hash.h */ +void +br_multihash_zero(br_multihash_context *ctx) +{ + /* + * This is not standard, but yields very short and efficient code, + * and it works "everywhere". + */ + memset(ctx, 0, sizeof *ctx); +} + +/* see bearssl_hash.h */ +void +br_multihash_init(br_multihash_context *ctx) +{ + int i; + + ctx->count = 0; + for (i = 1; i <= 6; i ++) { + const br_hash_class *hc; + + hc = ctx->impl[i - 1]; + if (hc != NULL) { + gen_hash_context g; + + hc->init(&g.vtable); + hc->state(&g.vtable, + (unsigned char *)ctx + get_state_offset(i)); + } + } +} + +/* see bearssl_hash.h */ +void +br_multihash_update(br_multihash_context *ctx, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)ctx->count & 127; + while (len > 0) { + size_t clen; + + clen = 128 - ptr; + if (clen > len) { + clen = len; + } + memcpy(ctx->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + ctx->count += (uint64_t)clen; + if (ptr == 128) { + int i; + + for (i = 1; i <= 6; i ++) { + const br_hash_class *hc; + + hc = ctx->impl[i - 1]; + if (hc != NULL) { + gen_hash_context g; + unsigned char *state; + + state = (unsigned char *)ctx + + get_state_offset(i); + hc->set_state(&g.vtable, + state, ctx->count - 128); + hc->update(&g.vtable, ctx->buf, 128); + hc->state(&g.vtable, state); + } + } + ptr = 0; + } + } +} + +/* see bearssl_hash.h */ +size_t +br_multihash_out(const br_multihash_context *ctx, int id, void *dst) +{ + const br_hash_class *hc; + gen_hash_context g; + const unsigned char *state; + + hc = ctx->impl[id - 1]; + if (hc == NULL) { + return 0; + } + state = (const unsigned char *)ctx + get_state_offset(id); + hc->set_state(&g.vtable, state, ctx->count & ~(uint64_t)127); + hc->update(&g.vtable, ctx->buf, ctx->count & (uint64_t)127); + hc->out(&g.vtable, dst); + return (hc->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c new file mode 100644 index 0000000000..9f5cf81b5d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/sha1.c @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define F(B, C, D) ((((C) ^ (D)) & (B)) ^ (D)) +#define G(B, C, D) ((B) ^ (C) ^ (D)) +#define H(B, C, D) (((D) & (C)) | (((D) | (C)) & (B))) +#define I(B, C, D) G(B, C, D) + +#define ROTL(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) + +#define K1 ((uint32_t)0x5A827999) +#define K2 ((uint32_t)0x6ED9EBA1) +#define K3 ((uint32_t)0x8F1BBCDC) +#define K4 ((uint32_t)0xCA62C1D6) + +/* see inner.h */ +const uint32_t br_sha1_IV[5] PROGMEM = { + 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 +}; + +/* see inner.h */ +void +br_sha1_round(const unsigned char *buf, uint32_t *val) +{ + uint32_t m[80]; + uint32_t a, b, c, d, e; + int i; + + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + br_range_dec32be(m, 16, buf); + for (i = 16; i < 80; i ++) { + uint32_t x = m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]; + m[i] = ROTL(x, 1); + } + + for (i = 0; i < 20; i += 5) { + e += ROTL(a, 5) + F(b, c, d) + K1 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + F(a, b, c) + K1 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + F(e, a, b) + K1 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + F(d, e, a) + K1 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + F(c, d, e) + K1 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 20; i < 40; i += 5) { + e += ROTL(a, 5) + G(b, c, d) + K2 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + G(a, b, c) + K2 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + G(e, a, b) + K2 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + G(d, e, a) + K2 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + G(c, d, e) + K2 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 40; i < 60; i += 5) { + e += ROTL(a, 5) + H(b, c, d) + K3 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + H(a, b, c) + K3 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + H(e, a, b) + K3 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + H(d, e, a) + K3 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + H(c, d, e) + K3 + m[i + 4]; c = ROTL(c, 30); + } + for (i = 60; i < 80; i += 5) { + e += ROTL(a, 5) + I(b, c, d) + K4 + m[i + 0]; b = ROTL(b, 30); + d += ROTL(e, 5) + I(a, b, c) + K4 + m[i + 1]; a = ROTL(a, 30); + c += ROTL(d, 5) + I(e, a, b) + K4 + m[i + 2]; e = ROTL(e, 30); + b += ROTL(c, 5) + I(d, e, a) + K4 + m[i + 3]; d = ROTL(d, 30); + a += ROTL(b, 5) + I(c, d, e) + K4 + m[i + 4]; c = ROTL(c, 30); + } + + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; +} + +/* see bearssl.h */ +void +br_sha1_init(br_sha1_context *cc) +{ + cc->vtable = &br_sha1_vtable; + memcpy(cc->val, br_sha1_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha1_update(br_sha1_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + cc->count += (uint64_t)clen; + if (ptr == 64) { + br_sha1_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +/* see bearssl.h */ +void +br_sha1_out(const br_sha1_context *cc, void *dst) +{ + unsigned char buf[64]; + uint32_t val[5]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_sha1_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64be(buf + 56, cc->count << 3); + br_sha1_round(buf, val); + br_range_enc32be(dst, val, 5); +} + +/* see bearssl.h */ +uint64_t +br_sha1_state(const br_sha1_context *cc, void *dst) +{ + br_range_enc32be(dst, cc->val, 5); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha1_set_state(br_sha1_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32be(cc->val, 5, stb); + cc->count = count; +} + +/* see bearssl.h */ +const br_hash_class br_sha1_vtable PROGMEM = { + sizeof(br_sha1_context), + BR_HASHDESC_ID(br_sha1_ID) + | BR_HASHDESC_OUT(20) + | BR_HASHDESC_STATE(20) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha1_init, + (void (*)(const br_hash_class **, const void *, size_t))&br_sha1_update, + (void (*)(const br_hash_class *const *, void *))&br_sha1_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha1_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha1_set_state +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c new file mode 100644 index 0000000000..2a8d99b958 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2big.c @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR(x, n) (((uint64_t)(x) << (64 - (n))) | ((uint64_t)(x) >> (n))) + +#define BSG5_0(x) (ROTR(x, 28) ^ ROTR(x, 34) ^ ROTR(x, 39)) +#define BSG5_1(x) (ROTR(x, 14) ^ ROTR(x, 18) ^ ROTR(x, 41)) +#define SSG5_0(x) (ROTR(x, 1) ^ ROTR(x, 8) ^ (uint64_t)((x) >> 7)) +#define SSG5_1(x) (ROTR(x, 19) ^ ROTR(x, 61) ^ (uint64_t)((x) >> 6)) + +static const uint64_t IV384[8] PROGMEM = { + 0xCBBB9D5DC1059ED8, 0x629A292A367CD507, + 0x9159015A3070DD17, 0x152FECD8F70E5939, + 0x67332667FFC00B31, 0x8EB44A8768581511, + 0xDB0C2E0D64F98FA7, 0x47B5481DBEFA4FA4 +}; + +static const uint64_t IV512[8] PROGMEM = { + 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, + 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, + 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, + 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179 +}; + +static const uint64_t K[80] PROGMEM = { + 0x428A2F98D728AE22, 0x7137449123EF65CD, + 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, + 0x3956C25BF348B538, 0x59F111F1B605D019, + 0x923F82A4AF194F9B, 0xAB1C5ED5DA6D8118, + 0xD807AA98A3030242, 0x12835B0145706FBE, + 0x243185BE4EE4B28C, 0x550C7DC3D5FFB4E2, + 0x72BE5D74F27B896F, 0x80DEB1FE3B1696B1, + 0x9BDC06A725C71235, 0xC19BF174CF692694, + 0xE49B69C19EF14AD2, 0xEFBE4786384F25E3, + 0x0FC19DC68B8CD5B5, 0x240CA1CC77AC9C65, + 0x2DE92C6F592B0275, 0x4A7484AA6EA6E483, + 0x5CB0A9DCBD41FBD4, 0x76F988DA831153B5, + 0x983E5152EE66DFAB, 0xA831C66D2DB43210, + 0xB00327C898FB213F, 0xBF597FC7BEEF0EE4, + 0xC6E00BF33DA88FC2, 0xD5A79147930AA725, + 0x06CA6351E003826F, 0x142929670A0E6E70, + 0x27B70A8546D22FFC, 0x2E1B21385C26C926, + 0x4D2C6DFC5AC42AED, 0x53380D139D95B3DF, + 0x650A73548BAF63DE, 0x766A0ABB3C77B2A8, + 0x81C2C92E47EDAEE6, 0x92722C851482353B, + 0xA2BFE8A14CF10364, 0xA81A664BBC423001, + 0xC24B8B70D0F89791, 0xC76C51A30654BE30, + 0xD192E819D6EF5218, 0xD69906245565A910, + 0xF40E35855771202A, 0x106AA07032BBD1B8, + 0x19A4C116B8D2D0C8, 0x1E376C085141AB53, + 0x2748774CDF8EEB99, 0x34B0BCB5E19B48A8, + 0x391C0CB3C5C95A63, 0x4ED8AA4AE3418ACB, + 0x5B9CCA4F7763E373, 0x682E6FF3D6B2B8A3, + 0x748F82EE5DEFB2FC, 0x78A5636F43172F60, + 0x84C87814A1F0AB72, 0x8CC702081A6439EC, + 0x90BEFFFA23631E28, 0xA4506CEBDE82BDE9, + 0xBEF9A3F7B2C67915, 0xC67178F2E372532B, + 0xCA273ECEEA26619C, 0xD186B8C721C0C207, + 0xEADA7DD6CDE0EB1E, 0xF57D4F7FEE6ED178, + 0x06F067AA72176FBA, 0x0A637DC5A2C898A6, + 0x113F9804BEF90DAE, 0x1B710B35131C471B, + 0x28DB77F523047D84, 0x32CAAB7B40C72493, + 0x3C9EBE0A15C9BEBC, 0x431D67C49C100D4C, + 0x4CC5D4BECB3E42B6, 0x597F299CFC657E2A, + 0x5FCB6FAB3AD6FAEC, 0x6C44198C4A475817 +}; + +static void +sha2big_round(const unsigned char *buf, uint64_t *val) +{ + +#define SHA2BIG_STEP(A, B, C, D, E, F, G, H, j) do { \ + uint64_t T1, T2; \ + T1 = H + BSG5_1(E) + CH(E, F, G) + K[j] + w[j]; \ + T2 = BSG5_0(A) + MAJ(A, B, C); \ + D += T1; \ + H = T1 + T2; \ + } while (0) + + int i; + uint64_t a, b, c, d, e, f, g, h; + uint64_t w[80]; + + br_range_dec64be(w, 16, buf); + for (i = 16; i < 80; i ++) { + w[i] = SSG5_1(w[i - 2]) + w[i - 7] + + SSG5_0(w[i - 15]) + w[i - 16]; + } + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + f = val[5]; + g = val[6]; + h = val[7]; + for (i = 0; i < 80; i += 8) { + SHA2BIG_STEP(a, b, c, d, e, f, g, h, i + 0); + SHA2BIG_STEP(h, a, b, c, d, e, f, g, i + 1); + SHA2BIG_STEP(g, h, a, b, c, d, e, f, i + 2); + SHA2BIG_STEP(f, g, h, a, b, c, d, e, i + 3); + SHA2BIG_STEP(e, f, g, h, a, b, c, d, i + 4); + SHA2BIG_STEP(d, e, f, g, h, a, b, c, i + 5); + SHA2BIG_STEP(c, d, e, f, g, h, a, b, i + 6); + SHA2BIG_STEP(b, c, d, e, f, g, h, a, i + 7); + } + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; + val[5] += f; + val[6] += g; + val[7] += h; +} + +static void +sha2big_update(br_sha384_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 127; + cc->count += (uint64_t)len; + while (len > 0) { + size_t clen; + + clen = 128 - ptr; + if (clen > len) { + clen = len; + } + memcpy(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + if (ptr == 128) { + sha2big_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +static void +sha2big_out(const br_sha384_context *cc, void *dst, int num) +{ + unsigned char buf[128]; + uint64_t val[8]; + size_t ptr; + + ptr = (size_t)cc->count & 127; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 112) { + memset(buf + ptr, 0, 128 - ptr); + sha2big_round(buf, val); + memset(buf, 0, 112); + } else { + memset(buf + ptr, 0, 112 - ptr); + } + br_enc64be(buf + 112, cc->count >> 61); + br_enc64be(buf + 120, cc->count << 3); + sha2big_round(buf, val); + br_range_enc64be(dst, val, num); +} + +/* see bearssl.h */ +void +br_sha384_init(br_sha384_context *cc) +{ + cc->vtable = &br_sha384_vtable; + memcpy(cc->val, IV384, sizeof IV384); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha384_update(br_sha384_context *cc, const void *data, size_t len) +{ + sha2big_update(cc, data, len); +} + +/* see bearssl.h */ +void +br_sha384_out(const br_sha384_context *cc, void *dst) +{ + sha2big_out(cc, dst, 6); +} + +/* see bearssl.h */ +uint64_t +br_sha384_state(const br_sha384_context *cc, void *dst) +{ + br_range_enc64be(dst, cc->val, 8); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha384_set_state(br_sha384_context *cc, const void *stb, uint64_t count) +{ + br_range_dec64be(cc->val, 8, stb); + cc->count = count; +} + +/* see bearssl.h */ +void +br_sha512_init(br_sha512_context *cc) +{ + cc->vtable = &br_sha512_vtable; + memcpy(cc->val, IV512, sizeof IV512); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha512_out(const br_sha512_context *cc, void *dst) +{ + sha2big_out(cc, dst, 8); +} + +/* see bearssl.h */ +const br_hash_class br_sha384_vtable PROGMEM = { + sizeof(br_sha384_context), + BR_HASHDESC_ID(br_sha384_ID) + | BR_HASHDESC_OUT(48) + | BR_HASHDESC_STATE(64) + | BR_HASHDESC_LBLEN(7) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE + | BR_HASHDESC_MD_PADDING_128, + (void (*)(const br_hash_class **))&br_sha384_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_sha384_update, + (void (*)(const br_hash_class *const *, void *))&br_sha384_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha384_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha384_set_state +}; + +/* see bearssl.h */ +const br_hash_class br_sha512_vtable PROGMEM = { + sizeof(br_sha512_context), + BR_HASHDESC_ID(br_sha512_ID) + | BR_HASHDESC_OUT(64) + | BR_HASHDESC_STATE(64) + | BR_HASHDESC_LBLEN(7) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE + | BR_HASHDESC_MD_PADDING_128, + (void (*)(const br_hash_class **))&br_sha512_init, + (void (*)(const br_hash_class **, const void *, size_t)) + &br_sha512_update, + (void (*)(const br_hash_class *const *, void *))&br_sha512_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha512_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha512_set_state +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c new file mode 100644 index 0000000000..b4b12b13d3 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/hash/sha2small.c @@ -0,0 +1,341 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define CH(X, Y, Z) ((((Y) ^ (Z)) & (X)) ^ (Z)) +#define MAJ(X, Y, Z) (((Y) & (Z)) | (((Y) | (Z)) & (X))) + +#define ROTR(x, n) (((uint32_t)(x) << (32 - (n))) | ((uint32_t)(x) >> (n))) + +#define BSG2_0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define BSG2_1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SSG2_0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ (uint32_t)((x) >> 3)) +#define SSG2_1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ (uint32_t)((x) >> 10)) + +/* see inner.h */ +const uint32_t br_sha224_IV[8] PROGMEM = { + 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, + 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4 +}; + +/* see inner.h */ +const uint32_t br_sha256_IV[8] PROGMEM = { + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 +}; + +static const uint32_t K[64] PROGMEM = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 +}; + +/* see inner.h */ +void +br_sha2small_round(const unsigned char *buf, uint32_t *val) +{ + +#define SHA2_STEP(A, B, C, D, E, F, G, H, j) do { \ + uint32_t T1, T2; \ + T1 = H + BSG2_1(E) + CH(E, F, G) + K[j] + w[j]; \ + T2 = BSG2_0(A) + MAJ(A, B, C); \ + D += T1; \ + H = T1 + T2; \ + } while (0) + + int i; + uint32_t a, b, c, d, e, f, g, h; + uint32_t w[64]; + + br_range_dec32be(w, 16, buf); + for (i = 16; i < 64; i ++) { + w[i] = SSG2_1(w[i - 2]) + w[i - 7] + + SSG2_0(w[i - 15]) + w[i - 16]; + } + a = val[0]; + b = val[1]; + c = val[2]; + d = val[3]; + e = val[4]; + f = val[5]; + g = val[6]; + h = val[7]; + for (i = 0; i < 64; i += 8) { + SHA2_STEP(a, b, c, d, e, f, g, h, i + 0); + SHA2_STEP(h, a, b, c, d, e, f, g, i + 1); + SHA2_STEP(g, h, a, b, c, d, e, f, i + 2); + SHA2_STEP(f, g, h, a, b, c, d, e, i + 3); + SHA2_STEP(e, f, g, h, a, b, c, d, i + 4); + SHA2_STEP(d, e, f, g, h, a, b, c, i + 5); + SHA2_STEP(c, d, e, f, g, h, a, b, i + 6); + SHA2_STEP(b, c, d, e, f, g, h, a, i + 7); + } + val[0] += a; + val[1] += b; + val[2] += c; + val[3] += d; + val[4] += e; + val[5] += f; + val[6] += g; + val[7] += h; + +#if 0 +/* obsolete */ +#define SHA2_MEXP1(pc) do { \ + W[pc] = br_dec32be(buf + ((pc) << 2)); \ + } while (0) + +#define SHA2_MEXP2(pc) do { \ + W[(pc) & 0x0F] = SSG2_1(W[((pc) - 2) & 0x0F]) \ + + W[((pc) - 7) & 0x0F] \ + + SSG2_0(W[((pc) - 15) & 0x0F]) + W[(pc) & 0x0F]; \ + } while (0) + +#define SHA2_STEPn(n, a, b, c, d, e, f, g, h, pc) do { \ + uint32_t t1, t2; \ + SHA2_MEXP ## n(pc); \ + t1 = h + BSG2_1(e) + CH(e, f, g) \ + + K[pcount + (pc)] + W[(pc) & 0x0F]; \ + t2 = BSG2_0(a) + MAJ(a, b, c); \ + d += t1; \ + h = t1 + t2; \ + } while (0) + +#define SHA2_STEP1(a, b, c, d, e, f, g, h, pc) \ + SHA2_STEPn(1, a, b, c, d, e, f, g, h, pc) +#define SHA2_STEP2(a, b, c, d, e, f, g, h, pc) \ + SHA2_STEPn(2, a, b, c, d, e, f, g, h, pc) + + uint32_t A, B, C, D, E, F, G, H; + uint32_t W[16]; + unsigned pcount; + + A = val[0]; + B = val[1]; + C = val[2]; + D = val[3]; + E = val[4]; + F = val[5]; + G = val[6]; + H = val[7]; + pcount = 0; + SHA2_STEP1(A, B, C, D, E, F, G, H, 0); + SHA2_STEP1(H, A, B, C, D, E, F, G, 1); + SHA2_STEP1(G, H, A, B, C, D, E, F, 2); + SHA2_STEP1(F, G, H, A, B, C, D, E, 3); + SHA2_STEP1(E, F, G, H, A, B, C, D, 4); + SHA2_STEP1(D, E, F, G, H, A, B, C, 5); + SHA2_STEP1(C, D, E, F, G, H, A, B, 6); + SHA2_STEP1(B, C, D, E, F, G, H, A, 7); + SHA2_STEP1(A, B, C, D, E, F, G, H, 8); + SHA2_STEP1(H, A, B, C, D, E, F, G, 9); + SHA2_STEP1(G, H, A, B, C, D, E, F, 10); + SHA2_STEP1(F, G, H, A, B, C, D, E, 11); + SHA2_STEP1(E, F, G, H, A, B, C, D, 12); + SHA2_STEP1(D, E, F, G, H, A, B, C, 13); + SHA2_STEP1(C, D, E, F, G, H, A, B, 14); + SHA2_STEP1(B, C, D, E, F, G, H, A, 15); + for (pcount = 16; pcount < 64; pcount += 16) { + SHA2_STEP2(A, B, C, D, E, F, G, H, 0); + SHA2_STEP2(H, A, B, C, D, E, F, G, 1); + SHA2_STEP2(G, H, A, B, C, D, E, F, 2); + SHA2_STEP2(F, G, H, A, B, C, D, E, 3); + SHA2_STEP2(E, F, G, H, A, B, C, D, 4); + SHA2_STEP2(D, E, F, G, H, A, B, C, 5); + SHA2_STEP2(C, D, E, F, G, H, A, B, 6); + SHA2_STEP2(B, C, D, E, F, G, H, A, 7); + SHA2_STEP2(A, B, C, D, E, F, G, H, 8); + SHA2_STEP2(H, A, B, C, D, E, F, G, 9); + SHA2_STEP2(G, H, A, B, C, D, E, F, 10); + SHA2_STEP2(F, G, H, A, B, C, D, E, 11); + SHA2_STEP2(E, F, G, H, A, B, C, D, 12); + SHA2_STEP2(D, E, F, G, H, A, B, C, 13); + SHA2_STEP2(C, D, E, F, G, H, A, B, 14); + SHA2_STEP2(B, C, D, E, F, G, H, A, 15); + } + val[0] += A; + val[1] += B; + val[2] += C; + val[3] += D; + val[4] += E; + val[5] += F; + val[6] += G; + val[7] += H; +#endif +} + +static void +sha2small_update(br_sha224_context *cc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t ptr; + + buf = data; + ptr = (size_t)cc->count & 63; + cc->count += (uint64_t)len; + while (len > 0) { + size_t clen; + + clen = 64 - ptr; + if (clen > len) { + clen = len; + } + memcpy_P(cc->buf + ptr, buf, clen); + ptr += clen; + buf += clen; + len -= clen; + if (ptr == 64) { + br_sha2small_round(cc->buf, cc->val); + ptr = 0; + } + } +} + +static void +sha2small_out(const br_sha224_context *cc, void *dst, int num) +{ + unsigned char buf[64]; + uint32_t val[8]; + size_t ptr; + + ptr = (size_t)cc->count & 63; + memcpy(buf, cc->buf, ptr); + memcpy(val, cc->val, sizeof val); + buf[ptr ++] = 0x80; + if (ptr > 56) { + memset(buf + ptr, 0, 64 - ptr); + br_sha2small_round(buf, val); + memset(buf, 0, 56); + } else { + memset(buf + ptr, 0, 56 - ptr); + } + br_enc64be(buf + 56, cc->count << 3); + br_sha2small_round(buf, val); + br_range_enc32be(dst, val, num); +} + +/* see bearssl.h */ +void +br_sha224_init(br_sha224_context *cc) +{ + cc->vtable = &br_sha224_vtable; + memcpy(cc->val, br_sha224_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha224_update(br_sha224_context *cc, const void *data, size_t len) +{ + sha2small_update(cc, data, len); +} + +/* see bearssl.h */ +void +br_sha224_out(const br_sha224_context *cc, void *dst) +{ + sha2small_out(cc, dst, 7); +} + +/* see bearssl.h */ +uint64_t +br_sha224_state(const br_sha224_context *cc, void *dst) +{ + br_range_enc32be(dst, cc->val, 8); + return cc->count; +} + +/* see bearssl.h */ +void +br_sha224_set_state(br_sha224_context *cc, const void *stb, uint64_t count) +{ + br_range_dec32be(cc->val, 8, stb); + cc->count = count; +} + +/* see bearssl.h */ +void +br_sha256_init(br_sha256_context *cc) +{ + cc->vtable = &br_sha256_vtable; + memcpy(cc->val, br_sha256_IV, sizeof cc->val); + cc->count = 0; +} + +/* see bearssl.h */ +void +br_sha256_out(const br_sha256_context *cc, void *dst) +{ + sha2small_out(cc, dst, 8); +} + +/* see bearssl.h */ +const br_hash_class br_sha224_vtable PROGMEM = { + sizeof(br_sha224_context), + BR_HASHDESC_ID(br_sha224_ID) + | BR_HASHDESC_OUT(28) + | BR_HASHDESC_STATE(32) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha224_init, + (void (*)(const br_hash_class **, + const void *, size_t))&br_sha224_update, + (void (*)(const br_hash_class *const *, void *))&br_sha224_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha224_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha224_set_state +}; + +/* see bearssl.h */ +const br_hash_class br_sha256_vtable PROGMEM = { + sizeof(br_sha256_context), + BR_HASHDESC_ID(br_sha256_ID) + | BR_HASHDESC_OUT(32) + | BR_HASHDESC_STATE(32) + | BR_HASHDESC_LBLEN(6) + | BR_HASHDESC_MD_PADDING + | BR_HASHDESC_MD_PADDING_BE, + (void (*)(const br_hash_class **))&br_sha256_init, + (void (*)(const br_hash_class **, + const void *, size_t))&br_sha256_update, + (void (*)(const br_hash_class *const *, void *))&br_sha256_out, + (uint64_t (*)(const br_hash_class *const *, void *))&br_sha256_state, + (void (*)(const br_hash_class **, const void *, uint64_t)) + &br_sha256_set_state +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c new file mode 100644 index 0000000000..f82e142594 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_add.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_add(uint16_t *a, const uint16_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 31) >> 4; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = pgm_read_word(&b[u]); + naw = aw + bw + cc; + cc = naw >> 15; + a[u] = MUX(ctl, naw & 0x7FFF, aw); + } + return cc; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c new file mode 100644 index 0000000000..c903a7199a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_bitlen.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_bit_length(uint16_t *x, size_t xlen) +{ + uint32_t tw, twk; + + tw = 0; + twk = 0; + while (xlen -- > 0) { + uint32_t w, c; + + c = EQ(tw, 0); + w = x[xlen]; + tw = MUX(c, w, tw); + twk = MUX(c, (uint32_t)xlen, twk); + } + return (twk << 4) + BIT_LENGTH(tw); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c new file mode 100644 index 0000000000..1da5dbe97e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decmod.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_decode_mod(uint16_t *x, const void *src, size_t len, const uint16_t *m) +{ + /* + * Two-pass algorithm: in the first pass, we determine whether the + * value fits; in the second pass, we do the actual write. + * + * During the first pass, 'r' contains the comparison result so + * far: + * 0x00000000 value is equal to the modulus + * 0x00000001 value is greater than the modulus + * 0xFFFFFFFF value is lower than the modulus + * + * Since we iterate starting with the least significant bytes (at + * the end of src[]), each new comparison overrides the previous + * except when the comparison yields 0 (equal). + * + * During the second pass, 'r' is either 0xFFFFFFFF (value fits) + * or 0x00000000 (value does not fit). + * + * We must iterate over all bytes of the source, _and_ possibly + * some extra virtual bytes (with value 0) so as to cover the + * complete modulus as well. We also add 4 such extra bytes beyond + * the modulus length because it then guarantees that no accumulated + * partial word remains to be processed. + */ + const unsigned char *buf; + size_t mlen, tlen; + int pass; + uint32_t r; + + buf = src; + mlen = (pgm_read_word(&m[0]) + 15) >> 4; + tlen = (mlen << 1); + if (tlen < len) { + tlen = len; + } + tlen += 4; + r = 0; + for (pass = 0; pass < 2; pass ++) { + size_t u, v; + uint32_t acc; + int acc_len; + + v = 1; + acc = 0; + acc_len = 0; + for (u = 0; u < tlen; u ++) { + uint32_t b; + + if (u < len) { + b = pgm_read_byte(&buf[len - 1 - u]); + } else { + b = 0; + } + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 15) { + uint32_t xw; + + xw = acc & (uint32_t)0x7FFF; + acc_len -= 15; + acc = b >> (8 - acc_len); + if (v <= mlen) { + if (pass) { + x[v] = r & xw; + } else { + uint32_t cc; + + cc = (uint32_t)CMP(xw, pgm_read_word(&m[v])); + r = MUX(EQ(cc, 0), r, cc); + } + } else { + if (!pass) { + r = MUX(EQ(xw, 0), r, 1); + } + } + v ++; + } + } + + /* + * When we reach this point at the end of the first pass: + * r is either 0, 1 or -1; we want to set r to 0 if it + * is equal to 0 or 1, and leave it to -1 otherwise. + * + * When we reach this point at the end of the second pass: + * r is either 0 or -1; we want to leave that value + * untouched. This is a subcase of the previous. + */ + r >>= 1; + r |= (r << 1); + } + + x[0] = pgm_read_word(&m[0]); + return r & (uint32_t)1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c new file mode 100644 index 0000000000..f277a3485d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decode.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_decode(uint16_t *x, const void *src, size_t len) +{ + const unsigned char *buf; + size_t v; + uint32_t acc; + int acc_len; + + buf = src; + v = 1; + acc = 0; + acc_len = 0; + while (len -- > 0) { + uint32_t b; + + b = pgm_read_byte(&buf[len]); + acc |= (b << acc_len); + acc_len += 8; + if (acc_len >= 15) { + x[v ++] = acc & 0x7FFF; + acc_len -= 15; + acc >>= 15; + } + } + if (acc_len != 0) { + x[v ++] = acc; + } + x[0] = br_i15_bit_length(x + 1, v - 1); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c new file mode 100644 index 0000000000..c0953cd1e3 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_decred.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_decode_reduce(uint16_t *x, + const void *src, size_t len, const uint16_t *m) +{ + uint32_t m_ebitlen, m_rbitlen; + size_t mblen, k; + const unsigned char *buf; + uint32_t acc; + int acc_len; + + /* + * Get the encoded bit length. + */ + m_ebitlen = pgm_read_word(&m[0]); + + /* + * Special case for an invalid (null) modulus. + */ + if (m_ebitlen == 0) { + x[0] = 0; + return; + } + + /* + * Clear the destination. + */ + br_i15_zero(x, m_ebitlen); + + /* + * First decode directly as many bytes as possible. This requires + * computing the actual bit length. + */ + m_rbitlen = m_ebitlen >> 4; + m_rbitlen = (m_ebitlen & 15) + (m_rbitlen << 4) - m_rbitlen; + mblen = (m_rbitlen + 7) >> 3; + k = mblen - 1; + if (k >= len) { + br_i15_decode(x, src, len); + x[0] = m_ebitlen; + return; + } + buf = src; + br_i15_decode(x, buf, k); + x[0] = m_ebitlen; + + /* + * Input remaining bytes, using 15-bit words. + */ + acc = 0; + acc_len = 0; + while (k < len) { + uint32_t v; + + v = pgm_read_byte(&buf[k ++]); + acc = (acc << 8) | v; + acc_len += 8; + if (acc_len >= 15) { + br_i15_muladd_small(x, acc >> (acc_len - 15), m); + acc_len -= 15; + acc &= ~((uint32_t)-1 << acc_len); + } + } + + /* + * We may have some bits accumulated. We then perform a shift to + * be able to inject these bits as a full 15-bit word. + */ + if (acc_len != 0) { + acc = (acc | (x[1] << acc_len)) & 0x7FFF; + br_i15_rshift(x, 15 - acc_len); + br_i15_muladd_small(x, acc, m); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c new file mode 100644 index 0000000000..bc4cad4076 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_encode.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_encode(void *dst, size_t len, const uint16_t *x) +{ + unsigned char *buf; + size_t u, xlen; + uint32_t acc; + int acc_len; + + xlen = (pgm_read_word(&x[0]) + 15) >> 4; + if (xlen == 0) { + memset(dst, 0, len); + return; + } + u = 1; + acc = 0; + acc_len = 0; + buf = dst; + while (len -- > 0) { + if (acc_len < 8) { + if (u <= xlen) { + acc += (uint32_t)pgm_read_word(&x[u ++]) << acc_len; + } + acc_len += 15; + } + buf[len] = (unsigned char)acc; + acc >>= 8; + acc_len -= 8; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c new file mode 100644 index 0000000000..8bf73a59fa --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_fmont.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_from_monty(uint16_t *x, const uint16_t *m, uint16_t m0i) +{ + size_t len, u, v; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + for (u = 0; u < len; u ++) { + uint32_t f, cc; + + f = MUL15(x[1], m0i) & 0x7FFF; + cc = 0; + for (v = 0; v < len; v ++) { + uint32_t z; + + z = (uint32_t)x[v + 1] + MUL15(f, pgm_read_word(&m[v + 1])) + cc; + cc = z >> 15; + if (v != 0) { + x[v] = z & 0x7FFF; + } + } + x[len] = cc; + } + + /* + * We may have to do an extra subtraction, but only if the + * value in x[] is indeed greater than or equal to that of m[], + * which is why we must do two calls (first call computes the + * carry, second call performs the subtraction only if the carry + * is 0). + */ + br_i15_sub(x, m, NOT(br_i15_sub(x, m, 0))); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c new file mode 100644 index 0000000000..596d8b9a73 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_iszero.c @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_iszero(const uint16_t *x) +{ + uint32_t z; + size_t u; + + z = 0; + for (u = (pgm_read_word(&x[0]) + 15) >> 4; u > 0; u --) { + z |= pgm_read_word(&x[u]); + } + return ~(z | -z) >> 31; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c new file mode 100644 index 0000000000..d265a7d901 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_moddiv.c @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * In this file, we handle big integers with a custom format, i.e. + * without the usual one-word header. Value is split into 15-bit words, + * each stored in a 16-bit slot (top bit is zero) in little-endian + * order. The length (in words) is provided explicitly. In some cases, + * the value can be negative (using two's complement representation). In + * some cases, the top word is allowed to have a 16th bit. + */ + +/* + * Negate big integer conditionally. The value consists of 'len' words, + * with 15 bits in each word (the top bit of each word should be 0, + * except possibly for the last word). If 'ctl' is 1, the negation is + * computed; otherwise, if 'ctl' is 0, then the value is unchanged. + */ +static void +cond_negate(uint16_t *a, size_t len, uint32_t ctl) +{ + size_t k; + uint32_t cc, xm; + + cc = ctl; + xm = 0x7FFF & -ctl; + for (k = 0; k < len; k ++) { + uint32_t aw; + + aw = a[k]; + aw = (aw ^ xm) + cc; + a[k] = aw & 0x7FFF; + cc = (aw >> 15) & 1; + } +} + +/* + * Finish modular reduction. Rules on input parameters: + * + * if neg = 1, then -m <= a < 0 + * if neg = 0, then 0 <= a < 2*m + * + * If neg = 0, then the top word of a[] may use 16 bits. + * + * Also, modulus m must be odd. + */ +static void +finish_mod(uint16_t *a, size_t len, const uint16_t *m, uint32_t neg) +{ + size_t k; + uint32_t cc, xm, ym; + + /* + * First pass: compare a (assumed nonnegative) with m. + */ + cc = 0; + for (k = 0; k < len; k ++) { + uint32_t aw, mw; + + aw = a[k]; + mw = pgm_read_word(&m[k]); + cc = (aw - mw - cc) >> 31; + } + + /* + * At this point: + * if neg = 1, then we must add m (regardless of cc) + * if neg = 0 and cc = 0, then we must subtract m + * if neg = 0 and cc = 1, then we must do nothing + */ + xm = 0x7FFF & -neg; + ym = -(neg | (1 - cc)); + cc = neg; + for (k = 0; k < len; k ++) { + uint32_t aw, mw; + + aw = a[k]; + mw = (pgm_read_word(&m[k]) ^ xm) & ym; + aw = aw - mw - cc; + a[k] = aw & 0x7FFF; + cc = aw >> 31; + } +} + +/* + * Compute: + * a <- (a*pa+b*pb)/(2^15) + * b <- (a*qa+b*qb)/(2^15) + * The division is assumed to be exact (i.e. the low word is dropped). + * If the final a is negative, then it is negated. Similarly for b. + * Returned value is the combination of two bits: + * bit 0: 1 if a had to be negated, 0 otherwise + * bit 1: 1 if b had to be negated, 0 otherwise + * + * Factors pa, pb, qa and qb must be at most 2^15 in absolute value. + * Source integers a and b must be nonnegative; top word is not allowed + * to contain an extra 16th bit. + */ +static uint32_t +co_reduce(uint16_t *a, uint16_t *b, size_t len, + int32_t pa, int32_t pb, int32_t qa, int32_t qb) +{ + size_t k; + int32_t cca, ccb; + uint32_t nega, negb; + + cca = 0; + ccb = 0; + for (k = 0; k < len; k ++) { + uint32_t wa, wb, za, zb; + uint16_t tta, ttb; + + /* + * Since: + * |pa| <= 2^15 + * |pb| <= 2^15 + * 0 <= wa <= 2^15 - 1 + * 0 <= wb <= 2^15 - 1 + * |cca| <= 2^16 - 1 + * Then: + * |za| <= (2^15-1)*(2^16) + (2^16-1) = 2^31 - 1 + * + * Thus, the new value of cca is such that |cca| <= 2^16 - 1. + * The same applies to ccb. + */ + wa = a[k]; + wb = b[k]; + za = wa * (uint32_t)pa + wb * (uint32_t)pb + (uint32_t)cca; + zb = wa * (uint32_t)qa + wb * (uint32_t)qb + (uint32_t)ccb; + if (k > 0) { + a[k - 1] = za & 0x7FFF; + b[k - 1] = zb & 0x7FFF; + } + tta = za >> 15; + ttb = zb >> 15; + cca = *(int16_t *)&tta; + ccb = *(int16_t *)&ttb; + } + a[len - 1] = (uint16_t)cca; + b[len - 1] = (uint16_t)ccb; + nega = (uint32_t)cca >> 31; + negb = (uint32_t)ccb >> 31; + cond_negate(a, len, nega); + cond_negate(b, len, negb); + return nega | (negb << 1); +} + +/* + * Compute: + * a <- (a*pa+b*pb)/(2^15) mod m + * b <- (a*qa+b*qb)/(2^15) mod m + * + * m0i is equal to -1/m[0] mod 2^15. + * + * Factors pa, pb, qa and qb must be at most 2^15 in absolute value. + * Source integers a and b must be nonnegative; top word is not allowed + * to contain an extra 16th bit. + */ +static void +co_reduce_mod(uint16_t *a, uint16_t *b, size_t len, + int32_t pa, int32_t pb, int32_t qa, int32_t qb, + const uint16_t *m, uint16_t m0i) +{ + size_t k; + int32_t cca, ccb, fa, fb; + + cca = 0; + ccb = 0; + fa = ((a[0] * (uint32_t)pa + b[0] * (uint32_t)pb) * m0i) & 0x7FFF; + fb = ((a[0] * (uint32_t)qa + b[0] * (uint32_t)qb) * m0i) & 0x7FFF; + for (k = 0; k < len; k ++) { + uint32_t wa, wb, za, zb; + uint32_t tta, ttb; + + /* + * In this loop, carries 'cca' and 'ccb' always fit on + * 17 bits (in absolute value). + */ + wa = a[k]; + wb = b[k]; + za = wa * (uint32_t)pa + wb * (uint32_t)pb + + pgm_read_word(&m[k]) * (uint32_t)fa + (uint32_t)cca; + zb = wa * (uint32_t)qa + wb * (uint32_t)qb + + pgm_read_word(&m[k]) * (uint32_t)fb + (uint32_t)ccb; + if (k > 0) { + a[k - 1] = za & 0x7FFF; + b[k - 1] = zb & 0x7FFF; + } + + /* + * The XOR-and-sub construction below does an arithmetic + * right shift in a portable way (technically, right-shifting + * a negative signed value is implementation-defined in C). + */ +#define M ((uint32_t)1 << 16) + tta = za >> 15; + ttb = zb >> 15; + tta = (tta ^ M) - M; + ttb = (ttb ^ M) - M; + cca = *(int32_t *)&tta; + ccb = *(int32_t *)&ttb; +#undef M + } + a[len - 1] = (uint32_t)cca; + b[len - 1] = (uint32_t)ccb; + + /* + * At this point: + * -m <= a < 2*m + * -m <= b < 2*m + * (this is a case of Montgomery reduction) + * The top word of 'a' and 'b' may have a 16-th bit set. + * We may have to add or subtract the modulus. + */ + finish_mod(a, len, m, (uint32_t)cca >> 31); + finish_mod(b, len, m, (uint32_t)ccb >> 31); +} + +/* see inner.h */ +uint32_t +br_i15_moddiv(uint16_t *x, const uint16_t *y, const uint16_t *m, uint16_t m0i, + uint16_t *t) +{ + /* + * Algorithm is an extended binary GCD. We maintain four values + * a, b, u and v, with the following invariants: + * + * a * x = y * u mod m + * b * x = y * v mod m + * + * Starting values are: + * + * a = y + * b = m + * u = x + * v = 0 + * + * The formal definition of the algorithm is a sequence of steps: + * + * - If a is even, then a <- a/2 and u <- u/2 mod m. + * - Otherwise, if b is even, then b <- b/2 and v <- v/2 mod m. + * - Otherwise, if a > b, then a <- (a-b)/2 and u <- (u-v)/2 mod m. + * - Otherwise, b <- (b-a)/2 and v <- (v-u)/2 mod m. + * + * Algorithm stops when a = b. At that point, they both are equal + * to GCD(y,m); the modular division succeeds if that value is 1. + * The result of the modular division is then u (or v: both are + * equal at that point). + * + * Each step makes either a or b shrink by at least one bit; hence, + * if m has bit length k bits, then 2k-2 steps are sufficient. + * + * + * Though complexity is quadratic in the size of m, the bit-by-bit + * processing is not very efficient. We can speed up processing by + * remarking that the decisions are taken based only on observation + * of the top and low bits of a and b. + * + * In the loop below, at each iteration, we use the two top words + * of a and b, and the low words of a and b, to compute reduction + * parameters pa, pb, qa and qb such that the new values for a + * and b are: + * + * a' = (a*pa + b*pb) / (2^15) + * b' = (a*qa + b*qb) / (2^15) + * + * the division being exact. + * + * Since the choices are based on the top words, they may be slightly + * off, requiring an optional correction: if a' < 0, then we replace + * pa with -pa, and pb with -pb. The total length of a and b is + * thus reduced by at least 14 bits at each iteration. + * + * The stopping conditions are still the same, though: when a + * and b become equal, they must be both odd (since m is odd, + * the GCD cannot be even), therefore the next operation is a + * subtraction, and one of the values becomes 0. At that point, + * nothing else happens, i.e. one value is stuck at 0, and the + * other one is the GCD. + */ + size_t len, k; + uint16_t *a, *b, *u, *v; + uint32_t num, r; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + a = t; + b = a + len; + u = x + 1; + v = b + len; + memcpy_P(a, y + 1, len * sizeof *y); + memcpy_P(b, m + 1, len * sizeof *m); + memset(v, 0, len * sizeof *v); + + /* + * Loop below ensures that a and b are reduced by some bits each, + * for a total of at least 14 bits. + */ + for (num = ((pgm_read_word(&m[0]) - (pgm_read_word(&m[0]) >> 4)) << 1) + 14; num >= 14; num -= 14) { + size_t j; + uint32_t c0, c1; + uint32_t a0, a1, b0, b1; + uint32_t a_hi, b_hi, a_lo, b_lo; + int32_t pa, pb, qa, qb; + int i; + + /* + * Extract top words of a and b. If j is the highest + * index >= 1 such that a[j] != 0 or b[j] != 0, then we want + * (a[j] << 15) + a[j - 1], and (b[j] << 15) + b[j - 1]. + * If a and b are down to one word each, then we use a[0] + * and b[0]. + */ + c0 = (uint32_t)-1; + c1 = (uint32_t)-1; + a0 = 0; + a1 = 0; + b0 = 0; + b1 = 0; + j = len; + while (j -- > 0) { + uint32_t aw, bw; + + aw = a[j]; + bw = b[j]; + a0 ^= (a0 ^ aw) & c0; + a1 ^= (a1 ^ aw) & c1; + b0 ^= (b0 ^ bw) & c0; + b1 ^= (b1 ^ bw) & c1; + c1 = c0; + c0 &= (((aw | bw) + 0xFFFF) >> 16) - (uint32_t)1; + } + + /* + * If c1 = 0, then we grabbed two words for a and b. + * If c1 != 0 but c0 = 0, then we grabbed one word. It + * is not possible that c1 != 0 and c0 != 0, because that + * would mean that both integers are zero. + */ + a1 |= a0 & c1; + a0 &= ~c1; + b1 |= b0 & c1; + b0 &= ~c1; + a_hi = (a0 << 15) + a1; + b_hi = (b0 << 15) + b1; + a_lo = a[0]; + b_lo = b[0]; + + /* + * Compute reduction factors: + * + * a' = a*pa + b*pb + * b' = a*qa + b*qb + * + * such that a' and b' are both multiple of 2^15, but are + * only marginally larger than a and b. + */ + pa = 1; + pb = 0; + qa = 0; + qb = 1; + for (i = 0; i < 15; i ++) { + /* + * At each iteration: + * + * a <- (a-b)/2 if: a is odd, b is odd, a_hi > b_hi + * b <- (b-a)/2 if: a is odd, b is odd, a_hi <= b_hi + * a <- a/2 if: a is even + * b <- b/2 if: a is odd, b is even + * + * We multiply a_lo and b_lo by 2 at each + * iteration, thus a division by 2 really is a + * non-multiplication by 2. + */ + uint32_t r, oa, ob, cAB, cBA, cA; + + /* + * cAB = 1 if b must be subtracted from a + * cBA = 1 if a must be subtracted from b + * cA = 1 if a is divided by 2, 0 otherwise + * + * Rules: + * + * cAB and cBA cannot be both 1. + * if a is not divided by 2, b is. + */ + r = GT(a_hi, b_hi); + oa = (a_lo >> i) & 1; + ob = (b_lo >> i) & 1; + cAB = oa & ob & r; + cBA = oa & ob & NOT(r); + cA = cAB | NOT(oa); + + /* + * Conditional subtractions. + */ + a_lo -= b_lo & -cAB; + a_hi -= b_hi & -cAB; + pa -= qa & -(int32_t)cAB; + pb -= qb & -(int32_t)cAB; + b_lo -= a_lo & -cBA; + b_hi -= a_hi & -cBA; + qa -= pa & -(int32_t)cBA; + qb -= pb & -(int32_t)cBA; + + /* + * Shifting. + */ + a_lo += a_lo & (cA - 1); + pa += pa & ((int32_t)cA - 1); + pb += pb & ((int32_t)cA - 1); + a_hi ^= (a_hi ^ (a_hi >> 1)) & -cA; + b_lo += b_lo & -cA; + qa += qa & -(int32_t)cA; + qb += qb & -(int32_t)cA; + b_hi ^= (b_hi ^ (b_hi >> 1)) & (cA - 1); + } + + /* + * Replace a and b with new values a' and b'. + */ + r = co_reduce(a, b, len, pa, pb, qa, qb); + pa -= pa * ((r & 1) << 1); + pb -= pb * ((r & 1) << 1); + qa -= qa * (r & 2); + qb -= qb * (r & 2); + co_reduce_mod(u, v, len, pa, pb, qa, qb, m + 1, m0i); + } + + /* + * Now one of the arrays should be 0, and the other contains + * the GCD. If a is 0, then u is 0 as well, and v contains + * the division result. + * Result is correct if and only if GCD is 1. + */ + r = (a[0] | b[0]) ^ 1; + u[0] |= v[0]; + for (k = 1; k < len; k ++) { + r |= a[k] | b[k]; + u[k] |= v[k]; + } + return EQ0(r); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c new file mode 100644 index 0000000000..bcfa2110ce --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_modpow(uint16_t *x, + const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *t1, uint16_t *t2) +{ + size_t mlen; + unsigned k; + + mlen = ((pgm_read_word(&m[0]) + 31) >> 4) * sizeof m[0]; + memcpy(t1, x, mlen); + br_i15_to_monty(t1, m); + br_i15_zero(x, pgm_read_word(&m[0])); + x[1] = 1; + for (k = 0; k < ((unsigned)elen << 3); k ++) { + uint32_t ctl; + + ctl = (pgm_read_byte(&e[elen - 1 - (k >> 3)]) >> (k & 7)) & 1; + br_i15_montymul(t2, x, t1, m, m0i); + CCOPY(ctl, x, t2, mlen); + br_i15_montymul(t2, t1, t1, m, m0i); + memcpy(t1, t2, mlen); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c new file mode 100644 index 0000000000..e58045f014 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_modpow2.c @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_modpow_opt(uint16_t *x, + const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *tmp, size_t twlen) +{ + size_t mlen, mwlen; + uint16_t *t1, *t2, *base; + size_t u, v; + uint32_t acc; + int acc_len, win_len; + + /* + * Get modulus size. + */ + mwlen = (pgm_read_word(&m[0]) + 31) >> 4; + mlen = mwlen * sizeof m[0]; + mwlen += (mwlen & 1); + t1 = tmp; + t2 = tmp + mwlen; + + /* + * Compute possible window size, with a maximum of 5 bits. + * When the window has size 1 bit, we use a specific code + * that requires only two temporaries. Otherwise, for a + * window of k bits, we need 2^k+1 temporaries. + */ + if (twlen < (mwlen << 1)) { + return 0; + } + for (win_len = 5; win_len > 1; win_len --) { + if ((((uint32_t)1 << win_len) + 1) * mwlen <= twlen) { + break; + } + } + + /* + * Everything is done in Montgomery representation. + */ + br_i15_to_monty(x, m); + + /* + * Compute window contents. If the window has size one bit only, + * then t2 is set to x; otherwise, t2[0] is left untouched, and + * t2[k] is set to x^k (for k >= 1). + */ + if (win_len == 1) { + memcpy(t2, x, mlen); + } else { + memcpy(t2 + mwlen, x, mlen); + base = t2 + mwlen; + for (u = 2; u < ((unsigned)1 << win_len); u ++) { + br_i15_montymul(base + mwlen, base, x, m, m0i); + base += mwlen; + } + } + + /* + * We need to set x to 1, in Montgomery representation. This can + * be done efficiently by setting the high word to 1, then doing + * one word-sized shift. + */ + br_i15_zero(x, pgm_read_word(&m[0])); + x[(pgm_read_word(&m[0]) + 15) >> 4] = 1; + br_i15_muladd_small(x, 0, m); + + /* + * We process bits from most to least significant. At each + * loop iteration, we have acc_len bits in acc. + */ + acc = 0; + acc_len = 0; + while (acc_len > 0 || elen > 0) { + int i, k; + uint32_t bits; + + /* + * Get the next bits. + */ + k = win_len; + if (acc_len < win_len) { + if (elen > 0) { + acc = (acc << 8) | pgm_read_byte(&*e ++); + elen --; + acc_len += 8; + } else { + k = acc_len; + } + } + bits = (acc >> (acc_len - k)) & (((uint32_t)1 << k) - 1); + acc_len -= k; + + /* + * We could get exactly k bits. Compute k squarings. + */ + for (i = 0; i < k; i ++) { + br_i15_montymul(t1, x, x, m, m0i); + memcpy(x, t1, mlen); + } + + /* + * Window lookup: we want to set t2 to the window + * lookup value, assuming the bits are non-zero. If + * the window length is 1 bit only, then t2 is + * already set; otherwise, we do a constant-time lookup. + */ + if (win_len > 1) { + br_i15_zero(t2, pgm_read_word(&m[0])); + base = t2 + mwlen; + for (u = 1; u < ((uint32_t)1 << k); u ++) { + uint32_t mask; + + mask = -EQ(u, bits); + for (v = 1; v < mwlen; v ++) { + t2[v] |= mask & base[v]; + } + base += mwlen; + } + } + + /* + * Multiply with the looked-up value. We keep the + * product only if the exponent bits are not all-zero. + */ + br_i15_montymul(t1, x, t2, m, m0i); + CCOPY(NEQ(bits, 0), x, t1, mlen); + } + + /* + * Convert back from Montgomery representation, and exit. + */ + br_i15_from_monty(x, m, m0i); + return 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c new file mode 100644 index 0000000000..b2206f5870 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_montmul.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_montymul(uint16_t *d, const uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i) +{ + size_t len, len4, u, v; + uint32_t dh; + + len = (pgm_read_word(&m[0]) + 15) >> 4; + len4 = len & ~(size_t)3; + br_i15_zero(d, pgm_read_word(&m[0])); + dh = 0; + for (u = 0; u < len; u ++) { + uint32_t f, xu, r, zh; + + xu = pgm_read_word(&x[u + 1]); + f = MUL15((d[1] + MUL15(pgm_read_word(&x[u + 1]), pgm_read_word(&y[1]))) & 0x7FFF, m0i) + & 0x7FFF; +#if BR_ARMEL_CORTEXM_GCC + if (len4 != 0) { + uint16_t *limit; + + limit = d + len4; + asm volatile ( +"\n\ + @ carry: r=r2 \n\ + @ multipliers: xu=r3 f=r4 \n\ + @ base registers: d+v=r5 y+v=r6 m+v=r7 \n\ + @ r8 contains 0x7FFF \n\ + @ r9 contains d+len4 \n\ + ldr r0, %[limit] \n\ + ldr r3, %[xu] \n\ + mov r9, r0 \n\ + ldr r4, %[f] \n\ + eor r2, r2 \n\ + ldr r5, %[d] \n\ + sub r1, r2, #1 \n\ + ldr r6, %[y] \n\ + lsr r1, r1, #17 \n\ + ldr r7, %[m] \n\ + mov r8, r1 \n\ +loop%=: \n\ + ldrh r0, [r6, #2] \n\ + ldrh r1, [r7, #2] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #2] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #0] \n\ + \n\ + ldrh r0, [r6, #4] \n\ + ldrh r1, [r7, #4] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #4] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #2] \n\ + \n\ + ldrh r0, [r6, #6] \n\ + ldrh r1, [r7, #6] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #6] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #4] \n\ + \n\ + ldrh r0, [r6, #8] \n\ + ldrh r1, [r7, #8] \n\ + mul r0, r3 \n\ + mul r1, r4 \n\ + add r2, r0, r2 \n\ + ldrh r0, [r5, #8] \n\ + add r2, r1, r2 \n\ + mov r1, r8 \n\ + add r2, r0, r2 \n\ + and r1, r2 \n\ + lsr r2, r2, #15 \n\ + strh r1, [r5, #6] \n\ + \n\ + add r5, r5, #8 \n\ + add r6, r6, #8 \n\ + add r7, r7, #8 \n\ + cmp r5, r9 \n\ + bne loop%= \n\ + \n\ + str r2, %[carry] \n\ +" +: [carry] "=m" (r) +: [xu] "m" (xu), [f] "m" (f), [d] "m" (d), [y] "m" (y), + [m] "m" (m), [limit] "m" (limit) +: "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9" ); + } else { + r = 0; + } + v = len4; +#else + r = 0; + const uint16_t *py; + const uint16_t *pm; + + py = &y[0]; // addresses of both arrays that will be scanned as uint16_t + pm = &m[0]; + int py_unaligned = (((intptr_t)py) & 2) != 0; + int pm_unaligned = (((intptr_t)pm) & 2) != 0; + uint32_t ty, tm; // 32 bits buffers + if (!py_unaligned && !pm_unaligned) { + // both are aligned to 32 bits, we always skip the first 16 bits + ty = *(uint32_t*)py; // pre-load with 32 bits value, next value will be loaded at end of loop iteration + tm = *(uint32_t*)pm; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; // we define 4 variables for easy reading + uint16_t m1, m2, m3, m4; // but optimizer will collapse them into 1 + + uint32_t z; + + y1 = ty >> 16; // v+1, get upper 16 bits current 32 bits + m1 = tm >> 16; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y2 = ty; // get lower 16 bits + tm = *(uint32_t*)(pm = pm + 2); + m2 = tm; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + y3 = ty >> 16; + m3 = tm >> 16; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y4 = ty; // get lower 16 bits + tm = *(uint32_t*)(pm = pm + 2); + m4 = tm; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else if (!py_unaligned && pm_unaligned) { + pm--; // we decrement by 1 because will increment by 2 at beginning of loop + ty = *(uint32_t*)py; // pre-load with 32 bits value + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + y1 = ty >> 16; // +1 + tm = *(uint32_t*)(pm = pm + 2); + m1 = tm; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y2 = ty; + m2 = tm >> 16; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + y3 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m3 = tm; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y4 = ty; + m4 = tm >> 16; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else if (py_unaligned && !pm_unaligned) { // buggy + // py unaligned, pm aligned + py--; + tm = *(uint32_t*)pm; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y1 = ty; + m1 = tm >> 16; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + y2 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m2 = tm; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y3 = ty; + m3 = tm >> 16; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + y4 = ty >> 16; + tm = *(uint32_t*)(pm = pm + 2); + m4 = tm; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } else { // if (py_unaligned && pm_unaligned) { + // py unaligned, pm unaligned + py--; + pm--; + for (v = 0; v < len4; v += 4) { + uint16_t y1, y2, y3, y4; + uint16_t m1, m2, m3, m4; + uint32_t z; + + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y1 = ty; // +1 + tm = *(uint32_t*)(pm = pm + 2); + m1 = tm; + z = d[v + 1] + MUL15(xu, y1) + MUL15(f, m1) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + // + y2 = ty >> 16; + m2 = tm >> 16; + z = d[v + 2] + MUL15(xu, y2) + MUL15(f, m2) + r; + r = z >> 15; + d[v + 1] = z & 0x7FFF; + // + ty = *(uint32_t*)(py = py + 2); // next 32 bits + y3 = ty; + tm = *(uint32_t*)(pm = pm + 2); + m3 = tm; + z = d[v + 3] + MUL15(xu, y3) + MUL15(f, m3) + r; + r = z >> 15; + d[v + 2] = z & 0x7FFF; + // + y4 = ty >> 16; + m4 = tm >> 16; + z = d[v + 4] + MUL15(xu, y4) + MUL15(f, m4) + r; + r = z >> 15; + d[v + 3] = z & 0x7FFF; + } + } +#endif // BR_ARMEL_CORTEXM_GCC + for (; v < len; v ++) { + uint32_t z; + + z = d[v + 1] + MUL15(xu, pgm_read_word(&y[v + 1])) + + MUL15(f, pgm_read_word(&m[v + 1])) + r; + r = z >> 15; + d[v + 0] = z & 0x7FFF; + } + + zh = dh + r; + d[len] = zh & 0x7FFF; + dh = zh >> 15; + } + + /* + * Restore the bit length (it was overwritten in the loop above). + */ + d[0] = pgm_read_word(&m[0]); + + /* + * d[] may be greater than m[], but it is still lower than twice + * the modulus. + */ + br_i15_sub(d, m, NEQ(dh, 0) | NOT(br_i15_sub(d, m, 0))); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c new file mode 100644 index 0000000000..ee4bb157d7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_mulacc.c @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b) +{ + size_t alen, blen, u; + unsigned dl, dh; + + alen = (pgm_read_word(&a[0]) + 15) >> 4; + blen = (pgm_read_word(&b[0]) + 15) >> 4; + + /* + * Announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[]; but the lengths are encoded. + */ + dl = (pgm_read_word(&a[0]) & 15) + (pgm_read_word(&b[0]) & 15); + dh = (pgm_read_word(&a[0]) >> 4) + (pgm_read_word(&b[0]) >> 4); + d[0] = (dh << 4) + dl + (~(uint32_t)(dl - 15) >> 31); + + for (u = 0; u < blen; u ++) { + uint32_t f; + size_t v; + uint32_t cc; + + f = pgm_read_word(&b[1 + u]); + cc = 0; + for (v = 0; v < alen; v ++) { + uint32_t z; + + z = (uint32_t)d[1 + u + v] + MUL15(f, pgm_read_word(&a[1 + v])) + cc; + cc = z >> 15; + d[1 + u + v] = z & 0x7FFF; + } + d[1 + u + alen] = cc; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c new file mode 100644 index 0000000000..d8d73b335d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_muladd.c @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Constant-time division. The divisor must not be larger than 16 bits, + * and the quotient must fit on 17 bits. + */ +static uint32_t +divrem16(uint32_t x, uint32_t d, uint32_t *r) +{ + int i; + uint32_t q; + + q = 0; + d <<= 16; + for (i = 16; i >= 0; i --) { + uint32_t ctl; + + ctl = LE(d, x); + q |= ctl << i; + x -= (-ctl) & d; + d >>= 1; + } + if (r != NULL) { + *r = x; + } + return q; +} + +/* see inner.h */ +void +br_i15_muladd_small(uint16_t *x, uint16_t z, const uint16_t *m) +{ + /* + * Constant-time: we accept to leak the exact bit length of the + * modulus m. + */ + unsigned m_bitlen, mblr; + size_t u, mlen; + uint32_t hi, a0, a, b, q; + uint32_t cc, tb, over, under; + + /* + * Simple case: the modulus fits on one word. + */ + m_bitlen = pgm_read_word(&m[0]); + if (m_bitlen == 0) { + return; + } + if (m_bitlen <= 15) { + uint32_t rem; + + divrem16(((uint32_t)x[1] << 15) | z, pgm_read_word(&m[1]), &rem); + x[1] = rem; + return; + } + mlen = (m_bitlen + 15) >> 4; + mblr = m_bitlen & 15; + + /* + * Principle: we estimate the quotient (x*2^15+z)/m by + * doing a 30/15 division with the high words. + * + * Let: + * w = 2^15 + * a = (w*a0 + a1) * w^N + a2 + * b = b0 * w^N + b2 + * such that: + * 0 <= a0 < w + * 0 <= a1 < w + * 0 <= a2 < w^N + * w/2 <= b0 < w + * 0 <= b2 < w^N + * a < w*b + * I.e. the two top words of a are a0:a1, the top word of b is + * b0, we ensured that b0 is "full" (high bit set), and a is + * such that the quotient q = a/b fits on one word (0 <= q < w). + * + * If a = b*q + r (with 0 <= r < q), then we can estimate q by + * using a division on the top words: + * a0*w + a1 = b0*u + v (with 0 <= v < b0) + * Then the following holds: + * 0 <= u <= w + * u-2 <= q <= u + */ + hi = x[mlen]; + if (mblr == 0) { + a0 = x[mlen]; + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a = (a0 << 15) + x[mlen]; + b = pgm_read_word(&m[mlen]); + } else { + a0 = (x[mlen] << (15 - mblr)) | (x[mlen - 1] >> mblr); + memmove(x + 2, x + 1, (mlen - 1) * sizeof *x); + x[1] = z; + a = (a0 << 15) | (((x[mlen] << (15 - mblr)) + | (x[mlen - 1] >> mblr)) & 0x7FFF); + b = (pgm_read_word(&m[mlen]) << (15 - mblr)) | (pgm_read_word(&m[mlen - 1]) >> mblr); + } + q = divrem16(a, b, NULL); + + /* + * We computed an estimate for q, but the real one may be q, + * q-1 or q-2; moreover, the division may have returned a value + * 8000 or even 8001 if the two high words were identical, and + * we want to avoid values beyond 7FFF. We thus adjust q so + * that the "true" multiplier will be q+1, q or q-1, and q is + * in the 0000..7FFF range. + */ + q = MUX(EQ(b, a0), 0x7FFF, q - 1 + ((q - 1) >> 31)); + + /* + * We subtract q*m from x (x has an extra high word of value 'hi'). + * Since q may be off by 1 (in either direction), we may have to + * add or subtract m afterwards. + * + * The 'tb' flag will be true (1) at the end of the loop if the + * result is greater than or equal to the modulus (not counting + * 'hi' or the carry). + */ + cc = 0; + tb = 1; + for (u = 1; u <= mlen; u ++) { + uint32_t mw, zl, xw, nxw; + + mw = pgm_read_word(&m[u]); + zl = MUL15(mw, q) + cc; + cc = zl >> 15; + zl &= 0x7FFF; + xw = x[u]; + nxw = xw - zl; + cc += nxw >> 31; + nxw &= 0x7FFF; + x[u] = nxw; + tb = MUX(EQ(nxw, mw), tb, GT(nxw, mw)); + } + + /* + * If we underestimated q, then either cc < hi (one extra bit + * beyond the top array word), or cc == hi and tb is true (no + * extra bit, but the result is not lower than the modulus). + * + * If we overestimated q, then cc > hi. + */ + over = GT(cc, hi); + under = ~over & (tb | LT(cc, hi)); + br_i15_add(x, m, over); + br_i15_sub(x, m, under); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c new file mode 100644 index 0000000000..08028e5198 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_ninv15.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint16_t +br_i15_ninv15(uint16_t x) +{ + uint32_t y; + + y = 2 - x; + y = MUL15(y, 2 - MUL15(x, y)); + y = MUL15(y, 2 - MUL15(x, y)); + y = MUL15(y, 2 - MUL15(x, y)); + return MUX(x & 1, -y, 0) & 0x7FFF; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c new file mode 100644 index 0000000000..714ac5bb01 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_reduce.c @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_reduce(uint16_t *x, const uint16_t *a, const uint16_t *m) +{ + uint32_t m_bitlen, a_bitlen; + size_t mlen, alen, u; + + m_bitlen = pgm_read_word(&m[0]); + mlen = (m_bitlen + 15) >> 4; + + x[0] = m_bitlen; + if (m_bitlen == 0) { + return; + } + + /* + * If the source is shorter, then simply copy all words from a[] + * and zero out the upper words. + */ + a_bitlen = pgm_read_word(&a[0]); + alen = (a_bitlen + 15) >> 4; + if (a_bitlen < m_bitlen) { + memcpy_P(x + 1, a + 1, alen * sizeof *a); + for (u = alen; u < mlen; u ++) { + x[u + 1] = 0; + } + return; + } + + /* + * The source length is at least equal to that of the modulus. + * We must thus copy N-1 words, and input the remaining words + * one by one. + */ + memcpy_P(x + 1, a + 2 + (alen - mlen), (mlen - 1) * sizeof *a); + x[mlen] = 0; + for (u = 1 + alen - mlen; u > 0; u --) { + br_i15_muladd_small(x, pgm_read_word(&a[u]), m); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c new file mode 100644 index 0000000000..08208918c0 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_rshift.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_rshift(uint16_t *x, int count) +{ + size_t u, len; + unsigned r; + + len = (x[0] + 15) >> 4; + if (len == 0) { + return; + } + r = x[1] >> count; + for (u = 2; u <= len; u ++) { + unsigned w; + + w = x[u]; + x[u - 1] = ((w << (15 - count)) | r) & 0x7FFF; + r = w >> count; + } + x[len] = r; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c new file mode 100644 index 0000000000..4f75bda499 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_sub.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_i15_sub(uint16_t *a, const uint16_t *b, uint32_t ctl) +{ + uint32_t cc; + size_t u, m; + + cc = 0; + m = (a[0] + 31) >> 4; + for (u = 1; u < m; u ++) { + uint32_t aw, bw, naw; + + aw = a[u]; + bw = pgm_read_word(&b[u]); + naw = aw - bw - cc; + cc = naw >> 31; + a[u] = MUX(ctl, naw & 0x7FFF, aw); + } + return cc; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c b/lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c new file mode 100644 index 0000000000..538ed64cb7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/int/i15_tmont.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_i15_to_monty(uint16_t *x, const uint16_t *m) +{ + unsigned k; + + for (k = (pgm_read_word(&m[0]) + 15) >> 4; k > 0; k --) { + br_i15_muladd_small(x, 0, m); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c b/lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c new file mode 100644 index 0000000000..3a1814fa87 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/kdf/hkdf.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +const unsigned char br_hkdf_no_salt = 0; + +/* see bearssl_kdf.h */ +void +br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable, + const void *salt, size_t salt_len) +{ + br_hmac_key_context kc; + unsigned char tmp[64]; + + if (salt == BR_HKDF_NO_SALT) { + salt = tmp; + salt_len = br_digest_size(digest_vtable); + memset(tmp, 0, salt_len); + } + br_hmac_key_init(&kc, digest_vtable, salt, salt_len); + br_hmac_init(&hc->u.hmac_ctx, &kc, 0); + hc->dig_len = br_hmac_size(&hc->u.hmac_ctx); +} + +/* see bearssl_kdf.h */ +void +br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len) +{ + br_hmac_update(&hc->u.hmac_ctx, ikm, ikm_len); +} + +/* see bearssl_kdf.h */ +void +br_hkdf_flip(br_hkdf_context *hc) +{ + unsigned char tmp[64]; + + br_hmac_out(&hc->u.hmac_ctx, tmp); + br_hmac_key_init(&hc->u.prk_ctx, + br_hmac_get_digest(&hc->u.hmac_ctx), tmp, hc->dig_len); + hc->ptr = hc->dig_len; + hc->chunk_num = 0; +} + +/* see bearssl_kdf.h */ +size_t +br_hkdf_produce(br_hkdf_context *hc, + const void *info, size_t info_len, void *out, size_t out_len) +{ + size_t tlen; + + tlen = 0; + while (out_len > 0) { + size_t clen; + + if (hc->ptr == hc->dig_len) { + br_hmac_context hmac_ctx; + unsigned char x; + + hc->chunk_num ++; + if (hc->chunk_num == 256) { + return tlen; + } + x = hc->chunk_num; + br_hmac_init(&hmac_ctx, &hc->u.prk_ctx, 0); + if (x != 1) { + br_hmac_update(&hmac_ctx, hc->buf, hc->dig_len); + } + br_hmac_update(&hmac_ctx, info, info_len); + br_hmac_update(&hmac_ctx, &x, 1); + br_hmac_out(&hmac_ctx, hc->buf); + hc->ptr = 0; + } + clen = hc->dig_len - hc->ptr; + if (clen > out_len) { + clen = out_len; + } + memcpy(out, hc->buf + hc->ptr, clen); + out = (unsigned char *)out + clen; + out_len -= clen; + hc->ptr += clen; + tlen += clen; + } + return tlen; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c b/lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c new file mode 100644 index 0000000000..f9376c1c6e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/kdf/shake.c @@ -0,0 +1,590 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Round constants. + */ +static const uint64_t RC[] PROGMEM = { + 0x0000000000000001, 0x0000000000008082, + 0x800000000000808A, 0x8000000080008000, + 0x000000000000808B, 0x0000000080000001, + 0x8000000080008081, 0x8000000000008009, + 0x000000000000008A, 0x0000000000000088, + 0x0000000080008009, 0x000000008000000A, + 0x000000008000808B, 0x800000000000008B, + 0x8000000000008089, 0x8000000000008003, + 0x8000000000008002, 0x8000000000000080, + 0x000000000000800A, 0x800000008000000A, + 0x8000000080008081, 0x8000000000008080, + 0x0000000080000001, 0x8000000080008008 +}; + +/* + * XOR a block of data into the provided state. This supports only + * blocks whose length is a multiple of 64 bits. + */ +static void +xor_block(uint64_t *A, const void *data, size_t rate) +{ + size_t u; + + for (u = 0; u < rate; u += 8) { + A[u >> 3] ^= br_dec64le((const unsigned char *)data + u); + } +} + +/* + * Process a block with the provided data. The data length must be a + * multiple of 8 (in bytes); normally, this is the "rate". + */ +static void +process_block(uint64_t *A) +{ + uint64_t t0, t1, t2, t3, t4; + uint64_t tt0, tt1, tt2, tt3; + uint64_t t, kt; + uint64_t c0, c1, c2, c3, c4, bnn; + int j; + + /* + * Compute the 24 rounds. This loop is partially unrolled (each + * iteration computes two rounds). + */ + for (j = 0; j < 24; j += 2) { + + tt0 = A[ 1] ^ A[ 6]; + tt1 = A[11] ^ A[16]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 4] ^ A[ 9]; + tt3 = A[14] ^ A[19]; + tt0 ^= A[24]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[ 2] ^ A[ 7]; + tt1 = A[12] ^ A[17]; + tt0 ^= A[22] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 0] ^ A[ 5]; + tt3 = A[10] ^ A[15]; + tt0 ^= A[20]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[ 3] ^ A[ 8]; + tt1 = A[13] ^ A[18]; + tt0 ^= A[23] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 1] ^ A[ 6]; + tt3 = A[11] ^ A[16]; + tt0 ^= A[21]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[ 4] ^ A[ 9]; + tt1 = A[14] ^ A[19]; + tt0 ^= A[24] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 2] ^ A[ 7]; + tt3 = A[12] ^ A[17]; + tt0 ^= A[22]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 5]; + tt1 = A[10] ^ A[15]; + tt0 ^= A[20] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 3] ^ A[ 8]; + tt3 = A[13] ^ A[18]; + tt0 ^= A[23]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 5] = A[ 5] ^ t0; + A[10] = A[10] ^ t0; + A[15] = A[15] ^ t0; + A[20] = A[20] ^ t0; + A[ 1] = A[ 1] ^ t1; + A[ 6] = A[ 6] ^ t1; + A[11] = A[11] ^ t1; + A[16] = A[16] ^ t1; + A[21] = A[21] ^ t1; + A[ 2] = A[ 2] ^ t2; + A[ 7] = A[ 7] ^ t2; + A[12] = A[12] ^ t2; + A[17] = A[17] ^ t2; + A[22] = A[22] ^ t2; + A[ 3] = A[ 3] ^ t3; + A[ 8] = A[ 8] ^ t3; + A[13] = A[13] ^ t3; + A[18] = A[18] ^ t3; + A[23] = A[23] ^ t3; + A[ 4] = A[ 4] ^ t4; + A[ 9] = A[ 9] ^ t4; + A[14] = A[14] ^ t4; + A[19] = A[19] ^ t4; + A[24] = A[24] ^ t4; + A[ 5] = (A[ 5] << 36) | (A[ 5] >> (64 - 36)); + A[10] = (A[10] << 3) | (A[10] >> (64 - 3)); + A[15] = (A[15] << 41) | (A[15] >> (64 - 41)); + A[20] = (A[20] << 18) | (A[20] >> (64 - 18)); + A[ 1] = (A[ 1] << 1) | (A[ 1] >> (64 - 1)); + A[ 6] = (A[ 6] << 44) | (A[ 6] >> (64 - 44)); + A[11] = (A[11] << 10) | (A[11] >> (64 - 10)); + A[16] = (A[16] << 45) | (A[16] >> (64 - 45)); + A[21] = (A[21] << 2) | (A[21] >> (64 - 2)); + A[ 2] = (A[ 2] << 62) | (A[ 2] >> (64 - 62)); + A[ 7] = (A[ 7] << 6) | (A[ 7] >> (64 - 6)); + A[12] = (A[12] << 43) | (A[12] >> (64 - 43)); + A[17] = (A[17] << 15) | (A[17] >> (64 - 15)); + A[22] = (A[22] << 61) | (A[22] >> (64 - 61)); + A[ 3] = (A[ 3] << 28) | (A[ 3] >> (64 - 28)); + A[ 8] = (A[ 8] << 55) | (A[ 8] >> (64 - 55)); + A[13] = (A[13] << 25) | (A[13] >> (64 - 25)); + A[18] = (A[18] << 21) | (A[18] >> (64 - 21)); + A[23] = (A[23] << 56) | (A[23] >> (64 - 56)); + A[ 4] = (A[ 4] << 27) | (A[ 4] >> (64 - 27)); + A[ 9] = (A[ 9] << 20) | (A[ 9] >> (64 - 20)); + A[14] = (A[14] << 39) | (A[14] >> (64 - 39)); + A[19] = (A[19] << 8) | (A[19] >> (64 - 8)); + A[24] = (A[24] << 14) | (A[24] >> (64 - 14)); + bnn = ~A[12]; + kt = A[ 6] | A[12]; + c0 = A[ 0] ^ kt; + kt = bnn | A[18]; + c1 = A[ 6] ^ kt; + kt = A[18] & A[24]; + c2 = A[12] ^ kt; + kt = A[24] | A[ 0]; + c3 = A[18] ^ kt; + kt = A[ 0] & A[ 6]; + c4 = A[24] ^ kt; + A[ 0] = c0; + A[ 6] = c1; + A[12] = c2; + A[18] = c3; + A[24] = c4; + bnn = ~A[22]; + kt = A[ 9] | A[10]; + c0 = A[ 3] ^ kt; + kt = A[10] & A[16]; + c1 = A[ 9] ^ kt; + kt = A[16] | bnn; + c2 = A[10] ^ kt; + kt = A[22] | A[ 3]; + c3 = A[16] ^ kt; + kt = A[ 3] & A[ 9]; + c4 = A[22] ^ kt; + A[ 3] = c0; + A[ 9] = c1; + A[10] = c2; + A[16] = c3; + A[22] = c4; + bnn = ~A[19]; + kt = A[ 7] | A[13]; + c0 = A[ 1] ^ kt; + kt = A[13] & A[19]; + c1 = A[ 7] ^ kt; + kt = bnn & A[20]; + c2 = A[13] ^ kt; + kt = A[20] | A[ 1]; + c3 = bnn ^ kt; + kt = A[ 1] & A[ 7]; + c4 = A[20] ^ kt; + A[ 1] = c0; + A[ 7] = c1; + A[13] = c2; + A[19] = c3; + A[20] = c4; + bnn = ~A[17]; + kt = A[ 5] & A[11]; + c0 = A[ 4] ^ kt; + kt = A[11] | A[17]; + c1 = A[ 5] ^ kt; + kt = bnn | A[23]; + c2 = A[11] ^ kt; + kt = A[23] & A[ 4]; + c3 = bnn ^ kt; + kt = A[ 4] | A[ 5]; + c4 = A[23] ^ kt; + A[ 4] = c0; + A[ 5] = c1; + A[11] = c2; + A[17] = c3; + A[23] = c4; + bnn = ~A[ 8]; + kt = bnn & A[14]; + c0 = A[ 2] ^ kt; + kt = A[14] | A[15]; + c1 = bnn ^ kt; + kt = A[15] & A[21]; + c2 = A[14] ^ kt; + kt = A[21] | A[ 2]; + c3 = A[15] ^ kt; + kt = A[ 2] & A[ 8]; + c4 = A[21] ^ kt; + A[ 2] = c0; + A[ 8] = c1; + A[14] = c2; + A[15] = c3; + A[21] = c4; + A[ 0] = A[ 0] ^ RC[j + 0]; + + tt0 = A[ 6] ^ A[ 9]; + tt1 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[24] ^ A[22]; + tt3 = A[20] ^ A[23]; + tt0 ^= A[21]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[12] ^ A[10]; + tt1 = A[13] ^ A[11]; + tt0 ^= A[14] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 0] ^ A[ 3]; + tt3 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[18] ^ A[16]; + tt1 = A[19] ^ A[17]; + tt0 ^= A[15] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[ 6] ^ A[ 9]; + tt3 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[24] ^ A[22]; + tt1 = A[20] ^ A[23]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[12] ^ A[10]; + tt3 = A[13] ^ A[11]; + tt0 ^= A[14]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 3]; + tt1 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >> 63); + tt2 = A[18] ^ A[16]; + tt3 = A[19] ^ A[17]; + tt0 ^= A[15]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 3] = A[ 3] ^ t0; + A[ 1] = A[ 1] ^ t0; + A[ 4] = A[ 4] ^ t0; + A[ 2] = A[ 2] ^ t0; + A[ 6] = A[ 6] ^ t1; + A[ 9] = A[ 9] ^ t1; + A[ 7] = A[ 7] ^ t1; + A[ 5] = A[ 5] ^ t1; + A[ 8] = A[ 8] ^ t1; + A[12] = A[12] ^ t2; + A[10] = A[10] ^ t2; + A[13] = A[13] ^ t2; + A[11] = A[11] ^ t2; + A[14] = A[14] ^ t2; + A[18] = A[18] ^ t3; + A[16] = A[16] ^ t3; + A[19] = A[19] ^ t3; + A[17] = A[17] ^ t3; + A[15] = A[15] ^ t3; + A[24] = A[24] ^ t4; + A[22] = A[22] ^ t4; + A[20] = A[20] ^ t4; + A[23] = A[23] ^ t4; + A[21] = A[21] ^ t4; + A[ 3] = (A[ 3] << 36) | (A[ 3] >> (64 - 36)); + A[ 1] = (A[ 1] << 3) | (A[ 1] >> (64 - 3)); + A[ 4] = (A[ 4] << 41) | (A[ 4] >> (64 - 41)); + A[ 2] = (A[ 2] << 18) | (A[ 2] >> (64 - 18)); + A[ 6] = (A[ 6] << 1) | (A[ 6] >> (64 - 1)); + A[ 9] = (A[ 9] << 44) | (A[ 9] >> (64 - 44)); + A[ 7] = (A[ 7] << 10) | (A[ 7] >> (64 - 10)); + A[ 5] = (A[ 5] << 45) | (A[ 5] >> (64 - 45)); + A[ 8] = (A[ 8] << 2) | (A[ 8] >> (64 - 2)); + A[12] = (A[12] << 62) | (A[12] >> (64 - 62)); + A[10] = (A[10] << 6) | (A[10] >> (64 - 6)); + A[13] = (A[13] << 43) | (A[13] >> (64 - 43)); + A[11] = (A[11] << 15) | (A[11] >> (64 - 15)); + A[14] = (A[14] << 61) | (A[14] >> (64 - 61)); + A[18] = (A[18] << 28) | (A[18] >> (64 - 28)); + A[16] = (A[16] << 55) | (A[16] >> (64 - 55)); + A[19] = (A[19] << 25) | (A[19] >> (64 - 25)); + A[17] = (A[17] << 21) | (A[17] >> (64 - 21)); + A[15] = (A[15] << 56) | (A[15] >> (64 - 56)); + A[24] = (A[24] << 27) | (A[24] >> (64 - 27)); + A[22] = (A[22] << 20) | (A[22] >> (64 - 20)); + A[20] = (A[20] << 39) | (A[20] >> (64 - 39)); + A[23] = (A[23] << 8) | (A[23] >> (64 - 8)); + A[21] = (A[21] << 14) | (A[21] >> (64 - 14)); + bnn = ~A[13]; + kt = A[ 9] | A[13]; + c0 = A[ 0] ^ kt; + kt = bnn | A[17]; + c1 = A[ 9] ^ kt; + kt = A[17] & A[21]; + c2 = A[13] ^ kt; + kt = A[21] | A[ 0]; + c3 = A[17] ^ kt; + kt = A[ 0] & A[ 9]; + c4 = A[21] ^ kt; + A[ 0] = c0; + A[ 9] = c1; + A[13] = c2; + A[17] = c3; + A[21] = c4; + bnn = ~A[14]; + kt = A[22] | A[ 1]; + c0 = A[18] ^ kt; + kt = A[ 1] & A[ 5]; + c1 = A[22] ^ kt; + kt = A[ 5] | bnn; + c2 = A[ 1] ^ kt; + kt = A[14] | A[18]; + c3 = A[ 5] ^ kt; + kt = A[18] & A[22]; + c4 = A[14] ^ kt; + A[18] = c0; + A[22] = c1; + A[ 1] = c2; + A[ 5] = c3; + A[14] = c4; + bnn = ~A[23]; + kt = A[10] | A[19]; + c0 = A[ 6] ^ kt; + kt = A[19] & A[23]; + c1 = A[10] ^ kt; + kt = bnn & A[ 2]; + c2 = A[19] ^ kt; + kt = A[ 2] | A[ 6]; + c3 = bnn ^ kt; + kt = A[ 6] & A[10]; + c4 = A[ 2] ^ kt; + A[ 6] = c0; + A[10] = c1; + A[19] = c2; + A[23] = c3; + A[ 2] = c4; + bnn = ~A[11]; + kt = A[ 3] & A[ 7]; + c0 = A[24] ^ kt; + kt = A[ 7] | A[11]; + c1 = A[ 3] ^ kt; + kt = bnn | A[15]; + c2 = A[ 7] ^ kt; + kt = A[15] & A[24]; + c3 = bnn ^ kt; + kt = A[24] | A[ 3]; + c4 = A[15] ^ kt; + A[24] = c0; + A[ 3] = c1; + A[ 7] = c2; + A[11] = c3; + A[15] = c4; + bnn = ~A[16]; + kt = bnn & A[20]; + c0 = A[12] ^ kt; + kt = A[20] | A[ 4]; + c1 = bnn ^ kt; + kt = A[ 4] & A[ 8]; + c2 = A[20] ^ kt; + kt = A[ 8] | A[12]; + c3 = A[ 4] ^ kt; + kt = A[12] & A[16]; + c4 = A[ 8] ^ kt; + A[12] = c0; + A[16] = c1; + A[20] = c2; + A[ 4] = c3; + A[ 8] = c4; + A[ 0] = A[ 0] ^ RC[j + 1]; + t = A[ 5]; + A[ 5] = A[18]; + A[18] = A[11]; + A[11] = A[10]; + A[10] = A[ 6]; + A[ 6] = A[22]; + A[22] = A[20]; + A[20] = A[12]; + A[12] = A[19]; + A[19] = A[15]; + A[15] = A[24]; + A[24] = A[ 8]; + A[ 8] = t; + t = A[ 1]; + A[ 1] = A[ 9]; + A[ 9] = A[14]; + A[14] = A[ 2]; + A[ 2] = A[13]; + A[13] = A[23]; + A[23] = A[ 4]; + A[ 4] = A[21]; + A[21] = A[16]; + A[16] = A[ 3]; + A[ 3] = A[17]; + A[17] = A[ 7]; + A[ 7] = t; + } +} + +/* see bearssl_kdf.h */ +void +br_shake_init(br_shake_context *sc, int security_level) +{ + sc->rate = 200 - (size_t)(security_level >> 2); + sc->dptr = 0; + memset(sc->A, 0, sizeof sc->A); + sc->A[ 1] = ~(uint64_t)0; + sc->A[ 2] = ~(uint64_t)0; + sc->A[ 8] = ~(uint64_t)0; + sc->A[12] = ~(uint64_t)0; + sc->A[17] = ~(uint64_t)0; + sc->A[20] = ~(uint64_t)0; +} + +/* see bearssl_kdf.h */ +void +br_shake_inject(br_shake_context *sc, const void *data, size_t len) +{ + const unsigned char *buf; + size_t rate, dptr; + + buf = data; + rate = sc->rate; + dptr = sc->dptr; + while (len > 0) { + size_t clen; + + clen = rate - dptr; + if (clen > len) { + clen = len; + } + memcpy(sc->dbuf + dptr, buf, clen); + dptr += clen; + buf += clen; + len -= clen; + if (dptr == rate) { + xor_block(sc->A, sc->dbuf, rate); + process_block(sc->A); + dptr = 0; + } + } + sc->dptr = dptr; +} + +/* see bearssl_kdf.h */ +void +br_shake_flip(br_shake_context *sc) +{ + /* + * We apply padding and pre-XOR the value into the state. We + * set dptr to the end of the buffer, so that first call to + * shake_extract() will process the block. + */ + if ((sc->dptr + 1) == sc->rate) { + sc->dbuf[sc->dptr ++] = 0x9F; + } else { + sc->dbuf[sc->dptr ++] = 0x1F; + memset(sc->dbuf + sc->dptr, 0x00, sc->rate - sc->dptr - 1); + sc->dbuf[sc->rate - 1] = 0x80; + sc->dptr = sc->rate; + } + xor_block(sc->A, sc->dbuf, sc->rate); +} + +/* see bearssl_kdf.h */ +void +br_shake_produce(br_shake_context *sc, void *out, size_t len) +{ + unsigned char *buf; + size_t dptr, rate; + + buf = out; + dptr = sc->dptr; + rate = sc->rate; + while (len > 0) { + size_t clen; + + if (dptr == rate) { + unsigned char *dbuf; + uint64_t *A; + + A = sc->A; + dbuf = sc->dbuf; + process_block(A); + br_enc64le(dbuf + 0, A[ 0]); + br_enc64le(dbuf + 8, ~A[ 1]); + br_enc64le(dbuf + 16, ~A[ 2]); + br_enc64le(dbuf + 24, A[ 3]); + br_enc64le(dbuf + 32, A[ 4]); + br_enc64le(dbuf + 40, A[ 5]); + br_enc64le(dbuf + 48, A[ 6]); + br_enc64le(dbuf + 56, A[ 7]); + br_enc64le(dbuf + 64, ~A[ 8]); + br_enc64le(dbuf + 72, A[ 9]); + br_enc64le(dbuf + 80, A[10]); + br_enc64le(dbuf + 88, A[11]); + br_enc64le(dbuf + 96, ~A[12]); + br_enc64le(dbuf + 104, A[13]); + br_enc64le(dbuf + 112, A[14]); + br_enc64le(dbuf + 120, A[15]); + br_enc64le(dbuf + 128, A[16]); + br_enc64le(dbuf + 136, ~A[17]); + br_enc64le(dbuf + 144, A[18]); + br_enc64le(dbuf + 152, A[19]); + br_enc64le(dbuf + 160, ~A[20]); + br_enc64le(dbuf + 168, A[21]); + br_enc64le(dbuf + 176, A[22]); + br_enc64le(dbuf + 184, A[23]); + br_enc64le(dbuf + 192, A[24]); + dptr = 0; + } + clen = rate - dptr; + if (clen > len) { + clen = len; + } + memcpy(buf, sc->dbuf + dptr, clen); + dptr += clen; + buf += clen; + len -= clen; + } + sc->dptr = dptr; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c new file mode 100644 index 0000000000..ea00a5327a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline size_t +block_size(const br_hash_class *dig) +{ + unsigned ls; + + ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF) + & BR_HASHDESC_LBLEN_MASK; + return (size_t)1 << ls; +} + +static void +process_key(const br_hash_class **hc, void *ks, + const void *key, size_t key_len, unsigned bb) +{ + unsigned char tmp[256]; + size_t blen, u; + + blen = block_size(*hc); + memcpy(tmp, key, key_len); + for (u = 0; u < key_len; u ++) { + tmp[u] ^= (unsigned char)bb; + } + memset(tmp + key_len, bb, blen - key_len); + (*hc)->init(hc); + (*hc)->update(hc, tmp, blen); + (*hc)->state(hc, ks); +} + +/* see bearssl.h */ +void +br_hmac_key_init(br_hmac_key_context *kc, + const br_hash_class *dig, const void *key, size_t key_len) +{ + br_hash_compat_context hc; + unsigned char kbuf[64]; + + kc->dig_vtable = dig; + hc.vtable = dig; + if (key_len > block_size(dig)) { + dig->init(&hc.vtable); + dig->update(&hc.vtable, key, key_len); + dig->out(&hc.vtable, kbuf); + key = kbuf; + key_len = br_digest_size(dig); + } + process_key(&hc.vtable, kc->ksi, key, key_len, 0x36); + process_key(&hc.vtable, kc->kso, key, key_len, 0x5C); +} + +/* see bearssl.h */ +void +br_hmac_init(br_hmac_context *ctx, + const br_hmac_key_context *kc, size_t out_len) +{ + const br_hash_class *dig; + size_t blen, hlen; + + dig = kc->dig_vtable; + blen = block_size(dig); + dig->init(&ctx->dig.vtable); + dig->set_state(&ctx->dig.vtable, kc->ksi, (uint64_t)blen); + memcpy(ctx->kso, kc->kso, sizeof kc->kso); + hlen = br_digest_size(dig); + if (out_len > 0 && out_len < hlen) { + hlen = out_len; + } + ctx->out_len = hlen; +} + +/* see bearssl.h */ +void +br_hmac_update(br_hmac_context *ctx, const void *data, size_t len) +{ + ctx->dig.vtable->update(&ctx->dig.vtable, data, len); +} + +/* see bearssl.h */ +size_t +br_hmac_out(const br_hmac_context *ctx, void *out) +{ + const br_hash_class *dig; + br_hash_compat_context hc; + unsigned char tmp[64]; + size_t blen, hlen; + + dig = ctx->dig.vtable; + dig->out(&ctx->dig.vtable, tmp); + blen = block_size(dig); + dig->init(&hc.vtable); + dig->set_state(&hc.vtable, ctx->kso, (uint64_t)blen); + hlen = br_digest_size(dig); + dig->update(&hc.vtable, tmp, hlen); + dig->out(&hc.vtable, tmp); + memcpy(out, tmp, ctx->out_len); + return ctx->out_len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c new file mode 100644 index 0000000000..576136a136 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/mac/hmac_ct.c @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline size_t +hash_size(const br_hash_class *dig) +{ + return (unsigned)(dig->desc >> BR_HASHDESC_OUT_OFF) + & BR_HASHDESC_OUT_MASK; +} + +static inline size_t +block_size(const br_hash_class *dig) +{ + unsigned ls; + + ls = (unsigned)(dig->desc >> BR_HASHDESC_LBLEN_OFF) + & BR_HASHDESC_LBLEN_MASK; + return (size_t)1 << ls; +} + +/* see bearssl.h */ +size_t +br_hmac_outCT(const br_hmac_context *ctx, + const void *data, size_t len, size_t min_len, size_t max_len, + void *out) +{ + /* + * Method implemented here is inspired from the descriptions on: + * https://www.imperialviolet.org/2013/02/04/luckythirteen.html + * + * Principle: we input bytes one by one. We use a MUX to push + * padding bytes instead of data bytes when appropriate. At each + * block limit, we get the current hash function state: this is + * a potential output, since we handle MD padding ourselves. + * + * be 1 for big-endian, 0 for little-endian + * po minimal MD padding length + * bs block size (always a power of 2) + * hlen hash output size + */ + + const br_hash_class *dig; + br_hash_compat_context hc; + int be; + uint32_t po, bs; + uint32_t kr, km, kl, kz, u; + uint64_t count, ncount, bit_len; + unsigned char tmp1[64], tmp2[64]; + size_t hlen; + + /* + * Copy the current hash context. + */ + hc = ctx->dig; + + /* + * Get function-specific information. + */ + dig = hc.vtable; + be = (dig->desc & BR_HASHDESC_MD_PADDING_BE) != 0; + po = 9; + if (dig->desc & BR_HASHDESC_MD_PADDING_128) { + po += 8; + } + bs = block_size(dig); + hlen = hash_size(dig); + + /* + * Get current input length and compute total bit length. + */ + count = dig->state(&hc.vtable, tmp1); + bit_len = (count + (uint64_t)len) << 3; + + /* + * We can input the blocks that we are sure we will use. + * This offers better performance (no MUX for these blocks) + * and also ensures that the remaining lengths fit on 32 bits. + */ + ncount = (count + (uint64_t)min_len) & ~(uint64_t)(bs - 1); + if (ncount > count) { + size_t zlen; + + zlen = (size_t)(ncount - count); + dig->update(&hc.vtable, data, zlen); + data = (const unsigned char *)data + zlen; + len -= zlen; + max_len -= zlen; + count = ncount; + } + + /* + * At that point: + * -- 'count' contains the number of bytes already processed + * (in total). + * -- We must input 'len' bytes. 'min_len' is unimportant: we + * used it to know how many full blocks we could process + * directly. Now only len and max_len matter. + * + * We compute kr, kl, kz and km. + * kr number of input bytes already in the current block + * km index of the first byte after the end of the last padding + * block, if length is max_len + * kz index of the last byte of the actual last padding block + * kl index of the start of the encoded length + * + * km, kz and kl are counted from the current offset in the + * input data. + */ + kr = (uint32_t)count & (bs - 1); + kz = ((kr + (uint32_t)len + po + bs - 1) & ~(bs - 1)) - 1 - kr; + kl = kz - 7; + km = ((kr + (uint32_t)max_len + po + bs - 1) & ~(bs - 1)) - kr; + + /* + * We must now process km bytes. For index u from 0 to km-1: + * d is from data[] if u < max_len, 0x00 otherwise + * e is an encoded length byte or 0x00, depending on u + * The tests for d and e need not be constant-time, since + * they relate only to u and max_len, not to the actual length. + * + * Actual input length is then: + * d if u < len + * 0x80 if u == len + * 0x00 if u > len and u < kl + * e if u >= kl + * + * Hash state is obtained whenever we reach a full block. This + * is the result we want if and only if u == kz. + */ + memset(tmp2, 0, sizeof tmp2); + for (u = 0; u < km; u ++) { + uint32_t v; + uint32_t d, e, x0, x1; + unsigned char x[1]; + + d = (u < max_len) ? ((const unsigned char *)data)[u] : 0x00; + v = (kr + u) & (bs - 1); + if (v >= (bs - 8)) { + unsigned j; + + j = (v - (bs - 8)) << 3; + if (be) { + e = (uint32_t)(bit_len >> (56 - j)); + } else { + e = (uint32_t)(bit_len >> j); + } + e &= 0xFF; + } else { + e = 0x00; + } + x0 = MUX(EQ(u, (uint32_t)len), 0x80, d); + x1 = MUX(LT(u, kl), 0x00, e); + x[0] = MUX(LE(u, (uint32_t)len), x0, x1); + dig->update(&hc.vtable, x, 1); + if (v == (bs - 1)) { + dig->state(&hc.vtable, tmp1); + CCOPY(EQ(u, kz), tmp2, tmp1, hlen); + } + } + + /* + * Inner hash output is in tmp2[]; we finish processing. + */ + dig->init(&hc.vtable); + dig->set_state(&hc.vtable, ctx->kso, (uint64_t)bs); + dig->update(&hc.vtable, tmp2, hlen); + dig->out(&hc.vtable, tmp2); + memcpy(out, tmp2, ctx->out_len); + return ctx->out_len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h b/lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h new file mode 100644 index 0000000000..852640d423 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/pgmspace_bearssl.h @@ -0,0 +1,64 @@ +/* PGMSPACE.H - Accessor utilities/types for accessing PROGMEM data */ + +#ifndef _PGMSPACEWRAPPER_H_ +#define _PGMSPACEWRAPPER_H_ + +#include + +#ifdef ESP8266 + +#include + +#else //!ESP8266 + +#ifndef ICACHE_RODATA_ATTR +#define ICACHE_RODATA_ATTR +#endif + +#ifndef PROGMEM +#define PROGMEM +#endif + +#ifndef PGM_P +#define PGM_P const char * +#endif + +#ifndef PGM_VOID_P +#define PGM_VOID_P const void * +#endif + +#ifndef PSTR +#define PSTR +#endif + +#ifdef __cplusplus + #define pgm_read_byte(addr) (*reinterpret_cast(addr)) + #define pgm_read_word(addr) (*reinterpret_cast(addr)) + #define pgm_read_dword(addr) (*reinterpret_cast(addr)) + #define pgm_read_float(addr) (*reinterpret_cast(addr)) + #define pgm_read_ptr(addr) (*reinterpret_cast(addr)) +#else + #define pgm_read_byte(addr) (*(const uint8_t*)(addr)) + #define pgm_read_word(addr) (*(const uint16_t*)(addr)) + #define pgm_read_dword(addr) (*(const uint32_t*)(addr)) + #define pgm_read_float(addr) (*(const float*)(addr)) + #define pgm_read_ptr(addr) (*(const void const *)(addr)) +#endif + +#define pgm_read_byte_near(addr) pgm_read_byte(addr) +#define pgm_read_word_near(addr) pgm_read_word(addr) +#define pgm_read_dword_near(addr) pgm_read_dword(addr) +#define pgm_read_float_near(addr) pgm_read_float(addr) +#define pgm_read_ptr_near(addr) pgm_read_ptr(addr) +#define pgm_read_byte_far(addr) pgm_read_byte(addr) +#define pgm_read_word_far(addr) pgm_read_word(addr) +#define pgm_read_dword_far(addr) pgm_read_dword(addr) +#define pgm_read_float_far(addr) pgm_read_float(addr) +#define pgm_read_ptr_far(addr) pgm_read_ptr(addr) + +#define memcpy_P memcpy +#define memcmp_P memcmp + +#endif // !ESP8266 + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c b/lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c new file mode 100644 index 0000000000..b3e4ff9db5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rand/aesctr_drbg.c @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_init(br_aesctr_drbg_context *ctx, + const br_block_ctr_class *aesctr, + const void *seed, size_t len) +{ + unsigned char tmp[16]; + + ctx->vtable = &br_aesctr_drbg_vtable; + memset(tmp, 0, sizeof tmp); + aesctr->init(&ctx->sk.vtable, tmp, 16); + ctx->cc = 0; + br_aesctr_drbg_update(ctx, seed, len); +} + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx, void *out, size_t len) +{ + unsigned char *buf; + unsigned char iv[12]; + + buf = out; + memset(iv, 0, sizeof iv); + while (len > 0) { + size_t clen; + + /* + * We generate data by blocks of at most 65280 bytes. This + * allows for unambiguously testing the counter overflow + * condition; also, it should work on 16-bit architectures + * (where 'size_t' is 16 bits only). + */ + clen = len; + if (clen > 65280) { + clen = 65280; + } + + /* + * We make sure that the counter won't exceed the configured + * limit. + */ + if ((uint32_t)(ctx->cc + ((clen + 15) >> 4)) > 32768) { + clen = (32768 - ctx->cc) << 4; + if (clen > len) { + clen = len; + } + } + + /* + * Run CTR. + */ + memset(buf, 0, clen); + ctx->cc = ctx->sk.vtable->run(&ctx->sk.vtable, + iv, ctx->cc, buf, clen); + buf += clen; + len -= clen; + + /* + * Every 32768 blocks, we force a state update. + */ + if (ctx->cc >= 32768) { + br_aesctr_drbg_update(ctx, NULL, 0); + } + } +} + +/* see bearssl_rand.h */ +void +br_aesctr_drbg_update(br_aesctr_drbg_context *ctx, const void *seed, size_t len) +{ + /* + * We use a Hirose construction on AES-256 to make a hash function. + * Function definition: + * - running state consists in two 16-byte blocks G and H + * - initial values of G and H are conventional + * - there is a fixed block-sized constant C + * - for next data block m: + * set AES key to H||m + * G' = E(G) xor G + * H' = E(G xor C) xor G xor C + * G <- G', H <- H' + * - once all blocks have been processed, output is H||G + * + * Constants: + * G_init = B6 B6 ... B6 + * H_init = A5 A5 ... A5 + * C = 01 00 ... 00 + * + * With this hash function h(), we compute the new state as + * follows: + * - produce a state-dependent value s as encryption of an + * all-one block with AES and the current key + * - compute the new key as the first 128 bits of h(s||seed) + * + * Original Hirose article: + * https://www.iacr.org/archive/fse2006/40470213/40470213.pdf + */ + + unsigned char s[16], iv[12]; + unsigned char G[16], H[16]; + int first; + + /* + * Use an all-one IV to get a fresh output block that depends on the + * current seed. + */ + memset(iv, 0xFF, sizeof iv); + memset(s, 0, 16); + ctx->sk.vtable->run(&ctx->sk.vtable, iv, 0xFFFFFFFF, s, 16); + + /* + * Set G[] and H[] to conventional start values. + */ + memset(G, 0xB6, sizeof G); + memset(H, 0x5A, sizeof H); + + /* + * Process the concatenation of the current state and the seed + * with the custom hash function. + */ + first = 1; + for (;;) { + unsigned char tmp[32]; + unsigned char newG[16]; + + /* + * Assemble new key H||m into tmp[]. + */ + memcpy(tmp, H, 16); + if (first) { + memcpy(tmp + 16, s, 16); + first = 0; + } else { + size_t clen; + + if (len == 0) { + break; + } + clen = len < 16 ? len : 16; + memcpy(tmp + 16, seed, clen); + memset(tmp + 16 + clen, 0, 16 - clen); + seed = (const unsigned char *)seed + clen; + len -= clen; + } + ctx->sk.vtable->init(&ctx->sk.vtable, tmp, 32); + + /* + * Compute new G and H values. + */ + memcpy(iv, G, 12); + memcpy(newG, G, 16); + ctx->sk.vtable->run(&ctx->sk.vtable, iv, + br_dec32be(G + 12), newG, 16); + iv[0] ^= 0x01; + memcpy(H, G, 16); + H[0] ^= 0x01; + ctx->sk.vtable->run(&ctx->sk.vtable, iv, + br_dec32be(G + 12), H, 16); + memcpy(G, newG, 16); + } + + /* + * Output hash value is H||G. We truncate it to its first 128 bits, + * i.e. H; that's our new AES key. + */ + ctx->sk.vtable->init(&ctx->sk.vtable, H, 16); + ctx->cc = 0; +} + +/* see bearssl_rand.h */ +const br_prng_class br_aesctr_drbg_vtable = { + sizeof(br_aesctr_drbg_context), + (void (*)(const br_prng_class **, const void *, const void *, size_t)) + &br_aesctr_drbg_init, + (void (*)(const br_prng_class **, void *, size_t)) + &br_aesctr_drbg_generate, + (void (*)(const br_prng_class **, const void *, size_t)) + &br_aesctr_drbg_update +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c b/lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c new file mode 100644 index 0000000000..77246968cf --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rand/hmac_drbg.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_hmac_drbg_init(br_hmac_drbg_context *ctx, + const br_hash_class *digest_class, const void *seed, size_t len) +{ + size_t hlen; + + ctx->vtable = &br_hmac_drbg_vtable; + hlen = br_digest_size(digest_class); + memset(ctx->K, 0x00, hlen); + memset(ctx->V, 0x01, hlen); + ctx->digest_class = digest_class; + br_hmac_drbg_update(ctx, seed, len); +} + +/* see bearssl.h */ +void +br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len) +{ + const br_hash_class *dig; + br_hmac_key_context kc; + br_hmac_context hc; + size_t hlen; + unsigned char *buf; + unsigned char x; + + dig = ctx->digest_class; + hlen = br_digest_size(dig); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + buf = out; + while (len > 0) { + size_t clen; + + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); + clen = hlen; + if (clen > len) { + clen = len; + } + memcpy(buf, ctx->V, clen); + buf += clen; + len -= clen; + } + + /* + * To prepare the state for the next request, we should call + * br_hmac_drbg_update() with an empty additional seed. However, + * we already have an initialized HMAC context with the right + * initial key, and we don't want to push another one on the + * stack, so we inline that update() call here. + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x00; + br_hmac_update(&hc, &x, 1); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); +} + +/* see bearssl.h */ +void +br_hmac_drbg_update(br_hmac_drbg_context *ctx, const void *seed, size_t len) +{ + const br_hash_class *dig; + br_hmac_key_context kc; + br_hmac_context hc; + size_t hlen; + unsigned char x; + + dig = ctx->digest_class; + hlen = br_digest_size(dig); + + /* + * 1. K = HMAC(K, V || 0x00 || seed) + */ + br_hmac_key_init(&kc, dig, ctx->K, hlen); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x00; + br_hmac_update(&hc, &x, 1); + br_hmac_update(&hc, seed, len); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + + /* + * 2. V = HMAC(K, V) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); + + /* + * 3. If the additional seed is empty, then stop here. + */ + if (len == 0) { + return; + } + + /* + * 4. K = HMAC(K, V || 0x01 || seed) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + x = 0x01; + br_hmac_update(&hc, &x, 1); + br_hmac_update(&hc, seed, len); + br_hmac_out(&hc, ctx->K); + br_hmac_key_init(&kc, dig, ctx->K, hlen); + + /* + * 5. V = HMAC(K, V) + */ + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, ctx->V, hlen); + br_hmac_out(&hc, ctx->V); +} + +/* see bearssl.h */ +const br_prng_class br_hmac_drbg_vtable PROGMEM = { + sizeof(br_hmac_drbg_context), + (void (*)(const br_prng_class **, const void *, const void *, size_t)) + &br_hmac_drbg_init, + (void (*)(const br_prng_class **, void *, size_t)) + &br_hmac_drbg_generate, + (void (*)(const br_prng_class **, const void *, size_t)) + &br_hmac_drbg_update +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c b/lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c new file mode 100644 index 0000000000..8fa4f3403c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rand/sysrng.c @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +#if BR_USE_URANDOM +#include +#include +#include +#include +#endif + +#if BR_USE_WIN32_RAND +#include +#include +#pragma comment(lib, "advapi32") +#endif + +#if BR_RDRAND +BR_TARGETS_X86_UP +BR_TARGET("rdrnd") +static int +seeder_rdrand(const br_prng_class **ctx) +{ + unsigned char tmp[32]; + size_t u; + + for (u = 0; u < sizeof tmp; u += sizeof(uint32_t)) { + int j; + uint32_t x; + + /* + * We use the 32-bit intrinsic so that code is compatible + * with both 32-bit and 64-bit architectures. + * + * Intel recommends trying at least 10 times in case of + * failure. + */ + for (j = 0; j < 10; j ++) { + if (_rdrand32_step(&x)) { + goto next_word; + } + } + return 0; + next_word: + br_enc32le(tmp + u, x); + } + (*ctx)->update(ctx, tmp, sizeof tmp); + return 1; +} +BR_TARGETS_X86_DOWN + +static int +rdrand_supported(void) +{ + /* + * The RDRND support is bit 30 of ECX, as returned by CPUID. + */ + return br_cpuid(0, 0, 0x40000000, 0); +} + +#endif + +#if BR_USE_URANDOM +static int +seeder_urandom(const br_prng_class **ctx) +{ + int f; + + f = open("/dev/urandom", O_RDONLY); + if (f >= 0) { + unsigned char tmp[32]; + size_t u; + + for (u = 0; u < sizeof tmp;) { + ssize_t len; + + len = read(f, tmp + u, (sizeof tmp) - u); + if (len < 0) { + if (errno == EINTR) { + continue; + } + break; + } + u += (size_t)len; + } + close(f); + if (u == sizeof tmp) { + (*ctx)->update(ctx, tmp, sizeof tmp); + return 1; + } + } + return 0; +} +#endif + +#if BR_USE_WIN32_RAND +static int +seeder_win32(const br_prng_class **ctx) +{ + HCRYPTPROV hp; + + if (CryptAcquireContext(&hp, 0, 0, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT | CRYPT_SILENT)) + { + BYTE buf[32]; + BOOL r; + + r = CryptGenRandom(hp, sizeof buf, buf); + CryptReleaseContext(hp, 0); + if (r) { + (*ctx)->update(ctx, buf, sizeof buf); + return 1; + } + } + return 0; +} +#endif + +#if BR_USE_ESP8266_RAND +#ifdef ESP32 +extern uint32_t esp_random(void); + +static int +seeder_esp8266(const br_prng_class **ctx) //TODO: rename/refactor it to ESP32 +{ + uint32_t tmp[32 / sizeof(uint32_t)]; + size_t i; + + for (i=0; iupdate(ctx, tmp, sizeof tmp); + + return 1; +} +#else +extern uint32_t phy_get_rand(void); // From the ESP8266 SDK + +static int +seeder_esp8266(const br_prng_class **ctx) +{ + uint32_t tmp[32 / sizeof(uint32_t)]; + size_t i; + + for (i=0; iupdate(ctx, tmp, sizeof tmp); + + return 1; +} +#endif +#endif + +/* see bearssl_rand.h */ + +br_prng_seeder +br_prng_seeder_system(const char **name) +{ +#if BR_RDRAND + if (rdrand_supported()) { + if (name != NULL) { + *name = "rdrand"; + } + return &seeder_rdrand; + } +#endif +#if BR_USE_URANDOM + if (name != NULL) { + *name = "urandom"; + } + return &seeder_urandom; +#elif BR_USE_WIN32_RAND + if (name != NULL) { + *name = "win32"; + } + return &seeder_win32; +#elif BR_USE_ESP8266_RAND + if (name != NULL) { + *name = "esp8266"; + } + return &seeder_esp8266; +#else + if (name != NULL) { + *name = "none"; + } + return 0; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c new file mode 100644 index 0000000000..d07971aa5c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_keygen.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_keygen +br_rsa_keygen_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_keygen; +#elif BR_LOMUL + return &br_rsa_i15_keygen; +#else + return &br_rsa_i31_keygen; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c new file mode 100644 index 0000000000..a4ead3b652 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_modulus.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_modulus +br_rsa_compute_modulus_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_modulus; +#else + return &br_rsa_i31_compute_modulus; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c new file mode 100644 index 0000000000..7ca825f1e1 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_decrypt.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_oaep_decrypt +br_rsa_oaep_decrypt_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_oaep_decrypt; +#elif BR_LOMUL + return &br_rsa_i15_oaep_decrypt; +#else + return &br_rsa_i31_oaep_decrypt; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c new file mode 100644 index 0000000000..518a5f4243 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_oaep_encrypt.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_oaep_encrypt +br_rsa_oaep_encrypt_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_oaep_encrypt; +#elif BR_LOMUL + return &br_rsa_i15_oaep_encrypt; +#else + return &br_rsa_i31_oaep_encrypt; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c new file mode 100644 index 0000000000..09af0624c3 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_sign.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pkcs1_sign +br_rsa_pkcs1_sign_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pkcs1_sign; +#elif BR_LOMUL + return &br_rsa_i15_pkcs1_sign; +#else + return &br_rsa_i31_pkcs1_sign; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c new file mode 100644 index 0000000000..791467d4ba --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pkcs1_vrfy.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pkcs1_vrfy +br_rsa_pkcs1_vrfy_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pkcs1_vrfy; +#elif BR_LOMUL + return &br_rsa_i15_pkcs1_vrfy; +#else + return &br_rsa_i31_pkcs1_vrfy; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c new file mode 100644 index 0000000000..8fc9e5cad9 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_priv.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_private +br_rsa_private_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_private; +#elif BR_LOMUL + return &br_rsa_i15_private; +#else + return &br_rsa_i31_private; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c new file mode 100644 index 0000000000..015ba197a7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_privexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_privexp +br_rsa_compute_privexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_privexp; +#else + return &br_rsa_i31_compute_privexp; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c new file mode 100644 index 0000000000..72549e66a6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_sign.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pss_sign +br_rsa_pss_sign_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pss_sign; +#elif BR_LOMUL + return &br_rsa_i15_pss_sign; +#else + return &br_rsa_i31_pss_sign; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c new file mode 100644 index 0000000000..1321d0a7f6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pss_vrfy.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_pss_vrfy +br_rsa_pss_vrfy_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_pss_vrfy; +#elif BR_LOMUL + return &br_rsa_i15_pss_vrfy; +#else + return &br_rsa_i31_pss_vrfy; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c new file mode 100644 index 0000000000..5d42fbd57a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pub.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_public +br_rsa_public_get_default(void) +{ +#if BR_INT128 || BR_UMUL128 + return &br_rsa_i62_public; +#elif BR_LOMUL + return &br_rsa_i15_public; +#else + return &br_rsa_i31_public; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c new file mode 100644 index 0000000000..c3ca497fd7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_default_pubexp.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +br_rsa_compute_pubexp +br_rsa_compute_pubexp_get_default(void) +{ +#if BR_LOMUL + return &br_rsa_i15_compute_pubexp; +#else + return &br_rsa_i31_compute_pubexp; +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c new file mode 100644 index 0000000000..3481b46361 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_keygen.c @@ -0,0 +1,585 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Make a random integer of the provided size. The size is encoded. + * The header word is untouched. + */ +static void +mkrand(const br_prng_class **rng, uint16_t *x, uint32_t esize) +{ + size_t u, len; + unsigned m; + + len = (esize + 15) >> 4; + (*rng)->generate(rng, x + 1, len * sizeof(uint16_t)); + for (u = 1; u < len; u ++) { + x[u] &= 0x7FFF; + } + m = esize & 15; + if (m == 0) { + x[len] &= 0x7FFF; + } else { + x[len] &= 0x7FFF >> (15 - m); + } +} + +/* + * This is the big-endian unsigned representation of the product of + * all small primes from 13 to 1481. + */ +static const unsigned char SMALL_PRIMES[] PROGMEM = { + 0x2E, 0xAB, 0x92, 0xD1, 0x8B, 0x12, 0x47, 0x31, 0x54, 0x0A, + 0x99, 0x5D, 0x25, 0x5E, 0xE2, 0x14, 0x96, 0x29, 0x1E, 0xB7, + 0x78, 0x70, 0xCC, 0x1F, 0xA5, 0xAB, 0x8D, 0x72, 0x11, 0x37, + 0xFB, 0xD8, 0x1E, 0x3F, 0x5B, 0x34, 0x30, 0x17, 0x8B, 0xE5, + 0x26, 0x28, 0x23, 0xA1, 0x8A, 0xA4, 0x29, 0xEA, 0xFD, 0x9E, + 0x39, 0x60, 0x8A, 0xF3, 0xB5, 0xA6, 0xEB, 0x3F, 0x02, 0xB6, + 0x16, 0xC3, 0x96, 0x9D, 0x38, 0xB0, 0x7D, 0x82, 0x87, 0x0C, + 0xF7, 0xBE, 0x24, 0xE5, 0x5F, 0x41, 0x04, 0x79, 0x76, 0x40, + 0xE7, 0x00, 0x22, 0x7E, 0xB5, 0x85, 0x7F, 0x8D, 0x01, 0x50, + 0xE9, 0xD3, 0x29, 0x42, 0x08, 0xB3, 0x51, 0x40, 0x7B, 0xD7, + 0x8D, 0xCC, 0x10, 0x01, 0x64, 0x59, 0x28, 0xB6, 0x53, 0xF3, + 0x50, 0x4E, 0xB1, 0xF2, 0x58, 0xCD, 0x6E, 0xF5, 0x56, 0x3E, + 0x66, 0x2F, 0xD7, 0x07, 0x7F, 0x52, 0x4C, 0x13, 0x24, 0xDC, + 0x8E, 0x8D, 0xCC, 0xED, 0x77, 0xC4, 0x21, 0xD2, 0xFD, 0x08, + 0xEA, 0xD7, 0xC0, 0x5C, 0x13, 0x82, 0x81, 0x31, 0x2F, 0x2B, + 0x08, 0xE4, 0x80, 0x04, 0x7A, 0x0C, 0x8A, 0x3C, 0xDC, 0x22, + 0xE4, 0x5A, 0x7A, 0xB0, 0x12, 0x5E, 0x4A, 0x76, 0x94, 0x77, + 0xC2, 0x0E, 0x92, 0xBA, 0x8A, 0xA0, 0x1F, 0x14, 0x51, 0x1E, + 0x66, 0x6C, 0x38, 0x03, 0x6C, 0xC7, 0x4A, 0x4B, 0x70, 0x80, + 0xAF, 0xCA, 0x84, 0x51, 0xD8, 0xD2, 0x26, 0x49, 0xF5, 0xA8, + 0x5E, 0x35, 0x4B, 0xAC, 0xCE, 0x29, 0x92, 0x33, 0xB7, 0xA2, + 0x69, 0x7D, 0x0C, 0xE0, 0x9C, 0xDB, 0x04, 0xD6, 0xB4, 0xBC, + 0x39, 0xD7, 0x7F, 0x9E, 0x9D, 0x78, 0x38, 0x7F, 0x51, 0x54, + 0x50, 0x8B, 0x9E, 0x9C, 0x03, 0x6C, 0xF5, 0x9D, 0x2C, 0x74, + 0x57, 0xF0, 0x27, 0x2A, 0xC3, 0x47, 0xCA, 0xB9, 0xD7, 0x5C, + 0xFF, 0xC2, 0xAC, 0x65, 0x4E, 0xBD +}; + +/* + * We need temporary values for at least 7 integers of the same size + * as a factor (including header word); more space helps with performance + * (in modular exponentiations), but we much prefer to remain under + * 2 kilobytes in total, to save stack space. The macro TEMPS below + * exceeds 1024 (which is a count in 16-bit words) when BR_MAX_RSA_SIZE + * is greater than 4350 (default value is 4096, so the 2-kB limit is + * maintained unless BR_MAX_RSA_SIZE was modified). + */ +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define TEMPS MAX(1024, 7 * ((((BR_MAX_RSA_SIZE + 1) >> 1) + 29) / 15)) + +/* + * Perform trial division on a candidate prime. This computes + * y = SMALL_PRIMES mod x, then tries to compute y/y mod x. The + * br_i15_moddiv() function will report an error if y is not invertible + * modulo x. Returned value is 1 on success (none of the small primes + * divides x), 0 on error (a non-trivial GCD is obtained). + * + * This function assumes that x is odd. + */ +static uint32_t +trial_divisions(const uint16_t *x, uint16_t *t) +{ + uint16_t *y; + uint16_t x0i; + unsigned char small_primes_ram[sizeof SMALL_PRIMES]; + memcpy_P(small_primes_ram, SMALL_PRIMES, sizeof SMALL_PRIMES); + + y = t; + t += 1 + ((x[0] + 15) >> 4); + x0i = br_i15_ninv15(x[1]); + br_i15_decode_reduce(y, SMALL_PRIMES, sizeof SMALL_PRIMES, x); + return br_i15_moddiv(y, y, x, x0i, t); +} + +/* + * Perform n rounds of Miller-Rabin on the candidate prime x. This + * function assumes that x = 3 mod 4. + * + * Returned value is 1 on success (all rounds completed successfully), + * 0 otherwise. + */ +static uint32_t +miller_rabin(const br_prng_class **rng, const uint16_t *x, int n, + uint16_t *t, size_t tlen) +{ + /* + * Since x = 3 mod 4, the Miller-Rabin test is simple: + * - get a random base a (such that 1 < a < x-1) + * - compute z = a^((x-1)/2) mod x + * - if z != 1 and z != x-1, the number x is composite + * + * We generate bases 'a' randomly with a size which is + * one bit less than x, which ensures that a < x-1. It + * is not useful to verify that a > 1 because the probability + * that we get a value a equal to 0 or 1 is much smaller + * than the probability of our Miller-Rabin tests not to + * detect a composite, which is already quite smaller than the + * probability of the hardware misbehaving and return a + * composite integer because of some glitch (e.g. bad RAM + * or ill-timed cosmic ray). + */ + unsigned char *xm1d2; + size_t xlen, xm1d2_len, xm1d2_len_u16, u; + uint32_t asize; + unsigned cc; + uint16_t x0i; + + /* + * Compute (x-1)/2 (encoded). + */ + xm1d2 = (unsigned char *)t; + xm1d2_len = ((x[0] - (x[0] >> 4)) + 7) >> 3; + br_i15_encode(xm1d2, xm1d2_len, x); + cc = 0; + for (u = 0; u < xm1d2_len; u ++) { + unsigned w; + + w = xm1d2[u]; + xm1d2[u] = (unsigned char)((w >> 1) | cc); + cc = w << 7; + } + + /* + * We used some words of the provided buffer for (x-1)/2. + */ + xm1d2_len_u16 = (xm1d2_len + 1) >> 1; + t += xm1d2_len_u16; + tlen -= xm1d2_len_u16; + + xlen = (x[0] + 15) >> 4; + asize = x[0] - 1 - EQ0(x[0] & 15); + x0i = br_i15_ninv15(x[1]); + while (n -- > 0) { + uint16_t *a; + uint32_t eq1, eqm1; + + /* + * Generate a random base. We don't need the base to be + * really uniform modulo x, so we just get a random + * number which is one bit shorter than x. + */ + a = t; + a[0] = x[0]; + a[xlen] = 0; + mkrand(rng, a, asize); + + /* + * Compute a^((x-1)/2) mod x. We assume here that the + * function will not fail (the temporary array is large + * enough). + */ + br_i15_modpow_opt(a, xm1d2, xm1d2_len, + x, x0i, t + 1 + xlen, tlen - 1 - xlen); + + /* + * We must obtain either 1 or x-1. Note that x is odd, + * hence x-1 differs from x only in its low word (no + * carry). + */ + eq1 = a[1] ^ 1; + eqm1 = a[1] ^ (x[1] - 1); + for (u = 2; u <= xlen; u ++) { + eq1 |= a[u]; + eqm1 |= a[u] ^ x[u]; + } + + if ((EQ0(eq1) | EQ0(eqm1)) == 0) { + return 0; + } + } + return 1; +} + +/* + * Create a random prime of the provided size. 'size' is the _encoded_ + * bit length. The two top bits and the two bottom bits are set to 1. + */ +static void +mkprime(const br_prng_class **rng, uint16_t *x, uint32_t esize, + uint32_t pubexp, uint16_t *t, size_t tlen) +{ + size_t len; + + x[0] = esize; + len = (esize + 15) >> 4; + for (;;) { + size_t u; + uint32_t m3, m5, m7, m11; + int rounds; + + /* + * Generate random bits. We force the two top bits and the + * two bottom bits to 1. + */ + mkrand(rng, x, esize); + if ((esize & 15) == 0) { + x[len] |= 0x6000; + } else if ((esize & 15) == 1) { + x[len] |= 0x0001; + x[len - 1] |= 0x4000; + } else { + x[len] |= 0x0003 << ((esize & 15) - 2); + } + x[1] |= 0x0003; + + /* + * Trial division with low primes (3, 5, 7 and 11). We + * use the following properties: + * + * 2^2 = 1 mod 3 + * 2^4 = 1 mod 5 + * 2^3 = 1 mod 7 + * 2^10 = 1 mod 11 + */ + m3 = 0; + m5 = 0; + m7 = 0; + m11 = 0; + for (u = 0; u < len; u ++) { + uint32_t w; + + w = x[1 + u]; + m3 += w << (u & 1); + m3 = (m3 & 0xFF) + (m3 >> 8); + m5 += w << ((4 - u) & 3); + m5 = (m5 & 0xFF) + (m5 >> 8); + m7 += w; + m7 = (m7 & 0x1FF) + (m7 >> 9); + m11 += w << (5 & -(u & 1)); + m11 = (m11 & 0x3FF) + (m11 >> 10); + } + + /* + * Maximum values of m* at this point: + * m3: 511 + * m5: 2310 + * m7: 510 + * m11: 2047 + * We use the same properties to make further reductions. + */ + + m3 = (m3 & 0x0F) + (m3 >> 4); /* max: 46 */ + m3 = (m3 & 0x0F) + (m3 >> 4); /* max: 16 */ + m3 = ((m3 * 43) >> 5) & 3; + + m5 = (m5 & 0xFF) + (m5 >> 8); /* max: 263 */ + m5 = (m5 & 0x0F) + (m5 >> 4); /* max: 30 */ + m5 = (m5 & 0x0F) + (m5 >> 4); /* max: 15 */ + m5 -= 10 & -GT(m5, 9); + m5 -= 5 & -GT(m5, 4); + + m7 = (m7 & 0x3F) + (m7 >> 6); /* max: 69 */ + m7 = (m7 & 7) + (m7 >> 3); /* max: 14 */ + m7 = ((m7 * 147) >> 7) & 7; + + /* + * 2^5 = 32 = -1 mod 11. + */ + m11 = (m11 & 0x1F) + 66 - (m11 >> 5); /* max: 97 */ + m11 -= 88 & -GT(m11, 87); + m11 -= 44 & -GT(m11, 43); + m11 -= 22 & -GT(m11, 21); + m11 -= 11 & -GT(m11, 10); + + /* + * If any of these modulo is 0, then the candidate is + * not prime. Also, if pubexp is 3, 5, 7 or 11, and the + * corresponding modulus is 1, then the candidate must + * be rejected, because we need e to be invertible + * modulo p-1. We can use simple comparisons here + * because they won't leak information on a candidate + * that we keep, only on one that we reject (and is thus + * not secret). + */ + if (m3 == 0 || m5 == 0 || m7 == 0 || m11 == 0) { + continue; + } + if ((pubexp == 3 && m3 == 1) + || (pubexp == 5 && m5 == 1) + || (pubexp == 7 && m7 == 1) + || (pubexp == 11 && m11 == 1)) + { + continue; + } + + /* + * More trial divisions. + */ + if (!trial_divisions(x, t)) { + continue; + } + + /* + * Miller-Rabin algorithm. Since we selected a random + * integer, not a maliciously crafted integer, we can use + * relatively few rounds to lower the risk of a false + * positive (i.e. declaring prime a non-prime) under + * 2^(-80). It is not useful to lower the probability much + * below that, since that would be substantially below + * the probability of the hardware misbehaving. Sufficient + * numbers of rounds are extracted from the Handbook of + * Applied Cryptography, note 4.49 (page 149). + * + * Since we work on the encoded size (esize), we need to + * compare with encoded thresholds. + */ + if (esize < 320) { + rounds = 12; + } else if (esize < 480) { + rounds = 9; + } else if (esize < 693) { + rounds = 6; + } else if (esize < 906) { + rounds = 4; + } else if (esize < 1386) { + rounds = 3; + } else { + rounds = 2; + } + + if (miller_rabin(rng, x, rounds, t, tlen)) { + return; + } + } +} + +/* + * Let p be a prime (p > 2^33, p = 3 mod 4). Let m = (p-1)/2, provided + * as parameter (with announced bit length equal to that of p). This + * function computes d = 1/e mod p-1 (for an odd integer e). Returned + * value is 1 on success, 0 on error (an error is reported if e is not + * invertible modulo p-1). + * + * The temporary buffer (t) must have room for at least 4 integers of + * the size of p. + */ +static uint32_t +invert_pubexp(uint16_t *d, const uint16_t *m, uint32_t e, uint16_t *t) +{ + uint16_t *f; + uint32_t r; + + f = t; + t += 1 + ((m[0] + 15) >> 4); + + /* + * Compute d = 1/e mod m. Since p = 3 mod 4, m is odd. + */ + br_i15_zero(d, m[0]); + d[1] = 1; + br_i15_zero(f, m[0]); + f[1] = e & 0x7FFF; + f[2] = (e >> 15) & 0x7FFF; + f[3] = e >> 30; + r = br_i15_moddiv(d, f, m, br_i15_ninv15(m[1]), t); + + /* + * We really want d = 1/e mod p-1, with p = 2m. By the CRT, + * the result is either the d we got, or d + m. + * + * Let's write e*d = 1 + k*m, for some integer k. Integers e + * and m are odd. If d is odd, then e*d is odd, which implies + * that k must be even; in that case, e*d = 1 + (k/2)*2m, and + * thus d is already fine. Conversely, if d is even, then k + * is odd, and we must add m to d in order to get the correct + * result. + */ + br_i15_add(d, m, (uint32_t)(1 - (d[1] & 1))); + + return r; +} + +/* + * Swap two buffers in RAM. They must be disjoint. + */ +static void +bufswap(void *b1, void *b2, size_t len) +{ + size_t u; + unsigned char *buf1, *buf2; + + buf1 = b1; + buf2 = b2; + for (u = 0; u < len; u ++) { + unsigned w; + + w = buf1[u]; + buf1[u] = buf2[u]; + buf2[u] = w; + } +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_keygen(const br_prng_class **rng, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp) +{ + uint32_t esize_p, esize_q; + size_t plen, qlen, tlen; + uint16_t *p, *q, *t; + uint16_t tmp[TEMPS]; + uint32_t r; + + if (size < BR_MIN_RSA_SIZE || size > BR_MAX_RSA_SIZE) { + return 0; + } + if (pubexp == 0) { + pubexp = 3; + } else if (pubexp == 1 || (pubexp & 1) == 0) { + return 0; + } + + esize_p = (size + 1) >> 1; + esize_q = size - esize_p; + sk->n_bitlen = size; + sk->p = kbuf_priv; + sk->plen = (esize_p + 7) >> 3; + sk->q = sk->p + sk->plen; + sk->qlen = (esize_q + 7) >> 3; + sk->dp = sk->q + sk->qlen; + sk->dplen = sk->plen; + sk->dq = sk->dp + sk->dplen; + sk->dqlen = sk->qlen; + sk->iq = sk->dq + sk->dqlen; + sk->iqlen = sk->plen; + + if (pk != NULL) { + pk->n = kbuf_pub; + pk->nlen = (size + 7) >> 3; + pk->e = pk->n + pk->nlen; + pk->elen = 4; + br_enc32be(pk->e, pubexp); + while (*pk->e == 0) { + pk->e ++; + pk->elen --; + } + } + + /* + * We now switch to encoded sizes. + * + * floor((x * 17477) / (2^18)) is equal to floor(x/15) for all + * integers x from 0 to 23833. + */ + esize_p += MUL15(esize_p, 17477) >> 18; + esize_q += MUL15(esize_q, 17477) >> 18; + plen = (esize_p + 15) >> 4; + qlen = (esize_q + 15) >> 4; + p = tmp; + q = p + 1 + plen; + t = q + 1 + qlen; + tlen = ((sizeof tmp) / sizeof(uint16_t)) - (2 + plen + qlen); + + /* + * When looking for primes p and q, we temporarily divide + * candidates by 2, in order to compute the inverse of the + * public exponent. + */ + + for (;;) { + mkprime(rng, p, esize_p, pubexp, t, tlen); + br_i15_rshift(p, 1); + if (invert_pubexp(t, p, pubexp, t + 1 + plen)) { + br_i15_add(p, p, 1); + p[1] |= 1; + br_i15_encode(sk->p, sk->plen, p); + br_i15_encode(sk->dp, sk->dplen, t); + break; + } + } + + for (;;) { + mkprime(rng, q, esize_q, pubexp, t, tlen); + br_i15_rshift(q, 1); + if (invert_pubexp(t, q, pubexp, t + 1 + qlen)) { + br_i15_add(q, q, 1); + q[1] |= 1; + br_i15_encode(sk->q, sk->qlen, q); + br_i15_encode(sk->dq, sk->dqlen, t); + break; + } + } + + /* + * If p and q have the same size, then it is possible that q > p + * (when the target modulus size is odd, we generate p with a + * greater bit length than q). If q > p, we want to swap p and q + * (and also dp and dq) for two reasons: + * - The final step below (inversion of q modulo p) is easier if + * p > q. + * - While BearSSL's RSA code is perfectly happy with RSA keys such + * that p < q, some other implementations have restrictions and + * require p > q. + * + * Note that we can do a simple non-constant-time swap here, + * because the only information we leak here is that we insist on + * returning p and q such that p > q, which is not a secret. + */ + if (esize_p == esize_q && br_i15_sub(p, q, 0) == 1) { + bufswap(p, q, (1 + plen) * sizeof *p); + bufswap(sk->p, sk->q, sk->plen); + bufswap(sk->dp, sk->dq, sk->dplen); + } + + /* + * We have produced p, q, dp and dq. We can now compute iq = 1/d mod p. + * + * We ensured that p >= q, so this is just a matter of updating the + * header word for q (and possibly adding an extra word). + * + * Theoretically, the call below may fail, in case we were + * extraordinarily unlucky, and p = q. Another failure case is if + * Miller-Rabin failed us _twice_, and p and q are non-prime and + * have a factor is common. We report the error mostly because it + * is cheap and we can, but in practice this never happens (or, at + * least, it happens way less often than hardware glitches). + */ + q[0] = p[0]; + if (plen > qlen) { + q[plen] = 0; + t ++; + tlen --; + } + br_i15_zero(t, p[0]); + t[1] = 1; + r = br_i15_moddiv(t, q, p, br_i15_ninv15(p[1]), t + 1 + plen); + br_i15_encode(sk->iq, sk->iqlen, t); + + /* + * Compute the public modulus too, if required. + */ + if (pk != NULL) { + br_i15_zero(t, p[0]); + br_i15_mulacc(t, p, q); + br_i15_encode(pk->n, pk->nlen, t); + } + + return r; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c new file mode 100644 index 0000000000..6a35329370 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_modulus.c @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk) +{ + uint16_t tmp[4 * (((BR_MAX_RSA_SIZE / 2) + 14) / 15) + 5]; + uint16_t *t, *p, *q; + const unsigned char *pbuf, *qbuf; + size_t nlen, plen, qlen, tlen; + + /* + * Compute actual byte and lengths for p and q. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + + t = tmp; + tlen = (sizeof tmp) / (sizeof tmp[0]); + + /* + * Decode p. + */ + if ((15 * tlen) < (plen << 3) + 15) { + return 0; + } + br_i15_decode(t, pbuf, plen); + p = t; + plen = (p[0] + 31) >> 4; + t += plen; + tlen -= plen; + + /* + * Decode q. + */ + if ((15 * tlen) < (qlen << 3) + 15) { + return 0; + } + br_i15_decode(t, qbuf, qlen); + q = t; + qlen = (q[0] + 31) >> 4; + t += qlen; + tlen -= qlen; + + /* + * Computation can proceed only if we have enough room for the + * modulus. + */ + if (tlen < (plen + qlen + 1)) { + return 0; + } + + /* + * Private key already contains the modulus bit length, from which + * we can infer the output length. Even if n is NULL, we still had + * to decode p and q to make sure that the product can be computed. + */ + nlen = (sk->n_bitlen + 7) >> 3; + if (n != NULL) { + br_i15_zero(t, p[0]); + br_i15_mulacc(t, p, q); + br_i15_encode(n, nlen, t); + } + return nlen; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c new file mode 100644 index 0000000000..3ace2a9683 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_decrypt.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_oaep_decrypt(const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len) +{ + uint32_t r; + + if (*len != ((sk->n_bitlen + 7) >> 3)) { + return 0; + } + r = br_rsa_i15_private(data, sk); + r &= br_rsa_oaep_unpad(dig, label, label_len, data, len); + return r; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c new file mode 100644 index 0000000000..76a1d9d620 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_oaep_encrypt.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len) +{ + size_t dlen; + + dlen = br_rsa_oaep_pad(rnd, dig, label, label_len, + pk, dst, dst_max_len, src, src_len); + if (dlen == 0) { + return 0; + } + return dlen & -(size_t)br_rsa_i15_public(dst, dlen, pk); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c new file mode 100644 index 0000000000..a77f56feb7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_sign.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x) +{ + if (!br_rsa_pkcs1_sig_pad(hash_oid, hash, hash_len, sk->n_bitlen, x)) { + return 0; + } + return br_rsa_i15_private(x, sk); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c new file mode 100644 index 0000000000..78e76632cc --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pkcs1_vrfy.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out) +{ + unsigned char sig[BR_MAX_RSA_SIZE >> 3]; + + if (xlen > (sizeof sig)) { + return 0; + } + memcpy(sig, x, xlen); + if (!br_rsa_i15_public(sig, xlen, pk)) { + return 0; + } + return br_rsa_pkcs1_sig_unpad(sig, xlen, hash_oid, hash_len, hash_out); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c new file mode 100644 index 0000000000..b456a069e8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_priv.c @@ -0,0 +1,215 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define U (2 + ((BR_MAX_RSA_FACTOR + 14) / 15)) +#define TLEN (8 * U) + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_private(unsigned char *x, const br_rsa_private_key *sk) +{ + const unsigned char *p, *q; + size_t plen, qlen; + size_t fwlen; + uint16_t p0i, q0i; + size_t xlen, u; + uint16_t tmp[1 + TLEN]; + long z; + uint16_t *mp, *mq, *s1, *s2, *t1, *t2, *t3; + uint32_t r; + + /* + * Compute the actual lengths of p and q, in bytes. + * These lengths are not considered secret (we cannot really hide + * them anyway in constant-time code). + */ + p = sk->p; + plen = sk->plen; + while (plen > 0 && *p == 0) { + p ++; + plen --; + } + q = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *q == 0) { + q ++; + qlen --; + } + + /* + * Compute the maximum factor length, in words. + */ + z = (long)(plen > qlen ? plen : qlen) << 3; + fwlen = 1; + while (z > 0) { + z -= 15; + fwlen ++; + } + /* + * Round up the word length to an even number. + */ + fwlen += (fwlen & 1); + + /* + * We need to fit at least 6 values in the stack buffer. + */ + if (6 * fwlen > TLEN) { + return 0; + } + + /* + * Compute signature length (in bytes). + */ + xlen = (sk->n_bitlen + 7) >> 3; + + /* + * Ensure 32-bit alignment for value words. + */ + mq = tmp; + if (((uintptr_t)mq & 2) == 0) { + mq ++; + } + + /* + * Decode q. + */ + br_i15_decode(mq, q, qlen); + + /* + * Decode p. + */ + t1 = mq + fwlen; + br_i15_decode(t1, p, plen); + + /* + * Compute the modulus (product of the two factors), to compare + * it with the source value. We use br_i15_mulacc(), since it's + * already used later on. + */ + t2 = mq + 2 * fwlen; + br_i15_zero(t2, mq[0]); + br_i15_mulacc(t2, mq, t1); + + /* + * We encode the modulus into bytes, to perform the comparison + * with bytes. We know that the product length, in bytes, is + * exactly xlen. + * The comparison actually computes the carry when subtracting + * the modulus from the source value; that carry must be 1 for + * a value in the correct range. We keep it in r, which is our + * accumulator for the error code. + */ + t3 = mq + 4 * fwlen; + br_i15_encode(t3, xlen, t2); + u = xlen; + r = 0; + while (u > 0) { + uint32_t wn, wx; + + u --; + wn = ((unsigned char *)t3)[u]; + wx = x[u]; + r = ((wx - (wn + r)) >> 8) & 1; + } + + /* + * Move the decoded p to another temporary buffer. + */ + mp = mq + 2 * fwlen; + memmove(mp, t1, fwlen * sizeof *t1); + + stack_thunk_yield(); + + /* + * Compute s2 = x^dq mod q. + */ + q0i = br_i15_ninv15(mq[1]); + s2 = mq + fwlen; + br_i15_decode_reduce(s2, x, xlen, mq); + r &= br_i15_modpow_opt(s2, sk->dq, sk->dqlen, mq, q0i, + mq + 3 * fwlen, TLEN - 3 * fwlen); + + stack_thunk_yield(); + + /* + * Compute s1 = x^dq mod q. + */ + p0i = br_i15_ninv15(mp[1]); + s1 = mq + 3 * fwlen; + br_i15_decode_reduce(s1, x, xlen, mp); + r &= br_i15_modpow_opt(s1, sk->dp, sk->dplen, mp, p0i, + mq + 4 * fwlen, TLEN - 4 * fwlen); + + /* + * Compute: + * h = (s1 - s2)*(1/q) mod p + * s1 is an integer modulo p, but s2 is modulo q. PKCS#1 is + * unclear about whether p may be lower than q (some existing, + * widely deployed implementations of RSA don't tolerate p < q), + * but we want to support that occurrence, so we need to use the + * reduction function. + * + * Since we use br_i15_decode_reduce() for iq (purportedly, the + * inverse of q modulo p), we also tolerate improperly large + * values for this parameter. + */ + t1 = mq + 4 * fwlen; + t2 = mq + 5 * fwlen; + br_i15_reduce(t2, s2, mp); + br_i15_add(s1, mp, br_i15_sub(s1, t2, 1)); + br_i15_to_monty(s1, mp); + br_i15_decode_reduce(t1, sk->iq, sk->iqlen, mp); + br_i15_montymul(t2, s1, t1, mp, p0i); + + stack_thunk_yield(); + + /* + * h is now in t2. We compute the final result: + * s = s2 + q*h + * All these operations are non-modular. + * + * We need mq, s2 and t2. We use the t3 buffer as destination. + * The buffers mp, s1 and t1 are no longer needed, so we can + * reuse them for t3. Moreover, the first step of the computation + * is to copy s2 into t3, after which s2 is not needed. Right + * now, mq is in slot 0, s2 is in slot 1, and t2 in slot 5. + * Therefore, we have ample room for t3 by simply using s2. + */ + t3 = s2; + br_i15_mulacc(t3, mq, t2); + + /* + * Encode the result. Since we already checked the value of xlen, + * we can just use it right away. + */ + br_i15_encode(x, xlen, t3); + + /* + * The only error conditions remaining at that point are invalid + * values for p and q (even integers). + */ + return p0i & q0i & r; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c new file mode 100644 index 0000000000..a7a98f1ed5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_privexp.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +size_t +br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t e) +{ + /* + * We want to invert e modulo phi = (p-1)(q-1). This first + * requires computing phi, which is easy since we have the factors + * p and q in the private key structure. + * + * Since p = 3 mod 4 and q = 3 mod 4, phi/4 is an odd integer. + * We could invert e modulo phi/4 then patch the result to + * modulo phi, but this would involve assembling three modulus-wide + * values (phi/4, 1 and e) and calling moddiv, that requires + * three more temporaries, for a total of six big integers, or + * slightly more than 3 kB of stack space for RSA-4096. This + * exceeds our stack requirements. + * + * Instead, we first use one step of the extended GCD: + * + * - We compute phi = k*e + r (Euclidean division of phi by e). + * If public exponent e is correct, then r != 0 (e must be + * invertible modulo phi). We also have k != 0 since we + * enforce non-ridiculously-small factors. + * + * - We find small u, v such that u*e - v*r = 1 (using a + * binary GCD; we can arrange for u < r and v < e, i.e. all + * values fit on 32 bits). + * + * - Solution is: d = u + v*k + * This last computation is exact: since u < r and v < e, + * the above implies d < r + e*((phi-r)/e) = phi + */ + + uint16_t tmp[4 * ((BR_MAX_RSA_FACTOR + 14) / 15) + 12]; + uint16_t *p, *q, *k, *m, *z, *phi; + const unsigned char *pbuf, *qbuf; + size_t plen, qlen, u, len, dlen; + uint32_t r, a, b, u0, v0, u1, v1, he, hr; + int i; + + /* + * Check that e is correct. + */ + if (e < 3 || (e & 1) == 0) { + return 0; + } + + /* + * Check lengths of p and q, and that they are both odd. + */ + pbuf = sk->p; + plen = sk->plen; + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8) + || (pbuf[plen - 1] & 1) != 1) + { + return 0; + } + qbuf = sk->q; + qlen = sk->qlen; + while (qlen > 0 && *qbuf == 0) { + qbuf ++; + qlen --; + } + if (qlen < 5 || qlen > (BR_MAX_RSA_FACTOR / 8) + || (qbuf[qlen - 1] & 1) != 1) + { + return 0; + } + + /* + * Output length is that of the modulus. + */ + dlen = (sk->n_bitlen + 7) >> 3; + if (d == NULL) { + return dlen; + } + + p = tmp; + br_i15_decode(p, pbuf, plen); + plen = (p[0] + 15) >> 4; + q = p + 1 + plen; + br_i15_decode(q, qbuf, qlen); + qlen = (q[0] + 15) >> 4; + + /* + * Compute phi = (p-1)*(q-1), then move it over p-1 and q-1 (that + * we do not need anymore). The mulacc function sets the announced + * bit length of t to be the sum of the announced bit lengths of + * p-1 and q-1, which is usually exact but may overshoot by one 1 + * bit in some cases; we readjust it to its true length. + */ + p[1] --; + q[1] --; + phi = q + 1 + qlen; + br_i15_zero(phi, p[0]); + br_i15_mulacc(phi, p, q); + len = (phi[0] + 15) >> 4; + memmove(tmp, phi, (1 + len) * sizeof *phi); + phi = tmp; + phi[0] = br_i15_bit_length(phi + 1, len); + len = (phi[0] + 15) >> 4; + + /* + * Divide phi by public exponent e. The final remainder r must be + * non-zero (otherwise, the key is invalid). The quotient is k, + * which we write over phi, since we don't need phi after that. + */ + r = 0; + for (u = len; u >= 1; u --) { + /* + * Upon entry, r < e, and phi[u] < 2^15; hence, + * hi:lo < e*2^15. Thus, the produced word k[u] + * must be lower than 2^15, and the new remainder r + * is lower than e. + */ + uint32_t hi, lo; + + hi = r >> 17; + lo = (r << 15) + phi[u]; + phi[u] = br_divrem(hi, lo, e, &r); + } + if (r == 0) { + return 0; + } + k = phi; + + /* + * Compute u and v such that u*e - v*r = GCD(e,r). We use + * a binary GCD algorithm, with 6 extra integers a, b, + * u0, u1, v0 and v1. Initial values are: + * a = e u0 = 1 v0 = 0 + * b = r u1 = r v1 = e-1 + * The following invariants are maintained: + * a = u0*e - v0*r + * b = u1*e - v1*r + * 0 < a <= e + * 0 < b <= r + * 0 <= u0 <= r + * 0 <= v0 <= e + * 0 <= u1 <= r + * 0 <= v1 <= e + * + * At each iteration, we reduce either a or b by one bit, and + * adjust u0, u1, v0 and v1 to maintain the invariants: + * - if a is even, then a <- a/2 + * - otherwise, if b is even, then b <- b/2 + * - otherwise, if a > b, then a <- (a-b)/2 + * - otherwise, if b > a, then b <- (b-a)/2 + * Algorithm stops when a = b. At that point, the common value + * is the GCD of e and r; it must be 1 (otherwise, the private + * key or public exponent is not valid). The (u0,v0) or (u1,v1) + * pairs are the solution we are looking for. + * + * Since either a or b is reduced by at least 1 bit at each + * iteration, 62 iterations are enough to reach the end + * condition. + * + * To maintain the invariants, we must compute the same operations + * on the u* and v* values that we do on a and b: + * - When a is divided by 2, u0 and v0 must be divided by 2. + * - When b is divided by 2, u1 and v1 must be divided by 2. + * - When b is subtracted from a, u1 and v1 are subtracted from + * u0 and v0, respectively. + * - When a is subtracted from b, u0 and v0 are subtracted from + * u1 and v1, respectively. + * + * However, we want to keep the u* and v* values in their proper + * ranges. The following remarks apply: + * + * - When a is divided by 2, then a is even. Therefore: + * + * * If r is odd, then u0 and v0 must have the same parity; + * if they are both odd, then adding r to u0 and e to v0 + * makes them both even, and the division by 2 brings them + * back to the proper range. + * + * * If r is even, then u0 must be even; if v0 is odd, then + * adding r to u0 and e to v0 makes them both even, and the + * division by 2 brings them back to the proper range. + * + * Thus, all we need to do is to look at the parity of v0, + * and add (r,e) to (u0,v0) when v0 is odd. In order to avoid + * a 32-bit overflow, we can add ((r+1)/2,(e/2)+1) after the + * division (r+1 does not overflow since r < e; and (e/2)+1 + * is equal to (e+1)/2 since e is odd). + * + * - When we subtract b from a, three cases may occur: + * + * * u1 <= u0 and v1 <= v0: just do the subtractions + * + * * u1 > u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * * u1 <= u0 and v1 > v0: compute: + * (u0, v0) <- (u0 + r - u1, v0 + e - v1) + * + * The fourth case (u1 > u0 and v1 <= v0) is not possible + * because it would contradict "b < a" (which is the reason + * why we subtract b from a). + * + * The tricky case is the third one: from the equations, it + * seems that u0 may go out of range. However, the invariants + * and ranges of other values imply that, in that case, the + * new u0 does not actually exceed the range. + * + * We can thus handle the subtraction by adding (r,e) based + * solely on the comparison between v0 and v1. + */ + a = e; + b = r; + u0 = 1; + v0 = 0; + u1 = r; + v1 = e - 1; + hr = (r + 1) >> 1; + he = (e >> 1) + 1; + for (i = 0; i < 62; i ++) { + uint32_t oa, ob, agtb, bgta; + uint32_t sab, sba, da, db; + uint32_t ctl; + + oa = a & 1; /* 1 if a is odd */ + ob = b & 1; /* 1 if b is odd */ + agtb = GT(a, b); /* 1 if a > b */ + bgta = GT(b, a); /* 1 if b > a */ + + sab = oa & ob & agtb; /* 1 if a <- a-b */ + sba = oa & ob & bgta; /* 1 if b <- b-a */ + + /* a <- a-b, u0 <- u0-u1, v0 <- v0-v1 */ + ctl = GT(v1, v0); + a -= b & -sab; + u0 -= (u1 - (r & -ctl)) & -sab; + v0 -= (v1 - (e & -ctl)) & -sab; + + /* b <- b-a, u1 <- u1-u0 mod r, v1 <- v1-v0 mod e */ + ctl = GT(v0, v1); + b -= a & -sba; + u1 -= (u0 - (r & -ctl)) & -sba; + v1 -= (v0 - (e & -ctl)) & -sba; + + da = NOT(oa) | sab; /* 1 if a <- a/2 */ + db = (oa & NOT(ob)) | sba; /* 1 if b <- b/2 */ + + /* a <- a/2, u0 <- u0/2, v0 <- v0/2 */ + ctl = v0 & 1; + a ^= (a ^ (a >> 1)) & -da; + u0 ^= (u0 ^ ((u0 >> 1) + (hr & -ctl))) & -da; + v0 ^= (v0 ^ ((v0 >> 1) + (he & -ctl))) & -da; + + /* b <- b/2, u1 <- u1/2 mod r, v1 <- v1/2 mod e */ + ctl = v1 & 1; + b ^= (b ^ (b >> 1)) & -db; + u1 ^= (u1 ^ ((u1 >> 1) + (hr & -ctl))) & -db; + v1 ^= (v1 ^ ((v1 >> 1) + (he & -ctl))) & -db; + } + + /* + * Check that the GCD is indeed 1. If not, then the key is invalid + * (and there's no harm in leaking that piece of information). + */ + if (a != 1) { + return 0; + } + + /* + * Now we have u0*e - v0*r = 1. Let's compute the result as: + * d = u0 + v0*k + * We still have k in the tmp[] array, and its announced bit + * length is that of phi. + */ + m = k + 1 + len; + m[0] = (2 << 4) + 2; /* bit length is 32 bits, encoded */ + m[1] = v0 & 0x7FFF; + m[2] = (v0 >> 15) & 0x7FFF; + m[3] = v0 >> 30; + z = m + 4; + br_i15_zero(z, k[0]); + z[1] = u0 & 0x7FFF; + z[2] = (u0 >> 15) & 0x7FFF; + z[3] = u0 >> 30; + br_i15_mulacc(z, k, m); + + /* + * Encode the result. + */ + br_i15_encode(d, dlen, z); + return dlen; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c new file mode 100644 index 0000000000..09664e30e6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_sign.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x) +{ + if (!br_rsa_pss_sig_pad(rng, hf_data, hf_mgf1, hash, + salt_len, sk->n_bitlen, x)) + { + return 0; + } + return br_rsa_i15_private(x, sk); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c new file mode 100644 index 0000000000..de5d701954 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pss_vrfy.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk) +{ + unsigned char sig[BR_MAX_RSA_SIZE >> 3]; + + if (xlen > (sizeof sig)) { + return 0; + } + memcpy(sig, x, xlen); + if (!br_rsa_i15_public(sig, xlen, pk)) { + return 0; + } + return br_rsa_pss_sig_unpad(hf_data, hf_mgf1, + hash, salt_len, pk, sig); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c new file mode 100644 index 0000000000..2fa78312b8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pub.c @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * As a strict minimum, we need four buffers that can hold a + * modular integer. + */ +#define TLEN (4 * (2 + ((BR_MAX_RSA_SIZE + 14) / 15))) + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk) +{ + const unsigned char *n; + size_t nlen; + uint16_t tmp[1 + TLEN]; + uint16_t *m, *a, *t; + size_t fwlen; + long z; + uint16_t m0i; + uint32_t r; + + /* + * Get the actual length of the modulus, and see if it fits within + * our stack buffer. We also check that the length of x[] is valid. + */ + n = pk->n; + nlen = pk->nlen; + while (nlen > 0 && pgm_read_byte(n) == 0) { + n ++; + nlen --; + } + if (nlen == 0 || nlen > (BR_MAX_RSA_SIZE >> 3) || xlen != nlen) { + return 0; + } + z = (long)nlen << 3; + fwlen = 1; + while (z > 0) { + z -= 15; + fwlen ++; + } + /* + * Round up length to an even number. + */ + fwlen += (fwlen & 1); + + /* + * The modulus gets decoded into m[]. + * The value to exponentiate goes into a[]. + * The temporaries for modular exponentiations are in t[]. + * + * We want the first value word of each integer to be aligned + * on a 32-bit boundary. + */ + m = tmp; + if (((uintptr_t)m & 2) == 0) { + m ++; + } + a = m + fwlen; + t = m + 2 * fwlen; + + /* + * Decode the modulus. + */ + br_i15_decode(m, n, nlen); + m0i = br_i15_ninv15(m[1]); + + /* + * Note: if m[] is even, then m0i == 0. Otherwise, m0i must be + * an odd integer. + */ + r = m0i & 1; + + /* + * Decode x[] into a[]; we also check that its value is proper. + */ + r &= br_i15_decode_mod(a, x, xlen, m); + + /* + * Compute the modular exponentiation. + */ + br_i15_modpow_opt(a, pk->e, pk->elen, m, m0i, t, TLEN - 2 * fwlen); + + /* + * Encode the result. + */ + br_i15_encode(x, xlen, a); + return r; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c new file mode 100644 index 0000000000..b17299656f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_i15_pubexp.c @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Recompute public exponent, based on factor p and reduced private + * exponent dp. + */ +static uint32_t +get_pubexp(const unsigned char *pbuf, size_t plen, + const unsigned char *dpbuf, size_t dplen) +{ + /* + * dp is the inverse of e modulo p-1. If p = 3 mod 4, then + * p-1 = 2*((p-1)/2). Taken modulo 2, e is odd and has inverse 1; + * thus, dp must be odd. + * + * We compute the inverse of dp modulo (p-1)/2. This requires + * first reducing dp modulo (p-1)/2 (this can be done with a + * conditional subtract, no need to use the generic modular + * reduction function); then, we use moddiv. + */ + + uint16_t tmp[6 * ((BR_MAX_RSA_FACTOR + 29) / 15)]; + uint16_t *p, *dp, *x; + size_t len; + uint32_t e; + + /* + * Compute actual factor length (in bytes) and check that it fits + * under our size constraints. + */ + while (plen > 0 && *pbuf == 0) { + pbuf ++; + plen --; + } + if (plen == 0 || plen < 5 || plen > (BR_MAX_RSA_FACTOR / 8)) { + return 0; + } + + /* + * Compute actual reduced exponent length (in bytes) and check that + * it is not longer than p. + */ + while (dplen > 0 && *dpbuf == 0) { + dpbuf ++; + dplen --; + } + if (dplen > plen || dplen == 0 + || (dplen == plen && dpbuf[0] > pbuf[0])) + { + return 0; + } + + /* + * Verify that p = 3 mod 4 and that dp is odd. + */ + if ((pbuf[plen - 1] & 3) != 3 || (dpbuf[dplen - 1] & 1) != 1) { + return 0; + } + + /* + * Decode p and compute (p-1)/2. + */ + p = tmp; + br_i15_decode(p, pbuf, plen); + len = (p[0] + 31) >> 4; + br_i15_rshift(p, 1); + + /* + * Decode dp and make sure its announced bit length matches that of + * p (we already know that the size of dp, in bits, does not exceed + * the size of p, so we just have to copy the header word). + */ + dp = p + len; + memset(dp, 0, len * sizeof *dp); + br_i15_decode(dp, dpbuf, dplen); + dp[0] = p[0]; + + /* + * Subtract (p-1)/2 from dp if necessary. + */ + br_i15_sub(dp, p, NOT(br_i15_sub(dp, p, 0))); + + /* + * If another subtraction is needed, then this means that the + * value was invalid. We don't care to leak information about + * invalid keys. + */ + if (br_i15_sub(dp, p, 0) == 0) { + return 0; + } + + /* + * Invert dp modulo (p-1)/2. If the inversion fails, then the + * key value was invalid. + */ + x = dp + len; + br_i15_zero(x, p[0]); + x[1] = 1; + if (br_i15_moddiv(x, dp, p, br_i15_ninv15(p[1]), x + len) == 0) { + return 0; + } + + /* + * We now have an inverse. We must set it to zero (error) if its + * length is greater than 32 bits and/or if it is an even integer. + * Take care that the bit_length function returns an encoded + * bit length. + */ + e = (uint32_t)x[1] | ((uint32_t)x[2] << 15) | ((uint32_t)x[3] << 30); + e &= -LT(br_i15_bit_length(x + 1, len - 1), 35); + e &= -(e & 1); + return e; +} + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk) +{ + /* + * Get the public exponent from both p and q. This is the right + * exponent if we get twice the same value. + */ + uint32_t ep, eq; + + ep = get_pubexp(sk->p, sk->plen, sk->dp, sk->dplen); + eq = get_pubexp(sk->q, sk->qlen, sk->dq, sk->dqlen); + return ep & -EQ(ep, eq); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c new file mode 100644 index 0000000000..57fd9d78d9 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_pad.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Hash some data. This is put as a separate function so that stack + * allocation of the hash function context is done only for the duration + * of the hash. + */ +static void +hash_data(const br_hash_class *dig, void *dst, const void *src, size_t len) +{ + br_hash_compat_context hc; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, src, len); + dig->out(&hc.vtable, dst); +} + +/* see inner.h */ +size_t +br_rsa_oaep_pad(const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len) +{ + size_t k, hlen; + unsigned char *buf; + + hlen = br_digest_size(dig); + + /* + * Compute actual modulus length (in bytes). + */ + k = pk->nlen; + while (k > 0 && pk->n[k - 1] == 0) { + k --; + } + + /* + * An error is reported if: + * - the modulus is too short; + * - the source message length is too long; + * - the destination buffer is too short. + */ + if (k < ((hlen << 1) + 2) + || src_len > (k - (hlen << 1) - 2) + || dst_max_len < k) + { + return 0; + } + + /* + * Apply padding. At this point, things cannot fail. + */ + buf = dst; + + /* + * Assemble: DB = lHash || PS || 0x01 || M + * We first place the source message M with memmove(), so that + * overlaps between source and destination buffers are supported. + */ + memmove(buf + k - src_len, src, src_len); + hash_data(dig, buf + 1 + hlen, label, label_len); + memset(buf + 1 + (hlen << 1), 0, k - src_len - (hlen << 1) - 2); + buf[k - src_len - 1] = 0x01; + + /* + * Make the random seed. + */ + (*rnd)->generate(rnd, buf + 1, hlen); + + /* + * Mask DB with the mask generated from the seed. + */ + br_mgf1_xor(buf + 1 + hlen, k - hlen - 1, dig, buf + 1, hlen); + + /* + * Mask the seed with the mask generated from the masked DB. + */ + br_mgf1_xor(buf + 1, hlen, dig, buf + 1 + hlen, k - hlen - 1); + + /* + * Padding result: EM = 0x00 || maskedSeed || maskedDB. + */ + buf[0] = 0x00; + return k; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c new file mode 100644 index 0000000000..a6558dccc6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_oaep_unpad.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Hash some data and XOR the result into the provided buffer. This is put + * as a separate function so that stack allocation of the hash function + * context is done only for the duration of the hash. + */ +static void +xor_hash_data(const br_hash_class *dig, void *dst, const void *src, size_t len) +{ + br_hash_compat_context hc; + unsigned char tmp[64]; + unsigned char *buf; + size_t u, hlen; + + hc.vtable = dig; + dig->init(&hc.vtable); + dig->update(&hc.vtable, src, len); + dig->out(&hc.vtable, tmp); + buf = dst; + hlen = br_digest_size(dig); + for (u = 0; u < hlen; u ++) { + buf[u] ^= tmp[u]; + } +} + +/* see inner.h */ +uint32_t +br_rsa_oaep_unpad(const br_hash_class *dig, + const void *label, size_t label_len, + void *data, size_t *len) +{ + size_t u, k, hlen; + unsigned char *buf; + uint32_t r, s, zlen; + + hlen = br_digest_size(dig); + k = *len; + buf = data; + + /* + * There must be room for the padding. + */ + if (k < ((hlen << 1) + 2)) { + return 0; + } + + /* + * Unmask the seed, then the DB value. + */ + br_mgf1_xor(buf + 1, hlen, dig, buf + 1 + hlen, k - hlen - 1); + br_mgf1_xor(buf + 1 + hlen, k - hlen - 1, dig, buf + 1, hlen); + + /* + * Hash the label and XOR it with the value in the array; if + * they are equal then these should yield only zeros. + */ + xor_hash_data(dig, buf + 1 + hlen, label, label_len); + + /* + * At that point, if the padding was correct, when we should + * have: 0x00 || seed || 0x00 ... 0x00 0x01 || M + * Padding is valid as long as: + * - There is at least hlen+1 leading bytes of value 0x00. + * - There is at least one non-zero byte. + * - The first (leftmost) non-zero byte has value 0x01. + * + * Ultimately, we may leak the resulting message length, i.e. + * the position of the byte of value 0x01, but we must take care + * to do so only if the number of zero bytes has been verified + * to be at least hlen+1. + * + * The loop below counts the number of bytes of value 0x00, and + * checks that the next byte has value 0x01, in constant-time. + * + * - If the initial byte (before the seed) is not 0x00, then + * r and s are set to 0, and stay there. + * - Value r is 1 until the first non-zero byte is reached + * (after the seed); it switches to 0 at that point. + * - Value s is set to 1 if and only if the data encountered + * at the time of the transition of r from 1 to 0 has value + * exactly 0x01. + * - Value zlen counts the number of leading bytes of value zero + * (after the seed). + */ + r = 1 - ((buf[0] + 0xFF) >> 8); + s = 0; + zlen = 0; + for (u = hlen + 1; u < k; u ++) { + uint32_t w, nz; + + w = buf[u]; + + /* + * nz == 1 only for the first non-zero byte. + */ + nz = r & ((w + 0xFF) >> 8); + s |= nz & EQ(w, 0x01); + r &= NOT(nz); + zlen += r; + } + + /* + * Padding is correct only if s == 1, _and_ zlen >= hlen. + */ + s &= GE(zlen, (uint32_t)hlen); + + /* + * At that point, padding was verified, and we are now allowed + * to make conditional jumps. + */ + if (s) { + size_t plen; + + plen = 2 + hlen + zlen; + k -= plen; + memmove(buf, buf + plen, k); + *len = k; + } + return s; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c new file mode 100644 index 0000000000..6150be550f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_pad.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pkcs1_sig_pad(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + uint32_t n_bitlen, unsigned char *x) +{ + size_t u, x3, xlen; + + /* + * Padded hash value has format: + * 00 01 FF .. FF 00 30 x1 30 x2 06 x3 OID 05 00 04 x4 HASH + * + * with the following rules: + * + * -- Total length is equal to the modulus length (unsigned + * encoding). + * + * -- There must be at least eight bytes of value 0xFF. + * + * -- x4 is equal to the hash length (hash_len). + * + * -- x3 is equal to the encoded OID value length (hash_oid[0]). + * + * -- x2 = x3 + 4. + * + * -- x1 = x2 + x4 + 4 = x3 + x4 + 8. + * + * Note: the "05 00" is optional (signatures with and without + * that sequence exist in practice), but notes in PKCS#1 seem to + * indicate that the presence of that sequence (specifically, + * an ASN.1 NULL value for the hash parameters) may be slightly + * more "standard" than the opposite. + */ + xlen = (n_bitlen + 7) >> 3; + + if (hash_oid == NULL) { + if (xlen < hash_len + 11) { + return 0; + } + x[0] = 0x00; + x[1] = 0x01; + u = xlen - hash_len; + memset(x + 2, 0xFF, u - 3); + x[u - 1] = 0x00; + } else { + x3 = hash_oid[0]; + + /* + * Check that there is enough room for all the elements, + * including at least eight bytes of value 0xFF. + */ + if (xlen < (x3 + hash_len + 21)) { + return 0; + } + x[0] = 0x00; + x[1] = 0x01; + u = xlen - x3 - hash_len - 11; + memset(x + 2, 0xFF, u - 2); + x[u] = 0x00; + x[u + 1] = 0x30; + x[u + 2] = x3 + hash_len + 8; + x[u + 3] = 0x30; + x[u + 4] = x3 + 4; + x[u + 5] = 0x06; + memcpy(x + u + 6, hash_oid, x3 + 1); + u += x3 + 7; + x[u ++] = 0x05; + x[u ++] = 0x00; + x[u ++] = 0x04; + x[u ++] = hash_len; + } + memcpy(x + u, hash, hash_len); + return 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c new file mode 100644 index 0000000000..08a5532863 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pkcs1_sig_unpad.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_pkcs1_sig_unpad(const unsigned char *sig, size_t sig_len, + const unsigned char *hash_oid, size_t hash_len, + unsigned char *hash_out) +{ + static const unsigned char pad1[] = { + 0x00, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + + unsigned char pad2[43]; + size_t u, x2, x3, pad_len, zlen; + + if (sig_len < 11) { + return 0; + } + + /* + * Expected format: + * 00 01 FF ... FF 00 30 x1 30 x2 06 x3 OID [ 05 00 ] 04 x4 HASH + * + * with the following rules: + * + * -- Total length is that of the modulus and the signature + * (this was already verified by br_rsa_i31_public()). + * + * -- There are at least eight bytes of value 0xFF. + * + * -- x4 is equal to the hash length (hash_len). + * + * -- x3 is equal to the encoded OID value length (so x3 is the + * first byte of hash_oid[]). + * + * -- If the "05 00" is present, then x2 == x3 + 4; otherwise, + * x2 == x3 + 2. + * + * -- x1 == x2 + x4 + 4. + * + * So the total length after the last "FF" is either x3 + x4 + 11 + * (with the "05 00") or x3 + x4 + 9 (without the "05 00"). + */ + + /* + * Check the "00 01 FF .. FF 00" with at least eight 0xFF bytes. + * The comparison is valid because we made sure that the signature + * is at least 11 bytes long. + */ + if (memcmp(sig, pad1, sizeof pad1) != 0) { + return 0; + } + for (u = sizeof pad1; u < sig_len; u ++) { + if (sig[u] != 0xFF) { + break; + } + } + + /* + * Remaining length is sig_len - u bytes (including the 00 just + * after the last FF). This must be equal to one of the two + * possible values (depending on whether the "05 00" sequence is + * present or not). + */ + if (hash_oid == NULL) { + if (sig_len - u != hash_len + 1 || sig[u] != 0x00) { + return 0; + } + } else { + x3 = hash_oid[0]; + pad_len = x3 + 9; + memset(pad2, 0, pad_len); + zlen = sig_len - u - hash_len; + if (zlen == pad_len) { + x2 = x3 + 2; + } else if (zlen == pad_len + 2) { + x2 = x3 + 4; + pad_len = zlen; + pad2[pad_len - 4] = 0x05; + } else { + return 0; + } + pad2[1] = 0x30; + pad2[2] = x2 + hash_len + 4; + pad2[3] = 0x30; + pad2[4] = x2; + pad2[5] = 0x06; + memcpy(pad2 + 6, hash_oid, x3 + 1); + pad2[pad_len - 2] = 0x04; + pad2[pad_len - 1] = hash_len; + if (memcmp(pad2, sig + u, pad_len) != 0) { + return 0; + } + } + memcpy(hash_out, sig + sig_len - hash_len, hash_len); + return 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c new file mode 100644 index 0000000000..c65def3056 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_pad.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pss_sig_pad(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + uint32_t n_bitlen, unsigned char *x) +{ + size_t xlen, hash_len; + br_hash_compat_context hc; + unsigned char *salt, *seed; + + hash_len = br_digest_size(hf_data); + + /* + * The padded string is one bit smaller than the modulus; + * notably, if the modulus length is equal to 1 modulo 8, then + * the padded string will be one _byte_ smaller, and the first + * byte will be set to 0. We apply these transformations here. + */ + n_bitlen --; + if ((n_bitlen & 7) == 0) { + *x ++ = 0; + } + xlen = (n_bitlen + 7) >> 3; + + /* + * Check that the modulus is large enough for the hash value + * length combined with the intended salt length. + */ + if (hash_len > xlen || salt_len > xlen + || (hash_len + salt_len + 2) > xlen) + { + return 0; + } + + /* + * Produce a random salt. + */ + salt = x + xlen - hash_len - salt_len - 1; + if (salt_len != 0) { + (*rng)->generate(rng, salt, salt_len); + } + + /* + * Compute the seed for MGF1. + */ + seed = x + xlen - hash_len - 1; + hf_data->init(&hc.vtable); + memset(seed, 0, 8); + hf_data->update(&hc.vtable, seed, 8); + hf_data->update(&hc.vtable, hash, hash_len); + hf_data->update(&hc.vtable, salt, salt_len); + hf_data->out(&hc.vtable, seed); + + /* + * Prepare string PS (padded salt). The salt is already at the + * right place. + */ + memset(x, 0, xlen - salt_len - hash_len - 2); + x[xlen - salt_len - hash_len - 2] = 0x01; + + /* + * Generate the mask and XOR it into PS. + */ + br_mgf1_xor(x, xlen - hash_len - 1, hf_mgf1, seed, hash_len); + + /* + * Clear the top bits to ensure the value is lower than the + * modulus. + */ + x[0] &= 0xFF >> (((uint32_t)xlen << 3) - n_bitlen); + + /* + * The seed (H) is already in the right place. We just set the + * last byte. + */ + x[xlen - 1] = 0xBC; + + return 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c new file mode 100644 index 0000000000..ba81018a3c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_pss_sig_unpad.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +uint32_t +br_rsa_pss_sig_unpad(const br_hash_class *hf_data, + const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_public_key *pk, unsigned char *x) +{ + size_t u, xlen, hash_len; + br_hash_compat_context hc; + unsigned char *seed, *salt; + unsigned char tmp[64]; + uint32_t r, n_bitlen; + + hash_len = br_digest_size(hf_data); + + /* + * Value r will be set to a non-zero value is any test fails. + */ + r = 0; + + /* + * The value bit length (as an integer) must be strictly less than + * that of the modulus. + */ + for (u = 0; u < pk->nlen; u ++) { + if (pk->n[u] != 0) { + break; + } + } + if (u == pk->nlen) { + return 0; + } + n_bitlen = BIT_LENGTH(pk->n[u]) + ((uint32_t)(pk->nlen - u - 1) << 3); + n_bitlen --; + if ((n_bitlen & 7) == 0) { + r |= *x ++; + } else { + r |= x[0] & (0xFF << (n_bitlen & 7)); + } + xlen = (n_bitlen + 7) >> 3; + + /* + * Check that the modulus is large enough for the hash value + * length combined with the intended salt length. + */ + if (hash_len > xlen || salt_len > xlen + || (hash_len + salt_len + 2) > xlen) + { + return 0; + } + + /* + * Check value of rightmost byte. + */ + r |= x[xlen - 1] ^ 0xBC; + + /* + * Generate the mask and XOR it into the first bytes to reveal PS; + * we must also mask out the leading bits. + */ + seed = x + xlen - hash_len - 1; + br_mgf1_xor(x, xlen - hash_len - 1, hf_mgf1, seed, hash_len); + if ((n_bitlen & 7) != 0) { + x[0] &= 0xFF >> (8 - (n_bitlen & 7)); + } + + /* + * Check that all padding bytes have the expected value. + */ + for (u = 0; u < (xlen - hash_len - salt_len - 2); u ++) { + r |= x[u]; + } + r |= x[xlen - hash_len - salt_len - 2] ^ 0x01; + + /* + * Recompute H. + */ + salt = x + xlen - hash_len - salt_len - 1; + hf_data->init(&hc.vtable); + memset(tmp, 0, 8); + hf_data->update(&hc.vtable, tmp, 8); + hf_data->update(&hc.vtable, hash, hash_len); + hf_data->update(&hc.vtable, salt, salt_len); + hf_data->out(&hc.vtable, tmp); + + /* + * Check that the recomputed H value matches the one appearing + * in the string. + */ + for (u = 0; u < hash_len; u ++) { + r |= tmp[u] ^ x[(xlen - hash_len - 1) + u]; + } + + return EQ0(r); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c new file mode 100644 index 0000000000..a45fc7b278 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/rsa/rsa_ssl_decrypt.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_rsa.h */ +uint32_t +br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk, + unsigned char *data, size_t len) +{ + uint32_t x; + size_t u; + + /* + * A first check on length. Since this test works only on the + * buffer length, it needs not (and cannot) be constant-time. + */ + if (len < 59 || len != (sk->n_bitlen + 7) >> 3) { + return 0; + } + x = core(data, sk); + + x &= EQ(data[0], 0x00); + x &= EQ(data[1], 0x02); + for (u = 2; u < (len - 49); u ++) { + x &= NEQ(data[u], 0); + } + x &= EQ(data[len - 49], 0x00); + memmove(data, data + len - 48, 48); + return x; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/settings.c b/lib/lib_ssl/bearssl-esp8266/src/settings.c new file mode 100644 index 0000000000..18e9991f64 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/settings.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const br_config_option config[] = { + { "BR_64", +#if BR_64 + 1 +#else + 0 +#endif + }, + { "BR_AES_X86NI", +#if BR_AES_X86NI + 1 +#else + 0 +#endif + }, + { "BR_amd64", +#if BR_amd64 + 1 +#else + 0 +#endif + }, + { "BR_ARMEL_CORTEXM_GCC", +#if BR_ARMEL_CORTEXM_GCC + 1 +#else + 0 +#endif + }, + { "BR_BE_UNALIGNED", +#if BR_BE_UNALIGNED + 1 +#else + 0 +#endif + }, + { "BR_CLANG", +#if BR_CLANG + 1 +#else + 0 +#endif + }, + { "BR_CLANG_3_7", +#if BR_CLANG_3_7 + 1 +#else + 0 +#endif + }, + { "BR_CLANG_3_8", +#if BR_CLANG_3_8 + 1 +#else + 0 +#endif + }, + { "BR_CT_MUL15", +#if BR_CT_MUL15 + 1 +#else + 0 +#endif + }, + { "BR_CT_MUL31", +#if BR_CT_MUL31 + 1 +#else + 0 +#endif + }, + { "BR_GCC", +#if BR_GCC + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_4", +#if BR_GCC_4_4 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_5", +#if BR_GCC_4_5 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_6", +#if BR_GCC_4_6 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_7", +#if BR_GCC_4_7 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_8", +#if BR_GCC_4_8 + 1 +#else + 0 +#endif + }, + { "BR_GCC_4_9", +#if BR_GCC_4_9 + 1 +#else + 0 +#endif + }, + { "BR_GCC_5_0", +#if BR_GCC_5_0 + 1 +#else + 0 +#endif + }, + { "BR_i386", +#if BR_i386 + 1 +#else + 0 +#endif + }, + { "BR_INT128", +#if BR_INT128 + 1 +#else + 0 +#endif + }, + { "BR_LE_UNALIGNED", +#if BR_LE_UNALIGNED + 1 +#else + 0 +#endif + }, + { "BR_LOMUL", +#if BR_LOMUL + 1 +#else + 0 +#endif + }, + { "BR_MAX_EC_SIZE", BR_MAX_EC_SIZE }, + { "BR_MAX_RSA_SIZE", BR_MAX_RSA_SIZE }, + { "BR_MAX_RSA_FACTOR", BR_MAX_RSA_FACTOR }, + { "BR_MSC", +#if BR_MSC + 1 +#else + 0 +#endif + }, + { "BR_MSC_2005", +#if BR_MSC_2005 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2008", +#if BR_MSC_2008 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2010", +#if BR_MSC_2010 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2012", +#if BR_MSC_2012 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2013", +#if BR_MSC_2013 + 1 +#else + 0 +#endif + }, + { "BR_MSC_2015", +#if BR_MSC_2015 + 1 +#else + 0 +#endif + }, + { "BR_POWER8", +#if BR_POWER8 + 1 +#else + 0 +#endif + }, + { "BR_RDRAND", +#if BR_RDRAND + 1 +#else + 0 +#endif + }, + { "BR_ESP8266_RAND", +#if BR_USE_ESP8266_RAND + 1 +#else + 0 +#endif + }, + { "BR_SLOW_MUL", +#if BR_SLOW_MUL + 1 +#else + 0 +#endif + }, + { "BR_SLOW_MUL15", +#if BR_SLOW_MUL15 + 1 +#else + 0 +#endif + }, + { "BR_SSE2", +#if BR_SSE2 + 1 +#else + 0 +#endif + }, + { "BR_UMUL128", +#if BR_UMUL128 + 1 +#else + 0 +#endif + }, + { "BR_USE_UNIX_TIME", +#if BR_USE_UNIX_TIME + 1 +#else + 0 +#endif + }, + { "BR_USE_WIN32_RAND", +#if BR_USE_WIN32_RAND + 1 +#else + 0 +#endif + }, + { "BR_USE_WIN32_TIME", +#if BR_USE_WIN32_TIME + 1 +#else + 0 +#endif + }, + + { NULL, 0 } +}; + +/* see bearssl.h */ +const br_config_option * +br_get_config(void) +{ + return config; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c new file mode 100644 index 0000000000..113097213e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_tls_phash(void *dst, size_t len, + const br_hash_class *dig, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + unsigned char *buf; + unsigned char tmp[64], a[64]; + br_hmac_key_context kc; + br_hmac_context hc; + size_t label_len, hlen, u; + + if (len == 0) { + return; + } + buf = dst; + for (label_len = 0; label[label_len]; label_len ++); + hlen = br_digest_size(dig); + br_hmac_key_init(&kc, dig, secret, secret_len); + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, label, label_len); + for (u = 0; u < seed_num; u ++) { + br_hmac_update(&hc, seed[u].data, seed[u].len); + } + br_hmac_out(&hc, a); + for (;;) { + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, a, hlen); + br_hmac_update(&hc, label, label_len); + for (u = 0; u < seed_num; u ++) { + br_hmac_update(&hc, seed[u].data, seed[u].len); + } + br_hmac_out(&hc, tmp); + for (u = 0; u < hlen && u < len; u ++) { + buf[u] ^= tmp[u]; + } + buf += u; + len -= u; + if (len == 0) { + return; + } + br_hmac_init(&hc, &kc, 0); + br_hmac_update(&hc, a, hlen); + br_hmac_out(&hc, a); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c new file mode 100644 index 0000000000..37e3021f13 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_md5sha1.c @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls10_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + const unsigned char *s1; + size_t slen; + + s1 = secret; + slen = (secret_len + 1) >> 1; + memset(dst, 0, len); + br_tls_phash(dst, len, &br_md5_vtable, + s1, slen, label, seed_num, seed); + br_tls_phash(dst, len, &br_sha1_vtable, + s1 + secret_len - slen, slen, label, seed_num, seed); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c new file mode 100644 index 0000000000..dfb8309e7a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha256.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls12_sha256_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + memset(dst, 0, len); + br_tls_phash(dst, len, &br_sha256_vtable, + secret, secret_len, label, seed_num, seed); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c new file mode 100644 index 0000000000..3b0955002a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/prf_sha384.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl.h */ +void +br_tls12_sha384_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed) +{ + memset(dst, 0, len); + br_tls_phash(dst, len, &br_sha384_vtable, + secret, secret_len, label, seed_num, seed); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c new file mode 100644 index 0000000000..7e9530cd00 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_ec.c @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +cc_none0(const br_ssl_client_certificate_class **pctx) +{ + (void)pctx; +} + +static void +cc_none1(const br_ssl_client_certificate_class **pctx, size_t len) +{ + (void)pctx; + (void)len; +} + +static void +cc_none2(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len) +{ + (void)pctx; + (void)data; + (void)len; +} + +static void +cc_choose(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices) +{ + br_ssl_client_certificate_ec_context *zc; + int x; + int scurve; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + scurve = br_ssl_client_get_server_curve(cc); + + if ((zc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && scurve == zc->sk->curve) + { + int x; + + x = (zc->issuer_key_type == BR_KEYTYPE_RSA) ? 16 : 17; + if (((auth_types >> x) & 1) != 0) { + choices->auth_type = BR_AUTH_ECDH; + choices->hash_id = -1; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; + } + } + + /* + * For ECDSA authentication, we must choose an appropriate + * hash function. + */ + x = br_ssl_choose_hash((unsigned)(auth_types >> 8)); + if (x == 0 || (zc->allowed_usages & BR_KEYTYPE_SIGN) == 0) { + memset(choices, 0, sizeof *choices); + return; + } + choices->auth_type = BR_AUTH_ECDSA; + choices->hash_id = x; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; +} + +static uint32_t +cc_do_keyx(const br_ssl_client_certificate_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_client_certificate_ec_context *zc; + uint32_t r; + size_t xoff, xlen; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + r = zc->iec->mul(data, *len, zc->sk->x, zc->sk->xlen, zc->sk->curve); + xoff = zc->iec->xoff(zc->sk->curve, &xlen); + memmove(data, data + xoff, xlen); + *len = xlen; + return r; +} + +static size_t +cc_do_sign(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len) +{ + br_ssl_client_certificate_ec_context *zc; + unsigned char hv[64]; + const br_hash_class *hc; + + zc = (br_ssl_client_certificate_ec_context *)pctx; + memcpy(hv, data, hv_len); + hc = br_multihash_getimpl(zc->mhash, hash_id); + if (hc == NULL) { + return 0; + } + if (len < 139) { + return 0; + } + return zc->iecdsa(zc->iec, hc, hv, zc->sk, data); +} + +static const br_ssl_client_certificate_class ccert_vtable PROGMEM = { + sizeof(br_ssl_client_certificate_ec_context), + cc_none0, /* start_name_list */ + cc_none1, /* start_name */ + cc_none2, /* append_name */ + cc_none0, /* end_name */ + cc_none0, /* end_name_list */ + cc_choose, + cc_do_keyx, + cc_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_single_ec(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa) +{ + cc->client_auth.single_ec.vtable = &ccert_vtable; + cc->client_auth.single_ec.chain = chain; + cc->client_auth.single_ec.chain_len = chain_len; + cc->client_auth.single_ec.sk = sk; + cc->client_auth.single_ec.allowed_usages = allowed_usages; + cc->client_auth.single_ec.issuer_key_type = cert_issuer_key_type; + cc->client_auth.single_ec.mhash = &cc->eng.mhash; + cc->client_auth.single_ec.iec = iec; + cc->client_auth.single_ec.iecdsa = iecdsa; + cc->client_auth_vtable = &cc->client_auth.single_ec.vtable; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c new file mode 100644 index 0000000000..d74bf111de --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_ccert_single_rsa.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +cc_none0(const br_ssl_client_certificate_class **pctx) +{ + (void)pctx; +} + +static void +cc_none1(const br_ssl_client_certificate_class **pctx, size_t len) +{ + (void)pctx; + (void)len; +} + +static void +cc_none2(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len) +{ + (void)pctx; + (void)data; + (void)len; +} + +static void +cc_choose(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices) +{ + br_ssl_client_certificate_rsa_context *zc; + int x; + + (void)cc; + zc = (br_ssl_client_certificate_rsa_context *)pctx; + x = br_ssl_choose_hash((unsigned)auth_types); + if (x == 0 && (auth_types & 1) == 0) { + memset(choices, 0, sizeof *choices); + } + choices->auth_type = BR_AUTH_RSA; + choices->hash_id = x; + choices->chain = zc->chain; + choices->chain_len = zc->chain_len; +} + +/* + * OID for hash functions in RSA signatures. + */ +#if 0 +static const unsigned char HASH_OID_SHA1[] = { + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +static const unsigned char HASH_OID_SHA224[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +static const unsigned char HASH_OID_SHA256[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +static const unsigned char HASH_OID_SHA384[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +static const unsigned char HASH_OID_SHA512[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; +#endif +// EFP3 - use copy of these present in another file. GCC won't merge local static arrays, even if they're equal, it seems +extern const unsigned char HASH_OID_SHA1[]; +extern const unsigned char HASH_OID_SHA224[]; +extern const unsigned char HASH_OID_SHA256[]; +extern const unsigned char HASH_OID_SHA384[]; +extern const unsigned char HASH_OID_SHA512[]; + +static const unsigned char *HASH_OID[] PROGMEM = { + HASH_OID_SHA1, + HASH_OID_SHA224, + HASH_OID_SHA256, + HASH_OID_SHA384, + HASH_OID_SHA512 +}; + +static size_t +cc_do_sign(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len) +{ + br_ssl_client_certificate_rsa_context *zc; + unsigned char hv[64]; + const unsigned char *hash_oid; + size_t sig_len; + + zc = (br_ssl_client_certificate_rsa_context *)pctx; + memcpy(hv, data, hv_len); + if (hash_id == 0) { + hash_oid = NULL; + } else if (hash_id >= 2 && hash_id <= 6) { + hash_oid = HASH_OID[hash_id - 2]; + } else { + return 0; + } + sig_len = (zc->sk->n_bitlen + 7) >> 3; + if (len < sig_len) { + return 0; + } + return zc->irsasign(hash_oid, hv, hv_len, zc->sk, data) ? sig_len : 0; +} + +static const br_ssl_client_certificate_class ccert_vtable PROGMEM = { + sizeof(br_ssl_client_certificate_rsa_context), + cc_none0, /* start_name_list */ + cc_none1, /* start_name */ + cc_none2, /* append_name */ + cc_none0, /* end_name */ + cc_none0, /* end_name_list */ + cc_choose, + 0, + cc_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_single_rsa(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, br_rsa_pkcs1_sign irsasign) +{ + cc->client_auth.single_rsa.vtable = &ccert_vtable; + cc->client_auth.single_rsa.chain = chain; + cc->client_auth.single_rsa.chain_len = chain_len; + cc->client_auth.single_rsa.sk = sk; + cc->client_auth.single_rsa.irsasign = irsasign; + cc->client_auth_vtable = &cc->client_auth.single_rsa.vtable; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c new file mode 100644 index 0000000000..73010c43c2 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_zero(br_ssl_client_context *cc) +{ + /* + * For really standard C, we should explicitly set to NULL all + * pointers, and 0 all other fields. However, on all our target + * architectures, a direct memset() will work, be faster, and + * use a lot less code. + */ + memset(cc, 0, sizeof *cc); +} + +/* see bearssl_ssl.h */ +int +br_ssl_client_reset(br_ssl_client_context *cc, + const char *server_name, int resume_session) +{ + size_t n; + + br_ssl_engine_set_buffer(&cc->eng, NULL, 0, 0); + cc->eng.version_out = cc->eng.version_min; + if (!resume_session) { + br_ssl_client_forget_session(cc); + } + if (!br_ssl_engine_init_rand(&cc->eng)) { + return 0; + } + + /* + * We always set back the "reneg" flag to 0 because we use it + * to distinguish between first handshake and renegotiation. + * Note that "renegotiation" and "session resumption" are two + * different things. + */ + cc->eng.reneg = 0; + + if (server_name == NULL) { + cc->eng.server_name[0] = 0; + } else { + n = strlen(server_name) + 1; + if (n > sizeof cc->eng.server_name) { + br_ssl_engine_fail(&cc->eng, BR_ERR_BAD_PARAM); + return 0; + } + memcpy(cc->eng.server_name, server_name, n); + } + + br_ssl_engine_hs_reset(&cc->eng, + br_ssl_hs_client_init_main, br_ssl_hs_client_run); + return br_ssl_engine_last_error(&cc->eng) == BR_ERR_OK; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c new file mode 100644 index 0000000000..0cedecdb99 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_default_rsapub.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_set_default_rsapub(br_ssl_client_context *cc) +{ + br_ssl_client_set_rsapub(cc, br_rsa_public_get_default()); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c new file mode 100644 index 0000000000..a7755862ce --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_client_full.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_client_init_full(br_ssl_client_context *cc, + br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + /* + * The "full" profile supports all implemented cipher suites. + * + * Rationale for suite order, from most important to least + * important rule: + * + * -- Don't use 3DES if AES or ChaCha20 is available. + * -- Try to have Forward Secrecy (ECDHE suite) if possible. + * -- When not using Forward Secrecy, ECDH key exchange is + * better than RSA key exchange (slightly more expensive on the + * client, but much cheaper on the server, and it implies smaller + * messages). + * -- ChaCha20+Poly1305 is better than AES/GCM (faster, smaller code). + * -- GCM is better than CCM and CBC. CCM is better than CBC. + * -- CCM is preferable over CCM_8 (with CCM_8, forgeries may succeed + * with probability 2^(-64)). + * -- AES-128 is preferred over AES-256 (AES-128 is already + * strong enough, and AES-256 is 40% more expensive). + */ + static const uint16_t suites[] = { + BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_RSA_WITH_AES_128_GCM_SHA256, + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_RSA_WITH_AES_128_CCM, + BR_TLS_RSA_WITH_AES_256_CCM, + BR_TLS_RSA_WITH_AES_128_CCM_8, + BR_TLS_RSA_WITH_AES_256_CCM_8, + BR_TLS_RSA_WITH_AES_128_CBC_SHA256, + BR_TLS_RSA_WITH_AES_256_CBC_SHA256, + BR_TLS_RSA_WITH_AES_128_CBC_SHA, + BR_TLS_RSA_WITH_AES_256_CBC_SHA, + BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA, + BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA + }; + + /* + * All hash functions are activated. + * Note: the X.509 validation engine will nonetheless refuse to + * validate signatures that use MD5 as hash function. + */ + static const br_hash_class *hashes[] = { + &br_md5_vtable, + &br_sha1_vtable, + &br_sha224_vtable, + &br_sha256_vtable, + &br_sha384_vtable, + &br_sha512_vtable + }; + + int id; + + /* + * Reset client context and set supported versions from TLS-1.0 + * to TLS-1.2 (inclusive). + */ + br_ssl_client_zero(cc); + br_ssl_engine_set_versions(&cc->eng, BR_TLS10, BR_TLS12); + + /* + * X.509 engine uses SHA-256 to hash certificate DN (for + * comparisons). + */ + br_x509_minimal_init(xc, &br_sha256_vtable, + trust_anchors, trust_anchors_num); + + /* + * Set suites and asymmetric crypto implementations. We use the + * "i31" code for RSA (it is somewhat faster than the "i32" + * implementation). + * TODO: change that when better implementations are made available. + */ + br_ssl_engine_set_suites(&cc->eng, suites, + (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); + br_ssl_engine_set_default_ecdsa(&cc->eng); + br_x509_minimal_set_rsa(xc, br_ssl_engine_get_rsavrfy(&cc->eng)); + br_x509_minimal_set_ecdsa(xc, + br_ssl_engine_get_ec(&cc->eng), + br_ssl_engine_get_ecdsa(&cc->eng)); + + /* + * Set supported hash functions, for the SSL engine and for the + * X.509 engine. + */ + for (id = br_md5_ID; id <= br_sha512_ID; id ++) { + const br_hash_class *hc; + + hc = hashes[id - 1]; + br_ssl_engine_set_hash(&cc->eng, id, hc); + br_x509_minimal_set_hash(xc, id, hc); + } + + /* + * Link the X.509 engine in the SSL engine. + */ + br_ssl_engine_set_x509(&cc->eng, &xc->vtable); + + /* + * Set the PRF implementations. + */ + br_ssl_engine_set_prf10(&cc->eng, &br_tls10_prf); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + br_ssl_engine_set_prf_sha384(&cc->eng, &br_tls12_sha384_prf); + + /* + * Symmetric encryption. We use the "default" implementations + * (fastest among constant-time implementations). + */ + br_ssl_engine_set_default_aes_cbc(&cc->eng); + br_ssl_engine_set_default_aes_ccm(&cc->eng); + br_ssl_engine_set_default_aes_gcm(&cc->eng); + br_ssl_engine_set_default_des_cbc(&cc->eng); + br_ssl_engine_set_default_chapol(&cc->eng); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c new file mode 100644 index 0000000000..efd3eabfd7 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine.c @@ -0,0 +1,1584 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#if 0 +/* obsolete */ + +/* + * If BR_USE_URANDOM is not defined, then try to autodetect its presence + * through compiler macros. + */ +#ifndef BR_USE_URANDOM + +/* + * Macro values documented on: + * https://sourceforge.net/p/predef/wiki/OperatingSystems/ + * + * Only the most common systems have been included here for now. This + * should be enriched later on. + */ +#if defined _AIX \ + || defined __ANDROID__ \ + || defined __FreeBSD__ \ + || defined __NetBSD__ \ + || defined __OpenBSD__ \ + || defined __DragonFly__ \ + || defined __linux__ \ + || (defined __sun && (defined __SVR4 || defined __svr4__)) \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_URANDOM 1 +#endif + +#endif + +/* + * If BR_USE_WIN32_RAND is not defined, perform autodetection here. + */ +#ifndef BR_USE_WIN32_RAND + +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_RAND 1 +#endif + +#endif + +#if BR_USE_URANDOM +#include +#include +#include +#include +#endif + +#if BR_USE_WIN32_RAND +#include +#include +#pragma comment(lib, "advapi32") +#endif + +#endif + +/* ==================================================================== */ +/* + * This part of the file does the low-level record management. + */ + +/* + * IMPLEMENTATION NOTES + * ==================== + * + * In this file, we designate by "input" (and the "i" letter) the "recv" + * operations: incoming records from the peer, from which payload data + * is obtained, and must be extracted by the application (or the SSL + * handshake engine). Similarly, "output" (and the "o" letter) is for + * "send": payload data injected by the application (and SSL handshake + * engine), to be wrapped into records, that are then conveyed to the + * peer over the transport medium. + * + * The input and output buffers may be distinct or shared. When + * shared, input and output cannot occur concurrently; the caller + * must make sure that it never needs to output data while input + * data has been received. In practice, a shared buffer prevents + * pipelining of HTTP requests, or similar protocols; however, a + * shared buffer saves RAM. + * + * The input buffer is pointed to by 'ibuf' and has size 'ibuf_len'; + * the output buffer is pointed to by 'obuf' and has size 'obuf_len'. + * From the size of these buffers is derived the maximum fragment + * length, which will be honoured upon sending records; regardless of + * that length, incoming records will be processed as long as they + * fit in the input buffer, and their length still complies with the + * protocol specification (maximum plaintext payload length is 16384 + * bytes). + * + * Three registers are used to manage buffering in ibuf, called ixa, + * ixb and ixc. Similarly, three registers are used to manage buffering + * in obuf, called oxa, oxb and oxc. + * + * + * At any time, the engine is in one of the following modes: + * -- Failed mode: an error occurs, no I/O can happen. + * -- Input mode: the engine can either receive record bytes from the + * transport layer, or it has some buffered payload bytes to yield. + * -- Output mode: the engine can either receive payload bytes, or it + * has some record bytes to send to the transport layer. + * -- Input/Output mode: both input and output modes are active. When + * the buffer is shared, this can happen only when the buffer is empty + * (no buffered payload bytes or record bytes in either direction). + * + * + * Failed mode: + * ------------ + * + * I/O failed for some reason (invalid received data, not enough room + * for the next record...). No I/O may ever occur again for this context, + * until an explicit reset is performed. This mode, and the error code, + * are also used for protocol errors, especially handshake errors. + * + * + * Input mode: + * ----------- + * + * ixa index within ibuf[] for the currently read data + * ixb maximum index within ibuf[] for the currently read data + * ixc number of bytes not yet received for the current record + * + * -- When ixa == ixb, there is no available data for readers. When + * ixa != ixb, there is available data and it starts at offset ixa. + * + * -- When waiting for the next record header, ixa and ixb are equal + * and contain a value ranging from 0 to 4; ixc is equal to 5-ixa. + * + * -- When the header has been received, record data is obtained. The + * ixc field records how many bytes are still needed to reach the + * end of the current record. + * + * ** If encryption is active, then ixa and ixb are kept equal, and + * point to the end of the currently received record bytes. When + * ixc reaches 0, decryption/MAC is applied, and ixa and ixb are + * adjusted. + * + * ** If encryption is not active, then ixa and ixb are distinct + * and data can be read right away. Additional record data is + * obtained only when ixa == ixb. + * + * Note: in input mode and no encryption, records larger than the buffer + * size are allowed. When encryption is active, the complete record must + * fit within the buffer, since it cannot be decrypted/MACed until it + * has been completely received. + * + * -- When receiving the next record header, 'version_in' contains the + * expected input version (0 if not expecting a specific version); on + * mismatch, the mode switches to 'failed'. + * + * -- When the header has been received, 'version_in' contains the received + * version. It is up to the caller to check and adjust the 'version_in' field + * to implement the required semantics. + * + * -- The 'record_type_in' field is updated with the incoming record type + * when the next record header has been received. + * + * + * Output mode: + * ------------ + * + * oxa index within obuf[] for the currently accumulated data + * oxb maximum index within obuf[] for record data + * oxc pointer for start of record data, and for record sending + * + * -- When oxa != oxb, more data can be accumulated into the current + * record; when oxa == oxb, a closed record is being sent. + * + * -- When accumulating data, oxc points to the start of the data. + * + * -- During record sending, oxa (and oxb) point to the next record byte + * to send, and oxc indicates the end of the current record. + * + * Note: sent records must fit within the buffer, since the header is + * adjusted only when the complete record has been assembled. + * + * -- The 'version_out' and 'record_type_out' fields are used to build the + * record header when the mode is switched to 'sending'. + * + * + * Modes: + * ------ + * + * The state register iomode contains one of the following values: + * + * BR_IO_FAILED I/O failed + * BR_IO_IN input mode + * BR_IO_OUT output mode + * BR_IO_INOUT input/output mode + * + * Whether encryption is active on incoming records is indicated by the + * incrypt flag. For outgoing records, there is no such flag; "encryption" + * is always considered active, but initially uses functions that do not + * encrypt anything. The 'incrypt' flag is needed because when there is + * no active encryption, records larger than the I/O buffer are accepted. + * + * Note: we do not support no-encryption modes (MAC only). + * + * TODO: implement GCM support + * + * + * Misc: + * ----- + * + * 'max_frag_len' is the maximum plaintext size for an outgoing record. + * By default, it is set to the maximum value that fits in the provided + * buffers, in the following list: 512, 1024, 2048, 4096, 16384. The + * caller may change it if needed, but the new value MUST still fit in + * the buffers, and it MUST be one of the list above for compatibility + * with the Maximum Fragment Length extension. + * + * For incoming records, only the total buffer length and current + * encryption mode impact the maximum length for incoming records. The + * 'max_frag_len' value is still adjusted so that records up to that + * length can be both received and sent. + * + * + * Offsets and lengths: + * -------------------- + * + * When sending fragments with TLS-1.1+, the maximum overhead is: + * 5 bytes for the record header + * 16 bytes for the explicit IV + * 48 bytes for the MAC (HMAC/SHA-384) + * 16 bytes for the padding (AES) + * so a total of 85 extra bytes. Note that we support block cipher sizes + * up to 16 bytes (AES) and HMAC output sizes up to 48 bytes (SHA-384). + * + * With TLS-1.0 and CBC mode, we apply a 1/n-1 split, for a maximum + * overhead of: + * 5 bytes for the first record header + * 32 bytes for the first record payload (AES-CBC + HMAC/SHA-1) + * 5 bytes for the second record header + * 20 bytes for the MAC (HMAC/SHA-1) + * 16 bytes for the padding (AES) + * -1 byte to account for the payload byte in the first record + * so a total of 77 extra bytes at most, less than the 85 bytes above. + * Note that with TLS-1.0, the MAC is HMAC with either MD5 or SHA-1, but + * no other hash function. + * + * The implementation does not try to send larger records when the current + * encryption mode has less overhead. + * + * Maximum input record overhead is: + * 5 bytes for the record header + * 16 bytes for the explicit IV (TLS-1.1+) + * 48 bytes for the MAC (HMAC/SHA-384) + * 256 bytes for the padding + * so a total of 325 extra bytes. + * + * When receiving the next record header, it is written into the buffer + * bytes 0 to 4 (inclusive). Record data is always written into buf[] + * starting at offset 5. When encryption is active, the plaintext data + * may start at a larger offset (e.g. because of an explicit IV). + */ + +#define MAX_OUT_OVERHEAD 85 +#define MAX_IN_OVERHEAD 325 + +/* see inner.h */ +void +br_ssl_engine_fail(br_ssl_engine_context *rc, int err) +{ + if (rc->iomode != BR_IO_FAILED) { + rc->iomode = BR_IO_FAILED; + rc->err = err; + } +} + +/* + * Adjust registers for a new incoming record. + */ +static void +make_ready_in(br_ssl_engine_context *rc) +{ + rc->ixa = rc->ixb = 0; + rc->ixc = 5; + if (rc->iomode == BR_IO_IN) { + rc->iomode = BR_IO_INOUT; + } +} + +/* + * Adjust registers for a new outgoing record. + */ +static void +make_ready_out(br_ssl_engine_context *rc) +{ + size_t a, b; + + a = 5; + b = rc->obuf_len - a; + rc->out.vtable->max_plaintext(&rc->out.vtable, &a, &b); + if ((b - a) > rc->max_frag_len) { + b = a + rc->max_frag_len; + } + rc->oxa = a; + rc->oxb = b; + rc->oxc = a; + if (rc->iomode == BR_IO_OUT) { + rc->iomode = BR_IO_INOUT; + } +} + +/* see inner.h */ +void +br_ssl_engine_new_max_frag_len(br_ssl_engine_context *rc, unsigned max_frag_len) +{ + size_t nxb; + + rc->max_frag_len = max_frag_len; + nxb = rc->oxc + max_frag_len; + if (rc->oxa < rc->oxb && rc->oxb > nxb && rc->oxa < nxb) { + rc->oxb = nxb; + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_buffer(br_ssl_engine_context *rc, + void *buf, size_t buf_len, int bidi) +{ + if (buf == NULL) { + br_ssl_engine_set_buffers_bidi(rc, NULL, 0, NULL, 0); + } else { + /* + * In bidirectional mode, we want to maximise input + * buffer size, since we support arbitrary fragmentation + * when sending, but the peer will not necessarily + * comply to any low fragment length (in particular if + * we are the server, because the maximum fragment + * length extension is under client control). + * + * We keep a minimum size of 512 bytes for the plaintext + * of our outgoing records. + * + * br_ssl_engine_set_buffers_bidi() will compute the maximum + * fragment length for outgoing records by using the minimum + * of allocated spaces for both input and output records, + * rounded down to a standard length. + */ + if (bidi) { + size_t w; + + if (buf_len < (512 + MAX_IN_OVERHEAD + + 512 + MAX_OUT_OVERHEAD)) + { + rc->iomode = BR_IO_FAILED; + rc->err = BR_ERR_BAD_PARAM; + return; + } else if (buf_len < (16384 + MAX_IN_OVERHEAD + + 512 + MAX_OUT_OVERHEAD)) + { + w = 512 + MAX_OUT_OVERHEAD; + } else { + w = buf_len - (16384 + MAX_IN_OVERHEAD); + } + br_ssl_engine_set_buffers_bidi(rc, + buf, buf_len - w, + (unsigned char *)buf + w, w); + } else { + br_ssl_engine_set_buffers_bidi(rc, + buf, buf_len, NULL, 0); + } + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *rc, + void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len) +{ + rc->iomode = BR_IO_INOUT; + rc->incrypt = 0; + rc->err = BR_ERR_OK; + rc->version_in = 0; + rc->record_type_in = 0; + rc->version_out = 0; + rc->record_type_out = 0; + if (ibuf == NULL) { + if (rc->ibuf == NULL) { + br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM); + } + } else { + unsigned u; + + rc->ibuf = ibuf; + rc->ibuf_len = ibuf_len; + if (obuf == NULL) { + obuf = ibuf; + obuf_len = ibuf_len; + } + rc->obuf = obuf; + rc->obuf_len = obuf_len; + + /* + * Compute the maximum fragment length, that fits for + * both incoming and outgoing records. This length will + * be used in fragment length negotiation, so we must + * honour it both ways. Regardless, larger incoming + * records will be accepted, as long as they fit in the + * actual buffer size. + */ + for (u = 14; u >= 9; u --) { + size_t flen; + + flen = (size_t)1 << u; + if (obuf_len >= flen + MAX_OUT_OVERHEAD + && ibuf_len >= flen + MAX_IN_OVERHEAD) + { + break; + } + } + if (u == 8) { + br_ssl_engine_fail(rc, BR_ERR_BAD_PARAM); + return; + } else if (u == 13) { + u = 12; + } + rc->max_frag_len = (size_t)1 << u; + rc->log_max_frag_len = u; + rc->peer_log_max_frag_len = 0; + } + rc->out.vtable = &br_sslrec_out_clear_vtable; + make_ready_in(rc); + make_ready_out(rc); +} + +/* + * Clear buffers in both directions. + */ +static void +engine_clearbuf(br_ssl_engine_context *rc) +{ + make_ready_in(rc); + make_ready_out(rc); +} + +/* + * Make sure the internal PRNG is initialised (but not necessarily + * seeded properly yet). + */ +static int +rng_init(br_ssl_engine_context *cc) +{ + const br_hash_class *h; + + if (cc->rng_init_done != 0) { + return 1; + } + + /* + * If using TLS-1.2, then SHA-256 or SHA-384 must be present (or + * both); we prefer SHA-256 which is faster for 32-bit systems. + * + * If using TLS-1.0 or 1.1 then SHA-1 must be present. + * + * Though HMAC_DRBG/SHA-1 is, as far as we know, as safe as + * these things can be, we still prefer the SHA-2 functions over + * SHA-1, if only for public relations (known theoretical + * weaknesses of SHA-1 with regards to collisions are mostly + * irrelevant here, but they still make people nervous). + */ + h = br_multihash_getimpl(&cc->mhash, br_sha256_ID); + if (!h) { + h = br_multihash_getimpl(&cc->mhash, br_sha384_ID); + if (!h) { + h = br_multihash_getimpl(&cc->mhash, + br_sha1_ID); + if (!h) { + br_ssl_engine_fail(cc, BR_ERR_BAD_STATE); + return 0; + } + } + } + br_hmac_drbg_init(&cc->rng, h, NULL, 0); + cc->rng_init_done = 1; + return 1; +} + +/* see inner.h */ +int +br_ssl_engine_init_rand(br_ssl_engine_context *cc) +{ + if (!rng_init(cc)) { + return 0; + } + + /* + * We always try OS/hardware seeding once. If it works, then + * we assume proper seeding. If not, then external entropy must + * have been injected; otherwise, we report an error. + */ + if (!cc->rng_os_rand_done) { + br_prng_seeder sd; + + sd = br_prng_seeder_system(NULL); + if (sd != 0 && sd(&cc->rng.vtable)) { + cc->rng_init_done = 2; + } + cc->rng_os_rand_done = 1; + } + if (cc->rng_init_done < 2) { + br_ssl_engine_fail(cc, BR_ERR_NO_RANDOM); + return 0; + } + return 1; +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_inject_entropy(br_ssl_engine_context *cc, + const void *data, size_t len) +{ + /* + * Externally provided entropy is assumed to be "good enough" + * (we cannot really test its quality) so if the RNG structure + * could be initialised at all, then we marked the RNG as + * "properly seeded". + */ + if (!rng_init(cc)) { + return; + } + br_hmac_drbg_update(&cc->rng, data, len); + cc->rng_init_done = 2; +} + +/* + * We define a few internal functions that implement the low-level engine + * API for I/O; the external API (br_ssl_engine_sendapp_buf() and similar + * functions) is built upon these function, with special processing for + * records which are not of type "application data". + * + * recvrec_buf, recvrec_ack receives bytes from transport medium + * sendrec_buf, sendrec_ack send bytes to transport medium + * recvpld_buf, recvpld_ack receives payload data from engine + * sendpld_buf, sendpld_ack send payload data to engine + */ + +static unsigned char * +recvrec_buf(const br_ssl_engine_context *rc, size_t *len) +{ + if (rc->shutdown_recv) { + *len = 0; + return NULL; + } + + /* + * Bytes from the transport can be injected only if the mode is + * compatible (in or in/out), and ixa == ixb; ixc then contains + * the number of bytes that are still expected (but it may + * exceed our buffer size). + * + * We cannot get "stuck" here (buffer is full, but still more + * data is expected) because oversized records are detected when + * their header is processed. + */ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + if (rc->ixa == rc->ixb) { + size_t z; + + z = rc->ixc; + if (z > rc->ibuf_len - rc->ixa) { + z = rc->ibuf_len - rc->ixa; + } + *len = z; + return rc->ibuf + rc->ixa; + } + break; + } + *len = 0; + return NULL; +} + +static void +recvrec_ack(br_ssl_engine_context *rc, size_t len) +{ + unsigned char *pbuf; + size_t pbuf_len; + + /* + * Adjust state if necessary (for a shared input/output buffer): + * we got some incoming bytes, so we cannot (temporarily) handle + * outgoing data. + */ + if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) { + rc->iomode = BR_IO_IN; + } + + /* + * Adjust data pointers. + */ + rc->ixb = (rc->ixa += len); + rc->ixc -= len; + + /* + * If we are receiving a header and did not fully obtained it + * yet, then just wait for the next bytes. + */ + if (rc->ixa < 5) { + return; + } + + /* + * If we just obtained a full header, process it. + */ + if (rc->ixa == 5) { + unsigned version; + unsigned rlen; + + /* + * Get record type and version. We support only versions + * 3.x (if the version major number does not match, then + * we suppose that the record format is too alien for us + * to process it). + * + * Note: right now, we reject clients that try to send + * a ClientHello in a format compatible with SSL-2.0. It + * is unclear whether this will ever be supported; and + * if we want to support it, then this might be done in + * in the server-specific code, not here. + */ + rc->record_type_in = rc->ibuf[0]; + version = br_dec16be(rc->ibuf + 1); + if ((version >> 8) != 3) { + br_ssl_engine_fail(rc, BR_ERR_UNSUPPORTED_VERSION); + return; + } + + /* + * We ensure that successive records have the same + * version. The handshake code must check and adjust the + * variables when necessary to accommodate the protocol + * negotiation details. + */ + if (rc->version_in != 0 && rc->version_in != version) { + br_ssl_engine_fail(rc, BR_ERR_BAD_VERSION); + return; + } + rc->version_in = version; + + /* + * Decode record length. We must check that the length + * is valid (relatively to the current encryption mode) + * and also (if encryption is active) that the record + * will fit in our buffer. + * + * When no encryption is active, we can process records + * by chunks, and thus accept any record up to the + * maximum allowed plaintext length (16384 bytes). + */ + rlen = br_dec16be(rc->ibuf + 3); + if (rc->incrypt) { + if (!rc->in.vtable->check_length( + &rc->in.vtable, rlen)) + { + br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH); + return; + } + if (rlen > (rc->ibuf_len - 5)) { + br_ssl_engine_fail(rc, BR_ERR_TOO_LARGE); + return; + } + } else { + if (rlen > 16384) { + br_ssl_engine_fail(rc, BR_ERR_BAD_LENGTH); + return; + } + } + + /* + * If the record is completely empty then we must switch + * to a new record. Note that, in that case, we + * completely ignore the record type, which is fitting + * since we received no actual data of that type. + * + * A completely empty record is technically allowed as + * long as encryption/MAC is not active, i.e. before + * completion of the first handshake. It it still weird; + * it might conceptually be useful as a heartbeat or + * keep-alive mechanism while some lengthy operation is + * going on, e.g. interaction with a human user. + */ + if (rlen == 0) { + make_ready_in(rc); + } else { + rc->ixa = rc->ixb = 5; + rc->ixc = rlen; + } + return; + } + + /* + * If there is no active encryption, then the data can be read + * right away. Note that we do not receive bytes from the + * transport medium when we still have payload bytes to be + * acknowledged. + */ + if (!rc->incrypt) { + rc->ixa = 5; + return; + } + + /* + * Since encryption is active, we must wait for a full record + * before processing it. + */ + if (rc->ixc != 0) { + return; + } + + /* + * We got the full record. Decrypt it. + */ + pbuf_len = rc->ixa - 5; + pbuf = rc->in.vtable->decrypt(&rc->in.vtable, + rc->record_type_in, rc->version_in, rc->ibuf + 5, &pbuf_len); + if (pbuf == 0) { + br_ssl_engine_fail(rc, BR_ERR_BAD_MAC); + return; + } + rc->ixa = (size_t)(pbuf - rc->ibuf); + rc->ixb = rc->ixa + pbuf_len; + + /* + * Decryption may have yielded an empty record, in which case + * we get back to "ready" state immediately. + */ + if (rc->ixa == rc->ixb) { + make_ready_in(rc); + } +} + +/* see inner.h */ +int +br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc) +{ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + return rc->ixc == 0 || rc->ixa < 5; + default: + return 1; + } +} + +static unsigned char * +recvpld_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * There is payload data to be read only if the mode is + * compatible, and ixa != ixb. + */ + switch (rc->iomode) { + case BR_IO_IN: + case BR_IO_INOUT: + *len = rc->ixb - rc->ixa; + return (*len == 0) ? NULL : (rc->ibuf + rc->ixa); + default: + *len = 0; + return NULL; + } +} + +static void +recvpld_ack(br_ssl_engine_context *rc, size_t len) +{ + rc->ixa += len; + + /* + * If we read all the available data, then we either expect + * the remainder of the current record (if the current record + * was not finished; this may happen when encryption is not + * active), or go to "ready" state. + */ + if (rc->ixa == rc->ixb) { + if (rc->ixc == 0) { + make_ready_in(rc); + } else { + rc->ixa = rc->ixb = 5; + } + } +} + +static unsigned char * +sendpld_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * Payload data can be injected only if the current mode is + * compatible, and oxa != oxb. + */ + switch (rc->iomode) { + case BR_IO_OUT: + case BR_IO_INOUT: + *len = rc->oxb - rc->oxa; + return (*len == 0) ? NULL : (rc->obuf + rc->oxa); + default: + *len = 0; + return NULL; + } +} + +/* + * If some payload bytes have been accumulated, then wrap them into + * an outgoing record. Otherwise, this function does nothing, unless + * 'force' is non-zero, in which case an empty record is assembled. + * + * The caller must take care not to invoke this function if the engine + * is not currently ready to receive payload bytes to send. + */ +static void +sendpld_flush(br_ssl_engine_context *rc, int force) +{ + size_t xlen; + unsigned char *buf; + + if (rc->oxa == rc->oxb) { + return; + } + xlen = rc->oxa - rc->oxc; + if (xlen == 0 && !force) { + return; + } + buf = rc->out.vtable->encrypt(&rc->out.vtable, + rc->record_type_out, rc->version_out, + rc->obuf + rc->oxc, &xlen); + rc->oxb = rc->oxa = (size_t)(buf - rc->obuf); + rc->oxc = rc->oxa + xlen; +} + +static void +sendpld_ack(br_ssl_engine_context *rc, size_t len) +{ + /* + * If using a shared buffer, then we may have to modify the + * current mode. + */ + if (rc->iomode == BR_IO_INOUT && rc->ibuf == rc->obuf) { + rc->iomode = BR_IO_OUT; + } + rc->oxa += len; + if (rc->oxa >= rc->oxb) { + /* + * Set oxb to one more than oxa so that sendpld_flush() + * does not mistakingly believe that a record is + * already prepared and being sent. + */ + rc->oxb = rc->oxa + 1; + sendpld_flush(rc, 0); + } +} + +static unsigned char * +sendrec_buf(const br_ssl_engine_context *rc, size_t *len) +{ + /* + * When still gathering payload bytes, oxc points to the start + * of the record data, so oxc <= oxa. However, when a full + * record has been completed, oxc points to the end of the record, + * so oxc > oxa. + */ + switch (rc->iomode) { + case BR_IO_OUT: + case BR_IO_INOUT: + if (rc->oxc > rc->oxa) { + *len = rc->oxc - rc->oxa; + return rc->obuf + rc->oxa; + } + break; + } + *len = 0; + return NULL; +} + +static void +sendrec_ack(br_ssl_engine_context *rc, size_t len) +{ + rc->oxb = (rc->oxa += len); + if (rc->oxa == rc->oxc) { + make_ready_out(rc); + } +} + +/* + * Test whether there is some buffered outgoing record that still must + * sent. + */ +static inline int +has_rec_tosend(const br_ssl_engine_context *rc) +{ + return rc->oxa == rc->oxb && rc->oxa != rc->oxc; +} + +/* + * The "no encryption" mode has no overhead. It limits the payload size + * to the maximum size allowed by the standard (16384 bytes); the caller + * is responsible for possibly enforcing a smaller fragment length. + */ +static void +clear_max_plaintext(const br_sslrec_out_clear_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + len = *end - *start; + if (len > 16384) { + *end = *start + 16384; + } +} + +/* + * In "no encryption" mode, encryption is trivial (a no-operation) so + * we just have to encode the header. + */ +static unsigned char * +clear_encrypt(br_sslrec_out_clear_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + + (void)cc; + buf = (unsigned char *)data - 5; + buf[0] = record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, *data_len); + *data_len += 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_class br_sslrec_out_clear_vtable PROGMEM = { + sizeof(br_sslrec_out_clear_context), + (void (*)(const br_sslrec_out_class *const *, size_t *, size_t *)) + &clear_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &clear_encrypt +}; + +/* ==================================================================== */ +/* + * In this part of the file, we handle the various record types, and + * communications with the handshake processor. + */ + +/* + * IMPLEMENTATION NOTES + * ==================== + * + * The handshake processor is written in T0 and runs as a coroutine. + * It receives the contents of all records except application data, and + * is responsible for producing the contents of all records except + * application data. + * + * A state flag is maintained, which specifies whether application data + * is acceptable or not. When it is set: + * + * -- Application data can be injected as payload data (provided that + * the output buffer is ready for that). + * + * -- Incoming application data records are accepted, and yield data + * that the caller may retrieve. + * + * When the flag is cleared, application data is not accepted from the + * application, and incoming application data records trigger an error. + * + * + * Records of type handshake, alert or change-cipher-spec are handled + * by the handshake processor. The handshake processor is written in T0 + * and runs as a coroutine; it gets invoked whenever one of the following + * situations is reached: + * + * -- An incoming record has type handshake, alert or change-cipher-spec, + * and yields data that can be read (zero-length records are thus + * ignored). + * + * -- An outgoing record has just finished being sent, and the "application + * data" flag is cleared. + * + * -- The caller wishes to perform a close (call to br_ssl_engine_close()). + * + * -- The caller wishes to perform a renegotiation (call to + * br_ssl_engine_renegotiate()). + * + * Whenever the handshake processor is entered, access to the payload + * buffers is provided, along with some information about explicit + * closures or renegotiations. + */ + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_suites(br_ssl_engine_context *cc, + const uint16_t *suites, size_t suites_num) +{ + if ((suites_num * sizeof *suites) > sizeof cc->suites_buf) { + br_ssl_engine_fail(cc, BR_ERR_BAD_PARAM); + return; + } + memcpy(cc->suites_buf, suites, suites_num * sizeof *suites); + cc->suites_num = suites_num; +} + +/* + * Give control to handshake processor. 'action' is 1 for a close, + * 2 for a renegotiation, or 0 for a jump due to I/O completion. + */ +static void +jump_handshake(br_ssl_engine_context *cc, int action) +{ + /* + * We use a loop because the handshake processor actions may + * allow for more actions; namely, if the processor reads all + * input data, then it may allow for output data to be produced, + * in case of a shared in/out buffer. + */ + for (;;) { + size_t hlen_in, hlen_out; + + /* + * Get input buffer. We do not want to provide + * application data to the handshake processor (we could + * get called with an explicit close or renegotiation + * while there is application data ready to be read). + */ + cc->hbuf_in = recvpld_buf(cc, &hlen_in); + if (cc->hbuf_in != NULL + && cc->record_type_in == BR_SSL_APPLICATION_DATA) + { + hlen_in = 0; + } + + /* + * Get output buffer. The handshake processor never + * leaves an unfinished outgoing record, so if there is + * buffered output, then it MUST be some application + * data, so the processor cannot write to it. + */ + cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &hlen_out); + if (cc->hbuf_out != NULL && br_ssl_engine_has_pld_to_send(cc)) { + hlen_out = 0; + } + + /* + * Note: hlen_in and hlen_out can be both non-zero only if + * the input and output buffers are disjoint. Thus, we can + * offer both buffers to the handshake code. + */ + + cc->hlen_in = hlen_in; + cc->hlen_out = hlen_out; + cc->action = action; + cc->hsrun(&cc->cpu); + if (br_ssl_engine_closed(cc)) { + return; + } + if (cc->hbuf_out != cc->saved_hbuf_out) { + sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out); + } + if (hlen_in != cc->hlen_in) { + recvpld_ack(cc, hlen_in - cc->hlen_in); + if (cc->hlen_in == 0) { + /* + * We read all data bytes, which may have + * released the output buffer in case it + * is shared with the input buffer, and + * the handshake code might be waiting for + * that. + */ + action = 0; + continue; + } + } + break; + } +} + +/* see inner.h */ +void +br_ssl_engine_flush_record(br_ssl_engine_context *cc) +{ + if (cc->hbuf_out != cc->saved_hbuf_out) { + sendpld_ack(cc, cc->hbuf_out - cc->saved_hbuf_out); + } + if (br_ssl_engine_has_pld_to_send(cc)) { + sendpld_flush(cc, 0); + } + cc->saved_hbuf_out = cc->hbuf_out = sendpld_buf(cc, &cc->hlen_out); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) +{ + if (!(cc->application_data & 1)) { + *len = 0; + return NULL; + } + return sendpld_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) +{ + sendpld_ack(cc, len); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) +{ + if (!(cc->application_data & 1) + || cc->record_type_in != BR_SSL_APPLICATION_DATA) + { + *len = 0; + return NULL; + } + return recvpld_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) +{ + recvpld_ack(cc, len); +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) +{ + return sendrec_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) +{ + sendrec_ack(cc, len); + if (len != 0 && !has_rec_tosend(cc) + && (cc->record_type_out != BR_SSL_APPLICATION_DATA + || (cc->application_data & 1) == 0)) + { + jump_handshake(cc, 0); + } +} + +/* see bearssl_ssl.h */ +unsigned char * +br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) +{ + return recvrec_buf(cc, len); +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) +{ + unsigned char *buf; + + recvrec_ack(cc, len); + if (br_ssl_engine_closed(cc)) { + return; + } + + /* + * We just received some bytes from the peer. This may have + * yielded some payload bytes, in which case we must process + * them according to the record type. + */ + buf = recvpld_buf(cc, &len); + if (buf != NULL) { + switch (cc->record_type_in) { + case BR_SSL_CHANGE_CIPHER_SPEC: + case BR_SSL_ALERT: + case BR_SSL_HANDSHAKE: + jump_handshake(cc, 0); + break; + case BR_SSL_APPLICATION_DATA: + if (cc->application_data == 1) { + break; + } + + /* + * If we are currently closing, and waiting for + * a close_notify from the peer, then incoming + * application data should be discarded. + */ + if (cc->application_data == 2) { + recvpld_ack(cc, len); + break; + } + + /* Fall through */ + default: + br_ssl_engine_fail(cc, BR_ERR_UNEXPECTED); + break; + } + } +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_close(br_ssl_engine_context *cc) +{ + if (!br_ssl_engine_closed(cc)) { + /* + * If we are not already closed, then we need to + * initiate the closure. Once closing, any incoming + * application data is discarded; we should also discard + * application data which is already there but has not + * been acknowledged by the application yet (this mimics + * usual semantics on BSD sockets: you cannot read() + * once you called close(), even if there was some + * unread data already buffered). + */ + size_t len; + + if (br_ssl_engine_recvapp_buf(cc, &len) != NULL && len != 0) { + br_ssl_engine_recvapp_ack(cc, len); + } + jump_handshake(cc, 1); + } +} + +/* see bearssl_ssl.h */ +int +br_ssl_engine_renegotiate(br_ssl_engine_context *cc) +{ + size_t len; + + if (br_ssl_engine_closed(cc) || cc->reneg == 1 + || (cc->flags & BR_OPT_NO_RENEGOTIATION) != 0 + || br_ssl_engine_recvapp_buf(cc, &len) != NULL) + { + return 0; + } + jump_handshake(cc, 2); + return 1; +} + +/* see bearssl.h */ +unsigned +br_ssl_engine_current_state(const br_ssl_engine_context *cc) +{ + unsigned s; + size_t len; + + if (br_ssl_engine_closed(cc)) { + return BR_SSL_CLOSED; + } + + s = 0; + if (br_ssl_engine_sendrec_buf(cc, &len) != NULL) { + s |= BR_SSL_SENDREC; + } + if (br_ssl_engine_recvrec_buf(cc, &len) != NULL) { + s |= BR_SSL_RECVREC; + } + if (br_ssl_engine_sendapp_buf(cc, &len) != NULL) { + s |= BR_SSL_SENDAPP; + } + if (br_ssl_engine_recvapp_buf(cc, &len) != NULL) { + s |= BR_SSL_RECVAPP; + } + return s; +} + +/* see bearssl_ssl.h */ +void +br_ssl_engine_flush(br_ssl_engine_context *cc, int force) +{ + if (!br_ssl_engine_closed(cc) && (cc->application_data & 1) != 0) { + sendpld_flush(cc, force); + } +} + +/* see inner.h */ +void +br_ssl_engine_hs_reset(br_ssl_engine_context *cc, + void (*hsinit)(void *), void (*hsrun)(void *)) +{ + engine_clearbuf(cc); + cc->cpu.dp = cc->dp_stack; + cc->cpu.rp = cc->rp_stack; + hsinit(&cc->cpu); + cc->hsrun = hsrun; + cc->shutdown_recv = 0; + cc->application_data = 0; + cc->alert = 0; + jump_handshake(cc, 0); +} + +/* see inner.h */ +br_tls_prf_impl +br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id) +{ + if (cc->session.version >= BR_TLS12) { + if (prf_id == br_sha384_ID) { + return cc->prf_sha384; + } else { + return cc->prf_sha256; + } + } else { + return cc->prf10; + } +} + +/* see inner.h */ +void +br_ssl_engine_compute_master(br_ssl_engine_context *cc, + int prf_id, const void *pms, size_t pms_len) +{ + br_tls_prf_impl iprf; + br_tls_prf_seed_chunk seed[2] = { + { cc->client_random, sizeof cc->client_random }, + { cc->server_random, sizeof cc->server_random } + }; + + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(cc->session.master_secret, sizeof cc->session.master_secret, + pms, pms_len, "master secret", 2, seed); +} + +/* + * Compute key block. + */ +static void +compute_key_block(br_ssl_engine_context *cc, int prf_id, + size_t half_len, unsigned char *kb) +{ + br_tls_prf_impl iprf; + br_tls_prf_seed_chunk seed[2] = { + { cc->server_random, sizeof cc->server_random }, + { cc->client_random, sizeof cc->client_random } + }; + + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(kb, half_len << 1, + cc->session.master_secret, sizeof cc->session.master_secret, + "key expansion", 2, seed); +} + +/* see inner.h */ +void +br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcdec_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[192]; + unsigned char *cipher_key, *mac_key, *iv; + const br_hash_class *imh; + size_t mac_key_len, mac_out_len, iv_len; + + imh = br_ssl_engine_get_hash(cc, mac_id); + mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + mac_key_len = mac_out_len; + + /* + * TLS 1.1+ uses per-record explicit IV, so no IV to generate here. + */ + if (cc->session.version >= BR_TLS11) { + iv_len = 0; + } else { + iv_len = bc_impl->block_size; + } + compute_key_block(cc, prf_id, + mac_key_len + cipher_key_len + iv_len, kb); + if (is_client) { + mac_key = &kb[mac_key_len]; + cipher_key = &kb[(mac_key_len << 1) + cipher_key_len]; + iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len]; + } else { + mac_key = &kb[0]; + cipher_key = &kb[mac_key_len << 1]; + iv = &kb[(mac_key_len + cipher_key_len) << 1]; + } + if (iv_len == 0) { + iv = NULL; + } + cc->icbc_in->init(&cc->in.cbc.vtable, + bc_impl, cipher_key, cipher_key_len, + imh, mac_key, mac_key_len, mac_out_len, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcenc_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[192]; + unsigned char *cipher_key, *mac_key, *iv; + const br_hash_class *imh; + size_t mac_key_len, mac_out_len, iv_len; + + imh = br_ssl_engine_get_hash(cc, mac_id); + mac_out_len = (imh->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + mac_key_len = mac_out_len; + + /* + * TLS 1.1+ uses per-record explicit IV, so no IV to generate here. + */ + if (cc->session.version >= BR_TLS11) { + iv_len = 0; + } else { + iv_len = bc_impl->block_size; + } + compute_key_block(cc, prf_id, + mac_key_len + cipher_key_len + iv_len, kb); + if (is_client) { + mac_key = &kb[0]; + cipher_key = &kb[mac_key_len << 1]; + iv = &kb[(mac_key_len + cipher_key_len) << 1]; + } else { + mac_key = &kb[mac_key_len]; + cipher_key = &kb[(mac_key_len << 1) + cipher_key_len]; + iv = &kb[((mac_key_len + cipher_key_len) << 1) + iv_len]; + } + if (iv_len == 0) { + iv = NULL; + } + cc->icbc_out->init(&cc->out.cbc.vtable, + bc_impl, cipher_key, cipher_key_len, + imh, mac_key, mac_key_len, mac_out_len, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } else { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } + cc->igcm_in->init(&cc->in.gcm.vtable.in, + bc_impl, cipher_key, cipher_key_len, cc->ighash, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } else { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } + cc->igcm_out->init(&cc->out.gcm.vtable.out, + bc_impl, cipher_key, cipher_key_len, cc->ighash, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_chapol_in(br_ssl_engine_context *cc, + int is_client, int prf_id) +{ + unsigned char kb[88]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, 44, kb); + if (is_client) { + cipher_key = &kb[32]; + iv = &kb[76]; + } else { + cipher_key = &kb[0]; + iv = &kb[64]; + } + cc->ichapol_in->init(&cc->in.chapol.vtable.in, + cc->ichacha, cc->ipoly, cipher_key, iv); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc, + int is_client, int prf_id) +{ + unsigned char kb[88]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, 44, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[64]; + } else { + cipher_key = &kb[32]; + iv = &kb[76]; + } + cc->ichapol_out->init(&cc->out.chapol.vtable.out, + cc->ichacha, cc->ipoly, cipher_key, iv); +} + +/* see inner.h */ +void +br_ssl_engine_switch_ccm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } else { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } + cc->iccm_in->init(&cc->in.ccm.vtable.in, + bc_impl, cipher_key, cipher_key_len, iv, tag_len); + cc->incrypt = 1; +} + +/* see inner.h */ +void +br_ssl_engine_switch_ccm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len) +{ + unsigned char kb[72]; + unsigned char *cipher_key, *iv; + + compute_key_block(cc, prf_id, cipher_key_len + 4, kb); + if (is_client) { + cipher_key = &kb[0]; + iv = &kb[cipher_key_len << 1]; + } else { + cipher_key = &kb[cipher_key_len]; + iv = &kb[(cipher_key_len << 1) + 4]; + } + cc->iccm_out->init(&cc->out.ccm.vtable.out, + bc_impl, cipher_key, cipher_key_len, iv, tag_len); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c new file mode 100644 index 0000000000..8a257e16ba --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aescbc.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_cbc(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_cbcenc_class *ienc; + const br_block_cbcdec_class *idec; +#endif + + br_ssl_engine_set_cbc(cc, + &br_sslrec_in_cbc_vtable, + &br_sslrec_out_cbc_vtable); +#if BR_AES_X86NI + ienc = br_aes_x86ni_cbcenc_get_vtable(); + idec = br_aes_x86ni_cbcdec_get_vtable(); + if (ienc != NULL && idec != NULL) { + br_ssl_engine_set_aes_cbc(cc, ienc, idec); + return; + } +#endif +#if BR_POWER8 + ienc = br_aes_pwr8_cbcenc_get_vtable(); + idec = br_aes_pwr8_cbcdec_get_vtable(); + if (ienc != NULL && idec != NULL) { + br_ssl_engine_set_aes_cbc(cc, ienc, idec); + return; + } +#endif +#if BR_64 + br_ssl_engine_set_aes_cbc(cc, + &br_aes_ct64_cbcenc_vtable, + &br_aes_ct64_cbcdec_vtable); +#else + br_ssl_engine_set_aes_cbc(cc, + &br_aes_ct_cbcenc_vtable, + &br_aes_ct_cbcdec_vtable); +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c new file mode 100644 index 0000000000..815458fc3f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesccm.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_ccm(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_ctrcbc_class *ictrcbc; +#endif + + br_ssl_engine_set_ccm(cc, + &br_sslrec_in_ccm_vtable, + &br_sslrec_out_ccm_vtable); +#if BR_AES_X86NI + ictrcbc = br_aes_x86ni_ctrcbc_get_vtable(); + if (ictrcbc != NULL) { + br_ssl_engine_set_aes_ctrcbc(cc, ictrcbc); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif + } +#elif BR_POWER8 + ictrcbc = br_aes_pwr8_ctrcbc_get_vtable(); + if (ictrcbc != NULL) { + br_ssl_engine_set_aes_ctrcbc(cc, ictrcbc); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif + } +#else +#if BR_64 + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct64_ctrcbc_vtable); +#else + br_ssl_engine_set_aes_ctrcbc(cc, &br_aes_ct_ctrcbc_vtable); +#endif +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c new file mode 100644 index 0000000000..0f0220b085 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_aesgcm.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_aes_gcm(br_ssl_engine_context *cc) +{ +#if BR_AES_X86NI || BR_POWER8 + const br_block_ctr_class *ictr; + br_ghash ighash; +#endif + + br_ssl_engine_set_gcm(cc, + &br_sslrec_in_gcm_vtable, + &br_sslrec_out_gcm_vtable); +#if BR_AES_X86NI + ictr = br_aes_x86ni_ctr_get_vtable(); + if (ictr != NULL) { + br_ssl_engine_set_aes_ctr(cc, ictr); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif + } +#elif BR_POWER8 + ictr = br_aes_pwr8_ctr_get_vtable(); + if (ictr != NULL) { + br_ssl_engine_set_aes_ctr(cc, ictr); + } else { +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif + } +#else +#if BR_64 + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct64_ctr_vtable); +#else + br_ssl_engine_set_aes_ctr(cc, &br_aes_ct_ctr_vtable); +#endif +#endif +#if BR_AES_X86NI + ighash = br_ghash_pclmul_get(); + if (ighash != 0) { + br_ssl_engine_set_ghash(cc, ighash); + return; + } +#endif +#if BR_POWER8 + ighash = br_ghash_pwr8_get(); + if (ighash != 0) { + br_ssl_engine_set_ghash(cc, ighash); + return; + } +#endif +#if BR_LOMUL + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul32); +#elif BR_64 + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul64); +#else + br_ssl_engine_set_ghash(cc, &br_ghash_ctmul); +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c new file mode 100644 index 0000000000..2a49095f1c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_chapol.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_chapol(br_ssl_engine_context *cc) +{ +#if BR_INT128 || BR_UMUL128 + br_poly1305_run bp; +#endif +#if BR_SSE2 + br_chacha20_run bc; +#endif + + br_ssl_engine_set_chapol(cc, + &br_sslrec_in_chapol_vtable, + &br_sslrec_out_chapol_vtable); +#if BR_SSE2 + bc = br_chacha20_sse2_get(); + if (bc) { + br_ssl_engine_set_chacha20(cc, bc); + } else { +#endif + br_ssl_engine_set_chacha20(cc, &br_chacha20_ct_run); +#if BR_SSE2 + } +#endif +#if BR_INT128 || BR_UMUL128 + bp = br_poly1305_ctmulq_get(); + if (bp) { + br_ssl_engine_set_poly1305(cc, bp); + } else { +#endif +#if BR_LOMUL + br_ssl_engine_set_poly1305(cc, &br_poly1305_ctmul32_run); +#else + br_ssl_engine_set_poly1305(cc, &br_poly1305_ctmul_run); +#endif +#if BR_INT128 || BR_UMUL128 + } +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c new file mode 100644 index 0000000000..b747d5467f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_descbc.c @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_des_cbc(br_ssl_engine_context *cc) +{ + br_ssl_engine_set_cbc(cc, + &br_sslrec_in_cbc_vtable, + &br_sslrec_out_cbc_vtable); + br_ssl_engine_set_des_cbc(cc, + &br_des_ct_cbcenc_vtable, + &br_des_ct_cbcdec_vtable); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c new file mode 100644 index 0000000000..2ce4f38c77 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ec.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_ec(br_ssl_engine_context *cc) +{ +#if BR_LOMUL + br_ssl_engine_set_ec(cc, &br_ec_all_m15); +#else + br_ssl_engine_set_ec(cc, &br_ec_all_m31); +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c new file mode 100644 index 0000000000..26edd91d58 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_ecdsa.c @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_ecdsa(br_ssl_engine_context *cc) +{ +#if BR_LOMUL + br_ssl_engine_set_ec(cc, &br_ec_all_m15); + br_ssl_engine_set_ecdsa(cc, &br_ecdsa_i15_vrfy_asn1); +#else + br_ssl_engine_set_ec(cc, &br_ec_all_m31); + br_ssl_engine_set_ecdsa(cc, &br_ecdsa_i31_vrfy_asn1); +#endif +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c new file mode 100644 index 0000000000..7ab55f0424 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_engine_default_rsavrfy.c @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_ssl_engine_set_default_rsavrfy(br_ssl_engine_context *cc) +{ + br_ssl_engine_set_rsavrfy(cc, br_rsa_pkcs1_vrfy_get_default()); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c new file mode 100644 index 0000000000..d031f65c83 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hashes.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +int +br_ssl_choose_hash(unsigned bf) +{ + static const unsigned char pref[] = { + br_sha256_ID, br_sha384_ID, br_sha512_ID, + br_sha224_ID, br_sha1_ID + }; + size_t u; + + for (u = 0; u < sizeof pref; u ++) { + int x; + + x = pref[u]; + if ((bf >> x) & 1) { + return x; + } + } + return 0; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c new file mode 100644 index 0000000000..3f883a17b8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_client.c @@ -0,0 +1,1927 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_ssl_hs_client_init_main(void *t0ctx); + +void br_ssl_hs_client_run(void *t0ctx); + + + +#include +#include + +#include "t_inner.h" + +/* + * This macro evaluates to a pointer to the current engine context. + */ +#define ENG ((br_ssl_engine_context *)(void *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu))) + + + + + +/* + * This macro evaluates to a pointer to the client context, under that + * specific name. It must be noted that since the engine context is the + * first field of the br_ssl_client_context structure ('eng'), then + * pointers values of both types are interchangeable, modulo an + * appropriate cast. This also means that "addresses" computed as offsets + * within the structure work for both kinds of context. + */ +#define CTX ((br_ssl_client_context *)ENG) + +/* + * Generate the pre-master secret for RSA key exchange, and encrypt it + * with the server's public key. Returned value is either the encrypted + * data length (in bytes), or -x on error, with 'x' being an error code. + * + * This code assumes that the public key has been already verified (it + * was properly obtained by the X.509 engine, and it has the right type, + * i.e. it is of type RSA and suitable for encryption). + */ +static int +make_pms_rsa(br_ssl_client_context *ctx, int prf_id) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + const unsigned char *n; + unsigned char *pms; + size_t nlen, u; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + + /* + * Compute actual RSA key length, in case there are leading zeros. + */ + n = pk->key.rsa.n; + nlen = pk->key.rsa.nlen; + while (nlen > 0 && *n == 0) { + n ++; + nlen --; + } + + /* + * We need at least 59 bytes (48 bytes for pre-master secret, and + * 11 bytes for the PKCS#1 type 2 padding). Note that the X.509 + * minimal engine normally blocks RSA keys shorter than 128 bytes, + * so this is mostly for public keys provided explicitly by the + * caller. + */ + if (nlen < 59) { + return -BR_ERR_X509_WEAK_PUBLIC_KEY; + } + if (nlen > sizeof ctx->eng.pad) { + return -BR_ERR_LIMIT_EXCEEDED; + } + + /* + * Make PMS. + */ + pms = ctx->eng.pad + nlen - 48; + br_enc16be(pms, ctx->eng.version_max); + br_hmac_drbg_generate(&ctx->eng.rng, pms + 2, 46); + br_ssl_engine_compute_master(&ctx->eng, prf_id, pms, 48); + + /* + * Apply PKCS#1 type 2 padding. + */ + ctx->eng.pad[0] = 0x00; + ctx->eng.pad[1] = 0x02; + ctx->eng.pad[nlen - 49] = 0x00; + br_hmac_drbg_generate(&ctx->eng.rng, ctx->eng.pad + 2, nlen - 51); + for (u = 2; u < nlen - 49; u ++) { + while (ctx->eng.pad[u] == 0) { + br_hmac_drbg_generate(&ctx->eng.rng, + &ctx->eng.pad[u], 1); + } + } + + /* + * Compute RSA encryption. + */ + if (!ctx->irsapub(ctx->eng.pad, nlen, &pk->key.rsa)) { + return -BR_ERR_LIMIT_EXCEEDED; + } + return (int)nlen; +} + +/* + * OID for hash functions in RSA signatures. + */ +static const unsigned char *HASH_OID[] PROGMEM = { + BR_HASH_OID_SHA1, + BR_HASH_OID_SHA224, + BR_HASH_OID_SHA256, + BR_HASH_OID_SHA384, + BR_HASH_OID_SHA512 +}; + +/* + * Check the RSA signature on the ServerKeyExchange message. + * + * hash hash function ID (2 to 6), or 0 for MD5+SHA-1 (with RSA only) + * use_rsa non-zero for RSA signature, zero for ECDSA + * sig_len signature length (in bytes); signature value is in the pad + * + * Returned value is 0 on success, or an error code. + */ +static int +verify_SKE_sig(br_ssl_client_context *ctx, + int hash, int use_rsa, size_t sig_len) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + br_multihash_context mhc; + unsigned char hv[64], head[4]; + size_t hv_len; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + br_multihash_zero(&mhc); + br_multihash_copyimpl(&mhc, &ctx->eng.mhash); + br_multihash_init(&mhc); + br_multihash_update(&mhc, + ctx->eng.client_random, sizeof ctx->eng.client_random); + br_multihash_update(&mhc, + ctx->eng.server_random, sizeof ctx->eng.server_random); + head[0] = 3; + head[1] = 0; + head[2] = ctx->eng.ecdhe_curve; + head[3] = ctx->eng.ecdhe_point_len; + br_multihash_update(&mhc, head, sizeof head); + br_multihash_update(&mhc, + ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len); + if (hash) { + hv_len = br_multihash_out(&mhc, hash, hv); + if (hv_len == 0) { + return BR_ERR_INVALID_ALGORITHM; + } + } else { + if (!br_multihash_out(&mhc, br_md5_ID, hv) + || !br_multihash_out(&mhc, br_sha1_ID, hv + 16)) + { + return BR_ERR_INVALID_ALGORITHM; + } + hv_len = 36; + } + if (use_rsa) { + unsigned char tmp[64]; + const unsigned char *hash_oid; + + if (hash) { + hash_oid = HASH_OID[hash - 2]; + } else { + hash_oid = NULL; + } + if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len, + hash_oid, hv_len, &pk->key.rsa, tmp) + || memcmp(tmp, hv, hv_len) != 0) + { + return BR_ERR_BAD_SIGNATURE; + } + } else { + if (!ctx->eng.iecdsa(ctx->eng.iec, hv, hv_len, &pk->key.ec, + ctx->eng.pad, sig_len)) + { + return BR_ERR_BAD_SIGNATURE; + } + } + return 0; +} + +/* + * Perform client-side ECDH (or ECDHE). The point that should be sent to + * the server is written in the pad; returned value is either the point + * length (in bytes), or -x on error, with 'x' being an error code. + * + * The point _from_ the server is taken from ecdhe_point[] if 'ecdhe' + * is non-zero, or from the X.509 engine context if 'ecdhe' is zero + * (for static ECDH). + */ +static int +make_pms_ecdh(br_ssl_client_context *ctx, unsigned ecdhe, int prf_id) +{ + int curve; + unsigned char key[66], point[133]; + const unsigned char *order, *point_src; + size_t glen, olen, point_len, xoff, xlen; + unsigned char mask; + + if (ecdhe) { + curve = ctx->eng.ecdhe_curve; + point_src = ctx->eng.ecdhe_point; + point_len = ctx->eng.ecdhe_point_len; + } else { + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + curve = pk->key.ec.curve; + point_src = pk->key.ec.q; + point_len = pk->key.ec.qlen; + } + if ((ctx->eng.iec->supported_curves & ((uint32_t)1 << curve)) == 0) { + return -BR_ERR_INVALID_ALGORITHM; + } + + /* + * We need to generate our key, as a non-zero random value which + * is lower than the curve order, in a "large enough" range. We + * force top bit to 0 and bottom bit to 1, which guarantees that + * the value is in the proper range. + */ + order = ctx->eng.iec->order(curve, &olen); + mask = 0xFF; + while (mask >= pgm_read_byte(&order[0])) { + mask >>= 1; + } + br_hmac_drbg_generate(&ctx->eng.rng, key, olen); + key[0] &= mask; + key[olen - 1] |= 0x01; + + /* + * Compute the common ECDH point, whose X coordinate is the + * pre-master secret. + */ + ctx->eng.iec->generator(curve, &glen); + if (glen != point_len) { + return -BR_ERR_INVALID_ALGORITHM; + } + + memcpy_P(point, point_src, glen); + if (!ctx->eng.iec->mul(point, glen, key, olen, curve)) { + return -BR_ERR_INVALID_ALGORITHM; + } + + /* + * The pre-master secret is the X coordinate. + */ + xoff = ctx->eng.iec->xoff(curve, &xlen); + br_ssl_engine_compute_master(&ctx->eng, prf_id, point + xoff, xlen); + + ctx->eng.iec->mulgen(point, key, olen, curve); + memcpy(ctx->eng.pad, point, glen); + return (int)glen; +} + +/* + * Perform full static ECDH. This occurs only in the context of client + * authentication with certificates: the server uses an EC public key, + * the cipher suite is of type ECDH (not ECDHE), the server requested a + * client certificate and accepts static ECDH, the client has a + * certificate with an EC public key in the same curve, and accepts + * static ECDH as well. + * + * Returned value is 0 on success, -1 on error. + */ +static int +make_pms_static_ecdh(br_ssl_client_context *ctx, int prf_id) +{ + unsigned char point[133]; + size_t point_len; + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + point_len = pk->key.ec.qlen; + if (point_len > sizeof point) { + return -1; + } + memcpy(point, pk->key.ec.q, point_len); + if (!(*ctx->client_auth_vtable)->do_keyx( + ctx->client_auth_vtable, point, &point_len)) + { + return -1; + } + br_ssl_engine_compute_master(&ctx->eng, + prf_id, point, point_len); + return 0; +} + +/* + * Compute the client-side signature. This is invoked only when a + * signature-based client authentication was selected. The computed + * signature is in the pad; its length (in bytes) is returned. On + * error, 0 is returned. + */ +static size_t +make_client_sign(br_ssl_client_context *ctx) +{ + size_t hv_len; + + /* + * Compute hash of handshake messages so far. This "cannot" fail + * because the list of supported hash functions provided to the + * client certificate handler was trimmed to include only the + * hash functions that the multi-hasher supports. + */ + if (ctx->hash_id) { + hv_len = br_multihash_out(&ctx->eng.mhash, + ctx->hash_id, ctx->eng.pad); + } else { + br_multihash_out(&ctx->eng.mhash, + br_md5_ID, ctx->eng.pad); + br_multihash_out(&ctx->eng.mhash, + br_sha1_ID, ctx->eng.pad + 16); + hv_len = 36; + } + return (*ctx->client_auth_vtable)->do_sign( + ctx->client_auth_vtable, ctx->hash_id, hv_len, + ctx->eng.pad, sizeof ctx->eng.pad); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02, + 0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03, + 0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41, + 0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21, + 0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31, + 0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11, + 0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22, + 0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11, + 0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32, + 0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43, + 0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14, + 0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xC0, 0x9C, 0x06, + 0x04, 0xC0, 0x9D, 0x07, 0x04, 0xC0, 0xA0, 0x08, 0x04, 0xC0, 0xA1, 0x09, + 0x04, 0xC0, 0xAC, 0x26, 0x04, 0xC0, 0xAD, 0x27, 0x04, 0xC0, 0xAE, 0x28, + 0x04, 0xC0, 0xAF, 0x29, 0x04, 0xCC, 0xA8, 0x15, 0x04, 0xCC, 0xA9, 0x25, + 0x04, 0x00, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x01, + 0x00, 0x0E, 0x00, 0x00, 0x01, 0x00, 0x0F, 0x00, 0x00, 0x01, 0x01, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00, + 0x01, 0x02, 0x09, 0x00, 0x00, 0x25, 0x25, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CIPHER_SUITE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_COMPRESSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HELLO_DONE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SNI), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_BAD_VERSION), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_EXTRA_EXTENSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_INVALID_ALGORITHM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_RESUME_MISMATCH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNSUPPORTED_VERSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_WRONG_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_client_context, auth_type)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, client_random)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_curve)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, flags)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_client_context, hash_id)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_client_context, hashes)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_client_context, min_clienthello_len)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, protocol_names_num)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, record_type_in)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, selected_protocol)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)), + 0x00, 0x00, 0x09, 0x26, 0x59, 0x06, 0x02, 0x69, 0x28, 0x00, 0x00, 0x06, + 0x08, 0x2C, 0x0E, 0x05, 0x02, 0x72, 0x28, 0x04, 0x01, 0x3D, 0x00, 0x00, + 0x01, 0x01, 0x00, 0x01, 0x03, 0x00, 0x9A, 0x26, 0x5F, 0x45, 0x9E, 0x26, + 0x05, 0x04, 0x61, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0E, 0x06, 0x02, 0x9E, + 0x00, 0x5F, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x69, 0x28, 0x00, 0x00, 0x26, + 0x8A, 0x45, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x45, 0x7A, 0x2C, 0xAC, 0x1C, + 0x85, 0x01, 0x0C, 0x31, 0x00, 0x00, 0x26, 0x1F, 0x01, 0x08, 0x0B, 0x45, + 0x5D, 0x1F, 0x08, 0x00, 0x01, 0x03, 0x00, 0x78, 0x2E, 0x02, 0x00, 0x36, + 0x17, 0x01, 0x01, 0x0B, 0x78, 0x3F, 0x29, 0x1A, 0x36, 0x06, 0x07, 0x02, + 0x00, 0xD0, 0x03, 0x00, 0x04, 0x75, 0x01, 0x00, 0xC6, 0x02, 0x00, 0x26, + 0x1A, 0x17, 0x06, 0x02, 0x70, 0x28, 0xD0, 0x04, 0x76, 0x01, 0x01, 0x00, + 0x78, 0x3F, 0x01, 0x16, 0x88, 0x3F, 0x01, 0x00, 0x8B, 0x3D, 0x34, 0xD6, + 0x29, 0xB5, 0x06, 0x09, 0x01, 0x7F, 0xB0, 0x01, 0x7F, 0xD3, 0x04, 0x80, + 0x53, 0xB2, 0x7A, 0x2C, 0xA2, 0x01, T0_INT1(BR_KEYTYPE_SIGN), 0x17, + 0x06, 0x01, 0xB6, 0xB9, 0x26, 0x01, 0x0D, 0x0E, 0x06, 0x07, 0x25, 0xB8, + 0xB9, 0x01, 0x7F, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, 0x01, 0x0E, 0x0E, + 0x05, 0x02, 0x73, 0x28, 0x06, 0x02, 0x68, 0x28, 0x33, 0x06, 0x02, 0x73, + 0x28, 0x02, 0x00, 0x06, 0x1C, 0xD4, 0x81, 0x2E, 0x01, 0x81, 0x7F, 0x0E, + 0x06, 0x0D, 0x25, 0x01, 0x10, 0xDF, 0x01, 0x00, 0xDE, 0x7A, 0x2C, 0xAC, + 0x24, 0x04, 0x04, 0xD7, 0x06, 0x01, 0xD5, 0x04, 0x01, 0xD7, 0x01, 0x7F, + 0xD3, 0x01, 0x7F, 0xB0, 0x01, 0x01, 0x78, 0x3F, 0x01, 0x17, 0x88, 0x3F, + 0x00, 0x00, 0x38, 0x38, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x01, 0x00, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_KEYX), 0x04, 0x30, 0x01, 0x01, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_RSA | BR_KEYTYPE_SIGN), 0x04, 0x25, 0x01, 0x02, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_SIGN), 0x04, 0x1A, 0x01, 0x03, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_KEYX), 0x04, 0x0F, 0x01, 0x04, + 0x38, 0x0E, 0x06, 0x05, 0x25, 0x01, + T0_INT1(BR_KEYTYPE_EC | BR_KEYTYPE_KEYX), 0x04, 0x04, 0x01, 0x00, + 0x45, 0x25, 0x00, 0x00, 0x83, 0x2E, 0x01, 0x0E, 0x0E, 0x06, 0x04, 0x01, + 0x00, 0x04, 0x02, 0x01, 0x05, 0x00, 0x00, 0x41, 0x06, 0x04, 0x01, 0x06, + 0x04, 0x02, 0x01, 0x00, 0x00, 0x00, 0x89, 0x2E, 0x26, 0x06, 0x08, 0x01, + 0x01, 0x09, 0x01, 0x11, 0x07, 0x04, 0x03, 0x25, 0x01, 0x05, 0x00, 0x01, + 0x42, 0x03, 0x00, 0x25, 0x01, 0x00, 0x44, 0x06, 0x03, 0x02, 0x00, 0x08, + 0x43, 0x06, 0x03, 0x02, 0x00, 0x08, 0x26, 0x06, 0x06, 0x01, 0x01, 0x0B, + 0x01, 0x06, 0x08, 0x00, 0x00, 0x8C, 0x40, 0x26, 0x06, 0x03, 0x01, 0x09, + 0x08, 0x00, 0x01, 0x41, 0x26, 0x06, 0x1E, 0x01, 0x00, 0x03, 0x00, 0x26, + 0x06, 0x0E, 0x26, 0x01, 0x01, 0x17, 0x02, 0x00, 0x08, 0x03, 0x00, 0x01, + 0x01, 0x11, 0x04, 0x6F, 0x25, 0x02, 0x00, 0x01, 0x01, 0x0B, 0x01, 0x06, + 0x08, 0x00, 0x00, 0x80, 0x2D, 0x45, 0x11, 0x01, 0x01, 0x17, 0x35, 0x00, + 0x00, 0xA0, 0xCF, 0x26, 0x01, 0x07, 0x17, 0x01, 0x00, 0x38, 0x0E, 0x06, + 0x09, 0x25, 0x01, 0x10, 0x17, 0x06, 0x01, 0xA0, 0x04, 0x35, 0x01, 0x01, + 0x38, 0x0E, 0x06, 0x2C, 0x25, 0x25, 0x01, 0x00, 0x78, 0x3F, 0xB4, 0x89, + 0x2E, 0x01, 0x01, 0x0E, 0x01, 0x01, 0xA9, 0x37, 0x06, 0x17, 0x29, 0x1A, + 0x36, 0x06, 0x04, 0xCF, 0x25, 0x04, 0x78, 0x01, 0x80, 0x64, 0xC6, 0x01, + 0x01, 0x78, 0x3F, 0x01, 0x17, 0x88, 0x3F, 0x04, 0x01, 0xA0, 0x04, 0x03, + 0x73, 0x28, 0x25, 0x04, 0xFF, 0x34, 0x01, 0x26, 0x03, 0x00, 0x09, 0x26, + 0x59, 0x06, 0x02, 0x69, 0x28, 0x02, 0x00, 0x00, 0x00, 0x9B, 0x01, 0x0F, + 0x17, 0x00, 0x00, 0x77, 0x2E, 0x01, 0x00, 0x38, 0x0E, 0x06, 0x10, 0x25, + 0x26, 0x01, 0x01, 0x0D, 0x06, 0x03, 0x25, 0x01, 0x02, 0x77, 0x3F, 0x01, + 0x00, 0x04, 0x21, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x14, 0x25, 0x01, 0x00, + 0x77, 0x3F, 0x26, 0x01, 0x80, 0x64, 0x0E, 0x06, 0x05, 0x01, 0x82, 0x00, + 0x08, 0x28, 0x5B, 0x04, 0x07, 0x25, 0x01, 0x82, 0x00, 0x08, 0x28, 0x25, + 0x00, 0x00, 0x01, 0x00, 0x2F, 0x06, 0x05, 0x3A, 0xAD, 0x37, 0x04, 0x78, + 0x26, 0x06, 0x04, 0x01, 0x01, 0x90, 0x3F, 0x00, 0x01, 0xC0, 0xAB, 0xC0, + 0xAB, 0xC2, 0x85, 0x45, 0x26, 0x03, 0x00, 0xB7, 0x9C, 0x9C, 0x02, 0x00, + 0x4E, 0x26, 0x59, 0x06, 0x0A, 0x01, 0x03, 0xA9, 0x06, 0x02, 0x73, 0x28, + 0x25, 0x04, 0x03, 0x5D, 0x8B, 0x3D, 0x00, 0x00, 0x2F, 0x06, 0x0B, 0x87, + 0x2E, 0x01, 0x14, 0x0D, 0x06, 0x02, 0x73, 0x28, 0x04, 0x11, 0xCF, 0x01, + 0x07, 0x17, 0x26, 0x01, 0x02, 0x0D, 0x06, 0x06, 0x06, 0x02, 0x73, 0x28, + 0x04, 0x70, 0x25, 0xC3, 0x01, 0x01, 0x0D, 0x33, 0x37, 0x06, 0x02, 0x62, + 0x28, 0x26, 0x01, 0x01, 0xC9, 0x36, 0xB3, 0x00, 0x01, 0xB9, 0x01, 0x0B, + 0x0E, 0x05, 0x02, 0x73, 0x28, 0x26, 0x01, 0x03, 0x0E, 0x06, 0x08, 0xC1, + 0x06, 0x02, 0x69, 0x28, 0x45, 0x25, 0x00, 0x45, 0x58, 0xC1, 0xAB, 0x26, + 0x06, 0x23, 0xC1, 0xAB, 0x26, 0x57, 0x26, 0x06, 0x18, 0x26, 0x01, 0x82, + 0x00, 0x0F, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x26, 0x03, 0x00, + 0x85, 0x02, 0x00, 0xB7, 0x02, 0x00, 0x54, 0x04, 0x65, 0x9C, 0x55, 0x04, + 0x5A, 0x9C, 0x9C, 0x56, 0x26, 0x06, 0x02, 0x35, 0x00, 0x25, 0x2B, 0x00, + 0x00, 0x7A, 0x2C, 0xA2, 0x01, 0x7F, 0xB1, 0x26, 0x59, 0x06, 0x02, 0x35, + 0x28, 0x26, 0x05, 0x02, 0x73, 0x28, 0x38, 0x17, 0x0D, 0x06, 0x02, 0x75, + 0x28, 0x3C, 0x00, 0x00, 0x9D, 0xB9, 0x01, 0x14, 0x0D, 0x06, 0x02, 0x73, + 0x28, 0x85, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0xB7, 0x9C, 0x85, 0x26, 0x01, + 0x0C, 0x08, 0x01, 0x0C, 0x30, 0x05, 0x02, 0x65, 0x28, 0x00, 0x00, 0xBA, + 0x06, 0x02, 0x73, 0x28, 0x06, 0x02, 0x67, 0x28, 0x00, 0x0A, 0xB9, 0x01, + 0x02, 0x0E, 0x05, 0x02, 0x73, 0x28, 0xC0, 0x03, 0x00, 0x02, 0x00, 0x96, + 0x2C, 0x0A, 0x02, 0x00, 0x95, 0x2C, 0x0F, 0x37, 0x06, 0x02, 0x74, 0x28, + 0x02, 0x00, 0x94, 0x2C, 0x0D, 0x06, 0x02, 0x6C, 0x28, 0x02, 0x00, 0x97, + 0x3D, 0x8D, 0x01, 0x20, 0xB7, 0x01, 0x00, 0x03, 0x01, 0xC2, 0x03, 0x02, + 0x02, 0x02, 0x01, 0x20, 0x0F, 0x06, 0x02, 0x71, 0x28, 0x85, 0x02, 0x02, + 0xB7, 0x02, 0x02, 0x8F, 0x2E, 0x0E, 0x02, 0x02, 0x01, 0x00, 0x0F, 0x17, + 0x06, 0x0B, 0x8E, 0x85, 0x02, 0x02, 0x30, 0x06, 0x04, 0x01, 0x7F, 0x03, + 0x01, 0x8E, 0x85, 0x02, 0x02, 0x31, 0x02, 0x02, 0x8F, 0x3F, 0x02, 0x00, + 0x93, 0x02, 0x01, 0x99, 0xC0, 0x26, 0xC4, 0x59, 0x06, 0x02, 0x63, 0x28, + 0x26, 0xCE, 0x02, 0x00, 0x01, 0x86, 0x03, 0x0A, 0x17, 0x06, 0x02, 0x63, + 0x28, 0x7A, 0x02, 0x01, 0x99, 0xC2, 0x06, 0x02, 0x64, 0x28, 0x01, 0x00, + 0x3B, 0x26, 0x06, 0x81, 0x47, 0xC0, 0xAB, 0xA7, 0x03, 0x03, 0xA5, 0x03, + 0x04, 0xA3, 0x03, 0x05, 0xA6, 0x03, 0x06, 0xA8, 0x03, 0x07, 0xA4, 0x03, + 0x08, 0x27, 0x03, 0x09, 0x26, 0x06, 0x81, 0x18, 0xC0, 0x01, 0x00, 0x38, + 0x0E, 0x06, 0x0F, 0x25, 0x02, 0x03, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, + 0x03, 0x03, 0xBF, 0x04, 0x80, 0x7F, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x0F, + 0x25, 0x02, 0x05, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x05, 0xBD, + 0x04, 0x80, 0x6A, 0x01, 0x83, 0xFE, 0x01, 0x38, 0x0E, 0x06, 0x0F, 0x25, + 0x02, 0x04, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x04, 0xBE, 0x04, + 0x80, 0x53, 0x01, 0x0D, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x06, 0x05, + 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x06, 0xBB, 0x04, 0x3F, 0x01, 0x0A, + 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x07, 0x05, 0x02, 0x6D, 0x28, 0x01, + 0x00, 0x03, 0x07, 0xBB, 0x04, 0x2B, 0x01, 0x0B, 0x38, 0x0E, 0x06, 0x0E, + 0x25, 0x02, 0x08, 0x05, 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x08, 0xBB, + 0x04, 0x17, 0x01, 0x10, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x02, 0x09, 0x05, + 0x02, 0x6D, 0x28, 0x01, 0x00, 0x03, 0x09, 0xAF, 0x04, 0x03, 0x6D, 0x28, + 0x25, 0x04, 0xFE, 0x64, 0x02, 0x04, 0x06, 0x0D, 0x02, 0x04, 0x01, 0x05, + 0x0F, 0x06, 0x02, 0x6A, 0x28, 0x01, 0x01, 0x89, 0x3F, 0x9C, 0x04, 0x0C, + 0xA5, 0x01, 0x05, 0x0F, 0x06, 0x02, 0x6A, 0x28, 0x01, 0x01, 0x89, 0x3F, + 0x9C, 0x02, 0x01, 0x00, 0x04, 0xB9, 0x01, 0x0C, 0x0E, 0x05, 0x02, 0x73, + 0x28, 0xC2, 0x01, 0x03, 0x0E, 0x05, 0x02, 0x6E, 0x28, 0xC0, 0x26, 0x7D, + 0x3F, 0x26, 0x01, 0x20, 0x10, 0x06, 0x02, 0x6E, 0x28, 0x41, 0x45, 0x11, + 0x01, 0x01, 0x17, 0x05, 0x02, 0x6E, 0x28, 0xC2, 0x26, 0x01, 0x81, 0x05, + 0x0F, 0x06, 0x02, 0x6E, 0x28, 0x26, 0x7F, 0x3F, 0x7E, 0x45, 0xB7, 0x93, + 0x2C, 0x01, 0x86, 0x03, 0x10, 0x03, 0x00, 0x7A, 0x2C, 0xCC, 0x03, 0x01, + 0x01, 0x02, 0x03, 0x02, 0x02, 0x00, 0x06, 0x21, 0xC2, 0x26, 0x26, 0x01, + 0x02, 0x0A, 0x45, 0x01, 0x06, 0x0F, 0x37, 0x06, 0x02, 0x6E, 0x28, 0x03, + 0x02, 0xC2, 0x02, 0x01, 0x01, 0x01, 0x0B, 0x01, 0x03, 0x08, 0x0E, 0x05, + 0x02, 0x6E, 0x28, 0x04, 0x08, 0x02, 0x01, 0x06, 0x04, 0x01, 0x00, 0x03, + 0x02, 0xC0, 0x26, 0x03, 0x03, 0x26, 0x01, 0x84, 0x00, 0x0F, 0x06, 0x02, + 0x6F, 0x28, 0x85, 0x45, 0xB7, 0x02, 0x02, 0x02, 0x01, 0x02, 0x03, 0x51, + 0x26, 0x06, 0x01, 0x28, 0x25, 0x9C, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, + 0x02, 0x00, 0x98, 0x02, 0x01, 0x02, 0x00, 0x39, 0x26, 0x01, 0x00, 0x0E, + 0x06, 0x02, 0x61, 0x00, 0xD1, 0x04, 0x74, 0x02, 0x01, 0x00, 0x03, 0x00, + 0xC2, 0xAB, 0x26, 0x06, 0x80, 0x43, 0xC2, 0x01, 0x01, 0x38, 0x0E, 0x06, + 0x06, 0x25, 0x01, 0x81, 0x7F, 0x04, 0x2E, 0x01, 0x80, 0x40, 0x38, 0x0E, + 0x06, 0x07, 0x25, 0x01, 0x83, 0xFE, 0x00, 0x04, 0x20, 0x01, 0x80, 0x41, + 0x38, 0x0E, 0x06, 0x07, 0x25, 0x01, 0x84, 0x80, 0x00, 0x04, 0x12, 0x01, + 0x80, 0x42, 0x38, 0x0E, 0x06, 0x07, 0x25, 0x01, 0x88, 0x80, 0x00, 0x04, + 0x04, 0x01, 0x00, 0x45, 0x25, 0x02, 0x00, 0x37, 0x03, 0x00, 0x04, 0xFF, + 0x39, 0x9C, 0x7A, 0x2C, 0xCA, 0x05, 0x09, 0x02, 0x00, 0x01, 0x83, 0xFF, + 0x7F, 0x17, 0x03, 0x00, 0x93, 0x2C, 0x01, 0x86, 0x03, 0x10, 0x06, 0x3A, + 0xBC, 0x26, 0x82, 0x3E, 0x42, 0x25, 0x26, 0x01, 0x08, 0x0B, 0x37, 0x01, + 0x8C, 0x80, 0x00, 0x37, 0x17, 0x02, 0x00, 0x17, 0x02, 0x00, 0x01, 0x8C, + 0x80, 0x00, 0x17, 0x06, 0x19, 0x26, 0x01, 0x81, 0x7F, 0x17, 0x06, 0x05, + 0x01, 0x84, 0x80, 0x00, 0x37, 0x26, 0x01, 0x83, 0xFE, 0x00, 0x17, 0x06, + 0x05, 0x01, 0x88, 0x80, 0x00, 0x37, 0x03, 0x00, 0x04, 0x09, 0x02, 0x00, + 0x01, 0x8C, 0x88, 0x01, 0x17, 0x03, 0x00, 0x16, 0xC0, 0xAB, 0x26, 0x06, + 0x23, 0xC0, 0xAB, 0x26, 0x15, 0x26, 0x06, 0x18, 0x26, 0x01, 0x82, 0x00, + 0x0F, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x26, 0x03, 0x01, 0x85, + 0x02, 0x01, 0xB7, 0x02, 0x01, 0x12, 0x04, 0x65, 0x9C, 0x13, 0x04, 0x5A, + 0x9C, 0x14, 0x9C, 0x02, 0x00, 0x2A, 0x00, 0x00, 0xBA, 0x26, 0x5B, 0x06, + 0x07, 0x25, 0x06, 0x02, 0x67, 0x28, 0x04, 0x74, 0x00, 0x00, 0xC3, 0x01, + 0x03, 0xC1, 0x45, 0x25, 0x45, 0x00, 0x00, 0xC0, 0xC7, 0x00, 0x03, 0x01, + 0x00, 0x03, 0x00, 0xC0, 0xAB, 0x26, 0x06, 0x80, 0x50, 0xC2, 0x03, 0x01, + 0xC2, 0x03, 0x02, 0x02, 0x01, 0x01, 0x08, 0x0E, 0x06, 0x16, 0x02, 0x02, + 0x01, 0x0F, 0x0C, 0x06, 0x0D, 0x01, 0x01, 0x02, 0x02, 0x01, 0x10, 0x08, + 0x0B, 0x02, 0x00, 0x37, 0x03, 0x00, 0x04, 0x2A, 0x02, 0x01, 0x01, 0x02, + 0x10, 0x02, 0x01, 0x01, 0x06, 0x0C, 0x17, 0x02, 0x02, 0x01, 0x01, 0x0E, + 0x02, 0x02, 0x01, 0x03, 0x0E, 0x37, 0x17, 0x06, 0x11, 0x02, 0x00, 0x01, + 0x01, 0x02, 0x02, 0x5E, 0x01, 0x02, 0x0B, 0x02, 0x01, 0x08, 0x0B, 0x37, + 0x03, 0x00, 0x04, 0xFF, 0x2C, 0x9C, 0x02, 0x00, 0x00, 0x00, 0xC0, 0x01, + 0x01, 0x0E, 0x05, 0x02, 0x66, 0x28, 0xC2, 0x01, 0x08, 0x08, 0x83, 0x2E, + 0x0E, 0x05, 0x02, 0x66, 0x28, 0x01, 0x01, 0x3B, 0x00, 0x00, 0xC0, 0x89, + 0x2E, 0x05, 0x15, 0x01, 0x01, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0xC2, 0x01, + 0x00, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0x01, 0x02, 0x89, 0x3F, 0x04, 0x1C, + 0x01, 0x19, 0x0E, 0x05, 0x02, 0x6A, 0x28, 0xC2, 0x01, 0x18, 0x0E, 0x05, + 0x02, 0x6A, 0x28, 0x85, 0x01, 0x18, 0xB7, 0x8A, 0x85, 0x01, 0x18, 0x30, + 0x05, 0x02, 0x6A, 0x28, 0x00, 0x00, 0xC0, 0x06, 0x02, 0x6B, 0x28, 0x00, + 0x00, 0x01, 0x02, 0x98, 0xC3, 0x01, 0x08, 0x0B, 0xC3, 0x08, 0x00, 0x00, + 0x01, 0x03, 0x98, 0xC3, 0x01, 0x08, 0x0B, 0xC3, 0x08, 0x01, 0x08, 0x0B, + 0xC3, 0x08, 0x00, 0x00, 0x01, 0x01, 0x98, 0xC3, 0x00, 0x00, 0x3A, 0x26, + 0x59, 0x05, 0x01, 0x00, 0x25, 0xD1, 0x04, 0x76, 0x02, 0x03, 0x00, 0x92, + 0x2E, 0x03, 0x01, 0x01, 0x00, 0x26, 0x02, 0x01, 0x0A, 0x06, 0x10, 0x26, + 0x01, 0x01, 0x0B, 0x91, 0x08, 0x2C, 0x02, 0x00, 0x0E, 0x06, 0x01, 0x00, + 0x5D, 0x04, 0x6A, 0x25, 0x01, 0x7F, 0x00, 0x00, 0x01, 0x15, 0x88, 0x3F, + 0x45, 0x53, 0x25, 0x53, 0x25, 0x29, 0x00, 0x00, 0x01, 0x01, 0x45, 0xC5, + 0x00, 0x00, 0x45, 0x38, 0x98, 0x45, 0x26, 0x06, 0x05, 0xC3, 0x25, 0x5E, + 0x04, 0x78, 0x25, 0x00, 0x00, 0x26, 0x01, 0x81, 0xAC, 0x00, 0x0E, 0x06, + 0x04, 0x25, 0x01, 0x7F, 0x00, 0x9B, 0x5A, 0x00, 0x02, 0x03, 0x00, 0x7A, + 0x2C, 0x9B, 0x03, 0x01, 0x02, 0x01, 0x01, 0x0F, 0x17, 0x02, 0x01, 0x01, + 0x04, 0x11, 0x01, 0x0F, 0x17, 0x02, 0x01, 0x01, 0x08, 0x11, 0x01, 0x0F, + 0x17, 0x01, 0x00, 0x38, 0x0E, 0x06, 0x10, 0x25, 0x01, 0x00, 0x01, 0x18, + 0x02, 0x00, 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x81, 0x0D, 0x01, + 0x01, 0x38, 0x0E, 0x06, 0x10, 0x25, 0x01, 0x01, 0x01, 0x10, 0x02, 0x00, + 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x80, 0x77, 0x01, 0x02, 0x38, + 0x0E, 0x06, 0x10, 0x25, 0x01, 0x01, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, + 0x4A, 0x04, 0x01, 0x4B, 0x04, 0x80, 0x61, 0x01, 0x03, 0x38, 0x0E, 0x06, + 0x0F, 0x25, 0x25, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, + 0x49, 0x04, 0x80, 0x4C, 0x01, 0x04, 0x38, 0x0E, 0x06, 0x0E, 0x25, 0x25, + 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, 0x49, 0x04, 0x38, + 0x01, 0x05, 0x38, 0x0E, 0x06, 0x0C, 0x25, 0x25, 0x02, 0x00, 0x06, 0x03, + 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x26, 0x26, 0x01, 0x09, 0x0F, 0x06, 0x02, + 0x69, 0x28, 0x45, 0x25, 0x26, 0x01, 0x01, 0x17, 0x01, 0x04, 0x0B, 0x01, + 0x10, 0x08, 0x45, 0x01, 0x08, 0x17, 0x01, 0x10, 0x45, 0x09, 0x02, 0x00, + 0x06, 0x03, 0x46, 0x04, 0x01, 0x47, 0x00, 0x25, 0x00, 0x00, 0x9B, 0x01, + 0x0C, 0x11, 0x01, 0x02, 0x0F, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x26, + 0x5C, 0x45, 0x01, 0x03, 0x0A, 0x17, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, + 0x01, 0x01, 0x0E, 0x00, 0x00, 0x9B, 0x01, 0x0C, 0x11, 0x5B, 0x00, 0x00, + 0x9B, 0x01, 0x81, 0x70, 0x17, 0x01, 0x20, 0x0D, 0x00, 0x00, 0x1B, 0x01, + 0x00, 0x76, 0x2E, 0x26, 0x06, 0x22, 0x01, 0x01, 0x38, 0x0E, 0x06, 0x06, + 0x25, 0x01, 0x00, 0x9F, 0x04, 0x14, 0x01, 0x02, 0x38, 0x0E, 0x06, 0x0D, + 0x25, 0x78, 0x2E, 0x01, 0x01, 0x0E, 0x06, 0x03, 0x01, 0x10, 0x37, 0x04, + 0x01, 0x25, 0x04, 0x01, 0x25, 0x7C, 0x2E, 0x05, 0x33, 0x2F, 0x06, 0x30, + 0x87, 0x2E, 0x01, 0x14, 0x38, 0x0E, 0x06, 0x06, 0x25, 0x01, 0x02, 0x37, + 0x04, 0x22, 0x01, 0x15, 0x38, 0x0E, 0x06, 0x09, 0x25, 0xAE, 0x06, 0x03, + 0x01, 0x7F, 0x9F, 0x04, 0x13, 0x01, 0x16, 0x38, 0x0E, 0x06, 0x06, 0x25, + 0x01, 0x01, 0x37, 0x04, 0x07, 0x25, 0x01, 0x04, 0x37, 0x01, 0x00, 0x25, + 0x1A, 0x06, 0x03, 0x01, 0x08, 0x37, 0x00, 0x00, 0x1B, 0x26, 0x05, 0x13, + 0x2F, 0x06, 0x10, 0x87, 0x2E, 0x01, 0x15, 0x0E, 0x06, 0x08, 0x25, 0xAE, + 0x01, 0x00, 0x78, 0x3F, 0x04, 0x01, 0x20, 0x00, 0x00, 0xCF, 0x01, 0x07, + 0x17, 0x01, 0x01, 0x0F, 0x06, 0x02, 0x73, 0x28, 0x00, 0x01, 0x03, 0x00, + 0x29, 0x1A, 0x06, 0x05, 0x02, 0x00, 0x88, 0x3F, 0x00, 0xCF, 0x25, 0x04, + 0x74, 0x00, 0x01, 0x14, 0xD2, 0x01, 0x01, 0xDF, 0x29, 0x26, 0x01, 0x00, + 0xC9, 0x01, 0x16, 0xD2, 0xD8, 0x29, 0x00, 0x00, 0x01, 0x0B, 0xDF, 0x4F, + 0x26, 0x26, 0x01, 0x03, 0x08, 0xDE, 0xDE, 0x18, 0x26, 0x59, 0x06, 0x02, + 0x25, 0x00, 0xDE, 0x1D, 0x26, 0x06, 0x05, 0x85, 0x45, 0xD9, 0x04, 0x77, + 0x25, 0x04, 0x6C, 0x00, 0x21, 0x01, 0x0F, 0xDF, 0x26, 0x93, 0x2C, 0x01, + 0x86, 0x03, 0x10, 0x06, 0x0C, 0x01, 0x04, 0x08, 0xDE, 0x81, 0x2E, 0xDF, + 0x79, 0x2E, 0xDF, 0x04, 0x02, 0x5F, 0xDE, 0x26, 0xDD, 0x85, 0x45, 0xD9, + 0x00, 0x02, 0xA5, 0xA7, 0x08, 0xA3, 0x08, 0xA6, 0x08, 0xA8, 0x08, 0xA4, + 0x08, 0x27, 0x08, 0x03, 0x00, 0x01, 0x01, 0xDF, 0x01, 0x27, 0x8F, 0x2E, + 0x08, 0x92, 0x2E, 0x01, 0x01, 0x0B, 0x08, 0x02, 0x00, 0x06, 0x04, 0x5F, + 0x02, 0x00, 0x08, 0x84, 0x2C, 0x38, 0x09, 0x26, 0x5C, 0x06, 0x24, 0x02, + 0x00, 0x05, 0x04, 0x45, 0x5F, 0x45, 0x60, 0x01, 0x04, 0x09, 0x26, 0x59, + 0x06, 0x03, 0x25, 0x01, 0x00, 0x26, 0x01, 0x04, 0x08, 0x02, 0x00, 0x08, + 0x03, 0x00, 0x45, 0x01, 0x04, 0x08, 0x38, 0x08, 0x45, 0x04, 0x03, 0x25, + 0x01, 0x7F, 0x03, 0x01, 0xDE, 0x95, 0x2C, 0xDD, 0x7B, 0x01, 0x04, 0x19, + 0x7B, 0x01, 0x04, 0x08, 0x01, 0x1C, 0x32, 0x7B, 0x01, 0x20, 0xD9, 0x8E, + 0x8F, 0x2E, 0xDB, 0x92, 0x2E, 0x26, 0x01, 0x01, 0x0B, 0xDD, 0x91, 0x45, + 0x26, 0x06, 0x0F, 0x5E, 0x38, 0x2C, 0x26, 0xC8, 0x05, 0x02, 0x63, 0x28, + 0xDD, 0x45, 0x5F, 0x45, 0x04, 0x6E, 0x61, 0x01, 0x01, 0xDF, 0x01, 0x00, + 0xDF, 0x02, 0x00, 0x06, 0x81, 0x5A, 0x02, 0x00, 0xDD, 0xA5, 0x06, 0x0E, + 0x01, 0x83, 0xFE, 0x01, 0xDD, 0x8A, 0xA5, 0x01, 0x04, 0x09, 0x26, 0xDD, + 0x5E, 0xDB, 0xA7, 0x06, 0x16, 0x01, 0x00, 0xDD, 0x8C, 0xA7, 0x01, 0x04, + 0x09, 0x26, 0xDD, 0x01, 0x02, 0x09, 0x26, 0xDD, 0x01, 0x00, 0xDF, 0x01, + 0x03, 0x09, 0xDA, 0xA3, 0x06, 0x0C, 0x01, 0x01, 0xDD, 0x01, 0x01, 0xDD, + 0x83, 0x2E, 0x01, 0x08, 0x09, 0xDF, 0xA6, 0x06, 0x19, 0x01, 0x0D, 0xDD, + 0xA6, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x01, 0x02, 0x09, 0xDD, 0x43, 0x06, + 0x03, 0x01, 0x03, 0xDC, 0x44, 0x06, 0x03, 0x01, 0x01, 0xDC, 0xA8, 0x26, + 0x06, 0x36, 0x01, 0x0A, 0xDD, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x60, 0xDD, + 0x41, 0x01, 0x00, 0x26, 0x01, 0x82, 0x80, 0x80, 0x80, 0x00, 0x17, 0x06, + 0x0A, 0x01, 0xFD, 0xFF, 0xFF, 0xFF, 0x7F, 0x17, 0x01, 0x1D, 0xDD, 0x26, + 0x01, 0x20, 0x0A, 0x06, 0x0C, 0xA1, 0x11, 0x01, 0x01, 0x17, 0x06, 0x02, + 0x26, 0xDD, 0x5D, 0x04, 0x6E, 0x61, 0x04, 0x01, 0x25, 0xA4, 0x06, 0x0A, + 0x01, 0x0B, 0xDD, 0x01, 0x02, 0xDD, 0x01, 0x82, 0x00, 0xDD, 0x27, 0x26, + 0x06, 0x1F, 0x01, 0x10, 0xDD, 0x01, 0x04, 0x09, 0x26, 0xDD, 0x60, 0xDD, + 0x86, 0x2C, 0x01, 0x00, 0xA1, 0x0F, 0x06, 0x0A, 0x26, 0x1E, 0x26, 0xDF, + 0x85, 0x45, 0xD9, 0x5D, 0x04, 0x72, 0x61, 0x04, 0x01, 0x25, 0x02, 0x01, + 0x59, 0x05, 0x11, 0x01, 0x15, 0xDD, 0x02, 0x01, 0x26, 0xDD, 0x26, 0x06, + 0x06, 0x5E, 0x01, 0x00, 0xDF, 0x04, 0x77, 0x25, 0x00, 0x00, 0x01, 0x10, + 0xDF, 0x7A, 0x2C, 0x26, 0xCD, 0x06, 0x0C, 0xAC, 0x23, 0x26, 0x5F, 0xDE, + 0x26, 0xDD, 0x85, 0x45, 0xD9, 0x04, 0x0D, 0x26, 0xCB, 0x45, 0xAC, 0x22, + 0x26, 0x5D, 0xDE, 0x26, 0xDF, 0x85, 0x45, 0xD9, 0x00, 0x00, 0x9D, 0x01, + 0x14, 0xDF, 0x01, 0x0C, 0xDE, 0x85, 0x01, 0x0C, 0xD9, 0x00, 0x00, 0x52, + 0x26, 0x01, 0x00, 0x0E, 0x06, 0x02, 0x61, 0x00, 0xCF, 0x25, 0x04, 0x73, + 0x00, 0x26, 0xDD, 0xD9, 0x00, 0x00, 0x26, 0xDF, 0xD9, 0x00, 0x01, 0x03, + 0x00, 0x42, 0x25, 0x26, 0x01, 0x10, 0x17, 0x06, 0x06, 0x01, 0x04, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x08, 0x17, 0x06, 0x06, 0x01, 0x03, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x20, 0x17, 0x06, 0x06, 0x01, 0x05, 0xDF, + 0x02, 0x00, 0xDF, 0x26, 0x01, 0x80, 0x40, 0x17, 0x06, 0x06, 0x01, 0x06, + 0xDF, 0x02, 0x00, 0xDF, 0x01, 0x04, 0x17, 0x06, 0x06, 0x01, 0x02, 0xDF, + 0x02, 0x00, 0xDF, 0x00, 0x00, 0x26, 0x01, 0x08, 0x50, 0xDF, 0xDF, 0x00, + 0x00, 0x26, 0x01, 0x10, 0x50, 0xDF, 0xDD, 0x00, 0x00, 0x26, 0x53, 0x06, + 0x02, 0x25, 0x00, 0xCF, 0x25, 0x04, 0x76 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 92, + 96, + 100, + 104, + 108, + 112, + 116, + 120, + 124, + 129, + 134, + 139, + 144, + 149, + 154, + 159, + 164, + 169, + 174, + 179, + 184, + 189, + 194, + 199, + 204, + 209, + 214, + 219, + 224, + 229, + 234, + 239, + 244, + 249, + 254, + 259, + 264, + 269, + 274, + 279, + 284, + 289, + 294, + 303, + 316, + 320, + 345, + 351, + 370, + 381, + 422, + 542, + 546, + 611, + 626, + 637, + 655, + 684, + 694, + 730, + 740, + 818, + 832, + 838, + 897, + 916, + 951, + 1000, + 1076, + 1103, + 1134, + 1145, + 1500, + 1647, + 1671, + 1887, + 1901, + 1910, + 1914, + 2009, + 2033, + 2089, + 2096, + 2107, + 2123, + 2129, + 2140, + 2175, + 2187, + 2193, + 2208, + 2224, + 2417, + 2426, + 2439, + 2448, + 2455, + 2465, + 2571, + 2596, + 2609, + 2625, + 2643, + 2675, + 2709, + 3077, + 3113, + 3126, + 3140, + 3145, + 3150, + 3216, + 3224, + 3232 +}; + +#define T0_INTERPRETED 89 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_ssl_hs_client_init_main, 170) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_ssl_hs_client_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 8: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 9: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 13: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 14: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 15: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 16: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 17: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 18: { + /* anchor-dn-append-name */ + + size_t len; + + len = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->append_name( + CTX->client_auth_vtable, ENG->pad, len); + } + + } + break; + case 19: { + /* anchor-dn-end-name */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->end_name( + CTX->client_auth_vtable); + } + + } + break; + case 20: { + /* anchor-dn-end-name-list */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->end_name_list( + CTX->client_auth_vtable); + } + + } + break; + case 21: { + /* anchor-dn-start-name */ + + size_t len; + + len = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->start_name( + CTX->client_auth_vtable, len); + } + + } + break; + case 22: { + /* anchor-dn-start-name-list */ + + if (CTX->client_auth_vtable != NULL) { + (*CTX->client_auth_vtable)->start_name_list( + CTX->client_auth_vtable); + } + + } + break; + case 23: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 24: { + /* begin-cert */ + + if (ENG->chain_len == 0) { + T0_PUSHi(-1); + } else { + ENG->cert_cur = ENG->chain->data; + ENG->cert_len = ENG->chain->data_len; + ENG->chain ++; + ENG->chain_len --; + T0_PUSH(ENG->cert_len); + } + + } + break; + case 25: { + /* bzero */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + memset(addr, 0, len); + + } + break; + case 26: { + /* can-output? */ + + T0_PUSHi(-(ENG->hlen_out > 0)); + + } + break; + case 27: { + /* co */ + T0_CO(); + } + break; + case 28: { + /* compute-Finished-inner */ + + int prf_id = T0_POP(); + int from_client = T0_POPi(); + unsigned char tmp[48]; + br_tls_prf_seed_chunk seed; + + br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id); + seed.data = tmp; + if (ENG->session.version >= BR_TLS12) { + seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp); + } else { + br_multihash_out(&ENG->mhash, br_md5_ID, tmp); + br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16); + seed.len = 36; + } + prf(ENG->pad, 12, ENG->session.master_secret, + sizeof ENG->session.master_secret, + from_client ? "client finished" : "server finished", + 1, &seed); + + } + break; + case 29: { + /* copy-cert-chunk */ + + size_t clen; + + clen = ENG->cert_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy_P(ENG->pad, ENG->cert_cur, clen); + ENG->cert_cur += clen; + ENG->cert_len -= clen; + T0_PUSH(clen); + + } + break; + case 30: { + /* copy-protocol-name */ + + size_t idx = T0_POP(); + size_t len = strlen(ENG->protocol_names[idx]); + memcpy(ENG->pad, ENG->protocol_names[idx], len); + T0_PUSH(len); + + } + break; + case 31: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 32: { + /* discard-input */ + + ENG->hlen_in = 0; + + } + break; + case 33: { + /* do-client-sign */ + + size_t sig_len; + + sig_len = make_client_sign(CTX); + if (sig_len == 0) { + br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM); + T0_CO(); + } + T0_PUSH(sig_len); + + } + break; + case 34: { + /* do-ecdh */ + + unsigned prf_id = T0_POP(); + unsigned ecdhe = T0_POP(); + int x; + + x = make_pms_ecdh(CTX, ecdhe, prf_id); + if (x < 0) { + br_ssl_engine_fail(ENG, -x); + T0_CO(); + } else { + T0_PUSH(x); + } + + } + break; + case 35: { + /* do-rsa-encrypt */ + + int x; + + x = make_pms_rsa(CTX, T0_POP()); + if (x < 0) { + br_ssl_engine_fail(ENG, -x); + T0_CO(); + } else { + T0_PUSH(x); + } + + } + break; + case 36: { + /* do-static-ecdh */ + + unsigned prf_id = T0_POP(); + + if (make_pms_static_ecdh(CTX, prf_id) < 0) { + br_ssl_engine_fail(ENG, BR_ERR_INVALID_ALGORITHM); + T0_CO(); + } + + } + break; + case 37: { + /* drop */ + (void)T0_POP(); + } + break; + case 38: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 39: { + /* ext-ALPN-length */ + + size_t u, len; + + if (ENG->protocol_names_num == 0) { + T0_PUSH(0); + T0_RET(); + } + len = 6; + for (u = 0; u < ENG->protocol_names_num; u ++) { + len += 1 + strlen(ENG->protocol_names[u]); + } + T0_PUSH(len); + + } + break; + case 40: { + /* fail */ + + br_ssl_engine_fail(ENG, (int)T0_POPi()); + T0_CO(); + + } + break; + case 41: { + /* flush-record */ + + br_ssl_engine_flush_record(ENG); + + } + break; + case 42: { + /* get-client-chain */ + + uint32_t auth_types; + + auth_types = T0_POP(); + if (CTX->client_auth_vtable != NULL) { + br_ssl_client_certificate ux; + + (*CTX->client_auth_vtable)->choose(CTX->client_auth_vtable, + CTX, auth_types, &ux); + CTX->auth_type = (unsigned char)ux.auth_type; + CTX->hash_id = (unsigned char)ux.hash_id; + ENG->chain = ux.chain; + ENG->chain_len = ux.chain_len; + } else { + CTX->hash_id = 0; + ENG->chain_len = 0; + } + + } + break; + case 43: { + /* get-key-type-usages */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + unsigned usages; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, &usages); + if (pk == NULL) { + T0_PUSH(0); + } else { + T0_PUSH(pk->key_type | usages); + } + + } + break; + case 44: { + /* get16 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 45: { + /* get32 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 46: { + /* get8 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*((unsigned char *)ENG + addr)); + + } + break; + case 47: { + /* has-input? */ + + T0_PUSHi(-(ENG->hlen_in != 0)); + + } + break; + case 48: { + /* memcmp */ + + size_t len = (size_t)T0_POP(); + void *addr2 = (unsigned char *)ENG + (size_t)T0_POP(); + void *addr1 = (unsigned char *)ENG + (size_t)T0_POP(); + int x = memcmp(addr1, addr2, len); + T0_PUSH((uint32_t)-(x == 0)); + + } + break; + case 49: { + /* memcpy */ + + size_t len = (size_t)T0_POP(); + void *src = (unsigned char *)ENG + (size_t)T0_POP(); + void *dst = (unsigned char *)ENG + (size_t)T0_POP(); + memcpy(dst, src, len); + + } + break; + case 50: { + /* mkrand */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + br_hmac_drbg_generate(&ENG->rng, addr, len); + + } + break; + case 51: { + /* more-incoming-bytes? */ + + T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG)); + + } + break; + case 52: { + /* multihash-init */ + + br_multihash_init(&ENG->mhash); + + } + break; + case 53: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 54: { + /* not */ + + uint32_t a = T0_POP(); + T0_PUSH(~a); + + } + break; + case 55: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 56: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 57: { + /* read-chunk-native */ + + size_t clen = ENG->hlen_in; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy_P((unsigned char *)ENG + addr, ENG->hbuf_in, clen); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_in += clen; + ENG->hlen_in -= clen; + } + + } + break; + case 58: { + /* read8-native */ + + if (ENG->hlen_in > 0) { + unsigned char x; + + x = pgm_read_byte(ENG->hbuf_in ++); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + T0_PUSH(x); + ENG->hlen_in --; + } else { + T0_PUSHi(-1); + } + + } + break; + case 59: { + /* set-mfln-status */ + + int val = T0_POP(); + ENG->max_frag_len_negotiated = val; + + } + break; + case 60: { + /* set-server-curve */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, NULL); + CTX->server_curve = + (pk->key_type == BR_KEYTYPE_EC) ? pk->key.ec.curve : 0; + + } + break; + case 61: { + /* set16 */ + + size_t addr = (size_t)T0_POP(); + *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP(); + + } + break; + case 62: { + /* set32 */ + + size_t addr = (size_t)T0_POP(); + *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP(); + + } + break; + case 63: { + /* set8 */ + + size_t addr = (size_t)T0_POP(); + *((unsigned char *)ENG + addr) = (unsigned char)T0_POP(); + + } + break; + case 64: { + /* strlen */ + + void *str = (unsigned char *)ENG + (size_t)T0_POP(); + T0_PUSH((uint32_t)strlen(str)); + + } + break; + case 65: { + /* supported-curves */ + + uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves; + T0_PUSH(x); + + } + break; + case 66: { + /* supported-hash-functions */ + + int i; + unsigned x, num; + + x = 0; + num = 0; + for (i = br_sha1_ID; i <= br_sha512_ID; i ++) { + if (br_multihash_getimpl(&ENG->mhash, i)) { + x |= 1U << i; + num ++; + } + } + T0_PUSH(x); + T0_PUSH(num); + + } + break; + case 67: { + /* supports-ecdsa? */ + + T0_PUSHi(-(ENG->iecdsa != 0)); + + } + break; + case 68: { + /* supports-rsa-sign? */ + + T0_PUSHi(-(ENG->irsavrfy != 0)); + + } + break; + case 69: { + /* swap */ + T0_SWAP(); + } + break; + case 70: { + /* switch-aesccm-in */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 71: { + /* switch-aesccm-out */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 72: { + /* switch-aesgcm-in */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 73: { + /* switch-aesgcm-out */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 74: { + /* switch-cbc-in */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len); + + } + break; + case 75: { + /* switch-cbc-out */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len); + + } + break; + case 76: { + /* switch-chapol-in */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id); + + } + break; + case 77: { + /* switch-chapol-out */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id); + + } + break; + case 78: { + /* test-protocol-name */ + + size_t len = T0_POP(); + size_t u; + + for (u = 0; u < ENG->protocol_names_num; u ++) { + const char *name; + + name = ENG->protocol_names[u]; + if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) { + T0_PUSH(u); + T0_RET(); + } + } + T0_PUSHi(-1); + + } + break; + case 79: { + /* total-chain-length */ + + size_t u; + uint32_t total; + + total = 0; + for (u = 0; u < ENG->chain_len; u ++) { + total += 3 + (uint32_t)ENG->chain[u].data_len; + } + T0_PUSH(total); + + } + break; + case 80: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + case 81: { + /* verify-SKE-sig */ + + size_t sig_len = T0_POP(); + int use_rsa = T0_POPi(); + int hash = T0_POPi(); + + T0_PUSH(verify_SKE_sig(CTX, hash, use_rsa, sig_len)); + + } + break; + case 82: { + /* write-blob-chunk */ + + size_t clen = ENG->hlen_out; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen); + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_out += clen; + ENG->hlen_out -= clen; + } + + } + break; + case 83: { + /* write8-native */ + + unsigned char x; + + x = (unsigned char)T0_POP(); + if (ENG->hlen_out > 0) { + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + *ENG->hbuf_out ++ = x; + ENG->hlen_out --; + T0_PUSHi(-1); + } else { + T0_PUSHi(0); + } + + } + break; + case 84: { + /* x509-append */ + + const br_x509_class *xc; + size_t len; + + xc = *(ENG->x509ctx); + len = T0_POP(); + xc->append(ENG->x509ctx, ENG->pad, len); + + } + break; + case 85: { + /* x509-end-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->end_cert(ENG->x509ctx); + + } + break; + case 86: { + /* x509-end-chain */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + T0_PUSH(xc->end_chain(ENG->x509ctx)); + + } + break; + case 87: { + /* x509-start-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->start_cert(ENG->x509ctx, T0_POP()); + + } + break; + case 88: { + /* x509-start-chain */ + + const br_x509_class *xc; + uint32_t bc; + + bc = T0_POP(); + xc = *(ENG->x509ctx); + xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c new file mode 100644 index 0000000000..9bce996450 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_hs_server.c @@ -0,0 +1,1995 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_ssl_hs_server_init_main(void *t0ctx); + +void br_ssl_hs_server_run(void *t0ctx); + + + +#include +#include + +#include "t_inner.h" + +/* + * This macro evaluates to a pointer to the current engine context. + */ +#define ENG ((br_ssl_engine_context *)(void *)((unsigned char *)t0ctx - offsetof(br_ssl_engine_context, cpu))) + + + + + +/* + * This macro evaluates to a pointer to the server context, under that + * specific name. It must be noted that since the engine context is the + * first field of the br_ssl_server_context structure ('eng'), then + * pointers values of both types are interchangeable, modulo an + * appropriate cast. This also means that "addresses" computed as offsets + * within the structure work for both kinds of context. + */ +#define CTX ((br_ssl_server_context *)ENG) + +/* + * Decrypt the pre-master secret (RSA key exchange). + */ +static void +do_rsa_decrypt(br_ssl_server_context *ctx, int prf_id, + unsigned char *epms, size_t len) +{ + uint32_t x; + unsigned char rpms[48]; + + /* + * Decrypt the PMS. + */ + x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, epms, &len); + + /* + * Set the first two bytes to the maximum supported client + * protocol version. These bytes are used for version rollback + * detection; forceing the two bytes will make the master secret + * wrong if the bytes are not correct. This process is + * recommended by RFC 5246 (section 7.4.7.1). + */ + br_enc16be(epms, ctx->client_max_version); + + /* + * Make a random PMS and copy it above the decrypted value if the + * decryption failed. Note that we use a constant-time conditional + * copy. + */ + br_hmac_drbg_generate(&ctx->eng.rng, rpms, sizeof rpms); + br_ccopy(x ^ 1, epms, rpms, sizeof rpms); + + /* + * Compute master secret. + */ + br_ssl_engine_compute_master(&ctx->eng, prf_id, epms, 48); + + /* + * Clear the pre-master secret from RAM: it is normally a buffer + * in the context, hence potentially long-lived. + */ + memset(epms, 0, len); +} + +/* + * Common part for ECDH and ECDHE. + */ +static void +ecdh_common(br_ssl_server_context *ctx, int prf_id, + unsigned char *xcoor, size_t xcoor_len, uint32_t ctl) +{ + unsigned char rpms[80]; + + if (xcoor_len > sizeof rpms) { + xcoor_len = sizeof rpms; + ctl = 0; + } + + /* + * Make a random PMS and copy it above the decrypted value if the + * decryption failed. Note that we use a constant-time conditional + * copy. + */ + br_hmac_drbg_generate(&ctx->eng.rng, rpms, xcoor_len); + br_ccopy(ctl ^ 1, xcoor, rpms, xcoor_len); + + /* + * Compute master secret. + */ + br_ssl_engine_compute_master(&ctx->eng, prf_id, xcoor, xcoor_len); + + /* + * Clear the pre-master secret from RAM: it is normally a buffer + * in the context, hence potentially long-lived. + */ + memset(xcoor, 0, xcoor_len); +} + +/* + * Do the ECDH key exchange (not ECDHE). + */ +static void +do_ecdh(br_ssl_server_context *ctx, int prf_id, + unsigned char *cpoint, size_t cpoint_len) +{ + uint32_t x; + + /* + * Finalise the key exchange. + */ + x = (*ctx->policy_vtable)->do_keyx(ctx->policy_vtable, + cpoint, &cpoint_len); + ecdh_common(ctx, prf_id, cpoint, cpoint_len, x); +} + +/* + * Do the full static ECDH key exchange. When this function is called, + * it has already been verified that the cipher suite uses ECDH (not ECDHE), + * and the client's public key (from its certificate) has type EC and is + * apt for key exchange. + */ +static void +do_static_ecdh(br_ssl_server_context *ctx, int prf_id) +{ + unsigned char cpoint[133]; + size_t cpoint_len; + const br_x509_class **xc; + const br_x509_pkey *pk; + + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + cpoint_len = pk->key.ec.qlen; + if (cpoint_len > sizeof cpoint) { + /* + * If the point is larger than our buffer then we need to + * restrict it. Length 2 is not a valid point length, so + * the ECDH will fail. + */ + cpoint_len = 2; + } + memcpy(cpoint, pk->key.ec.q, cpoint_len); + do_ecdh(ctx, prf_id, cpoint, cpoint_len); +} + +static size_t +hash_data(br_ssl_server_context *ctx, + void *dst, int hash_id, const void *src, size_t len) +{ + const br_hash_class *hf; + br_hash_compat_context hc; + + if (hash_id == 0) { + unsigned char tmp[36]; + + hf = br_multihash_getimpl(&ctx->eng.mhash, br_md5_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp); + hf = br_multihash_getimpl(&ctx->eng.mhash, br_sha1_ID); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, tmp + 16); + memcpy(dst, tmp, 36); + return 36; + } else { + hf = br_multihash_getimpl(&ctx->eng.mhash, hash_id); + if (hf == NULL) { + return 0; + } + hf->init(&hc.vtable); + hf->update(&hc.vtable, src, len); + hf->out(&hc.vtable, dst); + return (hf->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK; + } +} + +/* + * Do the ECDHE key exchange (part 1: generation of transient key, and + * computing of the point to send to the client). Returned value is the + * signature length (in bytes), or -x on error (with x being an error + * code). The encoded point is written in the ecdhe_point[] context buffer + * (length in ecdhe_point_len). + */ +static int +do_ecdhe_part1(br_ssl_server_context *ctx, int curve) +{ + unsigned algo_id; + unsigned mask; + const unsigned char *order; + size_t olen, glen; + size_t hv_len, sig_len; + + if (!((ctx->eng.iec->supported_curves >> curve) & 1)) { + return -BR_ERR_INVALID_ALGORITHM; + } + ctx->eng.ecdhe_curve = curve; + + /* + * Generate our private key. We need a non-zero random value + * which is lower than the curve order, in a "large enough" + * range. We force the top bit to 0 and bottom bit to 1, which + * does the trick. Note that contrary to what happens in ECDSA, + * this is not a problem if we do not cover the full range of + * possible values. + */ + order = ctx->eng.iec->order(curve, &olen); + mask = 0xFF; + while (mask >= pgm_read_byte(&order[0])) { + mask >>= 1; + } + br_hmac_drbg_generate(&ctx->eng.rng, ctx->ecdhe_key, olen); + ctx->ecdhe_key[0] &= mask; + ctx->ecdhe_key[olen - 1] |= 0x01; + ctx->ecdhe_key_len = olen; + + /* + * Compute our ECDH point. + */ + glen = ctx->eng.iec->mulgen(ctx->eng.ecdhe_point, + ctx->ecdhe_key, olen, curve); + ctx->eng.ecdhe_point_len = glen; + + /* + * Assemble the message to be signed, and possibly hash it. + */ + memcpy(ctx->eng.pad, ctx->eng.client_random, 32); + memcpy(ctx->eng.pad + 32, ctx->eng.server_random, 32); + ctx->eng.pad[64 + 0] = 0x03; + ctx->eng.pad[64 + 1] = 0x00; + ctx->eng.pad[64 + 2] = curve; + ctx->eng.pad[64 + 3] = ctx->eng.ecdhe_point_len; + memcpy(ctx->eng.pad + 64 + 4, + ctx->eng.ecdhe_point, ctx->eng.ecdhe_point_len); + hv_len = 64 + 4 + ctx->eng.ecdhe_point_len; + algo_id = ctx->sign_hash_id; + if (algo_id >= (unsigned)0xFF00) { + hv_len = hash_data(ctx, ctx->eng.pad, algo_id & 0xFF, + ctx->eng.pad, hv_len); + if (hv_len == 0) { + return -BR_ERR_INVALID_ALGORITHM; + } + } + + sig_len = (*ctx->policy_vtable)->do_sign(ctx->policy_vtable, + algo_id, ctx->eng.pad, hv_len, sizeof ctx->eng.pad); + return sig_len ? (int)sig_len : -BR_ERR_INVALID_ALGORITHM; +} + +/* + * Do the ECDHE key exchange (part 2: computation of the shared secret + * from the point sent by the client). + */ +static void +do_ecdhe_part2(br_ssl_server_context *ctx, int prf_id, + unsigned char *cpoint, size_t cpoint_len) +{ + int curve; + uint32_t ctl; + size_t xoff, xlen; + + curve = ctx->eng.ecdhe_curve; + + /* + * Finalise the key exchange. + */ + ctl = ctx->eng.iec->mul(cpoint, cpoint_len, + ctx->ecdhe_key, ctx->ecdhe_key_len, curve); + xoff = ctx->eng.iec->xoff(curve, &xlen); + ecdh_common(ctx, prf_id, cpoint + xoff, xlen, ctl); + + /* + * Clear the ECDHE private key. Forward Secrecy is achieved insofar + * as that key does not get stolen, so we'd better destroy it + * as soon as it ceases to be useful. + */ + memset(ctx->ecdhe_key, 0, ctx->ecdhe_key_len); +} + +/* + * Offset for hash value within the pad (when obtaining all hash values, + * in preparation for verification of the CertificateVerify message). + * Order is MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512; last value + * is used to get the total length. + */ +static const unsigned char HASH_PAD_OFF[] = { 0, 16, 36, 64, 96, 144, 208 }; + +/* + * OID for hash functions in RSA signatures. + */ +static const unsigned char HASH_OID[][10] PROGMEM = { + { 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A }, //HASH_OID_SHA1, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 }, // HASH_OID_SHA224, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 }, // HASH_OID_SHA256, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 }, // HASH_OID_SHA384, + { 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 } // HASH_OID_SHA512 +}; + +/* + * Verify the signature in CertificateVerify. Returned value is 0 on + * success, or a non-zero error code. Lack of implementation of the + * designated signature algorithm is reported as a "bad signature" + * error (because it means that the peer did not honour our advertised + * set of supported signature algorithms). + */ +static int +verify_CV_sig(br_ssl_server_context *ctx, size_t sig_len) +{ + const br_x509_class **xc; + const br_x509_pkey *pk; + int id; + + id = ctx->hash_CV_id; + xc = ctx->eng.x509ctx; + pk = (*xc)->get_pkey(xc, NULL); + if (pk->key_type == BR_KEYTYPE_RSA) { + unsigned char tmp[64]; + unsigned char hash_oid_ram[10]; + const unsigned char *hash_oid; + + if (id == 0) { + hash_oid = NULL; + } else { + memcpy_P(hash_oid_ram, HASH_OID[id - 2], sizeof(HASH_OID[0])); + hash_oid = hash_oid_ram; + } + if (ctx->eng.irsavrfy == 0) { + return BR_ERR_BAD_SIGNATURE; + } + if (!ctx->eng.irsavrfy(ctx->eng.pad, sig_len, + hash_oid, ctx->hash_CV_len, &pk->key.rsa, tmp) + || memcmp(tmp, ctx->hash_CV, ctx->hash_CV_len) != 0) + { + return BR_ERR_BAD_SIGNATURE; + } + } else { + if (ctx->eng.iecdsa == 0) { + return BR_ERR_BAD_SIGNATURE; + } + if (!ctx->eng.iecdsa(ctx->eng.iec, + ctx->hash_CV, ctx->hash_CV_len, + &pk->key.ec, ctx->eng.pad, sig_len)) + { + return BR_ERR_BAD_SIGNATURE; + } + } + return 0; +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x00, 0x0A, 0x00, 0x24, 0x00, 0x2F, 0x01, 0x24, 0x00, 0x35, 0x02, + 0x24, 0x00, 0x3C, 0x01, 0x44, 0x00, 0x3D, 0x02, 0x44, 0x00, 0x9C, 0x03, + 0x04, 0x00, 0x9D, 0x04, 0x05, 0xC0, 0x03, 0x40, 0x24, 0xC0, 0x04, 0x41, + 0x24, 0xC0, 0x05, 0x42, 0x24, 0xC0, 0x08, 0x20, 0x24, 0xC0, 0x09, 0x21, + 0x24, 0xC0, 0x0A, 0x22, 0x24, 0xC0, 0x0D, 0x30, 0x24, 0xC0, 0x0E, 0x31, + 0x24, 0xC0, 0x0F, 0x32, 0x24, 0xC0, 0x12, 0x10, 0x24, 0xC0, 0x13, 0x11, + 0x24, 0xC0, 0x14, 0x12, 0x24, 0xC0, 0x23, 0x21, 0x44, 0xC0, 0x24, 0x22, + 0x55, 0xC0, 0x25, 0x41, 0x44, 0xC0, 0x26, 0x42, 0x55, 0xC0, 0x27, 0x11, + 0x44, 0xC0, 0x28, 0x12, 0x55, 0xC0, 0x29, 0x31, 0x44, 0xC0, 0x2A, 0x32, + 0x55, 0xC0, 0x2B, 0x23, 0x04, 0xC0, 0x2C, 0x24, 0x05, 0xC0, 0x2D, 0x43, + 0x04, 0xC0, 0x2E, 0x44, 0x05, 0xC0, 0x2F, 0x13, 0x04, 0xC0, 0x30, 0x14, + 0x05, 0xC0, 0x31, 0x33, 0x04, 0xC0, 0x32, 0x34, 0x05, 0xC0, 0x9C, 0x06, + 0x04, 0xC0, 0x9D, 0x07, 0x04, 0xC0, 0xA0, 0x08, 0x04, 0xC0, 0xA1, 0x09, + 0x04, 0xC0, 0xAC, 0x26, 0x04, 0xC0, 0xAD, 0x27, 0x04, 0xC0, 0xAE, 0x28, + 0x04, 0xC0, 0xAF, 0x29, 0x04, 0xCC, 0xA8, 0x15, 0x04, 0xCC, 0xA9, 0x25, + 0x04, 0x00, 0x00 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0B, 0x00, 0x00, 0x01, 0x00, 0x0E, 0x00, 0x00, 0x01, + 0x00, 0x0F, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x01, 0x08, + 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x02, 0x08, 0x00, 0x00, + 0x01, 0x02, 0x09, 0x00, 0x00, 0x29, 0x29, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_CCS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FINISHED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_FRAGLEN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_HANDSHAKE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_PARAM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SECRENEG), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_SIGNATURE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_BAD_VERSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_INVALID_ALGORITHM), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_NO_CLIENT_AUTH), 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OK), + 0x00, 0x00, 0x01, T0_INT1(BR_ERR_OVERSIZED_ID), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_WRONG_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, action)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, alert)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, application_data)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, cipher_suite)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_server_context, client_max_version)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, client_random)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_server_context, client_suites)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, client_suites_num)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, close_received)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, curves)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, ecdhe_point_len)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, flags)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_server_context, hashes)), + 0x00, 0x00, 0x7B, 0x01, + T0_INT2(BR_MAX_CIPHER_SUITES * sizeof(br_suite_translated)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, log_max_frag_len)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, pad)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, peer_log_max_frag_len)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, protocol_names_num)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, record_type_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, record_type_out)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, reneg)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, saved_finished)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, selected_protocol)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, server_name)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, server_random)), 0x00, 0x00, + 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, session_id_len)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, shutdown_recv)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_server_context, sign_hash_id)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_buf)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, suites_num)), 0x00, + 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, session) + offsetof(br_ssl_session_parameters, version)), + 0x00, 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_in)), + 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_ssl_engine_context, version_max)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_min)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(br_ssl_engine_context, version_out)), + 0x00, 0x00, 0x09, 0x2A, 0x5D, 0x06, 0x02, 0x6A, 0x2B, 0x00, 0x00, 0x01, + 0x01, 0x00, 0x01, 0x03, 0x00, 0x9B, 0x2A, 0x63, 0x47, 0x9F, 0x2A, 0x05, + 0x04, 0x65, 0x01, 0x00, 0x00, 0x02, 0x00, 0x0F, 0x06, 0x02, 0x9F, 0x00, + 0x63, 0x04, 0x6B, 0x00, 0x06, 0x02, 0x6A, 0x2B, 0x00, 0x00, 0x2A, 0x8B, + 0x47, 0x05, 0x03, 0x01, 0x0C, 0x08, 0x47, 0x78, 0x2E, 0xA8, 0x1C, 0x85, + 0x01, 0x0C, 0x33, 0x00, 0x00, 0x2A, 0x22, 0x01, 0x08, 0x0C, 0x47, 0x61, + 0x22, 0x08, 0x00, 0x01, 0x03, 0x00, 0x77, 0x30, 0x02, 0x00, 0x38, 0x13, + 0x01, 0x01, 0x0C, 0x77, 0x42, 0x2C, 0x19, 0x38, 0x06, 0x07, 0x02, 0x00, + 0xD0, 0x03, 0x00, 0x04, 0x75, 0x01, 0x00, 0xC7, 0x02, 0x00, 0x2A, 0x19, + 0x13, 0x06, 0x02, 0x71, 0x2B, 0xD0, 0x04, 0x76, 0x00, 0x01, 0x00, 0x77, + 0x42, 0x01, 0x16, 0x89, 0x42, 0x01, 0x00, 0x8C, 0x40, 0x36, 0xB1, 0x35, + 0x06, 0x02, 0x73, 0x2B, 0x06, 0x0A, 0xD7, 0x01, 0x00, 0xD3, 0x01, 0x00, + 0xAD, 0x04, 0x80, 0x46, 0xD7, 0xD4, 0x29, 0xD9, 0x50, 0x06, 0x01, 0xD5, + 0xD8, 0x2C, 0x50, 0x06, 0x31, 0x01, 0x00, 0xAE, 0x2A, 0x5D, 0x06, 0x0F, + 0x01, 0x02, 0xA4, 0x05, 0x02, 0x37, 0x2B, 0x29, 0xB2, 0xB0, 0x2A, 0xC9, + 0x29, 0x04, 0x19, 0x2A, 0x5F, 0x06, 0x0B, 0x29, 0x01, 0x02, 0xA4, 0x05, + 0x02, 0x70, 0x2B, 0xB2, 0x04, 0x0A, 0xB4, 0x2A, 0x05, 0x04, 0x29, 0xAB, + 0x04, 0x02, 0xB3, 0xAF, 0x04, 0x01, 0xB2, 0x01, 0x00, 0xAD, 0x01, 0x00, + 0xD3, 0x3E, 0x01, 0x01, 0x77, 0x42, 0x01, 0x17, 0x89, 0x42, 0x00, 0x00, + 0x3A, 0x3A, 0x00, 0x01, 0x03, 0x00, 0x2C, 0x19, 0x38, 0x06, 0x04, 0xCF, + 0x29, 0x04, 0x78, 0x01, 0x02, 0x02, 0x00, 0xC6, 0x19, 0x38, 0x06, 0x04, + 0xCF, 0x29, 0x04, 0x78, 0x02, 0x00, 0x01, 0x84, 0x00, 0x08, 0x2B, 0x00, + 0x00, 0x81, 0x2F, 0x47, 0x12, 0x01, 0x01, 0x13, 0x37, 0x00, 0x00, 0x2A, + 0x05, 0x04, 0x29, 0x01, 0x7F, 0x00, 0x01, 0x00, 0xA2, 0x12, 0x01, 0x01, + 0x13, 0x5F, 0x06, 0x03, 0x61, 0x04, 0x75, 0x47, 0x29, 0x00, 0x00, 0x01, + 0x7F, 0xA1, 0xCF, 0x2A, 0x01, 0x07, 0x13, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x0D, 0x29, 0x01, 0x10, 0x13, 0x06, 0x05, 0x01, 0x00, 0x77, 0x42, 0xC5, + 0x04, 0x33, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x2A, 0x29, 0x29, 0x8A, 0x30, + 0x01, 0x01, 0x0F, 0x01, 0x01, 0xA4, 0x39, 0x06, 0x18, 0xC8, 0x2C, 0x19, + 0x38, 0x06, 0x04, 0xCF, 0x29, 0x04, 0x78, 0x01, 0x80, 0x64, 0xC7, 0x01, + 0x01, 0x77, 0x42, 0x01, 0x17, 0x89, 0x42, 0x04, 0x03, 0x01, 0x00, 0xA1, + 0x04, 0x03, 0x73, 0x2B, 0x29, 0x04, 0xFF, 0x32, 0x01, 0x2A, 0x03, 0x00, + 0x09, 0x2A, 0x5D, 0x06, 0x02, 0x6A, 0x2B, 0x02, 0x00, 0x00, 0x00, 0x9C, + 0x01, 0x0F, 0x13, 0x00, 0x00, 0x76, 0x30, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x10, 0x29, 0x2A, 0x01, 0x01, 0x0E, 0x06, 0x03, 0x29, 0x01, 0x02, 0x76, + 0x42, 0x01, 0x00, 0x04, 0x21, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x14, 0x29, + 0x01, 0x00, 0x76, 0x42, 0x2A, 0x01, 0x80, 0x64, 0x0F, 0x06, 0x05, 0x01, + 0x82, 0x00, 0x08, 0x2B, 0x5F, 0x04, 0x07, 0x29, 0x01, 0x82, 0x00, 0x08, + 0x2B, 0x29, 0x00, 0x00, 0x01, 0x00, 0x31, 0x06, 0x05, 0x3D, 0xA9, 0x39, + 0x04, 0x78, 0x2A, 0x06, 0x04, 0x01, 0x01, 0x91, 0x42, 0x00, 0x00, 0x01, + 0x1F, 0x13, 0x01, 0x12, 0x0F, 0x05, 0x02, 0x74, 0x2B, 0x78, 0x2E, 0x2A, + 0xCB, 0x05, 0x02, 0x73, 0x2B, 0xA8, 0x28, 0x00, 0x02, 0x87, 0x2E, 0x05, + 0x02, 0xBC, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x01, 0x7E, 0x03, 0x00, 0x2A, + 0x06, 0x17, 0xC2, 0x2A, 0x03, 0x01, 0x85, 0x47, 0xB6, 0x02, 0x01, 0x51, + 0x2A, 0x02, 0x00, 0x53, 0x06, 0x04, 0x03, 0x00, 0x04, 0x01, 0x29, 0x04, + 0x66, 0x9D, 0x9D, 0x02, 0x00, 0x61, 0x8C, 0x40, 0x00, 0x00, 0x31, 0x06, + 0x0B, 0x88, 0x30, 0x01, 0x14, 0x0E, 0x06, 0x02, 0x73, 0x2B, 0x04, 0x11, + 0xCF, 0x01, 0x07, 0x13, 0x2A, 0x01, 0x02, 0x0E, 0x06, 0x06, 0x06, 0x02, + 0x73, 0x2B, 0x04, 0x70, 0x29, 0xC3, 0x01, 0x01, 0x0E, 0x35, 0x39, 0x06, + 0x02, 0x66, 0x2B, 0x2A, 0x01, 0x01, 0xCA, 0x38, 0xB5, 0x00, 0x01, 0xBA, + 0x01, 0x0B, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x2A, 0x01, 0x03, 0x0F, 0x06, + 0x08, 0xC1, 0x06, 0x02, 0x6A, 0x2B, 0x47, 0x29, 0x00, 0x47, 0x5C, 0xC1, + 0xA7, 0x2A, 0x06, 0x23, 0xC1, 0xA7, 0x2A, 0x5B, 0x2A, 0x06, 0x18, 0x2A, + 0x01, 0x82, 0x00, 0x10, 0x06, 0x05, 0x01, 0x82, 0x00, 0x04, 0x01, 0x2A, + 0x03, 0x00, 0x85, 0x02, 0x00, 0xB6, 0x02, 0x00, 0x58, 0x04, 0x65, 0x9D, + 0x59, 0x04, 0x5A, 0x9D, 0x9D, 0x5A, 0x2A, 0x06, 0x02, 0x37, 0x00, 0x29, + 0x2D, 0x00, 0x02, 0x2A, 0x01, 0x20, 0x13, 0x05, 0x02, 0x74, 0x2B, 0x01, + 0x0F, 0x13, 0x03, 0x00, 0xB0, 0x95, 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, + 0x23, 0xC0, 0x2A, 0x01, 0x81, 0x7F, 0x13, 0x61, 0x01, 0x01, 0x12, 0x02, + 0x00, 0x0F, 0x05, 0x02, 0x6C, 0x2B, 0x01, 0x08, 0x12, 0x2A, 0x01, 0x02, + 0x0B, 0x3A, 0x01, 0x06, 0x10, 0x39, 0x06, 0x02, 0x6E, 0x2B, 0x04, 0x0D, + 0x02, 0x00, 0x01, 0x01, 0x0F, 0x06, 0x04, 0x01, 0x00, 0x04, 0x02, 0x01, + 0x02, 0x20, 0x05, 0x02, 0x6E, 0x2B, 0xC0, 0x2A, 0x03, 0x01, 0x2A, 0x01, + 0x84, 0x00, 0x10, 0x06, 0x02, 0x6F, 0x2B, 0x85, 0x47, 0xB6, 0x02, 0x01, + 0x55, 0x2A, 0x06, 0x01, 0x2B, 0x29, 0x9D, 0x00, 0x00, 0x1D, 0xBA, 0x01, + 0x0F, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x00, 0x0A, 0xBA, 0x01, 0x01, 0x0F, + 0x05, 0x02, 0x73, 0x2B, 0xC0, 0x2A, 0x03, 0x00, 0x79, 0x40, 0x7A, 0x01, + 0x20, 0xB6, 0xC2, 0x2A, 0x01, 0x20, 0x10, 0x06, 0x02, 0x72, 0x2B, 0x2A, + 0x90, 0x42, 0x8F, 0x47, 0xB6, 0x1A, 0x03, 0x01, 0xC0, 0xA7, 0x01, 0x00, + 0x03, 0x02, 0x01, 0x00, 0x03, 0x03, 0x83, 0xA2, 0x17, 0x3A, 0x08, 0x03, + 0x04, 0x03, 0x05, 0x2A, 0x06, 0x80, 0x6D, 0xC0, 0x2A, 0x03, 0x06, 0x02, + 0x01, 0x06, 0x0A, 0x2A, 0x78, 0x2E, 0x0F, 0x06, 0x04, 0x01, 0x7F, 0x03, + 0x03, 0x2A, 0x01, 0x81, 0x7F, 0x0F, 0x06, 0x0A, 0x8A, 0x30, 0x06, 0x02, + 0x6B, 0x2B, 0x01, 0x7F, 0x03, 0x02, 0x2A, 0x01, 0x81, 0xAC, 0x00, 0x0F, + 0x06, 0x11, 0x02, 0x00, 0x98, 0x2E, 0x11, 0x02, 0x00, 0x97, 0x2E, 0x0B, + 0x13, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x00, 0xC4, 0x2A, 0x5D, 0x06, 0x03, + 0x29, 0x04, 0x26, 0x01, 0x00, 0xA4, 0x06, 0x0B, 0x01, 0x02, 0x0C, 0x7B, + 0x08, 0x02, 0x06, 0x47, 0x40, 0x04, 0x16, 0x29, 0x02, 0x05, 0x02, 0x04, + 0x11, 0x06, 0x02, 0x69, 0x2B, 0x02, 0x06, 0x02, 0x05, 0x40, 0x02, 0x05, + 0x01, 0x04, 0x08, 0x03, 0x05, 0x04, 0xFF, 0x0F, 0x29, 0x01, 0x00, 0x03, + 0x07, 0xC2, 0xA7, 0x2A, 0x06, 0x09, 0xC2, 0x05, 0x04, 0x01, 0x7F, 0x03, + 0x07, 0x04, 0x74, 0x9D, 0x01, 0x00, 0x8D, 0x42, 0x01, 0x88, 0x04, 0x82, + 0x41, 0x01, 0x84, 0x80, 0x80, 0x00, 0x7E, 0x41, 0x2A, 0x06, 0x80, 0x4E, + 0xC0, 0xA7, 0x2A, 0x06, 0x80, 0x47, 0xC0, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x04, 0x29, 0xB9, 0x04, 0x39, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x04, 0x29, + 0xB7, 0x04, 0x2F, 0x01, 0x83, 0xFE, 0x01, 0x3A, 0x0F, 0x06, 0x04, 0x29, + 0xB8, 0x04, 0x23, 0x01, 0x0D, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xBE, 0x04, + 0x19, 0x01, 0x0A, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xBF, 0x04, 0x0F, 0x01, + 0x10, 0x3A, 0x0F, 0x06, 0x04, 0x29, 0xAC, 0x04, 0x05, 0x29, 0xBC, 0x01, + 0x00, 0x29, 0x04, 0xFF, 0x35, 0x9D, 0x9D, 0x02, 0x01, 0x02, 0x03, 0x13, + 0x03, 0x01, 0x02, 0x00, 0x5D, 0x06, 0x08, 0x79, 0x2E, 0x99, 0x40, 0x01, + 0x80, 0x56, 0xA3, 0x97, 0x2E, 0x2A, 0x02, 0x00, 0x10, 0x06, 0x03, 0x29, + 0x02, 0x00, 0x2A, 0x01, 0x86, 0x00, 0x0B, 0x06, 0x02, 0x6D, 0x2B, 0x02, + 0x00, 0x98, 0x2E, 0x0B, 0x06, 0x04, 0x01, 0x80, 0x46, 0xA3, 0x02, 0x01, + 0x06, 0x10, 0x95, 0x2E, 0x02, 0x00, 0x0D, 0x06, 0x05, 0x29, 0x95, 0x2E, + 0x04, 0x04, 0x01, 0x00, 0x03, 0x01, 0x2A, 0x95, 0x40, 0x2A, 0x96, 0x40, + 0x2A, 0x99, 0x40, 0x01, 0x86, 0x03, 0x11, 0x03, 0x08, 0x02, 0x02, 0x06, + 0x04, 0x01, 0x02, 0x8A, 0x42, 0x8A, 0x30, 0x05, 0x04, 0x01, 0x01, 0x8A, + 0x42, 0x02, 0x07, 0x05, 0x03, 0x01, 0x28, 0xA3, 0x44, 0x29, 0x01, 0x82, + 0x01, 0x07, 0x01, 0xFC, 0x80, 0x00, 0x39, 0x82, 0x2F, 0x13, 0x2A, 0x82, + 0x41, 0x2A, 0x01, 0x81, 0x7F, 0x13, 0x5E, 0x37, 0x47, 0x01, 0x08, 0x12, + 0x5E, 0x01, 0x02, 0x13, 0x39, 0x01, 0x0C, 0x0C, 0x03, 0x09, 0x7E, 0x2F, + 0x43, 0x13, 0x2A, 0x7E, 0x41, 0x05, 0x04, 0x01, 0x00, 0x03, 0x09, 0x02, + 0x01, 0x06, 0x03, 0x01, 0x7F, 0x00, 0x8F, 0x01, 0x20, 0x34, 0x01, 0x20, + 0x90, 0x42, 0x7B, 0x2A, 0x03, 0x05, 0x2A, 0x02, 0x04, 0x0B, 0x06, 0x80, + 0x49, 0x2A, 0x2E, 0x2A, 0x9C, 0x2A, 0x01, 0x0C, 0x12, 0x2A, 0x01, 0x01, + 0x0F, 0x47, 0x01, 0x02, 0x0F, 0x39, 0x06, 0x0A, 0x2A, 0x02, 0x09, 0x13, + 0x05, 0x04, 0x65, 0x01, 0x00, 0x2A, 0x02, 0x08, 0x05, 0x0E, 0x2A, 0x01, + 0x81, 0x70, 0x13, 0x01, 0x20, 0x0E, 0x06, 0x04, 0x65, 0x01, 0x00, 0x2A, + 0x2A, 0x06, 0x10, 0x02, 0x05, 0x63, 0x40, 0x02, 0x05, 0x40, 0x02, 0x05, + 0x01, 0x04, 0x08, 0x03, 0x05, 0x04, 0x01, 0x65, 0x01, 0x04, 0x08, 0x04, + 0xFF, 0x30, 0x29, 0x02, 0x05, 0x7B, 0x09, 0x01, 0x02, 0x12, 0x2A, 0x05, + 0x03, 0x01, 0x28, 0xA3, 0x7C, 0x42, 0x8C, 0x2E, 0x01, 0x83, 0xFF, 0x7F, + 0x0F, 0x06, 0x0D, 0x01, 0x03, 0xA4, 0x06, 0x04, 0x01, 0x80, 0x78, 0xA3, + 0x01, 0x00, 0x8C, 0x40, 0x18, 0x05, 0x03, 0x01, 0x28, 0xA3, 0x01, 0x00, + 0x00, 0x00, 0xB4, 0xB3, 0x00, 0x04, 0x78, 0x2E, 0xCE, 0x06, 0x16, 0xC0, + 0x2A, 0x01, 0x84, 0x00, 0x10, 0x06, 0x02, 0x6F, 0x2B, 0x2A, 0x03, 0x00, + 0x85, 0x47, 0xB6, 0x02, 0x00, 0x78, 0x2E, 0xA8, 0x27, 0x78, 0x2E, 0x2A, + 0xCC, 0x47, 0xCB, 0x03, 0x01, 0x03, 0x02, 0x02, 0x01, 0x02, 0x02, 0x39, + 0x06, 0x14, 0xC2, 0x2A, 0x03, 0x03, 0x85, 0x47, 0xB6, 0x02, 0x03, 0x78, + 0x2E, 0xA8, 0x02, 0x02, 0x06, 0x03, 0x26, 0x04, 0x01, 0x24, 0x9D, 0x00, + 0x00, 0xBA, 0x01, 0x10, 0x0F, 0x05, 0x02, 0x73, 0x2B, 0x00, 0x00, 0x9E, + 0xBA, 0x01, 0x14, 0x0E, 0x06, 0x02, 0x73, 0x2B, 0x85, 0x01, 0x0C, 0x08, + 0x01, 0x0C, 0xB6, 0x9D, 0x85, 0x2A, 0x01, 0x0C, 0x08, 0x01, 0x0C, 0x32, + 0x05, 0x02, 0x67, 0x2B, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x02, 0x00, + 0x9A, 0x02, 0x01, 0x02, 0x00, 0x3C, 0x2A, 0x01, 0x00, 0x0F, 0x06, 0x02, + 0x65, 0x00, 0xD1, 0x04, 0x74, 0x00, 0xC0, 0x01, 0x01, 0x0E, 0x06, 0x02, + 0x68, 0x2B, 0xC2, 0x2A, 0x2A, 0x5F, 0x47, 0x01, 0x05, 0x11, 0x39, 0x06, + 0x02, 0x68, 0x2B, 0x01, 0x08, 0x08, 0x2A, 0x84, 0x30, 0x0B, 0x06, 0x0D, + 0x2A, 0x01, 0x01, 0x47, 0x0C, 0x3F, 0x2A, 0x84, 0x42, 0x86, 0x42, 0x04, + 0x01, 0x29, 0x00, 0x00, 0xC0, 0x8A, 0x30, 0x01, 0x00, 0x3A, 0x0F, 0x06, + 0x13, 0x29, 0x01, 0x01, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0xC2, 0x06, 0x02, + 0x6B, 0x2B, 0x01, 0x02, 0x8A, 0x42, 0x04, 0x28, 0x01, 0x02, 0x3A, 0x0F, + 0x06, 0x1F, 0x29, 0x01, 0x0D, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0xC2, 0x01, + 0x0C, 0x0F, 0x05, 0x02, 0x6B, 0x2B, 0x85, 0x01, 0x0C, 0xB6, 0x8B, 0x85, + 0x01, 0x0C, 0x32, 0x05, 0x02, 0x6B, 0x2B, 0x04, 0x03, 0x6B, 0x2B, 0x29, + 0x00, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x2A, 0x06, 0x1D, 0xC2, 0x06, 0x03, + 0xBC, 0x04, 0x15, 0xC0, 0x2A, 0x01, 0x81, 0x7F, 0x0D, 0x06, 0x0C, 0x2A, + 0x8D, 0x08, 0x01, 0x00, 0x47, 0x42, 0x8D, 0x47, 0xB6, 0x04, 0x01, 0xC9, + 0x04, 0x60, 0x9D, 0x9D, 0x00, 0x00, 0xBB, 0x2A, 0x5F, 0x06, 0x07, 0x29, + 0x06, 0x02, 0x69, 0x2B, 0x04, 0x74, 0x00, 0x00, 0xC3, 0x01, 0x03, 0xC1, + 0x47, 0x29, 0x47, 0x00, 0x00, 0xC0, 0xC9, 0x00, 0x03, 0x01, 0x00, 0x03, + 0x00, 0xC0, 0xA7, 0x2A, 0x06, 0x80, 0x50, 0xC2, 0x03, 0x01, 0xC2, 0x03, + 0x02, 0x02, 0x01, 0x01, 0x08, 0x0F, 0x06, 0x16, 0x02, 0x02, 0x01, 0x0F, + 0x0D, 0x06, 0x0D, 0x01, 0x01, 0x02, 0x02, 0x01, 0x10, 0x08, 0x0C, 0x02, + 0x00, 0x39, 0x03, 0x00, 0x04, 0x2A, 0x02, 0x01, 0x01, 0x02, 0x11, 0x02, + 0x01, 0x01, 0x06, 0x0D, 0x13, 0x02, 0x02, 0x01, 0x01, 0x0F, 0x02, 0x02, + 0x01, 0x03, 0x0F, 0x39, 0x13, 0x06, 0x11, 0x02, 0x00, 0x01, 0x01, 0x02, + 0x02, 0x62, 0x01, 0x02, 0x0C, 0x02, 0x01, 0x08, 0x0C, 0x39, 0x03, 0x00, + 0x04, 0xFF, 0x2C, 0x9D, 0x02, 0x00, 0x00, 0x00, 0xC0, 0xA7, 0xBD, 0x82, + 0x41, 0x9D, 0x00, 0x00, 0xC0, 0xA7, 0xC0, 0xA7, 0x01, 0x00, 0x7E, 0x41, + 0x2A, 0x06, 0x15, 0xC0, 0x2A, 0x01, 0x20, 0x0B, 0x06, 0x0B, 0x01, 0x01, + 0x47, 0x0C, 0x7E, 0x2F, 0x39, 0x7E, 0x41, 0x04, 0x01, 0x29, 0x04, 0x68, + 0x9D, 0x9D, 0x00, 0x00, 0x01, 0x02, 0x9A, 0xC3, 0x01, 0x08, 0x0C, 0xC3, + 0x08, 0x00, 0x00, 0x01, 0x03, 0x9A, 0xC3, 0x01, 0x08, 0x0C, 0xC3, 0x08, + 0x01, 0x08, 0x0C, 0xC3, 0x08, 0x00, 0x00, 0x01, 0x01, 0x9A, 0xC3, 0x00, + 0x00, 0x3D, 0x2A, 0x5D, 0x05, 0x01, 0x00, 0x29, 0xD1, 0x04, 0x76, 0x02, + 0x03, 0x00, 0x94, 0x30, 0x03, 0x01, 0x01, 0x00, 0x2A, 0x02, 0x01, 0x0B, + 0x06, 0x10, 0x2A, 0x01, 0x01, 0x0C, 0x93, 0x08, 0x2E, 0x02, 0x00, 0x0F, + 0x06, 0x01, 0x00, 0x61, 0x04, 0x6A, 0x29, 0x01, 0x7F, 0x00, 0x00, 0x2C, + 0x19, 0x38, 0x06, 0x04, 0xCF, 0x29, 0x04, 0x78, 0x01, 0x16, 0x89, 0x42, + 0x01, 0x00, 0xE2, 0x01, 0x00, 0xE1, 0x2C, 0x01, 0x17, 0x89, 0x42, 0x00, + 0x00, 0x01, 0x15, 0x89, 0x42, 0x47, 0x57, 0x29, 0x57, 0x29, 0x2C, 0x00, + 0x00, 0x01, 0x01, 0x47, 0xC6, 0x00, 0x00, 0xBB, 0x01, 0x01, 0x0F, 0x05, + 0x02, 0x73, 0x2B, 0x2A, 0xC9, 0x29, 0x00, 0x00, 0x47, 0x3A, 0x9A, 0x47, + 0x2A, 0x06, 0x05, 0xC3, 0x29, 0x62, 0x04, 0x78, 0x29, 0x00, 0x02, 0x03, + 0x00, 0x78, 0x2E, 0x9C, 0x03, 0x01, 0x02, 0x01, 0x01, 0x0F, 0x13, 0x02, + 0x01, 0x01, 0x04, 0x12, 0x01, 0x0F, 0x13, 0x02, 0x01, 0x01, 0x08, 0x12, + 0x01, 0x0F, 0x13, 0x01, 0x00, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x00, + 0x01, 0x18, 0x02, 0x00, 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x81, + 0x0D, 0x01, 0x01, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x01, 0x01, 0x10, + 0x02, 0x00, 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x80, 0x77, 0x01, + 0x02, 0x3A, 0x0F, 0x06, 0x10, 0x29, 0x01, 0x01, 0x01, 0x20, 0x02, 0x00, + 0x06, 0x03, 0x4C, 0x04, 0x01, 0x4D, 0x04, 0x80, 0x61, 0x01, 0x03, 0x3A, + 0x0F, 0x06, 0x0F, 0x29, 0x29, 0x01, 0x10, 0x02, 0x00, 0x06, 0x03, 0x4A, + 0x04, 0x01, 0x4B, 0x04, 0x80, 0x4C, 0x01, 0x04, 0x3A, 0x0F, 0x06, 0x0E, + 0x29, 0x29, 0x01, 0x20, 0x02, 0x00, 0x06, 0x03, 0x4A, 0x04, 0x01, 0x4B, + 0x04, 0x38, 0x01, 0x05, 0x3A, 0x0F, 0x06, 0x0C, 0x29, 0x29, 0x02, 0x00, + 0x06, 0x03, 0x4E, 0x04, 0x01, 0x4F, 0x04, 0x26, 0x2A, 0x01, 0x09, 0x10, + 0x06, 0x02, 0x6A, 0x2B, 0x47, 0x29, 0x2A, 0x01, 0x01, 0x13, 0x01, 0x04, + 0x0C, 0x01, 0x10, 0x08, 0x47, 0x01, 0x08, 0x13, 0x01, 0x10, 0x47, 0x09, + 0x02, 0x00, 0x06, 0x03, 0x48, 0x04, 0x01, 0x49, 0x00, 0x29, 0x00, 0x00, + 0x9C, 0x01, 0x0C, 0x12, 0x01, 0x02, 0x10, 0x00, 0x00, 0x9C, 0x01, 0x0C, + 0x12, 0x2A, 0x60, 0x47, 0x01, 0x03, 0x0B, 0x13, 0x00, 0x00, 0x9C, 0x01, + 0x0C, 0x12, 0x01, 0x01, 0x0F, 0x00, 0x00, 0x9C, 0x01, 0x0C, 0x12, 0x5F, + 0x00, 0x00, 0x1B, 0x01, 0x00, 0x75, 0x30, 0x2A, 0x06, 0x22, 0x01, 0x01, + 0x3A, 0x0F, 0x06, 0x06, 0x29, 0x01, 0x00, 0xA0, 0x04, 0x14, 0x01, 0x02, + 0x3A, 0x0F, 0x06, 0x0D, 0x29, 0x77, 0x30, 0x01, 0x01, 0x0F, 0x06, 0x03, + 0x01, 0x10, 0x39, 0x04, 0x01, 0x29, 0x04, 0x01, 0x29, 0x7D, 0x30, 0x05, + 0x33, 0x31, 0x06, 0x30, 0x88, 0x30, 0x01, 0x14, 0x3A, 0x0F, 0x06, 0x06, + 0x29, 0x01, 0x02, 0x39, 0x04, 0x22, 0x01, 0x15, 0x3A, 0x0F, 0x06, 0x09, + 0x29, 0xAA, 0x06, 0x03, 0x01, 0x7F, 0xA0, 0x04, 0x13, 0x01, 0x16, 0x3A, + 0x0F, 0x06, 0x06, 0x29, 0x01, 0x01, 0x39, 0x04, 0x07, 0x29, 0x01, 0x04, + 0x39, 0x01, 0x00, 0x29, 0x19, 0x06, 0x03, 0x01, 0x08, 0x39, 0x00, 0x00, + 0x1B, 0x2A, 0x05, 0x13, 0x31, 0x06, 0x10, 0x88, 0x30, 0x01, 0x15, 0x0F, + 0x06, 0x08, 0x29, 0xAA, 0x01, 0x00, 0x77, 0x42, 0x04, 0x01, 0x23, 0x00, + 0x00, 0xCF, 0x01, 0x07, 0x13, 0x01, 0x01, 0x10, 0x06, 0x02, 0x73, 0x2B, + 0x00, 0x01, 0x03, 0x00, 0x2C, 0x19, 0x06, 0x05, 0x02, 0x00, 0x89, 0x42, + 0x00, 0xCF, 0x29, 0x04, 0x74, 0x00, 0x01, 0x14, 0xD2, 0x01, 0x01, 0xE2, + 0x2C, 0x2A, 0x01, 0x00, 0xCA, 0x01, 0x16, 0xD2, 0xD6, 0x2C, 0x00, 0x00, + 0x01, 0x0B, 0xE2, 0x52, 0x2A, 0x2A, 0x01, 0x03, 0x08, 0xE1, 0xE1, 0x14, + 0x2A, 0x5D, 0x06, 0x02, 0x29, 0x00, 0xE1, 0x1E, 0x2A, 0x06, 0x05, 0x85, + 0x47, 0xDA, 0x04, 0x77, 0x29, 0x04, 0x6C, 0x00, 0x01, 0x00, 0xDC, 0x95, + 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, 0x05, 0x63, 0x01, 0x00, 0xDD, 0x08, + 0x50, 0x08, 0x01, 0x03, 0x08, 0x01, 0x0D, 0xE2, 0xE1, 0x01, 0x00, 0xDC, + 0xE2, 0x01, 0x01, 0xDC, 0x29, 0x95, 0x2E, 0x01, 0x86, 0x03, 0x11, 0x06, + 0x08, 0x01, 0x00, 0xDD, 0xE0, 0x01, 0x01, 0xDD, 0x29, 0x50, 0xE0, 0x16, + 0x15, 0x2A, 0x5D, 0x06, 0x02, 0x29, 0x00, 0xE0, 0x1F, 0x2A, 0x06, 0x05, + 0x85, 0x47, 0xDA, 0x04, 0x77, 0x29, 0x04, 0x6C, 0x00, 0x9E, 0x01, 0x14, + 0xE2, 0x01, 0x0C, 0xE1, 0x85, 0x01, 0x0C, 0xDA, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0xE2, 0x01, 0x80, 0x46, 0x8A, 0x30, 0x01, 0x02, 0x0F, 0x06, + 0x0C, 0x02, 0x00, 0x06, 0x04, 0x01, 0x05, 0x04, 0x02, 0x01, 0x1D, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x01, 0x86, 0x30, 0x06, 0x04, 0x01, 0x05, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x02, 0x8C, 0x2E, 0x2A, 0x06, 0x05, 0x62, 0x21, + 0x01, 0x07, 0x08, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x08, 0x02, 0x03, + 0x08, 0x2A, 0x06, 0x03, 0x01, 0x02, 0x08, 0x08, 0xE1, 0x95, 0x2E, 0xE0, + 0x8E, 0x01, 0x04, 0x17, 0x8E, 0x01, 0x04, 0x08, 0x01, 0x1C, 0x34, 0x8E, + 0x01, 0x20, 0xDA, 0x01, 0x20, 0xE2, 0x8F, 0x01, 0x20, 0xDA, 0x78, 0x2E, + 0xE0, 0x01, 0x00, 0xE2, 0x02, 0x01, 0x02, 0x02, 0x08, 0x02, 0x03, 0x08, + 0x2A, 0x06, 0x80, 0x40, 0xE0, 0x02, 0x01, 0x2A, 0x06, 0x10, 0x01, 0x83, + 0xFE, 0x01, 0xE0, 0x01, 0x04, 0x09, 0x2A, 0xE0, 0x62, 0x8B, 0x47, 0xDB, + 0x04, 0x01, 0x29, 0x02, 0x02, 0x06, 0x0C, 0x01, 0x01, 0xE0, 0x01, 0x01, + 0xE0, 0x86, 0x30, 0x01, 0x08, 0x09, 0xE2, 0x02, 0x03, 0x2A, 0x06, 0x11, + 0x01, 0x10, 0xE0, 0x01, 0x04, 0x09, 0x2A, 0xE0, 0x64, 0x2A, 0xE0, 0x62, + 0x85, 0x47, 0xDB, 0x04, 0x01, 0x29, 0x04, 0x01, 0x29, 0x00, 0x00, 0x01, + 0x0E, 0xE2, 0x01, 0x00, 0xE1, 0x00, 0x03, 0x78, 0x2E, 0xCC, 0x05, 0x01, + 0x00, 0x7E, 0x2F, 0x2A, 0x01, 0x82, 0x80, 0x80, 0x80, 0x00, 0x13, 0x06, + 0x05, 0x29, 0x01, 0x1D, 0x04, 0x0E, 0x2A, 0x01, 0x83, 0xC0, 0x80, 0x80, + 0x00, 0x13, 0x2A, 0x06, 0x01, 0x47, 0x29, 0xA5, 0x03, 0x00, 0x02, 0x00, + 0x25, 0x2A, 0x5D, 0x06, 0x02, 0x37, 0x2B, 0x03, 0x01, 0x95, 0x2E, 0x01, + 0x86, 0x03, 0x11, 0x03, 0x02, 0x01, 0x0C, 0xE2, 0x02, 0x01, 0x80, 0x30, + 0x08, 0x02, 0x02, 0x01, 0x02, 0x13, 0x08, 0x01, 0x06, 0x08, 0xE1, 0x01, + 0x03, 0xE2, 0x02, 0x00, 0xE0, 0x7F, 0x80, 0x30, 0xDB, 0x02, 0x02, 0x06, + 0x1C, 0x92, 0x2E, 0x2A, 0x01, 0x83, 0xFE, 0x00, 0x0B, 0x06, 0x03, 0xE0, + 0x04, 0x0F, 0x01, 0x81, 0x7F, 0x13, 0xE2, 0x78, 0x2E, 0xCD, 0x01, 0x01, + 0x0C, 0x01, 0x03, 0x08, 0xE2, 0x02, 0x01, 0xE0, 0x85, 0x02, 0x01, 0xDA, + 0x00, 0x00, 0x56, 0x2A, 0x01, 0x00, 0x0F, 0x06, 0x02, 0x65, 0x00, 0xCF, + 0x29, 0x04, 0x73, 0x00, 0x2A, 0xE2, 0xDA, 0x00, 0x00, 0x01, 0x00, 0x78, + 0x2E, 0xCB, 0x06, 0x0C, 0x63, 0x3A, 0x06, 0x08, 0x01, 0x80, 0x41, 0xE2, + 0x01, 0x80, 0x42, 0xE2, 0x46, 0x06, 0x07, 0x61, 0x3A, 0x06, 0x03, 0x01, + 0x01, 0xE2, 0x45, 0x06, 0x08, 0x61, 0x3A, 0x06, 0x04, 0x01, 0x80, 0x40, + 0xE2, 0x47, 0x29, 0x00, 0x01, 0x01, 0x00, 0x03, 0x00, 0x46, 0x45, 0x39, + 0x05, 0x14, 0x01, 0x01, 0x01, 0x80, 0x7C, 0xDE, 0x03, 0x00, 0x01, 0x03, + 0x01, 0x80, 0x7C, 0xDE, 0x02, 0x00, 0x08, 0x47, 0x29, 0x00, 0x46, 0x06, + 0x07, 0x01, 0x01, 0x44, 0x29, 0xDE, 0x03, 0x00, 0x45, 0x06, 0x0A, 0x01, + 0x03, 0x44, 0x29, 0xDE, 0x02, 0x00, 0x08, 0x03, 0x00, 0x29, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x01, 0x04, 0xDF, 0x01, 0x05, 0xDF, 0x01, 0x06, + 0xDF, 0x01, 0x03, 0xDF, 0x01, 0x02, 0xDF, 0x0A, 0x65, 0x00, 0x01, 0x03, + 0x00, 0x3A, 0x01, 0x01, 0x02, 0x00, 0x0C, 0x13, 0x05, 0x01, 0x00, 0x63, + 0x01, 0x03, 0x3B, 0x06, 0x07, 0x02, 0x00, 0xE2, 0x01, 0x02, 0x3B, 0xE2, + 0x00, 0x00, 0x2A, 0x01, 0x08, 0x54, 0xE2, 0xE2, 0x00, 0x00, 0x2A, 0x01, + 0x10, 0x54, 0xE2, 0xE0, 0x00, 0x00, 0x2A, 0x57, 0x06, 0x02, 0x29, 0x00, + 0xCF, 0x29, 0x04, 0x76 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 30, + 35, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 92, + 96, + 100, + 104, + 109, + 114, + 119, + 124, + 129, + 134, + 139, + 144, + 149, + 154, + 159, + 164, + 169, + 174, + 180, + 185, + 190, + 195, + 200, + 205, + 210, + 215, + 220, + 225, + 230, + 235, + 240, + 245, + 250, + 255, + 260, + 265, + 270, + 275, + 280, + 285, + 290, + 299, + 303, + 328, + 334, + 353, + 364, + 405, + 516, + 520, + 553, + 563, + 587, + 669, + 683, + 689, + 748, + 767, + 789, + 838, + 887, + 963, + 1065, + 1076, + 1670, + 1674, + 1741, + 1751, + 1782, + 1806, + 1852, + 1922, + 1962, + 1976, + 1985, + 1989, + 2084, + 2092, + 2128, + 2139, + 2155, + 2161, + 2172, + 2207, + 2233, + 2245, + 2251, + 2264, + 2279, + 2472, + 2481, + 2494, + 2503, + 2510, + 2616, + 2641, + 2654, + 2670, + 2688, + 2720, + 2793, + 2806, + 2987, + 2995, + 3122, + 3136, + 3141, + 3185, + 3242, + 3263, + 3290, + 3298, + 3306 +}; + +#define T0_INTERPRETED 93 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_ssl_hs_server_init_main, 166) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_ssl_hs_server_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 8: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 9: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 10: { + /* -rot */ + T0_NROT(); + } + break; + case 11: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 12: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 13: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 14: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 15: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 16: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 17: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 18: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 19: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 20: { + /* begin-cert */ + + if (ENG->chain_len == 0) { + T0_PUSHi(-1); + } else { + ENG->cert_cur = ENG->chain->data; + ENG->cert_len = ENG->chain->data_len; + ENG->chain ++; + ENG->chain_len --; + T0_PUSH(ENG->cert_len); + } + + } + break; + case 21: { + /* begin-ta-name */ + + const br_x500_name *dn; + if (CTX->cur_dn_index >= CTX->num_tas) { + T0_PUSHi(-1); + } else { + if (CTX->ta_names == NULL) { + dn = &CTX->tas[CTX->cur_dn_index].dn; + } else { + dn = &CTX->ta_names[CTX->cur_dn_index]; + } + CTX->cur_dn_index ++; + CTX->cur_dn = dn->data; + CTX->cur_dn_len = dn->len; + T0_PUSH(CTX->cur_dn_len); + } + + } + break; + case 22: { + /* begin-ta-name-list */ + + CTX->cur_dn_index = 0; + + } + break; + case 23: { + /* bzero */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + memset(addr, 0, len); + + } + break; + case 24: { + /* call-policy-handler */ + + int x; + br_ssl_server_choices choices; + + x = (*CTX->policy_vtable)->choose( + CTX->policy_vtable, CTX, &choices); + ENG->session.cipher_suite = choices.cipher_suite; + CTX->sign_hash_id = choices.algo_id; + ENG->chain = choices.chain; + ENG->chain_len = choices.chain_len; + T0_PUSHi(-(x != 0)); + + } + break; + case 25: { + /* can-output? */ + + T0_PUSHi(-(ENG->hlen_out > 0)); + + } + break; + case 26: { + /* check-resume */ + + if (ENG->session.session_id_len == 32 + && CTX->cache_vtable != NULL && (*CTX->cache_vtable)->load( + CTX->cache_vtable, CTX, &ENG->session)) + { + T0_PUSHi(-1); + } else { + T0_PUSH(0); + } + + } + break; + case 27: { + /* co */ + T0_CO(); + } + break; + case 28: { + /* compute-Finished-inner */ + + int prf_id = T0_POP(); + int from_client = T0_POPi(); + unsigned char tmp[48]; + br_tls_prf_seed_chunk seed; + + br_tls_prf_impl prf = br_ssl_engine_get_PRF(ENG, prf_id); + seed.data = tmp; + if (ENG->session.version >= BR_TLS12) { + seed.len = br_multihash_out(&ENG->mhash, prf_id, tmp); + } else { + br_multihash_out(&ENG->mhash, br_md5_ID, tmp); + br_multihash_out(&ENG->mhash, br_sha1_ID, tmp + 16); + seed.len = 36; + } + prf(ENG->pad, 12, ENG->session.master_secret, + sizeof ENG->session.master_secret, + from_client ? "client finished" : "server finished", + 1, &seed); + + } + break; + case 29: { + /* compute-hash-CV */ + + int i; + + for (i = 1; i <= 6; i ++) { + br_multihash_out(&ENG->mhash, i, + ENG->pad + HASH_PAD_OFF[i - 1]); + } + + } + break; + case 30: { + /* copy-cert-chunk */ + + size_t clen; + + clen = ENG->cert_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy_P(ENG->pad, ENG->cert_cur, clen); + ENG->cert_cur += clen; + ENG->cert_len -= clen; + T0_PUSH(clen); + + } + break; + case 31: { + /* copy-dn-chunk */ + + size_t clen; + + clen = CTX->cur_dn_len; + if (clen > sizeof ENG->pad) { + clen = sizeof ENG->pad; + } + memcpy(ENG->pad, CTX->cur_dn, clen); + CTX->cur_dn += clen; + CTX->cur_dn_len -= clen; + T0_PUSH(clen); + + } + break; + case 32: { + /* copy-hash-CV */ + + int id = T0_POP(); + size_t off, len; + + if (id == 0) { + off = 0; + len = 36; + } else { + if (br_multihash_getimpl(&ENG->mhash, id) == 0) { + T0_PUSH(0); + T0_RET(); + } + off = HASH_PAD_OFF[id - 1]; + len = HASH_PAD_OFF[id] - off; + } + memcpy(CTX->hash_CV, ENG->pad + off, len); + CTX->hash_CV_len = len; + CTX->hash_CV_id = id; + T0_PUSHi(-1); + + } + break; + case 33: { + /* copy-protocol-name */ + + size_t idx = T0_POP(); + size_t len = strlen(ENG->protocol_names[idx]); + memcpy(ENG->pad, ENG->protocol_names[idx], len); + T0_PUSH(len); + + } + break; + case 34: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 35: { + /* discard-input */ + + ENG->hlen_in = 0; + + } + break; + case 36: { + /* do-ecdh */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_ecdh(CTX, prf_id, ENG->pad, len); + + } + break; + case 37: { + /* do-ecdhe-part1 */ + + int curve = T0_POPi(); + T0_PUSHi(do_ecdhe_part1(CTX, curve)); + + } + break; + case 38: { + /* do-ecdhe-part2 */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_ecdhe_part2(CTX, prf_id, ENG->pad, len); + + } + break; + case 39: { + /* do-rsa-decrypt */ + + int prf_id = T0_POPi(); + size_t len = T0_POP(); + do_rsa_decrypt(CTX, prf_id, ENG->pad, len); + + } + break; + case 40: { + /* do-static-ecdh */ + + do_static_ecdh(CTX, T0_POP()); + + } + break; + case 41: { + /* drop */ + (void)T0_POP(); + } + break; + case 42: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 43: { + /* fail */ + + br_ssl_engine_fail(ENG, (int)T0_POPi()); + T0_CO(); + + } + break; + case 44: { + /* flush-record */ + + br_ssl_engine_flush_record(ENG); + + } + break; + case 45: { + /* get-key-type-usages */ + + const br_x509_class *xc; + const br_x509_pkey *pk; + unsigned usages; + + xc = *(ENG->x509ctx); + pk = xc->get_pkey(ENG->x509ctx, &usages); + if (pk == NULL) { + T0_PUSH(0); + } else { + T0_PUSH(pk->key_type | usages); + } + + } + break; + case 46: { + /* get16 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 47: { + /* get32 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)ENG + addr)); + + } + break; + case 48: { + /* get8 */ + + size_t addr = (size_t)T0_POP(); + T0_PUSH(*((unsigned char *)ENG + addr)); + + } + break; + case 49: { + /* has-input? */ + + T0_PUSHi(-(ENG->hlen_in != 0)); + + } + break; + case 50: { + /* memcmp */ + + size_t len = (size_t)T0_POP(); + void *addr2 = (unsigned char *)ENG + (size_t)T0_POP(); + void *addr1 = (unsigned char *)ENG + (size_t)T0_POP(); + int x = memcmp(addr1, addr2, len); + T0_PUSH((uint32_t)-(x == 0)); + + } + break; + case 51: { + /* memcpy */ + + size_t len = (size_t)T0_POP(); + void *src = (unsigned char *)ENG + (size_t)T0_POP(); + void *dst = (unsigned char *)ENG + (size_t)T0_POP(); + memcpy(dst, src, len); + + } + break; + case 52: { + /* mkrand */ + + size_t len = (size_t)T0_POP(); + void *addr = (unsigned char *)ENG + (size_t)T0_POP(); + br_hmac_drbg_generate(&ENG->rng, addr, len); + + } + break; + case 53: { + /* more-incoming-bytes? */ + + T0_PUSHi(ENG->hlen_in != 0 || !br_ssl_engine_recvrec_finished(ENG)); + + } + break; + case 54: { + /* multihash-init */ + + br_multihash_init(&ENG->mhash); + + } + break; + case 55: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 56: { + /* not */ + + uint32_t a = T0_POP(); + T0_PUSH(~a); + + } + break; + case 57: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 58: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 59: { + /* pick */ + T0_PICK(T0_POP()); + } + break; + case 60: { + /* read-chunk-native */ + + size_t clen = ENG->hlen_in; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy_P((unsigned char *)ENG + addr, ENG->hbuf_in, clen); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_in, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_in += clen; + ENG->hlen_in -= clen; + } + + } + break; + case 61: { + /* read8-native */ + + if (ENG->hlen_in > 0) { + unsigned char x; + + x = pgm_read_byte(ENG->hbuf_in ++); + if (ENG->record_type_in == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + T0_PUSH(x); + ENG->hlen_in --; + } else { + T0_PUSHi(-1); + } + + } + break; + case 62: { + /* save-session */ + + if (CTX->cache_vtable != NULL) { + (*CTX->cache_vtable)->save( + CTX->cache_vtable, CTX, &ENG->session); + } + + } + break; + case 63: { + /* set-max-frag-len */ + + size_t max_frag_len = T0_POP(); + + br_ssl_engine_new_max_frag_len(ENG, max_frag_len); + + /* + * We must adjust our own output limit. Since we call this only + * after receiving a ClientHello and before beginning to send + * the ServerHello, the next output record should be empty at + * that point, so we can use max_frag_len as a limit. + */ + if (ENG->hlen_out > max_frag_len) { + ENG->hlen_out = max_frag_len; + } + + } + break; + case 64: { + /* set16 */ + + size_t addr = (size_t)T0_POP(); + *(uint16_t *)(void *)((unsigned char *)ENG + addr) = (uint16_t)T0_POP(); + + } + break; + case 65: { + /* set32 */ + + size_t addr = (size_t)T0_POP(); + *(uint32_t *)(void *)((unsigned char *)ENG + addr) = (uint32_t)T0_POP(); + + } + break; + case 66: { + /* set8 */ + + size_t addr = (size_t)T0_POP(); + *((unsigned char *)ENG + addr) = (unsigned char)T0_POP(); + + } + break; + case 67: { + /* supported-curves */ + + uint32_t x = ENG->iec == NULL ? 0 : ENG->iec->supported_curves; + T0_PUSH(x); + + } + break; + case 68: { + /* supported-hash-functions */ + + int i; + unsigned x, num; + + x = 0; + num = 0; + for (i = br_sha1_ID; i <= br_sha512_ID; i ++) { + if (br_multihash_getimpl(&ENG->mhash, i)) { + x |= 1U << i; + num ++; + } + } + T0_PUSH(x); + T0_PUSH(num); + + } + break; + case 69: { + /* supports-ecdsa? */ + + T0_PUSHi(-(ENG->iecdsa != 0)); + + } + break; + case 70: { + /* supports-rsa-sign? */ + + T0_PUSHi(-(ENG->irsavrfy != 0)); + + } + break; + case 71: { + /* swap */ + T0_SWAP(); + } + break; + case 72: { + /* switch-aesccm-in */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_in(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 73: { + /* switch-aesccm-out */ + + int is_client, prf_id; + unsigned cipher_key_len, tag_len; + + tag_len = T0_POP(); + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_ccm_out(ENG, is_client, prf_id, + ENG->iaes_ctrcbc, cipher_key_len, tag_len); + + } + break; + case 74: { + /* switch-aesgcm-in */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_in(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 75: { + /* switch-aesgcm-out */ + + int is_client, prf_id; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_gcm_out(ENG, is_client, prf_id, + ENG->iaes_ctr, cipher_key_len); + + } + break; + case 76: { + /* switch-cbc-in */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_in(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcdec : ENG->ides_cbcdec, cipher_key_len); + + } + break; + case 77: { + /* switch-cbc-out */ + + int is_client, prf_id, mac_id, aes; + unsigned cipher_key_len; + + cipher_key_len = T0_POP(); + aes = T0_POP(); + mac_id = T0_POP(); + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_cbc_out(ENG, is_client, prf_id, mac_id, + aes ? ENG->iaes_cbcenc : ENG->ides_cbcenc, cipher_key_len); + + } + break; + case 78: { + /* switch-chapol-in */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_in(ENG, is_client, prf_id); + + } + break; + case 79: { + /* switch-chapol-out */ + + int is_client, prf_id; + + prf_id = T0_POP(); + is_client = T0_POP(); + br_ssl_engine_switch_chapol_out(ENG, is_client, prf_id); + + } + break; + case 80: { + /* ta-names-total-length */ + + size_t u, len; + + len = 0; + if (CTX->ta_names != NULL) { + for (u = 0; u < CTX->num_tas; u ++) { + len += CTX->ta_names[u].len + 2; + } + } else if (CTX->tas != NULL) { + for (u = 0; u < CTX->num_tas; u ++) { + len += CTX->tas[u].dn.len + 2; + } + } + T0_PUSH(len); + + } + break; + case 81: { + /* test-protocol-name */ + + size_t len = T0_POP(); + size_t u; + + for (u = 0; u < ENG->protocol_names_num; u ++) { + const char *name; + + name = ENG->protocol_names[u]; + if (len == strlen(name) && memcmp(ENG->pad, name, len) == 0) { + T0_PUSH(u); + T0_RET(); + } + } + T0_PUSHi(-1); + + } + break; + case 82: { + /* total-chain-length */ + + size_t u; + uint32_t total; + + total = 0; + for (u = 0; u < ENG->chain_len; u ++) { + total += 3 + (uint32_t)ENG->chain[u].data_len; + } + T0_PUSH(total); + + } + break; + case 83: { + /* u< */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 84: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + case 85: { + /* verify-CV-sig */ + + int err; + + err = verify_CV_sig(CTX, T0_POP()); + T0_PUSHi(err); + + } + break; + case 86: { + /* write-blob-chunk */ + + size_t clen = ENG->hlen_out; + if (clen > 0) { + uint32_t addr, len; + + len = T0_POP(); + addr = T0_POP(); + if ((size_t)len < clen) { + clen = (size_t)len; + } + memcpy(ENG->hbuf_out, (unsigned char *)ENG + addr, clen); + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, ENG->hbuf_out, clen); + } + T0_PUSH(addr + (uint32_t)clen); + T0_PUSH(len - (uint32_t)clen); + ENG->hbuf_out += clen; + ENG->hlen_out -= clen; + } + + } + break; + case 87: { + /* write8-native */ + + unsigned char x; + + x = (unsigned char)T0_POP(); + if (ENG->hlen_out > 0) { + if (ENG->record_type_out == BR_SSL_HANDSHAKE) { + br_multihash_update(&ENG->mhash, &x, 1); + } + *ENG->hbuf_out ++ = x; + ENG->hlen_out --; + T0_PUSHi(-1); + } else { + T0_PUSHi(0); + } + + } + break; + case 88: { + /* x509-append */ + + const br_x509_class *xc; + size_t len; + + xc = *(ENG->x509ctx); + len = T0_POP(); + xc->append(ENG->x509ctx, ENG->pad, len); + + } + break; + case 89: { + /* x509-end-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->end_cert(ENG->x509ctx); + + } + break; + case 90: { + /* x509-end-chain */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + T0_PUSH(xc->end_chain(ENG->x509ctx)); + + } + break; + case 91: { + /* x509-start-cert */ + + const br_x509_class *xc; + + xc = *(ENG->x509ctx); + xc->start_cert(ENG->x509ctx, T0_POP()); + + } + break; + case 92: { + /* x509-start-chain */ + + const br_x509_class *xc; + uint32_t bc; + + bc = T0_POP(); + xc = *(ENG->x509ctx); + xc->start_chain(ENG->x509ctx, bc ? ENG->server_name : NULL); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c new file mode 100644 index 0000000000..a4c54e617d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_io.c @@ -0,0 +1,261 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_ssl.h */ +void +br_sslio_init(br_sslio_context *ctx, + br_ssl_engine_context *engine, + int (*low_read)(void *read_context, + unsigned char *data, size_t len), + void *read_context, + int (*low_write)(void *write_context, + const unsigned char *data, size_t len), + void *write_context) +{ + ctx->engine = engine; + ctx->low_read = low_read; + ctx->read_context = read_context; + ctx->low_write = low_write; + ctx->write_context = write_context; +} + +/* + * Run the engine, until the specified target state is achieved, or + * an error occurs. The target state is SENDAPP, RECVAPP, or the + * combination of both (the combination matches either). When a match is + * achieved, this function returns 0. On error, it returns -1. + */ +static int +run_until(br_sslio_context *ctx, unsigned target) +{ + for (;;) { + unsigned state; + + state = br_ssl_engine_current_state(ctx->engine); + if (state & BR_SSL_CLOSED) { + return -1; + } + + /* + * If there is some record data to send, do it. This takes + * precedence over everything else. + */ + if (state & BR_SSL_SENDREC) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(ctx->engine, &len); + wlen = ctx->low_write(ctx->write_context, buf, len); + if (wlen < 0) { + /* + * If we received a close_notify and we + * still send something, then we have our + * own response close_notify to send, and + * the peer is allowed by RFC 5246 not to + * wait for it. + */ + if (!ctx->engine->shutdown_recv) { + br_ssl_engine_fail( + ctx->engine, BR_ERR_IO); + } + return -1; + } + if (wlen > 0) { + br_ssl_engine_sendrec_ack(ctx->engine, wlen); + } + continue; + } + + /* + * If we reached our target, then we are finished. + */ + if (state & target) { + return 0; + } + + /* + * If some application data must be read, and we did not + * exit, then this means that we are trying to write data, + * and that's not possible until the application data is + * read. This may happen if using a shared in/out buffer, + * and the underlying protocol is not strictly half-duplex. + * This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) { + return -1; + } + + /* + * If we reached that point, then either we are trying + * to read data and there is some, or the engine is stuck + * until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(ctx->engine, &len); + rlen = ctx->low_read(ctx->read_context, buf, len); + if (rlen < 0) { + br_ssl_engine_fail(ctx->engine, BR_ERR_IO); + return -1; + } + if (rlen > 0) { + br_ssl_engine_recvrec_ack(ctx->engine, rlen); + } + continue; + } + + /* + * We can reach that point if the target RECVAPP, and + * the state contains SENDAPP only. This may happen with + * a shared in/out buffer. In that case, we must flush + * the buffered data to "make room" for a new incoming + * record. + */ + br_ssl_engine_flush(ctx->engine, 0); + } +} + +/* see bearssl_ssl.h */ +int +br_sslio_read(br_sslio_context *ctx, void *dst, size_t len) +{ + unsigned char *buf; + size_t alen; + + if (len == 0) { + return 0; + } + if (run_until(ctx, BR_SSL_RECVAPP) < 0) { + return -1; + } + buf = br_ssl_engine_recvapp_buf(ctx->engine, &alen); + if (alen > len) { + alen = len; + } + memcpy(dst, buf, alen); + br_ssl_engine_recvapp_ack(ctx->engine, alen); + return (int)alen; +} + +/* see bearssl_ssl.h */ +int +br_sslio_read_all(br_sslio_context *ctx, void *dst, size_t len) +{ + unsigned char *buf; + + buf = dst; + while (len > 0) { + int rlen; + + rlen = br_sslio_read(ctx, buf, len); + if (rlen < 0) { + return -1; + } + buf += rlen; + len -= (size_t)rlen; + } + return 0; +} + +/* see bearssl_ssl.h */ +int +br_sslio_write(br_sslio_context *ctx, const void *src, size_t len) +{ + unsigned char *buf; + size_t alen; + + if (len == 0) { + return 0; + } + if (run_until(ctx, BR_SSL_SENDAPP) < 0) { + return -1; + } + buf = br_ssl_engine_sendapp_buf(ctx->engine, &alen); + if (alen > len) { + alen = len; + } + memcpy(buf, src, alen); + br_ssl_engine_sendapp_ack(ctx->engine, alen); + return (int)alen; +} + +/* see bearssl_ssl.h */ +int +br_sslio_write_all(br_sslio_context *ctx, const void *src, size_t len) +{ + const unsigned char *buf; + + buf = src; + while (len > 0) { + int wlen; + + wlen = br_sslio_write(ctx, buf, len); + if (wlen < 0) { + return -1; + } + buf += wlen; + len -= (size_t)wlen; + } + return 0; +} + +/* see bearssl_ssl.h */ +int +br_sslio_flush(br_sslio_context *ctx) +{ + /* + * We trigger a flush. We know the data is gone when there is + * no longer any record data to send, and we can either read + * or write application data. The call to run_until() does the + * job because it ensures that any assembled record data is + * first sent down the wire before considering anything else. + */ + br_ssl_engine_flush(ctx->engine, 0); + return run_until(ctx, BR_SSL_SENDAPP | BR_SSL_RECVAPP); +} + +/* see bearssl_ssl.h */ +int +br_sslio_close(br_sslio_context *ctx) +{ + br_ssl_engine_close(ctx->engine); + while (br_ssl_engine_current_state(ctx->engine) != BR_SSL_CLOSED) { + /* + * Discard any incoming application data. + */ + size_t len; + + run_until(ctx, BR_SSL_RECVAPP); + if (br_ssl_engine_recvapp_buf(ctx->engine, &len) != NULL) { + br_ssl_engine_recvapp_ack(ctx->engine, len); + } + } + return br_ssl_engine_last_error(ctx->engine) == BR_ERR_OK; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c new file mode 100644 index 0000000000..66fbe9ebb3 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_keyexport.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Supported cipher suites that use SHA-384 for the PRF when selected + * for TLS 1.2. All other cipher suites are deemed to use SHA-256. + */ +static const uint16_t suites_sha384[] PROGMEM = { + BR_TLS_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384, + BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 +}; + +/* see bearssl_ssl.h */ +int +br_ssl_key_export(br_ssl_engine_context *cc, + void *dst, size_t len, const char *label, + const void *context, size_t context_len) +{ + br_tls_prf_seed_chunk chunks[4]; + br_tls_prf_impl iprf; + size_t num_chunks, u; + unsigned char tmp[2]; + int prf_id; + + if (cc->application_data != 1) { + return 0; + } + chunks[0].data = cc->client_random; + chunks[0].len = sizeof cc->client_random; + chunks[1].data = cc->server_random; + chunks[1].len = sizeof cc->server_random; + if (context != NULL) { + br_enc16be(tmp, (unsigned)context_len); + chunks[2].data = tmp; + chunks[2].len = 2; + chunks[3].data = context; + chunks[3].len = context_len; + num_chunks = 4; + } else { + num_chunks = 2; + } + prf_id = BR_SSLPRF_SHA256; + for (u = 0; u < (sizeof suites_sha384) / sizeof(uint16_t); u ++) { + if (pgm_read_word(&suites_sha384[u]) == cc->session.cipher_suite) { + prf_id = BR_SSLPRF_SHA384; + } + } + iprf = br_ssl_engine_get_PRF(cc, prf_id); + iprf(dst, len, + cc->session.master_secret, sizeof cc->session.master_secret, + label, num_chunks, chunks); + return 1; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c new file mode 100644 index 0000000000..dd64f42ed8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_lru.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Each entry consists in a fixed number of bytes. Entries are concatenated + * in the store block. "Addresses" are really offsets in the block, + * expressed over 32 bits (so the cache may have size at most 4 GB, which + * "ought to be enough for everyone"). The "null address" is 0xFFFFFFFF. + * Note that since the storage block alignment is in no way guaranteed, we + * perform only accesses that can handle unaligned data. + * + * Two concurrent data structures are maintained: + * + * -- Entries are organised in a doubly-linked list; saved entries are added + * at the head, and loaded entries are moved to the head. Eviction uses + * the list tail (this is the LRU algorithm). + * + * -- Entries are indexed with a binary tree: all left descendants of a + * node have a lower session ID (in lexicographic order), while all + * right descendants have a higher session ID. The tree is heuristically + * balanced. + * + * Entry format: + * + * session ID 32 bytes + * master secret 48 bytes + * protocol version 2 bytes (big endian) + * cipher suite 2 bytes (big endian) + * list prev 4 bytes (big endian) + * list next 4 bytes (big endian) + * tree left child 4 bytes (big endian) + * tree right child 4 bytes (big endian) + * + * If an entry has a protocol version set to 0, then it is "disabled": + * it was a session pushed to the cache at some point, but it has + * been explicitly removed. + * + * We need to keep the tree balanced because an attacker could make + * handshakes, selecting some specific sessions (by reusing them) to + * try to make us make an imbalanced tree that makes lookups expensive + * (a denial-of-service attack that would persist as long as the cache + * remains, i.e. even after the attacker made all his connections). + * To do that, we replace the session ID (or the start of the session ID) + * with a HMAC value computed over the replaced part; the hash function + * implementation and the key are obtained from the server context upon + * first save() call. + * + * Theoretically, an attacker could use the exact timing of the lookup + * to infer the current tree topology, and try to revive entries to make + * it as unbalanced as possible. However, since the session ID are + * chosen randomly by the server, and the attacker cannot see the + * indexing values and must thus rely on blind selection, it should be + * exponentially difficult for the attacker to maintain a large + * imbalance. + */ +#define SESSION_ID_LEN 32 +#define MASTER_SECRET_LEN 48 + +#define SESSION_ID_OFF 0 +#define MASTER_SECRET_OFF 32 +#define VERSION_OFF 80 +#define CIPHER_SUITE_OFF 82 +#define LIST_PREV_OFF 84 +#define LIST_NEXT_OFF 88 +#define TREE_LEFT_OFF 92 +#define TREE_RIGHT_OFF 96 + +#define LRU_ENTRY_LEN 100 + +#define ADDR_NULL ((uint32_t)-1) + +#define GETSET(name, off) \ +static inline uint32_t get_ ## name(br_ssl_session_cache_lru *cc, uint32_t x) \ +{ \ + return br_dec32be(cc->store + x + (off)); \ +} \ +static inline void set_ ## name(br_ssl_session_cache_lru *cc, \ + uint32_t x, uint32_t val) \ +{ \ + br_enc32be(cc->store + x + (off), val); \ +} + +GETSET(prev, LIST_PREV_OFF) +GETSET(next, LIST_NEXT_OFF) +GETSET(left, TREE_LEFT_OFF) +GETSET(right, TREE_RIGHT_OFF) + +/* + * Transform the session ID by replacing the first N bytes with a HMAC + * value computed over these bytes, using the random key K (the HMAC + * value is truncated if needed). HMAC will use the same hash function + * as the DRBG in the SSL server context, so with SHA-256, SHA-384, + * or SHA-1, depending on what is available. + * + * The risk of collision is considered too small to be a concern; and + * the impact of a collision is low (the handshake won't succeed). This + * risk is much lower than any transmission error, which would lead to + * the same consequences. + * + * Source and destination arrays msut be disjoint. + */ +static void +mask_id(br_ssl_session_cache_lru *cc, + const unsigned char *src, unsigned char *dst) +{ + br_hmac_key_context hkc; + br_hmac_context hc; + + memcpy(dst, src, SESSION_ID_LEN); + br_hmac_key_init(&hkc, cc->hash, cc->index_key, sizeof cc->index_key); + br_hmac_init(&hc, &hkc, SESSION_ID_LEN); + br_hmac_update(&hc, src, SESSION_ID_LEN); + br_hmac_out(&hc, dst); +} + +/* + * Find a node by ID. Returned value is the node address, or ADDR_NULL if + * the node is not found. + * + * If addr_link is not NULL, then '*addr_link' is set to the address of the + * last followed link. If the found node is the root, or if the tree is + * empty, then '*addr_link' is set to ADDR_NULL. + */ +static uint32_t +find_node(br_ssl_session_cache_lru *cc, const unsigned char *id, + uint32_t *addr_link) +{ + uint32_t x, y; + + x = cc->root; + y = ADDR_NULL; + while (x != ADDR_NULL) { + int r; + + r = memcmp(id, cc->store + x + SESSION_ID_OFF, SESSION_ID_LEN); + if (r < 0) { + y = x + TREE_LEFT_OFF; + x = get_left(cc, x); + } else if (r == 0) { + if (addr_link != NULL) { + *addr_link = y; + } + return x; + } else { + y = x + TREE_RIGHT_OFF; + x = get_right(cc, x); + } + } + if (addr_link != NULL) { + *addr_link = y; + } + return ADDR_NULL; +} + +/* + * For node x, find its replacement upon removal. + * + * -- If node x has no child, then this returns ADDR_NULL. + * -- Otherwise, if node x has a left child, then the replacement is the + * rightmost left-descendent. + * -- Otherwise, the replacement is the leftmost right-descendent. + * + * If a node is returned, then '*al' is set to the address of the field + * that points to that node. Otherwise (node x has no child), '*al' is + * set to ADDR_NULL. + * + * Note that the replacement node, when found, is always a descendent + * of node 'x', so it cannot be the tree root. Thus, '*al' can be set + * to ADDR_NULL only when no node is found and ADDR_NULL is returned. + */ +static uint32_t +find_replacement_node(br_ssl_session_cache_lru *cc, uint32_t x, uint32_t *al) +{ + uint32_t y1, y2; + + y1 = get_left(cc, x); + if (y1 != ADDR_NULL) { + y2 = x + TREE_LEFT_OFF; + for (;;) { + uint32_t z; + + z = get_right(cc, y1); + if (z == ADDR_NULL) { + *al = y2; + return y1; + } + y2 = y1 + TREE_RIGHT_OFF; + y1 = z; + } + } + y1 = get_right(cc, x); + if (y1 != ADDR_NULL) { + y2 = x + TREE_RIGHT_OFF; + for (;;) { + uint32_t z; + + z = get_left(cc, y1); + if (z == ADDR_NULL) { + *al = y2; + return y1; + } + y2 = y1 + TREE_LEFT_OFF; + y1 = z; + } + } + *al = ADDR_NULL; + return ADDR_NULL; +} + +/* + * Set the link at address 'alx' to point to node 'x'. If 'alx' is + * ADDR_NULL, then this sets the tree root to 'x'. + */ +static inline void +set_link(br_ssl_session_cache_lru *cc, uint32_t alx, uint32_t x) +{ + if (alx == ADDR_NULL) { + cc->root = x; + } else { + br_enc32be(cc->store + alx, x); + } +} + +/* + * Remove node 'x' from the tree. This function shall not be called if + * node 'x' is not part of the tree. + */ +static void +remove_node(br_ssl_session_cache_lru *cc, uint32_t x) +{ + uint32_t alx, y, aly; + + /* + * Removal algorithm: + * ------------------ + * + * - If we remove the root, then the tree becomes empty. + * + * - If the removed node has no child, then we can simply remove + * it, with nothing else to do. + * + * - Otherwise, the removed node must be replaced by either its + * rightmost left-descendent, or its leftmost right-descendent. + * The replacement node itself must be removed from its current + * place. By definition, that replacement node has either no + * child, or at most a single child that will replace it in the + * tree. + */ + + /* + * Find node back and its ancestor link. If the node was the + * root, then alx is set to ADDR_NULL. + */ + find_node(cc, cc->store + x + SESSION_ID_OFF, &alx); + + /* + * Find replacement node 'y', and 'aly' is set to the address of + * the link to that replacement node. If the removed node has no + * child, then both 'y' and 'aly' are set to ADDR_NULL. + */ + y = find_replacement_node(cc, x, &aly); + + if (y != ADDR_NULL) { + uint32_t z; + + /* + * The unlinked replacement node may have one child (but + * not two) that takes its place. + */ + z = get_left(cc, y); + if (z == ADDR_NULL) { + z = get_right(cc, y); + } + set_link(cc, aly, z); + + /* + * Link the replacement node in its new place, overwriting + * the current link to the node 'x' (which removes 'x'). + */ + set_link(cc, alx, y); + + /* + * The replacement node adopts the left and right children + * of the removed node. Note that this also works even if + * the replacement node was a direct descendent of the + * removed node, since we unlinked it previously. + */ + set_left(cc, y, get_left(cc, x)); + set_right(cc, y, get_right(cc, x)); + } else { + /* + * No replacement, we simply unlink the node 'x'. + */ + set_link(cc, alx, ADDR_NULL); + } +} + +static void +lru_save(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + const br_ssl_session_parameters *params) +{ + br_ssl_session_cache_lru *cc; + unsigned char id[SESSION_ID_LEN]; + uint32_t x, alx; + + cc = (br_ssl_session_cache_lru *)ctx; + + /* + * If the buffer is too small, we don't record anything. This + * test avoids problems in subsequent code. + */ + if (cc->store_len < LRU_ENTRY_LEN) { + return; + } + + /* + * Upon the first save in a session cache instance, we obtain + * a random key for our indexing. + */ + if (!cc->init_done) { + br_hmac_drbg_generate(&server_ctx->eng.rng, + cc->index_key, sizeof cc->index_key); + cc->hash = br_hmac_drbg_get_hash(&server_ctx->eng.rng); + cc->init_done = 1; + } + mask_id(cc, params->session_id, id); + + /* + * Look for the node in the tree. If the same ID is already used, + * then reject it. This is a collision event, which should be + * exceedingly rare. + * Note: we do NOT record the emplacement here, because the + * removal of an entry may change the tree topology. + */ + if (find_node(cc, id, NULL) != ADDR_NULL) { + return; + } + + /* + * Find some room for the new parameters. If the cache is not + * full yet, add it to the end of the area and bump the pointer up. + * Otherwise, evict the list tail entry. Note that we already + * filtered out the case of a ridiculously small buffer that + * cannot hold any entry at all; thus, if there is no room for an + * extra entry, then the cache cannot be empty. + */ + if (cc->store_ptr > (cc->store_len - LRU_ENTRY_LEN)) { + /* + * Evict tail. If the buffer has room for a single entry, + * then this may also be the head. + */ + x = cc->tail; + cc->tail = get_prev(cc, x); + if (cc->tail == ADDR_NULL) { + cc->head = ADDR_NULL; + } else { + set_next(cc, cc->tail, ADDR_NULL); + } + + /* + * Remove the node from the tree. + */ + remove_node(cc, x); + } else { + /* + * Allocate room for new node. + */ + x = cc->store_ptr; + cc->store_ptr += LRU_ENTRY_LEN; + } + + /* + * Find the emplacement for the new node, and link it. + */ + find_node(cc, id, &alx); + set_link(cc, alx, x); + set_left(cc, x, ADDR_NULL); + set_right(cc, x, ADDR_NULL); + + /* + * New entry becomes new list head. It may also become the list + * tail if the cache was empty at that point. + */ + if (cc->head == ADDR_NULL) { + cc->tail = x; + } else { + set_prev(cc, cc->head, x); + } + set_prev(cc, x, ADDR_NULL); + set_next(cc, x, cc->head); + cc->head = x; + + /* + * Fill data in the entry. + */ + memcpy(cc->store + x + SESSION_ID_OFF, id, SESSION_ID_LEN); + memcpy(cc->store + x + MASTER_SECRET_OFF, + params->master_secret, MASTER_SECRET_LEN); + br_enc16be(cc->store + x + VERSION_OFF, params->version); + br_enc16be(cc->store + x + CIPHER_SUITE_OFF, params->cipher_suite); +} + +static int +lru_load(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + br_ssl_session_parameters *params) +{ + br_ssl_session_cache_lru *cc; + unsigned char id[SESSION_ID_LEN]; + uint32_t x; + + (void)server_ctx; + cc = (br_ssl_session_cache_lru *)ctx; + if (!cc->init_done) { + return 0; + } + mask_id(cc, params->session_id, id); + x = find_node(cc, id, NULL); + if (x != ADDR_NULL) { + unsigned version; + + version = br_dec16be(cc->store + x + VERSION_OFF); + if (version == 0) { + /* + * Entry is disabled, we pretend we did not find it. + * Notably, we don't move it to the front of the + * LRU list. + */ + return 0; + } + params->version = version; + params->cipher_suite = br_dec16be( + cc->store + x + CIPHER_SUITE_OFF); + memcpy(params->master_secret, + cc->store + x + MASTER_SECRET_OFF, + MASTER_SECRET_LEN); + if (x != cc->head) { + /* + * Found node is not at list head, so move + * it to the head. + */ + uint32_t p, n; + + p = get_prev(cc, x); + n = get_next(cc, x); + set_next(cc, p, n); + if (n == ADDR_NULL) { + cc->tail = p; + } else { + set_prev(cc, n, p); + } + set_prev(cc, cc->head, x); + set_next(cc, x, cc->head); + set_prev(cc, x, ADDR_NULL); + cc->head = x; + } + return 1; + } + return 0; +} + +static const br_ssl_session_cache_class lru_class = { + sizeof(br_ssl_session_cache_lru), + &lru_save, + &lru_load +}; + +/* see inner.h */ +void +br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc, + unsigned char *store, size_t store_len) +{ + cc->vtable = &lru_class; + cc->store = store; + cc->store_len = store_len; + cc->store_ptr = 0; + cc->init_done = 0; + cc->head = ADDR_NULL; + cc->tail = ADDR_NULL; + cc->root = ADDR_NULL; +} + +/* see bearssl_ssl.h */ +void br_ssl_session_cache_lru_forget( + br_ssl_session_cache_lru *cc, const unsigned char *id) +{ + unsigned char mid[SESSION_ID_LEN]; + uint32_t addr; + + /* + * If the cache is not initialised yet, then it is empty, and + * there is nothing to forget. + */ + if (!cc->init_done) { + return; + } + + /* + * Look for the node in the tree. If found, the entry is marked + * as "disabled"; it will be reused in due course, as it ages + * through the list. + * + * We do not go through the complex moves of actually releasing + * the entry right away because explicitly forgetting sessions + * should be a rare event, meant mostly for testing purposes, + * so this is not worth the extra code size. + */ + mask_id(cc, id, mid); + addr = find_node(cc, mid, NULL); + if (addr != ADDR_NULL) { + br_enc16be(cc->store + addr + VERSION_OFF, 0); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c new file mode 100644 index 0000000000..e49f9fb988 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_cbc.c @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +in_cbc_init(br_sslrec_in_cbc_context *cc, + const br_block_cbcdec_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv) +{ + cc->vtable = &br_sslrec_in_cbc_vtable; + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len); + br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len); + cc->mac_len = mac_out_len; + if (iv == NULL) { + memset(cc->iv, 0, sizeof cc->iv); + cc->explicit_IV = 1; + } else { + memcpy(cc->iv, iv, bc_impl->block_size); + cc->explicit_IV = 0; + } +} + +static int +cbc_check_length(const br_sslrec_in_cbc_context *cc, size_t rlen) +{ + /* + * Plaintext size: at most 16384 bytes + * Padding: at most 256 bytes + * MAC: mac_len extra bytes + * TLS 1.1+: each record has an explicit IV + * + * Minimum length includes at least one byte of padding, and the + * MAC. + * + * Total length must be a multiple of the block size. + */ + size_t blen; + size_t min_len, max_len; + + blen = cc->bc.vtable->block_size; + min_len = (blen + cc->mac_len) & ~(blen - 1); + max_len = (16384 + 256 + cc->mac_len) & ~(blen - 1); + if (cc->explicit_IV) { + min_len += blen; + max_len += blen; + } + return min_len <= rlen && rlen <= max_len; +} + +/* + * Rotate array buf[] of length 'len' to the left (towards low indices) + * by 'num' bytes if ctl is 1; otherwise, leave it unchanged. This is + * constant-time. 'num' MUST be lower than 'len'. 'len' MUST be lower + * than or equal to 64. + */ +static void +cond_rotate(uint32_t ctl, unsigned char *buf, size_t len, size_t num) +{ + unsigned char tmp[64]; + size_t u, v; + + for (u = 0, v = num; u < len; u ++) { + tmp[u] = MUX(ctl, buf[v], buf[u]); + if (++ v == len) { + v = 0; + } + } + memcpy(buf, tmp, len); +} + +static unsigned char * +cbc_decrypt(br_sslrec_in_cbc_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + /* + * We represent all lengths on 32-bit integers, because: + * -- SSL record lengths always fit in 32 bits; + * -- our constant-time primitives operate on 32-bit integers. + */ + unsigned char *buf; + uint32_t u, v, len, blen, min_len, max_len; + uint32_t good, pad_len, rot_count, len_withmac, len_nomac; + unsigned char tmp1[64], tmp2[64]; + int i; + br_hmac_context hc; + + buf = data; + len = *data_len; + blen = cc->bc.vtable->block_size; + + /* + * Decrypt data, and skip the explicit IV (if applicable). Note + * that the total length is supposed to have been verified by + * the caller. If there is an explicit IV, then we actually + * "decrypt" it using the implicit IV (from previous record), + * which is useless but harmless. + */ + cc->bc.vtable->run(&cc->bc.vtable, cc->iv, data, len); + if (cc->explicit_IV) { + buf += blen; + len -= blen; + } + + /* + * Compute minimum and maximum length of plaintext + MAC. These + * lengths can be inferred from the outside: they are not secret. + */ + min_len = (cc->mac_len + 256 < len) ? len - 256 : cc->mac_len; + max_len = len - 1; + + /* + * Use the last decrypted byte to compute the actual payload + * length. Take care not to overflow (we use unsigned types). + */ + pad_len = buf[max_len]; + good = LE(pad_len, (uint32_t)(max_len - min_len)); + len = MUX(good, (uint32_t)(max_len - pad_len), min_len); + + /* + * Check padding contents: all padding bytes must be equal to + * the value of pad_len. + */ + for (u = min_len; u < max_len; u ++) { + good &= LT(u, len) | EQ(buf[u], pad_len); + } + + /* + * Extract the MAC value. This is done in one pass, but results + * in a "rotated" MAC value depending on where it actually + * occurs. The 'rot_count' value is set to the offset of the + * first MAC byte within tmp1[]. + * + * min_len and max_len are also adjusted to the minimum and + * maximum lengths of the plaintext alone (without the MAC). + */ + len_withmac = (uint32_t)len; + len_nomac = len_withmac - cc->mac_len; + min_len -= cc->mac_len; + rot_count = 0; + memset(tmp1, 0, cc->mac_len); + v = 0; + for (u = min_len; u < max_len; u ++) { + tmp1[v] |= MUX(GE(u, len_nomac) & LT(u, len_withmac), + buf[u], 0x00); + rot_count = MUX(EQ(u, len_nomac), v, rot_count); + if (++ v == cc->mac_len) { + v = 0; + } + } + max_len -= cc->mac_len; + + /* + * Rotate back the MAC value. The loop below does the constant-time + * rotation in time n*log n for a MAC output of length n. We assume + * that the MAC output length is no more than 64 bytes, so the + * rotation count fits on 6 bits. + */ + for (i = 5; i >= 0; i --) { + uint32_t rc; + + rc = (uint32_t)1 << i; + cond_rotate(rot_count >> i, tmp1, cc->mac_len, rc); + rot_count &= ~rc; + } + + /* + * Recompute the HMAC value. The input is the concatenation of + * the sequence number (8 bytes), the record header (5 bytes), + * and the payload. + * + * At that point, min_len is the minimum plaintext length, but + * max_len still includes the MAC length. + */ + br_enc64be(tmp2, cc->seq ++); + tmp2[8] = (unsigned char)record_type; + br_enc16be(tmp2 + 9, version); + br_enc16be(tmp2 + 11, len_nomac); + br_hmac_init(&hc, &cc->mac, cc->mac_len); + br_hmac_update(&hc, tmp2, 13); + br_hmac_outCT(&hc, buf, len_nomac, min_len, max_len, tmp2); + + /* + * Compare the extracted and recomputed MAC values. + */ + for (u = 0; u < cc->mac_len; u ++) { + good &= EQ0(tmp1[u] ^ tmp2[u]); + } + + /* + * Check that the plaintext length is valid. The previous + * check was on the encrypted length, but the padding may have + * turned shorter than expected. + * + * Once this final test is done, the critical "constant-time" + * section ends and we can make conditional jumps again. + */ + good &= LE(len_nomac, 16384); + + if (!good) { + return 0; + } + *data_len = len_nomac; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable PROGMEM = { + { + sizeof(br_sslrec_in_cbc_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &cbc_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &cbc_decrypt + }, + (void (*)(const br_sslrec_in_cbc_class **, + const br_block_cbcdec_class *, const void *, size_t, + const br_hash_class *, const void *, size_t, size_t, + const void *)) + &in_cbc_init +}; + +/* + * For CBC output: + * + * -- With TLS 1.1+, there is an explicit IV. Generation method uses + * HMAC, computed over the current sequence number, and the current MAC + * key. The resulting value is truncated to the size of a block, and + * added at the head of the plaintext; it will get encrypted along with + * the data. This custom generation mechanism is "safe" under the + * assumption that HMAC behaves like a random oracle; since the MAC for + * a record is computed over the concatenation of the sequence number, + * the record header and the plaintext, the HMAC-for-IV will not collide + * with the normal HMAC. + * + * -- With TLS 1.0, for application data, we want to enforce a 1/n-1 + * split, as a countermeasure against chosen-plaintext attacks. We thus + * need to leave some room in the buffer for that extra record. + */ + +static void +out_cbc_init(br_sslrec_out_cbc_context *cc, + const br_block_cbcenc_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv) +{ + cc->vtable = &br_sslrec_out_cbc_vtable; + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, bc_key, bc_key_len); + br_hmac_key_init(&cc->mac, dig_impl, mac_key, mac_key_len); + cc->mac_len = mac_out_len; + if (iv == NULL) { + memset(cc->iv, 0, sizeof cc->iv); + cc->explicit_IV = 1; + } else { + memcpy(cc->iv, iv, bc_impl->block_size); + cc->explicit_IV = 0; + } +} + +static void +cbc_max_plaintext(const br_sslrec_out_cbc_context *cc, + size_t *start, size_t *end) +{ + size_t blen, len; + + blen = cc->bc.vtable->block_size; + if (cc->explicit_IV) { + *start += blen; + } else { + *start += 4 + ((cc->mac_len + blen + 1) & ~(blen - 1)); + } + len = (*end - *start) & ~(blen - 1); + len -= 1 + cc->mac_len; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +cbc_encrypt(br_sslrec_out_cbc_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf, *rbuf; + size_t len, blen, plen; + unsigned char tmp[13]; + br_hmac_context hc; + + buf = data; + len = *data_len; + blen = cc->bc.vtable->block_size; + + /* + * If using TLS 1.0, with more than one byte of plaintext, and + * the record is application data, then we need to compute + * a "split". We do not perform the split on other record types + * because it turned out that some existing, deployed + * implementations of SSL/TLS do not tolerate the splitting of + * some message types (in particular the Finished message). + * + * If using TLS 1.1+, then there is an explicit IV. We produce + * that IV by adding an extra initial plaintext block, whose + * value is computed with HMAC over the record sequence number. + */ + if (cc->explicit_IV) { + /* + * We use here the fact that all the HMAC variants we + * support can produce at least 16 bytes, while all the + * block ciphers we support have blocks of no more than + * 16 bytes. Thus, we can always truncate the HMAC output + * down to the block size. + */ + br_enc64be(tmp, cc->seq); + br_hmac_init(&hc, &cc->mac, blen); + br_hmac_update(&hc, tmp, 8); + br_hmac_out(&hc, buf - blen); + rbuf = buf - blen - 5; + } else { + if (len > 1 && record_type == BR_SSL_APPLICATION_DATA) { + /* + * To do the split, we use a recursive invocation; + * since we only give one byte to the inner call, + * the recursion stops there. + * + * We need to compute the exact size of the extra + * record, so that the two resulting records end up + * being sequential in RAM. + * + * We use here the fact that cbc_max_plaintext() + * adjusted the start offset to leave room for the + * initial fragment. + */ + size_t xlen; + + rbuf = buf - 4 + - ((cc->mac_len + blen + 1) & ~(blen - 1)); + rbuf[0] = buf[0]; + xlen = 1; + rbuf = cbc_encrypt(cc, record_type, + version, rbuf, &xlen); + buf ++; + len --; + } else { + rbuf = buf - 5; + } + } + + /* + * Compute MAC. + */ + br_enc64be(tmp, cc->seq ++); + tmp[8] = record_type; + br_enc16be(tmp + 9, version); + br_enc16be(tmp + 11, len); + br_hmac_init(&hc, &cc->mac, cc->mac_len); + br_hmac_update(&hc, tmp, 13); + br_hmac_update(&hc, buf, len); + br_hmac_out(&hc, buf + len); + len += cc->mac_len; + + /* + * Add padding. + */ + plen = blen - (len & (blen - 1)); + memset(buf + len, (unsigned)plen - 1, plen); + len += plen; + + /* + * If an explicit IV is used, the corresponding extra block was + * already put in place earlier; we just have to account for it + * here. + */ + if (cc->explicit_IV) { + buf -= blen; + len += blen; + } + + /* + * Encrypt the whole thing. If there is an explicit IV, we also + * encrypt it, which is fine (encryption of a uniformly random + * block is still a uniformly random block). + */ + cc->bc.vtable->run(&cc->bc.vtable, cc->iv, buf, len); + + /* + * Add the header and return. + */ + buf[-5] = record_type; + br_enc16be(buf - 4, version); + br_enc16be(buf - 2, len); + *data_len = (size_t)((buf + len) - rbuf); + return rbuf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable PROGMEM = { + { + sizeof(br_sslrec_out_cbc_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &cbc_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &cbc_encrypt + }, + (void (*)(const br_sslrec_out_cbc_class **, + const br_block_cbcenc_class *, const void *, size_t, + const br_hash_class *, const void *, size_t, size_t, + const void *)) + &out_cbc_init +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c new file mode 100644 index 0000000000..5c03ab7f83 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_ccm.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * CCM initialisation. This does everything except setting the vtable, + * which depends on whether this is a context for encrypting or for + * decrypting. + */ +static void +gen_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, key, key_len); + memcpy(cc->iv, iv, sizeof cc->iv); + cc->tag_len = tag_len; +} + +static void +in_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->vtable.in = &br_sslrec_in_ccm_vtable; + gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); +} + +static int +ccm_check_length(const br_sslrec_ccm_context *cc, size_t rlen) +{ + /* + * CCM overhead is 8 bytes for nonce_explicit, and the tag + * (normally 8 or 16 bytes, depending on cipher suite). + */ + size_t over; + + over = 8 + cc->tag_len; + return rlen >= over && rlen <= (16384 + over); +} + +static unsigned char * +ccm_decrypt(br_sslrec_ccm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + br_ccm_context zc; + unsigned char *buf; + unsigned char nonce[12], header[13]; + size_t len; + + buf = (unsigned char *)data + 8; + len = *data_len - (8 + cc->tag_len); + + /* + * Make nonce (implicit + explicit parts). + */ + memcpy(nonce, cc->iv, sizeof cc->iv); + memcpy(nonce + 4, data, 8); + + /* + * Assemble synthetic header for the AAD. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + + /* + * Perform CCM decryption. + */ + br_ccm_init(&zc, &cc->bc.vtable); + br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); + br_ccm_aad_inject(&zc, header, sizeof header); + br_ccm_flip(&zc); + br_ccm_run(&zc, 0, buf, len); + if (!br_ccm_check_tag(&zc, buf + len)) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_ccm_class br_sslrec_in_ccm_vtable PROGMEM = { + { + sizeof(br_sslrec_ccm_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &ccm_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &ccm_decrypt + }, + (void (*)(const br_sslrec_in_ccm_class **, + const br_block_ctrcbc_class *, const void *, size_t, + const void *, size_t)) + &in_ccm_init +}; + +static void +out_ccm_init(br_sslrec_ccm_context *cc, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len) +{ + cc->vtable.out = &br_sslrec_out_ccm_vtable; + gen_ccm_init(cc, bc_impl, key, key_len, iv, tag_len); +} + +static void +ccm_max_plaintext(const br_sslrec_ccm_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + *start += 8; + len = *end - *start - cc->tag_len; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +ccm_encrypt(br_sslrec_ccm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + br_ccm_context zc; + unsigned char *buf; + unsigned char nonce[12], header[13]; + size_t len; + + buf = (unsigned char *)data; + len = *data_len; + + /* + * Make nonce; the explicit part is an encoding of the sequence + * number. + */ + memcpy(nonce, cc->iv, sizeof cc->iv); + br_enc64be(nonce + 4, cc->seq); + + /* + * Assemble synthetic header for the AAD. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + + /* + * Perform CCM encryption. + */ + br_ccm_init(&zc, &cc->bc.vtable); + br_ccm_reset(&zc, nonce, sizeof nonce, sizeof header, len, cc->tag_len); + br_ccm_aad_inject(&zc, header, sizeof header); + br_ccm_flip(&zc); + br_ccm_run(&zc, 1, buf, len); + br_ccm_get_tag(&zc, buf + len); + + /* + * Assemble header and adjust pointer/length. + */ + len += 8 + cc->tag_len; + buf -= 13; + memcpy(buf + 5, nonce + 4, 8); + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len); + *data_len = len + 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_ccm_class br_sslrec_out_ccm_vtable PROGMEM = { + { + sizeof(br_sslrec_ccm_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &ccm_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &ccm_encrypt + }, + (void (*)(const br_sslrec_out_ccm_class **, + const br_block_ctrcbc_class *, const void *, size_t, + const void *, size_t)) + &out_ccm_init +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c new file mode 100644 index 0000000000..6273680a34 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_chapol.c @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static void +gen_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->seq = 0; + cc->ichacha = ichacha; + cc->ipoly = ipoly; + memcpy(cc->key, key, sizeof cc->key); + memcpy(cc->iv, iv, sizeof cc->iv); +} + +static void +gen_chapol_process(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t len, + void *tag, int encrypt) +{ + unsigned char header[13]; + unsigned char nonce[12]; + uint64_t seq; + size_t u; + + seq = cc->seq ++; + br_enc64be(header, seq); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + memcpy(nonce, cc->iv, 12); + for (u = 0; u < 8; u ++) { + nonce[11 - u] ^= (unsigned char)seq; + seq >>= 8; + } + cc->ipoly(cc->key, nonce, data, len, header, sizeof header, + tag, cc->ichacha, encrypt); +} + +static void +in_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->vtable.in = &br_sslrec_in_chapol_vtable; + gen_chapol_init(cc, ichacha, ipoly, key, iv); +} + +static int +chapol_check_length(const br_sslrec_chapol_context *cc, size_t rlen) +{ + /* + * Overhead is just the authentication tag (16 bytes). + */ + (void)cc; + return rlen >= 16 && rlen <= (16384 + 16); +} + +static unsigned char * +chapol_decrypt(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t u, len; + unsigned char tag[16]; + unsigned bad; + + buf = data; + len = *data_len - 16; + gen_chapol_process(cc, record_type, version, buf, len, tag, 0); + bad = 0; + for (u = 0; u < 16; u ++) { + bad |= tag[u] ^ buf[len + u]; + } + if (bad) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_chapol_class br_sslrec_in_chapol_vtable PROGMEM = { + { + sizeof(br_sslrec_chapol_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &chapol_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &chapol_decrypt + }, + (void (*)(const br_sslrec_in_chapol_class **, + br_chacha20_run, br_poly1305_run, + const void *, const void *)) + &in_chapol_init +}; + +static void +out_chapol_init(br_sslrec_chapol_context *cc, + br_chacha20_run ichacha, br_poly1305_run ipoly, + const void *key, const void *iv) +{ + cc->vtable.out = &br_sslrec_out_chapol_vtable; + gen_chapol_init(cc, ichacha, ipoly, key, iv); +} + +static void +chapol_max_plaintext(const br_sslrec_chapol_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + len = *end - *start - 16; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +chapol_encrypt(br_sslrec_chapol_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t len; + + buf = data; + len = *data_len; + gen_chapol_process(cc, record_type, version, buf, len, buf + len, 1); + buf -= 5; + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len + 16); + *data_len = len + 21; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_chapol_class br_sslrec_out_chapol_vtable PROGMEM = { + { + sizeof(br_sslrec_chapol_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &chapol_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &chapol_encrypt + }, + (void (*)(const br_sslrec_out_chapol_class **, + br_chacha20_run, br_poly1305_run, + const void *, const void *)) + &out_chapol_init +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c new file mode 100644 index 0000000000..933eaffb46 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_rec_gcm.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * GCM initialisation. This does everything except setting the vtable, + * which depends on whether this is a context for encrypting or for + * decrypting. + */ +static void +gen_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + unsigned char tmp[12]; + + cc->seq = 0; + bc_impl->init(&cc->bc.vtable, key, key_len); + cc->gh = gh_impl; + memcpy(cc->iv, iv, sizeof cc->iv); + memset(cc->h, 0, sizeof cc->h); + memset(tmp, 0, sizeof tmp); + bc_impl->run(&cc->bc.vtable, tmp, 0, cc->h, sizeof cc->h); +} + +static void +in_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + cc->vtable.in = &br_sslrec_in_gcm_vtable; + gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv); +} + +static int +gcm_check_length(const br_sslrec_gcm_context *cc, size_t rlen) +{ + /* + * GCM adds a fixed overhead: + * 8 bytes for the nonce_explicit (before the ciphertext) + * 16 bytes for the authentication tag (after the ciphertext) + */ + (void)cc; + return rlen >= 24 && rlen <= (16384 + 24); +} + +/* + * Compute the authentication tag. The value written in 'tag' must still + * be CTR-encrypted. + */ +static void +do_tag(br_sslrec_gcm_context *cc, + int record_type, unsigned version, + void *data, size_t len, void *tag) +{ + unsigned char header[13]; + unsigned char footer[16]; + + /* + * Compute authentication tag. Three elements must be injected in + * sequence, each possibly 0-padded to reach a length multiple + * of the block size: the 13-byte header (sequence number, record + * type, protocol version, record length), the cipher text, and + * the word containing the encodings of the bit lengths of the two + * other elements. + */ + br_enc64be(header, cc->seq ++); + header[8] = (unsigned char)record_type; + br_enc16be(header + 9, version); + br_enc16be(header + 11, len); + br_enc64be(footer, (uint64_t)(sizeof header) << 3); + br_enc64be(footer + 8, (uint64_t)len << 3); + memset(tag, 0, 16); + cc->gh(tag, cc->h, header, sizeof header); + cc->gh(tag, cc->h, data, len); + cc->gh(tag, cc->h, footer, sizeof footer); +} + +/* + * Do CTR encryption. This also does CTR encryption of a single block at + * address 'xortag' with the counter value appropriate for the final + * processing of the authentication tag. + */ +static void +do_ctr(br_sslrec_gcm_context *cc, const void *nonce, void *data, size_t len, + void *xortag) +{ + unsigned char iv[12]; + + memcpy(iv, cc->iv, 4); + memcpy(iv + 4, nonce, 8); + cc->bc.vtable->run(&cc->bc.vtable, iv, 2, data, len); + cc->bc.vtable->run(&cc->bc.vtable, iv, 1, xortag, 16); +} + +static unsigned char * +gcm_decrypt(br_sslrec_gcm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t len, u; + uint32_t bad; + unsigned char tag[16]; + + buf = (unsigned char *)data + 8; + len = *data_len - 24; + do_tag(cc, record_type, version, buf, len, tag); + do_ctr(cc, data, buf, len, tag); + + /* + * Compare the computed tag with the value from the record. It + * is possibly useless to do a constant-time comparison here, + * but it does not hurt. + */ + bad = 0; + for (u = 0; u < 16; u ++) { + bad |= tag[u] ^ buf[len + u]; + } + if (bad) { + return NULL; + } + *data_len = len; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable PROGMEM = { + { + sizeof(br_sslrec_gcm_context), + (int (*)(const br_sslrec_in_class *const *, size_t)) + &gcm_check_length, + (unsigned char *(*)(const br_sslrec_in_class **, + int, unsigned, void *, size_t *)) + &gcm_decrypt + }, + (void (*)(const br_sslrec_in_gcm_class **, + const br_block_ctr_class *, const void *, size_t, + br_ghash, const void *)) + &in_gcm_init +}; + +static void +out_gcm_init(br_sslrec_gcm_context *cc, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv) +{ + cc->vtable.out = &br_sslrec_out_gcm_vtable; + gen_gcm_init(cc, bc_impl, key, key_len, gh_impl, iv); +} + +static void +gcm_max_plaintext(const br_sslrec_gcm_context *cc, + size_t *start, size_t *end) +{ + size_t len; + + (void)cc; + *start += 8; + len = *end - *start - 16; + if (len > 16384) { + len = 16384; + } + *end = *start + len; +} + +static unsigned char * +gcm_encrypt(br_sslrec_gcm_context *cc, + int record_type, unsigned version, void *data, size_t *data_len) +{ + unsigned char *buf; + size_t u, len; + unsigned char tmp[16]; + + buf = (unsigned char *)data; + len = *data_len; + memset(tmp, 0, sizeof tmp); + br_enc64be(buf - 8, cc->seq); + do_ctr(cc, buf - 8, buf, len, tmp); + do_tag(cc, record_type, version, buf, len, buf + len); + for (u = 0; u < 16; u ++) { + buf[len + u] ^= tmp[u]; + } + len += 24; + buf -= 13; + buf[0] = (unsigned char)record_type; + br_enc16be(buf + 1, version); + br_enc16be(buf + 3, len); + *data_len = len + 5; + return buf; +} + +/* see bearssl_ssl.h */ +const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable PROGMEM = { + { + sizeof(br_sslrec_gcm_context), + (void (*)(const br_sslrec_out_class *const *, + size_t *, size_t *)) + &gcm_max_plaintext, + (unsigned char *(*)(const br_sslrec_out_class **, + int, unsigned, void *, size_t *)) + &gcm_encrypt + }, + (void (*)(const br_sslrec_out_gcm_class **, + const br_block_ctr_class *, const void *, size_t, + br_ghash, const void *)) + &out_gcm_init +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c new file mode 100644 index 0000000000..9d1bfa52d6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_ec.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static int +se_choose(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices) +{ + br_ssl_server_policy_ec_context *pc; + const br_suite_translated *st; + size_t u, st_num; + unsigned hash_id; + + pc = (br_ssl_server_policy_ec_context *)pctx; + st = br_ssl_server_get_client_suites(cc, &st_num); + hash_id = br_ssl_choose_hash(br_ssl_server_get_client_hashes(cc) >> 8); + if (cc->eng.session.version < BR_TLS12) { + hash_id = br_sha1_ID; + } + choices->chain = pc->chain; + choices->chain_len = pc->chain_len; + for (u = 0; u < st_num; u ++) { + unsigned tt; + + tt = st[u][1]; + switch (tt >> 12) { + case BR_SSLKEYX_ECDH_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && pc->cert_issuer_key_type == BR_KEYTYPE_RSA) + { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDH_ECDSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0 + && pc->cert_issuer_key_type == BR_KEYTYPE_EC) + { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDHE_ECDSA: + if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0 + && hash_id != 0) + { + choices->cipher_suite = st[u][0]; + choices->algo_id = hash_id + 0xFF00; + return 1; + } + break; + } + } + return 0; +} + +static uint32_t +se_do_keyx(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_server_policy_ec_context *pc; + uint32_t r; + size_t xoff, xlen; + + pc = (br_ssl_server_policy_ec_context *)pctx; + r = pc->iec->mul(data, *len, pc->sk->x, pc->sk->xlen, pc->sk->curve); + xoff = pc->iec->xoff(pc->sk->curve, &xlen); + memmove(data, data + xoff, xlen); + *len = xlen; + return r; +} + +static size_t +se_do_sign(const br_ssl_server_policy_class **pctx, + unsigned algo_id, unsigned char *data, size_t hv_len, size_t len) +{ + br_ssl_server_policy_ec_context *pc; + unsigned char hv[64]; + const br_hash_class *hc; + + algo_id &= 0xFF; + pc = (br_ssl_server_policy_ec_context *)pctx; + hc = br_multihash_getimpl(pc->mhash, algo_id); + if (hc == NULL) { + return 0; + } + memcpy(hv, data, hv_len); + if (len < 139) { + return 0; + } + return pc->iecdsa(pc->iec, hc, hv, pc->sk, data); +} + +static const br_ssl_server_policy_class se_policy_vtable PROGMEM = { + sizeof(br_ssl_server_policy_ec_context), + se_choose, + se_do_keyx, + se_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_server_set_single_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa) +{ + cc->chain_handler.single_ec.vtable = &se_policy_vtable; + cc->chain_handler.single_ec.chain = chain; + cc->chain_handler.single_ec.chain_len = chain_len; + cc->chain_handler.single_ec.sk = sk; + cc->chain_handler.single_ec.allowed_usages = allowed_usages; + cc->chain_handler.single_ec.cert_issuer_key_type = cert_issuer_key_type; + cc->chain_handler.single_ec.mhash = &cc->eng.mhash; + cc->chain_handler.single_ec.iec = iec; + cc->chain_handler.single_ec.iecdsa = iecdsa; + cc->policy_vtable = &cc->chain_handler.single_ec.vtable; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c new file mode 100644 index 0000000000..01de6d0c9f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/ssl/ssl_scert_single_rsa.c @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static int +sr_choose(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices) +{ + br_ssl_server_policy_rsa_context *pc; + const br_suite_translated *st; + size_t u, st_num; + unsigned hash_id; + int fh; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + st = br_ssl_server_get_client_suites(cc, &st_num); + if (cc->eng.session.version < BR_TLS12) { + hash_id = 0; + fh = 1; + } else { + hash_id = br_ssl_choose_hash( + br_ssl_server_get_client_hashes(cc)); + fh = (hash_id != 0); + } + choices->chain = pc->chain; + choices->chain_len = pc->chain_len; + for (u = 0; u < st_num; u ++) { + unsigned tt; + + tt = st[u][1]; + switch (tt >> 12) { + case BR_SSLKEYX_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_KEYX) != 0) { + choices->cipher_suite = st[u][0]; + return 1; + } + break; + case BR_SSLKEYX_ECDHE_RSA: + if ((pc->allowed_usages & BR_KEYTYPE_SIGN) != 0 && fh) { + choices->cipher_suite = st[u][0]; + choices->algo_id = hash_id + 0xFF00; + return 1; + } + break; + } + } + return 0; +} + +static uint32_t +sr_do_keyx(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len) +{ + br_ssl_server_policy_rsa_context *pc; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + return br_rsa_ssl_decrypt(pc->irsacore, pc->sk, data, *len); +} + +/* + * OID for hash functions in RSA signatures. + */ +/*static*/ const unsigned char HASH_OID_SHA1[] = { + 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A +}; + +/*static*/ const unsigned char HASH_OID_SHA224[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04 +}; + +/*static*/ const unsigned char HASH_OID_SHA256[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 +}; + +/*static*/ const unsigned char HASH_OID_SHA384[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 +}; + +/*static*/ const unsigned char HASH_OID_SHA512[] = { + 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 +}; + +static const unsigned char *HASH_OID[] PROGMEM = { + HASH_OID_SHA1, + HASH_OID_SHA224, + HASH_OID_SHA256, + HASH_OID_SHA384, + HASH_OID_SHA512 +}; + +static size_t +sr_do_sign(const br_ssl_server_policy_class **pctx, + unsigned algo_id, unsigned char *data, size_t hv_len, size_t len) +{ + br_ssl_server_policy_rsa_context *pc; + unsigned char hv[64]; + size_t sig_len; + const unsigned char *hash_oid; + + pc = (br_ssl_server_policy_rsa_context *)pctx; + memcpy(hv, data, hv_len); + algo_id &= 0xFF; + if (algo_id == 0) { + hash_oid = NULL; + } else if (algo_id >= 2 && algo_id <= 6) { + hash_oid = HASH_OID[algo_id - 2]; + } else { + return 0; + } + sig_len = (pc->sk->n_bitlen + 7) >> 3; + if (len < sig_len) { + return 0; + } + return pc->irsasign(hash_oid, hv, hv_len, pc->sk, data) ? sig_len : 0; +} + +static const br_ssl_server_policy_class sr_policy_vtable PROGMEM = { + sizeof(br_ssl_server_policy_rsa_context), + sr_choose, + sr_do_keyx, + sr_do_sign +}; + +/* see bearssl_ssl.h */ +void +br_ssl_server_set_single_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, unsigned allowed_usages, + br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign) +{ + cc->chain_handler.single_rsa.vtable = &sr_policy_vtable; + cc->chain_handler.single_rsa.chain = chain; + cc->chain_handler.single_rsa.chain_len = chain_len; + cc->chain_handler.single_rsa.sk = sk; + cc->chain_handler.single_rsa.allowed_usages = allowed_usages; + cc->chain_handler.single_rsa.irsacore = irsacore; + cc->chain_handler.single_rsa.irsasign = irsasign; + cc->policy_vtable = &cc->chain_handler.single_rsa.vtable; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c new file mode 100644 index 0000000000..fe11a0c4eb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_cbcdec_vtable; + ctx->num_rounds = br_aes_big_keysched_inv(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[16]; + int i; + + memcpy(tmp, buf, 16); + br_aes_big_decrypt(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_big_cbcdec_vtable PROGMEM = { + sizeof(br_aes_big_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_big_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_big_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c new file mode 100644 index 0000000000..215870c48a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_cbcenc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_big_cbcenc_vtable PROGMEM = { + sizeof(br_aes_big_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_big_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_big_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c new file mode 100644 index 0000000000..6f853dbcb1 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_ctr_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + + memcpy(tmp, iv, 12); + br_enc32be(tmp + 12, cc ++); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp); + if (len <= 16) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_big_ctr_vtable PROGMEM = { + sizeof(br_aes_big_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_big_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_big_ctr_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c new file mode 100644 index 0000000000..aed59ed547 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_ctrcbc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_big_ctrcbc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf, *bctr; + uint32_t cc0, cc1, cc2, cc3; + + buf = data; + bctr = ctr; + cc3 = br_dec32be(bctr + 0); + cc2 = br_dec32be(bctr + 4); + cc1 = br_dec32be(bctr + 8); + cc0 = br_dec32be(bctr + 12); + while (len > 0) { + unsigned char tmp[16]; + uint32_t carry; + + br_enc32be(tmp + 0, cc3); + br_enc32be(tmp + 4, cc2); + br_enc32be(tmp + 8, cc1); + br_enc32be(tmp + 12, cc0); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, tmp); + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + cc0 ++; + carry = (~(cc0 | -cc0)) >> 31; + cc1 += carry; + carry &= (~(cc1 | -cc1)) >> 31; + cc2 += carry; + carry &= (~(cc2 | -cc2)) >> 31; + cc3 += carry; + } + br_enc32be(bctr + 0, cc3); + br_enc32be(bctr + 4, cc2); + br_enc32be(bctr + 8, cc1); + br_enc32be(bctr + 12, cc0); +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + xorbuf(cbcmac, buf, 16); + br_aes_big_encrypt(ctx->num_rounds, ctx->skey, cbcmac); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_big_ctrcbc_ctr(ctx, ctr, data, len); + br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len); +} + +/* see bearssl_block.h */ +void +br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_big_ctrcbc_mac(ctx, cbcmac, data, len); + br_aes_big_ctrcbc_ctr(ctx, ctr, data, len); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_big_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_big_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_big_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_big_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_big_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_big_ctrcbc_mac +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c new file mode 100644 index 0000000000..a762402ee8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_dec.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Inverse S-box (used in key schedule for decryption). + */ +static const unsigned char iS[] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D +}; + +static const uint32_t iSsm0[] = { + 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, + 0xACFA58AB, 0x4BE30393, 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, + 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F, 0xDEB15A49, 0x25BA1B67, + 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6, + 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, + 0x49E06929, 0x8EC9C844, 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, + 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4, 0x63DF4A18, 0xE51A3182, + 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94, + 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, + 0xE31F8F57, 0x6655AB2A, 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, + 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C, 0x8ACF1C2B, 0xA779B492, + 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A, + 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, + 0x5E719F06, 0xBD6E1051, 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, + 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF, 0x1998FB24, 0xD6BDE997, + 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB, + 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, + 0x1E1170AC, 0x6C5A724E, 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, + 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A, 0x0C0A67B1, 0x9357E70F, + 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16, + 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, + 0x2DB6A8B9, 0x141EA9C8, 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, + 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34, 0x8B432976, 0xCB23C6DC, + 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120, + 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, + 0x0D8652EC, 0x77C1E3D0, 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, + 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF, 0x87494EC7, 0xD938D1C1, + 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4, + 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, + 0x2E39F75E, 0x82C3AFF5, 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, + 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B, 0xCD267809, 0x6E5918F4, + 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6, + 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, + 0xC6A59430, 0x35A266C0, 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, + 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F, 0x764DD68D, 0x43EFB04D, + 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F, + 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, + 0xE9105633, 0x6DD64713, 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, + 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C, 0x9CD2DF59, 0x55F2733F, + 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86, + 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, + 0x283C498B, 0xFF0D9541, 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, + 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742 +}; + +static unsigned +mul2(unsigned x) +{ + x <<= 1; + return x ^ ((unsigned)(-(int)(x >> 8)) & 0x11B); +} + +static unsigned +mul9(unsigned x) +{ + return x ^ mul2(mul2(mul2(x))); +} + +static unsigned +mulb(unsigned x) +{ + unsigned x2; + + x2 = mul2(x); + return x ^ x2 ^ mul2(mul2(x2)); +} + +static unsigned +muld(unsigned x) +{ + unsigned x4; + + x4 = mul2(mul2(x)); + return x ^ x4 ^ mul2(x4); +} + +static unsigned +mule(unsigned x) +{ + unsigned x2, x4; + + x2 = mul2(x); + x4 = mul2(x2); + return x2 ^ x4 ^ mul2(x4); +} + +/* see inner.h */ +unsigned +br_aes_big_keysched_inv(uint32_t *skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, m; + + /* + * Sub-keys for decryption are distinct from encryption sub-keys + * in that InvMixColumns() is already applied for the inner + * rounds. + */ + num_rounds = br_aes_keysched(skey, key, key_len); + m = (int)(num_rounds << 2); + for (i = 4; i < m; i ++) { + uint32_t p; + unsigned p0, p1, p2, p3; + uint32_t q0, q1, q2, q3; + + p = skey[i]; + p0 = p >> 24; + p1 = (p >> 16) & 0xFF; + p2 = (p >> 8) & 0xFF; + p3 = p & 0xFF; + q0 = mule(p0) ^ mulb(p1) ^ muld(p2) ^ mul9(p3); + q1 = mul9(p0) ^ mule(p1) ^ mulb(p2) ^ muld(p3); + q2 = muld(p0) ^ mul9(p1) ^ mule(p2) ^ mulb(p3); + q3 = mulb(p0) ^ muld(p1) ^ mul9(p2) ^ mule(p3); + skey[i] = (q0 << 24) | (q1 << 16) | (q2 << 8) | q3; + } + return num_rounds; +} + +static inline uint32_t +rotr(uint32_t x, int n) +{ + return (x << (32 - n)) | (x >> n); +} + +#define iSboxExt0(x) (iSsm0[x]) +#define iSboxExt1(x) (rotr(iSsm0[x], 8)) +#define iSboxExt2(x) (rotr(iSsm0[x], 16)) +#define iSboxExt3(x) (rotr(iSsm0[x], 24)) + +/* see bearssl.h */ +void +br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + uint32_t s0, s1, s2, s3; + uint32_t t0, t1, t2, t3; + unsigned u; + + buf = data; + s0 = br_dec32be(buf); + s1 = br_dec32be(buf + 4); + s2 = br_dec32be(buf + 8); + s3 = br_dec32be(buf + 12); + s0 ^= skey[(num_rounds << 2) + 0]; + s1 ^= skey[(num_rounds << 2) + 1]; + s2 ^= skey[(num_rounds << 2) + 2]; + s3 ^= skey[(num_rounds << 2) + 3]; + for (u = num_rounds - 1; u > 0; u --) { + uint32_t v0 = iSboxExt0(s0 >> 24) + ^ iSboxExt1((s3 >> 16) & 0xFF) + ^ iSboxExt2((s2 >> 8) & 0xFF) + ^ iSboxExt3(s1 & 0xFF); + uint32_t v1 = iSboxExt0(s1 >> 24) + ^ iSboxExt1((s0 >> 16) & 0xFF) + ^ iSboxExt2((s3 >> 8) & 0xFF) + ^ iSboxExt3(s2 & 0xFF); + uint32_t v2 = iSboxExt0(s2 >> 24) + ^ iSboxExt1((s1 >> 16) & 0xFF) + ^ iSboxExt2((s0 >> 8) & 0xFF) + ^ iSboxExt3(s3 & 0xFF); + uint32_t v3 = iSboxExt0(s3 >> 24) + ^ iSboxExt1((s2 >> 16) & 0xFF) + ^ iSboxExt2((s1 >> 8) & 0xFF) + ^ iSboxExt3(s0 & 0xFF); + s0 = v0; + s1 = v1; + s2 = v2; + s3 = v3; + s0 ^= skey[u << 2]; + s1 ^= skey[(u << 2) + 1]; + s2 ^= skey[(u << 2) + 2]; + s3 ^= skey[(u << 2) + 3]; + } + t0 = ((uint32_t)iS[s0 >> 24] << 24) + | ((uint32_t)iS[(s3 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s2 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s1 & 0xFF]; + t1 = ((uint32_t)iS[s1 >> 24] << 24) + | ((uint32_t)iS[(s0 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s3 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s2 & 0xFF]; + t2 = ((uint32_t)iS[s2 >> 24] << 24) + | ((uint32_t)iS[(s1 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s0 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s3 & 0xFF]; + t3 = ((uint32_t)iS[s3 >> 24] << 24) + | ((uint32_t)iS[(s2 >> 16) & 0xFF] << 16) + | ((uint32_t)iS[(s1 >> 8) & 0xFF] << 8) + | (uint32_t)iS[s0 & 0xFF]; + s0 = t0 ^ skey[0]; + s1 = t1 ^ skey[1]; + s2 = t2 ^ skey[2]; + s3 = t3 ^ skey[3]; + br_enc32be(buf, s0); + br_enc32be(buf + 4, s1); + br_enc32be(buf + 8, s2); + br_enc32be(buf + 12, s3); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c new file mode 100644 index 0000000000..9964dcb87a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_big_enc.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define S br_aes_S + +static const uint32_t Ssm0[] = { + 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, + 0xDE6F6FB1, 0x91C5C554, 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, + 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A, 0x8FCACA45, 0x1F82829D, + 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B, + 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, + 0xE4727296, 0x9BC0C05B, 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, + 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F, 0x6834345C, 0x51A5A5F4, + 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F, + 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, + 0x0A05050F, 0x2F9A9AB5, 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, + 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F, 0x1209091B, 0x1D83839E, + 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB, + 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, + 0x5E2F2F71, 0x13848497, 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, + 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED, 0xD46A6ABE, 0x8DCBCB46, + 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A, + 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, + 0x66333355, 0x11858594, 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, + 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3, 0xA25151F3, 0x5DA3A3FE, + 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504, + 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, + 0xFDF3F30E, 0xBFD2D26D, 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, + 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739, 0x93C4C457, 0x55A7A7F2, + 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395, + 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, + 0x3B9090AB, 0x0B888883, 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, + 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76, 0xDBE0E03B, 0x64323256, + 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4, + 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, + 0xD3E4E437, 0xF279798B, 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, + 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0, 0xD86C6CB4, 0xAC5656FA, + 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818, + 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, + 0x73B4B4C7, 0x97C6C651, 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, + 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85, 0xE0707090, 0x7C3E3E42, + 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12, + 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, + 0x3A1D1D27, 0x279E9EB9, 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, + 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7, 0x2D9B9BB6, 0x3C1E1E22, + 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A, + 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, + 0x844242C6, 0xD06868B8, 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, + 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A +}; + +static inline uint32_t +rotr(uint32_t x, int n) +{ + return (x << (32 - n)) | (x >> n); +} + +#define SboxExt0(x) (Ssm0[x]) +#define SboxExt1(x) (rotr(Ssm0[x], 8)) +#define SboxExt2(x) (rotr(Ssm0[x], 16)) +#define SboxExt3(x) (rotr(Ssm0[x], 24)) + + +/* see bearssl.h */ +void +br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + uint32_t s0, s1, s2, s3; + uint32_t t0, t1, t2, t3; + unsigned u; + + buf = data; + s0 = br_dec32be(buf); + s1 = br_dec32be(buf + 4); + s2 = br_dec32be(buf + 8); + s3 = br_dec32be(buf + 12); + s0 ^= skey[0]; + s1 ^= skey[1]; + s2 ^= skey[2]; + s3 ^= skey[3]; + for (u = 1; u < num_rounds; u ++) { + uint32_t v0, v1, v2, v3; + + v0 = SboxExt0(s0 >> 24) + ^ SboxExt1((s1 >> 16) & 0xFF) + ^ SboxExt2((s2 >> 8) & 0xFF) + ^ SboxExt3(s3 & 0xFF); + v1 = SboxExt0(s1 >> 24) + ^ SboxExt1((s2 >> 16) & 0xFF) + ^ SboxExt2((s3 >> 8) & 0xFF) + ^ SboxExt3(s0 & 0xFF); + v2 = SboxExt0(s2 >> 24) + ^ SboxExt1((s3 >> 16) & 0xFF) + ^ SboxExt2((s0 >> 8) & 0xFF) + ^ SboxExt3(s1 & 0xFF); + v3 = SboxExt0(s3 >> 24) + ^ SboxExt1((s0 >> 16) & 0xFF) + ^ SboxExt2((s1 >> 8) & 0xFF) + ^ SboxExt3(s2 & 0xFF); + s0 = v0; + s1 = v1; + s2 = v2; + s3 = v3; + s0 ^= skey[u << 2]; + s1 ^= skey[(u << 2) + 1]; + s2 ^= skey[(u << 2) + 2]; + s3 ^= skey[(u << 2) + 3]; + } + t0 = ((uint32_t)S[s0 >> 24] << 24) + | ((uint32_t)S[(s1 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s2 >> 8) & 0xFF] << 8) + | (uint32_t)S[s3 & 0xFF]; + t1 = ((uint32_t)S[s1 >> 24] << 24) + | ((uint32_t)S[(s2 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s3 >> 8) & 0xFF] << 8) + | (uint32_t)S[s0 & 0xFF]; + t2 = ((uint32_t)S[s2 >> 24] << 24) + | ((uint32_t)S[(s3 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s0 >> 8) & 0xFF] << 8) + | (uint32_t)S[s1 & 0xFF]; + t3 = ((uint32_t)S[s3 >> 24] << 24) + | ((uint32_t)S[(s0 >> 16) & 0xFF] << 16) + | ((uint32_t)S[(s1 >> 8) & 0xFF] << 8) + | (uint32_t)S[s2 & 0xFF]; + s0 = t0 ^ skey[num_rounds << 2]; + s1 = t1 ^ skey[(num_rounds << 2) + 1]; + s2 = t2 ^ skey[(num_rounds << 2) + 2]; + s3 = t3 ^ skey[(num_rounds << 2) + 3]; + br_enc32be(buf, s0); + br_enc32be(buf + 4, s1); + br_enc32be(buf + 8, s2); + br_enc32be(buf + 12, s3); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c new file mode 100644 index 0000000000..28c4ca63c0 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_common.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static const uint32_t Rcon[] PROGMEM = { + 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000, 0x20000000, + 0x40000000, 0x80000000, 0x1B000000, 0x36000000 +}; + +#define S br_aes_S + +/* see inner.h */ +const unsigned char br_aes_S[] = { + 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, + 0xFE, 0xD7, 0xAB, 0x76, 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, + 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0, 0xB7, 0xFD, 0x93, 0x26, + 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15, + 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, + 0xEB, 0x27, 0xB2, 0x75, 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, + 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84, 0x53, 0xD1, 0x00, 0xED, + 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF, + 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, + 0x50, 0x3C, 0x9F, 0xA8, 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, + 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2, 0xCD, 0x0C, 0x13, 0xEC, + 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73, + 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, + 0xDE, 0x5E, 0x0B, 0xDB, 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, + 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79, 0xE7, 0xC8, 0x37, 0x6D, + 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08, + 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, + 0x4B, 0xBD, 0x8B, 0x8A, 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, + 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E, 0xE1, 0xF8, 0x98, 0x11, + 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF, + 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, + 0xB0, 0x54, 0xBB, 0x16 +}; + +static uint32_t +SubWord(uint32_t x) +{ + return ((uint32_t)S[x >> 24] << 24) + | ((uint32_t)S[(x >> 16) & 0xFF] << 16) + | ((uint32_t)S[(x >> 8) & 0xFF] << 8) + | (uint32_t)S[x & 0xFF]; +} + +/* see inner.h */ +unsigned +br_aes_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + for (i = 0; i < nk; i ++) { + skey[i] = br_dec32be((const unsigned char *)key + (i << 2)); + } + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + uint32_t tmp; + + tmp = skey[i - 1]; + if (j == 0) { + tmp = (tmp << 8) | (tmp >> 24); + tmp = SubWord(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = SubWord(tmp); + } + skey[i] = skey[i - nk] ^ tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + return num_rounds; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c new file mode 100644 index 0000000000..5150f42c82 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct_bitslice_Sbox(uint32_t *q) +{ + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint32_t x0, x1, x2, x3, x4, x5, x6, x7; + uint32_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21; + uint32_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint32_t z10, z11, z12, z13, z14, z15, z16, z17; + uint32_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint32_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint32_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint32_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint32_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint32_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint32_t t60, t61, t62, t63, t64, t65, t66, t67; + uint32_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +/* see inner.h */ +void +br_aes_ct_ortho(uint32_t *q) +{ +#define SWAPN(cl, ch, s, x, y) do { \ + uint32_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint32_t)cl) | ((b & (uint32_t)cl) << (s)); \ + (y) = ((a & (uint32_t)ch) >> (s)) | (b & (uint32_t)ch); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x55555555, 0xAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x33333333, 0xCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F, 0xF0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + +static const unsigned char Rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 +}; + +static uint32_t +sub_word(uint32_t x) +{ + uint32_t q[8]; + int i; + + for (i = 0; i < 8; i ++) { + q[i] = x; + } + br_aes_ct_ortho(q); + br_aes_ct_bitslice_Sbox(q); + br_aes_ct_ortho(q); + return q[0]; +} + +/* see inner.h */ +unsigned +br_aes_ct_keysched(uint32_t *comp_skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + uint32_t tmp; + uint32_t skey[120]; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + tmp = 0; + for (i = 0; i < nk; i ++) { + tmp = br_dec32le((const unsigned char *)key + (i << 2)); + skey[(i << 1) + 0] = tmp; + skey[(i << 1) + 1] = tmp; + } + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + if (j == 0) { + tmp = (tmp << 24) | (tmp >> 8); + tmp = sub_word(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = sub_word(tmp); + } + tmp ^= skey[(i - nk) << 1]; + skey[(i << 1) + 0] = tmp; + skey[(i << 1) + 1] = tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + for (i = 0; i < nkf; i += 4) { + br_aes_ct_ortho(skey + (i << 1)); + } + for (i = 0, j = 0; i < nkf; i ++, j += 2) { + comp_skey[i] = (skey[j + 0] & 0x55555555) + | (skey[j + 1] & 0xAAAAAAAA); + } + return num_rounds; +} + +/* see inner.h */ +void +br_aes_ct_skey_expand(uint32_t *skey, + unsigned num_rounds, const uint32_t *comp_skey) +{ + unsigned u, v, n; + + n = (num_rounds + 1) << 2; + for (u = 0, v = 0; u < n; u ++, v += 2) { + uint32_t x, y; + + x = y = comp_skey[u]; + x &= 0x55555555; + skey[v + 0] = x | (x << 1); + y &= 0xAAAAAAAA; + skey[v + 1] = y | (y >> 1); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c new file mode 100644 index 0000000000..b4c6ff68e4 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64.c @@ -0,0 +1,398 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct64_bitslice_Sbox(uint64_t *q) +{ + /* + * This S-box implementation is a straightforward translation of + * the circuit described by Boyar and Peralta in "A new + * combinational logic minimization technique with applications + * to cryptology" (https://eprint.iacr.org/2009/191.pdf). + * + * Note that variables x* (input) and s* (output) are numbered + * in "reverse" order (x0 is the high bit, x7 is the low bit). + */ + + uint64_t x0, x1, x2, x3, x4, x5, x6, x7; + uint64_t y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint64_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint64_t y20, y21; + uint64_t z0, z1, z2, z3, z4, z5, z6, z7, z8, z9; + uint64_t z10, z11, z12, z13, z14, z15, z16, z17; + uint64_t t0, t1, t2, t3, t4, t5, t6, t7, t8, t9; + uint64_t t10, t11, t12, t13, t14, t15, t16, t17, t18, t19; + uint64_t t20, t21, t22, t23, t24, t25, t26, t27, t28, t29; + uint64_t t30, t31, t32, t33, t34, t35, t36, t37, t38, t39; + uint64_t t40, t41, t42, t43, t44, t45, t46, t47, t48, t49; + uint64_t t50, t51, t52, t53, t54, t55, t56, t57, t58, t59; + uint64_t t60, t61, t62, t63, t64, t65, t66, t67; + uint64_t s0, s1, s2, s3, s4, s5, s6, s7; + + x0 = q[7]; + x1 = q[6]; + x2 = q[5]; + x3 = q[4]; + x4 = q[3]; + x5 = q[2]; + x6 = q[1]; + x7 = q[0]; + + /* + * Top linear transformation. + */ + y14 = x3 ^ x5; + y13 = x0 ^ x6; + y9 = x0 ^ x3; + y8 = x0 ^ x5; + t0 = x1 ^ x2; + y1 = t0 ^ x7; + y4 = y1 ^ x3; + y12 = y13 ^ y14; + y2 = y1 ^ x0; + y5 = y1 ^ x6; + y3 = y5 ^ y8; + t1 = x4 ^ y12; + y15 = t1 ^ x5; + y20 = t1 ^ x1; + y6 = y15 ^ x7; + y10 = y15 ^ t0; + y11 = y20 ^ y9; + y7 = x7 ^ y11; + y17 = y10 ^ y11; + y19 = y10 ^ y8; + y16 = t0 ^ y11; + y21 = y13 ^ y16; + y18 = x0 ^ y16; + + /* + * Non-linear section. + */ + t2 = y12 & y15; + t3 = y3 & y6; + t4 = t3 ^ t2; + t5 = y4 & x7; + t6 = t5 ^ t2; + t7 = y13 & y16; + t8 = y5 & y1; + t9 = t8 ^ t7; + t10 = y2 & y7; + t11 = t10 ^ t7; + t12 = y9 & y11; + t13 = y14 & y17; + t14 = t13 ^ t12; + t15 = y8 & y10; + t16 = t15 ^ t12; + t17 = t4 ^ t14; + t18 = t6 ^ t16; + t19 = t9 ^ t14; + t20 = t11 ^ t16; + t21 = t17 ^ y20; + t22 = t18 ^ y19; + t23 = t19 ^ y21; + t24 = t20 ^ y18; + + t25 = t21 ^ t22; + t26 = t21 & t23; + t27 = t24 ^ t26; + t28 = t25 & t27; + t29 = t28 ^ t22; + t30 = t23 ^ t24; + t31 = t22 ^ t26; + t32 = t31 & t30; + t33 = t32 ^ t24; + t34 = t23 ^ t33; + t35 = t27 ^ t33; + t36 = t24 & t35; + t37 = t36 ^ t34; + t38 = t27 ^ t36; + t39 = t29 & t38; + t40 = t25 ^ t39; + + t41 = t40 ^ t37; + t42 = t29 ^ t33; + t43 = t29 ^ t40; + t44 = t33 ^ t37; + t45 = t42 ^ t41; + z0 = t44 & y15; + z1 = t37 & y6; + z2 = t33 & x7; + z3 = t43 & y16; + z4 = t40 & y1; + z5 = t29 & y7; + z6 = t42 & y11; + z7 = t45 & y17; + z8 = t41 & y10; + z9 = t44 & y12; + z10 = t37 & y3; + z11 = t33 & y4; + z12 = t43 & y13; + z13 = t40 & y5; + z14 = t29 & y2; + z15 = t42 & y9; + z16 = t45 & y14; + z17 = t41 & y8; + + /* + * Bottom linear transformation. + */ + t46 = z15 ^ z16; + t47 = z10 ^ z11; + t48 = z5 ^ z13; + t49 = z9 ^ z10; + t50 = z2 ^ z12; + t51 = z2 ^ z5; + t52 = z7 ^ z8; + t53 = z0 ^ z3; + t54 = z6 ^ z7; + t55 = z16 ^ z17; + t56 = z12 ^ t48; + t57 = t50 ^ t53; + t58 = z4 ^ t46; + t59 = z3 ^ t54; + t60 = t46 ^ t57; + t61 = z14 ^ t57; + t62 = t52 ^ t58; + t63 = t49 ^ t58; + t64 = z4 ^ t59; + t65 = t61 ^ t62; + t66 = z1 ^ t63; + s0 = t59 ^ t63; + s6 = t56 ^ ~t62; + s7 = t48 ^ ~t60; + t67 = t64 ^ t65; + s3 = t53 ^ t66; + s4 = t51 ^ t66; + s5 = t47 ^ t65; + s1 = t64 ^ ~s3; + s2 = t55 ^ ~t67; + + q[7] = s0; + q[6] = s1; + q[5] = s2; + q[4] = s3; + q[3] = s4; + q[2] = s5; + q[1] = s6; + q[0] = s7; +} + +/* see inner.h */ +void +br_aes_ct64_ortho(uint64_t *q) +{ +#define SWAPN(cl, ch, s, x, y) do { \ + uint64_t a, b; \ + a = (x); \ + b = (y); \ + (x) = (a & (uint64_t)cl) | ((b & (uint64_t)cl) << (s)); \ + (y) = ((a & (uint64_t)ch) >> (s)) | (b & (uint64_t)ch); \ + } while (0) + +#define SWAP2(x, y) SWAPN(0x5555555555555555, 0xAAAAAAAAAAAAAAAA, 1, x, y) +#define SWAP4(x, y) SWAPN(0x3333333333333333, 0xCCCCCCCCCCCCCCCC, 2, x, y) +#define SWAP8(x, y) SWAPN(0x0F0F0F0F0F0F0F0F, 0xF0F0F0F0F0F0F0F0, 4, x, y) + + SWAP2(q[0], q[1]); + SWAP2(q[2], q[3]); + SWAP2(q[4], q[5]); + SWAP2(q[6], q[7]); + + SWAP4(q[0], q[2]); + SWAP4(q[1], q[3]); + SWAP4(q[4], q[6]); + SWAP4(q[5], q[7]); + + SWAP8(q[0], q[4]); + SWAP8(q[1], q[5]); + SWAP8(q[2], q[6]); + SWAP8(q[3], q[7]); +} + +/* see inner.h */ +void +br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w) +{ + uint64_t x0, x1, x2, x3; + + x0 = w[0]; + x1 = w[1]; + x2 = w[2]; + x3 = w[3]; + x0 |= (x0 << 16); + x1 |= (x1 << 16); + x2 |= (x2 << 16); + x3 |= (x3 << 16); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + x0 |= (x0 << 8); + x1 |= (x1 << 8); + x2 |= (x2 << 8); + x3 |= (x3 << 8); + x0 &= (uint64_t)0x00FF00FF00FF00FF; + x1 &= (uint64_t)0x00FF00FF00FF00FF; + x2 &= (uint64_t)0x00FF00FF00FF00FF; + x3 &= (uint64_t)0x00FF00FF00FF00FF; + *q0 = x0 | (x2 << 8); + *q1 = x1 | (x3 << 8); +} + +/* see inner.h */ +void +br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1) +{ + uint64_t x0, x1, x2, x3; + + x0 = q0 & (uint64_t)0x00FF00FF00FF00FF; + x1 = q1 & (uint64_t)0x00FF00FF00FF00FF; + x2 = (q0 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x3 = (q1 >> 8) & (uint64_t)0x00FF00FF00FF00FF; + x0 |= (x0 >> 8); + x1 |= (x1 >> 8); + x2 |= (x2 >> 8); + x3 |= (x3 >> 8); + x0 &= (uint64_t)0x0000FFFF0000FFFF; + x1 &= (uint64_t)0x0000FFFF0000FFFF; + x2 &= (uint64_t)0x0000FFFF0000FFFF; + x3 &= (uint64_t)0x0000FFFF0000FFFF; + w[0] = (uint32_t)x0 | (uint32_t)(x0 >> 16); + w[1] = (uint32_t)x1 | (uint32_t)(x1 >> 16); + w[2] = (uint32_t)x2 | (uint32_t)(x2 >> 16); + w[3] = (uint32_t)x3 | (uint32_t)(x3 >> 16); +} + +static const unsigned char Rcon[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 +}; + +static uint32_t +sub_word(uint32_t x) +{ + uint64_t q[8]; + + memset(q, 0, sizeof q); + q[0] = x; + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_Sbox(q); + br_aes_ct64_ortho(q); + return (uint32_t)q[0]; +} + +/* see inner.h */ +unsigned +br_aes_ct64_keysched(uint64_t *comp_skey, const void *key, size_t key_len) +{ + unsigned num_rounds; + int i, j, k, nk, nkf; + uint32_t tmp; + uint32_t skey[60]; + + switch (key_len) { + case 16: + num_rounds = 10; + break; + case 24: + num_rounds = 12; + break; + case 32: + num_rounds = 14; + break; + default: + /* abort(); */ + return 0; + } + nk = (int)(key_len >> 2); + nkf = (int)((num_rounds + 1) << 2); + br_range_dec32le(skey, (key_len >> 2), key); + tmp = skey[(key_len >> 2) - 1]; + for (i = nk, j = 0, k = 0; i < nkf; i ++) { + if (j == 0) { + tmp = (tmp << 24) | (tmp >> 8); + tmp = sub_word(tmp) ^ Rcon[k]; + } else if (nk > 6 && j == 4) { + tmp = sub_word(tmp); + } + tmp ^= skey[i - nk]; + skey[i] = tmp; + if (++ j == nk) { + j = 0; + k ++; + } + } + + for (i = 0, j = 0; i < nkf; i += 4, j += 2) { + uint64_t q[8]; + + br_aes_ct64_interleave_in(&q[0], &q[4], skey + i); + q[1] = q[0]; + q[2] = q[0]; + q[3] = q[0]; + q[5] = q[4]; + q[6] = q[4]; + q[7] = q[4]; + br_aes_ct64_ortho(q); + comp_skey[j + 0] = + (q[0] & (uint64_t)0x1111111111111111) + | (q[1] & (uint64_t)0x2222222222222222) + | (q[2] & (uint64_t)0x4444444444444444) + | (q[3] & (uint64_t)0x8888888888888888); + comp_skey[j + 1] = + (q[4] & (uint64_t)0x1111111111111111) + | (q[5] & (uint64_t)0x2222222222222222) + | (q[6] & (uint64_t)0x4444444444444444) + | (q[7] & (uint64_t)0x8888888888888888); + } + return num_rounds; +} + +/* see inner.h */ +void +br_aes_ct64_skey_expand(uint64_t *skey, + unsigned num_rounds, const uint64_t *comp_skey) +{ + unsigned u, v, n; + + n = (num_rounds + 1) << 1; + for (u = 0, v = 0; u < n; u ++, v += 4) { + uint64_t x0, x1, x2, x3; + + x0 = x1 = x2 = x3 = comp_skey[u]; + x0 &= (uint64_t)0x1111111111111111; + x1 &= (uint64_t)0x2222222222222222; + x2 &= (uint64_t)0x4444444444444444; + x3 &= (uint64_t)0x8888888888888888; + x1 >>= 1; + x2 >>= 2; + x3 >>= 3; + skey[v + 0] = (x0 << 4) - x0; + skey[v + 1] = (x1 << 4) - x1; + skey[v + 2] = (x2 << 4) - x2; + skey[v + 3] = (x3 << 4) - x3; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c new file mode 100644 index 0000000000..091a98c3bc --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcdec.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_cbcdec_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf; + uint64_t sk_exp[120]; + uint32_t ivw[4]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 4, iv); + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w1[16], w2[16]; + int i; + + if (len >= 64) { + br_range_dec32le(w1, 16, buf); + } else { + br_range_dec32le(w1, len >> 2, buf); + } + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w1 + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_decrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w2 + (i << 2), q[i], q[i + 4]); + } + for (i = 0; i < 4; i ++) { + w2[i] ^= ivw[i]; + } + if (len >= 64) { + for (i = 4; i < 16; i ++) { + w2[i] ^= w1[i - 4]; + } + memcpy(ivw, w1 + 12, sizeof ivw); + br_range_enc32le(buf, w2, 16); + } else { + int j; + + j = (int)(len >> 2); + for (i = 4; i < j; i ++) { + w2[i] ^= w1[i - 4]; + } + memcpy(ivw, w1 + j - 4, sizeof ivw); + br_range_enc32le(buf, w2, j); + break; + } + buf += 64; + len -= 64; + } + br_range_enc32le(iv, ivw, 4); +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable PROGMEM = { + sizeof(br_aes_ct64_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_ct64_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_ct64_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c new file mode 100644 index 0000000000..3ab6e3ea96 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_cbcenc.c @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_cbcenc_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf; + uint64_t sk_exp[120]; + uint32_t ivw[4]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 4, iv); + buf = data; + while (len > 0) { + uint32_t w[4]; + uint64_t q[8]; + + w[0] = ivw[0] ^ br_dec32le(buf); + w[1] = ivw[1] ^ br_dec32le(buf + 4); + w[2] = ivw[2] ^ br_dec32le(buf + 8); + w[3] = ivw[3] ^ br_dec32le(buf + 12); + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + memcpy(ivw, w, sizeof w); + br_enc32le(buf, w[0]); + br_enc32le(buf + 4, w[1]); + br_enc32le(buf + 8, w[2]); + br_enc32le(buf + 12, w[3]); + buf += 16; + len -= 16; + } + br_range_enc32le(iv, ivw, 4); +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable PROGMEM = { + sizeof(br_aes_ct64_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_ct64_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_ct64_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c new file mode 100644 index 0000000000..9adac85d44 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctr.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_ctr_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t ivw[16]; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + br_range_dec32le(ivw, 3, iv); + memcpy(ivw + 4, ivw, 3 * sizeof(uint32_t)); + memcpy(ivw + 8, ivw, 3 * sizeof(uint32_t)); + memcpy(ivw + 12, ivw, 3 * sizeof(uint32_t)); + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w[16]; + unsigned char tmp[64]; + int i; + + /* + * TODO: see if we can save on the first br_aes_ct64_ortho() + * call, since iv0/iv1/iv2 are constant for the whole run. + */ + memcpy(w, ivw, sizeof ivw); + w[3] = br_swap32(cc); + w[7] = br_swap32(cc + 1); + w[11] = br_swap32(cc + 2); + w[15] = br_swap32(cc + 3); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w + (i << 2), q[i], q[i + 4]); + } + br_range_enc32le(tmp, w, 16); + if (len <= 64) { + xorbuf(buf, tmp, len); + cc += (uint32_t)len >> 4; + break; + } + xorbuf(buf, tmp, 64); + buf += 64; + len -= 64; + cc += 4; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_ct64_ctr_vtable PROGMEM = { + sizeof(br_aes_ct64_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_ct64_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_ct64_ctr_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c new file mode 100644 index 0000000000..b5e52e420a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_ctrcbc.c @@ -0,0 +1,433 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct64_ctrcbc_vtable; + ctx->num_rounds = br_aes_ct64_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + buf = data; + while (len > 0) { + uint64_t q[8]; + uint32_t w[16]; + unsigned char tmp[64]; + int i, j; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + j = (len >= 64) ? 16 : (int)(len >> 2); + for (i = 0; i < j; i += 4) { + uint32_t carry; + + w[i + 0] = br_swap32(iv0); + w[i + 1] = br_swap32(iv1); + w[i + 2] = br_swap32(iv2); + w[i + 3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + } + memset(w + i, 0, (16 - i) * sizeof(uint32_t)); + + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_in( + &q[i], &q[i + 4], w + (i << 2)); + } + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + for (i = 0; i < 4; i ++) { + br_aes_ct64_interleave_out( + w + (i << 2), q[i], q[i + 4]); + } + + br_range_enc32le(tmp, w, 16); + if (len <= 64) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 64); + buf += 64; + len -= 64; + } + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + uint32_t cm0, cm1, cm2, cm3; + uint64_t q[8]; + uint64_t sk_exp[120]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[4]; + + w[0] = cm0 ^ br_dec32le(buf + 0); + w[1] = cm1 ^ br_dec32le(buf + 4); + w[2] = cm2 ^ br_dec32le(buf + 8); + w[3] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + + cm0 = w[0]; + cm1 = w[1]; + cm2 = w[2]; + cm3 = w[3]; + buf += 16; + len -= 16; + } + + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + /* + * When encrypting, the CBC-MAC processing must be lagging by + * one block, since it operates on the encrypted values, so + * it must wait for that encryption to complete. + */ + + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint64_t sk_exp[120]; + uint64_t q[8]; + int first_iter; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + first_iter = 1; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[8], carry; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + w[0] = br_swap32(iv0); + w[1] = br_swap32(iv1); + w[2] = br_swap32(iv2); + w[3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The block for CBC-MAC. + */ + w[4] = cm0; + w[5] = cm1; + w[6] = cm2; + w[7] = cm3; + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_interleave_in(&q[1], &q[5], w + 4); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + br_aes_ct64_interleave_out(w + 4, q[1], q[5]); + + /* + * We do the XOR with the plaintext in 32-bit registers, + * so that the value are available for CBC-MAC processing + * as well. + */ + w[0] ^= br_dec32le(buf + 0); + w[1] ^= br_dec32le(buf + 4); + w[2] ^= br_dec32le(buf + 8); + w[3] ^= br_dec32le(buf + 12); + br_enc32le(buf + 0, w[0]); + br_enc32le(buf + 4, w[1]); + br_enc32le(buf + 8, w[2]); + br_enc32le(buf + 12, w[3]); + + buf += 16; + len -= 16; + + /* + * We set the cm* values to the block to encrypt in the + * next iteration. + */ + if (first_iter) { + first_iter = 0; + cm0 ^= w[0]; + cm1 ^= w[1]; + cm2 ^= w[2]; + cm3 ^= w[3]; + } else { + cm0 = w[0] ^ w[4]; + cm1 = w[1] ^ w[5]; + cm2 = w[2] ^ w[6]; + cm3 = w[3] ^ w[7]; + } + + /* + * If this was the last iteration, then compute the + * extra block encryption to complete CBC-MAC. + */ + if (len == 0) { + w[0] = cm0; + w[1] = cm1; + w[2] = cm2; + w[3] = cm3; + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt( + ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + cm0 = w[0]; + cm1 = w[1]; + cm2 = w[2]; + cm3 = w[3]; + break; + } + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint64_t sk_exp[120]; + uint64_t q[8]; + + br_aes_ct64_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + memset(q, 0, sizeof q); + while (len > 0) { + uint32_t w[8], carry; + unsigned char tmp[16]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + w[0] = br_swap32(iv0); + w[1] = br_swap32(iv1); + w[2] = br_swap32(iv2); + w[3] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The block for CBC-MAC. + */ + w[4] = cm0 ^ br_dec32le(buf + 0); + w[5] = cm1 ^ br_dec32le(buf + 4); + w[6] = cm2 ^ br_dec32le(buf + 8); + w[7] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct64_interleave_in(&q[0], &q[4], w); + br_aes_ct64_interleave_in(&q[1], &q[5], w + 4); + br_aes_ct64_ortho(q); + br_aes_ct64_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct64_ortho(q); + br_aes_ct64_interleave_out(w, q[0], q[4]); + br_aes_ct64_interleave_out(w + 4, q[1], q[5]); + + br_enc32le(tmp + 0, w[0]); + br_enc32le(tmp + 4, w[1]); + br_enc32le(tmp + 8, w[2]); + br_enc32le(tmp + 12, w[3]); + xorbuf(buf, tmp, 16); + cm0 = w[4]; + cm1 = w[5]; + cm2 = w[6]; + cm3 = w[7]; + buf += 16; + len -= 16; + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_ct64_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_ct64_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct64_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct64_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_ct64_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_ct64_ctrcbc_mac +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c new file mode 100644 index 0000000000..687c90a8cb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_dec.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct64_bitslice_invSbox(uint64_t *q) +{ + /* + * See br_aes_ct_bitslice_invSbox(). This is the natural extension + * to 64-bit registers. + */ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; + + br_aes_ct64_bitslice_Sbox(q); + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; +} + +static void +add_round_key(uint64_t *q, const uint64_t *sk) +{ + int i; + + for (i = 0; i < 8; i ++) { + q[i] ^= sk[i]; + } +} + +static void +inv_shift_rows(uint64_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x000000000FFF0000) << 4) + | ((x & (uint64_t)0x00000000F0000000) >> 12) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000F000000000000) << 12) + | ((x & (uint64_t)0xFFF0000000000000) >> 4); + } +} + +static inline uint64_t +rotr32(uint64_t x) +{ + return (x << 32) | (x >> 32); +} + +static void +inv_mix_columns(uint64_t *q) +{ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr32(q0 ^ q5 ^ q6 ^ r0 ^ r5); + q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6); + q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr32(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7); + q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr32(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7); + q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6); + q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr32(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7); + q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr32(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7); + q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr32(q4 ^ q5 ^ q7 ^ r4 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct64_bitslice_decrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q) +{ + unsigned u; + + add_round_key(q, skey + (num_rounds << 3)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(q); + br_aes_ct64_bitslice_invSbox(q); + add_round_key(q, skey + (u << 3)); + inv_mix_columns(q); + } + inv_shift_rows(q); + br_aes_ct64_bitslice_invSbox(q); + add_round_key(q, skey); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c new file mode 100644 index 0000000000..7057176016 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct64_enc.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline void +add_round_key(uint64_t *q, const uint64_t *sk) +{ + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void +shift_rows(uint64_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint64_t x; + + x = q[i]; + q[i] = (x & (uint64_t)0x000000000000FFFF) + | ((x & (uint64_t)0x00000000FFF00000) >> 4) + | ((x & (uint64_t)0x00000000000F0000) << 12) + | ((x & (uint64_t)0x0000FF0000000000) >> 8) + | ((x & (uint64_t)0x000000FF00000000) << 8) + | ((x & (uint64_t)0xF000000000000000) >> 12) + | ((x & (uint64_t)0x0FFF000000000000) << 4); + } +} + +static inline uint64_t +rotr32(uint64_t x) +{ + return (x << 32) | (x >> 32); +} + +static inline void +mix_columns(uint64_t *q) +{ + uint64_t q0, q1, q2, q3, q4, q5, q6, q7; + uint64_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 16) | (q0 << 48); + r1 = (q1 >> 16) | (q1 << 48); + r2 = (q2 >> 16) | (q2 << 48); + r3 = (q3 >> 16) | (q3 << 48); + r4 = (q4 >> 16) | (q4 << 48); + r5 = (q5 >> 16) | (q5 << 48); + r6 = (q6 >> 16) | (q6 << 48); + r7 = (q7 >> 16) | (q7 << 48); + + q[0] = q7 ^ r7 ^ r0 ^ rotr32(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr32(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr32(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr32(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr32(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr32(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr32(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr32(q7 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct64_bitslice_encrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q) +{ + unsigned u; + + add_round_key(q, skey); + for (u = 1; u < num_rounds; u ++) { + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, skey + (u << 3)); + } + br_aes_ct64_bitslice_Sbox(q); + shift_rows(q); + add_round_key(q, skey + (num_rounds << 3)); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c new file mode 100644 index 0000000000..7bc258ae64 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcdec.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_cbcdec_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + iv3 = br_dec32le(ivbuf + 12); + buf = data; + while (len > 0) { + uint32_t q[8], sq[8]; + + q[0] = br_dec32le(buf); + q[2] = br_dec32le(buf + 4); + q[4] = br_dec32le(buf + 8); + q[6] = br_dec32le(buf + 12); + if (len >= 32) { + q[1] = br_dec32le(buf + 16); + q[3] = br_dec32le(buf + 20); + q[5] = br_dec32le(buf + 24); + q[7] = br_dec32le(buf + 28); + } else { + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + } + memcpy(sq, q, sizeof q); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_decrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + br_enc32le(buf, q[0] ^ iv0); + br_enc32le(buf + 4, q[2] ^ iv1); + br_enc32le(buf + 8, q[4] ^ iv2); + br_enc32le(buf + 12, q[6] ^ iv3); + if (len < 32) { + iv0 = sq[0]; + iv1 = sq[2]; + iv2 = sq[4]; + iv3 = sq[6]; + break; + } + br_enc32le(buf + 16, q[1] ^ sq[0]); + br_enc32le(buf + 20, q[3] ^ sq[2]); + br_enc32le(buf + 24, q[5] ^ sq[4]); + br_enc32le(buf + 28, q[7] ^ sq[6]); + iv0 = sq[1]; + iv1 = sq[3]; + iv2 = sq[5]; + iv3 = sq[7]; + buf += 32; + len -= 32; + } + br_enc32le(ivbuf, iv0); + br_enc32le(ivbuf + 4, iv1); + br_enc32le(ivbuf + 8, iv2); + br_enc32le(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_ct_cbcdec_vtable PROGMEM = { + sizeof(br_aes_ct_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_ct_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_ct_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c new file mode 100644 index 0000000000..07fd5725ab --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_cbcenc.c @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_cbcenc_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t q[8]; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + iv3 = br_dec32le(ivbuf + 12); + buf = data; + while (len > 0) { + q[0] = iv0 ^ br_dec32le(buf); + q[2] = iv1 ^ br_dec32le(buf + 4); + q[4] = iv2 ^ br_dec32le(buf + 8); + q[6] = iv3 ^ br_dec32le(buf + 12); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + iv0 = q[0]; + iv1 = q[2]; + iv2 = q[4]; + iv3 = q[6]; + br_enc32le(buf, iv0); + br_enc32le(buf + 4, iv1); + br_enc32le(buf + 8, iv2); + br_enc32le(buf + 12, iv3); + buf += 16; + len -= 16; + } + br_enc32le(ivbuf, iv0); + br_enc32le(ivbuf + 4, iv1); + br_enc32le(ivbuf + 8, iv2); + br_enc32le(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_ct_cbcenc_vtable PROGMEM = { + sizeof(br_aes_ct_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_ct_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_ct_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c new file mode 100644 index 0000000000..dfa882955b --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctr.c @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_ctr_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + const unsigned char *ivbuf; + uint32_t iv0, iv1, iv2; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + iv0 = br_dec32le(ivbuf); + iv1 = br_dec32le(ivbuf + 4); + iv2 = br_dec32le(ivbuf + 8); + buf = data; + while (len > 0) { + uint32_t q[8]; + unsigned char tmp[32]; + + /* + * TODO: see if we can save on the first br_aes_ct_ortho() + * call, since iv0/iv1/iv2 are constant for the whole run. + */ + q[0] = q[1] = iv0; + q[2] = q[3] = iv1; + q[4] = q[5] = iv2; + q[6] = br_swap32(cc); + q[7] = br_swap32(cc + 1); + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + br_enc32le(tmp, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + br_enc32le(tmp + 16, q[1]); + br_enc32le(tmp + 20, q[3]); + br_enc32le(tmp + 24, q[5]); + br_enc32le(tmp + 28, q[7]); + + if (len <= 32) { + xorbuf(buf, tmp, len); + cc ++; + if (len > 16) { + cc ++; + } + break; + } + xorbuf(buf, tmp, 32); + buf += 32; + len -= 32; + cc += 2; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_ct_ctr_vtable PROGMEM = { + sizeof(br_aes_ct_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_ct_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_ct_ctr_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c new file mode 100644 index 0000000000..ec451c6afe --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_ctrcbc.c @@ -0,0 +1,422 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_ct_ctrcbc_vtable; + ctx->num_rounds = br_aes_ct_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + buf = data; + while (len > 0) { + uint32_t q[8], carry; + unsigned char tmp[32]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + q[1] = br_swap32(iv0); + q[3] = br_swap32(iv1); + q[5] = br_swap32(iv2); + q[7] = br_swap32(iv3); + if (len > 16) { + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + } + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + br_enc32le(tmp, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + br_enc32le(tmp + 16, q[1]); + br_enc32le(tmp + 20, q[3]); + br_enc32le(tmp + 24, q[5]); + br_enc32le(tmp + 28, q[7]); + + if (len <= 32) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 32); + buf += 32; + len -= 32; + } + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + uint32_t cm0, cm1, cm2, cm3; + uint32_t q[8]; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + buf = data; + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + q[1] = 0; + q[3] = 0; + q[5] = 0; + q[7] = 0; + + while (len > 0) { + q[0] = cm0 ^ br_dec32le(buf + 0); + q[2] = cm1 ^ br_dec32le(buf + 4); + q[4] = cm2 ^ br_dec32le(buf + 8); + q[6] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + cm0 = q[0]; + cm1 = q[2]; + cm2 = q[4]; + cm3 = q[6]; + buf += 16; + len -= 16; + } + + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + /* + * When encrypting, the CBC-MAC processing must be lagging by + * one block, since it operates on the encrypted values, so + * it must wait for that encryption to complete. + */ + + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint32_t sk_exp[120]; + int first_iter; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + first_iter = 1; + while (len > 0) { + uint32_t q[8], carry; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The odd values are used for CBC-MAC. + */ + q[1] = cm0; + q[3] = cm1; + q[5] = cm2; + q[7] = cm3; + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + /* + * We do the XOR with the plaintext in 32-bit registers, + * so that the value are available for CBC-MAC processing + * as well. + */ + q[0] ^= br_dec32le(buf + 0); + q[2] ^= br_dec32le(buf + 4); + q[4] ^= br_dec32le(buf + 8); + q[6] ^= br_dec32le(buf + 12); + br_enc32le(buf + 0, q[0]); + br_enc32le(buf + 4, q[2]); + br_enc32le(buf + 8, q[4]); + br_enc32le(buf + 12, q[6]); + + buf += 16; + len -= 16; + + /* + * We set the cm* values to the block to encrypt in the + * next iteration. + */ + if (first_iter) { + first_iter = 0; + cm0 ^= q[0]; + cm1 ^= q[2]; + cm2 ^= q[4]; + cm3 ^= q[6]; + } else { + cm0 = q[0] ^ q[1]; + cm1 = q[2] ^ q[3]; + cm2 = q[4] ^ q[5]; + cm3 = q[6] ^ q[7]; + } + + /* + * If this was the last iteration, then compute the + * extra block encryption to complete CBC-MAC. + */ + if (len == 0) { + q[0] = cm0; + q[2] = cm1; + q[4] = cm2; + q[6] = cm3; + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + cm0 = q[0]; + cm1 = q[2]; + cm2 = q[4]; + cm3 = q[6]; + break; + } + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +void +br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + unsigned char *buf; + unsigned char *ivbuf; + uint32_t iv0, iv1, iv2, iv3; + uint32_t cm0, cm1, cm2, cm3; + uint32_t sk_exp[120]; + + br_aes_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + + /* + * We keep the counter as four 32-bit values, with big-endian + * convention, because that's what is expected for purposes of + * incrementing the counter value. + */ + ivbuf = ctr; + iv0 = br_dec32be(ivbuf + 0); + iv1 = br_dec32be(ivbuf + 4); + iv2 = br_dec32be(ivbuf + 8); + iv3 = br_dec32be(ivbuf + 12); + + /* + * The current CBC-MAC value is kept in little-endian convention. + */ + cm0 = br_dec32le((unsigned char *)cbcmac + 0); + cm1 = br_dec32le((unsigned char *)cbcmac + 4); + cm2 = br_dec32le((unsigned char *)cbcmac + 8); + cm3 = br_dec32le((unsigned char *)cbcmac + 12); + + buf = data; + while (len > 0) { + uint32_t q[8], carry; + unsigned char tmp[16]; + + /* + * The bitslice implementation expects values in + * little-endian convention, so we have to byteswap them. + */ + q[0] = br_swap32(iv0); + q[2] = br_swap32(iv1); + q[4] = br_swap32(iv2); + q[6] = br_swap32(iv3); + iv3 ++; + carry = ~(iv3 | -iv3) >> 31; + iv2 += carry; + carry &= -(~(iv2 | -iv2) >> 31); + iv1 += carry; + carry &= -(~(iv1 | -iv1) >> 31); + iv0 += carry; + + /* + * The odd values are used for CBC-MAC. + */ + q[1] = cm0 ^ br_dec32le(buf + 0); + q[3] = cm1 ^ br_dec32le(buf + 4); + q[5] = cm2 ^ br_dec32le(buf + 8); + q[7] = cm3 ^ br_dec32le(buf + 12); + + br_aes_ct_ortho(q); + br_aes_ct_bitslice_encrypt(ctx->num_rounds, sk_exp, q); + br_aes_ct_ortho(q); + + br_enc32le(tmp + 0, q[0]); + br_enc32le(tmp + 4, q[2]); + br_enc32le(tmp + 8, q[4]); + br_enc32le(tmp + 12, q[6]); + xorbuf(buf, tmp, 16); + cm0 = q[1]; + cm1 = q[3]; + cm2 = q[5]; + cm3 = q[7]; + buf += 16; + len -= 16; + } + + br_enc32be(ivbuf + 0, iv0); + br_enc32be(ivbuf + 4, iv1); + br_enc32be(ivbuf + 8, iv2); + br_enc32be(ivbuf + 12, iv3); + br_enc32le((unsigned char *)cbcmac + 0, cm0); + br_enc32le((unsigned char *)cbcmac + 4, cm1); + br_enc32le((unsigned char *)cbcmac + 8, cm2); + br_enc32le((unsigned char *)cbcmac + 12, cm3); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_ct_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_ct_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_ct_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_ct_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_ct_ctrcbc_mac +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c new file mode 100644 index 0000000000..1ba86b7e0c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_dec.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_aes_ct_bitslice_invSbox(uint32_t *q) +{ + /* + * AES S-box is: + * S(x) = A(I(x)) ^ 0x63 + * where I() is inversion in GF(256), and A() is a linear + * transform (0 is formally defined to be its own inverse). + * Since inversion is an involution, the inverse S-box can be + * computed from the S-box as: + * iS(x) = B(S(B(x ^ 0x63)) ^ 0x63) + * where B() is the inverse of A(). Indeed, for any y in GF(256): + * iS(S(y)) = B(A(I(B(A(I(y)) ^ 0x63 ^ 0x63))) ^ 0x63 ^ 0x63) = y + * + * Note: we reuse the implementation of the forward S-box, + * instead of duplicating it here, so that total code size is + * lower. By merging the B() transforms into the S-box circuit + * we could make faster CBC decryption, but CBC decryption is + * already quite faster than CBC encryption because we can + * process two blocks in parallel. + */ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; + + br_aes_ct_bitslice_Sbox(q); + + q0 = ~q[0]; + q1 = ~q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = ~q[5]; + q6 = ~q[6]; + q7 = q[7]; + q[7] = q1 ^ q4 ^ q6; + q[6] = q0 ^ q3 ^ q5; + q[5] = q7 ^ q2 ^ q4; + q[4] = q6 ^ q1 ^ q3; + q[3] = q5 ^ q0 ^ q2; + q[2] = q4 ^ q7 ^ q1; + q[1] = q3 ^ q6 ^ q0; + q[0] = q2 ^ q5 ^ q7; +} + +static void +add_round_key(uint32_t *q, const uint32_t *sk) +{ + int i; + + for (i = 0; i < 8; i ++) { + q[i] ^= sk[i]; + } +} + +static void +inv_shift_rows(uint32_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x00003F00) << 2) | ((x & 0x0000C000) >> 6) + | ((x & 0x000F0000) << 4) | ((x & 0x00F00000) >> 4) + | ((x & 0x03000000) << 6) | ((x & 0xFC000000) >> 2); + } +} + +static inline uint32_t +rotr16(uint32_t x) +{ + return (x << 16) | (x >> 16); +} + +static void +inv_mix_columns(uint32_t *q) +{ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q5 ^ q6 ^ q7 ^ r0 ^ r5 ^ r7 ^ rotr16(q0 ^ q5 ^ q6 ^ r0 ^ r5); + q[1] = q0 ^ q5 ^ r0 ^ r1 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q5 ^ q7 ^ r1 ^ r5 ^ r6); + q[2] = q0 ^ q1 ^ q6 ^ r1 ^ r2 ^ r6 ^ r7 ^ rotr16(q0 ^ q2 ^ q6 ^ r2 ^ r6 ^ r7); + q[3] = q0 ^ q1 ^ q2 ^ q5 ^ q6 ^ r0 ^ r2 ^ r3 ^ r5 ^ rotr16(q0 ^ q1 ^ q3 ^ q5 ^ q6 ^ q7 ^ r0 ^ r3 ^ r5 ^ r7); + q[4] = q1 ^ q2 ^ q3 ^ q5 ^ r1 ^ r3 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q1 ^ q2 ^ q4 ^ q5 ^ q7 ^ r1 ^ r4 ^ r5 ^ r6); + q[5] = q2 ^ q3 ^ q4 ^ q6 ^ r2 ^ r4 ^ r5 ^ r6 ^ r7 ^ rotr16(q2 ^ q3 ^ q5 ^ q6 ^ r2 ^ r5 ^ r6 ^ r7); + q[6] = q3 ^ q4 ^ q5 ^ q7 ^ r3 ^ r5 ^ r6 ^ r7 ^ rotr16(q3 ^ q4 ^ q6 ^ q7 ^ r3 ^ r6 ^ r7); + q[7] = q4 ^ q5 ^ q6 ^ r4 ^ r6 ^ r7 ^ rotr16(q4 ^ q5 ^ q7 ^ r4 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct_bitslice_decrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q) +{ + unsigned u; + + add_round_key(q, skey + (num_rounds << 3)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(q); + br_aes_ct_bitslice_invSbox(q); + add_round_key(q, skey + (u << 3)); + inv_mix_columns(q); + } + inv_shift_rows(q); + br_aes_ct_bitslice_invSbox(q); + add_round_key(q, skey); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c new file mode 100644 index 0000000000..f81bc956a1 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_ct_enc.c @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +static inline void +add_round_key(uint32_t *q, const uint32_t *sk) +{ + q[0] ^= sk[0]; + q[1] ^= sk[1]; + q[2] ^= sk[2]; + q[3] ^= sk[3]; + q[4] ^= sk[4]; + q[5] ^= sk[5]; + q[6] ^= sk[6]; + q[7] ^= sk[7]; +} + +static inline void +shift_rows(uint32_t *q) +{ + int i; + + for (i = 0; i < 8; i ++) { + uint32_t x; + + x = q[i]; + q[i] = (x & 0x000000FF) + | ((x & 0x0000FC00) >> 2) | ((x & 0x00000300) << 6) + | ((x & 0x00F00000) >> 4) | ((x & 0x000F0000) << 4) + | ((x & 0xC0000000) >> 6) | ((x & 0x3F000000) << 2); + } +} + +static inline uint32_t +rotr16(uint32_t x) +{ + return (x << 16) | (x >> 16); +} + +static inline void +mix_columns(uint32_t *q) +{ + uint32_t q0, q1, q2, q3, q4, q5, q6, q7; + uint32_t r0, r1, r2, r3, r4, r5, r6, r7; + + q0 = q[0]; + q1 = q[1]; + q2 = q[2]; + q3 = q[3]; + q4 = q[4]; + q5 = q[5]; + q6 = q[6]; + q7 = q[7]; + r0 = (q0 >> 8) | (q0 << 24); + r1 = (q1 >> 8) | (q1 << 24); + r2 = (q2 >> 8) | (q2 << 24); + r3 = (q3 >> 8) | (q3 << 24); + r4 = (q4 >> 8) | (q4 << 24); + r5 = (q5 >> 8) | (q5 << 24); + r6 = (q6 >> 8) | (q6 << 24); + r7 = (q7 >> 8) | (q7 << 24); + + q[0] = q7 ^ r7 ^ r0 ^ rotr16(q0 ^ r0); + q[1] = q0 ^ r0 ^ q7 ^ r7 ^ r1 ^ rotr16(q1 ^ r1); + q[2] = q1 ^ r1 ^ r2 ^ rotr16(q2 ^ r2); + q[3] = q2 ^ r2 ^ q7 ^ r7 ^ r3 ^ rotr16(q3 ^ r3); + q[4] = q3 ^ r3 ^ q7 ^ r7 ^ r4 ^ rotr16(q4 ^ r4); + q[5] = q4 ^ r4 ^ r5 ^ rotr16(q5 ^ r5); + q[6] = q5 ^ r5 ^ r6 ^ rotr16(q6 ^ r6); + q[7] = q6 ^ r6 ^ r7 ^ rotr16(q7 ^ r7); +} + +/* see inner.h */ +void +br_aes_ct_bitslice_encrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q) +{ + unsigned u; + + add_round_key(q, skey); + for (u = 1; u < num_rounds; u ++) { + br_aes_ct_bitslice_Sbox(q); + shift_rows(q); + mix_columns(q); + add_round_key(q, skey + (u << 3)); + } + br_aes_ct_bitslice_Sbox(q); + shift_rows(q); + add_round_key(q, skey + (num_rounds << 3)); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c new file mode 100644 index 0000000000..53e4a98050 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcdec.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_cbcdec_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[16]; + int i; + + memcpy(tmp, buf, 16); + br_aes_small_decrypt(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_aes_small_cbcdec_vtable PROGMEM = { + sizeof(br_aes_small_cbcdec_keys), + 16, + 4, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_aes_small_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_aes_small_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c new file mode 100644 index 0000000000..19bbd6c2bb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_cbcenc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 16; i ++) { + buf[i] ^= ivbuf[i]; + } + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 16); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_aes_small_cbcenc_vtable PROGMEM = { + sizeof(br_aes_small_cbcenc_keys), + 16, + 4, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_aes_small_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_aes_small_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c new file mode 100644 index 0000000000..ec8df3e86d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctr.c @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_ctr_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +uint32_t +br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + + memcpy(tmp, iv, 12); + br_enc32be(tmp + 12, cc ++); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp); + if (len <= 16) { + xorbuf(buf, tmp, len); + break; + } + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + } + return cc; +} + +/* see bearssl_block.h */ +const br_block_ctr_class br_aes_small_ctr_vtable PROGMEM = { + sizeof(br_aes_small_ctr_keys), + 16, + 4, + (void (*)(const br_block_ctr_class **, const void *, size_t)) + &br_aes_small_ctr_init, + (uint32_t (*)(const br_block_ctr_class *const *, + const void *, uint32_t, void *, size_t)) + &br_aes_small_ctr_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c new file mode 100644 index 0000000000..49de1868fc --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_ctrcbc.c @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_aes_small_ctrcbc_vtable; + ctx->num_rounds = br_aes_keysched(ctx->skey, key, len); +} + +static void +xorbuf(void *dst, const void *src, size_t len) +{ + unsigned char *d; + const unsigned char *s; + + d = dst; + s = src; + while (len -- > 0) { + *d ++ ^= *s ++; + } +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len) +{ + unsigned char *buf, *bctr; + uint32_t cc0, cc1, cc2, cc3; + + buf = data; + bctr = ctr; + cc3 = br_dec32be(bctr + 0); + cc2 = br_dec32be(bctr + 4); + cc1 = br_dec32be(bctr + 8); + cc0 = br_dec32be(bctr + 12); + while (len > 0) { + unsigned char tmp[16]; + uint32_t carry; + + br_enc32be(tmp + 0, cc3); + br_enc32be(tmp + 4, cc2); + br_enc32be(tmp + 8, cc1); + br_enc32be(tmp + 12, cc0); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, tmp); + xorbuf(buf, tmp, 16); + buf += 16; + len -= 16; + cc0 ++; + carry = (~(cc0 | -cc0)) >> 31; + cc1 += carry; + carry &= (~(cc1 | -cc1)) >> 31; + cc2 += carry; + carry &= (~(cc2 | -cc2)) >> 31; + cc3 += carry; + } + br_enc32be(bctr + 0, cc3); + br_enc32be(bctr + 4, cc2); + br_enc32be(bctr + 8, cc1); + br_enc32be(bctr + 12, cc0); +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + xorbuf(cbcmac, buf, 16); + br_aes_small_encrypt(ctx->num_rounds, ctx->skey, cbcmac); + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_small_ctrcbc_ctr(ctx, ctr, data, len); + br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len); +} + +/* see bearssl_block.h */ +void +br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len) +{ + br_aes_small_ctrcbc_mac(ctx, cbcmac, data, len); + br_aes_small_ctrcbc_ctr(ctx, ctr, data, len); +} + +/* see bearssl_block.h */ +const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable PROGMEM = { + sizeof(br_aes_small_ctrcbc_keys), + 16, + 4, + (void (*)(const br_block_ctrcbc_class **, const void *, size_t)) + &br_aes_small_ctrcbc_init, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_small_ctrcbc_encrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, void *, size_t)) + &br_aes_small_ctrcbc_decrypt, + (void (*)(const br_block_ctrcbc_class *const *, + void *, void *, size_t)) + &br_aes_small_ctrcbc_ctr, + (void (*)(const br_block_ctrcbc_class *const *, + void *, const void *, size_t)) + &br_aes_small_ctrcbc_mac +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c new file mode 100644 index 0000000000..6c5753021c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_dec.c @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Inverse S-box. + */ +static const unsigned char iS[] = { + 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, + 0x81, 0xF3, 0xD7, 0xFB, 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, + 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB, 0x54, 0x7B, 0x94, 0x32, + 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E, + 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, + 0x6D, 0x8B, 0xD1, 0x25, 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, + 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92, 0x6C, 0x70, 0x48, 0x50, + 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84, + 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, + 0xB8, 0xB3, 0x45, 0x06, 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, + 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B, 0x3A, 0x91, 0x11, 0x41, + 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73, + 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, + 0x1C, 0x75, 0xDF, 0x6E, 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, + 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x3E, 0x4B, + 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4, + 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, + 0x27, 0x80, 0xEC, 0x5F, 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, + 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF, 0xA0, 0xE0, 0x3B, 0x4D, + 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61, + 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, + 0x55, 0x21, 0x0C, 0x7D +}; + +static void +add_round_key(unsigned *state, const uint32_t *skeys) +{ + int i; + + for (i = 0; i < 16; i += 4) { + uint32_t k; + + k = *skeys ++; + state[i + 0] ^= (unsigned)(k >> 24); + state[i + 1] ^= (unsigned)(k >> 16) & 0xFF; + state[i + 2] ^= (unsigned)(k >> 8) & 0xFF; + state[i + 3] ^= (unsigned)k & 0xFF; + } +} + +static void +inv_sub_bytes(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i ++) { + state[i] = iS[state[i]]; + } +} + +static void +inv_shift_rows(unsigned *state) +{ + unsigned tmp; + + tmp = state[13]; + state[13] = state[9]; + state[9] = state[5]; + state[5] = state[1]; + state[1] = tmp; + + tmp = state[2]; + state[2] = state[10]; + state[10] = tmp; + tmp = state[6]; + state[6] = state[14]; + state[14] = tmp; + + tmp = state[3]; + state[3] = state[7]; + state[7] = state[11]; + state[11] = state[15]; + state[15] = tmp; +} + +static inline unsigned +gf256red(unsigned x) +{ + unsigned y; + + y = x >> 8; + return (x ^ y ^ (y << 1) ^ (y << 3) ^ (y << 4)) & 0xFF; +} + +static void +inv_mix_columns(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i += 4) { + unsigned s0, s1, s2, s3; + unsigned t0, t1, t2, t3; + + s0 = state[i + 0]; + s1 = state[i + 1]; + s2 = state[i + 2]; + s3 = state[i + 3]; + t0 = (s0 << 1) ^ (s0 << 2) ^ (s0 << 3) + ^ s1 ^ (s1 << 1) ^ (s1 << 3) + ^ s2 ^ (s2 << 2) ^ (s2 << 3) + ^ s3 ^ (s3 << 3); + t1 = s0 ^ (s0 << 3) + ^ (s1 << 1) ^ (s1 << 2) ^ (s1 << 3) + ^ s2 ^ (s2 << 1) ^ (s2 << 3) + ^ s3 ^ (s3 << 2) ^ (s3 << 3); + t2 = s0 ^ (s0 << 2) ^ (s0 << 3) + ^ s1 ^ (s1 << 3) + ^ (s2 << 1) ^ (s2 << 2) ^ (s2 << 3) + ^ s3 ^ (s3 << 1) ^ (s3 << 3); + t3 = s0 ^ (s0 << 1) ^ (s0 << 3) + ^ s1 ^ (s1 << 2) ^ (s1 << 3) + ^ s2 ^ (s2 << 3) + ^ (s3 << 1) ^ (s3 << 2) ^ (s3 << 3); + state[i + 0] = gf256red(t0); + state[i + 1] = gf256red(t1); + state[i + 2] = gf256red(t2); + state[i + 3] = gf256red(t3); + } +} + +/* see inner.h */ +void +br_aes_small_decrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + unsigned state[16]; + unsigned u; + + buf = data; + for (u = 0; u < 16; u ++) { + state[u] = buf[u]; + } + add_round_key(state, skey + (num_rounds << 2)); + for (u = num_rounds - 1; u > 0; u --) { + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, skey + (u << 2)); + inv_mix_columns(state); + } + inv_shift_rows(state); + inv_sub_bytes(state); + add_round_key(state, skey); + for (u = 0; u < 16; u ++) { + buf[u] = state[u]; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c new file mode 100644 index 0000000000..0dca62c49e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/aes_small_enc.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#define S br_aes_S + +static void +add_round_key(unsigned *state, const uint32_t *skeys) +{ + int i; + + for (i = 0; i < 16; i += 4) { + uint32_t k; + + k = *skeys ++; + state[i + 0] ^= (unsigned)(k >> 24); + state[i + 1] ^= (unsigned)(k >> 16) & 0xFF; + state[i + 2] ^= (unsigned)(k >> 8) & 0xFF; + state[i + 3] ^= (unsigned)k & 0xFF; + } +} + +static void +sub_bytes(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i ++) { + state[i] = S[state[i]]; + } +} + +static void +shift_rows(unsigned *state) +{ + unsigned tmp; + + tmp = state[1]; + state[1] = state[5]; + state[5] = state[9]; + state[9] = state[13]; + state[13] = tmp; + + tmp = state[2]; + state[2] = state[10]; + state[10] = tmp; + tmp = state[6]; + state[6] = state[14]; + state[14] = tmp; + + tmp = state[15]; + state[15] = state[11]; + state[11] = state[7]; + state[7] = state[3]; + state[3] = tmp; +} + +static void +mix_columns(unsigned *state) +{ + int i; + + for (i = 0; i < 16; i += 4) { + unsigned s0, s1, s2, s3; + unsigned t0, t1, t2, t3; + + s0 = state[i + 0]; + s1 = state[i + 1]; + s2 = state[i + 2]; + s3 = state[i + 3]; + t0 = (s0 << 1) ^ s1 ^ (s1 << 1) ^ s2 ^ s3; + t1 = s0 ^ (s1 << 1) ^ s2 ^ (s2 << 1) ^ s3; + t2 = s0 ^ s1 ^ (s2 << 1) ^ s3 ^ (s3 << 1); + t3 = s0 ^ (s0 << 1) ^ s1 ^ s2 ^ (s3 << 1); + state[i + 0] = t0 ^ ((unsigned)(-(int)(t0 >> 8)) & 0x11B); + state[i + 1] = t1 ^ ((unsigned)(-(int)(t1 >> 8)) & 0x11B); + state[i + 2] = t2 ^ ((unsigned)(-(int)(t2 >> 8)) & 0x11B); + state[i + 3] = t3 ^ ((unsigned)(-(int)(t3 >> 8)) & 0x11B); + } +} + +/* see inner.h */ +void +br_aes_small_encrypt(unsigned num_rounds, const uint32_t *skey, void *data) +{ + unsigned char *buf; + unsigned state[16]; + unsigned u; + + buf = data; + for (u = 0; u < 16; u ++) { + state[u] = buf[u]; + } + add_round_key(state, skey); + for (u = 1; u < num_rounds; u ++) { + sub_bytes(state); + shift_rows(state); + mix_columns(state); + add_round_key(state, skey + (u << 2)); + } + sub_bytes(state); + shift_rows(state); + add_round_key(state, skey + (num_rounds << 2)); + for (u = 0; u < 16; u ++) { + buf[u] = state[u]; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c new file mode 100644 index 0000000000..da4024cd12 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_ct.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +uint32_t +br_chacha20_ct_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t kw[8], ivw[3]; + size_t u; + +#ifdef ESP32 + static const uint32_t CW[] = { +#else + static const uint32_t CW[] PROGMEM = { +#endif + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + + buf = data; + for (u = 0; u < 8; u ++) { + kw[u] = br_dec32le((const unsigned char *)key + (u << 2)); + } + for (u = 0; u < 3; u ++) { + ivw[u] = br_dec32le((const unsigned char *)iv + (u << 2)); + } + while (len > 0) { + uint32_t state[16]; + int i; + size_t clen; + unsigned char tmp[64]; + + memcpy(&state[0], CW, sizeof CW); + memcpy(&state[4], kw, sizeof kw); + state[12] = cc; + memcpy(&state[13], ivw, sizeof ivw); + for (i = 0; i < 10; i ++) { + +#define QROUND(a, b, c, d) do { \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = (state[d] << 16) | (state[d] >> 16); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = (state[b] << 12) | (state[b] >> 20); \ + state[a] += state[b]; \ + state[d] ^= state[a]; \ + state[d] = (state[d] << 8) | (state[d] >> 24); \ + state[c] += state[d]; \ + state[b] ^= state[c]; \ + state[b] = (state[b] << 7) | (state[b] >> 25); \ + } while (0) + + QROUND( 0, 4, 8, 12); + QROUND( 1, 5, 9, 13); + QROUND( 2, 6, 10, 14); + QROUND( 3, 7, 11, 15); + QROUND( 0, 5, 10, 15); + QROUND( 1, 6, 11, 12); + QROUND( 2, 7, 8, 13); + QROUND( 3, 4, 9, 14); + +#undef QROUND + + } + for (u = 0; u < 4; u ++) { + br_enc32le(&tmp[u << 2], state[u] + CW[u]); + } + for (u = 4; u < 12; u ++) { + br_enc32le(&tmp[u << 2], state[u] + kw[u - 4]); + } + br_enc32le(&tmp[48], state[12] + cc); + for (u = 13; u < 16; u ++) { + br_enc32le(&tmp[u << 2], state[u] + ivw[u - 13]); + } + + clen = len < 64 ? len : 64; + for (u = 0; u < clen; u ++) { + buf[u] ^= tmp[u]; + } + buf += clen; + len -= clen; + cc ++; + } + return cc; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c new file mode 100644 index 0000000000..52c213903b --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/chacha20_sse2.c @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#define BR_ENABLE_INTRINSICS 1 +#include "t_inner.h" + +#if BR_SSE2 + +/* + * This file contains a ChaCha20 implementation that leverages SSE2 + * opcodes for better performance. + */ + +/* see bearssl_block.h */ +br_chacha20_run +br_chacha20_sse2_get(void) +{ + /* + * If using 64-bit mode, then SSE2 opcodes should be automatically + * available, since they are part of the ABI. + * + * In 32-bit mode, we use CPUID to detect the SSE2 feature. + */ + +#if BR_amd64 + return &br_chacha20_sse2_run; +#else + + /* + * SSE2 support is indicated by bit 26 in EDX. + */ + if (br_cpuid(0, 0, 0, 0x04000000)) { + return &br_chacha20_sse2_run; + } else { + return 0; + } +#endif +} + +BR_TARGETS_X86_UP + +/* see bearssl_block.h */ +BR_TARGET("sse2") +uint32_t +br_chacha20_sse2_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len) +{ + unsigned char *buf; + uint32_t ivtmp[4]; + __m128i kw0, kw1; + __m128i iw, cw; + __m128i one; + + static const uint32_t CW[] = { + 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 + }; + + buf = data; + kw0 = _mm_loadu_si128(key); + kw1 = _mm_loadu_si128((const void *)((const unsigned char *)key + 16)); + ivtmp[0] = cc; + memcpy(ivtmp + 1, iv, 12); + iw = _mm_loadu_si128((const void *)ivtmp); + cw = _mm_loadu_si128((const void *)CW); + one = _mm_set_epi32(0, 0, 0, 1); + + while (len > 0) { + /* + * sj contains state words 4*j to 4*j+3. + */ + __m128i s0, s1, s2, s3; + int i; + + s0 = cw; + s1 = kw0; + s2 = kw1; + s3 = iw; + for (i = 0; i < 10; i ++) { + /* + * Even round is straightforward application on + * the state words. + */ + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 16), + _mm_srli_epi32(s3, 16)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 12), + _mm_srli_epi32(s1, 20)); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 8), + _mm_srli_epi32(s3, 24)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 7), + _mm_srli_epi32(s1, 25)); + + /* + * For the odd round, we must rotate some state + * words so that the computations apply on the + * right combinations of words. + */ + s1 = _mm_shuffle_epi32(s1, 0x39); + s2 = _mm_shuffle_epi32(s2, 0x4E); + s3 = _mm_shuffle_epi32(s3, 0x93); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 16), + _mm_srli_epi32(s3, 16)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 12), + _mm_srli_epi32(s1, 20)); + + s0 = _mm_add_epi32(s0, s1); + s3 = _mm_xor_si128(s3, s0); + s3 = _mm_or_si128( + _mm_slli_epi32(s3, 8), + _mm_srli_epi32(s3, 24)); + + s2 = _mm_add_epi32(s2, s3); + s1 = _mm_xor_si128(s1, s2); + s1 = _mm_or_si128( + _mm_slli_epi32(s1, 7), + _mm_srli_epi32(s1, 25)); + + /* + * After the odd round, we rotate back the values + * to undo the rotate at the start of the odd round. + */ + s1 = _mm_shuffle_epi32(s1, 0x93); + s2 = _mm_shuffle_epi32(s2, 0x4E); + s3 = _mm_shuffle_epi32(s3, 0x39); + } + + /* + * Addition with the initial state. + */ + s0 = _mm_add_epi32(s0, cw); + s1 = _mm_add_epi32(s1, kw0); + s2 = _mm_add_epi32(s2, kw1); + s3 = _mm_add_epi32(s3, iw); + + /* + * Increment block counter. + */ + iw = _mm_add_epi32(iw, one); + + /* + * XOR final state with the data. + */ + if (len < 64) { + unsigned char tmp[64]; + size_t u; + + _mm_storeu_si128((void *)(tmp + 0), s0); + _mm_storeu_si128((void *)(tmp + 16), s1); + _mm_storeu_si128((void *)(tmp + 32), s2); + _mm_storeu_si128((void *)(tmp + 48), s3); + for (u = 0; u < len; u ++) { + buf[u] ^= tmp[u]; + } + break; + } else { + __m128i b0, b1, b2, b3; + + b0 = _mm_loadu_si128((const void *)(buf + 0)); + b1 = _mm_loadu_si128((const void *)(buf + 16)); + b2 = _mm_loadu_si128((const void *)(buf + 32)); + b3 = _mm_loadu_si128((const void *)(buf + 48)); + b0 = _mm_xor_si128(b0, s0); + b1 = _mm_xor_si128(b1, s1); + b2 = _mm_xor_si128(b2, s2); + b3 = _mm_xor_si128(b3, s3); + _mm_storeu_si128((void *)(buf + 0), b0); + _mm_storeu_si128((void *)(buf + 16), b1); + _mm_storeu_si128((void *)(buf + 32), b2); + _mm_storeu_si128((void *)(buf + 48), b3); + buf += 64; + len -= 64; + } + } + + /* + * _mm_extract_epi32() requires SSE4.1. We prefer to stick to + * raw SSE2, thus we use _mm_extract_epi16(). + */ + return (uint32_t)_mm_extract_epi16(iw, 0) + | ((uint32_t)_mm_extract_epi16(iw, 1) << 16); +} + +BR_TARGETS_X86_DOWN + +#else + +/* see bearssl_block.h */ +br_chacha20_run +br_chacha20_sse2_get(void) +{ + return 0; +} + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c new file mode 100644 index 0000000000..238717630f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct.c @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * During key schedule, we need to apply bit extraction PC-2 then permute + * things into our bitslice representation. PC-2 extracts 48 bits out + * of two 28-bit words (kl and kr), and we store these bits into two + * 32-bit words sk0 and sk1. + * + * -- bit 16+x of sk0 comes from bit QL0[x] of kl + * -- bit x of sk0 comes from bit QR0[x] of kr + * -- bit 16+x of sk1 comes from bit QL1[x] of kl + * -- bit x of sk1 comes from bit QR1[x] of kr + */ + +static const unsigned char QL0[] PROGMEM = { + 17, 4, 27, 23, 13, 22, 7, 18, + 16, 24, 2, 20, 1, 8, 15, 26 +}; + +static const unsigned char QR0[] PROGMEM = { + 25, 19, 9, 1, 5, 11, 23, 8, + 17, 0, 22, 3, 6, 20, 27, 24 +}; + +static const unsigned char QL1[] PROGMEM = { + 28, 28, 14, 11, 28, 28, 25, 0, + 28, 28, 5, 9, 28, 28, 12, 21 +}; + +static const unsigned char QR1[] PROGMEM = { + 28, 28, 15, 4, 28, 28, 26, 16, + 28, 28, 12, 7, 28, 28, 10, 14 +}; + +/* + * 32-bit rotation. The C compiler is supposed to recognize it as a + * rotation and use the local architecture rotation opcode (if available). + */ +static inline uint32_t +rotl(uint32_t x, int n) +{ + return (x << n) | (x >> (32 - n)); +} + +/* + * Compute key schedule for 8 key bytes (produces 32 subkey words). + */ +static void +keysched_unit(uint32_t *skey, const void *key) +{ + int i; + + br_des_keysched_unit(skey, key); + + /* + * Apply PC-2 + bitslicing. + */ + for (i = 0; i < 16; i ++) { + uint32_t kl, kr, sk0, sk1; + int j; + + kl = skey[(i << 1) + 0]; + kr = skey[(i << 1) + 1]; + sk0 = 0; + sk1 = 0; + for (j = 0; j < 16; j ++) { + sk0 <<= 1; + sk1 <<= 1; + sk0 |= ((kl >> pgm_read_byte(&QL0[j])) & (uint32_t)1) << 16; + sk0 |= (kr >> pgm_read_byte(&QR0[j])) & (uint32_t)1; + sk1 |= ((kl >> pgm_read_byte(&QL1[j])) & (uint32_t)1) << 16; + sk1 |= (kr >> pgm_read_byte(&QR1[j])) & (uint32_t)1; + } + + skey[(i << 1) + 0] = sk0; + skey[(i << 1) + 1] = sk1; + } + +#if 0 + /* + * Speed-optimized version for PC-2 + bitslicing. + * (Unused. Kept for reference only.) + */ + sk0 = kl & (uint32_t)0x00100000; + sk0 |= (kl & (uint32_t)0x08008000) << 2; + sk0 |= (kl & (uint32_t)0x00400000) << 4; + sk0 |= (kl & (uint32_t)0x00800000) << 5; + sk0 |= (kl & (uint32_t)0x00040000) << 6; + sk0 |= (kl & (uint32_t)0x00010000) << 7; + sk0 |= (kl & (uint32_t)0x00000100) << 10; + sk0 |= (kl & (uint32_t)0x00022000) << 14; + sk0 |= (kl & (uint32_t)0x00000082) << 18; + sk0 |= (kl & (uint32_t)0x00000004) << 19; + sk0 |= (kl & (uint32_t)0x04000000) >> 10; + sk0 |= (kl & (uint32_t)0x00000010) << 26; + sk0 |= (kl & (uint32_t)0x01000000) >> 2; + + sk0 |= kr & (uint32_t)0x00000100; + sk0 |= (kr & (uint32_t)0x00000008) << 1; + sk0 |= (kr & (uint32_t)0x00000200) << 4; + sk0 |= rotl(kr & (uint32_t)0x08000021, 6); + sk0 |= (kr & (uint32_t)0x01000000) >> 24; + sk0 |= (kr & (uint32_t)0x00000002) << 11; + sk0 |= (kr & (uint32_t)0x00100000) >> 18; + sk0 |= (kr & (uint32_t)0x00400000) >> 17; + sk0 |= (kr & (uint32_t)0x00800000) >> 14; + sk0 |= (kr & (uint32_t)0x02020000) >> 10; + sk0 |= (kr & (uint32_t)0x00080000) >> 5; + sk0 |= (kr & (uint32_t)0x00000040) >> 3; + sk0 |= (kr & (uint32_t)0x00000800) >> 1; + + sk1 = kl & (uint32_t)0x02000000; + sk1 |= (kl & (uint32_t)0x00001000) << 5; + sk1 |= (kl & (uint32_t)0x00000200) << 11; + sk1 |= (kl & (uint32_t)0x00004000) << 15; + sk1 |= (kl & (uint32_t)0x00000020) << 16; + sk1 |= (kl & (uint32_t)0x00000800) << 17; + sk1 |= (kl & (uint32_t)0x00000001) << 24; + sk1 |= (kl & (uint32_t)0x00200000) >> 5; + + sk1 |= (kr & (uint32_t)0x00000010) << 8; + sk1 |= (kr & (uint32_t)0x04000000) >> 17; + sk1 |= (kr & (uint32_t)0x00004000) >> 14; + sk1 |= (kr & (uint32_t)0x00000400) >> 9; + sk1 |= (kr & (uint32_t)0x00010000) >> 8; + sk1 |= (kr & (uint32_t)0x00001000) >> 7; + sk1 |= (kr & (uint32_t)0x00000080) >> 3; + sk1 |= (kr & (uint32_t)0x00008000) >> 2; +#endif +} + +/* see inner.h */ +unsigned +br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + switch (key_len) { + case 8: + keysched_unit(skey, key); + return 1; + case 16: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + memcpy(skey + 64, skey, 32 * sizeof *skey); + return 3; + default: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + keysched_unit(skey + 64, (const unsigned char *)key + 16); + return 3; + } +} + +/* + * DES confusion function. This function performs expansion E (32 to + * 48 bits), XOR with subkey, S-boxes, and permutation P. + */ +static inline uint32_t +Fconf(uint32_t r0, const uint32_t *sk) +{ + /* + * Each 6->4 S-box is virtually turned into four 6->1 boxes; we + * thus end up with 32 boxes that we call "T-boxes" here. We will + * evaluate them with bitslice code. + * + * Each T-box is a circuit of multiplexers (sort of) and thus + * takes 70 inputs: the 6 actual T-box inputs, and 64 constants + * that describe the T-box output for all combinations of the + * 6 inputs. With this model, all T-boxes are identical (with + * distinct inputs) and thus can be executed in parallel with + * bitslice code. + * + * T-boxes are numbered from 0 to 31, in least-to-most + * significant order. Thus, S-box S1 corresponds to T-boxes 31, + * 30, 29 and 28, in that order. T-box 'n' is computed with the + * bits at rank 'n' in the 32-bit words. + * + * Words x0 to x5 contain the T-box inputs 0 to 5. + */ + uint32_t x0, x1, x2, x3, x4, x5, z0; + uint32_t y0, y1, y2, y3, y4, y5, y6, y7, y8, y9; + uint32_t y10, y11, y12, y13, y14, y15, y16, y17, y18, y19; + uint32_t y20, y21, y22, y23, y24, y25, y26, y27, y28, y29; + uint32_t y30; + + /* + * Spread input bits over the 6 input words x*. + */ + x1 = r0 & (uint32_t)0x11111111; + x2 = (r0 >> 1) & (uint32_t)0x11111111; + x3 = (r0 >> 2) & (uint32_t)0x11111111; + x4 = (r0 >> 3) & (uint32_t)0x11111111; + x1 = (x1 << 4) - x1; + x2 = (x2 << 4) - x2; + x3 = (x3 << 4) - x3; + x4 = (x4 << 4) - x4; + x0 = (x4 << 4) | (x4 >> 28); + x5 = (x1 >> 4) | (x1 << 28); + + /* + * XOR with the subkey for this round. + */ + x0 ^= sk[0]; + x1 ^= sk[1]; + x2 ^= sk[2]; + x3 ^= sk[3]; + x4 ^= sk[4]; + x5 ^= sk[5]; + + /* + * The T-boxes are done in parallel, since they all use a + * "tree of multiplexer". We use "fake multiplexers": + * + * y = a ^ (x & b) + * + * computes y as either 'a' (if x == 0) or 'a ^ b' (if x == 1). + */ + y0 = (uint32_t)0xEFA72C4D ^ (x0 & (uint32_t)0xEC7AC69C); + y1 = (uint32_t)0xAEAAEDFF ^ (x0 & (uint32_t)0x500FB821); + y2 = (uint32_t)0x37396665 ^ (x0 & (uint32_t)0x40EFA809); + y3 = (uint32_t)0x68D7B833 ^ (x0 & (uint32_t)0xA5EC0B28); + y4 = (uint32_t)0xC9C755BB ^ (x0 & (uint32_t)0x252CF820); + y5 = (uint32_t)0x73FC3606 ^ (x0 & (uint32_t)0x40205801); + y6 = (uint32_t)0xA2A0A918 ^ (x0 & (uint32_t)0xE220F929); + y7 = (uint32_t)0x8222BD90 ^ (x0 & (uint32_t)0x44A3F9E1); + y8 = (uint32_t)0xD6B6AC77 ^ (x0 & (uint32_t)0x794F104A); + y9 = (uint32_t)0x3069300C ^ (x0 & (uint32_t)0x026F320B); + y10 = (uint32_t)0x6CE0D5CC ^ (x0 & (uint32_t)0x7640B01A); + y11 = (uint32_t)0x59A9A22D ^ (x0 & (uint32_t)0x238F1572); + y12 = (uint32_t)0xAC6D0BD4 ^ (x0 & (uint32_t)0x7A63C083); + y13 = (uint32_t)0x21C83200 ^ (x0 & (uint32_t)0x11CCA000); + y14 = (uint32_t)0xA0E62188 ^ (x0 & (uint32_t)0x202F69AA); + /* y15 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */ + y16 = (uint32_t)0xAF7D655A ^ (x0 & (uint32_t)0x51B33BE9); + y17 = (uint32_t)0xF0168AA3 ^ (x0 & (uint32_t)0x3B0FE8AE); + y18 = (uint32_t)0x90AA30C6 ^ (x0 & (uint32_t)0x90BF8816); + y19 = (uint32_t)0x5AB2750A ^ (x0 & (uint32_t)0x09E34F9B); + y20 = (uint32_t)0x5391BE65 ^ (x0 & (uint32_t)0x0103BE88); + y21 = (uint32_t)0x93372BAF ^ (x0 & (uint32_t)0x49AC8E25); + y22 = (uint32_t)0xF288210C ^ (x0 & (uint32_t)0x922C313D); + y23 = (uint32_t)0x920AF5C0 ^ (x0 & (uint32_t)0x70EF31B0); + y24 = (uint32_t)0x63D312C0 ^ (x0 & (uint32_t)0x6A707100); + y25 = (uint32_t)0x537B3006 ^ (x0 & (uint32_t)0xB97C9011); + y26 = (uint32_t)0xA2EFB0A5 ^ (x0 & (uint32_t)0xA320C959); + y27 = (uint32_t)0xBC8F96A5 ^ (x0 & (uint32_t)0x6EA0AB4A); + y28 = (uint32_t)0xFAD176A5 ^ (x0 & (uint32_t)0x6953DDF8); + y29 = (uint32_t)0x665A14A3 ^ (x0 & (uint32_t)0xF74F3E2B); + y30 = (uint32_t)0xF2EFF0CC ^ (x0 & (uint32_t)0xF0306CAD); + /* y31 = (uint32_t)0x00000000 ^ (x0 & (uint32_t)0x00000000); */ + + y0 = y0 ^ (x1 & y1); + y1 = y2 ^ (x1 & y3); + y2 = y4 ^ (x1 & y5); + y3 = y6 ^ (x1 & y7); + y4 = y8 ^ (x1 & y9); + y5 = y10 ^ (x1 & y11); + y6 = y12 ^ (x1 & y13); + y7 = y14; /* was: y14 ^ (x1 & y15) */ + y8 = y16 ^ (x1 & y17); + y9 = y18 ^ (x1 & y19); + y10 = y20 ^ (x1 & y21); + y11 = y22 ^ (x1 & y23); + y12 = y24 ^ (x1 & y25); + y13 = y26 ^ (x1 & y27); + y14 = y28 ^ (x1 & y29); + y15 = y30; /* was: y30 ^ (x1 & y31) */ + + y0 = y0 ^ (x2 & y1); + y1 = y2 ^ (x2 & y3); + y2 = y4 ^ (x2 & y5); + y3 = y6 ^ (x2 & y7); + y4 = y8 ^ (x2 & y9); + y5 = y10 ^ (x2 & y11); + y6 = y12 ^ (x2 & y13); + y7 = y14 ^ (x2 & y15); + + y0 = y0 ^ (x3 & y1); + y1 = y2 ^ (x3 & y3); + y2 = y4 ^ (x3 & y5); + y3 = y6 ^ (x3 & y7); + + y0 = y0 ^ (x4 & y1); + y1 = y2 ^ (x4 & y3); + + y0 = y0 ^ (x5 & y1); + + /* + * The P permutation: + * -- Each bit move is converted into a mask + left rotation. + * -- Rotations that use the same movement are coalesced together. + * -- Left and right shifts are used as alternatives to a rotation + * where appropriate (this will help architectures that do not have + * a rotation opcode). + */ + z0 = (y0 & (uint32_t)0x00000004) << 3; + z0 |= (y0 & (uint32_t)0x00004000) << 4; + z0 |= rotl(y0 & 0x12020120, 5); + z0 |= (y0 & (uint32_t)0x00100000) << 6; + z0 |= (y0 & (uint32_t)0x00008000) << 9; + z0 |= (y0 & (uint32_t)0x04000000) >> 22; + z0 |= (y0 & (uint32_t)0x00000001) << 11; + z0 |= rotl(y0 & 0x20000200, 12); + z0 |= (y0 & (uint32_t)0x00200000) >> 19; + z0 |= (y0 & (uint32_t)0x00000040) << 14; + z0 |= (y0 & (uint32_t)0x00010000) << 15; + z0 |= (y0 & (uint32_t)0x00000002) << 16; + z0 |= rotl(y0 & 0x40801800, 17); + z0 |= (y0 & (uint32_t)0x00080000) >> 13; + z0 |= (y0 & (uint32_t)0x00000010) << 21; + z0 |= (y0 & (uint32_t)0x01000000) >> 10; + z0 |= rotl(y0 & 0x88000008, 24); + z0 |= (y0 & (uint32_t)0x00000480) >> 7; + z0 |= (y0 & (uint32_t)0x00442000) >> 6; + return z0; +} + +/* + * Process one block through 16 successive rounds, omitting the swap + * in the final round. + */ +static void +process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *sk_exp) +{ + int i; + uint32_t l, r; + + l = *pl; + r = *pr; + for (i = 0; i < 16; i ++) { + uint32_t t; + + t = l ^ Fconf(r, sk_exp); + l = r; + r = t; + sk_exp += 6; + } + *pl = r; + *pr = l; +} + +/* see inner.h */ +void +br_des_ct_process_block(unsigned num_rounds, + const uint32_t *sk_exp, void *block) +{ + unsigned char *buf; + uint32_t l, r; + + buf = block; + l = br_dec32be(buf); + r = br_dec32be(buf + 4); + br_des_do_IP(&l, &r); + while (num_rounds -- > 0) { + process_block_unit(&l, &r, sk_exp); + sk_exp += 96; + } + br_des_do_invIP(&l, &r); + br_enc32be(buf, l); + br_enc32be(buf + 4, r); +} + +/* see inner.h */ +void +br_des_ct_skey_expand(uint32_t *sk_exp, + unsigned num_rounds, const uint32_t *skey) +{ + num_rounds <<= 4; + while (num_rounds -- > 0) { + uint32_t v, w0, w1, w2, w3; + + v = *skey ++; + w0 = v & 0x11111111; + w1 = (v >> 1) & 0x11111111; + w2 = (v >> 2) & 0x11111111; + w3 = (v >> 3) & 0x11111111; + *sk_exp ++ = (w0 << 4) - w0; + *sk_exp ++ = (w1 << 4) - w1; + *sk_exp ++ = (w2 << 4) - w2; + *sk_exp ++ = (w3 << 4) - w3; + v = *skey ++; + w0 = v & 0x11111111; + w1 = (v >> 1) & 0x11111111; + *sk_exp ++ = (w0 << 4) - w0; + *sk_exp ++ = (w1 << 4) - w1; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c new file mode 100644 index 0000000000..37645c98e1 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcdec.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_ct_cbcdec_vtable; + ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len); + if (len == 8) { + br_des_rev_skey(ctx->skey); + } else { + int i; + + for (i = 0; i < 48; i += 2) { + uint32_t t; + + t = ctx->skey[i]; + ctx->skey[i] = ctx->skey[94 - i]; + ctx->skey[94 - i] = t; + t = ctx->skey[i + 1]; + ctx->skey[i + 1] = ctx->skey[95 - i]; + ctx->skey[95 - i] = t; + } + } +} + +/* see bearssl_block.h */ +void +br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t sk_exp[288]; + + br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[8]; + int i; + + memcpy(tmp, buf, 8); + br_des_ct_process_block(ctx->num_rounds, sk_exp, buf); + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_des_ct_cbcdec_vtable PROGMEM = { + sizeof(br_des_ct_cbcdec_keys), + 8, + 3, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_des_ct_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_des_ct_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c new file mode 100644 index 0000000000..da6fbb6381 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_ct_cbcenc.c @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_ct_cbcenc_vtable; + ctx->num_rounds = br_des_ct_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + uint32_t sk_exp[288]; + + br_des_ct_skey_expand(sk_exp, ctx->num_rounds, ctx->skey); + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + br_des_ct_process_block(ctx->num_rounds, sk_exp, buf); + memcpy(ivbuf, buf, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_des_ct_cbcenc_vtable PROGMEM = { + sizeof(br_des_ct_cbcenc_keys), + 8, + 3, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_des_ct_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_des_ct_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c new file mode 100644 index 0000000000..2176c90a1b --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_support.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +void +br_des_do_IP(uint32_t *xl, uint32_t *xr) +{ + /* + * Permutation algorithm is initially from Richard Outerbridge; + * implementation here is adapted from Crypto++ "des.cpp" file + * (which is in public domain). + */ + uint32_t l, r, t; + + l = *xl; + r = *xr; + t = ((l >> 4) ^ r) & (uint32_t)0x0F0F0F0F; + r ^= t; + l ^= t << 4; + t = ((l >> 16) ^ r) & (uint32_t)0x0000FFFF; + r ^= t; + l ^= t << 16; + t = ((r >> 2) ^ l) & (uint32_t)0x33333333; + l ^= t; + r ^= t << 2; + t = ((r >> 8) ^ l) & (uint32_t)0x00FF00FF; + l ^= t; + r ^= t << 8; + t = ((l >> 1) ^ r) & (uint32_t)0x55555555; + r ^= t; + l ^= t << 1; + *xl = l; + *xr = r; +} + +/* see inner.h */ +void +br_des_do_invIP(uint32_t *xl, uint32_t *xr) +{ + /* + * See br_des_do_IP(). + */ + uint32_t l, r, t; + + l = *xl; + r = *xr; + t = ((l >> 1) ^ r) & 0x55555555; + r ^= t; + l ^= t << 1; + t = ((r >> 8) ^ l) & 0x00FF00FF; + l ^= t; + r ^= t << 8; + t = ((r >> 2) ^ l) & 0x33333333; + l ^= t; + r ^= t << 2; + t = ((l >> 16) ^ r) & 0x0000FFFF; + r ^= t; + l ^= t << 16; + t = ((l >> 4) ^ r) & 0x0F0F0F0F; + r ^= t; + l ^= t << 4; + *xl = l; + *xr = r; +} + +/* see inner.h */ +void +br_des_keysched_unit(uint32_t *skey, const void *key) +{ + uint32_t xl, xr, kl, kr; + int i; + + xl = br_dec32be(key); + xr = br_dec32be((const unsigned char *)key + 4); + + /* + * Permutation PC-1 is quite similar to the IP permutation. + * Definition of IP (in FIPS 46-3 notations) is: + * 58 50 42 34 26 18 10 2 + * 60 52 44 36 28 20 12 4 + * 62 54 46 38 30 22 14 6 + * 64 56 48 40 32 24 16 8 + * 57 49 41 33 25 17 9 1 + * 59 51 43 35 27 19 11 3 + * 61 53 45 37 29 21 13 5 + * 63 55 47 39 31 23 15 7 + * + * Definition of PC-1 is: + * 57 49 41 33 25 17 9 1 + * 58 50 42 34 26 18 10 2 + * 59 51 43 35 27 19 11 3 + * 60 52 44 36 + * 63 55 47 39 31 23 15 7 + * 62 54 46 38 30 22 14 6 + * 61 53 45 37 29 21 13 5 + * 28 20 12 4 + */ + br_des_do_IP(&xl, &xr); + kl = ((xr & (uint32_t)0xFF000000) >> 4) + | ((xl & (uint32_t)0xFF000000) >> 12) + | ((xr & (uint32_t)0x00FF0000) >> 12) + | ((xl & (uint32_t)0x00FF0000) >> 20); + kr = ((xr & (uint32_t)0x000000FF) << 20) + | ((xl & (uint32_t)0x0000FF00) << 4) + | ((xr & (uint32_t)0x0000FF00) >> 4) + | ((xl & (uint32_t)0x000F0000) >> 16); + + /* + * For each round, rotate the two 28-bit words kl and kr. + * The extraction of the 48-bit subkey (PC-2) is not done yet. + */ + for (i = 0; i < 16; i ++) { + if ((1 << i) & 0x8103) { + kl = (kl << 1) | (kl >> 27); + kr = (kr << 1) | (kr >> 27); + } else { + kl = (kl << 2) | (kl >> 26); + kr = (kr << 2) | (kr >> 26); + } + kl &= (uint32_t)0x0FFFFFFF; + kr &= (uint32_t)0x0FFFFFFF; + skey[(i << 1) + 0] = kl; + skey[(i << 1) + 1] = kr; + } +} + +/* see inner.h */ +void +br_des_rev_skey(uint32_t *skey) +{ + int i; + + for (i = 0; i < 16; i += 2) { + uint32_t t; + + t = skey[i + 0]; + skey[i + 0] = skey[30 - i]; + skey[30 - i] = t; + t = skey[i + 1]; + skey[i + 1] = skey[31 - i]; + skey[31 - i] = t; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c new file mode 100644 index 0000000000..1aefe2147d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab.c @@ -0,0 +1,310 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * PC2left[x] tells where bit x goes when applying PC-2. 'x' is a bit + * position in the left rotated key word. Both position are in normal + * order (rightmost bit is 0). + */ +static const unsigned char PC2left[] = { + 16, 3, 7, 24, 20, 11, 24, + 13, 2, 10, 24, 22, 5, 15, + 23, 1, 9, 21, 12, 24, 6, + 4, 14, 18, 8, 17, 0, 19 +}; + +/* + * Similar to PC2left[x], for the right rotated key word. + */ +static const unsigned char PC2right[] = { + 8, 18, 24, 6, 22, 15, 3, + 10, 12, 19, 5, 14, 11, 24, + 4, 23, 16, 9, 24, 20, 2, + 24, 7, 13, 0, 21, 17, 1 +}; + +/* + * S-boxes and PC-1 merged. + */ +static const uint32_t S1[] PROGMEM = { + 0x00808200, 0x00000000, 0x00008000, 0x00808202, + 0x00808002, 0x00008202, 0x00000002, 0x00008000, + 0x00000200, 0x00808200, 0x00808202, 0x00000200, + 0x00800202, 0x00808002, 0x00800000, 0x00000002, + 0x00000202, 0x00800200, 0x00800200, 0x00008200, + 0x00008200, 0x00808000, 0x00808000, 0x00800202, + 0x00008002, 0x00800002, 0x00800002, 0x00008002, + 0x00000000, 0x00000202, 0x00008202, 0x00800000, + 0x00008000, 0x00808202, 0x00000002, 0x00808000, + 0x00808200, 0x00800000, 0x00800000, 0x00000200, + 0x00808002, 0x00008000, 0x00008200, 0x00800002, + 0x00000200, 0x00000002, 0x00800202, 0x00008202, + 0x00808202, 0x00008002, 0x00808000, 0x00800202, + 0x00800002, 0x00000202, 0x00008202, 0x00808200, + 0x00000202, 0x00800200, 0x00800200, 0x00000000, + 0x00008002, 0x00008200, 0x00000000, 0x00808002 +}; + +static const uint32_t S2[] PROGMEM = { + 0x40084010, 0x40004000, 0x00004000, 0x00084010, + 0x00080000, 0x00000010, 0x40080010, 0x40004010, + 0x40000010, 0x40084010, 0x40084000, 0x40000000, + 0x40004000, 0x00080000, 0x00000010, 0x40080010, + 0x00084000, 0x00080010, 0x40004010, 0x00000000, + 0x40000000, 0x00004000, 0x00084010, 0x40080000, + 0x00080010, 0x40000010, 0x00000000, 0x00084000, + 0x00004010, 0x40084000, 0x40080000, 0x00004010, + 0x00000000, 0x00084010, 0x40080010, 0x00080000, + 0x40004010, 0x40080000, 0x40084000, 0x00004000, + 0x40080000, 0x40004000, 0x00000010, 0x40084010, + 0x00084010, 0x00000010, 0x00004000, 0x40000000, + 0x00004010, 0x40084000, 0x00080000, 0x40000010, + 0x00080010, 0x40004010, 0x40000010, 0x00080010, + 0x00084000, 0x00000000, 0x40004000, 0x00004010, + 0x40000000, 0x40080010, 0x40084010, 0x00084000 +}; + +static const uint32_t S3[] PROGMEM = { + 0x00000104, 0x04010100, 0x00000000, 0x04010004, + 0x04000100, 0x00000000, 0x00010104, 0x04000100, + 0x00010004, 0x04000004, 0x04000004, 0x00010000, + 0x04010104, 0x00010004, 0x04010000, 0x00000104, + 0x04000000, 0x00000004, 0x04010100, 0x00000100, + 0x00010100, 0x04010000, 0x04010004, 0x00010104, + 0x04000104, 0x00010100, 0x00010000, 0x04000104, + 0x00000004, 0x04010104, 0x00000100, 0x04000000, + 0x04010100, 0x04000000, 0x00010004, 0x00000104, + 0x00010000, 0x04010100, 0x04000100, 0x00000000, + 0x00000100, 0x00010004, 0x04010104, 0x04000100, + 0x04000004, 0x00000100, 0x00000000, 0x04010004, + 0x04000104, 0x00010000, 0x04000000, 0x04010104, + 0x00000004, 0x00010104, 0x00010100, 0x04000004, + 0x04010000, 0x04000104, 0x00000104, 0x04010000, + 0x00010104, 0x00000004, 0x04010004, 0x00010100 +}; + +static const uint32_t S4[] PROGMEM = { + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x00401040, 0x80400040, 0x80400000, 0x80001000, + 0x00000000, 0x00401000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00400040, 0x80400000, + 0x80000000, 0x00001000, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x80001000, 0x00001040, + 0x80400040, 0x80000000, 0x00001040, 0x00400040, + 0x00001000, 0x00401040, 0x80401040, 0x80000040, + 0x00400040, 0x80400000, 0x00401000, 0x80401040, + 0x80000040, 0x00000000, 0x00000000, 0x00401000, + 0x00001040, 0x00400040, 0x80400040, 0x80000000, + 0x80401000, 0x80001040, 0x80001040, 0x00000040, + 0x80401040, 0x80000040, 0x80000000, 0x00001000, + 0x80400000, 0x80001000, 0x00401040, 0x80400040, + 0x80001000, 0x00001040, 0x00400000, 0x80401000, + 0x00000040, 0x00400000, 0x00001000, 0x00401040 +}; + +static const uint32_t S5[] PROGMEM = { + 0x00000080, 0x01040080, 0x01040000, 0x21000080, + 0x00040000, 0x00000080, 0x20000000, 0x01040000, + 0x20040080, 0x00040000, 0x01000080, 0x20040080, + 0x21000080, 0x21040000, 0x00040080, 0x20000000, + 0x01000000, 0x20040000, 0x20040000, 0x00000000, + 0x20000080, 0x21040080, 0x21040080, 0x01000080, + 0x21040000, 0x20000080, 0x00000000, 0x21000000, + 0x01040080, 0x01000000, 0x21000000, 0x00040080, + 0x00040000, 0x21000080, 0x00000080, 0x01000000, + 0x20000000, 0x01040000, 0x21000080, 0x20040080, + 0x01000080, 0x20000000, 0x21040000, 0x01040080, + 0x20040080, 0x00000080, 0x01000000, 0x21040000, + 0x21040080, 0x00040080, 0x21000000, 0x21040080, + 0x01040000, 0x00000000, 0x20040000, 0x21000000, + 0x00040080, 0x01000080, 0x20000080, 0x00040000, + 0x00000000, 0x20040000, 0x01040080, 0x20000080 +}; + +static const uint32_t S6[] PROGMEM= { + 0x10000008, 0x10200000, 0x00002000, 0x10202008, + 0x10200000, 0x00000008, 0x10202008, 0x00200000, + 0x10002000, 0x00202008, 0x00200000, 0x10000008, + 0x00200008, 0x10002000, 0x10000000, 0x00002008, + 0x00000000, 0x00200008, 0x10002008, 0x00002000, + 0x00202000, 0x10002008, 0x00000008, 0x10200008, + 0x10200008, 0x00000000, 0x00202008, 0x10202000, + 0x00002008, 0x00202000, 0x10202000, 0x10000000, + 0x10002000, 0x00000008, 0x10200008, 0x00202000, + 0x10202008, 0x00200000, 0x00002008, 0x10000008, + 0x00200000, 0x10002000, 0x10000000, 0x00002008, + 0x10000008, 0x10202008, 0x00202000, 0x10200000, + 0x00202008, 0x10202000, 0x00000000, 0x10200008, + 0x00000008, 0x00002000, 0x10200000, 0x00202008, + 0x00002000, 0x00200008, 0x10002008, 0x00000000, + 0x10202000, 0x10000000, 0x00200008, 0x10002008 +}; + +static const uint32_t S7[] PROGMEM= { + 0x00100000, 0x02100001, 0x02000401, 0x00000000, + 0x00000400, 0x02000401, 0x00100401, 0x02100400, + 0x02100401, 0x00100000, 0x00000000, 0x02000001, + 0x00000001, 0x02000000, 0x02100001, 0x00000401, + 0x02000400, 0x00100401, 0x00100001, 0x02000400, + 0x02000001, 0x02100000, 0x02100400, 0x00100001, + 0x02100000, 0x00000400, 0x00000401, 0x02100401, + 0x00100400, 0x00000001, 0x02000000, 0x00100400, + 0x02000000, 0x00100400, 0x00100000, 0x02000401, + 0x02000401, 0x02100001, 0x02100001, 0x00000001, + 0x00100001, 0x02000000, 0x02000400, 0x00100000, + 0x02100400, 0x00000401, 0x00100401, 0x02100400, + 0x00000401, 0x02000001, 0x02100401, 0x02100000, + 0x00100400, 0x00000000, 0x00000001, 0x02100401, + 0x00000000, 0x00100401, 0x02100000, 0x00000400, + 0x02000001, 0x02000400, 0x00000400, 0x00100001 +}; + +static const uint32_t S8[] PROGMEM = { + 0x08000820, 0x00000800, 0x00020000, 0x08020820, + 0x08000000, 0x08000820, 0x00000020, 0x08000000, + 0x00020020, 0x08020000, 0x08020820, 0x00020800, + 0x08020800, 0x00020820, 0x00000800, 0x00000020, + 0x08020000, 0x08000020, 0x08000800, 0x00000820, + 0x00020800, 0x00020020, 0x08020020, 0x08020800, + 0x00000820, 0x00000000, 0x00000000, 0x08020020, + 0x08000020, 0x08000800, 0x00020820, 0x00020000, + 0x00020820, 0x00020000, 0x08020800, 0x00000800, + 0x00000020, 0x08020020, 0x00000800, 0x00020820, + 0x08000800, 0x00000020, 0x08000020, 0x08020000, + 0x08020020, 0x08000000, 0x00020000, 0x08000820, + 0x00000000, 0x08020820, 0x00020020, 0x08000020, + 0x08020000, 0x08000800, 0x08000820, 0x00000000, + 0x08020820, 0x00020800, 0x00020800, 0x00000820, + 0x00000820, 0x00020020, 0x08000000, 0x08020800 +}; + +static inline uint32_t +Fconf(uint32_t r0, uint32_t skl, uint32_t skr) +{ + uint32_t r1; + + r1 = (r0 << 16) | (r0 >> 16); + return + S1[((r1 >> 11) ^ (skl >> 18)) & 0x3F] + | S2[((r0 >> 23) ^ (skl >> 12)) & 0x3F] + | S3[((r0 >> 19) ^ (skl >> 6)) & 0x3F] + | S4[((r0 >> 15) ^ (skl )) & 0x3F] + | S5[((r0 >> 11) ^ (skr >> 18)) & 0x3F] + | S6[((r0 >> 7) ^ (skr >> 12)) & 0x3F] + | S7[((r0 >> 3) ^ (skr >> 6)) & 0x3F] + | S8[((r1 >> 15) ^ (skr )) & 0x3F]; +} + +static void +process_block_unit(uint32_t *pl, uint32_t *pr, const uint32_t *skey) +{ + int i; + uint32_t l, r; + + l = *pl; + r = *pr; + for (i = 0; i < 16; i ++) { + uint32_t t; + + t = l ^ Fconf(r, skey[(i << 1) + 0], skey[(i << 1) + 1]); + l = r; + r = t; + } + *pl = r; + *pr = l; +} + +/* see inner.h */ +void +br_des_tab_process_block(unsigned num_rounds, const uint32_t *skey, void *block) +{ + unsigned char *buf; + uint32_t l, r; + + buf = block; + l = br_dec32be(buf); + r = br_dec32be(buf + 4); + br_des_do_IP(&l, &r); + while (num_rounds -- > 0) { + process_block_unit(&l, &r, skey); + skey += 32; + } + br_des_do_invIP(&l, &r); + br_enc32be(buf, l); + br_enc32be(buf + 4, r); +} + +static void +keysched_unit(uint32_t *skey, const void *key) +{ + int i; + + br_des_keysched_unit(skey, key); + + /* + * Apply PC-2 to get the 48-bit subkeys. + */ + for (i = 0; i < 16; i ++) { + uint32_t xl, xr, ul, ur; + int j; + + xl = skey[(i << 1) + 0]; + xr = skey[(i << 1) + 1]; + ul = 0; + ur = 0; + for (j = 0; j < 28; j ++) { + ul |= (xl & 1) << PC2left[j]; + ur |= (xr & 1) << PC2right[j]; + xl >>= 1; + xr >>= 1; + } + skey[(i << 1) + 0] = ul; + skey[(i << 1) + 1] = ur; + } +} + +/* see inner.h */ +unsigned +br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len) +{ + switch (key_len) { + case 8: + keysched_unit(skey, key); + return 1; + case 16: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + memcpy(skey + 64, skey, 32 * sizeof *skey); + return 3; + default: + keysched_unit(skey, key); + keysched_unit(skey + 32, (const unsigned char *)key + 8); + br_des_rev_skey(skey + 32); + keysched_unit(skey + 64, (const unsigned char *)key + 16); + return 3; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c new file mode 100644 index 0000000000..647db004e5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcdec.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_tab_cbcdec_vtable; + ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len); + if (len == 8) { + br_des_rev_skey(ctx->skey); + } else { + int i; + + for (i = 0; i < 48; i += 2) { + uint32_t t; + + t = ctx->skey[i]; + ctx->skey[i] = ctx->skey[94 - i]; + ctx->skey[94 - i] = t; + t = ctx->skey[i + 1]; + ctx->skey[i + 1] = ctx->skey[95 - i]; + ctx->skey[95 - i] = t; + } + } +} + +/* see bearssl_block.h */ +void +br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + unsigned char tmp[8]; + int i; + + memcpy(tmp, buf, 8); + br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf); + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + memcpy(ivbuf, tmp, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcdec_class br_des_tab_cbcdec_vtable PROGMEM = { + sizeof(br_des_tab_cbcdec_keys), + 8, + 3, + (void (*)(const br_block_cbcdec_class **, const void *, size_t)) + &br_des_tab_cbcdec_init, + (void (*)(const br_block_cbcdec_class *const *, void *, void *, size_t)) + &br_des_tab_cbcdec_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c new file mode 100644 index 0000000000..a7ecee89cd --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/des_tab_cbcenc.c @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_block.h */ +void +br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx, + const void *key, size_t len) +{ + ctx->vtable = &br_des_tab_cbcenc_vtable; + ctx->num_rounds = br_des_tab_keysched(ctx->skey, key, len); +} + +/* see bearssl_block.h */ +void +br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx, + void *iv, void *data, size_t len) +{ + unsigned char *buf, *ivbuf; + + ivbuf = iv; + buf = data; + while (len > 0) { + int i; + + for (i = 0; i < 8; i ++) { + buf[i] ^= ivbuf[i]; + } + br_des_tab_process_block(ctx->num_rounds, ctx->skey, buf); + memcpy(ivbuf, buf, 8); + buf += 8; + len -= 8; + } +} + +/* see bearssl_block.h */ +const br_block_cbcenc_class br_des_tab_cbcenc_vtable PROGMEM = { + sizeof(br_des_tab_cbcenc_keys), + 8, + 3, + (void (*)(const br_block_cbcenc_class **, const void *, size_t)) + &br_des_tab_cbcenc_init, + (void (*)(const br_block_cbcenc_class *const *, void *, void *, size_t)) + &br_des_tab_cbcenc_run +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c new file mode 100644 index 0000000000..868e12f2bb --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul.c @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Perform the inner processing of blocks for Poly1305. The accumulator + * and the r key are provided as arrays of 26-bit words (these words + * are allowed to have an extra bit, i.e. use 27 bits). + * + * On output, all accumulator words fit on 26 bits, except acc[1], which + * may be slightly larger (but by a very small amount only). + */ +static void +poly1305_inner(uint32_t *acc, const uint32_t *r, const void *data, size_t len) +{ + /* + * Implementation notes: we split the 130-bit values into five + * 26-bit words. This gives us some space for carries. + * + * This code is inspired from the public-domain code available + * on: + * https://github.com/floodyberry/poly1305-donna + * + * Since we compute modulo 2^130-5, the "upper words" become + * low words with a factor of 5; that is, x*2^130 = x*5 mod p. + */ + const unsigned char *buf; + uint32_t a0, a1, a2, a3, a4; + uint32_t r0, r1, r2, r3, r4; + uint32_t u1, u2, u3, u4; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + r3 = r[3]; + r4 = r[4]; + + u1 = r1 * 5; + u2 = r2 * 5; + u3 = r3 * 5; + u4 = r4 * 5; + + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + a3 = acc[3]; + a4 = acc[4]; + + buf = data; + while (len > 0) { + uint64_t w0, w1, w2, w3, w4; + uint64_t c; + unsigned char tmp[16]; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit"; that value + * is added to the accumulator. + */ + a0 += br_dec32le(buf) & 0x03FFFFFF; + a1 += (br_dec32le(buf + 3) >> 2) & 0x03FFFFFF; + a2 += (br_dec32le(buf + 6) >> 4) & 0x03FFFFFF; + a3 += (br_dec32le(buf + 9) >> 6) & 0x03FFFFFF; + a4 += (br_dec32le(buf + 12) >> 8) | 0x01000000; + + /* + * Compute multiplication. + */ +#define M(x, y) ((uint64_t)(x) * (uint64_t)(y)) + + w0 = M(a0, r0) + M(a1, u4) + M(a2, u3) + M(a3, u2) + M(a4, u1); + w1 = M(a0, r1) + M(a1, r0) + M(a2, u4) + M(a3, u3) + M(a4, u2); + w2 = M(a0, r2) + M(a1, r1) + M(a2, r0) + M(a3, u4) + M(a4, u3); + w3 = M(a0, r3) + M(a1, r2) + M(a2, r1) + M(a3, r0) + M(a4, u4); + w4 = M(a0, r4) + M(a1, r3) + M(a2, r2) + M(a3, r1) + M(a4, r0); + +#undef M + /* + * Perform some (partial) modular reduction. This step is + * enough to keep values in ranges such that there won't + * be carry overflows. Most of the reduction was done in + * the multiplication step (by using the 'u*' values, and + * using the fact that 2^130 = -5 mod p); here we perform + * some carry propagation. + */ + c = w0 >> 26; + a0 = (uint32_t)w0 & 0x3FFFFFF; + w1 += c; + c = w1 >> 26; + a1 = (uint32_t)w1 & 0x3FFFFFF; + w2 += c; + c = w2 >> 26; + a2 = (uint32_t)w2 & 0x3FFFFFF; + w3 += c; + c = w3 >> 26; + a3 = (uint32_t)w3 & 0x3FFFFFF; + w4 += c; + c = w4 >> 26; + a4 = (uint32_t)w4 & 0x3FFFFFF; + a0 += (uint32_t)c * 5; + a1 += a0 >> 26; + a0 &= 0x3FFFFFF; + + buf += 16; + len -= 16; + } + + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; + acc[3] = a3; + acc[4] = a4; +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmul_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint32_t r[5], acc[5], cc, ctl, hi; + uint64_t w; + int i; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Decode the 'r' value into 26-bit words, with the "clamping" + * operation applied. + */ + r[0] = br_dec32le(pkey) & 0x03FFFFFF; + r[1] = (br_dec32le(pkey + 3) >> 2) & 0x03FFFF03; + r[2] = (br_dec32le(pkey + 6) >> 4) & 0x03FFC0FF; + r[3] = (br_dec32le(pkey + 9) >> 6) & 0x03F03FFF; + r[4] = (br_dec32le(pkey + 12) >> 8) & 0x000FFFFF; + + /* + * Accumulator is 0. + */ + memset(acc, 0, sizeof acc); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. This is done with carry propagation + * and applying the '2^130 = -5 mod p' rule. Note that the output + * of poly1035_inner() is already mostly reduced, since only + * acc[1] may be (very slightly) above 2^26. A single loop back + * to acc[1] will be enough to make the value fit in 130 bits. + */ + cc = 0; + for (i = 1; i <= 6; i ++) { + int j; + + j = (i >= 5) ? i - 5 : i; + acc[j] += cc; + cc = acc[j] >> 26; + acc[j] &= 0x03FFFFFF; + } + + /* + * We may still have a value in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p', + */ + ctl = GT(acc[0], 0x03FFFFFA); + for (i = 1; i < 5; i ++) { + ctl &= EQ(acc[i], 0x03FFFFFF); + } + cc = 5; + for (i = 0; i < 5; i ++) { + uint32_t t; + + t = (acc[i] + cc); + cc = t >> 26; + t &= 0x03FFFFFF; + acc[i] = MUX(ctl, t, acc[i]); + } + + /* + * Convert back the accumulator to 32-bit words, and add the + * 's' value (second half of pkey[]). That addition is done + * modulo 2^128. + */ + w = (uint64_t)acc[0] + ((uint64_t)acc[1] << 26) + br_dec32le(pkey + 16); + br_enc32le((unsigned char *)tag, (uint32_t)w); + w = (w >> 32) + ((uint64_t)acc[2] << 20) + br_dec32le(pkey + 20); + br_enc32le((unsigned char *)tag + 4, (uint32_t)w); + w = (w >> 32) + ((uint64_t)acc[3] << 14) + br_dec32le(pkey + 24); + br_enc32le((unsigned char *)tag + 8, (uint32_t)w); + hi = (uint32_t)(w >> 32) + (acc[4] << 8) + br_dec32le(pkey + 28); + br_enc32le((unsigned char *)tag + 12, hi); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c new file mode 100644 index 0000000000..c2d90941c8 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmul32.c @@ -0,0 +1,297 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * Perform the inner processing of blocks for Poly1305. + */ +static void +poly1305_inner(uint32_t *a, const uint32_t *r, const void *data, size_t len) +{ + /* + * Implementation notes: we split the 130-bit values into ten + * 13-bit words. This gives us some space for carries and allows + * using only 32x32->32 multiplications, which are way faster than + * 32x32->64 multiplications on the ARM Cortex-M0/M0+, and also + * help in making constant-time code on the Cortex-M3. + * + * Since we compute modulo 2^130-5, the "upper words" become + * low words with a factor of 5; that is, x*2^130 = x*5 mod p. + * This has already been integrated in the r[] array, which + * is extended to the 0..18 range. + * + * In each loop iteration, a[] and r[] words are 13-bit each, + * except a[1] which may use 14 bits. + */ + const unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16]; + uint32_t b[10]; + unsigned u, v; + uint32_t z, cc1, cc2; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit"; that value + * is added to the accumulator. + */ + v = br_dec16le(buf); + a[0] += v & 0x01FFF; + v >>= 13; + v |= buf[2] << 3; + v |= buf[3] << 11; + a[1] += v & 0x01FFF; + v >>= 13; + v |= buf[4] << 6; + a[2] += v & 0x01FFF; + v >>= 13; + v |= buf[5] << 1; + v |= buf[6] << 9; + a[3] += v & 0x01FFF; + v >>= 13; + v |= buf[7] << 4; + v |= buf[8] << 12; + a[4] += v & 0x01FFF; + v >>= 13; + v |= buf[9] << 7; + a[5] += v & 0x01FFF; + v >>= 13; + v |= buf[10] << 2; + v |= buf[11] << 10; + a[6] += v & 0x01FFF; + v >>= 13; + v |= buf[12] << 5; + a[7] += v & 0x01FFF; + v = br_dec16le(buf + 13); + a[8] += v & 0x01FFF; + v >>= 13; + v |= buf[15] << 3; + a[9] += v | 0x00800; + + /* + * At that point, all a[] values fit on 14 bits, while + * all r[] values fit on 13 bits. Thus products fit on + * 27 bits, and we can accumulate up to 31 of them in + * a 32-bit word and still have some room for carries. + */ + + /* + * Now a[] contains words with values up to 14 bits each. + * We perform the multiplication with r[]. + * + * The extended words of r[] may be larger than 13 bits + * (they are 5 times a 13-bit word) so the full summation + * may yield values up to 46 times a 27-bit word, which + * does not fit on a 32-bit word. To avoid that issue, we + * must split the loop below in two, with a carry + * propagation operation in the middle. + */ + cc1 = 0; + for (u = 0; u < 10; u ++) { + uint32_t s; + + s = cc1 + + MUL15(a[0], r[u + 9 - 0]) + + MUL15(a[1], r[u + 9 - 1]) + + MUL15(a[2], r[u + 9 - 2]) + + MUL15(a[3], r[u + 9 - 3]) + + MUL15(a[4], r[u + 9 - 4]); + b[u] = s & 0x1FFF; + cc1 = s >> 13; + } + cc2 = 0; + for (u = 0; u < 10; u ++) { + uint32_t s; + + s = b[u] + cc2 + + MUL15(a[5], r[u + 9 - 5]) + + MUL15(a[6], r[u + 9 - 6]) + + MUL15(a[7], r[u + 9 - 7]) + + MUL15(a[8], r[u + 9 - 8]) + + MUL15(a[9], r[u + 9 - 9]); + b[u] = s & 0x1FFF; + cc2 = s >> 13; + } + memcpy(a, b, sizeof b); + + /* + * The two carries "loop back" with a factor of 5. We + * propagate them into a[0] and a[1]. + */ + z = cc1 + cc2; + z += (z << 2) + a[0]; + a[0] = z & 0x1FFF; + a[1] += z >> 13; + + buf += 16; + len -= 16; + } +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmul32_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint32_t z, r[19], acc[10], cc, ctl; + int i; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Decode the 'r' value into 13-bit words, with the "clamping" + * operation applied. + */ + z = br_dec32le(pkey) & 0x03FFFFFF; + r[9] = z & 0x1FFF; + r[10] = z >> 13; + z = (br_dec32le(pkey + 3) >> 2) & 0x03FFFF03; + r[11] = z & 0x1FFF; + r[12] = z >> 13; + z = (br_dec32le(pkey + 6) >> 4) & 0x03FFC0FF; + r[13] = z & 0x1FFF; + r[14] = z >> 13; + z = (br_dec32le(pkey + 9) >> 6) & 0x03F03FFF; + r[15] = z & 0x1FFF; + r[16] = z >> 13; + z = (br_dec32le(pkey + 12) >> 8) & 0x000FFFFF; + r[17] = z & 0x1FFF; + r[18] = z >> 13; + + /* + * Extend r[] with the 5x factor pre-applied. + */ + for (i = 0; i < 9; i ++) { + r[i] = MUL15(5, r[i + 10]); + } + + /* + * Accumulator is 0. + */ + memset(acc, 0, sizeof acc); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. This is done with carry propagation + * and applying the '2^130 = -5 mod p' rule. Note that the output + * of poly1035_inner() is already mostly reduced, since only + * acc[1] may be (very slightly) above 2^13. A single loop back + * to acc[1] will be enough to make the value fit in 130 bits. + */ + cc = 0; + for (i = 1; i < 10; i ++) { + z = acc[i] + cc; + acc[i] = z & 0x1FFF; + cc = z >> 13; + } + z = acc[0] + cc + (cc << 2); + acc[0] = z & 0x1FFF; + acc[1] += z >> 13; + + /* + * We may still have a value in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p', + */ + ctl = GT(acc[0], 0x1FFA); + for (i = 1; i < 10; i ++) { + ctl &= EQ(acc[i], 0x1FFF); + } + acc[0] = MUX(ctl, acc[0] - 0x1FFB, acc[0]); + for (i = 1; i < 10; i ++) { + acc[i] &= ~(-ctl); + } + + /* + * Convert back the accumulator to 32-bit words, and add the + * 's' value (second half of pkey[]). That addition is done + * modulo 2^128. + */ + z = acc[0] + (acc[1] << 13) + br_dec16le(pkey + 16); + br_enc16le((unsigned char *)tag, z & 0xFFFF); + z = (z >> 16) + (acc[2] << 10) + br_dec16le(pkey + 18); + br_enc16le((unsigned char *)tag + 2, z & 0xFFFF); + z = (z >> 16) + (acc[3] << 7) + br_dec16le(pkey + 20); + br_enc16le((unsigned char *)tag + 4, z & 0xFFFF); + z = (z >> 16) + (acc[4] << 4) + br_dec16le(pkey + 22); + br_enc16le((unsigned char *)tag + 6, z & 0xFFFF); + z = (z >> 16) + (acc[5] << 1) + (acc[6] << 14) + br_dec16le(pkey + 24); + br_enc16le((unsigned char *)tag + 8, z & 0xFFFF); + z = (z >> 16) + (acc[7] << 11) + br_dec16le(pkey + 26); + br_enc16le((unsigned char *)tag + 10, z & 0xFFFF); + z = (z >> 16) + (acc[8] << 8) + br_dec16le(pkey + 28); + br_enc16le((unsigned char *)tag + 12, z & 0xFFFF); + z = (z >> 16) + (acc[9] << 5) + br_dec16le(pkey + 30); + br_enc16le((unsigned char *)tag + 14, z & 0xFFFF); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c new file mode 100644 index 0000000000..6b09445ce2 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_ctmulq.c @@ -0,0 +1,475 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +#if BR_INT128 || BR_UMUL128 + +#if BR_INT128 + +#define MUL128(hi, lo, x, y) do { \ + unsigned __int128 mul128tmp; \ + mul128tmp = (unsigned __int128)(x) * (unsigned __int128)(y); \ + (hi) = (uint64_t)(mul128tmp >> 64); \ + (lo) = (uint64_t)mul128tmp; \ + } while (0) + +#elif BR_UMUL128 + +#include + +#define MUL128(hi, lo, x, y) do { \ + (lo) = _umul128((x), (y), &(hi)); \ + } while (0) + +#endif + +#define MASK42 ((uint64_t)0x000003FFFFFFFFFF) +#define MASK44 ((uint64_t)0x00000FFFFFFFFFFF) + +/* + * The "accumulator" word is nominally a 130-bit value. We split it into + * words of 44 bits, each held in a 64-bit variable. + * + * If the current accumulator is a = a0 + a1*W + a2*W^2 (where W = 2^44) + * and r = r0 + r1*W + r2*W^2, then: + * + * a*r = (a0*r0) + * + (a0*r1 + a1*r0) * W + * + (a0*r2 + a1*r1 + a2*r0) * W^2 + * + (a1*r2 + a2*r1) * W^3 + * + (a2*r2) * W^4 + * + * We want to reduce that value modulo p = 2^130-5, so W^3 = 20 mod p, + * and W^4 = 20*W mod p. Thus, if we define u1 = 20*r1 and u2 = 20*r2, + * then the equations above become: + * + * b0 = a0*r0 + a1*u2 + a2*u1 + * b1 = a0*r1 + a1*r0 + a2*u2 + * b2 = a0*r2 + a1*r1 + a2*r0 + * + * In order to make u1 fit in 44 bits, we can change these equations + * into: + * + * b0 = a0*r0 + a1*u2 + a2*t1 + * b1 = a0*r1 + a1*r0 + a2*t2 + * b2 = a0*r2 + a1*r1 + a2*r0 + * + * Where t1 is u1 truncated to 44 bits, and t2 is u2 added to the extra + * bits of u1. Note that since r is clamped down to a 124-bit value, the + * values u2 and t2 fit on 44 bits too. + * + * The bx values are larger than 44 bits, so we may split them into a + * lower half (cx, 44 bits) and an upper half (dx). The new values for + * the accumulator are then: + * + * e0 = c0 + 20*d2 + * e1 = c1 + d0 + * e2 = c2 + d1 + * + * The equations allow for some room, i.e. the ax values may be larger + * than 44 bits. Similarly, the ex values will usually be larger than + * the ax. Thus, some sort of carry propagation must be done regularly, + * though not necessarily at each iteration. In particular, we do not + * need to compute the additions (for the bx values) over 128-bit + * quantities; we can stick to 64-bit computations. + * + * + * Since the 128-bit result of a 64x64 multiplication is actually + * represented over two 64-bit registers, it is cheaper to arrange for + * any split that happens between the "high" and "low" halves to be on + * that 64-bit boundary. This is done by left shifting the rx, ux and tx + * by 20 bits (since they all fit on 44 bits each, this shift is + * always possible). + */ + +static void +poly1305_inner_big(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + +#define MX(hi, lo, m0, m1, m2) do { \ + uint64_t mxhi, mxlo; \ + MUL128(mxhi, mxlo, a0, m0); \ + (hi) = mxhi; \ + (lo) = mxlo >> 20; \ + MUL128(mxhi, mxlo, a1, m1); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + MUL128(mxhi, mxlo, a2, m2); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + } while (0) + + const unsigned char *buf; + uint64_t a0, a1, a2; + uint64_t r0, r1, r2, t1, t2, u2; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + t1 = r[3]; + t2 = r[4]; + u2 = r[5]; + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + buf = data; + + while (len > 0) { + uint64_t v0, v1, v2; + uint64_t c0, c1, c2, d0, d1, d2; + + v0 = br_dec64le(buf + 0); + v1 = br_dec64le(buf + 8); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 16); + v1 = br_dec64le(buf + 24); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 32); + v1 = br_dec64le(buf + 40); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + v0 = br_dec64le(buf + 48); + v1 = br_dec64le(buf + 56); + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + a1 += a0 >> 44; + a0 &= MASK44; + a2 += a1 >> 44; + a1 &= MASK44; + a0 += 20 * (a2 >> 44); + a2 &= MASK44; + + buf += 64; + len -= 64; + } + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; + +#undef MX +} + +static void +poly1305_inner_small(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + const unsigned char *buf; + uint64_t a0, a1, a2; + uint64_t r0, r1, r2, t1, t2, u2; + + r0 = r[0]; + r1 = r[1]; + r2 = r[2]; + t1 = r[3]; + t2 = r[4]; + u2 = r[5]; + a0 = acc[0]; + a1 = acc[1]; + a2 = acc[2]; + buf = data; + + while (len > 0) { + uint64_t v0, v1, v2; + uint64_t c0, c1, c2, d0, d1, d2; + unsigned char tmp[16]; + + if (len < 16) { + memcpy(tmp, buf, len); + memset(tmp + len, 0, (sizeof tmp) - len); + buf = tmp; + len = 16; + } + v0 = br_dec64le(buf + 0); + v1 = br_dec64le(buf + 8); + + v2 = v1 >> 24; + v1 = ((v0 >> 44) | (v1 << 20)) & MASK44; + v0 &= MASK44; + + a0 += v0; + a1 += v1; + a2 += v2 + ((uint64_t)1 << 40); + +#define MX(hi, lo, m0, m1, m2) do { \ + uint64_t mxhi, mxlo; \ + MUL128(mxhi, mxlo, a0, m0); \ + (hi) = mxhi; \ + (lo) = mxlo >> 20; \ + MUL128(mxhi, mxlo, a1, m1); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + MUL128(mxhi, mxlo, a2, m2); \ + (hi) += mxhi; \ + (lo) += mxlo >> 20; \ + } while (0) + + MX(d0, c0, r0, u2, t1); + MX(d1, c1, r1, r0, t2); + MX(d2, c2, r2, r1, r0); + +#undef MX + + a0 = c0 + 20 * d2; + a1 = c1 + d0; + a2 = c2 + d1; + + a1 += a0 >> 44; + a0 &= MASK44; + a2 += a1 >> 44; + a1 &= MASK44; + a0 += 20 * (a2 >> 44); + a2 &= MASK44; + + buf += 16; + len -= 16; + } + acc[0] = a0; + acc[1] = a1; + acc[2] = a2; +} + +static inline void +poly1305_inner(uint64_t *acc, uint64_t *r, const void *data, size_t len) +{ + if (len >= 64) { + size_t len2; + + len2 = len & ~(size_t)63; + poly1305_inner_big(acc, r, data, len2); + data = (const unsigned char *)data + len2; + len -= len2; + } + if (len > 0) { + poly1305_inner_small(acc, r, data, len); + } +} + +/* see bearssl_block.h */ +void +br_poly1305_ctmulq_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint64_t r[6], acc[3], r0, r1; + uint32_t v0, v1, v2, v3, v4; + uint64_t w0, w1, w2, w3; + uint32_t ctl; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Apply the "clamping" on r. + */ + pkey[ 3] &= 0x0F; + pkey[ 4] &= 0xFC; + pkey[ 7] &= 0x0F; + pkey[ 8] &= 0xFC; + pkey[11] &= 0x0F; + pkey[12] &= 0xFC; + pkey[15] &= 0x0F; + + /* + * Decode the 'r' value into 44-bit words, left-shifted by 20 bits. + * Also compute the u1 and u2 values. + */ + r0 = br_dec64le(pkey + 0); + r1 = br_dec64le(pkey + 8); + r[0] = r0 << 20; + r[1] = ((r0 >> 24) | (r1 << 40)) & ~(uint64_t)0xFFFFF; + r[2] = (r1 >> 4) & ~(uint64_t)0xFFFFF; + r1 = 20 * (r[1] >> 20); + r[3] = r1 << 20; + r[5] = 20 * r[2]; + r[4] = (r[5] + (r1 >> 24)) & ~(uint64_t)0xFFFFF; + + /* + * Accumulator is 0. + */ + acc[0] = 0; + acc[1] = 0; + acc[2] = 0; + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner_small(acc, r, foot, sizeof foot); + + /* + * Finalise modular reduction. At that point, the value consists + * in three 44-bit values (the lowest one might be slightly above + * 2^44). Two loops shall be sufficient. + */ + acc[1] += (acc[0] >> 44); + acc[0] &= MASK44; + acc[2] += (acc[1] >> 44); + acc[1] &= MASK44; + acc[0] += 5 * (acc[2] >> 42); + acc[2] &= MASK42; + acc[1] += (acc[0] >> 44); + acc[0] &= MASK44; + acc[2] += (acc[1] >> 44); + acc[1] &= MASK44; + acc[0] += 5 * (acc[2] >> 42); + acc[2] &= MASK42; + + /* + * The value may still fall in the 2^130-5..2^130-1 range, in + * which case we must reduce it again. The code below selects, + * in constant-time, between 'acc' and 'acc-p'. We encode the + * value over four 32-bit integers to finish the operation. + */ + v0 = (uint32_t)acc[0]; + v1 = (uint32_t)(acc[0] >> 32) | ((uint32_t)acc[1] << 12); + v2 = (uint32_t)(acc[1] >> 20) | ((uint32_t)acc[2] << 24); + v3 = (uint32_t)(acc[2] >> 8); + v4 = (uint32_t)(acc[2] >> 40); + + ctl = GT(v0, 0xFFFFFFFA); + ctl &= EQ(v1, 0xFFFFFFFF); + ctl &= EQ(v2, 0xFFFFFFFF); + ctl &= EQ(v3, 0xFFFFFFFF); + ctl &= EQ(v4, 0x00000003); + v0 = MUX(ctl, v0 + 5, v0); + v1 = MUX(ctl, 0, v1); + v2 = MUX(ctl, 0, v2); + v3 = MUX(ctl, 0, v3); + + /* + * Add the "s" value. This is done modulo 2^128. Don't forget + * carry propagation... + */ + w0 = (uint64_t)v0 + (uint64_t)br_dec32le(pkey + 16); + w1 = (uint64_t)v1 + (uint64_t)br_dec32le(pkey + 20) + (w0 >> 32); + w2 = (uint64_t)v2 + (uint64_t)br_dec32le(pkey + 24) + (w1 >> 32); + w3 = (uint64_t)v3 + (uint64_t)br_dec32le(pkey + 28) + (w2 >> 32); + v0 = (uint32_t)w0; + v1 = (uint32_t)w1; + v2 = (uint32_t)w2; + v3 = (uint32_t)w3; + + /* + * Encode the tag. + */ + br_enc32le((unsigned char *)tag + 0, v0); + br_enc32le((unsigned char *)tag + 4, v1); + br_enc32le((unsigned char *)tag + 8, v2); + br_enc32le((unsigned char *)tag + 12, v3); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} + +/* see bearssl_block.h */ +br_poly1305_run +br_poly1305_ctmulq_get(void) +{ + return &br_poly1305_ctmulq_run; +} + +#else + +/* see bearssl_block.h */ +br_poly1305_run +br_poly1305_ctmulq_get(void) +{ + return 0; +} + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c new file mode 100644 index 0000000000..684074189e --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/symcipher/poly1305_i15.c @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* + * This is a "reference" implementation of Poly1305 that uses the + * generic "i15" code for big integers. It is slow, but it handles all + * big-integer operations with generic code, thereby avoiding most + * tricky situations with carry propagation and modular reduction. + */ + +/* + * Modulus: 2^130-5. + */ +static const uint16_t P1305[] = { + 0x008A, + 0x7FFB, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x7FFF, 0x03FF +}; + +/* + * -p mod 2^15. + */ +#define P0I 0x4CCD + +/* + * R^2 mod p, for conversion to Montgomery representation (R = 2^135, + * since we use 9 words of 15 bits each, and 15*9 = 135). + */ +static const uint16_t R2[] = { + 0x008A, + 0x6400, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 +}; + +/* + * Perform the inner processing of blocks for Poly1305. The "r" array + * is in Montgomery representation, while the "a" array is not. + */ +static void +poly1305_inner(uint16_t *a, const uint16_t *r, const void *data, size_t len) +{ + const unsigned char *buf; + + buf = data; + while (len > 0) { + unsigned char tmp[16], rev[16]; + uint16_t b[10]; + uint32_t ctl; + int i; + + /* + * If there is a partial block, right-pad it with zeros. + */ + if (len < 16) { + memset(tmp, 0, sizeof tmp); + memcpy(tmp, buf, len); + buf = tmp; + len = 16; + } + + /* + * Decode next block and apply the "high bit". Since + * decoding is little-endian, we must byte-swap the buffer. + */ + for (i = 0; i < 16; i ++) { + rev[i] = buf[15 - i]; + } + br_i15_decode_mod(b, rev, sizeof rev, P1305); + b[9] |= 0x0100; + + /* + * Add the accumulator to the decoded block (modular + * addition). + */ + ctl = br_i15_add(b, a, 1); + ctl |= NOT(br_i15_sub(b, P1305, 0)); + br_i15_sub(b, P1305, ctl); + + /* + * Multiply by r, result is the new accumulator value. + */ + br_i15_montymul(a, b, r, P1305, P0I); + + buf += 16; + len -= 16; + } +} + +/* + * Byteswap a 16-byte value. + */ +static void +byteswap16(unsigned char *buf) +{ + int i; + + for (i = 0; i < 8; i ++) { + unsigned x; + + x = buf[i]; + buf[i] = buf[15 - i]; + buf[15 - i] = x; + } +} + +/* see bearssl_block.h */ +void +br_poly1305_i15_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt) +{ + unsigned char pkey[32], foot[16]; + uint16_t t[10], r[10], acc[10]; + + /* + * Compute the MAC key. The 'r' value is the first 16 bytes of + * pkey[]. + */ + memset(pkey, 0, sizeof pkey); + ichacha(key, iv, 0, pkey, sizeof pkey); + + /* + * If encrypting, ChaCha20 must run first, followed by Poly1305. + * When decrypting, the operations are reversed. + */ + if (encrypt) { + ichacha(key, iv, 1, data, len); + } + + /* + * Run Poly1305. We must process the AAD, then ciphertext, then + * the footer (with the lengths). Note that the AAD and ciphertext + * are meant to be padded with zeros up to the next multiple of 16, + * and the length of the footer is 16 bytes as well. + */ + + /* + * Apply the "clamping" operation on the encoded 'r' value. + */ + pkey[ 3] &= 0x0F; + pkey[ 7] &= 0x0F; + pkey[11] &= 0x0F; + pkey[15] &= 0x0F; + pkey[ 4] &= 0xFC; + pkey[ 8] &= 0xFC; + pkey[12] &= 0xFC; + + /* + * Decode the clamped 'r' value. Decoding should use little-endian + * so we must byteswap the value first. + */ + byteswap16(pkey); + br_i15_decode_mod(t, pkey, 16, P1305); + + /* + * Convert 'r' to Montgomery representation. + */ + br_i15_montymul(r, t, R2, P1305, P0I); + + /* + * Accumulator is 0. + */ + br_i15_zero(acc, 0x8A); + + /* + * Process the additional authenticated data, ciphertext, and + * footer in due order. + */ + br_enc64le(foot, (uint64_t)aad_len); + br_enc64le(foot + 8, (uint64_t)len); + poly1305_inner(acc, r, aad, aad_len); + poly1305_inner(acc, r, data, len); + poly1305_inner(acc, r, foot, sizeof foot); + + /* + * Decode the value 's'. Again, a byteswap is needed. + */ + byteswap16(pkey + 16); + br_i15_decode_mod(t, pkey + 16, 16, P1305); + + /* + * Add the value 's' to the accumulator. That addition is done + * modulo 2^128, so we just ignore the carry. + */ + br_i15_add(acc, t, 1); + + /* + * Encode the result (128 low bits) to the tag. Encoding should + * be little-endian. + */ + br_i15_encode(tag, 16, acc); + byteswap16(tag); + + /* + * If decrypting, then ChaCha20 runs _after_ Poly1305. + */ + if (!encrypt) { + ichacha(key, iv, 1, data, len); + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h new file mode 100644 index 0000000000..50752a682d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl.h @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_H__ +#define BR_BEARSSL_H__ + +#include +#include + +/** \mainpage BearSSL API + * + * # API Layout + * + * The functions and structures defined by the BearSSL API are located + * in various header files: + * + * | Header file | Elements | + * | :-------------- | :------------------------------------------------ | + * | bearssl_hash.h | Hash functions | + * | bearssl_hmac.h | HMAC | + * | bearssl_kdf.h | Key Derivation Functions | + * | bearssl_rand.h | Pseudorandom byte generators | + * | bearssl_prf.h | PRF implementations (for SSL/TLS) | + * | bearssl_block.h | Symmetric encryption | + * | bearssl_aead.h | AEAD algorithms (combined encryption + MAC) | + * | bearssl_rsa.h | RSA encryption and signatures | + * | bearssl_ec.h | Elliptic curves support (including ECDSA) | + * | bearssl_ssl.h | SSL/TLS engine interface | + * | bearssl_x509.h | X.509 certificate decoding and validation | + * | bearssl_pem.h | Base64/PEM decoding support functions | + * + * Applications using BearSSL are supposed to simply include `bearssl.h` + * as follows: + * + * #include + * + * The `bearssl.h` file itself includes all the other header files. It is + * possible to include specific header files, but it has no practical + * advantage for the application. The API is separated into separate + * header files only for documentation convenience. + * + * + * # Conventions + * + * ## MUST and SHALL + * + * In all descriptions, the usual "MUST", "SHALL", "MAY",... terminology + * is used. Failure to meet requirements expressed with a "MUST" or + * "SHALL" implies undefined behaviour, which means that segmentation + * faults, buffer overflows, and other similar adverse events, may occur. + * + * In general, BearSSL is not very forgiving of programming errors, and + * does not include much failsafes or error reporting when the problem + * does not arise from external transient conditions, and can be fixed + * only in the application code. This is done so in order to make the + * total code footprint lighter. + * + * + * ## `NULL` values + * + * Function parameters with a pointer type shall not be `NULL` unless + * explicitly authorised by the documentation. As an exception, when + * the pointer aims at a sequence of bytes and is accompanied with + * a length parameter, and the length is zero (meaning that there is + * no byte at all to retrieve), then the pointer may be `NULL` even if + * not explicitly allowed. + * + * + * ## Memory Allocation + * + * BearSSL does not perform dynamic memory allocation. This implies that + * for any functionality that requires a non-transient state, the caller + * is responsible for allocating the relevant context structure. Such + * allocation can be done in any appropriate area, including static data + * segments, the heap, and the stack, provided that proper alignment is + * respected. The header files define these context structures + * (including size and contents), so the C compiler should handle + * alignment automatically. + * + * Since there is no dynamic resource allocation, there is also nothing to + * release. When the calling code is done with a BearSSL feature, it + * may simple release the context structures it allocated itself, with + * no "close function" to call. If the context structures were allocated + * on the stack (as local variables), then even that release operation is + * implicit. + * + * + * ## Structure Contents + * + * Except when explicitly indicated, structure contents are opaque: they + * are included in the header files so that calling code may know the + * structure sizes and alignment requirements, but callers SHALL NOT + * access individual fields directly. For fields that are supposed to + * be read from or written to, the API defines accessor functions (the + * simplest of these accessor functions are defined as `static inline` + * functions, and the C compiler will optimise them away). + * + * + * # API Usage + * + * BearSSL usage for running a SSL/TLS client or server is described + * on the [BearSSL Web site](https://www.bearssl.org/api1.html). The + * BearSSL source archive also comes with sample code. + */ + +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" +#include "t_bearssl_kdf.h" +#include "t_bearssl_rand.h" +#include "t_bearssl_prf.h" +#include "t_bearssl_block.h" +#include "t_bearssl_aead.h" +#include "t_bearssl_rsa.h" +#include "t_bearssl_ec.h" +#include "t_bearssl_ssl.h" +#include "t_bearssl_x509.h" +#include "t_bearssl_pem.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \brief Type for a configuration option. + * + * A "configuration option" is a value that is selected when the BearSSL + * library itself is compiled. Most options are boolean; their value is + * then either 1 (option is enabled) or 0 (option is disabled). Some + * values have other integer values. Option names correspond to macro + * names. Some of the options can be explicitly set in the internal + * `"t_config.h"` file. + */ +typedef struct { + /** \brief Configurable option name. */ + const char *name; + /** \brief Configurable option value. */ + long value; +} br_config_option; + +/** \brief Get configuration report. + * + * This function returns compiled configuration options, each as a + * 'long' value. Names match internal macro names, in particular those + * that can be set in the `"t_config.h"` inner file. For boolean options, + * the numerical value is 1 if enabled, 0 if disabled. For maximum + * key sizes, values are expressed in bits. + * + * The returned array is terminated by an entry whose `name` is `NULL`. + * + * \return the configuration report. + */ +const br_config_option *br_get_config(void); + +/* ======================================================================= */ + +/** \brief Version feature: support for time callback. */ +#define BR_FEATURE_X509_TIME_CALLBACK 1 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h new file mode 100644 index 0000000000..d4a1e777c2 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_aead.h @@ -0,0 +1,1059 @@ +/* + * Copyright (c) 2017 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_AEAD_H__ +#define BR_BEARSSL_AEAD_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_aead.h + * + * # Authenticated Encryption with Additional Data + * + * This file documents the API for AEAD encryption. + * + * + * ## Procedural API + * + * An AEAD algorithm processes messages and provides confidentiality + * (encryption) and checked integrity (MAC). It uses the following + * parameters: + * + * - A symmetric key. Exact size depends on the AEAD algorithm. + * + * - A nonce (IV). Size depends on the AEAD algorithm; for most + * algorithms, it is crucial for security that any given nonce + * value is never used twice for the same key and distinct + * messages. + * + * - Data to encrypt and protect. + * + * - Additional authenticated data, which is covered by the MAC but + * otherwise left untouched (i.e. not encrypted). + * + * The AEAD algorithm encrypts the data, and produces an authentication + * tag. It is assumed that the encrypted data, the tag, the additional + * authenticated data and the nonce are sent to the receiver; the + * additional data and the nonce may be implicit (e.g. using elements of + * the underlying transport protocol, such as record sequence numbers). + * The receiver will recompute the tag value and compare it with the one + * received; if they match, then the data is correct, and can be + * decrypted and used; otherwise, at least one of the elements was + * altered in transit, normally leading to wholesale rejection of the + * complete message. + * + * For each AEAD algorithm, identified by a symbolic name (hereafter + * denoted as "`xxx`"), the following functions are defined: + * + * - `br_xxx_init()` + * + * Initialise the AEAD algorithm, on a provided context structure. + * Exact parameters depend on the algorithm, and may include + * pointers to extra implementations and context structures. The + * secret key is provided at this point, either directly or + * indirectly. + * + * - `br_xxx_reset()` + * + * Start a new AEAD computation. The nonce value is provided as + * parameter to this function. + * + * - `br_xxx_aad_inject()` + * + * Inject some additional authenticated data. Additional data may + * be provided in several chunks of arbitrary length. + * + * - `br_xxx_flip()` + * + * This function MUST be called after injecting all additional + * authenticated data, and before beginning to encrypt the plaintext + * (or decrypt the ciphertext). + * + * - `br_xxx_run()` + * + * Process some plaintext (to encrypt) or ciphertext (to decrypt). + * Encryption/decryption is done in place. Data may be provided in + * several chunks of arbitrary length. + * + * - `br_xxx_get_tag()` + * + * Compute the authentication tag. All message data (encrypted or + * decrypted) must have been injected at that point. Also, this + * call may modify internal context elements, so it may be called + * only once for a given AEAD computation. + * + * - `br_xxx_check_tag()` + * + * An alternative to `br_xxx_get_tag()`, meant to be used by the + * receiver: the authentication tag is internally recomputed, and + * compared with the one provided as parameter. + * + * This API makes the following assumptions on the AEAD algorithm: + * + * - Encryption does not expand the size of the ciphertext; there is + * no padding. This is true of most modern AEAD modes such as GCM. + * + * - The additional authenticated data must be processed first, + * before the encrypted/decrypted data. + * + * - Nonce, plaintext and additional authenticated data all consist + * in an integral number of bytes. There is no provision to use + * elements whose length in bits is not a multiple of 8. + * + * Each AEAD algorithm has its own requirements and limits on the sizes + * of additional data and plaintext. This API does not provide any + * way to report invalid usage; it is up to the caller to ensure that + * the provided key, nonce, and data elements all fit the algorithm's + * requirements. + * + * + * ## Object-Oriented API + * + * Each context structure begins with a field (called `vtable`) that + * points to an instance of a structure that references the relevant + * functions through pointers. Each such structure contains the + * following: + * + * - `reset` + * + * Pointer to the reset function, that allows starting a new + * computation. + * + * - `aad_inject` + * + * Pointer to the additional authenticated data injection function. + * + * - `flip` + * + * Pointer to the function that transitions from additional data + * to main message data processing. + * + * - `get_tag` + * + * Pointer to the function that computes and returns the tag. + * + * - `check_tag` + * + * Pointer to the function that computes and verifies the tag against + * a received value. + * + * Note that there is no OOP method for context initialisation: the + * various AEAD algorithms have different requirements that would not + * map well to a single initialisation API. + * + * The OOP API is not provided for CCM, due to its specific requirements + * (length of plaintext must be known in advance). + */ + +/** + * \brief Class type of an AEAD algorithm. + */ +typedef struct br_aead_class_ br_aead_class; +struct br_aead_class_ { + + /** + * \brief Size (in bytes) of authentication tags created by + * this AEAD algorithm. + */ + size_t tag_size; + + /** + * \brief Reset an AEAD context. + * + * This function resets an already initialised AEAD context for + * a new computation run. Implementations and keys are + * conserved. This function can be called at any time; it + * cancels any ongoing AEAD computation that uses the provided + * context structure. + + * The provided IV is a _nonce_. Each AEAD algorithm has its + * own requirements on IV size and contents; for most of them, + * it is crucial to security that each nonce value is used + * only once for a given secret key. + * + * \param cc AEAD context structure. + * \param iv AEAD nonce to use. + * \param len AEAD nonce length (in bytes). + */ + void (*reset)(const br_aead_class **cc, const void *iv, size_t len); + + /** + * \brief Inject additional authenticated data. + * + * The provided data is injected into a running AEAD + * computation. Additional data must be injected _before_ the + * call to `flip()`. Additional data can be injected in several + * chunks of arbitrary length. + * + * \param cc AEAD context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ + void (*aad_inject)(const br_aead_class **cc, + const void *data, size_t len); + + /** + * \brief Finish injection of additional authenticated data. + * + * This function MUST be called before beginning the actual + * encryption or decryption (with `run()`), even if no + * additional authenticated data was injected. No additional + * authenticated data may be injected after this function call. + * + * \param cc AEAD context structure. + */ + void (*flip)(const br_aead_class **cc); + + /** + * \brief Encrypt or decrypt some data. + * + * Data encryption or decryption can be done after `flip()` has + * been called on the context. If `encrypt` is non-zero, then + * the provided data shall be plaintext, and it is encrypted in + * place. Otherwise, the data shall be ciphertext, and it is + * decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. + * + * \param cc AEAD context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ + void (*run)(const br_aead_class **cc, int encrypt, + void *data, size_t len); + + /** + * \brief Compute authentication tag. + * + * Compute the AEAD authentication tag. The tag length depends + * on the AEAD algorithm; it is written in the provided `tag` + * buffer. This call terminates the AEAD run: no data may be + * processed with that AEAD context afterwards, until `reset()` + * is called to initiate a new AEAD run. + * + * The tag value must normally be sent along with the encrypted + * data. When decrypting, the tag value must be recomputed and + * compared with the received tag: if the two tag values differ, + * then either the tag or the encrypted data was altered in + * transit. As an alternative to this function, the + * `check_tag()` function may be used to compute and check the + * tag value. + * + * Tag length depends on the AEAD algorithm. + * + * \param cc AEAD context structure. + * \param tag destination buffer for the tag. + */ + void (*get_tag)(const br_aead_class **cc, void *tag); + + /** + * \brief Compute and check authentication tag. + * + * This function is an alternative to `get_tag()`, and is + * normally used on the receiving end (i.e. when decrypting + * messages). The tag value is recomputed and compared with the + * provided tag value. If they match, 1 is returned; on + * mismatch, 0 is returned. A returned value of 0 means that the + * data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length depends on the AEAD algorithm. + * + * \param cc AEAD context structure. + * \param tag tag value to compare with. + * \return 1 on success (exact match of tag value), 0 otherwise. + */ + uint32_t (*check_tag)(const br_aead_class **cc, const void *tag); + + /** + * \brief Compute authentication tag (with truncation). + * + * This function is similar to `get_tag()`, except that the tag + * length is provided. Some AEAD algorithms allow several tag + * lengths, usually by truncating the normal tag. Shorter tags + * mechanically increase success probability of forgeries. + * The range of allowed tag lengths depends on the algorithm. + * + * \param cc AEAD context structure. + * \param tag destination buffer for the tag. + * \param len tag length (in bytes). + */ + void (*get_tag_trunc)(const br_aead_class **cc, void *tag, size_t len); + + /** + * \brief Compute and check authentication tag (with truncation). + * + * This function is similar to `check_tag()` except that it + * works over an explicit tag length. See `get_tag()` for a + * discussion of explicit tag lengths; the range of allowed tag + * lengths depends on the algorithm. + * + * \param cc AEAD context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ + uint32_t (*check_tag_trunc)(const br_aead_class **cc, + const void *tag, size_t len); +}; + +/** + * \brief Context structure for GCM. + * + * GCM is an AEAD mode that combines a block cipher in CTR mode with a + * MAC based on GHASH, to provide authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with GCM. + * + * - The nonce can have any length, from 0 up to 2^64-1 bits; however, + * 96-bit nonces (12 bytes) are recommended (nonces with a length + * distinct from 12 bytes are internally hashed, which risks reusing + * nonce value with a small but not always negligible probability). + * + * - Additional authenticated data may have length up to 2^64-1 bits. + * + * - Message length may range up to 2^39-256 bits at most. + * + * - The authentication tag has length 16 bytes. + * + * The GCM initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * GCM context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_aead_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctr_class **bctx; + br_ghash gh; + unsigned char h[16]; + unsigned char j0_1[12]; + unsigned char buf[16]; + unsigned char y[16]; + uint32_t j0_2, jc; + uint64_t count_aad, count_ctr; +#endif +} br_gcm_context; + +/** + * \brief Initialize a GCM context. + * + * A block cipher implementation, with its initialised context structure, + * is provided. The block cipher MUST use 16-byte blocks in CTR mode, + * and its secret key MUST have been already set in the provided context. + * A GHASH implementation must also be provided. The parameters are linked + * in the GCM context. + * + * After this function has been called, the `br_gcm_reset()` function must + * be called, to provide the IV for GCM computation. + * + * \param ctx GCM context structure. + * \param bctx block cipher context (already initialised with secret key). + * \param gh GHASH implementation. + */ +void br_gcm_init(br_gcm_context *ctx, + const br_block_ctr_class **bctx, br_ghash gh); + +/** + * \brief Reset a GCM context. + * + * This function resets an already initialised GCM context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing GCM computation that + * uses the provided context structure. + * + * The provided IV is a _nonce_. It is critical to GCM security that IV + * values are not repeated for the same encryption key. IV can have + * arbitrary length (up to 2^64-1 bits), but the "normal" length is + * 96 bits (12 bytes). + * + * \param ctx GCM context structure. + * \param iv GCM nonce to use. + * \param len GCM nonce length (in bytes). + */ +void br_gcm_reset(br_gcm_context *ctx, const void *iv, size_t len); + +/** + * \brief Inject additional authenticated data into GCM. + * + * The provided data is injected into a running GCM computation. Additional + * data must be injected _before_ the call to `br_gcm_flip()`. + * Additional data can be injected in several chunks of arbitrary length; + * the maximum total size of additional authenticated data is 2^64-1 + * bits. + * + * \param ctx GCM context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_gcm_aad_inject(br_gcm_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into GCM. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_gcm_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx GCM context structure. + */ +void br_gcm_flip(br_gcm_context *ctx); + +/** + * \brief Encrypt or decrypt some data with GCM. + * + * Data encryption or decryption can be done after `br_gcm_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. The maximum + * total length for data is 2^39-256 bits, i.e. about 65 gigabytes. + * + * \param ctx GCM context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_gcm_run(br_gcm_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute GCM authentication tag. + * + * Compute the GCM authentication tag. The tag is a 16-byte value which + * is written in the provided `tag` buffer. This call terminates the + * GCM run: no data may be processed with that GCM context afterwards, + * until `br_gcm_reset()` is called to initiate a new GCM run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_gcm_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx GCM context structure. + * \param tag destination buffer for the tag (16 bytes). + */ +void br_gcm_get_tag(br_gcm_context *ctx, void *tag); + +/** + * \brief Compute and check GCM authentication tag. + * + * This function is an alternative to `br_gcm_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx GCM context structure. + * \param tag tag value to compare with (16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_gcm_check_tag(br_gcm_context *ctx, const void *tag); + +/** + * \brief Compute GCM authentication tag (with truncation). + * + * This function is similar to `br_gcm_get_tag()`, except that it allows + * the tag to be truncated to a smaller length. The intended tag length + * is provided as `len` (in bytes); it MUST be no more than 16, but + * it may be smaller. Note that decreasing tag length mechanically makes + * forgeries easier; NIST SP 800-38D specifies that the tag length shall + * lie between 12 and 16 bytes (inclusive), but may be truncated down to + * 4 or 8 bytes, for specific applications that can tolerate it. It must + * also be noted that successful forgeries leak information on the + * authentication key, making subsequent forgeries easier. Therefore, + * tag truncation, and in particular truncation to sizes lower than 12 + * bytes, shall be envisioned only with great care. + * + * The tag is written in the provided `tag` buffer. This call terminates + * the GCM run: no data may be processed with that GCM context + * afterwards, until `br_gcm_reset()` is called to initiate a new GCM + * run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_gcm_check_tag_trunc()` function can be used to + * compute and check the tag value. + * + * \param ctx GCM context structure. + * \param tag destination buffer for the tag. + * \param len tag length (16 bytes or less). + */ +void br_gcm_get_tag_trunc(br_gcm_context *ctx, void *tag, size_t len); + +/** + * \brief Compute and check GCM authentication tag (with truncation). + * + * This function is an alternative to `br_gcm_get_tag_trunc()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length MUST be 16 bytes or less. The normal GCM tag length is 16 + * bytes. See `br_check_tag_trunc()` for some discussion on the potential + * perils of truncating authentication tags. + * + * \param ctx GCM context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_gcm_check_tag_trunc(br_gcm_context *ctx, + const void *tag, size_t len); + +/** + * \brief Class instance for GCM. + */ +extern const br_aead_class br_gcm_vtable; + +/** + * \brief Context structure for EAX. + * + * EAX is an AEAD mode that combines a block cipher in CTR mode with + * CBC-MAC using the same block cipher and the same key, to provide + * authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with EAX + * (technically, other block sizes are defined as well, but this + * is not implemented by these functions; shorter blocks also + * imply numerous security issues). + * + * - The nonce can have any length, as long as nonce values are + * not reused (thus, if nonces are randomly selected, the nonce + * size should be such that reuse probability is negligible). + * + * - Additional authenticated data length is unlimited. + * + * - Message length is unlimited. + * + * - The authentication tag has length 16 bytes. + * + * The EAX initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * EAX context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_aead_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctrcbc_class **bctx; + unsigned char L2[16]; + unsigned char L4[16]; + unsigned char nonce[16]; + unsigned char head[16]; + unsigned char ctr[16]; + unsigned char cbcmac[16]; + unsigned char buf[16]; + size_t ptr; +#endif +} br_eax_context; + +/** + * \brief EAX captured state. + * + * Some internal values computed by EAX may be captured at various + * points, and reused for another EAX run with the same secret key, + * for lower per-message overhead. Captured values do not depend on + * the nonce. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char st[3][16]; +#endif +} br_eax_state; + +/** + * \brief Initialize an EAX context. + * + * A block cipher implementation, with its initialised context + * structure, is provided. The block cipher MUST use 16-byte blocks in + * CTR + CBC-MAC mode, and its secret key MUST have been already set in + * the provided context. The parameters are linked in the EAX context. + * + * After this function has been called, the `br_eax_reset()` function must + * be called, to provide the nonce for EAX computation. + * + * \param ctx EAX context structure. + * \param bctx block cipher context (already initialised with secret key). + */ +void br_eax_init(br_eax_context *ctx, const br_block_ctrcbc_class **bctx); + +/** + * \brief Capture pre-AAD state. + * + * This function precomputes key-dependent data, and stores it in the + * provided `st` structure. This structure should then be used with + * `br_eax_reset_pre_aad()`, or updated with `br_eax_get_aad_mac()` + * and then used with `br_eax_reset_post_aad()`. + * + * The EAX context structure is unmodified by this call. + * + * \param ctx EAX context structure. + * \param st recipient for captured state. + */ +void br_eax_capture(const br_eax_context *ctx, br_eax_state *st); + +/** + * \brief Reset an EAX context. + * + * This function resets an already initialised EAX context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing EAX computation that + * uses the provided context structure. + * + * It is critical to EAX security that nonce values are not repeated for + * the same encryption key. Nonces can have arbitrary length. If nonces + * are randomly generated, then a nonce length of at least 128 bits (16 + * bytes) is recommended, to make nonce reuse probability sufficiently + * low. + * + * \param ctx EAX context structure. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset(br_eax_context *ctx, const void *nonce, size_t len); + +/** + * \brief Reset an EAX context with a pre-AAD captured state. + * + * This function is an alternative to `br_eax_reset()`, that reuses a + * previously captured state structure for lower per-message overhead. + * The state should have been populated with `br_eax_capture_state()` + * but not updated with `br_eax_get_aad_mac()`. + * + * After this function is called, additional authenticated data MUST + * be injected. At least one byte of additional authenticated data + * MUST be provided with `br_eax_aad_inject()`; computation result will + * be incorrect if `br_eax_flip()` is called right away. + * + * After injection of the AAD and call to `br_eax_flip()`, at least + * one message byte must be provided. Empty messages are not supported + * with this reset mode. + * + * \param ctx EAX context structure. + * \param st pre-AAD captured state. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset_pre_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len); + +/** + * \brief Reset an EAX context with a post-AAD captured state. + * + * This function is an alternative to `br_eax_reset()`, that reuses a + * previously captured state structure for lower per-message overhead. + * The state should have been populated with `br_eax_capture_state()` + * and then updated with `br_eax_get_aad_mac()`. + * + * After this function is called, message data MUST be injected. The + * `br_eax_flip()` function MUST NOT be called. At least one byte of + * message data MUST be provided with `br_eax_run()`; empty messages + * are not supported with this reset mode. + * + * \param ctx EAX context structure. + * \param st post-AAD captured state. + * \param nonce EAX nonce to use. + * \param len EAX nonce length (in bytes). + */ +void br_eax_reset_post_aad(br_eax_context *ctx, const br_eax_state *st, + const void *nonce, size_t len); + +/** + * \brief Inject additional authenticated data into EAX. + * + * The provided data is injected into a running EAX computation. Additional + * data must be injected _before_ the call to `br_eax_flip()`. + * Additional data can be injected in several chunks of arbitrary length; + * the total amount of additional authenticated data is unlimited. + * + * \param ctx EAX context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_eax_aad_inject(br_eax_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into EAX. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_eax_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx EAX context structure. + */ +void br_eax_flip(br_eax_context *ctx); + +/** + * \brief Obtain a copy of the MAC on additional authenticated data. + * + * This function may be called only after `br_eax_flip()`; it copies the + * AAD-specific MAC value into the provided state. The MAC value depends + * on the secret key and the additional data itself, but not on the + * nonce. The updated state `st` is meant to be used as parameter for a + * further `br_eax_reset_post_aad()` call. + * + * \param ctx EAX context structure. + * \param st captured state to update. + */ +static inline void +br_eax_get_aad_mac(const br_eax_context *ctx, br_eax_state *st) +{ + memcpy(st->st[1], ctx->head, sizeof ctx->head); +} + +/** + * \brief Encrypt or decrypt some data with EAX. + * + * Data encryption or decryption can be done after `br_eax_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length. + * + * \param ctx EAX context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_eax_run(br_eax_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute EAX authentication tag. + * + * Compute the EAX authentication tag. The tag is a 16-byte value which + * is written in the provided `tag` buffer. This call terminates the + * EAX run: no data may be processed with that EAX context afterwards, + * until `br_eax_reset()` is called to initiate a new EAX run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_eax_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx EAX context structure. + * \param tag destination buffer for the tag (16 bytes). + */ +void br_eax_get_tag(br_eax_context *ctx, void *tag); + +/** + * \brief Compute and check EAX authentication tag. + * + * This function is an alternative to `br_eax_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx EAX context structure. + * \param tag tag value to compare with (16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_eax_check_tag(br_eax_context *ctx, const void *tag); + +/** + * \brief Compute EAX authentication tag (with truncation). + * + * This function is similar to `br_eax_get_tag()`, except that it allows + * the tag to be truncated to a smaller length. The intended tag length + * is provided as `len` (in bytes); it MUST be no more than 16, but + * it may be smaller. Note that decreasing tag length mechanically makes + * forgeries easier; NIST SP 800-38D specifies that the tag length shall + * lie between 12 and 16 bytes (inclusive), but may be truncated down to + * 4 or 8 bytes, for specific applications that can tolerate it. It must + * also be noted that successful forgeries leak information on the + * authentication key, making subsequent forgeries easier. Therefore, + * tag truncation, and in particular truncation to sizes lower than 12 + * bytes, shall be envisioned only with great care. + * + * The tag is written in the provided `tag` buffer. This call terminates + * the EAX run: no data may be processed with that EAX context + * afterwards, until `br_eax_reset()` is called to initiate a new EAX + * run. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_eax_check_tag_trunc()` function can be used to + * compute and check the tag value. + * + * \param ctx EAX context structure. + * \param tag destination buffer for the tag. + * \param len tag length (16 bytes or less). + */ +void br_eax_get_tag_trunc(br_eax_context *ctx, void *tag, size_t len); + +/** + * \brief Compute and check EAX authentication tag (with truncation). + * + * This function is an alternative to `br_eax_get_tag_trunc()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * Tag length MUST be 16 bytes or less. The normal EAX tag length is 16 + * bytes. See `br_check_tag_trunc()` for some discussion on the potential + * perils of truncating authentication tags. + * + * \param ctx EAX context structure. + * \param tag tag value to compare with. + * \param len tag length (in bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_eax_check_tag_trunc(br_eax_context *ctx, + const void *tag, size_t len); + +/** + * \brief Class instance for EAX. + */ +extern const br_aead_class br_eax_vtable; + +/** + * \brief Context structure for CCM. + * + * CCM is an AEAD mode that combines a block cipher in CTR mode with + * CBC-MAC using the same block cipher and the same key, to provide + * authenticated encryption: + * + * - Any block cipher with 16-byte blocks can be used with CCM + * (technically, other block sizes are defined as well, but this + * is not implemented by these functions; shorter blocks also + * imply numerous security issues). + * + * - The authentication tag length, and plaintext length, MUST be + * known when starting processing data. Plaintext and ciphertext + * can still be provided by chunks, but the total size must match + * the value provided upon initialisation. + * + * - The nonce length is constrained between 7 and 13 bytes (inclusive). + * Furthermore, the plaintext length, when encoded, must fit over + * 15-nonceLen bytes; thus, if the nonce has length 13 bytes, then + * the plaintext length cannot exceed 65535 bytes. + * + * - Additional authenticated data length is practically unlimited + * (formal limit is at 2^64 bytes). + * + * - The authentication tag has length 4 to 16 bytes (even values only). + * + * The CCM initialisation function receives as parameter an + * _initialised_ block cipher implementation context, with the secret + * key already set. A pointer to that context will be kept within the + * CCM context structure. It is up to the caller to allocate and + * initialise that block cipher context. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + const br_block_ctrcbc_class **bctx; + unsigned char ctr[16]; + unsigned char cbcmac[16]; + unsigned char tagmask[16]; + unsigned char buf[16]; + size_t ptr; + size_t tag_len; +#endif +} br_ccm_context; + +/** + * \brief Initialize a CCM context. + * + * A block cipher implementation, with its initialised context + * structure, is provided. The block cipher MUST use 16-byte blocks in + * CTR + CBC-MAC mode, and its secret key MUST have been already set in + * the provided context. The parameters are linked in the CCM context. + * + * After this function has been called, the `br_ccm_reset()` function must + * be called, to provide the nonce for CCM computation. + * + * \param ctx CCM context structure. + * \param bctx block cipher context (already initialised with secret key). + */ +void br_ccm_init(br_ccm_context *ctx, const br_block_ctrcbc_class **bctx); + +/** + * \brief Reset a CCM context. + * + * This function resets an already initialised CCM context for a new + * computation run. Implementations and keys are conserved. This function + * can be called at any time; it cancels any ongoing CCM computation that + * uses the provided context structure. + * + * The `aad_len` parameter contains the total length, in bytes, of the + * additional authenticated data. It may be zero. That length MUST be + * exact. + * + * The `data_len` parameter contains the total length, in bytes, of the + * data that will be injected (plaintext or ciphertext). That length MUST + * be exact. Moreover, that length MUST be less than 2^(8*(15-nonce_len)). + * + * The nonce length (`nonce_len`), in bytes, must be in the 7..13 range + * (inclusive). + * + * The tag length (`tag_len`), in bytes, must be in the 4..16 range, and + * be an even integer. Short tags mechanically allow for higher forgery + * probabilities; hence, tag sizes smaller than 12 bytes shall be used only + * with care. + * + * It is critical to CCM security that nonce values are not repeated for + * the same encryption key. Random generation of nonces is not generally + * recommended, due to the relatively small maximum nonce value. + * + * Returned value is 1 on success, 0 on error. An error is reported if + * the tag or nonce length is out of range, or if the + * plaintext/ciphertext length cannot be encoded with the specified + * nonce length. + * + * \param ctx CCM context structure. + * \param nonce CCM nonce to use. + * \param nonce_len CCM nonce length (in bytes, 7 to 13). + * \param aad_len additional authenticated data length (in bytes). + * \param data_len plaintext/ciphertext length (in bytes). + * \param tag_len tag length (in bytes). + * \return 1 on success, 0 on error. + */ +int br_ccm_reset(br_ccm_context *ctx, const void *nonce, size_t nonce_len, + uint64_t aad_len, uint64_t data_len, size_t tag_len); + +/** + * \brief Inject additional authenticated data into CCM. + * + * The provided data is injected into a running CCM computation. Additional + * data must be injected _before_ the call to `br_ccm_flip()`. + * Additional data can be injected in several chunks of arbitrary length, + * but the total amount MUST exactly match the value which was provided + * to `br_ccm_reset()`. + * + * \param ctx CCM context structure. + * \param data pointer to additional authenticated data. + * \param len length of additional authenticated data (in bytes). + */ +void br_ccm_aad_inject(br_ccm_context *ctx, const void *data, size_t len); + +/** + * \brief Finish injection of additional authenticated data into CCM. + * + * This function MUST be called before beginning the actual encryption + * or decryption (with `br_ccm_run()`), even if no additional authenticated + * data was injected. No additional authenticated data may be injected + * after this function call. + * + * \param ctx CCM context structure. + */ +void br_ccm_flip(br_ccm_context *ctx); + +/** + * \brief Encrypt or decrypt some data with CCM. + * + * Data encryption or decryption can be done after `br_ccm_flip()` + * has been called on the context. If `encrypt` is non-zero, then the + * provided data shall be plaintext, and it is encrypted in place. + * Otherwise, the data shall be ciphertext, and it is decrypted in place. + * + * Data may be provided in several chunks of arbitrary length, provided + * that the total length exactly matches the length provided to the + * `br_ccm_reset()` call. + * + * \param ctx CCM context structure. + * \param encrypt non-zero for encryption, zero for decryption. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +void br_ccm_run(br_ccm_context *ctx, int encrypt, void *data, size_t len); + +/** + * \brief Compute CCM authentication tag. + * + * Compute the CCM authentication tag. This call terminates the CCM + * run: all data must have been injected with `br_ccm_run()` (in zero, + * one or more successive calls). After this function has been called, + * no more data can br processed; a `br_ccm_reset()` call is required + * to start a new message. + * + * The tag length was provided upon context initialisation (last call + * to `br_ccm_reset()`); it is returned by this function. + * + * The tag value must normally be sent along with the encrypted data. + * When decrypting, the tag value must be recomputed and compared with + * the received tag: if the two tag values differ, then either the tag + * or the encrypted data was altered in transit. As an alternative to + * this function, the `br_ccm_check_tag()` function can be used to + * compute and check the tag value. + * + * \param ctx CCM context structure. + * \param tag destination buffer for the tag (up to 16 bytes). + * \return the tag length (in bytes). + */ +size_t br_ccm_get_tag(br_ccm_context *ctx, void *tag); + +/** + * \brief Compute and check CCM authentication tag. + * + * This function is an alternative to `br_ccm_get_tag()`, normally used + * on the receiving end (i.e. when decrypting value). The tag value is + * recomputed and compared with the provided tag value. If they match, 1 + * is returned; on mismatch, 0 is returned. A returned value of 0 means + * that the data or the tag was altered in transit, normally leading to + * wholesale rejection of the complete message. + * + * \param ctx CCM context structure. + * \param tag tag value to compare with (up to 16 bytes). + * \return 1 on success (exact match of tag value), 0 otherwise. + */ +uint32_t br_ccm_check_tag(br_ccm_context *ctx, const void *tag); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h new file mode 100644 index 0000000000..683a4906d0 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_block.h @@ -0,0 +1,2618 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_BLOCK_H__ +#define BR_BEARSSL_BLOCK_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_block.h + * + * # Block Ciphers and Symmetric Ciphers + * + * This file documents the API for block ciphers and other symmetric + * ciphers. + * + * + * ## Procedural API + * + * For a block cipher implementation, up to three separate sets of + * functions are provided, for CBC encryption, CBC decryption, and CTR + * encryption/decryption. Each set has its own context structure, + * initialised with the encryption key. + * + * For CBC encryption and decryption, the data to encrypt or decrypt is + * referenced as a sequence of blocks. The implementations assume that + * there is no partial block; no padding is applied or removed. The + * caller is responsible for handling any kind of padding. + * + * Function for CTR encryption are defined only for block ciphers with + * blocks of 16 bytes or more (i.e. AES, but not DES/3DES). + * + * Each implemented block cipher is identified by an "internal name" + * from which are derived the names of structures and functions that + * implement the cipher. For the block cipher of internal name "`xxx`", + * the following are defined: + * + * - `br_xxx_BLOCK_SIZE` + * + * A macro that evaluates to the block size (in bytes) of the + * cipher. For all implemented block ciphers, this value is a + * power of two. + * + * - `br_xxx_cbcenc_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CBC encryption. The + * structure first field is called `vtable` and points to the + * appropriate OOP structure. + * + * - `br_xxx_cbcenc_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CBC encryption are computed and + * written in the provided context structure. The key length MUST be + * adequate for the implemented block cipher. This function also sets + * the `vtable` field. + * + * - `br_xxx_cbcenc_run(const br_xxx_cbcenc_keys *ctx, void *iv, void *data, size_t len)` + * + * Perform CBC encryption of `len` bytes, in place. The encrypted data + * replaces the cleartext. `len` MUST be a multiple of the block length + * (if it is not, the function may loop forever or overflow a buffer). + * The IV is provided with the `iv` pointer; it is also updated with + * a copy of the last encrypted block. + * + * - `br_xxx_cbcdec_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CBC decryption. The + * structure first field is called `vtable` and points to the + * appropriate OOP structure. + * + * - `br_xxx_cbcdec_init(br_xxx_cbcenc_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CBC decryption are computed and + * written in the provided context structure. The key length MUST be + * adequate for the implemented block cipher. This function also sets + * the `vtable` field. + * + * - `br_xxx_cbcdec_run(const br_xxx_cbcdec_keys *ctx, void *iv, void *data, size_t num_blocks)` + * + * Perform CBC decryption of `len` bytes, in place. The decrypted data + * replaces the ciphertext. `len` MUST be a multiple of the block length + * (if it is not, the function may loop forever or overflow a buffer). + * The IV is provided with the `iv` pointer; it is also updated with + * a copy of the last _encrypted_ block. + * + * - `br_xxx_ctr_keys` + * + * Context structure that contains the subkeys resulting from the key + * expansion. These subkeys are appropriate for CTR encryption and + * decryption. The structure first field is called `vtable` and + * points to the appropriate OOP structure. + * + * - `br_xxx_ctr_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for CTR encryption and decryption + * are computed and written in the provided context structure. The + * key length MUST be adequate for the implemented block cipher. This + * function also sets the `vtable` field. + * + * - `br_xxx_ctr_run(const br_xxx_ctr_keys *ctx, const void *iv, uint32_t cc, void *data, size_t len)` (returns `uint32_t`) + * + * Perform CTR encryption/decryption of some data. Processing is done + * "in place" (the output data replaces the input data). This function + * implements the "standard incrementing function" from NIST SP800-38A, + * annex B: the IV length shall be 4 bytes less than the block size + * (i.e. 12 bytes for AES) and the counter is the 32-bit value starting + * with `cc`. The data length (`len`) is not necessarily a multiple of + * the block size. The new counter value is returned, which supports + * chunked processing, provided that each chunk length (except possibly + * the last one) is a multiple of the block size. + * + * - `br_xxx_ctrcbc_keys` + * + * Context structure that contains the subkeys resulting from the + * key expansion. These subkeys are appropriate for doing combined + * CTR encryption/decryption and CBC-MAC, as used in the CCM and EAX + * authenticated encryption modes. The structure first field is + * called `vtable` and points to the appropriate OOP structure. + * + * - `br_xxx_ctrcbc_init(br_xxx_ctr_keys *ctx, const void *key, size_t len)` + * + * Perform key expansion: subkeys for combined CTR + * encryption/decryption and CBC-MAC are computed and written in the + * provided context structure. The key length MUST be adequate for + * the implemented block cipher. This function also sets the + * `vtable` field. + * + * - `br_xxx_ctrcbc_encrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)` + * + * Perform CTR encryption of some data, and CBC-MAC. Processing is + * done "in place" (the output data replaces the input data). This + * function applies CTR encryption on the data, using a full + * block-size counter (i.e. for 128-bit blocks, the counter is + * incremented as a 128-bit value). The 'ctr' array contains the + * initial value for the counter (used in the first block) and it is + * updated with the new value after data processing. The 'cbcmac' + * value shall point to a block-sized value which is used as IV for + * CBC-MAC, computed over the encrypted data (output of CTR + * encryption); the resulting CBC-MAC is written over 'cbcmac' on + * output. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_decrypt(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *cbcmac, void *data, size_t len)` + * + * Perform CTR decryption of some data, and CBC-MAC. Processing is + * done "in place" (the output data replaces the input data). This + * function applies CTR decryption on the data, using a full + * block-size counter (i.e. for 128-bit blocks, the counter is + * incremented as a 128-bit value). The 'ctr' array contains the + * initial value for the counter (used in the first block) and it is + * updated with the new value after data processing. The 'cbcmac' + * value shall point to a block-sized value which is used as IV for + * CBC-MAC, computed over the encrypted data (input of CTR + * encryption); the resulting CBC-MAC is written over 'cbcmac' on + * output. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_ctr(const br_xxx_ctrcbc_keys *ctx, void *ctr, void *data, size_t len)` + * + * Perform CTR encryption or decryption of the provided data. The + * data is processed "in place" (the output data replaces the input + * data). A full block-sized counter is applied (i.e. for 128-bit + * blocks, the counter is incremented as a 128-bit value). The 'ctr' + * array contains the initial value for the counter (used in the + * first block), and it is updated with the new value after data + * processing. + * + * The data length MUST be a multiple of the block size. + * + * - `br_xxx_ctrcbc_mac(const br_xxx_ctrcbc_keys *ctx, void *cbcmac, const void *data, size_t len)` + * + * Compute CBC-MAC over the provided data. The IV for CBC-MAC is + * provided as 'cbcmac'; the output is written over the same array. + * The data itself is untouched. The data length MUST be a multiple + * of the block size. + * + * + * It shall be noted that the key expansion functions return `void`. If + * the provided key length is not allowed, then there will be no error + * reporting; implementations need not validate the key length, thus an + * invalid key length may result in undefined behaviour (e.g. buffer + * overflow). + * + * Subkey structures contain no interior pointer, and no external + * resources are allocated upon key expansion. They can thus be + * discarded without any explicit deallocation. + * + * + * ## Object-Oriented API + * + * Each context structure begins with a field (called `vtable`) that + * points to an instance of a structure that references the relevant + * functions through pointers. Each such structure contains the + * following: + * + * - `context_size` + * + * The size (in bytes) of the context structure for subkeys. + * + * - `block_size` + * + * The cipher block size (in bytes). + * + * - `log_block_size` + * + * The base-2 logarithm of cipher block size (e.g. 4 for blocks + * of 16 bytes). + * + * - `init` + * + * Pointer to the key expansion function. + * + * - `run` + * + * Pointer to the encryption/decryption function. + * + * For combined CTR/CBC-MAC encryption, the `vtable` has a slightly + * different structure: + * + * - `context_size` + * + * The size (in bytes) of the context structure for subkeys. + * + * - `block_size` + * + * The cipher block size (in bytes). + * + * - `log_block_size` + * + * The base-2 logarithm of cipher block size (e.g. 4 for blocks + * of 16 bytes). + * + * - `init` + * + * Pointer to the key expansion function. + * + * - `encrypt` + * + * Pointer to the CTR encryption + CBC-MAC function. + * + * - `decrypt` + * + * Pointer to the CTR decryption + CBC-MAC function. + * + * - `ctr` + * + * Pointer to the CTR encryption/decryption function. + * + * - `mac` + * + * Pointer to the CBC-MAC function. + * + * For block cipher "`xxx`", static, constant instances of these + * structures are defined, under the names: + * + * - `br_xxx_cbcenc_vtable` + * - `br_xxx_cbcdec_vtable` + * - `br_xxx_ctr_vtable` + * - `br_xxx_ctrcbc_vtable` + * + * + * ## Implemented Block Ciphers + * + * Provided implementations are: + * + * | Name | Function | Block Size (bytes) | Key lengths (bytes) | + * | :-------- | :------- | :----------------: | :-----------------: | + * | aes_big | AES | 16 | 16, 24 and 32 | + * | aes_small | AES | 16 | 16, 24 and 32 | + * | aes_ct | AES | 16 | 16, 24 and 32 | + * | aes_ct64 | AES | 16 | 16, 24 and 32 | + * | aes_x86ni | AES | 16 | 16, 24 and 32 | + * | aes_pwr8 | AES | 16 | 16, 24 and 32 | + * | des_ct | DES/3DES | 8 | 8, 16 and 24 | + * | des_tab | DES/3DES | 8 | 8, 16 and 24 | + * + * **Note:** DES/3DES nominally uses keys of 64, 128 and 192 bits (i.e. 8, + * 16 and 24 bytes), but some of the bits are ignored by the algorithm, so + * the _effective_ key lengths, from a security point of view, are 56, + * 112 and 168 bits, respectively. + * + * `aes_big` is a "classical" AES implementation, using tables. It + * is fast but not constant-time, since it makes data-dependent array + * accesses. + * + * `aes_small` is an AES implementation optimized for code size. It + * is substantially slower than `aes_big`; it is not constant-time + * either. + * + * `aes_ct` is a constant-time implementation of AES; its code is about + * as big as that of `aes_big`, while its performance is comparable to + * that of `aes_small`. However, it is constant-time. This + * implementation should thus be considered to be the "default" AES in + * BearSSL, to be used unless the operational context guarantees that a + * non-constant-time implementation is safe, or an architecture-specific + * constant-time implementation can be used (e.g. using dedicated + * hardware opcodes). + * + * `aes_ct64` is another constant-time implementation of AES. It is + * similar to `aes_ct` but uses 64-bit values. On 32-bit machines, + * `aes_ct64` is not faster than `aes_ct`, often a bit slower, and has + * a larger footprint; however, on 64-bit architectures, `aes_ct64` + * is typically twice faster than `aes_ct` for modes that allow parallel + * operations (i.e. CTR, and CBC decryption, but not CBC encryption). + * + * `aes_x86ni` exists only on x86 architectures (32-bit and 64-bit). It + * uses the AES-NI opcodes when available. + * + * `aes_pwr8` exists only on PowerPC / POWER architectures (32-bit and + * 64-bit, both little-endian and big-endian). It uses the AES opcodes + * present in POWER8 and later. + * + * `des_tab` is a classic, table-based implementation of DES/3DES. It + * is not constant-time. + * + * `des_ct` is an constant-time implementation of DES/3DES. It is + * substantially slower than `des_tab`. + * + * ## ChaCha20 and Poly1305 + * + * ChaCha20 is a stream cipher. Poly1305 is a MAC algorithm. They + * are described in [RFC 7539](https://tools.ietf.org/html/rfc7539). + * + * Two function pointer types are defined: + * + * - `br_chacha20_run` describes a function that implements ChaCha20 + * only. + * + * - `br_poly1305_run` describes an implementation of Poly1305, + * in the AEAD combination with ChaCha20 specified in RFC 7539 + * (the ChaCha20 implementation is provided as a function pointer). + * + * `chacha20_ct` is a straightforward implementation of ChaCha20 in + * plain C; it is constant-time, small, and reasonably fast. + * + * `chacha20_sse2` leverages SSE2 opcodes (on x86 architectures that + * support these opcodes). It is faster than `chacha20_ct`. + * + * `poly1305_ctmul` is an implementation of the ChaCha20+Poly1305 AEAD + * construction, where the Poly1305 part is performed with mixed 32-bit + * multiplications (operands are 32-bit, result is 64-bit). + * + * `poly1305_ctmul32` implements ChaCha20+Poly1305 using pure 32-bit + * multiplications (32-bit operands, 32-bit result). It is slower than + * `poly1305_ctmul`, except on some specific architectures such as + * the ARM Cortex M0+. + * + * `poly1305_ctmulq` implements ChaCha20+Poly1305 with mixed 64-bit + * multiplications (operands are 64-bit, result is 128-bit) on 64-bit + * platforms that support such operations. + * + * `poly1305_i15` implements ChaCha20+Poly1305 with the generic "i15" + * big integer implementation. It is meant mostly for testing purposes, + * although it can help with saving a few hundred bytes of code footprint + * on systems where code size is scarce. + */ + +/** + * \brief Class type for CBC encryption implementations. + * + * A `br_block_cbcenc_class` instance points to the functions implementing + * a specific block cipher, when used in CBC mode for encrypting data. + */ +typedef struct br_block_cbcenc_class_ br_block_cbcenc_class; +struct br_block_cbcenc_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_cbcenc_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CBC encryption. + * + * The `iv` parameter points to the IV for this run; it is + * updated with a copy of the last encrypted block. The data + * is encrypted "in place"; its length (`len`) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param iv IV for CBC encryption (updated). + * \param data data to encrypt. + * \param len data length (in bytes, multiple of block size). + */ + void (*run)(const br_block_cbcenc_class *const *ctx, + void *iv, void *data, size_t len); +}; + +/** + * \brief Class type for CBC decryption implementations. + * + * A `br_block_cbcdec_class` instance points to the functions implementing + * a specific block cipher, when used in CBC mode for decrypting data. + */ +typedef struct br_block_cbcdec_class_ br_block_cbcdec_class; +struct br_block_cbcdec_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_cbcdec_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CBC decryption. + * + * The `iv` parameter points to the IV for this run; it is + * updated with a copy of the last encrypted block. The data + * is decrypted "in place"; its length (`len`) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param iv IV for CBC decryption (updated). + * \param data data to decrypt. + * \param len data length (in bytes, multiple of block size). + */ + void (*run)(const br_block_cbcdec_class *const *ctx, + void *iv, void *data, size_t len); +}; + +/** + * \brief Class type for CTR encryption/decryption implementations. + * + * A `br_block_ctr_class` instance points to the functions implementing + * a specific block cipher, when used in CTR mode for encrypting or + * decrypting data. + */ +typedef struct br_block_ctr_class_ br_block_ctr_class; +struct br_block_ctr_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_ctr_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CTR encryption or decryption. + * + * The `iv` parameter points to the IV for this run; its + * length is exactly 4 bytes less than the block size (e.g. + * 12 bytes for AES/CTR). The IV is combined with a 32-bit + * block counter to produce the block value which is processed + * with the block cipher. + * + * The data to encrypt or decrypt is updated "in place". Its + * length (`len` bytes) is not required to be a multiple of + * the block size; if the final block is partial, then the + * corresponding key stream bits are dropped. + * + * The resulting counter value is returned. + * + * \param ctx context structure (already initialised). + * \param iv IV for CTR encryption/decryption. + * \param cc initial value for the block counter. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \return the new block counter value. + */ + uint32_t (*run)(const br_block_ctr_class *const *ctx, + const void *iv, uint32_t cc, void *data, size_t len); +}; + +/** + * \brief Class type for combined CTR and CBC-MAC implementations. + * + * A `br_block_ctrcbc_class` instance points to the functions implementing + * a specific block cipher, when used in CTR mode for encrypting or + * decrypting data, along with CBC-MAC. + */ +typedef struct br_block_ctrcbc_class_ br_block_ctrcbc_class; +struct br_block_ctrcbc_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate + * for containing subkeys. + */ + size_t context_size; + + /** + * \brief Size of individual blocks (in bytes). + */ + unsigned block_size; + + /** + * \brief Base-2 logarithm of the size of individual blocks, + * expressed in bytes. + */ + unsigned log_block_size; + + /** + * \brief Initialisation function. + * + * This function sets the `vtable` field in the context structure. + * The key length MUST be one of the key lengths supported by + * the implementation. + * + * \param ctx context structure to initialise. + * \param key secret key. + * \param key_len key length (in bytes). + */ + void (*init)(const br_block_ctrcbc_class **ctx, + const void *key, size_t key_len); + + /** + * \brief Run the CTR encryption + CBC-MAC. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as encryption proceeds. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (output of CTR + * encryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data to encrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to encrypt. + * \param len data length (in bytes). + */ + void (*encrypt)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + + /** + * \brief Run the CTR decryption + CBC-MAC. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as decryption proceeds. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (i.e. before CTR + * decryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data to decrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*decrypt)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + + /** + * \brief Run the CTR encryption/decryption only. + * + * The `ctr` parameter points to the counter; its length shall + * be equal to the block size. It is updated by this function + * as decryption proceeds. + * + * The data to decrypt is updated "in place". Its length (`len` + * bytes) MUST be a multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param ctr counter for CTR encryption (initial and final). + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*ctr)(const br_block_ctrcbc_class *const *ctx, + void *ctr, void *data, size_t len); + + /** + * \brief Run the CBC-MAC only. + * + * The `cbcmac` parameter points to the IV for CBC-MAC. The MAC + * is computed over the encrypted data (i.e. before CTR + * decryption). Its length shall be equal to the block size. The + * computed CBC-MAC value is written over the `cbcmac` array. + * + * The data is unmodified. Its length (`len` bytes) MUST be a + * multiple of the block size. + * + * \param ctx context structure (already initialised). + * \param cbcmac IV and output buffer for CBC-MAC. + * \param data data to decrypt. + * \param len data length (in bytes). + */ + void (*mac)(const br_block_ctrcbc_class *const *ctx, + void *cbcmac, const void *data, size_t len); +}; + +/* + * Traditional, table-based AES implementation. It is fast, but uses + * internal tables (in particular a 1 kB table for encryption, another + * 1 kB table for decryption, and a 256-byte table for key schedule), + * and it is not constant-time. In contexts where cache-timing attacks + * apply, this implementation may leak the secret key. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_big_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_big` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_big_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_big` implementation). + */ +extern const br_block_cbcenc_class br_aes_big_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_big` implementation). + */ +extern const br_block_cbcdec_class br_aes_big_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_big` implementation). + */ +extern const br_block_ctr_class br_aes_big_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_big` implementation). + */ +extern const br_block_ctrcbc_class br_aes_big_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_cbcenc_init(br_aes_big_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_cbcdec_init(br_aes_big_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_ctr_init(br_aes_big_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_big` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_big_ctrcbc_init(br_aes_big_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_big_cbcenc_run(const br_aes_big_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_big_cbcdec_run(const br_aes_big_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to encrypt or decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_big_ctr_run(const br_aes_big_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_encrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_decrypt(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_ctr(const br_aes_big_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_big` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_big_ctrcbc_mac(const br_aes_big_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * AES implementation optimized for size. It is slower than the + * traditional table-based AES implementation, but requires much less + * code. It still uses data-dependent table accesses (albeit within a + * much smaller 256-byte table), which makes it conceptually vulnerable + * to cache-timing attacks. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_small_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_small` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_small_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_small` implementation). + */ +extern const br_block_cbcenc_class br_aes_small_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_small` implementation). + */ +extern const br_block_cbcdec_class br_aes_small_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_small` implementation). + */ +extern const br_block_ctr_class br_aes_small_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_small` implementation). + */ +extern const br_block_ctrcbc_class br_aes_small_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_cbcenc_init(br_aes_small_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_cbcdec_init(br_aes_small_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_ctr_init(br_aes_small_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_small` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_small_ctrcbc_init(br_aes_small_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_small_cbcenc_run(const br_aes_small_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_small_cbcdec_run(const br_aes_small_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_small_ctr_run(const br_aes_small_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_encrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_decrypt(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_ctr(const br_aes_small_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_small` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_small_ctrcbc_mac(const br_aes_small_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * Constant-time AES implementation. Its size is similar to that of + * 'aes_big', and its performance is similar to that of 'aes_small' (faster + * decryption, slower encryption). However, it is constant-time, i.e. + * immune to cache-timing and similar attacks. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_ct_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_ct` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[60]; + unsigned num_rounds; +#endif +} br_aes_ct_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_ct` implementation). + */ +extern const br_block_cbcenc_class br_aes_ct_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_ct` implementation). + */ +extern const br_block_cbcdec_class br_aes_ct_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_ct` implementation). + */ +extern const br_block_ctr_class br_aes_ct_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_ct` implementation). + */ +extern const br_block_ctrcbc_class br_aes_ct_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_cbcenc_init(br_aes_ct_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_cbcdec_init(br_aes_ct_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_ctr_init(br_aes_ct_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct_ctrcbc_init(br_aes_ct_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct_cbcenc_run(const br_aes_ct_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct_cbcdec_run(const br_aes_ct_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_ct_ctr_run(const br_aes_ct_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_encrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_decrypt(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_ctr(const br_aes_ct_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_ct` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct_ctrcbc_mac(const br_aes_ct_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * 64-bit constant-time AES implementation. It is similar to 'aes_ct' + * but uses 64-bit registers, making it about twice faster than 'aes_ct' + * on 64-bit platforms, while remaining constant-time and with a similar + * code size. (The doubling in performance is only for CBC decryption + * and CTR mode; CBC encryption is non-parallel and cannot benefit from + * the larger registers.) + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_ct64_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_ct64` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t skey[30]; + unsigned num_rounds; +#endif +} br_aes_ct64_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_ct64` implementation). + */ +extern const br_block_cbcenc_class br_aes_ct64_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_ct64` implementation). + */ +extern const br_block_cbcdec_class br_aes_ct64_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_ct64` implementation). + */ +extern const br_block_ctr_class br_aes_ct64_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_ct64` implementation). + */ +extern const br_block_ctrcbc_class br_aes_ct64_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_cbcenc_init(br_aes_ct64_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_cbcdec_init(br_aes_ct64_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_ctr_init(br_aes_ct64_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_ct64` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_ct64_ctrcbc_init(br_aes_ct64_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct64_cbcenc_run(const br_aes_ct64_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_ct64_cbcdec_run(const br_aes_ct64_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_ct64_ctr_run(const br_aes_ct64_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_encrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_decrypt(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_ctr(const br_aes_ct64_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_ct64` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_ct64_ctrcbc_mac(const br_aes_ct64_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/* + * AES implementation using AES-NI opcodes (x86 platform). + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_x86ni_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_x86ni` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_x86ni_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_cbcenc_get_vtable()`. + */ +extern const br_block_cbcenc_class br_aes_x86ni_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_cbcdec_get_vtable()`. + */ +extern const br_block_cbcdec_class br_aes_x86ni_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_ctr_get_vtable()`. + */ +extern const br_block_ctr_class br_aes_x86ni_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_x86ni` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_x86ni_ctrcbc_get_vtable()`. + */ +extern const br_block_ctrcbc_class br_aes_x86ni_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_cbcenc_init(br_aes_x86ni_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_cbcdec_init(br_aes_x86ni_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_ctr_init(br_aes_x86ni_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_x86ni` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_x86ni_ctrcbc_init(br_aes_x86ni_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_x86ni_cbcenc_run(const br_aes_x86ni_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_x86ni_cbcdec_run(const br_aes_x86ni_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_x86ni_ctr_run(const br_aes_x86ni_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_encrypt(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_decrypt(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_ctr(const br_aes_x86ni_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_x86ni` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_x86ni_ctrcbc_mac(const br_aes_x86ni_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/** + * \brief Obtain the `aes_x86ni` AES-CBC (encryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_cbcenc_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CBC (encryption) implementation, or `NULL`. + */ +const br_block_cbcenc_class *br_aes_x86ni_cbcenc_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CBC (decryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_cbcdec_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CBC (decryption) implementation, or `NULL`. + */ +const br_block_cbcdec_class *br_aes_x86ni_cbcdec_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CTR implementation, if available. + * + * This function returns a pointer to `br_aes_x86ni_ctr_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CTR implementation, or `NULL`. + */ +const br_block_ctr_class *br_aes_x86ni_ctr_get_vtable(void); + +/** + * \brief Obtain the `aes_x86ni` AES-CTR + CBC-MAC implementation, if + * available. + * + * This function returns a pointer to `br_aes_x86ni_ctrcbc_vtable`, if + * that implementation was compiled in the library _and_ the x86 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_x86ni` AES-CTR implementation, or `NULL`. + */ +const br_block_ctrcbc_class *br_aes_x86ni_ctrcbc_get_vtable(void); + +/* + * AES implementation using POWER8 opcodes. + */ + +/** \brief AES block size (16 bytes). */ +#define br_aes_pwr8_BLOCK_SIZE 16 + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_cbcenc_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_cbcdec_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CTR encryption + * and decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctr_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_ctr_keys; + +/** + * \brief Context for AES subkeys (`aes_pwr8` implementation, CTR encryption + * and decryption + CBC-MAC). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_ctrcbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + union { + unsigned char skni[16 * 15]; + } skey; + unsigned num_rounds; +#endif +} br_aes_pwr8_ctrcbc_keys; + +/** + * \brief Class instance for AES CBC encryption (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_cbcenc_get_vtable()`. + */ +extern const br_block_cbcenc_class br_aes_pwr8_cbcenc_vtable; + +/** + * \brief Class instance for AES CBC decryption (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_cbcdec_get_vtable()`. + */ +extern const br_block_cbcdec_class br_aes_pwr8_cbcdec_vtable; + +/** + * \brief Class instance for AES CTR encryption and decryption + * (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_ctr_get_vtable()`. + */ +extern const br_block_ctr_class br_aes_pwr8_ctr_vtable; + +/** + * \brief Class instance for AES CTR encryption/decryption + CBC-MAC + * (`aes_pwr8` implementation). + * + * Since this implementation might be omitted from the library, or the + * AES opcode unavailable on the current CPU, a pointer to this class + * instance should be obtained through `br_aes_pwr8_ctrcbc_get_vtable()`. + */ +extern const br_block_ctrcbc_class br_aes_pwr8_ctrcbc_vtable; + +/** + * \brief Context initialisation (key schedule) for AES CBC encryption + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_cbcenc_init(br_aes_pwr8_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CBC decryption + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_cbcdec_init(br_aes_pwr8_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR encryption + * and decryption (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_ctr_init(br_aes_pwr8_ctr_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for AES CTR + CBC-MAC + * (`aes_pwr8` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_aes_pwr8_ctrcbc_init(br_aes_pwr8_ctrcbc_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_pwr8_cbcenc_run(const br_aes_pwr8_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 16). + */ +void br_aes_pwr8_cbcdec_run(const br_aes_pwr8_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CTR encryption and decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (constant, 12 bytes). + * \param cc initial block counter value. + * \param data data to decrypt (updated). + * \param len data length (in bytes). + * \return new block counter value. + */ +uint32_t br_aes_pwr8_ctr_run(const br_aes_pwr8_ctr_keys *ctx, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief CTR encryption + CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_encrypt(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR decryption + CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_decrypt(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *cbcmac, void *data, size_t len); + +/** + * \brief CTR encryption/decryption with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param ctr counter for CTR (16 bytes, updated). + * \param data data to MAC (updated). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_ctr(const br_aes_pwr8_ctrcbc_keys *ctx, + void *ctr, void *data, size_t len); + +/** + * \brief CBC-MAC with AES (`aes_pwr8` implementation). + * + * \param ctx context (already initialised). + * \param cbcmac IV for CBC-MAC (updated). + * \param data data to MAC (unmodified). + * \param len data length (in bytes, MUST be a multiple of 16). + */ +void br_aes_pwr8_ctrcbc_mac(const br_aes_pwr8_ctrcbc_keys *ctx, + void *cbcmac, const void *data, size_t len); + +/** + * \brief Obtain the `aes_pwr8` AES-CBC (encryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_cbcenc_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 + * crypto opcodes are available on the currently running CPU. If either + * of these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CBC (encryption) implementation, or `NULL`. + */ +const br_block_cbcenc_class *br_aes_pwr8_cbcenc_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CBC (decryption) implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_cbcdec_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 + * crypto opcodes are available on the currently running CPU. If either + * of these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CBC (decryption) implementation, or `NULL`. + */ +const br_block_cbcdec_class *br_aes_pwr8_cbcdec_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CTR implementation, if available. + * + * This function returns a pointer to `br_aes_pwr8_ctr_vtable`, if that + * implementation was compiled in the library _and_ the POWER8 crypto + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CTR implementation, or `NULL`. + */ +const br_block_ctr_class *br_aes_pwr8_ctr_get_vtable(void); + +/** + * \brief Obtain the `aes_pwr8` AES-CTR + CBC-MAC implementation, if + * available. + * + * This function returns a pointer to `br_aes_pwr8_ctrcbc_vtable`, if + * that implementation was compiled in the library _and_ the POWER8 AES + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `NULL`. + * + * \return the `aes_pwr8` AES-CTR implementation, or `NULL`. + */ +const br_block_ctrcbc_class *br_aes_pwr8_ctrcbc_get_vtable(void); + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC encryption) for all AES implementations. + */ +typedef union { + const br_block_cbcenc_class *vtable; + br_aes_big_cbcenc_keys c_big; + br_aes_small_cbcenc_keys c_small; + br_aes_ct_cbcenc_keys c_ct; + br_aes_ct64_cbcenc_keys c_ct64; + br_aes_x86ni_cbcenc_keys c_x86ni; + br_aes_pwr8_cbcenc_keys c_pwr8; +} br_aes_gen_cbcenc_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC decryption) for all AES implementations. + */ +typedef union { + const br_block_cbcdec_class *vtable; + br_aes_big_cbcdec_keys c_big; + br_aes_small_cbcdec_keys c_small; + br_aes_ct_cbcdec_keys c_ct; + br_aes_ct64_cbcdec_keys c_ct64; + br_aes_x86ni_cbcdec_keys c_x86ni; + br_aes_pwr8_cbcdec_keys c_pwr8; +} br_aes_gen_cbcdec_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CTR encryption and decryption) for all AES implementations. + */ +typedef union { + const br_block_ctr_class *vtable; + br_aes_big_ctr_keys c_big; + br_aes_small_ctr_keys c_small; + br_aes_ct_ctr_keys c_ct; + br_aes_ct64_ctr_keys c_ct64; + br_aes_x86ni_ctr_keys c_x86ni; + br_aes_pwr8_ctr_keys c_pwr8; +} br_aes_gen_ctr_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CTR encryption/decryption + CBC-MAC) for all AES implementations. + */ +typedef union { + const br_block_ctrcbc_class *vtable; + br_aes_big_ctrcbc_keys c_big; + br_aes_small_ctrcbc_keys c_small; + br_aes_ct_ctrcbc_keys c_ct; + br_aes_ct64_ctrcbc_keys c_ct64; + br_aes_x86ni_ctrcbc_keys c_x86ni; + br_aes_pwr8_ctrcbc_keys c_pwr8; +} br_aes_gen_ctrcbc_keys; + +/* + * Traditional, table-based implementation for DES/3DES. Since tables are + * used, cache-timing attacks are conceptually possible. + */ + +/** \brief DES/3DES block size (8 bytes). */ +#define br_des_tab_BLOCK_SIZE 8 + +/** + * \brief Context for DES subkeys (`des_tab` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_tab_cbcenc_keys; + +/** + * \brief Context for DES subkeys (`des_tab` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_tab_cbcdec_keys; + +/** + * \brief Class instance for DES CBC encryption (`des_tab` implementation). + */ +extern const br_block_cbcenc_class br_des_tab_cbcenc_vtable; + +/** + * \brief Class instance for DES CBC decryption (`des_tab` implementation). + */ +extern const br_block_cbcdec_class br_des_tab_cbcdec_vtable; + +/** + * \brief Context initialisation (key schedule) for DES CBC encryption + * (`des_tab` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_tab_cbcenc_init(br_des_tab_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for DES CBC decryption + * (`des_tab` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_tab_cbcdec_init(br_des_tab_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with DES (`des_tab` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_tab_cbcenc_run(const br_des_tab_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with DES (`des_tab` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_tab_cbcdec_run(const br_des_tab_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/* + * Constant-time implementation for DES/3DES. It is substantially slower + * (by a factor of about 4x), but also immune to cache-timing attacks. + */ + +/** \brief DES/3DES block size (8 bytes). */ +#define br_des_ct_BLOCK_SIZE 8 + +/** + * \brief Context for DES subkeys (`des_ct` implementation, CBC encryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcenc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_ct_cbcenc_keys; + +/** + * \brief Context for DES subkeys (`des_ct` implementation, CBC decryption). + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** \brief Pointer to vtable for this context. */ + const br_block_cbcdec_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint32_t skey[96]; + unsigned num_rounds; +#endif +} br_des_ct_cbcdec_keys; + +/** + * \brief Class instance for DES CBC encryption (`des_ct` implementation). + */ +extern const br_block_cbcenc_class br_des_ct_cbcenc_vtable; + +/** + * \brief Class instance for DES CBC decryption (`des_ct` implementation). + */ +extern const br_block_cbcdec_class br_des_ct_cbcdec_vtable; + +/** + * \brief Context initialisation (key schedule) for DES CBC encryption + * (`des_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_ct_cbcenc_init(br_des_ct_cbcenc_keys *ctx, + const void *key, size_t len); + +/** + * \brief Context initialisation (key schedule) for DES CBC decryption + * (`des_ct` implementation). + * + * \param ctx context to initialise. + * \param key secret key. + * \param len secret key length (in bytes). + */ +void br_des_ct_cbcdec_init(br_des_ct_cbcdec_keys *ctx, + const void *key, size_t len); + +/** + * \brief CBC encryption with DES (`des_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to encrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_ct_cbcenc_run(const br_des_ct_cbcenc_keys *ctx, void *iv, + void *data, size_t len); + +/** + * \brief CBC decryption with DES (`des_ct` implementation). + * + * \param ctx context (already initialised). + * \param iv IV (updated). + * \param data data to decrypt (updated). + * \param len data length (in bytes, MUST be multiple of 8). + */ +void br_des_ct_cbcdec_run(const br_des_ct_cbcdec_keys *ctx, void *iv, + void *data, size_t len); + +/* + * These structures are large enough to accommodate subkeys for all + * DES/3DES implementations. + */ + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC encryption) for all DES implementations. + */ +typedef union { + const br_block_cbcenc_class *vtable; + br_des_tab_cbcenc_keys tab; + br_des_ct_cbcenc_keys ct; +} br_des_gen_cbcenc_keys; + +/** + * \brief Aggregate structure large enough to be used as context for + * subkeys (CBC decryption) for all DES implementations. + */ +typedef union { + const br_block_cbcdec_class *vtable; + br_des_tab_cbcdec_keys c_tab; + br_des_ct_cbcdec_keys c_ct; +} br_des_gen_cbcdec_keys; + +/** + * \brief Type for a ChaCha20 implementation. + * + * An implementation follows the description in RFC 7539: + * + * - Key is 256 bits (`key` points to exactly 32 bytes). + * + * - IV is 96 bits (`iv` points to exactly 12 bytes). + * + * - Block counter is over 32 bits and starts at value `cc`; the + * resulting value is returned. + * + * Data (pointed to by `data`, of length `len`) is encrypted/decrypted + * in place. If `len` is not a multiple of 64, then the excess bytes from + * the last block processing are dropped (therefore, "chunked" processing + * works only as long as each non-final chunk has a length multiple of 64). + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +typedef uint32_t (*br_chacha20_run)(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief ChaCha20 implementation (straightforward C code, constant-time). + * + * \see br_chacha20_run + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +uint32_t br_chacha20_ct_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief ChaCha20 implementation (SSE2 code, constant-time). + * + * This implementation is available only on x86 platforms, depending on + * compiler support. Moreover, in 32-bit mode, it might not actually run, + * if the underlying hardware does not implement the SSE2 opcode (in + * 64-bit mode, SSE2 is part of the ABI, so if the code could be compiled + * at all, then it can run). Use `br_chacha20_sse2_get()` to safely obtain + * a pointer to that function. + * + * \see br_chacha20_run + * + * \param key secret key (32 bytes). + * \param iv IV (12 bytes). + * \param cc initial counter value. + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + */ +uint32_t br_chacha20_sse2_run(const void *key, + const void *iv, uint32_t cc, void *data, size_t len); + +/** + * \brief Obtain the `sse2` ChaCha20 implementation, if available. + * + * This function returns a pointer to `br_chacha20_sse2_run`, if + * that implementation was compiled in the library _and_ the SSE2 + * opcodes are available on the currently running CPU. If either of + * these conditions is not met, then this function returns `0`. + * + * \return the `sse2` ChaCha20 implementation, or `0`. + */ +br_chacha20_run br_chacha20_sse2_get(void); + +/** + * \brief Type for a ChaCha20+Poly1305 AEAD implementation. + * + * The provided data is encrypted or decrypted with ChaCha20. The + * authentication tag is computed on the concatenation of the + * additional data and the ciphertext, with the padding and lengths + * as described in RFC 7539 (section 2.8). + * + * After decryption, the caller is responsible for checking that the + * computed tag matches the expected value. + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +typedef void (*br_poly1305_run)(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (mixed 32-bit multiplications). + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmul_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (pure 32-bit multiplications). + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmul32_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (i15). + * + * This implementation relies on the generic big integer code "i15" + * (which uses pure 32-bit multiplications). As such, it may save a + * little code footprint in a context where "i15" is already included + * (e.g. for elliptic curves or for RSA); however, it is also + * substantially slower than the ctmul and ctmul32 implementations. + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_i15_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief ChaCha20+Poly1305 AEAD implementation (ctmulq). + * + * This implementation uses 64-bit multiplications (result over 128 bits). + * It is available only on platforms that offer such a primitive (in + * practice, 64-bit architectures). Use `br_poly1305_ctmulq_get()` to + * dynamically obtain a pointer to that function, or 0 if not supported. + * + * \see br_poly1305_run + * + * \param key secret key (32 bytes). + * \param iv nonce (12 bytes). + * \param data data to encrypt or decrypt. + * \param len data length (in bytes). + * \param aad additional authenticated data. + * \param aad_len length of additional authenticated data (in bytes). + * \param tag output buffer for the authentication tag. + * \param ichacha implementation of ChaCha20. + * \param encrypt non-zero for encryption, zero for decryption. + */ +void br_poly1305_ctmulq_run(const void *key, const void *iv, + void *data, size_t len, const void *aad, size_t aad_len, + void *tag, br_chacha20_run ichacha, int encrypt); + +/** + * \brief Get the ChaCha20+Poly1305 "ctmulq" implementation, if available. + * + * This function returns a pointer to the `br_poly1305_ctmulq_run()` + * function if supported on the current platform; otherwise, it returns 0. + * + * \return the ctmulq ChaCha20+Poly1305 implementation, or 0. + */ +br_poly1305_run br_poly1305_ctmulq_get(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h new file mode 100644 index 0000000000..fbe69cc02f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ec.h @@ -0,0 +1,967 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_EC_H__ +#define BR_BEARSSL_EC_H__ + +#include +#include + +#include "t_bearssl_rand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_ec.h + * + * # Elliptic Curves + * + * This file documents the EC implementations provided with BearSSL, and + * ECDSA. + * + * ## Elliptic Curve API + * + * Only "named curves" are supported. Each EC implementation supports + * one or several named curves, identified by symbolic identifiers. + * These identifiers are small integers, that correspond to the values + * registered by the + * [IANA](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8). + * + * Since all currently defined elliptic curve identifiers are in the 0..31 + * range, it is convenient to encode support of some curves in a 32-bit + * word, such that bit x corresponds to curve of identifier x. + * + * An EC implementation is incarnated by a `br_ec_impl` instance, that + * offers the following fields: + * + * - `supported_curves` + * + * A 32-bit word that documents the identifiers of the curves supported + * by this implementation. + * + * - `generator()` + * + * Callback method that returns a pointer to the conventional generator + * point for that curve. + * + * - `order()` + * + * Callback method that returns a pointer to the subgroup order for + * that curve. That value uses unsigned big-endian encoding. + * + * - `xoff()` + * + * Callback method that returns the offset and length of the X + * coordinate in an encoded point. + * + * - `mul()` + * + * Multiply a curve point with an integer. + * + * - `mulgen()` + * + * Multiply the curve generator with an integer. This may be faster + * than the generic `mul()`. + * + * - `muladd()` + * + * Multiply two curve points by two integers, and return the sum of + * the two products. + * + * All curve points are represented in uncompressed format. The `mul()` + * and `muladd()` methods take care to validate that the provided points + * are really part of the relevant curve subgroup. + * + * For all point multiplication functions, the following holds: + * + * - Functions validate that the provided points are valid members + * of the relevant curve subgroup. An error is reported if that is + * not the case. + * + * - Processing is constant-time, even if the point operands are not + * valid. This holds for both the source and resulting points, and + * the multipliers (integers). Only the byte length of the provided + * multiplier arrays (not their actual value length in bits) may + * leak through timing-based side channels. + * + * - The multipliers (integers) MUST be lower than the subgroup order. + * If this property is not met, then the result is indeterminate, + * but an error value is not necessarily returned. + * + * + * ## ECDSA + * + * ECDSA signatures have two standard formats, called "raw" and "asn1". + * Internally, such a signature is a pair of modular integers `(r,s)`. + * The "raw" format is the concatenation of the unsigned big-endian + * encodings of these two integers, possibly left-padded with zeros so + * that they have the same encoded length. The "asn1" format is the + * DER encoding of an ASN.1 structure that contains the two integer + * values: + * + * ECDSASignature ::= SEQUENCE { + * r INTEGER, + * s INTEGER + * } + * + * In general, in all of X.509 and SSL/TLS, the "asn1" format is used. + * BearSSL offers ECDSA implementations for both formats; conversion + * functions between the two formats are also provided. Conversion of a + * "raw" format signature into "asn1" may enlarge a signature by no more + * than 9 bytes for all supported curves; conversely, conversion of an + * "asn1" signature to "raw" may expand the signature but the "raw" + * length will never be more than twice the length of the "asn1" length + * (and usually it will be shorter). + * + * Note that for a given signature, the "raw" format is not fully + * deterministic, in that it does not enforce a minimal common length. + */ + +/* + * Standard curve ID. These ID are equal to the assigned numerical + * identifiers assigned to these curves for TLS: + * http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 + */ + +/** \brief Identifier for named curve sect163k1. */ +#define BR_EC_sect163k1 1 + +/** \brief Identifier for named curve sect163r1. */ +#define BR_EC_sect163r1 2 + +/** \brief Identifier for named curve sect163r2. */ +#define BR_EC_sect163r2 3 + +/** \brief Identifier for named curve sect193r1. */ +#define BR_EC_sect193r1 4 + +/** \brief Identifier for named curve sect193r2. */ +#define BR_EC_sect193r2 5 + +/** \brief Identifier for named curve sect233k1. */ +#define BR_EC_sect233k1 6 + +/** \brief Identifier for named curve sect233r1. */ +#define BR_EC_sect233r1 7 + +/** \brief Identifier for named curve sect239k1. */ +#define BR_EC_sect239k1 8 + +/** \brief Identifier for named curve sect283k1. */ +#define BR_EC_sect283k1 9 + +/** \brief Identifier for named curve sect283r1. */ +#define BR_EC_sect283r1 10 + +/** \brief Identifier for named curve sect409k1. */ +#define BR_EC_sect409k1 11 + +/** \brief Identifier for named curve sect409r1. */ +#define BR_EC_sect409r1 12 + +/** \brief Identifier for named curve sect571k1. */ +#define BR_EC_sect571k1 13 + +/** \brief Identifier for named curve sect571r1. */ +#define BR_EC_sect571r1 14 + +/** \brief Identifier for named curve secp160k1. */ +#define BR_EC_secp160k1 15 + +/** \brief Identifier for named curve secp160r1. */ +#define BR_EC_secp160r1 16 + +/** \brief Identifier for named curve secp160r2. */ +#define BR_EC_secp160r2 17 + +/** \brief Identifier for named curve secp192k1. */ +#define BR_EC_secp192k1 18 + +/** \brief Identifier for named curve secp192r1. */ +#define BR_EC_secp192r1 19 + +/** \brief Identifier for named curve secp224k1. */ +#define BR_EC_secp224k1 20 + +/** \brief Identifier for named curve secp224r1. */ +#define BR_EC_secp224r1 21 + +/** \brief Identifier for named curve secp256k1. */ +#define BR_EC_secp256k1 22 + +/** \brief Identifier for named curve secp256r1. */ +#define BR_EC_secp256r1 23 + +/** \brief Identifier for named curve secp384r1. */ +#define BR_EC_secp384r1 24 + +/** \brief Identifier for named curve secp521r1. */ +#define BR_EC_secp521r1 25 + +/** \brief Identifier for named curve brainpoolP256r1. */ +#define BR_EC_brainpoolP256r1 26 + +/** \brief Identifier for named curve brainpoolP384r1. */ +#define BR_EC_brainpoolP384r1 27 + +/** \brief Identifier for named curve brainpoolP512r1. */ +#define BR_EC_brainpoolP512r1 28 + +/** \brief Identifier for named curve Curve25519. */ +#define BR_EC_curve25519 29 + +/** \brief Identifier for named curve Curve448. */ +#define BR_EC_curve448 30 + +/** + * \brief Structure for an EC public key. + */ +typedef struct { + /** \brief Identifier for the curve used by this key. */ + int curve; + /** \brief Public curve point (uncompressed format). */ + unsigned char *q; + /** \brief Length of public curve point (in bytes). */ + size_t qlen; +} br_ec_public_key; + +/** + * \brief Structure for an EC private key. + * + * The private key is an integer modulo the curve subgroup order. The + * encoding below tolerates extra leading zeros. In general, it is + * recommended that the private key has the same length as the curve + * subgroup order. + */ +typedef struct { + /** \brief Identifier for the curve used by this key. */ + int curve; + /** \brief Private key (integer, unsigned big-endian encoding). */ + unsigned char *x; + /** \brief Private key length (in bytes). */ + size_t xlen; +} br_ec_private_key; + +/** + * \brief Type for an EC implementation. + */ +typedef struct { + /** + * \brief Supported curves. + * + * This word is a bitfield: bit `x` is set if the curve of ID `x` + * is supported. E.g. an implementation supporting both NIST P-256 + * (secp256r1, ID 23) and NIST P-384 (secp384r1, ID 24) will have + * value `0x01800000` in this field. + */ + uint32_t supported_curves; + + /** + * \brief Get the conventional generator. + * + * This function returns the conventional generator (encoded + * curve point) for the specified curve. This function MUST NOT + * be called if the curve is not supported. + * + * \param curve curve identifier. + * \param len receiver for the encoded generator length (in bytes). + * \return the encoded generator. + */ + const unsigned char *(*generator)(int curve, size_t *len); + + /** + * \brief Get the subgroup order. + * + * This function returns the order of the subgroup generated by + * the conventional generator, for the specified curve. Unsigned + * big-endian encoding is used. This function MUST NOT be called + * if the curve is not supported. + * + * \param curve curve identifier. + * \param len receiver for the encoded order length (in bytes). + * \return the encoded order. + */ + const unsigned char *(*order)(int curve, size_t *len); + + /** + * \brief Get the offset and length for the X coordinate. + * + * This function returns the offset and length (in bytes) of + * the X coordinate in an encoded non-zero point. + * + * \param curve curve identifier. + * \param len receiver for the X coordinate length (in bytes). + * \return the offset for the X coordinate (in bytes). + */ + size_t (*xoff)(int curve, size_t *len); + + /** + * \brief Multiply a curve point by an integer. + * + * The source point is provided in array `G` (of size `Glen` bytes); + * the multiplication result is written over it. The multiplier + * `x` (of size `xlen` bytes) uses unsigned big-endian encoding. + * + * Rules: + * + * - The specified curve MUST be supported. + * + * - The source point must be a valid point on the relevant curve + * subgroup (and not the "point at infinity" either). If this is + * not the case, then this function returns an error (0). + * + * - The multiplier integer MUST be non-zero and less than the + * curve subgroup order. If this property does not hold, then + * the result is indeterminate and an error code is not + * guaranteed. + * + * Returned value is 1 on success, 0 on error. On error, the + * contents of `G` are indeterminate. + * + * \param G point to multiply. + * \param Glen length of the encoded point (in bytes). + * \param x multiplier (unsigned big-endian). + * \param xlen multiplier length (in bytes). + * \param curve curve identifier. + * \return 1 on success, 0 on error. + */ + uint32_t (*mul)(unsigned char *G, size_t Glen, + const unsigned char *x, size_t xlen, int curve); + + /** + * \brief Multiply the generator by an integer. + * + * The multiplier MUST be non-zero and less than the curve + * subgroup order. Results are indeterminate if this property + * does not hold. + * + * \param R output buffer for the point. + * \param x multiplier (unsigned big-endian). + * \param xlen multiplier length (in bytes). + * \param curve curve identifier. + * \return encoded result point length (in bytes). + */ + size_t (*mulgen)(unsigned char *R, + const unsigned char *x, size_t xlen, int curve); + + /** + * \brief Multiply two points by two integers and add the + * results. + * + * The point `x*A + y*B` is computed and written back in the `A` + * array. + * + * Rules: + * + * - The specified curve MUST be supported. + * + * - The source points (`A` and `B`) must be valid points on + * the relevant curve subgroup (and not the "point at + * infinity" either). If this is not the case, then this + * function returns an error (0). + * + * - If the `B` pointer is `NULL`, then the conventional + * subgroup generator is used. With some implementations, + * this may be faster than providing a pointer to the + * generator. + * + * - The multiplier integers (`x` and `y`) MUST be non-zero + * and less than the curve subgroup order. If either integer + * is zero, then an error is reported, but if one of them is + * not lower than the subgroup order, then the result is + * indeterminate and an error code is not guaranteed. + * + * - If the final result is the point at infinity, then an + * error is returned. + * + * Returned value is 1 on success, 0 on error. On error, the + * contents of `A` are indeterminate. + * + * \param A first point to multiply. + * \param B second point to multiply (`NULL` for the generator). + * \param len common length of the encoded points (in bytes). + * \param x multiplier for `A` (unsigned big-endian). + * \param xlen length of multiplier for `A` (in bytes). + * \param y multiplier for `A` (unsigned big-endian). + * \param ylen length of multiplier for `A` (in bytes). + * \param curve curve identifier. + * \return 1 on success, 0 on error. + */ + uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len, + const unsigned char *x, size_t xlen, + const unsigned char *y, size_t ylen, int curve); +} br_ec_impl; + +/** + * \brief EC implementation "i31". + * + * This implementation internally uses generic code for modular integers, + * with a representation as sequences of 31-bit words. It supports secp256r1, + * secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521). + */ +extern const br_ec_impl br_ec_prime_i31; + +/** + * \brief EC implementation "i15". + * + * This implementation internally uses generic code for modular integers, + * with a representation as sequences of 15-bit words. It supports secp256r1, + * secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521). + */ +extern const br_ec_impl br_ec_prime_i15; + +/** + * \brief EC implementation "m15" for P-256. + * + * This implementation uses specialised code for curve secp256r1 (also + * known as NIST P-256), with optional Karatsuba decomposition, and fast + * modular reduction thanks to the field modulus special format. Only + * 32-bit multiplications are used (with 32-bit results, not 64-bit). + */ +extern const br_ec_impl br_ec_p256_m15; + +/** + * \brief EC implementation "m31" for P-256. + * + * This implementation uses specialised code for curve secp256r1 (also + * known as NIST P-256), relying on multiplications of 31-bit values + * (MUL31). + */ +extern const br_ec_impl br_ec_p256_m31; + +/** + * \brief EC implementation "m62" (specialised code) for P-256. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_p256_m62_get()` to dynamically obtain a pointer + * to that implementation. + */ +extern const br_ec_impl br_ec_p256_m62; + +/** + * \brief Get the "m62" implementation of P-256, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_p256_m62_get(void); + +/** + * \brief EC implementation "m64" (specialised code) for P-256. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_p256_m64_get()` to dynamically obtain a pointer + * to that implementation. + */ +extern const br_ec_impl br_ec_p256_m64; + +/** + * \brief Get the "m64" implementation of P-256, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_p256_m64_get(void); + +/** + * \brief EC implementation "i15" (generic code) for Curve25519. + * + * This implementation uses the generic code for modular integers (with + * 15-bit words) to support Curve25519. Due to the specificities of the + * curve definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_i15; + +/** + * \brief EC implementation "i31" (generic code) for Curve25519. + * + * This implementation uses the generic code for modular integers (with + * 31-bit words) to support Curve25519. Due to the specificities of the + * curve definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_i31; + +/** + * \brief EC implementation "m15" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 15 bits. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m15; + +/** + * \brief EC implementation "m31" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 31 bits. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m31; + +/** + * \brief EC implementation "m62" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 62 bits, with a 124-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_c25519_m62_get()` to dynamically obtain a pointer + * to that implementation. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m62; + +/** + * \brief Get the "m62" implementation of Curve25519, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_c25519_m62_get(void); + +/** + * \brief EC implementation "m64" (specialised code) for Curve25519. + * + * This implementation uses custom code relying on multiplication of + * integers up to 64 bits, with a 128-bit result. This implementation is + * defined only on platforms that offer the 64x64->128 multiplication + * support; use `br_ec_c25519_m64_get()` to dynamically obtain a pointer + * to that implementation. Due to the specificities of the curve + * definition, the following applies: + * + * - `muladd()` is not implemented (the function returns 0 systematically). + * - `order()` returns 2^255-1, since the point multiplication algorithm + * accepts any 32-bit integer as input (it clears the top bit and low + * three bits systematically). + */ +extern const br_ec_impl br_ec_c25519_m64; + +/** + * \brief Get the "m64" implementation of Curve25519, if available. + * + * \return the implementation, or 0. + */ +const br_ec_impl *br_ec_c25519_m64_get(void); + +/** + * \brief Aggregate EC implementation "m15". + * + * This implementation is a wrapper for: + * + * - `br_ec_c25519_m15` for Curve25519 + * - `br_ec_p256_m15` for NIST P-256 + * - `br_ec_prime_i15` for other curves (NIST P-384 and NIST-P512) + */ +extern const br_ec_impl br_ec_all_m15; + +/** + * \brief Aggregate EC implementation "m31". + * + * This implementation is a wrapper for: + * + * - `br_ec_c25519_m31` for Curve25519 + * - `br_ec_p256_m31` for NIST P-256 + * - `br_ec_prime_i31` for other curves (NIST P-384 and NIST-P512) + */ +extern const br_ec_impl br_ec_all_m31; + +/** + * \brief Get the "default" EC implementation for the current system. + * + * This returns a pointer to the preferred implementation on the + * current system. + * + * \return the default EC implementation. + */ +const br_ec_impl *br_ec_get_default(void); + +/** + * \brief Convert a signature from "raw" to "asn1". + * + * Conversion is done "in place" and the new length is returned. + * Conversion may enlarge the signature, but by no more than 9 bytes at + * most. On error, 0 is returned (error conditions include an odd raw + * signature length, or an oversized integer). + * + * \param sig signature to convert. + * \param sig_len signature length (in bytes). + * \return the new signature length, or 0 on error. + */ +size_t br_ecdsa_raw_to_asn1(void *sig, size_t sig_len); + +/** + * \brief Convert a signature from "asn1" to "raw". + * + * Conversion is done "in place" and the new length is returned. + * Conversion may enlarge the signature, but the new signature length + * will be less than twice the source length at most. On error, 0 is + * returned (error conditions include an invalid ASN.1 structure or an + * oversized integer). + * + * \param sig signature to convert. + * \param sig_len signature length (in bytes). + * \return the new signature length, or 0 on error. + */ +size_t br_ecdsa_asn1_to_raw(void *sig, size_t sig_len); + +/** + * \brief Type for an ECDSA signer function. + * + * A pointer to the EC implementation is provided. The hash value is + * assumed to have the length inferred from the designated hash function + * class. + * + * Signature is written in the buffer pointed to by `sig`, and the length + * (in bytes) is returned. On error, nothing is written in the buffer, + * and 0 is returned. This function returns 0 if the specified curve is + * not supported by the provided EC implementation. + * + * The signature format is either "raw" or "asn1", depending on the + * implementation; maximum length is predictable from the implemented + * curve: + * + * | curve | raw | asn1 | + * | :--------- | --: | ---: | + * | NIST P-256 | 64 | 72 | + * | NIST P-384 | 96 | 104 | + * | NIST P-521 | 132 | 139 | + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +typedef size_t (*br_ecdsa_sign)(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief Type for an ECDSA signature verification function. + * + * A pointer to the EC implementation is provided. The hashed value, + * computed over the purportedly signed data, is also provided with + * its length. + * + * The signature format is either "raw" or "asn1", depending on the + * implementation. + * + * Returned value is 1 on success (valid signature), 0 on error. This + * function returns 0 if the specified curve is not supported by the + * provided EC implementation. + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_ecdsa_vrfy)(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature generator, "i31" implementation, "asn1" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i31_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature generator, "i31" implementation, "raw" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i31_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature verifier, "i31" implementation, "asn1" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature verifier, "i31" implementation, "raw" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature generator, "i15" implementation, "asn1" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i15_sign_asn1(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature generator, "i15" implementation, "raw" format. + * + * \see br_ecdsa_sign() + * + * \param impl EC implementation to use. + * \param hf hash function used to process the data. + * \param hash_value signed data (hashed). + * \param sk EC private key. + * \param sig destination buffer. + * \return the signature length (in bytes), or 0 on error. + */ +size_t br_ecdsa_i15_sign_raw(const br_ec_impl *impl, + const br_hash_class *hf, const void *hash_value, + const br_ec_private_key *sk, void *sig); + +/** + * \brief ECDSA signature verifier, "i15" implementation, "asn1" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief ECDSA signature verifier, "i15" implementation, "raw" format. + * + * \see br_ecdsa_vrfy() + * + * \param impl EC implementation to use. + * \param hash signed data (hashed). + * \param hash_len hash value length (in bytes). + * \param pk EC public key. + * \param sig signature. + * \param sig_len signature length (in bytes). + * \return 1 on success, 0 on error. + */ +uint32_t br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl, + const void *hash, size_t hash_len, + const br_ec_public_key *pk, const void *sig, size_t sig_len); + +/** + * \brief Get "default" ECDSA implementation (signer, asn1 format). + * + * This returns the preferred implementation of ECDSA signature generation + * ("asn1" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_sign br_ecdsa_sign_asn1_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (signer, raw format). + * + * This returns the preferred implementation of ECDSA signature generation + * ("raw" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_sign br_ecdsa_sign_raw_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (verifier, asn1 format). + * + * This returns the preferred implementation of ECDSA signature verification + * ("asn1" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_vrfy br_ecdsa_vrfy_asn1_get_default(void); + +/** + * \brief Get "default" ECDSA implementation (verifier, raw format). + * + * This returns the preferred implementation of ECDSA signature verification + * ("raw" output format) on the current system. + * + * \return the default implementation. + */ +br_ecdsa_vrfy br_ecdsa_vrfy_raw_get_default(void); + +/** + * \brief Maximum size for EC private key element buffer. + * + * This is the largest number of bytes that `br_ec_keygen()` may need or + * ever return. + */ +#define BR_EC_KBUF_PRIV_MAX_SIZE 72 + +/** + * \brief Maximum size for EC public key element buffer. + * + * This is the largest number of bytes that `br_ec_compute_public()` may + * need or ever return. + */ +#define BR_EC_KBUF_PUB_MAX_SIZE 145 + +/** + * \brief Generate a new EC private key. + * + * If the specified `curve` is not supported by the elliptic curve + * implementation (`impl`), then this function returns zero. + * + * The `sk` structure fields are set to the new private key data. In + * particular, `sk.x` is made to point to the provided key buffer (`kbuf`), + * in which the actual private key data is written. That buffer is assumed + * to be large enough. The `BR_EC_KBUF_PRIV_MAX_SIZE` defines the maximum + * size for all supported curves. + * + * The number of bytes used in `kbuf` is returned. If `kbuf` is `NULL`, then + * the private key is not actually generated, and `sk` may also be `NULL`; + * the minimum length for `kbuf` is still computed and returned. + * + * If `sk` is `NULL` but `kbuf` is not `NULL`, then the private key is + * still generated and stored in `kbuf`. + * + * \param rng_ctx source PRNG context (already initialized). + * \param impl the elliptic curve implementation. + * \param sk the private key structure to fill, or `NULL`. + * \param kbuf the key element buffer, or `NULL`. + * \param curve the curve identifier. + * \return the key data length (in bytes), or zero. + */ +size_t br_ec_keygen(const br_prng_class **rng_ctx, + const br_ec_impl *impl, br_ec_private_key *sk, + void *kbuf, int curve); + +/** + * \brief Compute EC public key from EC private key. + * + * This function uses the provided elliptic curve implementation (`impl`) + * to compute the public key corresponding to the private key held in `sk`. + * The public key point is written into `kbuf`, which is then linked from + * the `*pk` structure. The size of the public key point, i.e. the number + * of bytes used in `kbuf`, is returned. + * + * If `kbuf` is `NULL`, then the public key point is NOT computed, and + * the public key structure `*pk` is unmodified (`pk` may be `NULL` in + * that case). The size of the public key point is still returned. + * + * If `pk` is `NULL` but `kbuf` is not `NULL`, then the public key + * point is computed and stored in `kbuf`, and its size is returned. + * + * If the curve used by the private key is not supported by the curve + * implementation, then this function returns zero. + * + * The private key MUST be valid. An off-range private key value is not + * necessarily detected, and leads to unpredictable results. + * + * \param impl the elliptic curve implementation. + * \param pk the public key structure to fill (or `NULL`). + * \param kbuf the public key point buffer (or `NULL`). + * \param sk the source private key. + * \return the public key point length (in bytes), or zero. + */ +size_t br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk, + void *kbuf, const br_ec_private_key *sk); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h new file mode 100644 index 0000000000..ca4fa26cc4 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hash.h @@ -0,0 +1,1346 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_HASH_H__ +#define BR_BEARSSL_HASH_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_hash.h + * + * # Hash Functions + * + * This file documents the API for hash functions. + * + * + * ## Procedural API + * + * For each implemented hash function, of name "`xxx`", the following + * elements are defined: + * + * - `br_xxx_vtable` + * + * An externally defined instance of `br_hash_class`. + * + * - `br_xxx_SIZE` + * + * A macro that evaluates to the output size (in bytes) of the + * hash function. + * + * - `br_xxx_ID` + * + * A macro that evaluates to a symbolic identifier for the hash + * function. Such identifiers are used with HMAC and signature + * algorithm implementations. + * + * NOTE: for the "standard" hash functions defined in [the TLS + * standard](https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1), + * the symbolic identifiers match the constants used in TLS, i.e. + * 1 to 6 for MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512, + * respectively. + * + * - `br_xxx_context` + * + * Context for an ongoing computation. It is allocated by the + * caller, and a pointer to it is passed to all functions. A + * context contains no interior pointer, so it can be moved around + * and cloned (with a simple `memcpy()` or equivalent) in order to + * capture the function state at some point. Computations that use + * distinct context structures are independent of each other. The + * first field of `br_xxx_context` is always a pointer to the + * `br_xxx_vtable` structure; `br_xxx_init()` sets that pointer. + * + * - `br_xxx_init(br_xxx_context *ctx)` + * + * Initialise the provided context. Previous contents of the structure + * are ignored. This calls resets the context to the start of a new + * hash computation; it also sets the first field of the context + * structure (called `vtable`) to a pointer to the statically + * allocated constant `br_xxx_vtable` structure. + * + * - `br_xxx_update(br_xxx_context *ctx, const void *data, size_t len)` + * + * Add some more bytes to the hash computation represented by the + * provided context. + * + * - `br_xxx_out(const br_xxx_context *ctx, void *out)` + * + * Complete the hash computation and write the result in the provided + * buffer. The output buffer MUST be large enough to accommodate the + * result. The context is NOT modified by this operation, so this + * function can be used to get a "partial hash" while still keeping + * the possibility of adding more bytes to the input. + * + * - `br_xxx_state(const br_xxx_context *ctx, void *out)` + * + * Get a copy of the "current state" for the computation so far. For + * MD functions (MD5, SHA-1, SHA-2 family), this is the running state + * resulting from the processing of the last complete input block. + * Returned value is the current input length (in bytes). + * + * - `br_xxx_set_state(br_xxx_context *ctx, const void *stb, uint64_t count)` + * + * Set the internal state to the provided values. The 'stb' and + * 'count' values shall match that which was obtained from + * `br_xxx_state()`. This restores the hash state only if the state + * values were at an appropriate block boundary. This does NOT set + * the `vtable` pointer in the context. + * + * Context structures can be discarded without any explicit deallocation. + * Hash function implementations are purely software and don't reserve + * any resources outside of the context structure itself. + * + * + * ## Object-Oriented API + * + * For each hash function that follows the procedural API described + * above, an object-oriented API is also provided. In that API, function + * pointers from the vtable (`br_xxx_vtable`) are used. The vtable + * incarnates object-oriented programming. An introduction on the OOP + * concept used here can be read on the BearSSL Web site:
    + *    [https://www.bearssl.org/oop.html](https://www.bearssl.org/oop.html) + * + * The vtable offers functions called `init()`, `update()`, `out()`, + * `set()` and `set_state()`, which are in fact the functions from + * the procedural API. That vtable also contains two informative fields: + * + * - `context_size` + * + * The size of the context structure (`br_xxx_context`), in bytes. + * This can be used by generic implementations to perform dynamic + * context allocation. + * + * - `desc` + * + * A "descriptor" field that encodes some information on the hash + * function: symbolic identifier, output size, state size, + * internal block size, details on the padding. + * + * Users of this object-oriented API (in particular generic HMAC + * implementations) may make the following assumptions: + * + * - Hash output size is no more than 64 bytes. + * - Hash internal state size is no more than 64 bytes. + * - Internal block size is a power of two, no less than 16 and no more + * than 256. + * + * + * ## Implemented Hash Functions + * + * Implemented hash functions are: + * + * | Function | Name | Output length | State length | + * | :-------- | :------ | :-----------: | :----------: | + * | MD5 | md5 | 16 | 16 | + * | SHA-1 | sha1 | 20 | 20 | + * | SHA-224 | sha224 | 28 | 32 | + * | SHA-256 | sha256 | 32 | 32 | + * | SHA-384 | sha384 | 48 | 64 | + * | SHA-512 | sha512 | 64 | 64 | + * | MD5+SHA-1 | md5sha1 | 36 | 36 | + * + * (MD5+SHA-1 is the concatenation of MD5 and SHA-1 computed over the + * same input; in the implementation, the internal data buffer is + * shared, thus making it more memory-efficient than separate MD5 and + * SHA-1. It can be useful in implementing SSL 3.0, TLS 1.0 and TLS + * 1.1.) + * + * + * ## Multi-Hasher + * + * An aggregate hasher is provided, that can compute several standard + * hash functions in parallel. It uses `br_multihash_context` and a + * procedural API. It is configured with the implementations (the vtables) + * that it should use; it will then compute all these hash functions in + * parallel, on the same input. It is meant to be used in cases when the + * hash of an object will be used, but the exact hash function is not + * known yet (typically, streamed processing on X.509 certificates). + * + * Only the standard hash functions (MD5, SHA-1, SHA-224, SHA-256, SHA-384 + * and SHA-512) are supported by the multi-hasher. + * + * + * ## GHASH + * + * GHASH is not a generic hash function; it is a _universal_ hash function, + * which, as the name does not say, means that it CANNOT be used in most + * places where a hash function is needed. GHASH is used within the GCM + * encryption mode, to provide the checked integrity functionality. + * + * A GHASH implementation is basically a function that uses the type defined + * in this file under the name `br_ghash`: + * + * typedef void (*br_ghash)(void *y, const void *h, const void *data, size_t len); + * + * The `y` pointer refers to a 16-byte value which is used as input, and + * receives the output of the GHASH invocation. `h` is a 16-byte secret + * value (that serves as key). `data` and `len` define the input data. + * + * Three GHASH implementations are provided, all constant-time, based on + * the use of integer multiplications with appropriate masking to cancel + * carry propagation. + */ + +/** + * \brief Class type for hash function implementations. + * + * A `br_hash_class` instance references the methods implementing a hash + * function. Constant instances of this structure are defined for each + * implemented hash function. Such instances are also called "vtables". + * + * Vtables are used to support object-oriented programming, as + * described on [the BearSSL Web site](https://www.bearssl.org/oop.html). + */ +typedef struct br_hash_class_ br_hash_class; +struct br_hash_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate for + * computing this hash function. + */ + size_t context_size; + + /** + * \brief Descriptor word that contains information about the hash + * function. + * + * For each word `xxx` described below, use `BR_HASHDESC_xxx_OFF` + * and `BR_HASHDESC_xxx_MASK` to access the specific value, as + * follows: + * + * (hf->desc >> BR_HASHDESC_xxx_OFF) & BR_HASHDESC_xxx_MASK + * + * The defined elements are: + * + * - `ID`: the symbolic identifier for the function, as defined + * in [TLS](https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1) + * (MD5 = 1, SHA-1 = 2,...). + * + * - `OUT`: hash output size, in bytes. + * + * - `STATE`: internal running state size, in bytes. + * + * - `LBLEN`: base-2 logarithm for the internal block size, as + * defined for HMAC processing (this is 6 for MD5, SHA-1, SHA-224 + * and SHA-256, since these functions use 64-byte blocks; for + * SHA-384 and SHA-512, this is 7, corresponding to their + * 128-byte blocks). + * + * The descriptor may contain a few other flags. + */ + uint32_t desc; + + /** + * \brief Initialisation method. + * + * This method takes as parameter a pointer to a context area, + * that it initialises. The first field of the context is set + * to this vtable; other elements are initialised for a new hash + * computation. + * + * \param ctx pointer to (the first field of) the context. + */ + void (*init)(const br_hash_class **ctx); + + /** + * \brief Data injection method. + * + * The `len` bytes starting at address `data` are injected into + * the running hash computation incarnated by the specified + * context. The context is updated accordingly. It is allowed + * to have `len == 0`, in which case `data` is ignored (and could + * be `NULL`), and nothing happens. + * on the input data. + * + * \param ctx pointer to (the first field of) the context. + * \param data pointer to the first data byte to inject. + * \param len number of bytes to inject. + */ + void (*update)(const br_hash_class **ctx, const void *data, size_t len); + + /** + * \brief Produce hash output. + * + * The hash output corresponding to all data bytes injected in the + * context since the last `init()` call is computed, and written + * in the buffer pointed to by `dst`. The hash output size depends + * on the implemented hash function (e.g. 16 bytes for MD5). + * The context is _not_ modified by this call, so further bytes + * may be afterwards injected to continue the current computation. + * + * \param ctx pointer to (the first field of) the context. + * \param dst destination buffer for the hash output. + */ + void (*out)(const br_hash_class *const *ctx, void *dst); + + /** + * \brief Get running state. + * + * This method saves the current running state into the `dst` + * buffer. What constitutes the "running state" depends on the + * hash function; for Merkle-Damgård hash functions (like + * MD5 or SHA-1), this is the output obtained after processing + * each block. The number of bytes injected so far is returned. + * The context is not modified by this call. + * + * \param ctx pointer to (the first field of) the context. + * \param dst destination buffer for the state. + * \return the injected total byte length. + */ + uint64_t (*state)(const br_hash_class *const *ctx, void *dst); + + /** + * \brief Set running state. + * + * This methods replaces the running state for the function. + * + * \param ctx pointer to (the first field of) the context. + * \param stb source buffer for the state. + * \param count injected total byte length. + */ + void (*set_state)(const br_hash_class **ctx, + const void *stb, uint64_t count); +}; + +#ifndef BR_DOXYGEN_IGNORE +#define BR_HASHDESC_ID(id) ((uint32_t)(id) << BR_HASHDESC_ID_OFF) +#define BR_HASHDESC_ID_OFF 0 +#define BR_HASHDESC_ID_MASK 0xFF + +#define BR_HASHDESC_OUT(size) ((uint32_t)(size) << BR_HASHDESC_OUT_OFF) +#define BR_HASHDESC_OUT_OFF 8 +#define BR_HASHDESC_OUT_MASK 0x7F + +#define BR_HASHDESC_STATE(size) ((uint32_t)(size) << BR_HASHDESC_STATE_OFF) +#define BR_HASHDESC_STATE_OFF 15 +#define BR_HASHDESC_STATE_MASK 0xFF + +#define BR_HASHDESC_LBLEN(ls) ((uint32_t)(ls) << BR_HASHDESC_LBLEN_OFF) +#define BR_HASHDESC_LBLEN_OFF 23 +#define BR_HASHDESC_LBLEN_MASK 0x0F + +#define BR_HASHDESC_MD_PADDING ((uint32_t)1 << 28) +#define BR_HASHDESC_MD_PADDING_128 ((uint32_t)1 << 29) +#define BR_HASHDESC_MD_PADDING_BE ((uint32_t)1 << 30) +#endif + +/* + * Specific hash functions. + * + * Rules for contexts: + * -- No interior pointer. + * -- No pointer to external dynamically allocated resources. + * -- First field is called 'vtable' and is a pointer to a + * const-qualified br_hash_class instance (pointer is set by init()). + * -- SHA-224 and SHA-256 contexts are identical. + * -- SHA-384 and SHA-512 contexts are identical. + * + * Thus, contexts can be moved and cloned to capture the hash function + * current state; and there is no need for any explicit "release" function. + */ + +/** + * \brief Symbolic identifier for MD5. + */ +#define br_md5_ID 1 + +/** + * \brief MD5 output size (in bytes). + */ +#define br_md5_SIZE 16 + +/** + * \brief Constant vtable for MD5. + */ +extern const br_hash_class br_md5_vtable; + +/** + * \brief MD5 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[4]; +#endif +} br_md5_context; + +/** + * \brief MD5 context initialisation. + * + * This function initialises or resets a context for a new MD5 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_md5_init(br_md5_context *ctx); + +/** + * \brief Inject some data bytes in a running MD5 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_md5_update(br_md5_context *ctx, const void *data, size_t len); + +/** + * \brief Compute MD5 output. + * + * The MD5 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_md5_out(const br_md5_context *ctx, void *out); + +/** + * \brief Save MD5 running state. + * + * The running state for MD5 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_md5_state(const br_md5_context *ctx, void *out); + +/** + * \brief Restore MD5 running state. + * + * The running state for MD5 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_md5_set_state(br_md5_context *ctx, const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-1. + */ +#define br_sha1_ID 2 + +/** + * \brief SHA-1 output size (in bytes). + */ +#define br_sha1_SIZE 20 + +/** + * \brief Constant vtable for SHA-1. + */ +extern const br_hash_class br_sha1_vtable; + +/** + * \brief SHA-1 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[5]; +#endif +} br_sha1_context; + +/** + * \brief SHA-1 context initialisation. + * + * This function initialises or resets a context for a new SHA-1 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha1_init(br_sha1_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-1 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha1_update(br_sha1_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-1 output. + * + * The SHA-1 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha1_out(const br_sha1_context *ctx, void *out); + +/** + * \brief Save SHA-1 running state. + * + * The running state for SHA-1 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha1_state(const br_sha1_context *ctx, void *out); + +/** + * \brief Restore SHA-1 running state. + * + * The running state for SHA-1 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha1_set_state(br_sha1_context *ctx, const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-224. + */ +#define br_sha224_ID 3 + +/** + * \brief SHA-224 output size (in bytes). + */ +#define br_sha224_SIZE 28 + +/** + * \brief Constant vtable for SHA-224. + */ +extern const br_hash_class br_sha224_vtable; + +/** + * \brief SHA-224 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val[8]; +#endif +} br_sha224_context; + +/** + * \brief SHA-224 context initialisation. + * + * This function initialises or resets a context for a new SHA-224 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha224_init(br_sha224_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-224 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha224_update(br_sha224_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-224 output. + * + * The SHA-224 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha224_out(const br_sha224_context *ctx, void *out); + +/** + * \brief Save SHA-224 running state. + * + * The running state for SHA-224 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha224_state(const br_sha224_context *ctx, void *out); + +/** + * \brief Restore SHA-224 running state. + * + * The running state for SHA-224 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha224_set_state(br_sha224_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-256. + */ +#define br_sha256_ID 4 + +/** + * \brief SHA-256 output size (in bytes). + */ +#define br_sha256_SIZE 32 + +/** + * \brief Constant vtable for SHA-256. + */ +extern const br_hash_class br_sha256_vtable; + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief SHA-256 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +} br_sha256_context; +#else +typedef br_sha224_context br_sha256_context; +#endif + +/** + * \brief SHA-256 context initialisation. + * + * This function initialises or resets a context for a new SHA-256 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha256_init(br_sha256_context *ctx); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Inject some data bytes in a running SHA-256 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha256_update(br_sha256_context *ctx, const void *data, size_t len); +#else +#define br_sha256_update br_sha224_update +#endif + +/** + * \brief Compute SHA-256 output. + * + * The SHA-256 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha256_out(const br_sha256_context *ctx, void *out); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Save SHA-256 running state. + * + * The running state for SHA-256 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha256_state(const br_sha256_context *ctx, void *out); +#else +#define br_sha256_state br_sha224_state +#endif + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Restore SHA-256 running state. + * + * The running state for SHA-256 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha256_set_state(br_sha256_context *ctx, + const void *stb, uint64_t count); +#else +#define br_sha256_set_state br_sha224_set_state +#endif + +/** + * \brief Symbolic identifier for SHA-384. + */ +#define br_sha384_ID 5 + +/** + * \brief SHA-384 output size (in bytes). + */ +#define br_sha384_SIZE 48 + +/** + * \brief Constant vtable for SHA-384. + */ +extern const br_hash_class br_sha384_vtable; + +/** + * \brief SHA-384 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[128]; + uint64_t count; + uint64_t val[8]; +#endif +} br_sha384_context; + +/** + * \brief SHA-384 context initialisation. + * + * This function initialises or resets a context for a new SHA-384 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha384_init(br_sha384_context *ctx); + +/** + * \brief Inject some data bytes in a running SHA-384 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha384_update(br_sha384_context *ctx, const void *data, size_t len); + +/** + * \brief Compute SHA-384 output. + * + * The SHA-384 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha384_out(const br_sha384_context *ctx, void *out); + +/** + * \brief Save SHA-384 running state. + * + * The running state for SHA-384 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha384_state(const br_sha384_context *ctx, void *out); + +/** + * \brief Restore SHA-384 running state. + * + * The running state for SHA-384 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha384_set_state(br_sha384_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Symbolic identifier for SHA-512. + */ +#define br_sha512_ID 6 + +/** + * \brief SHA-512 output size (in bytes). + */ +#define br_sha512_SIZE 64 + +/** + * \brief Constant vtable for SHA-512. + */ +extern const br_hash_class br_sha512_vtable; + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief SHA-512 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +} br_sha512_context; +#else +typedef br_sha384_context br_sha512_context; +#endif + +/** + * \brief SHA-512 context initialisation. + * + * This function initialises or resets a context for a new SHA-512 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_sha512_init(br_sha512_context *ctx); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Inject some data bytes in a running SHA-512 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_sha512_update(br_sha512_context *ctx, const void *data, size_t len); +#else +#define br_sha512_update br_sha384_update +#endif + +/** + * \brief Compute SHA-512 output. + * + * The SHA-512 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_sha512_out(const br_sha512_context *ctx, void *out); + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Save SHA-512 running state. + * + * The running state for SHA-512 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_sha512_state(const br_sha512_context *ctx, void *out); +#else +#define br_sha512_state br_sha384_state +#endif + +#ifdef BR_DOXYGEN_IGNORE +/** + * \brief Restore SHA-512 running state. + * + * The running state for SHA-512 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_sha512_set_state(br_sha512_context *ctx, + const void *stb, uint64_t count); +#else +#define br_sha512_set_state br_sha384_set_state +#endif + +/* + * "md5sha1" is a special hash function that computes both MD5 and SHA-1 + * on the same input, and produces a 36-byte output (MD5 and SHA-1 + * concatenation, in that order). State size is also 36 bytes. + */ + +/** + * \brief Symbolic identifier for MD5+SHA-1. + * + * MD5+SHA-1 is the concatenation of MD5 and SHA-1, computed over the + * same input. It is not one of the functions identified in TLS, so + * we give it a symbolic identifier of value 0. + */ +#define br_md5sha1_ID 0 + +/** + * \brief MD5+SHA-1 output size (in bytes). + */ +#define br_md5sha1_SIZE 36 + +/** + * \brief Constant vtable for MD5+SHA-1. + */ +extern const br_hash_class br_md5sha1_vtable; + +/** + * \brief MD5+SHA-1 context. + * + * First field is a pointer to the vtable; it is set by the initialisation + * function. Other fields are not supposed to be accessed by user code. + */ +typedef struct { + /** + * \brief Pointer to vtable for this context. + */ + const br_hash_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[64]; + uint64_t count; + uint32_t val_md5[4]; + uint32_t val_sha1[5]; +#endif +} br_md5sha1_context; + +/** + * \brief MD5+SHA-1 context initialisation. + * + * This function initialises or resets a context for a new SHA-512 + * computation. It also sets the vtable pointer. + * + * \param ctx pointer to the context structure. + */ +void br_md5sha1_init(br_md5sha1_context *ctx); + +/** + * \brief Inject some data bytes in a running MD5+SHA-1 computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_md5sha1_update(br_md5sha1_context *ctx, const void *data, size_t len); + +/** + * \brief Compute MD5+SHA-1 output. + * + * The MD5+SHA-1 output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `out`. The context + * itself is not modified, so extra bytes may be injected afterwards + * to continue that computation. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the hash output. + */ +void br_md5sha1_out(const br_md5sha1_context *ctx, void *out); + +/** + * \brief Save MD5+SHA-1 running state. + * + * The running state for MD5+SHA-1 (output of the last internal block + * processing) is written in the buffer pointed to by `out`. The + * number of bytes injected since the last initialisation or reset + * call is returned. The context is not modified. + * + * \param ctx pointer to the context structure. + * \param out destination buffer for the running state. + * \return the injected total byte length. + */ +uint64_t br_md5sha1_state(const br_md5sha1_context *ctx, void *out); + +/** + * \brief Restore MD5+SHA-1 running state. + * + * The running state for MD5+SHA-1 is set to the provided values. + * + * \param ctx pointer to the context structure. + * \param stb source buffer for the running state. + * \param count the injected total byte length. + */ +void br_md5sha1_set_state(br_md5sha1_context *ctx, + const void *stb, uint64_t count); + +/** + * \brief Aggregate context for configurable hash function support. + * + * The `br_hash_compat_context` type is a type which is large enough to + * serve as context for all standard hash functions defined above. + */ +typedef union { + const br_hash_class *vtable; + br_md5_context md5; + br_sha1_context sha1; + br_sha224_context sha224; + br_sha256_context sha256; + br_sha384_context sha384; + br_sha512_context sha512; + br_md5sha1_context md5sha1; +} br_hash_compat_context; + +/* + * The multi-hasher is a construct that handles hashing of the same input + * data with several hash functions, with a single shared input buffer. + * It can handle MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 + * simultaneously, though which functions are activated depends on + * the set implementation pointers. + */ + +/** + * \brief Multi-hasher context structure. + * + * The multi-hasher runs up to six hash functions in the standard TLS list + * (MD5, SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512) in parallel, over + * the same input. + * + * The multi-hasher does _not_ follow the OOP structure with a vtable. + * Instead, it is configured with the vtables of the hash functions it + * should run. Structure fields are not supposed to be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char buf[128]; + uint64_t count; + uint32_t val_32[25]; + uint64_t val_64[16]; + const br_hash_class *impl[6]; +#endif +} br_multihash_context; + +/** + * \brief Clear a multi-hasher context. + * + * This should always be called once on a given context, _before_ setting + * the implementation pointers. + * + * \param ctx the multi-hasher context. + */ +void br_multihash_zero(br_multihash_context *ctx); + +/** + * \brief Set a hash function implementation. + * + * Implementations shall be set _after_ clearing the context (with + * `br_multihash_zero()`) but _before_ initialising the computation + * (with `br_multihash_init()`). The hash function implementation + * MUST be one of the standard hash functions (MD5, SHA-1, SHA-224, + * SHA-256, SHA-384 or SHA-512); it may also be `NULL` to remove + * an implementation from the multi-hasher. + * + * \param ctx the multi-hasher context. + * \param id the hash function symbolic identifier. + * \param impl the hash function vtable, or `NULL`. + */ +static inline void +br_multihash_setimpl(br_multihash_context *ctx, + int id, const br_hash_class *impl) +{ + /* + * This code relies on hash functions ID being values 1 to 6, + * in the MD5 to SHA-512 order. + */ + ctx->impl[id - 1] = impl; +} + +/** + * \brief Get a hash function implementation. + * + * This function returns the currently configured vtable for a given + * hash function (by symbolic ID). If no such function was configured in + * the provided multi-hasher context, then this function returns `NULL`. + * + * \param ctx the multi-hasher context. + * \param id the hash function symbolic identifier. + * \return the hash function vtable, or `NULL`. + */ +static inline const br_hash_class * +br_multihash_getimpl(const br_multihash_context *ctx, int id) +{ + return ctx->impl[id - 1]; +} + +/** + * \brief Reset a multi-hasher context. + * + * This function prepares the context for a new hashing computation, + * for all implementations configured at that point. + * + * \param ctx the multi-hasher context. + */ +void br_multihash_init(br_multihash_context *ctx); + +/** + * \brief Inject some data bytes in a running multi-hashing computation. + * + * The provided context is updated with some data bytes. If the number + * of bytes (`len`) is zero, then the data pointer (`data`) is ignored + * and may be `NULL`, and this function does nothing. + * + * \param ctx pointer to the context structure. + * \param data pointer to the injected data. + * \param len injected data length (in bytes). + */ +void br_multihash_update(br_multihash_context *ctx, + const void *data, size_t len); + +/** + * \brief Compute a hash output from a multi-hasher. + * + * The hash output for the concatenation of all bytes injected in the + * provided context since the last initialisation or reset call, is + * computed and written in the buffer pointed to by `dst`. The hash + * function to use is identified by `id` and must be one of the standard + * hash functions. If that hash function was indeed configured in the + * multi-hasher context, the corresponding hash value is written in + * `dst` and its length (in bytes) is returned. If the hash function + * was _not_ configured, then nothing is written in `dst` and 0 is + * returned. + * + * The context itself is not modified, so extra bytes may be injected + * afterwards to continue the hash computations. + * + * \param ctx pointer to the context structure. + * \param id the hash function symbolic identifier. + * \param dst destination buffer for the hash output. + * \return the hash output length (in bytes), or 0. + */ +size_t br_multihash_out(const br_multihash_context *ctx, int id, void *dst); + +/** + * \brief Type for a GHASH implementation. + * + * GHASH is a sort of keyed hash meant to be used to implement GCM in + * combination with a block cipher (with 16-byte blocks). + * + * The `y` array has length 16 bytes and is used for input and output; in + * a complete GHASH run, it starts with an all-zero value. `h` is a 16-byte + * value that serves as key (it is derived from the encryption key in GCM, + * using the block cipher). The data length (`len`) is expressed in bytes. + * The `y` array is updated. + * + * If the data length is not a multiple of 16, then the data is implicitly + * padded with zeros up to the next multiple of 16. Thus, when using GHASH + * in GCM, this method may be called twice, for the associated data and + * for the ciphertext, respectively; the zero-padding implements exactly + * the GCM rules. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +typedef void (*br_ghash)(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (mixed 32-bit). + * + * This implementation uses multiplications of 32-bit values, with a + * 64-bit result. It is constant-time (if multiplications are + * constant-time). + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (strict 32-bit). + * + * This implementation uses multiplications of 32-bit values, with a + * 32-bit result. It is usually somewhat slower than `br_ghash_ctmul()`, + * but it is expected to be faster on architectures for which the + * 32-bit multiplication opcode does not yield the upper 32 bits of the + * product. It is constant-time (if multiplications are constant-time). + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul32(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using multiplications (64-bit). + * + * This implementation uses multiplications of 64-bit values, with a + * 64-bit result. It is constant-time (if multiplications are + * constant-time). It is substantially faster than `br_ghash_ctmul()` + * and `br_ghash_ctmul32()` on most 64-bit architectures. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_ctmul64(void *y, const void *h, const void *data, size_t len); + +/** + * \brief GHASH implementation using the `pclmulqdq` opcode (part of the + * AES-NI instructions). + * + * This implementation is available only on x86 platforms where the + * compiler supports the relevant intrinsic functions. Even if the + * compiler supports these functions, the local CPU might not support + * the `pclmulqdq` opcode, meaning that a call will fail with an + * illegal instruction exception. To safely obtain a pointer to this + * function when supported (or 0 otherwise), use `br_ghash_pclmul_get()`. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_pclmul(void *y, const void *h, const void *data, size_t len); + +/** + * \brief Obtain the `pclmul` GHASH implementation, if available. + * + * If the `pclmul` implementation was compiled in the library (depending + * on the compiler abilities) _and_ the local CPU appears to support the + * opcode, then this function will return a pointer to the + * `br_ghash_pclmul()` function. Otherwise, it will return `0`. + * + * \return the `pclmul` GHASH implementation, or `0`. + */ +br_ghash br_ghash_pclmul_get(void); + +/** + * \brief GHASH implementation using the POWER8 opcodes. + * + * This implementation is available only on POWER8 platforms (and later). + * To safely obtain a pointer to this function when supported (or 0 + * otherwise), use `br_ghash_pwr8_get()`. + * + * \param y the array to update. + * \param h the GHASH key. + * \param data the input data (may be `NULL` if `len` is zero). + * \param len the input data length (in bytes). + */ +void br_ghash_pwr8(void *y, const void *h, const void *data, size_t len); + +/** + * \brief Obtain the `pwr8` GHASH implementation, if available. + * + * If the `pwr8` implementation was compiled in the library (depending + * on the compiler abilities) _and_ the local CPU appears to support the + * opcode, then this function will return a pointer to the + * `br_ghash_pwr8()` function. Otherwise, it will return `0`. + * + * \return the `pwr8` GHASH implementation, or `0`. + */ +br_ghash br_ghash_pwr8_get(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h new file mode 100644 index 0000000000..bab2afe140 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_hmac.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_HMAC_H__ +#define BR_BEARSSL_HMAC_H__ + +#include +#include + +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_hmac.h + * + * # HMAC + * + * HMAC is initialized with a key and an underlying hash function; it + * then fills a "key context". That context contains the processed + * key. + * + * With the key context, a HMAC context can be initialized to process + * the input bytes and obtain the MAC output. The key context is not + * modified during that process, and can be reused. + * + * IMPORTANT: HMAC shall be used only with functions that have the + * following properties: + * + * - hash output size does not exceed 64 bytes; + * - hash internal state size does not exceed 64 bytes; + * - internal block length is a power of 2 between 16 and 256 bytes. + */ + +/** + * \brief HMAC key context. + * + * The HMAC key context is initialised with a hash function implementation + * and a secret key. Contents are opaque (callers should not access them + * directly). The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + const br_hash_class *dig_vtable; + unsigned char ksi[64], kso[64]; +#endif +} br_hmac_key_context; + +/** + * \brief HMAC key context initialisation. + * + * Initialise the key context with the provided key, using the hash function + * identified by `digest_vtable`. This supports arbitrary key lengths. + * + * \param kc HMAC key context to initialise. + * \param digest_vtable pointer to the hash function implementation vtable. + * \param key pointer to the HMAC secret key. + * \param key_len HMAC secret key length (in bytes). + */ +void br_hmac_key_init(br_hmac_key_context *kc, + const br_hash_class *digest_vtable, const void *key, size_t key_len); + +/* + * \brief Get the underlying hash function. + * + * This function returns a pointer to the implementation vtable of the + * hash function used for this HMAC key context. + * + * \param kc HMAC key context. + * \return the hash function implementation. + */ +static inline const br_hash_class *br_hmac_key_get_digest( + const br_hmac_key_context *kc) +{ + return kc->dig_vtable; +} + +/** + * \brief HMAC computation context. + * + * The HMAC computation context maintains the state for a single HMAC + * computation. It is modified as input bytes are injected. The context + * is caller-allocated and has no release function since it does not + * dynamically allocate external resources. Its contents are opaque. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + br_hash_compat_context dig; + unsigned char kso[64]; + size_t out_len; +#endif +} br_hmac_context; + +/** + * \brief HMAC computation initialisation. + * + * Initialise a HMAC context with a key context. The key context is + * unmodified. Relevant data from the key context is immediately copied; + * the key context can thus be independently reused, modified or released + * without impacting this HMAC computation. + * + * An explicit output length can be specified; the actual output length + * will be the minimum of that value and the natural HMAC output length. + * If `out_len` is 0, then the natural HMAC output length is selected. The + * "natural output length" is the output length of the underlying hash + * function. + * + * \param ctx HMAC context to initialise. + * \param kc HMAC key context (already initialised with the key). + * \param out_len HMAC output length (0 to select "natural length"). + */ +void br_hmac_init(br_hmac_context *ctx, + const br_hmac_key_context *kc, size_t out_len); + +/** + * \brief Get the HMAC output size. + * + * The HMAC output size is the number of bytes that will actually be + * produced with `br_hmac_out()` with the provided context. This function + * MUST NOT be called on a non-initialised HMAC computation context. + * The returned value is the minimum of the HMAC natural length (output + * size of the underlying hash function) and the `out_len` parameter which + * was used with the last `br_hmac_init()` call on that context (if the + * initialisation `out_len` parameter was 0, then this function will + * return the HMAC natural length). + * + * \param ctx the (already initialised) HMAC computation context. + * \return the HMAC actual output size. + */ +static inline size_t +br_hmac_size(br_hmac_context *ctx) +{ + return ctx->out_len; +} + +/* + * \brief Get the underlying hash function. + * + * This function returns a pointer to the implementation vtable of the + * hash function used for this HMAC context. + * + * \param hc HMAC context. + * \return the hash function implementation. + */ +static inline const br_hash_class *br_hmac_get_digest( + const br_hmac_context *hc) +{ + return hc->dig.vtable; +} + +/** + * \brief Inject some bytes in HMAC. + * + * The provided `len` bytes are injected as extra input in the HMAC + * computation incarnated by the `ctx` HMAC context. It is acceptable + * that `len` is zero, in which case `data` is ignored (and may be + * `NULL`) and this function does nothing. + */ +void br_hmac_update(br_hmac_context *ctx, const void *data, size_t len); + +/** + * \brief Compute the HMAC output. + * + * The destination buffer MUST be large enough to accommodate the result; + * its length is at most the "natural length" of HMAC (i.e. the output + * length of the underlying hash function). The context is NOT modified; + * further bytes may be processed. Thus, "partial HMAC" values can be + * efficiently obtained. + * + * Returned value is the output length (in bytes). + * + * \param ctx HMAC computation context. + * \param out destination buffer for the HMAC output. + * \return the produced value length (in bytes). + */ +size_t br_hmac_out(const br_hmac_context *ctx, void *out); + +/** + * \brief Constant-time HMAC computation. + * + * This function compute the HMAC output in constant time. Some extra + * input bytes are processed, then the output is computed. The extra + * input consists in the `len` bytes pointed to by `data`. The `len` + * parameter must lie between `min_len` and `max_len` (inclusive); + * `max_len` bytes are actually read from `data`. Computing time (and + * memory access pattern) will not depend upon the data byte contents or + * the value of `len`. + * + * The output is written in the `out` buffer, that MUST be large enough + * to receive it. + * + * The difference `max_len - min_len` MUST be less than 230 + * (i.e. about one gigabyte). + * + * This function computes the output properly only if the underlying + * hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256, + * SHA-384 or SHA-512). + * + * The provided context is NOT modified. + * + * \param ctx the (already initialised) HMAC computation context. + * \param data the extra input bytes. + * \param len the extra input length (in bytes). + * \param min_len minimum extra input length (in bytes). + * \param max_len maximum extra input length (in bytes). + * \param out destination buffer for the HMAC output. + * \return the produced value length (in bytes). + */ +size_t br_hmac_outCT(const br_hmac_context *ctx, + const void *data, size_t len, size_t min_len, size_t max_len, + void *out); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h new file mode 100644 index 0000000000..417c8d6291 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_kdf.h @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_KDF_H__ +#define BR_BEARSSL_KDF_H__ + +#include +#include + +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_kdf.h + * + * # Key Derivation Functions + * + * KDF are functions that takes a variable length input, and provide a + * variable length output, meant to be used to derive subkeys from a + * master key. + * + * ## HKDF + * + * HKDF is a KDF defined by [RFC 5869](https://tools.ietf.org/html/rfc5869). + * It is based on HMAC, itself using an underlying hash function. Any + * hash function can be used, as long as it is compatible with the rules + * for the HMAC implementation (i.e. output size is 64 bytes or less, hash + * internal state size is 64 bytes or less, and the internal block length is + * a power of 2 between 16 and 256 bytes). HKDF has two phases: + * + * - HKDF-Extract: the input data in ingested, along with a "salt" value. + * + * - HKDF-Expand: the output is produced, from the result of processing + * the input and salt, and using an extra non-secret parameter called + * "info". + * + * The "salt" and "info" strings are non-secret and can be empty. Their role + * is normally to bind the input and output, respectively, to conventional + * identifiers that qualifu them within the used protocol or application. + * + * The implementation defined in this file uses the following functions: + * + * - `br_hkdf_init()`: initialize an HKDF context, with a hash function, + * and the salt. This starts the HKDF-Extract process. + * + * - `br_hkdf_inject()`: inject more input bytes. This function may be + * called repeatedly if the input data is provided by chunks. + * + * - `br_hkdf_flip()`: end the HKDF-Extract process, and start the + * HKDF-Expand process. + * + * - `br_hkdf_produce()`: get the next bytes of output. This function + * may be called several times to obtain the full output by chunks. + * For correct HKDF processing, the same "info" string must be + * provided for each call. + * + * Note that the HKDF total output size (the number of bytes that + * HKDF-Expand is willing to produce) is limited: if the hash output size + * is _n_ bytes, then the maximum output size is _255*n_. + * + * ## SHAKE + * + * SHAKE is defined in + * [FIPS 202](https://csrc.nist.gov/publications/detail/fips/202/final) + * under two versions: SHAKE128 and SHAKE256, offering an alleged + * "security level" of 128 and 256 bits, respectively (SHAKE128 is + * about 20 to 25% faster than SHAKE256). SHAKE internally relies on + * the Keccak family of sponge functions, not on any externally provided + * hash function. Contrary to HKDF, SHAKE does not have a concept of + * either a "salt" or an "info" string. The API consists in four + * functions: + * + * - `br_shake_init()`: initialize a SHAKE context for a given + * security level. + * + * - `br_shake_inject()`: inject more input bytes. This function may be + * called repeatedly if the input data is provided by chunks. + * + * - `br_shake_flip()`: end the data injection process, and start the + * data production process. + * + * - `br_shake_produce()`: get the next bytes of output. This function + * may be called several times to obtain the full output by chunks. + */ + +/** + * \brief HKDF context. + * + * The HKDF context is initialized with a hash function implementation + * and a salt value. Contents are opaque (callers should not access them + * directly). The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + union { + br_hmac_context hmac_ctx; + br_hmac_key_context prk_ctx; + } u; + unsigned char buf[64]; + size_t ptr; + size_t dig_len; + unsigned chunk_num; +#endif +} br_hkdf_context; + +/** + * \brief HKDF context initialization. + * + * The underlying hash function and salt value are provided. Arbitrary + * salt lengths can be used. + * + * HKDF makes a difference between a salt of length zero, and an + * absent salt (the latter being equivalent to a salt consisting of + * bytes of value zero, of the same length as the hash function output). + * If `salt_len` is zero, then this function assumes that the salt is + * present but of length zero. To specify an _absent_ salt, use + * `BR_HKDF_NO_SALT` as `salt` parameter (`salt_len` is then ignored). + * + * \param hc HKDF context to initialise. + * \param digest_vtable pointer to the hash function implementation vtable. + * \param salt HKDF-Extract salt. + * \param salt_len HKDF-Extract salt length (in bytes). + */ +void br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable, + const void *salt, size_t salt_len); + +/** + * \brief The special "absent salt" value for HKDF. + */ +#define BR_HKDF_NO_SALT (&br_hkdf_no_salt) + +#ifndef BR_DOXYGEN_IGNORE +extern const unsigned char br_hkdf_no_salt; +#endif + +/** + * \brief HKDF input injection (HKDF-Extract). + * + * This function injects some more input bytes ("key material") into + * HKDF. This function may be called several times, after `br_hkdf_init()` + * but before `br_hkdf_flip()`. + * + * \param hc HKDF context. + * \param ikm extra input bytes. + * \param ikm_len number of extra input bytes. + */ +void br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len); + +/** + * \brief HKDF switch to the HKDF-Expand phase. + * + * This call terminates the HKDF-Extract process (input injection), and + * starts the HKDF-Expand process (output production). + * + * \param hc HKDF context. + */ +void br_hkdf_flip(br_hkdf_context *hc); + +/** + * \brief HKDF output production (HKDF-Expand). + * + * Produce more output bytes from the current state. This function may be + * called several times, but only after `br_hkdf_flip()`. + * + * Returned value is the number of actually produced bytes. The total + * output length is limited to 255 times the output length of the + * underlying hash function. + * + * \param hc HKDF context. + * \param info application specific information string. + * \param info_len application specific information string length (in bytes). + * \param out destination buffer for the HKDF output. + * \param out_len the length of the requested output (in bytes). + * \return the produced output length (in bytes). + */ +size_t br_hkdf_produce(br_hkdf_context *hc, + const void *info, size_t info_len, void *out, size_t out_len); + +/** + * \brief SHAKE context. + * + * The HKDF context is initialized with a "security level". The internal + * notion is called "capacity"; the capacity is twice the security level + * (for instance, SHAKE128 has capacity 256). + * + * The caller is responsible for allocating the context where + * appropriate. Context initialisation and usage incurs no dynamic + * allocation, so there is no release function. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + unsigned char dbuf[200]; + size_t dptr; + size_t rate; + uint64_t A[25]; +#endif +} br_shake_context; + +/** + * \brief SHAKE context initialization. + * + * The context is initialized for the provided "security level". + * Internally, this sets the "capacity" to twice the security level; + * thus, for SHAKE128, the `security_level` parameter should be 128, + * which corresponds to a 256-bit capacity. + * + * Allowed security levels are all multiples of 32, from 32 to 768, + * inclusive. Larger security levels imply lower performance; levels + * beyond 256 bits don't make much sense. Standard levels are 128 + * and 256 bits (for SHAKE128 and SHAKE256, respectively). + * + * \param sc SHAKE context to initialise. + * \param security_level security level (in bits). + */ +void br_shake_init(br_shake_context *sc, int security_level); + +/** + * \brief SHAKE input injection. + * + * This function injects some more input bytes ("key material") into + * SHAKE. This function may be called several times, after `br_shake_init()` + * but before `br_shake_flip()`. + * + * \param sc SHAKE context. + * \param data extra input bytes. + * \param len number of extra input bytes. + */ +void br_shake_inject(br_shake_context *sc, const void *data, size_t len); + +/** + * \brief SHAKE switch to production phase. + * + * This call terminates the input injection process, and starts the + * output production process. + * + * \param sc SHAKE context. + */ +void br_shake_flip(br_shake_context *hc); + +/** + * \brief SHAKE output production. + * + * Produce more output bytes from the current state. This function may be + * called several times, but only after `br_shake_flip()`. + * + * There is no practical limit to the number of bytes that may be produced. + * + * \param sc SHAKE context. + * \param out destination buffer for the SHAKE output. + * \param len the length of the requested output (in bytes). + */ +void br_shake_produce(br_shake_context *sc, void *out, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h new file mode 100644 index 0000000000..8dba58272c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_pem.h @@ -0,0 +1,294 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_PEM_H__ +#define BR_BEARSSL_PEM_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_pem.h + * + * # PEM Support + * + * PEM is a traditional encoding layer use to store binary objects (in + * particular X.509 certificates, and private keys) in text files. While + * the acronym comes from an old, defunct standard ("Privacy Enhanced + * Mail"), the format has been reused, with some variations, by many + * systems, and is a _de facto_ standard, even though it is not, actually, + * specified in all clarity anywhere. + * + * ## Format Details + * + * BearSSL contains a generic, streamed PEM decoder, which handles the + * following format: + * + * - The input source (a sequence of bytes) is assumed to be the + * encoding of a text file in an ASCII-compatible charset. This + * includes ISO-8859-1, Windows-1252, and UTF-8 encodings. Each + * line ends on a newline character (U+000A LINE FEED). The + * U+000D CARRIAGE RETURN characters are ignored, so the code + * accepts both Windows-style and Unix-style line endings. + * + * - Each object begins with a banner that occurs at the start of + * a line; the first banner characters are "`-----BEGIN `" (five + * dashes, the word "BEGIN", and a space). The banner matching is + * not case-sensitive. + * + * - The _object name_ consists in the characters that follow the + * banner start sequence, up to the end of the line, but without + * trailing dashes (in "normal" PEM, there are five trailing + * dashes, but this implementation is not picky about these dashes). + * The BearSSL decoder normalises the name characters to uppercase + * (for ASCII letters only) and accepts names up to 127 characters. + * + * - The object ends with a banner that again occurs at the start of + * a line, and starts with "`-----END `" (again case-insensitive). + * + * - Between that start and end banner, only Base64 data shall occur. + * Base64 converts each sequence of three bytes into four + * characters; the four characters are ASCII letters, digits, "`+`" + * or "`-`" signs, and one or two "`=`" signs may occur in the last + * quartet. Whitespace is ignored (whitespace is any ASCII character + * of code 32 or less, so control characters are whitespace) and + * lines may have arbitrary length; the only restriction is that the + * four characters of a quartet must appear on the same line (no + * line break inside a quartet). + * + * - A single file may contain more than one PEM object. Bytes that + * occur between objects are ignored. + * + * + * ## PEM Decoder API + * + * The PEM decoder offers a state-machine API. The caller allocates a + * decoder context, then injects source bytes. Source bytes are pushed + * with `br_pem_decoder_push()`. The decoder stops accepting bytes when + * it reaches an "event", which is either the start of an object, the + * end of an object, or a decoding error within an object. + * + * The `br_pem_decoder_event()` function is used to obtain the current + * event; it also clears it, thus allowing the decoder to accept more + * bytes. When a object start event is raised, the decoder context + * offers the found object name (normalised to ASCII uppercase). + * + * When an object is reached, the caller must set an appropriate callback + * function, which will receive (by chunks) the decoded object data. + * + * Since the decoder context makes no dynamic allocation, it requires + * no explicit deallocation. + */ + +/** + * \brief PEM decoder context. + * + * Contents are opaque (they should not be accessed directly). + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + const unsigned char *hbuf; + size_t hlen; + + void (*dest)(void *dest_ctx, const void *src, size_t len); + void *dest_ctx; + + unsigned char event; + char name[128]; + unsigned char buf[255]; + size_t ptr; +#endif +} br_pem_decoder_context; + +/** + * \brief Initialise a PEM decoder structure. + * + * \param ctx decoder context to initialise. + */ +void br_pem_decoder_init(br_pem_decoder_context *ctx); + +/** + * \brief Push some bytes into the decoder. + * + * Returned value is the number of bytes actually consumed; this may be + * less than the number of provided bytes if an event is raised. When an + * event is raised, it must be read (with `br_pem_decoder_event()`); + * until the event is read, this function will return 0. + * + * \param ctx decoder context. + * \param data new data bytes. + * \param len number of new data bytes. + * \return the number of bytes actually received (may be less than `len`). + */ +size_t br_pem_decoder_push(br_pem_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Set the receiver for decoded data. + * + * When an object is entered, the provided function (with opaque context + * pointer) will be called repeatedly with successive chunks of decoded + * data for that object. If `dest` is set to 0, then decoded data is + * simply ignored. The receiver can be set at any time, but, in practice, + * it should be called immediately after receiving a "start of object" + * event. + * + * \param ctx decoder context. + * \param dest callback for receiving decoded data. + * \param dest_ctx opaque context pointer for the `dest` callback. + */ +static inline void +br_pem_decoder_setdest(br_pem_decoder_context *ctx, + void (*dest)(void *dest_ctx, const void *src, size_t len), + void *dest_ctx) +{ + ctx->dest = dest; + ctx->dest_ctx = dest_ctx; +} + +/** + * \brief Get the last event. + * + * If an event was raised, then this function returns the event value, and + * also clears it, thereby allowing the decoder to proceed. If no event + * was raised since the last call to `br_pem_decoder_event()`, then this + * function returns 0. + * + * \param ctx decoder context. + * \return the raised event, or 0. + */ +int br_pem_decoder_event(br_pem_decoder_context *ctx); + +/** + * \brief Event: start of object. + * + * This event is raised when the start of a new object has been detected. + * The object name (normalised to uppercase) can be accessed with + * `br_pem_decoder_name()`. + */ +#define BR_PEM_BEGIN_OBJ 1 + +/** + * \brief Event: end of object. + * + * This event is raised when the end of the current object is reached + * (normally, i.e. with no decoding error). + */ +#define BR_PEM_END_OBJ 2 + +/** + * \brief Event: decoding error. + * + * This event is raised when decoding fails within an object. + * This formally closes the current object and brings the decoder back + * to the "out of any object" state. The offending line in the source + * is consumed. + */ +#define BR_PEM_ERROR 3 + +/** + * \brief Get the name of the encountered object. + * + * The encountered object name is defined only when the "start of object" + * event is raised. That name is normalised to uppercase (for ASCII letters + * only) and does not include trailing dashes. + * + * \param ctx decoder context. + * \return the current object name. + */ +static inline const char * +br_pem_decoder_name(br_pem_decoder_context *ctx) +{ + return ctx->name; +} + +/** + * \brief Encode an object in PEM. + * + * This function encodes the provided binary object (`data`, of length `len` + * bytes) into PEM. The `banner` text will be included in the header and + * footer (e.g. use `"CERTIFICATE"` to get a `"BEGIN CERTIFICATE"` header). + * + * The length (in characters) of the PEM output is returned; that length + * does NOT include the terminating zero, that this function nevertheless + * adds. If using the returned value for allocation purposes, the allocated + * buffer size MUST be at least one byte larger than the returned size. + * + * If `dest` is `NULL`, then the encoding does not happen; however, the + * length of the encoded object is still computed and returned. + * + * The `data` pointer may be `NULL` only if `len` is zero (when encoding + * an object of length zero, which is not very useful), or when `dest` + * is `NULL` (in that case, source data bytes are ignored). + * + * Some `flags` can be specified to alter the encoding behaviour: + * + * - If `BR_PEM_LINE64` is set, then line-breaking will occur after + * every 64 characters of output, instead of the default of 76. + * + * - If `BR_PEM_CRLF` is set, then end-of-line sequence will use + * CR+LF instead of a single LF. + * + * The `data` and `dest` buffers may overlap, in which case the source + * binary data is destroyed in the process. Note that the PEM-encoded output + * is always larger than the source binary. + * + * \param dest the destination buffer (or `NULL`). + * \param data the source buffer (can be `NULL` in some cases). + * \param len the source length (in bytes). + * \param banner the PEM banner expression. + * \param flags the behavioural flags. + * \return the PEM object length (in characters), EXCLUDING the final zero. + */ +size_t br_pem_encode(void *dest, const void *data, size_t len, + const char *banner, unsigned flags); + +/** + * \brief PEM encoding flag: split lines at 64 characters. + */ +#define BR_PEM_LINE64 0x0001 + +/** + * \brief PEM encoding flag: use CR+LF line endings. + */ +#define BR_PEM_CRLF 0x0002 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h new file mode 100644 index 0000000000..fdf608c853 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_prf.h @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_PRF_H__ +#define BR_BEARSSL_PRF_H__ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_prf.h + * + * # The TLS PRF + * + * The "PRF" is the pseudorandom function used internally during the + * SSL/TLS handshake, notably to expand negotiated shared secrets into + * the symmetric encryption keys that will be used to process the + * application data. + * + * TLS 1.0 and 1.1 define a PRF that is based on both MD5 and SHA-1. This + * is implemented by the `br_tls10_prf()` function. + * + * TLS 1.2 redefines the PRF, using an explicit hash function. The + * `br_tls12_sha256_prf()` and `br_tls12_sha384_prf()` functions apply that + * PRF with, respectively, SHA-256 and SHA-384. Most standard cipher suites + * rely on the SHA-256 based PRF, but some use SHA-384. + * + * The PRF always uses as input three parameters: a "secret" (some + * bytes), a "label" (ASCII string), and a "seed" (again some bytes). An + * arbitrary output length can be produced. The "seed" is provided as an + * arbitrary number of binary chunks, that gets internally concatenated. + */ + +/** + * \brief Type for a seed chunk. + * + * Each chunk may have an arbitrary length, and may be empty (no byte at + * all). If the chunk length is zero, then the pointer to the chunk data + * may be `NULL`. + */ +typedef struct { + /** + * \brief Pointer to the chunk data. + */ + const void *data; + + /** + * \brief Chunk length (in bytes). + */ + size_t len; +} br_tls_prf_seed_chunk; + +/** + * \brief PRF implementation for TLS 1.0 and 1.1. + * + * This PRF is the one specified by TLS 1.0 and 1.1. It internally uses + * MD5 and SHA-1. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls10_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * \brief PRF implementation for TLS 1.2, with SHA-256. + * + * This PRF is the one specified by TLS 1.2, when the underlying hash + * function is SHA-256. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls12_sha256_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * \brief PRF implementation for TLS 1.2, with SHA-384. + * + * This PRF is the one specified by TLS 1.2, when the underlying hash + * function is SHA-384. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +void br_tls12_sha384_prf(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/** + * brief A convenient type name for a PRF implementation. + * + * \param dst destination buffer. + * \param len output length (in bytes). + * \param secret secret value (key) for this computation. + * \param secret_len length of "secret" (in bytes). + * \param label PRF label (zero-terminated ASCII string). + * \param seed_num number of seed chunks. + * \param seed seed chnks for this computation (usually non-secret). + */ +typedef void (*br_tls_prf_impl)(void *dst, size_t len, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h new file mode 100644 index 0000000000..84dea93a71 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rand.h @@ -0,0 +1,397 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_RAND_H__ +#define BR_BEARSSL_RAND_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_rand.h + * + * # Pseudo-Random Generators + * + * A PRNG is a state-based engine that outputs pseudo-random bytes on + * demand. It is initialized with an initial seed, and additional seed + * bytes can be added afterwards. Bytes produced depend on the seeds and + * also on the exact sequence of calls (including sizes requested for + * each call). + * + * + * ## Procedural and OOP API + * + * For the PRNG of name "`xxx`", two API are provided. The _procedural_ + * API defined a context structure `br_xxx_context` and three functions: + * + * - `br_xxx_init()` + * + * Initialise the context with an initial seed. + * + * - `br_xxx_generate()` + * + * Produce some pseudo-random bytes. + * + * - `br_xxx_update()` + * + * Inject some additional seed. + * + * The initialisation function sets the first context field (`vtable`) + * to a pointer to the vtable that supports the OOP API. The OOP API + * provides access to the same functions through function pointers, + * named `init()`, `generate()` and `update()`. + * + * Note that the context initialisation method may accept additional + * parameters, provided as a 'const void *' pointer at API level. These + * additional parameters depend on the implemented PRNG. + * + * + * ## HMAC_DRBG + * + * HMAC_DRBG is defined in [NIST SP 800-90A Revision + * 1](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf). + * It uses HMAC repeatedly, over some configurable underlying hash + * function. In BearSSL, it is implemented under the "`hmac_drbg`" name. + * The "extra parameters" pointer for context initialisation should be + * set to a pointer to the vtable for the underlying hash function (e.g. + * pointer to `br_sha256_vtable` to use HMAC_DRBG with SHA-256). + * + * According to the NIST standard, each request shall produce up to + * 219 bits (i.e. 64 kB of data); moreover, the context shall + * be reseeded at least once every 248 requests. This + * implementation does not maintain the reseed counter (the threshold is + * too high to be reached in practice) and does not object to producing + * more than 64 kB in a single request; thus, the code cannot fail, + * which corresponds to the fact that the API has no room for error + * codes. However, this implies that requesting more than 64 kB in one + * `generate()` request, or making more than 248 requests + * without reseeding, is formally out of NIST specification. There is + * no currently known security penalty for exceeding the NIST limits, + * and, in any case, HMAC_DRBG usage in implementing SSL/TLS always + * stays much below these thresholds. + * + * + * ## AESCTR_DRBG + * + * AESCTR_DRBG is a custom PRNG based on AES-128 in CTR mode. This is + * meant to be used only in situations where you are desperate for + * speed, and have an hardware-optimized AES/CTR implementation. Whether + * this will yield perceptible improvements depends on what you use the + * pseudorandom bytes for, and how many you want; for instance, RSA key + * pair generation uses a substantial amount of randomness, and using + * AESCTR_DRBG instead of HMAC_DRBG yields a 15 to 20% increase in key + * generation speed on a recent x86 CPU (Intel Core i7-6567U at 3.30 GHz). + * + * Internally, it uses CTR mode with successive counter values, starting + * at zero (counter value expressed over 128 bits, big-endian convention). + * The counter is not allowed to reach 32768; thus, every 32768*16 bytes + * at most, the `update()` function is run (on an empty seed, if none is + * provided). The `update()` function computes the new AES-128 key by + * applying a custom hash function to the concatenation of a state-dependent + * word (encryption of an all-one block with the current key) and the new + * seed. The custom hash function uses Hirose's construction over AES-256; + * see the comments in `aesctr_drbg.c` for details. + * + * This DRBG does not follow an existing standard, and thus should be + * considered as inadequate for production use until it has been properly + * analysed. + */ + +/** + * \brief Class type for PRNG implementations. + * + * A `br_prng_class` instance references the methods implementing a PRNG. + * Constant instances of this structure are defined for each implemented + * PRNG. Such instances are also called "vtables". + */ +typedef struct br_prng_class_ br_prng_class; +struct br_prng_class_ { + /** + * \brief Size (in bytes) of the context structure appropriate for + * running this PRNG. + */ + size_t context_size; + + /** + * \brief Initialisation method. + * + * The context to initialise is provided as a pointer to its + * first field (the vtable pointer); this function sets that + * first field to a pointer to the vtable. + * + * The extra parameters depend on the implementation; each + * implementation defines what kind of extra parameters it + * expects (if any). + * + * Requirements on the initial seed depend on the implemented + * PRNG. + * + * \param ctx PRNG context to initialise. + * \param params extra parameters for the PRNG. + * \param seed initial seed. + * \param seed_len initial seed length (in bytes). + */ + void (*init)(const br_prng_class **ctx, const void *params, + const void *seed, size_t seed_len); + + /** + * \brief Random bytes generation. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. + * + * \param ctx PRNG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ + void (*generate)(const br_prng_class **ctx, void *out, size_t len); + + /** + * \brief Inject additional seed bytes. + * + * The provided seed bytes are added into the PRNG internal + * entropy pool. + * + * \param ctx PRNG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ + void (*update)(const br_prng_class **ctx, + const void *seed, size_t seed_len); +}; + +/** + * \brief Context for HMAC_DRBG. + * + * The context contents are opaque, except the first field, which + * supports OOP. + */ +typedef struct { + /** + * \brief Pointer to the vtable. + * + * This field is set with the initialisation method/function. + */ + const br_prng_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char K[64]; + unsigned char V[64]; + const br_hash_class *digest_class; +#endif +} br_hmac_drbg_context; + +/** + * \brief Statically allocated, constant vtable for HMAC_DRBG. + */ +extern const br_prng_class br_hmac_drbg_vtable; + +/** + * \brief HMAC_DRBG initialisation. + * + * The context to initialise is provided as a pointer to its first field + * (the vtable pointer); this function sets that first field to a + * pointer to the vtable. + * + * The `seed` value is what is called, in NIST terminology, the + * concatenation of the "seed", "nonce" and "personalization string", in + * that order. + * + * The `digest_class` parameter defines the underlying hash function. + * Formally, the NIST standard specifies that the hash function shall + * be only SHA-1 or one of the SHA-2 functions. This implementation also + * works with any other implemented hash function (such as MD5), but + * this is non-standard and therefore not recommended. + * + * \param ctx HMAC_DRBG context to initialise. + * \param digest_class vtable for the underlying hash function. + * \param seed initial seed. + * \param seed_len initial seed length (in bytes). + */ +void br_hmac_drbg_init(br_hmac_drbg_context *ctx, + const br_hash_class *digest_class, const void *seed, size_t seed_len); + +/** + * \brief Random bytes generation with HMAC_DRBG. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. Formally, requesting + * more than 65536 bytes in one request falls out of specification + * limits (but it won't fail). + * + * \param ctx HMAC_DRBG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ +void br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len); + +/** + * \brief Inject additional seed bytes in HMAC_DRBG. + * + * The provided seed bytes are added into the HMAC_DRBG internal + * entropy pool. The process does not _replace_ existing entropy, + * thus pushing non-random bytes (i.e. bytes which are known to the + * attackers) does not degrade the overall quality of generated bytes. + * + * \param ctx HMAC_DRBG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ +void br_hmac_drbg_update(br_hmac_drbg_context *ctx, + const void *seed, size_t seed_len); + +/** + * \brief Get the hash function implementation used by a given instance of + * HMAC_DRBG. + * + * This calls MUST NOT be performed on a context which was not + * previously initialised. + * + * \param ctx HMAC_DRBG context. + * \return the hash function vtable. + */ +static inline const br_hash_class * +br_hmac_drbg_get_hash(const br_hmac_drbg_context *ctx) +{ + return ctx->digest_class; +} + +/** + * \brief Type for a provider of entropy seeds. + * + * A "seeder" is a function that is able to obtain random values from + * some source and inject them as entropy seed in a PRNG. A seeder + * shall guarantee that the total entropy of the injected seed is large + * enough to seed a PRNG for purposes of cryptographic key generation + * (i.e. at least 128 bits). + * + * A seeder may report a failure to obtain adequate entropy. Seeders + * shall endeavour to fix themselves transient errors by trying again; + * thus, callers may consider reported errors as permanent. + * + * \param ctx PRNG context to seed. + * \return 1 on success, 0 on error. + */ +typedef int (*br_prng_seeder)(const br_prng_class **ctx); + +/** + * \brief Get a seeder backed by the operating system or hardware. + * + * Get a seeder that feeds on RNG facilities provided by the current + * operating system or hardware. If no such facility is known, then 0 + * is returned. + * + * If `name` is not `NULL`, then `*name` is set to a symbolic string + * that identifies the seeder implementation. If no seeder is returned + * and `name` is not `NULL`, then `*name` is set to a pointer to the + * constant string `"none"`. + * + * \param name receiver for seeder name, or `NULL`. + * \return the system seeder, if available, or 0. + */ +br_prng_seeder br_prng_seeder_system(const char **name); + +/** + * \brief Context for AESCTR_DRBG. + * + * The context contents are opaque, except the first field, which + * supports OOP. + */ +typedef struct { + /** + * \brief Pointer to the vtable. + * + * This field is set with the initialisation method/function. + */ + const br_prng_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + br_aes_gen_ctr_keys sk; + uint32_t cc; +#endif +} br_aesctr_drbg_context; + +/** + * \brief Statically allocated, constant vtable for AESCTR_DRBG. + */ +extern const br_prng_class br_aesctr_drbg_vtable; + +/** + * \brief AESCTR_DRBG initialisation. + * + * The context to initialise is provided as a pointer to its first field + * (the vtable pointer); this function sets that first field to a + * pointer to the vtable. + * + * The internal AES key is first set to the all-zero key; then, the + * `br_aesctr_drbg_update()` function is called with the provided `seed`. + * The call is performed even if the seed length (`seed_len`) is zero. + * + * The `aesctr` parameter defines the underlying AES/CTR implementation. + * + * \param ctx AESCTR_DRBG context to initialise. + * \param aesctr vtable for the AES/CTR implementation. + * \param seed initial seed (can be `NULL` if `seed_len` is zero). + * \param seed_len initial seed length (in bytes). + */ +void br_aesctr_drbg_init(br_aesctr_drbg_context *ctx, + const br_block_ctr_class *aesctr, const void *seed, size_t seed_len); + +/** + * \brief Random bytes generation with AESCTR_DRBG. + * + * This method produces `len` pseudorandom bytes, in the `out` + * buffer. The context is updated accordingly. + * + * \param ctx AESCTR_DRBG context. + * \param out output buffer. + * \param len number of pseudorandom bytes to produce. + */ +void br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx, + void *out, size_t len); + +/** + * \brief Inject additional seed bytes in AESCTR_DRBG. + * + * The provided seed bytes are added into the AESCTR_DRBG internal + * entropy pool. The process does not _replace_ existing entropy, + * thus pushing non-random bytes (i.e. bytes which are known to the + * attackers) does not degrade the overall quality of generated bytes. + * + * \param ctx AESCTR_DRBG context. + * \param seed additional seed. + * \param seed_len additional seed length (in bytes). + */ +void br_aesctr_drbg_update(br_aesctr_drbg_context *ctx, + const void *seed, size_t seed_len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h new file mode 100644 index 0000000000..c4f329ec2c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_rsa.h @@ -0,0 +1,1655 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_RSA_H__ +#define BR_BEARSSL_RSA_H__ + +#include +#include + +#include "t_bearssl_hash.h" +#include "t_bearssl_rand.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_rsa.h + * + * # RSA + * + * This file documents the RSA implementations provided with BearSSL. + * Note that the SSL engine accesses these implementations through a + * configurable API, so it is possible to, for instance, run a SSL + * server which uses a RSA engine which is not based on this code. + * + * ## Key Elements + * + * RSA public and private keys consist in lists of big integers. All + * such integers are represented with big-endian unsigned notation: + * first byte is the most significant, and the value is positive (so + * there is no dedicated "sign bit"). Public and private key structures + * thus contain, for each such integer, a pointer to the first value byte + * (`unsigned char *`), and a length (`size_t`) which is the number of + * relevant bytes. As a general rule, minimal-length encoding is not + * enforced: values may have extra leading bytes of value 0. + * + * RSA public keys consist in two integers: + * + * - the modulus (`n`); + * - the public exponent (`e`). + * + * RSA private keys, as defined in + * [PKCS#1](https://tools.ietf.org/html/rfc3447), contain eight integers: + * + * - the modulus (`n`); + * - the public exponent (`e`); + * - the private exponent (`d`); + * - the first prime factor (`p`); + * - the second prime factor (`q`); + * - the first reduced exponent (`dp`, which is `d` modulo `p-1`); + * - the second reduced exponent (`dq`, which is `d` modulo `q-1`); + * - the CRT coefficient (`iq`, the inverse of `q` modulo `p`). + * + * However, the implementations defined in BearSSL use only five of + * these integers: `p`, `q`, `dp`, `dq` and `iq`. + * + * ## Security Features and Limitations + * + * The implementations contained in BearSSL have the following limitations + * and features: + * + * - They are constant-time. This means that the execution time and + * memory access pattern may depend on the _lengths_ of the private + * key components, but not on their value, nor on the value of + * the operand. Note that this property is not achieved through + * random masking, but "true" constant-time code. + * + * - They support only private keys with two prime factors. RSA private + * keys with three or more prime factors are nominally supported, but + * rarely used; they may offer faster operations, at the expense of + * more code and potentially a reduction in security if there are + * "too many" prime factors. + * + * - The public exponent may have arbitrary length. Of course, it is + * a good idea to keep public exponents small, so that public key + * operations are fast; but, contrary to some widely deployed + * implementations, BearSSL has no problem with public exponents + * longer than 32 bits. + * + * - The two prime factors of the modulus need not have the same length + * (but severely imbalanced factor lengths might reduce security). + * Similarly, there is no requirement that the first factor (`p`) + * be greater than the second factor (`q`). + * + * - Prime factors and modulus must be smaller than a compile-time limit. + * This is made necessary by the use of fixed-size stack buffers, and + * the limit has been adjusted to keep stack usage under 2 kB for the + * RSA operations. Currently, the maximum modulus size is 4096 bits, + * and the maximum prime factor size is 2080 bits. + * + * - The RSA functions themselves do not enforce lower size limits, + * except that which is absolutely necessary for the operation to + * mathematically make sense (e.g. a PKCS#1 v1.5 signature with + * SHA-1 requires a modulus of at least 361 bits). It is up to users + * of this code to enforce size limitations when appropriate (e.g. + * the X.509 validation engine, by default, rejects RSA keys of + * less than 1017 bits). + * + * - Within the size constraints expressed above, arbitrary bit lengths + * are supported. There is no requirement that prime factors or + * modulus have a size multiple of 8 or 16. + * + * - When verifying PKCS#1 v1.5 signatures, both variants of the hash + * function identifying header (with and without the ASN.1 NULL) are + * supported. When producing such signatures, the variant with the + * ASN.1 NULL is used. + * + * ## Implementations + * + * Three RSA implementations are included: + * + * - The **i32** implementation internally represents big integers + * as arrays of 32-bit integers. It is perfunctory and portable, + * but not very efficient. + * + * - The **i31** implementation uses 32-bit integers, each containing + * 31 bits worth of integer data. The i31 implementation is somewhat + * faster than the i32 implementation (the reduced integer size makes + * carry propagation easier) for a similar code footprint, but uses + * very slightly larger stack buffers (about 4% bigger). + * + * - The **i62** implementation is similar to the i31 implementation, + * except that it internally leverages the 64x64->128 multiplication + * opcode. This implementation is available only on architectures + * where such an opcode exists. It is much faster than i31. + * + * - The **i15** implementation uses 16-bit integers, each containing + * 15 bits worth of integer data. Multiplication results fit on + * 32 bits, so this won't use the "widening" multiplication routine + * on ARM Cortex M0/M0+, for much better performance and constant-time + * execution. + */ + +/** + * \brief RSA public key. + * + * The structure references the modulus and the public exponent. Both + * integers use unsigned big-endian representation; extra leading bytes + * of value 0 are allowed. + */ +typedef struct { + /** \brief Modulus. */ + unsigned char *n; + /** \brief Modulus length (in bytes). */ + size_t nlen; + /** \brief Public exponent. */ + unsigned char *e; + /** \brief Public exponent length (in bytes). */ + size_t elen; +} br_rsa_public_key; + +/** + * \brief RSA private key. + * + * The structure references the private factors, reduced private + * exponents, and CRT coefficient. It also contains the bit length of + * the modulus. The big integers use unsigned big-endian representation; + * extra leading bytes of value 0 are allowed. However, the modulus bit + * length (`n_bitlen`) MUST be exact. + */ +typedef struct { + /** \brief Modulus bit length (in bits, exact value). */ + uint32_t n_bitlen; + /** \brief First prime factor. */ + unsigned char *p; + /** \brief First prime factor length (in bytes). */ + size_t plen; + /** \brief Second prime factor. */ + unsigned char *q; + /** \brief Second prime factor length (in bytes). */ + size_t qlen; + /** \brief First reduced private exponent. */ + unsigned char *dp; + /** \brief First reduced private exponent length (in bytes). */ + size_t dplen; + /** \brief Second reduced private exponent. */ + unsigned char *dq; + /** \brief Second reduced private exponent length (in bytes). */ + size_t dqlen; + /** \brief CRT coefficient. */ + unsigned char *iq; + /** \brief CRT coefficient length (in bytes). */ + size_t iqlen; +} br_rsa_private_key; + +/** + * \brief Type for a RSA public key engine. + * + * The public key engine performs the modular exponentiation of the + * provided value with the public exponent. The value is modified in + * place. + * + * The value length (`xlen`) is verified to have _exactly_ the same + * length as the modulus (actual modulus length, without extra leading + * zeros in the modulus representation in memory). If the length does + * not match, then this function returns 0 and `x[]` is unmodified. + * + * It `xlen` is correct, then `x[]` is modified. Returned value is 1 + * on success, 0 on error. Error conditions include an oversized `x[]` + * (the array has the same length as the modulus, but the numerical value + * is not lower than the modulus) and an invalid modulus (e.g. an even + * integer). If an error is reported, then the new contents of `x[]` are + * unspecified. + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_public)(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief Type for a RSA signature verification engine (PKCS#1 v1.5). + * + * Parameters are: + * + * - The signature itself. The provided array is NOT modified. + * + * - The encoded OID for the hash function. The provided array must begin + * with a single byte that contains the length of the OID value (in + * bytes), followed by exactly that many bytes. This parameter may + * also be `NULL`, in which case the raw hash value should be used + * with the PKCS#1 v1.5 "type 1" padding (as used in SSL/TLS up + * to TLS-1.1, with a 36-byte hash value). + * + * - The hash output length, in bytes. + * + * - The public key. + * + * - An output buffer for the hash value. The caller must still compare + * it with the hash of the data over which the signature is computed. + * + * **Constraints:** + * + * - Hash length MUST be no more than 64 bytes. + * + * - OID value length MUST be no more than 32 bytes (i.e. `hash_oid[0]` + * must have a value in the 0..32 range, inclusive). + * + * This function verifies that the signature length (`xlen`) matches the + * modulus length (this function returns 0 on mismatch). If the modulus + * size exceeds the maximum supported RSA size, then the function also + * returns 0. + * + * Returned value is 1 on success, 0 on error. + * + * Implementations of this type need not be constant-time. + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pkcs1_vrfy)(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief Type for a RSA signature verification engine (PSS). + * + * Parameters are: + * + * - The signature itself. The provided array is NOT modified. + * + * - The hash function which was used to hash the message. + * + * - The hash function to use with MGF1 within the PSS padding. This + * is not necessarily the same hash function as the one which was + * used to hash the signed message. + * + * - The hashed message (as an array of bytes). + * + * - The PSS salt length (in bytes). + * + * - The public key. + * + * **Constraints:** + * + * - Hash message length MUST be no more than 64 bytes. + * + * Note that, contrary to PKCS#1 v1.5 signature, the hash value of the + * signed data cannot be extracted from the signature; it must be + * provided to the verification function. + * + * This function verifies that the signature length (`xlen`) matches the + * modulus length (this function returns 0 on mismatch). If the modulus + * size exceeds the maximum supported RSA size, then the function also + * returns 0. + * + * Returned value is 1 on success, 0 on error. + * + * Implementations of this type need not be constant-time. + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pss_vrfy)(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief Type for a RSA encryption engine (OAEP). + * + * Parameters are: + * + * - A source of random bytes. The source must be already initialized. + * + * - A hash function, used internally with the mask generation function + * (MGF1). + * + * - A label. The `label` pointer may be `NULL` if `label_len` is zero + * (an empty label, which is the default in PKCS#1 v2.2). + * + * - The public key. + * + * - The destination buffer. Its maximum length (in bytes) is provided; + * if that length is lower than the public key length, then an error + * is reported. + * + * - The source message. + * + * The encrypted message output has exactly the same length as the modulus + * (mathematical length, in bytes, not counting extra leading zeros in the + * modulus representation in the public key). + * + * The source message (`src`, length `src_len`) may overlap with the + * destination buffer (`dst`, length `dst_max_len`). + * + * This function returns the actual encrypted message length, in bytes; + * on error, zero is returned. An error is reported if the output buffer + * is not large enough, or the public is invalid, or the public key + * modulus exceeds the maximum supported RSA size. + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +typedef size_t (*br_rsa_oaep_encrypt)( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief Type for a RSA private key engine. + * + * The `x[]` buffer is modified in place, and its length is inferred from + * the modulus length (`x[]` is assumed to have a length of + * `(sk->n_bitlen+7)/8` bytes). + * + * Returned value is 1 on success, 0 on error. + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_private)(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief Type for a RSA signature generation engine (PKCS#1 v1.5). + * + * Parameters are: + * + * - The encoded OID for the hash function. The provided array must begin + * with a single byte that contains the length of the OID value (in + * bytes), followed by exactly that many bytes. This parameter may + * also be `NULL`, in which case the raw hash value should be used + * with the PKCS#1 v1.5 "type 1" padding (as used in SSL/TLS up + * to TLS-1.1, with a 36-byte hash value). + * + * - The hash value computes over the data to sign (its length is + * expressed in bytes). + * + * - The RSA private key. + * + * - The output buffer, that receives the signature. + * + * Returned value is 1 on success, 0 on error. Error conditions include + * a too small modulus for the provided hash OID and value, or some + * invalid key parameters. The signature length is exactly + * `(sk->n_bitlen+7)/8` bytes. + * + * This function is expected to be constant-time with regards to the + * private key bytes (lengths of the modulus and the individual factors + * may leak, though) and to the hashed data. + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pkcs1_sign)(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Type for a RSA signature generation engine (PSS). + * + * Parameters are: + * + * - An initialized PRNG for salt generation. If the salt length is + * zero (`salt_len` parameter), then the PRNG is optional (this is + * not the typical case, as the security proof of RSA/PSS is + * tighter when a non-empty salt is used). + * + * - The hash function which was used to hash the message. + * + * - The hash function to use with MGF1 within the PSS padding. This + * is not necessarily the same function as the one used to hash the + * message. + * + * - The hashed message. + * + * - The salt length, in bytes. + * + * - The RSA private key. + * + * - The output buffer, that receives the signature. + * + * Returned value is 1 on success, 0 on error. Error conditions include + * a too small modulus for the provided hash and salt lengths, or some + * invalid key parameters. The signature length is exactly + * `(sk->n_bitlen+7)/8` bytes. + * + * This function is expected to be constant-time with regards to the + * private key bytes (lengths of the modulus and the individual factors + * may leak, though) and to the hashed data. + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_pss_sign)(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Encoded OID for SHA-1 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA1 \ + ((const unsigned char *)"\x05\x2B\x0E\x03\x02\x1A") + +/** + * \brief Encoded OID for SHA-224 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA224 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04") + +/** + * \brief Encoded OID for SHA-256 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA256 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01") + +/** + * \brief Encoded OID for SHA-384 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA384 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02") + +/** + * \brief Encoded OID for SHA-512 (in RSA PKCS#1 signatures). + */ +#define BR_HASH_OID_SHA512 \ + ((const unsigned char *)"\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03") + +/** + * \brief Type for a RSA decryption engine (OAEP). + * + * Parameters are: + * + * - A hash function, used internally with the mask generation function + * (MGF1). + * + * - A label. The `label` pointer may be `NULL` if `label_len` is zero + * (an empty label, which is the default in PKCS#1 v2.2). + * + * - The private key. + * + * - The source and destination buffer. The buffer initially contains + * the encrypted message; the buffer contents are altered, and the + * decrypted message is written at the start of that buffer + * (decrypted message is always shorter than the encrypted message). + * + * If decryption fails in any way, then `*len` is unmodified, and the + * function returns 0. Otherwise, `*len` is set to the decrypted message + * length, and 1 is returned. The implementation is responsible for + * checking that the input message length matches the key modulus length, + * and that the padding is correct. + * + * Implementations MUST use constant-time check of the validity of the + * OAEP padding, at least until the leading byte and hash value have + * been checked. Whether overall decryption worked, and the length of + * the decrypted message, may leak. + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +typedef uint32_t (*br_rsa_oaep_decrypt)( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/* + * RSA "i32" engine. Integers are internally represented as arrays of + * 32-bit integers, and the core multiplication primitive is the + * 32x32->64 multiplication. + */ + +/** + * \brief RSA public key engine "i32". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i32" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i32" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i32". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i32" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i32" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/* + * RSA "i31" engine. Similar to i32, but only 31 bits are used per 32-bit + * word. This uses slightly more stack space (about 4% more) and code + * space, but it quite faster. + */ + +/** + * \brief RSA public key engine "i31". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i31" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i31" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i31". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i31" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i31" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/* + * RSA "i62" engine. Similar to i31, but internal multiplication use + * 64x64->128 multiplications. This is available only on architecture + * that offer such an opcode. + */ + +/** + * \brief RSA public key engine "i62". + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_public_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i62" (PKCS#1 v1.5 signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pkcs1_vrfy_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i62" (PSS signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pss_vrfy_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i62". + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_private_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i62" (PKCS#1 v1.5 signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pkcs1_sign_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i62" (PSS signatures). + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_pss_sign_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Get the RSA "i62" implementation (public key operations), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_public br_rsa_i62_public_get(void); + +/** + * \brief Get the RSA "i62" implementation (PKCS#1 v1.5 signature verification), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pkcs1_vrfy br_rsa_i62_pkcs1_vrfy_get(void); + +/** + * \brief Get the RSA "i62" implementation (PSS signature verification), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pss_vrfy br_rsa_i62_pss_vrfy_get(void); + +/** + * \brief Get the RSA "i62" implementation (private key operations), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_private br_rsa_i62_private_get(void); + +/** + * \brief Get the RSA "i62" implementation (PKCS#1 v1.5 signature generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pkcs1_sign br_rsa_i62_pkcs1_sign_get(void); + +/** + * \brief Get the RSA "i62" implementation (PSS signature generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_pss_sign br_rsa_i62_pss_sign_get(void); + +/** + * \brief Get the RSA "i62" implementation (OAEP encryption), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_oaep_encrypt br_rsa_i62_oaep_encrypt_get(void); + +/** + * \brief Get the RSA "i62" implementation (OAEP decryption), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_oaep_decrypt br_rsa_i62_oaep_decrypt_get(void); + +/* + * RSA "i15" engine. Integers are represented as 15-bit integers, so + * the code uses only 32-bit multiplication (no 64-bit result), which + * is vastly faster (and constant-time) on the ARM Cortex M0/M0+. + */ + +/** + * \brief RSA public key engine "i15". + * + * \see br_rsa_public + * + * \param x operand to exponentiate. + * \param xlen length of the operand (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_public(unsigned char *x, size_t xlen, + const br_rsa_public_key *pk); + +/** + * \brief RSA signature verification engine "i15" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash_len expected hash value length (in bytes). + * \param pk RSA public key. + * \param hash_out output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pkcs1_vrfy(const unsigned char *x, size_t xlen, + const unsigned char *hash_oid, size_t hash_len, + const br_rsa_public_key *pk, unsigned char *hash_out); + +/** + * \brief RSA signature verification engine "i15" (PSS signatures). + * + * \see br_rsa_pss_vrfy + * + * \param x signature buffer. + * \param xlen signature length (in bytes). + * \param hf_data hash function applied on the message. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hash value of the signed message. + * \param salt_len PSS salt length (in bytes). + * \param pk RSA public key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pss_vrfy(const unsigned char *x, size_t xlen, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const void *hash, size_t salt_len, const br_rsa_public_key *pk); + +/** + * \brief RSA private key engine "i15". + * + * \see br_rsa_private + * + * \param x operand to exponentiate. + * \param sk RSA private key. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_private(unsigned char *x, + const br_rsa_private_key *sk); + +/** + * \brief RSA signature generation engine "i15" (PKCS#1 v1.5 signatures). + * + * \see br_rsa_pkcs1_sign + * + * \param hash_oid encoded hash algorithm OID (or `NULL`). + * \param hash hash value. + * \param hash_len hash value length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the hash value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pkcs1_sign(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief RSA signature generation engine "i15" (PSS signatures). + * + * \see br_rsa_pss_sign + * + * \param rng PRNG for salt generation (`NULL` if `salt_len` is zero). + * \param hf_data hash function used to hash the signed data. + * \param hf_mgf1 hash function to use with MGF1. + * \param hash hashed message. + * \param salt_len salt length (in bytes). + * \param sk RSA private key. + * \param x output buffer for the signature value. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_pss_sign(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash_value, size_t salt_len, + const br_rsa_private_key *sk, unsigned char *x); + +/** + * \brief Get "default" RSA implementation (public-key operations). + * + * This returns the preferred implementation of RSA (public-key operations) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_public br_rsa_public_get_default(void); + +/** + * \brief Get "default" RSA implementation (private-key operations). + * + * This returns the preferred implementation of RSA (private-key operations) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_private br_rsa_private_get_default(void); + +/** + * \brief Get "default" RSA implementation (PKCS#1 v1.5 signature verification). + * + * This returns the preferred implementation of RSA (signature verification) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pkcs1_vrfy br_rsa_pkcs1_vrfy_get_default(void); + +/** + * \brief Get "default" RSA implementation (PSS signature verification). + * + * This returns the preferred implementation of RSA (signature verification) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pss_vrfy br_rsa_pss_vrfy_get_default(void); + +/** + * \brief Get "default" RSA implementation (PKCS#1 v1.5 signature generation). + * + * This returns the preferred implementation of RSA (signature generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pkcs1_sign br_rsa_pkcs1_sign_get_default(void); + +/** + * \brief Get "default" RSA implementation (PSS signature generation). + * + * This returns the preferred implementation of RSA (signature generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_pss_sign br_rsa_pss_sign_get_default(void); + +/** + * \brief Get "default" RSA implementation (OAEP encryption). + * + * This returns the preferred implementation of RSA (OAEP encryption) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_oaep_encrypt br_rsa_oaep_encrypt_get_default(void); + +/** + * \brief Get "default" RSA implementation (OAEP decryption). + * + * This returns the preferred implementation of RSA (OAEP decryption) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_oaep_decrypt br_rsa_oaep_decrypt_get_default(void); + +/** + * \brief RSA decryption helper, for SSL/TLS. + * + * This function performs the RSA decryption for a RSA-based key exchange + * in a SSL/TLS server. The provided RSA engine is used. The `data` + * parameter points to the value to decrypt, of length `len` bytes. On + * success, the 48-byte pre-master secret is copied into `data`, starting + * at the first byte of that buffer; on error, the contents of `data` + * become indeterminate. + * + * This function first checks that the provided value length (`len`) is + * not lower than 59 bytes, and matches the RSA modulus length; if neither + * of this property is met, then this function returns 0 and the buffer + * is unmodified. + * + * Otherwise, decryption and then padding verification are performed, both + * in constant-time. A decryption error, or a bad padding, or an + * incorrect decrypted value length are reported with a returned value of + * 0; on success, 1 is returned. The caller (SSL server engine) is supposed + * to proceed with a random pre-master secret in case of error. + * + * \param core RSA private key engine. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len length (in bytes) of the data to decrypt. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_ssl_decrypt(br_rsa_private core, const br_rsa_private_key *sk, + unsigned char *data, size_t len); + +/** + * \brief RSA encryption (OAEP) with the "i15" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i15_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i15" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i15_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i31" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i31_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i31" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i31_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i32" engine. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i32_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i32" engine. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i32_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief RSA encryption (OAEP) with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_oaep_encrypt_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_oaep_encrypt + * + * \param rnd source of random bytes. + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param pk RSA public key. + * \param dst destination buffer. + * \param dst_max_len destination buffer length (maximum encrypted data size). + * \param src message to encrypt. + * \param src_len source message length (in bytes). + * \return encrypted message length (in bytes), or 0 on error. + */ +size_t br_rsa_i62_oaep_encrypt( + const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, + const br_rsa_public_key *pk, + void *dst, size_t dst_max_len, + const void *src, size_t src_len); + +/** + * \brief RSA decryption (OAEP) with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_oaep_decrypt_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_oaep_decrypt + * + * \param dig hash function to use with MGF1. + * \param label label value (may be `NULL` if `label_len` is zero). + * \param label_len label length, in bytes. + * \param sk RSA private key. + * \param data input/output buffer. + * \param len encrypted/decrypted message length. + * \return 1 on success, 0 on error. + */ +uint32_t br_rsa_i62_oaep_decrypt( + const br_hash_class *dig, const void *label, size_t label_len, + const br_rsa_private_key *sk, void *data, size_t *len); + +/** + * \brief Get buffer size to hold RSA private key elements. + * + * This macro returns the length (in bytes) of the buffer needed to + * receive the elements of a RSA private key, as generated by one of + * the `br_rsa_*_keygen()` functions. If the provided size is a constant + * expression, then the whole macro evaluates to a constant expression. + * + * \param size target key size (modulus size, in bits) + * \return the length of the private key buffer, in bytes. + */ +#define BR_RSA_KBUF_PRIV_SIZE(size) (5 * (((size) + 15) >> 4)) + +/** + * \brief Get buffer size to hold RSA public key elements. + * + * This macro returns the length (in bytes) of the buffer needed to + * receive the elements of a RSA public key, as generated by one of + * the `br_rsa_*_keygen()` functions. If the provided size is a constant + * expression, then the whole macro evaluates to a constant expression. + * + * \param size target key size (modulus size, in bits) + * \return the length of the public key buffer, in bytes. + */ +#define BR_RSA_KBUF_PUB_SIZE(size) (4 + (((size) + 7) >> 3)) + +/** + * \brief Type for RSA key pair generator implementation. + * + * This function generates a new RSA key pair whose modulus has bit + * length `size` bits. The private key elements are written in the + * `kbuf_priv` buffer, and pointer values and length fields to these + * elements are populated in the provided private key structure `sk`. + * Similarly, the public key elements are written in `kbuf_pub`, with + * pointers and lengths set in `pk`. + * + * If `pk` is `NULL`, then `kbuf_pub` may be `NULL`, and only the + * private key is set. + * + * If `pubexp` is not zero, then its value will be used as public + * exponent. Valid RSA public exponent values are odd integers + * greater than 1. If `pubexp` is zero, then the public exponent will + * have value 3. + * + * The provided PRNG (`rng_ctx`) must have already been initialized + * and seeded. + * + * Returned value is 1 on success, 0 on error. An error is reported + * if the requested range is outside of the supported key sizes, or + * if an invalid non-zero public exponent value is provided. Supported + * range starts at 512 bits, and up to an implementation-defined + * maximum (by default 4096 bits). Note that key sizes up to 768 bits + * have been broken in practice, and sizes lower than 2048 bits are + * usually considered to be weak and should not be used. + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +typedef uint32_t (*br_rsa_keygen)( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i15" engine. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i15_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i31" engine. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i31_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief RSA key pair generation with the "i62" engine. + * + * This function is defined only on architecture that offer a 64x64->128 + * opcode. Use `br_rsa_i62_keygen_get()` to dynamically obtain a pointer + * to that function. + * + * \see br_rsa_keygen + * + * \param rng_ctx source PRNG context (already initialized) + * \param sk RSA private key structure (destination) + * \param kbuf_priv buffer for private key elements + * \param pk RSA public key structure (destination), or `NULL` + * \param kbuf_pub buffer for public key elements, or `NULL` + * \param size target RSA modulus size (in bits) + * \param pubexp public exponent to use, or zero + * \return 1 on success, 0 on error (invalid parameters) + */ +uint32_t br_rsa_i62_keygen( + const br_prng_class **rng_ctx, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp); + +/** + * \brief Get the RSA "i62" implementation (key pair generation), + * if available. + * + * \return the implementation, or 0. + */ +br_rsa_keygen br_rsa_i62_keygen_get(void); + +/** + * \brief Get "default" RSA implementation (key pair generation). + * + * This returns the preferred implementation of RSA (key pair generation) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_keygen br_rsa_keygen_get_default(void); + +/** + * \brief Type for a modulus computing function. + * + * Such a function computes the public modulus from the private key. The + * encoded modulus (unsigned big-endian) is written on `n`, and the size + * (in bytes) is returned. If `n` is `NULL`, then the size is returned but + * the modulus itself is not computed. + * + * If the key size exceeds an internal limit, 0 is returned. + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_modulus)(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i15" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA modulus ("i31" engine). + * + * \see br_rsa_compute_modulus + * + * \param n destination buffer (or `NULL`). + * \param sk RSA private key. + * \return the modulus length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_modulus(void *n, const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute modulus). + * + * This returns the preferred implementation of RSA (recompute modulus) + * on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_modulus br_rsa_compute_modulus_get_default(void); + +/** + * \brief Type for a public exponent computing function. + * + * Such a function recomputes the public exponent from the private key. + * 0 is returned if any of the following occurs: + * + * - Either `p` or `q` is not equal to 3 modulo 4. + * + * - The public exponent does not fit on 32 bits. + * + * - An internal limit is exceeded. + * + * - The private key is invalid in some way. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds and returns the true + * public exponent. The public exponent is always an odd integer greater + * than 1. + * + * \return the public exponent, or 0. + */ +typedef uint32_t (*br_rsa_compute_pubexp)(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i15" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i15_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Recompute RSA public exponent ("i31" engine). + * + * \see br_rsa_compute_pubexp + * + * \return the public exponent, or 0. + */ +uint32_t br_rsa_i31_compute_pubexp(const br_rsa_private_key *sk); + +/** + * \brief Get "default" RSA implementation (recompute public exponent). + * + * This returns the preferred implementation of RSA (recompute public + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_pubexp br_rsa_compute_pubexp_get_default(void); + +/** + * \brief Type for a private exponent computing function. + * + * An RSA private key (`br_rsa_private_key`) contains two reduced + * private exponents, which are sufficient to perform private key + * operations. However, standard encoding formats for RSA private keys + * require also a copy of the complete private exponent (non-reduced), + * which this function recomputes. + * + * This function suceeds if all the following conditions hold: + * + * - Both private factors `p` and `q` are equal to 3 modulo 4. + * + * - The provided public exponent `pubexp` is correct, and, in particular, + * is odd, relatively prime to `p-1` and `q-1`, and greater than 1. + * + * - No internal storage limit is exceeded. + * + * For all private keys produced by the key generator functions + * (`br_rsa_keygen` type), this function succeeds. Note that the API + * restricts the public exponent to a maximum size of 32 bits. + * + * The encoded private exponent is written in `d` (unsigned big-endian + * convention), and the length (in bytes) is returned. If `d` is `NULL`, + * then the exponent is not written anywhere, but the length is still + * returned. On error, 0 is returned. + * + * Not all error conditions are detected when `d` is `NULL`; therefore, the + * returned value shall be checked also when actually producing the value. + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +typedef size_t (*br_rsa_compute_privexp)(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i15" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i15_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Recompute RSA private exponent ("i31" engine). + * + * \see br_rsa_compute_privexp + * + * \param d destination buffer (or `NULL`). + * \param sk RSA private key. + * \param pubexp the public exponent. + * \return the private exponent length (in bytes), or 0. + */ +size_t br_rsa_i31_compute_privexp(void *d, + const br_rsa_private_key *sk, uint32_t pubexp); + +/** + * \brief Get "default" RSA implementation (recompute private exponent). + * + * This returns the preferred implementation of RSA (recompute private + * exponent) on the current system. + * + * \return the default implementation. + */ +br_rsa_compute_privexp br_rsa_compute_privexp_get_default(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h new file mode 100644 index 0000000000..1c4d691c3a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_ssl.h @@ -0,0 +1,4308 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_SSL_H__ +#define BR_BEARSSL_SSL_H__ + +#include +#include + +#include "t_bearssl_block.h" +#include "t_bearssl_hash.h" +#include "t_bearssl_hmac.h" +#include "t_bearssl_prf.h" +#include "t_bearssl_rand.h" +#include "t_bearssl_x509.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_ssl.h + * + * # SSL + * + * For an overview of the SSL/TLS API, see [the BearSSL Web + * site](https://www.bearssl.org/api1.html). + * + * The `BR_TLS_*` constants correspond to the standard cipher suites and + * their values in the [IANA + * registry](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-4). + * + * The `BR_ALERT_*` constants are for standard TLS alert messages. When + * a fatal alert message is sent of received, then the SSL engine context + * status is set to the sum of that alert value (an integer in the 0..255 + * range) and a fixed offset (`BR_ERR_SEND_FATAL_ALERT` for a sent alert, + * `BR_ERR_RECV_FATAL_ALERT` for a received alert). + */ + +/** \brief Optimal input buffer size. */ +#define BR_SSL_BUFSIZE_INPUT (16384 + 325) + +/** \brief Optimal output buffer size. */ +#define BR_SSL_BUFSIZE_OUTPUT (16384 + 85) + +/** \brief Optimal buffer size for monodirectional engine + (shared input/output buffer). */ +#define BR_SSL_BUFSIZE_MONO BR_SSL_BUFSIZE_INPUT + +/** \brief Optimal buffer size for bidirectional engine + (single buffer split into two separate input/output buffers). */ +#define BR_SSL_BUFSIZE_BIDI (BR_SSL_BUFSIZE_INPUT + BR_SSL_BUFSIZE_OUTPUT) + +/* + * Constants for known SSL/TLS protocol versions (SSL 3.0, TLS 1.0, TLS 1.1 + * and TLS 1.2). Note that though there is a constant for SSL 3.0, that + * protocol version is not actually supported. + */ + +/** \brief Protocol version: SSL 3.0 (unsupported). */ +#define BR_SSL30 0x0300 +/** \brief Protocol version: TLS 1.0. */ +#define BR_TLS10 0x0301 +/** \brief Protocol version: TLS 1.1. */ +#define BR_TLS11 0x0302 +/** \brief Protocol version: TLS 1.2. */ +#define BR_TLS12 0x0303 + +/* + * Error constants. They are used to report the reason why a context has + * been marked as failed. + * + * Implementation note: SSL-level error codes should be in the 1..31 + * range. The 32..63 range is for certificate decoding and validation + * errors. Received fatal alerts imply an error code in the 256..511 range. + */ + +/** \brief SSL status: no error so far (0). */ +#define BR_ERR_OK 0 + +/** \brief SSL status: caller-provided parameter is incorrect. */ +#define BR_ERR_BAD_PARAM 1 + +/** \brief SSL status: operation requested by the caller cannot be applied + with the current context state (e.g. reading data while outgoing data + is waiting to be sent). */ +#define BR_ERR_BAD_STATE 2 + +/** \brief SSL status: incoming protocol or record version is unsupported. */ +#define BR_ERR_UNSUPPORTED_VERSION 3 + +/** \brief SSL status: incoming record version does not match the expected + version. */ +#define BR_ERR_BAD_VERSION 4 + +/** \brief SSL status: incoming record length is invalid. */ +#define BR_ERR_BAD_LENGTH 5 + +/** \brief SSL status: incoming record is too large to be processed, or + buffer is too small for the handshake message to send. */ +#define BR_ERR_TOO_LARGE 6 + +/** \brief SSL status: decryption found an invalid padding, or the record + MAC is not correct. */ +#define BR_ERR_BAD_MAC 7 + +/** \brief SSL status: no initial entropy was provided, and none can be + obtained from the OS. */ +#define BR_ERR_NO_RANDOM 8 + +/** \brief SSL status: incoming record type is unknown. */ +#define BR_ERR_UNKNOWN_TYPE 9 + +/** \brief SSL status: incoming record or message has wrong type with + regards to the current engine state. */ +#define BR_ERR_UNEXPECTED 10 + +/** \brief SSL status: ChangeCipherSpec message from the peer has invalid + contents. */ +#define BR_ERR_BAD_CCS 12 + +/** \brief SSL status: alert message from the peer has invalid contents + (odd length). */ +#define BR_ERR_BAD_ALERT 13 + +/** \brief SSL status: incoming handshake message decoding failed. */ +#define BR_ERR_BAD_HANDSHAKE 14 + +/** \brief SSL status: ServerHello contains a session ID which is larger + than 32 bytes. */ +#define BR_ERR_OVERSIZED_ID 15 + +/** \brief SSL status: server wants to use a cipher suite that we did + not claim to support. This is also reported if we tried to advertise + a cipher suite that we do not support. */ +#define BR_ERR_BAD_CIPHER_SUITE 16 + +/** \brief SSL status: server wants to use a compression that we did not + claim to support. */ +#define BR_ERR_BAD_COMPRESSION 17 + +/** \brief SSL status: server's max fragment length does not match + client's. */ +#define BR_ERR_BAD_FRAGLEN 18 + +/** \brief SSL status: secure renegotiation failed. */ +#define BR_ERR_BAD_SECRENEG 19 + +/** \brief SSL status: server sent an extension type that we did not + announce, or used the same extension type several times in a single + ServerHello. */ +#define BR_ERR_EXTRA_EXTENSION 20 + +/** \brief SSL status: invalid Server Name Indication contents (when + used by the server, this extension shall be empty). */ +#define BR_ERR_BAD_SNI 21 + +/** \brief SSL status: invalid ServerHelloDone from the server (length + is not 0). */ +#define BR_ERR_BAD_HELLO_DONE 22 + +/** \brief SSL status: internal limit exceeded (e.g. server's public key + is too large). */ +#define BR_ERR_LIMIT_EXCEEDED 23 + +/** \brief SSL status: Finished message from peer does not match the + expected value. */ +#define BR_ERR_BAD_FINISHED 24 + +/** \brief SSL status: session resumption attempt with distinct version + or cipher suite. */ +#define BR_ERR_RESUME_MISMATCH 25 + +/** \brief SSL status: unsupported or invalid algorithm (ECDHE curve, + signature algorithm, hash function). */ +#define BR_ERR_INVALID_ALGORITHM 26 + +/** \brief SSL status: invalid signature (on ServerKeyExchange from + server, or in CertificateVerify from client). */ +#define BR_ERR_BAD_SIGNATURE 27 + +/** \brief SSL status: peer's public key does not have the proper type + or is not allowed for requested operation. */ +#define BR_ERR_WRONG_KEY_USAGE 28 + +/** \brief SSL status: client did not send a certificate upon request, + or the client certificate could not be validated. */ +#define BR_ERR_NO_CLIENT_AUTH 29 + +/** \brief SSL status: I/O error or premature close on underlying + transport stream. This error code is set only by the simplified + I/O API ("br_sslio_*"). */ +#define BR_ERR_IO 31 + +/** \brief SSL status: base value for a received fatal alert. + + When a fatal alert is received from the peer, the alert value + is added to this constant. */ +#define BR_ERR_RECV_FATAL_ALERT 256 + +/** \brief SSL status: base value for a sent fatal alert. + + When a fatal alert is sent to the peer, the alert value is added + to this constant. */ +#define BR_ERR_SEND_FATAL_ALERT 512 + +/* ===================================================================== */ + +/** + * \brief Decryption engine for SSL. + * + * When processing incoming records, the SSL engine will use a decryption + * engine that uses a specific context structure, and has a set of + * methods (a vtable) that follows this template. + * + * The decryption engine is responsible for applying decryption, verifying + * MAC, and keeping track of the record sequence number. + */ +typedef struct br_sslrec_in_class_ br_sslrec_in_class; +struct br_sslrec_in_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Test validity of the incoming record length. + * + * This function returns 1 if the announced length for an + * incoming record is valid, 0 otherwise, + * + * \param ctx decryption engine context. + * \param record_len incoming record length. + * \return 1 of a valid length, 0 otherwise. + */ + int (*check_length)(const br_sslrec_in_class *const *ctx, + size_t record_len); + + /** + * \brief Decrypt the incoming record. + * + * This function may assume that the record length is valid + * (it has been previously tested with `check_length()`). + * Decryption is done in place; `*len` is updated with the + * cleartext length, and the address of the first plaintext + * byte is returned. If the record is correct but empty, then + * `*len` is set to 0 and a non-`NULL` pointer is returned. + * + * On decryption/MAC error, `NULL` is returned. + * + * \param ctx decryption engine context. + * \param record_type record type (23 for application data, etc). + * \param version record version. + * \param payload address of encrypted payload. + * \param len pointer to payload length (updated). + * \return pointer to plaintext, or `NULL` on error. + */ + unsigned char *(*decrypt)(const br_sslrec_in_class **ctx, + int record_type, unsigned version, + void *payload, size_t *len); +}; + +/** + * \brief Encryption engine for SSL. + * + * When building outgoing records, the SSL engine will use an encryption + * engine that uses a specific context structure, and has a set of + * methods (a vtable) that follows this template. + * + * The encryption engine is responsible for applying encryption and MAC, + * and keeping track of the record sequence number. + */ +typedef struct br_sslrec_out_class_ br_sslrec_out_class; +struct br_sslrec_out_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Compute maximum plaintext sizes and offsets. + * + * When this function is called, the `*start` and `*end` + * values contain offsets designating the free area in the + * outgoing buffer for plaintext data; that free area is + * preceded by a 5-byte space which will receive the record + * header. + * + * The `max_plaintext()` function is responsible for adjusting + * both `*start` and `*end` to make room for any record-specific + * header, MAC, padding, and possible split. + * + * \param ctx encryption engine context. + * \param start pointer to start of plaintext offset (updated). + * \param end pointer to start of plaintext offset (updated). + */ + void (*max_plaintext)(const br_sslrec_out_class *const *ctx, + size_t *start, size_t *end); + + /** + * \brief Perform record encryption. + * + * This function encrypts the record. The plaintext address and + * length are provided. Returned value is the start of the + * encrypted record (or sequence of records, if a split was + * performed), _including_ the 5-byte header, and `*len` is + * adjusted to the total size of the record(s), there again + * including the header(s). + * + * \param ctx decryption engine context. + * \param record_type record type (23 for application data, etc). + * \param version record version. + * \param plaintext address of plaintext. + * \param len pointer to plaintext length (updated). + * \return pointer to start of built record. + */ + unsigned char *(*encrypt)(const br_sslrec_out_class **ctx, + int record_type, unsigned version, + void *plaintext, size_t *len); +}; + +/** + * \brief Context for a no-encryption engine. + * + * The no-encryption engine processes outgoing records during the initial + * handshake, before encryption is applied. + */ +typedef struct { + /** \brief No-encryption engine vtable. */ + const br_sslrec_out_class *vtable; +} br_sslrec_out_clear_context; + +/** \brief Static, constant vtable for the no-encryption engine. */ +extern const br_sslrec_out_class br_sslrec_out_clear_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for CBC mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for CBC processing: block cipher implementation, block cipher key, + * HMAC parameters (hash function, key, MAC length), and IV. If the + * IV is `NULL`, then a per-record IV will be used (TLS 1.1+). + */ +typedef struct br_sslrec_in_cbc_class_ br_sslrec_in_cbc_class; +struct br_sslrec_in_cbc_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CBC decryption). + * \param bc_key block cipher key. + * \param bc_key_len block cipher key length (in bytes). + * \param dig_impl hash function for HMAC. + * \param mac_key HMAC key. + * \param mac_key_len HMAC key length (in bytes). + * \param mac_out_len HMAC output length (in bytes). + * \param iv initial IV (or `NULL`). + */ + void (*init)(const br_sslrec_in_cbc_class **ctx, + const br_block_cbcdec_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv); +}; + +/** + * \brief Record encryption engine class, for CBC mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for CBC processing: block cipher implementation, block cipher key, + * HMAC parameters (hash function, key, MAC length), and IV. If the + * IV is `NULL`, then a per-record IV will be used (TLS 1.1+). + */ +typedef struct br_sslrec_out_cbc_class_ br_sslrec_out_cbc_class; +struct br_sslrec_out_cbc_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CBC encryption). + * \param bc_key block cipher key. + * \param bc_key_len block cipher key length (in bytes). + * \param dig_impl hash function for HMAC. + * \param mac_key HMAC key. + * \param mac_key_len HMAC key length (in bytes). + * \param mac_out_len HMAC output length (in bytes). + * \param iv initial IV (or `NULL`). + */ + void (*init)(const br_sslrec_out_cbc_class **ctx, + const br_block_cbcenc_class *bc_impl, + const void *bc_key, size_t bc_key_len, + const br_hash_class *dig_impl, + const void *mac_key, size_t mac_key_len, size_t mac_out_len, + const void *iv); +}; + +/** + * \brief Context structure for decrypting incoming records with + * CBC + HMAC. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_sslrec_in_cbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_cbcdec_class *vtable; + br_aes_gen_cbcdec_keys aes; + br_des_gen_cbcdec_keys des; + } bc; + br_hmac_key_context mac; + size_t mac_len; + unsigned char iv[16]; + int explicit_IV; +#endif +} br_sslrec_in_cbc_context; + +/** + * \brief Static, constant vtable for record decryption with CBC. + */ +extern const br_sslrec_in_cbc_class br_sslrec_in_cbc_vtable; + +/** + * \brief Context structure for encrypting outgoing records with + * CBC + HMAC. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_sslrec_out_cbc_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_cbcenc_class *vtable; + br_aes_gen_cbcenc_keys aes; + br_des_gen_cbcenc_keys des; + } bc; + br_hmac_key_context mac; + size_t mac_len; + unsigned char iv[16]; + int explicit_IV; +#endif +} br_sslrec_out_cbc_context; + +/** + * \brief Static, constant vtable for record encryption with CBC. + */ +extern const br_sslrec_out_cbc_class br_sslrec_out_cbc_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for GCM mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for GCM processing: block cipher implementation, block cipher key, + * GHASH implementation, and 4-byte IV. + */ +typedef struct br_sslrec_in_gcm_class_ br_sslrec_in_gcm_class; +struct br_sslrec_in_gcm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param gh_impl GHASH implementation. + * \param iv static IV (4 bytes). + */ + void (*init)(const br_sslrec_in_gcm_class **ctx, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv); +}; + +/** + * \brief Record encryption engine class, for GCM mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for GCM processing: block cipher implementation, block cipher key, + * GHASH implementation, and 4-byte IV. + */ +typedef struct br_sslrec_out_gcm_class_ br_sslrec_out_gcm_class; +struct br_sslrec_out_gcm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param gh_impl GHASH implementation. + * \param iv static IV (4 bytes). + */ + void (*init)(const br_sslrec_out_gcm_class **ctx, + const br_block_ctr_class *bc_impl, + const void *key, size_t key_len, + br_ghash gh_impl, + const void *iv); +}; + +/** + * \brief Context structure for processing records with GCM. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_gcm_class *in; + const br_sslrec_out_gcm_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_ctr_class *vtable; + br_aes_gen_ctr_keys aes; + } bc; + br_ghash gh; + unsigned char iv[4]; + unsigned char h[16]; +#endif +} br_sslrec_gcm_context; + +/** + * \brief Static, constant vtable for record decryption with GCM. + */ +extern const br_sslrec_in_gcm_class br_sslrec_in_gcm_vtable; + +/** + * \brief Static, constant vtable for record encryption with GCM. + */ +extern const br_sslrec_out_gcm_class br_sslrec_out_gcm_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for ChaCha20+Poly1305. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for ChaCha20+Poly1305 processing: ChaCha20 implementation, + * Poly1305 implementation, key, and 12-byte IV. + */ +typedef struct br_sslrec_in_chapol_class_ br_sslrec_in_chapol_class; +struct br_sslrec_in_chapol_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param ichacha ChaCha20 implementation. + * \param ipoly Poly1305 implementation. + * \param key secret key (32 bytes). + * \param iv static IV (12 bytes). + */ + void (*init)(const br_sslrec_in_chapol_class **ctx, + br_chacha20_run ichacha, + br_poly1305_run ipoly, + const void *key, const void *iv); +}; + +/** + * \brief Record encryption engine class, for ChaCha20+Poly1305. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for ChaCha20+Poly1305 processing: ChaCha20 implementation, + * Poly1305 implementation, key, and 12-byte IV. + */ +typedef struct br_sslrec_out_chapol_class_ br_sslrec_out_chapol_class; +struct br_sslrec_out_chapol_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param ichacha ChaCha20 implementation. + * \param ipoly Poly1305 implementation. + * \param key secret key (32 bytes). + * \param iv static IV (12 bytes). + */ + void (*init)(const br_sslrec_out_chapol_class **ctx, + br_chacha20_run ichacha, + br_poly1305_run ipoly, + const void *key, const void *iv); +}; + +/** + * \brief Context structure for processing records with ChaCha20+Poly1305. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_chapol_class *in; + const br_sslrec_out_chapol_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + unsigned char key[32]; + unsigned char iv[12]; + br_chacha20_run ichacha; + br_poly1305_run ipoly; +#endif +} br_sslrec_chapol_context; + +/** + * \brief Static, constant vtable for record decryption with ChaCha20+Poly1305. + */ +extern const br_sslrec_in_chapol_class br_sslrec_in_chapol_vtable; + +/** + * \brief Static, constant vtable for record encryption with ChaCha20+Poly1305. + */ +extern const br_sslrec_out_chapol_class br_sslrec_out_chapol_vtable; + +/* ===================================================================== */ + +/** + * \brief Record decryption engine class, for CCM mode. + * + * This class type extends the decryption engine class with an + * initialisation method that receives the parameters needed + * for CCM processing: block cipher implementation, block cipher key, + * and 4-byte IV. + */ +typedef struct br_sslrec_in_ccm_class_ br_sslrec_in_ccm_class; +struct br_sslrec_in_ccm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_in_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR+CBC). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param iv static IV (4 bytes). + * \param tag_len tag length (in bytes) + */ + void (*init)(const br_sslrec_in_ccm_class **ctx, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len); +}; + +/** + * \brief Record encryption engine class, for CCM mode. + * + * This class type extends the encryption engine class with an + * initialisation method that receives the parameters needed + * for CCM processing: block cipher implementation, block cipher key, + * and 4-byte IV. + */ +typedef struct br_sslrec_out_ccm_class_ br_sslrec_out_ccm_class; +struct br_sslrec_out_ccm_class_ { + /** + * \brief Superclass, as first vtable field. + */ + br_sslrec_out_class inner; + + /** + * \brief Engine initialisation method. + * + * This method sets the vtable field in the context. + * + * \param ctx context to initialise. + * \param bc_impl block cipher implementation (CTR+CBC). + * \param key block cipher key. + * \param key_len block cipher key length (in bytes). + * \param iv static IV (4 bytes). + * \param tag_len tag length (in bytes) + */ + void (*init)(const br_sslrec_out_ccm_class **ctx, + const br_block_ctrcbc_class *bc_impl, + const void *key, size_t key_len, + const void *iv, size_t tag_len); +}; + +/** + * \brief Context structure for processing records with CCM. + * + * The same context structure is used for encrypting and decrypting. + * + * The first field points to the vtable. The other fields are opaque + * and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + union { + const void *gen; + const br_sslrec_in_ccm_class *in; + const br_sslrec_out_ccm_class *out; + } vtable; +#ifndef BR_DOXYGEN_IGNORE + uint64_t seq; + union { + const br_block_ctrcbc_class *vtable; + br_aes_gen_ctrcbc_keys aes; + } bc; + unsigned char iv[4]; + size_t tag_len; +#endif +} br_sslrec_ccm_context; + +/** + * \brief Static, constant vtable for record decryption with CCM. + */ +extern const br_sslrec_in_ccm_class br_sslrec_in_ccm_vtable; + +/** + * \brief Static, constant vtable for record encryption with CCM. + */ +extern const br_sslrec_out_ccm_class br_sslrec_out_ccm_vtable; + +/* ===================================================================== */ + +/** + * \brief Type for session parameters, to be saved for session resumption. + */ +typedef struct { + /** \brief Session ID buffer. */ + unsigned char session_id[32]; + /** \brief Session ID length (in bytes, at most 32). */ + unsigned char session_id_len; + /** \brief Protocol version. */ + uint16_t version; + /** \brief Cipher suite. */ + uint16_t cipher_suite; + /** \brief Master secret. */ + unsigned char master_secret[48]; +} br_ssl_session_parameters; + +#ifndef BR_DOXYGEN_IGNORE +/* + * Maximum number of cipher suites supported by a client or server. + */ +#define BR_MAX_CIPHER_SUITES 48 +#endif + +/** + * \brief Context structure for SSL engine. + * + * This strucuture is common to the client and server; both the client + * context (`br_ssl_client_context`) and the server context + * (`br_ssl_server_context`) include a `br_ssl_engine_context` as their + * first field. + * + * The engine context manages records, including alerts, closures, and + * transitions to new encryption/MAC algorithms. Processing of handshake + * records is delegated to externally provided code. This structure + * should not be used directly. + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* + * The error code. When non-zero, then the state is "failed" and + * no I/O may occur until reset. + */ + int err; + + /* + * Configured I/O buffers. They are either disjoint, or identical. + */ + unsigned char *ibuf, *obuf; + size_t ibuf_len, obuf_len; + + /* + * Maximum fragment length applies to outgoing records; incoming + * records can be processed as long as they fit in the input + * buffer. It is guaranteed that incoming records at least as big + * as max_frag_len can be processed. + */ + uint16_t max_frag_len; + unsigned char log_max_frag_len; + unsigned char max_frag_len_negotiated; + unsigned char peer_log_max_frag_len; + + /* + * Buffering management registers. + */ + size_t ixa, ixb, ixc; + size_t oxa, oxb, oxc; + unsigned char iomode; + unsigned char incrypt; + + /* + * Shutdown flag: when set to non-zero, incoming record bytes + * will not be accepted anymore. This is used after a close_notify + * has been received: afterwards, the engine no longer claims that + * it could receive bytes from the transport medium. + */ + unsigned char shutdown_recv; + + /* + * 'record_type_in' is set to the incoming record type when the + * record header has been received. + * 'record_type_out' is used to make the next outgoing record + * header when it is ready to go. + */ + unsigned char record_type_in, record_type_out; + + /* + * When a record is received, its version is extracted: + * -- if 'version_in' is 0, then it is set to the received version; + * -- otherwise, if the received version is not identical to + * the 'version_in' contents, then a failure is reported. + * + * This implements the SSL requirement that all records shall + * use the negotiated protocol version, once decided (in the + * ServerHello). It is up to the handshake handler to adjust this + * field when necessary. + */ + uint16_t version_in; + + /* + * 'version_out' is used when the next outgoing record is ready + * to go. + */ + uint16_t version_out; + + /* + * Record handler contexts. + */ + union { + const br_sslrec_in_class *vtable; + br_sslrec_in_cbc_context cbc; + br_sslrec_gcm_context gcm; + br_sslrec_chapol_context chapol; + br_sslrec_ccm_context ccm; + } in; + union { + const br_sslrec_out_class *vtable; + br_sslrec_out_clear_context clear; + br_sslrec_out_cbc_context cbc; + br_sslrec_gcm_context gcm; + br_sslrec_chapol_context chapol; + br_sslrec_ccm_context ccm; + } out; + + /* + * The "application data" flag. Value: + * 0 handshake is in process, no application data acceptable + * 1 application data can be sent and received + * 2 closing, no application data can be sent, but some + * can still be received (and discarded) + */ + unsigned char application_data; + + /* + * Context RNG. + * + * rng_init_done is initially 0. It is set to 1 when the + * basic structure of the RNG is set, and 2 when some + * entropy has been pushed in. The value 2 marks the RNG + * as "properly seeded". + * + * rng_os_rand_done is initially 0. It is set to 1 when + * some seeding from the OS or hardware has been attempted. + */ + br_hmac_drbg_context rng; + int rng_init_done; + int rng_os_rand_done; + + /* + * Supported minimum and maximum versions, and cipher suites. + */ + uint16_t version_min; + uint16_t version_max; + uint16_t suites_buf[BR_MAX_CIPHER_SUITES]; + unsigned char suites_num; + + /* + * For clients, the server name to send as a SNI extension. For + * servers, the name received in the SNI extension (if any). + */ + char server_name[256]; + + /* + * "Security parameters". These are filled by the handshake + * handler, and used when switching encryption state. + */ + unsigned char client_random[32]; + unsigned char server_random[32]; + br_ssl_session_parameters session; + + /* + * ECDHE elements: curve and point from the peer. The server also + * uses that buffer for the point to send to the client. + */ + unsigned char ecdhe_curve; + unsigned char ecdhe_point[133]; + unsigned char ecdhe_point_len; + + /* + * Secure renegotiation (RFC 5746): 'reneg' can be: + * 0 first handshake (server support is not known) + * 1 peer does not support secure renegotiation + * 2 peer supports secure renegotiation + * + * The saved_finished buffer contains the client and the + * server "Finished" values from the last handshake, in + * that order (12 bytes each). + */ + unsigned char reneg; + unsigned char saved_finished[24]; + + /* + * Behavioural flags. + */ + uint32_t flags; + + /* + * Context variables for the handshake processor. The 'pad' must + * be large enough to accommodate an RSA-encrypted pre-master + * secret, or an RSA signature; since we want to support up to + * RSA-4096, this means at least 512 bytes. (Other pad usages + * require its length to be at least 256.) + */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + unsigned char pad[512]; + unsigned char *hbuf_in, *hbuf_out, *saved_hbuf_out; + size_t hlen_in, hlen_out; + void (*hsrun)(void *ctx); + + /* + * The 'action' value communicates OOB information between the + * engine and the handshake processor. + * + * From the engine: + * 0 invocation triggered by I/O + * 1 invocation triggered by explicit close + * 2 invocation triggered by explicit renegotiation + */ + unsigned char action; + + /* + * State for alert messages. Value is either 0, or the value of + * the alert level byte (level is either 1 for warning, or 2 for + * fatal; we convert all other values to 'fatal'). + */ + unsigned char alert; + + /* + * Closure flags. This flag is set when a close_notify has been + * received from the peer. + */ + unsigned char close_received; + + /* + * Multi-hasher for the handshake messages. The handshake handler + * is responsible for resetting it when appropriate. + */ + br_multihash_context mhash; + + /* + * Pointer to the X.509 engine. The engine is supposed to be + * already initialized. It is used to validate the peer's + * certificate. + */ + const br_x509_class **x509ctx; + + /* + * Certificate chain to send. This is used by both client and + * server, when they send their respective Certificate messages. + * If chain_len is 0, then chain may be NULL. + */ + const br_x509_certificate *chain; + size_t chain_len; + const unsigned char *cert_cur; + size_t cert_len; + + /* + * List of supported protocol names (ALPN extension). If unset, + * (number of names is 0), then: + * - the client sends no ALPN extension; + * - the server ignores any incoming ALPN extension. + * + * Otherwise: + * - the client sends an ALPN extension with all the names; + * - the server selects the first protocol in its list that + * the client also supports, or fails (fatal alert 120) + * if the client sends an ALPN extension and there is no + * match. + * + * The 'selected_protocol' field contains 1+n if the matching + * name has index n in the list (the value is 0 if no match was + * performed, e.g. the peer did not send an ALPN extension). + */ + const char **protocol_names; + uint16_t protocol_names_num; + uint16_t selected_protocol; + + /* + * Pointers to implementations; left to NULL for unsupported + * functions. For the raw hash functions, implementations are + * referenced from the multihasher (mhash field). + */ + br_tls_prf_impl prf10; + br_tls_prf_impl prf_sha256; + br_tls_prf_impl prf_sha384; + const br_block_cbcenc_class *iaes_cbcenc; + const br_block_cbcdec_class *iaes_cbcdec; + const br_block_ctr_class *iaes_ctr; + const br_block_ctrcbc_class *iaes_ctrcbc; + const br_block_cbcenc_class *ides_cbcenc; + const br_block_cbcdec_class *ides_cbcdec; + br_ghash ighash; + br_chacha20_run ichacha; + br_poly1305_run ipoly; + const br_sslrec_in_cbc_class *icbc_in; + const br_sslrec_out_cbc_class *icbc_out; + const br_sslrec_in_gcm_class *igcm_in; + const br_sslrec_out_gcm_class *igcm_out; + const br_sslrec_in_chapol_class *ichapol_in; + const br_sslrec_out_chapol_class *ichapol_out; + const br_sslrec_in_ccm_class *iccm_in; + const br_sslrec_out_ccm_class *iccm_out; + const br_ec_impl *iec; + br_rsa_pkcs1_vrfy irsavrfy; + br_ecdsa_vrfy iecdsa; +#endif +} br_ssl_engine_context; + +/** + * \brief Get currently defined engine behavioural flags. + * + * \param cc SSL engine context. + * \return the flags. + */ +static inline uint32_t +br_ssl_engine_get_flags(br_ssl_engine_context *cc) +{ + return cc->flags; +} + +/** + * \brief Set all engine behavioural flags. + * + * \param cc SSL engine context. + * \param flags new value for all flags. + */ +static inline void +br_ssl_engine_set_all_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags = flags; +} + +/** + * \brief Set some engine behavioural flags. + * + * The flags set in the `flags` parameter are set in the context; other + * flags are untouched. + * + * \param cc SSL engine context. + * \param flags additional set flags. + */ +static inline void +br_ssl_engine_add_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags |= flags; +} + +/** + * \brief Clear some engine behavioural flags. + * + * The flags set in the `flags` parameter are cleared from the context; other + * flags are untouched. + * + * \param cc SSL engine context. + * \param flags flags to remove. + */ +static inline void +br_ssl_engine_remove_flags(br_ssl_engine_context *cc, uint32_t flags) +{ + cc->flags &= ~flags; +} + +/** + * \brief Behavioural flag: enforce server preferences. + * + * If this flag is set, then the server will enforce its own cipher suite + * preference order; otherwise, it follows the client preferences. + */ +#define BR_OPT_ENFORCE_SERVER_PREFERENCES ((uint32_t)1 << 0) + +/** + * \brief Behavioural flag: disable renegotiation. + * + * If this flag is set, then renegotiations are rejected unconditionally: + * they won't be honoured if asked for programmatically, and requests from + * the peer are rejected. + */ +#define BR_OPT_NO_RENEGOTIATION ((uint32_t)1 << 1) + +/** + * \brief Behavioural flag: tolerate lack of client authentication. + * + * If this flag is set in a server and the server requests a client + * certificate, but the authentication fails (the client does not send + * a certificate, or the client's certificate chain cannot be validated), + * then the connection keeps on. Without this flag, a failed client + * authentication terminates the connection. + * + * Notes: + * + * - If the client's certificate can be validated and its public key is + * supported, then a wrong signature value terminates the connection + * regardless of that flag. + * + * - If using full-static ECDH, then a failure to validate the client's + * certificate prevents the handshake from succeeding. + */ +#define BR_OPT_TOLERATE_NO_CLIENT_AUTH ((uint32_t)1 << 2) + +/** + * \brief Behavioural flag: fail on application protocol mismatch. + * + * The ALPN extension ([RFC 7301](https://tools.ietf.org/html/rfc7301)) + * allows the client to send a list of application protocol names, and + * the server to select one. A mismatch is one of the following occurrences: + * + * - On the client: the client sends a list of names, the server + * responds with a protocol name which is _not_ part of the list of + * names sent by the client. + * + * - On the server: the client sends a list of names, and the server + * is also configured with a list of names, but there is no common + * protocol name between the two lists. + * + * Normal behaviour in case of mismatch is to report no matching name + * (`br_ssl_engine_get_selected_protocol()` returns `NULL`) and carry on. + * If the flag is set, then a mismatch implies a protocol failure (if + * the mismatch is detected by the server, it will send a fatal alert). + * + * Note: even with this flag, `br_ssl_engine_get_selected_protocol()` + * may still return `NULL` if the client or the server does not send an + * ALPN extension at all. + */ +#define BR_OPT_FAIL_ON_ALPN_MISMATCH ((uint32_t)1 << 3) + +/** + * \brief Set the minimum and maximum supported protocol versions. + * + * The two provided versions MUST be supported by the implementation + * (i.e. TLS 1.0, 1.1 and 1.2), and `version_max` MUST NOT be lower + * than `version_min`. + * + * \param cc SSL engine context. + * \param version_min minimum supported TLS version. + * \param version_max maximum supported TLS version. + */ +static inline void +br_ssl_engine_set_versions(br_ssl_engine_context *cc, + unsigned version_min, unsigned version_max) +{ + cc->version_min = (uint16_t)version_min; + cc->version_max = (uint16_t)version_max; +} + +/** + * \brief Set the list of cipher suites advertised by this context. + * + * The provided array is copied into the context. It is the caller + * responsibility to ensure that all provided suites will be supported + * by the context. The engine context has enough room to receive _all_ + * suites supported by the implementation. The provided array MUST NOT + * contain duplicates. + * + * If the engine is for a client, the "signaling" pseudo-cipher suite + * `TLS_FALLBACK_SCSV` can be added at the end of the list, if the + * calling application is performing a voluntary downgrade (voluntary + * downgrades are not recommended, but if such a downgrade is done, then + * adding the fallback pseudo-suite is a good idea). + * + * \param cc SSL engine context. + * \param suites cipher suites. + * \param suites_num number of cipher suites. + */ +void br_ssl_engine_set_suites(br_ssl_engine_context *cc, + const uint16_t *suites, size_t suites_num); + +/** + * \brief Set the X.509 engine. + * + * The caller shall ensure that the X.509 engine is properly initialised. + * + * \param cc SSL engine context. + * \param x509ctx X.509 certificate validation context. + */ +static inline void +br_ssl_engine_set_x509(br_ssl_engine_context *cc, const br_x509_class **x509ctx) +{ + cc->x509ctx = x509ctx; +} + +/** + * \brief Set the supported protocol names. + * + * Protocol names are part of the ALPN extension ([RFC + * 7301](https://tools.ietf.org/html/rfc7301)). Each protocol name is a + * character string, containing no more than 255 characters (256 with the + * terminating zero). When names are set, then: + * + * - The client will send an ALPN extension, containing the names. If + * the server responds with an ALPN extension, the client will verify + * that the response contains one of its name, and report that name + * through `br_ssl_engine_get_selected_protocol()`. + * + * - The server will parse incoming ALPN extension (from clients), and + * try to find a common protocol; if none is found, the connection + * is aborted with a fatal alert. On match, a response ALPN extension + * is sent, and name is reported through + * `br_ssl_engine_get_selected_protocol()`. + * + * The provided array is linked in, and must remain valid while the + * connection is live. + * + * Names MUST NOT be empty. Names MUST NOT be longer than 255 characters + * (excluding the terminating 0). + * + * \param ctx SSL engine context. + * \param names list of protocol names (zero-terminated). + * \param num number of protocol names (MUST be 1 or more). + */ +static inline void +br_ssl_engine_set_protocol_names(br_ssl_engine_context *ctx, + const char **names, size_t num) +{ + ctx->protocol_names = names; + ctx->protocol_names_num = (uint16_t)num; +} + +/** + * \brief Get the selected protocol. + * + * If this context was initialised with a non-empty list of protocol + * names, and both client and server sent ALPN extensions during the + * handshake, and a common name was found, then that name is returned. + * Otherwise, `NULL` is returned. + * + * The returned pointer is one of the pointers provided to the context + * with `br_ssl_engine_set_protocol_names()`. + * + * \return the selected protocol, or `NULL`. + */ +static inline const char * +br_ssl_engine_get_selected_protocol(br_ssl_engine_context *ctx) +{ + unsigned k; + + k = ctx->selected_protocol; + return (k == 0 || k == 0xFFFF) ? NULL : ctx->protocol_names[k - 1]; +} + +/** + * \brief Set a hash function implementation (by ID). + * + * Hash functions set with this call will be used for SSL/TLS specific + * usages, not X.509 certificate validation. Only "standard" hash functions + * may be set (MD5, SHA-1, SHA-224, SHA-256, SHA-384, SHA-512). If `impl` + * is `NULL`, then the hash function support is removed, not added. + * + * \param ctx SSL engine context. + * \param id hash function identifier. + * \param impl hash function implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_hash(br_ssl_engine_context *ctx, + int id, const br_hash_class *impl) +{ + br_multihash_setimpl(&ctx->mhash, id, impl); +} + +/** + * \brief Get a hash function implementation (by ID). + * + * This function retrieves a hash function implementation which was + * set with `br_ssl_engine_set_hash()`. + * + * \param ctx SSL engine context. + * \param id hash function identifier. + * \return the hash function implementation (or `NULL`). + */ +static inline const br_hash_class * +br_ssl_engine_get_hash(br_ssl_engine_context *ctx, int id) +{ + return br_multihash_getimpl(&ctx->mhash, id); +} + +/** + * \brief Set the PRF implementation (for TLS 1.0 and 1.1). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the PRF used in TLS 1.0 and 1.1. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf10(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf10 = impl; +} + +/** + * \brief Set the PRF implementation with SHA-256 (for TLS 1.2). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the SHA-256 variant of the PRF used in TLS 1.2. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf_sha256(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf_sha256 = impl; +} + +/** + * \brief Set the PRF implementation with SHA-384 (for TLS 1.2). + * + * This function sets (or removes, if `impl` is `NULL`) the implementation + * for the SHA-384 variant of the PRF used in TLS 1.2. + * + * \param cc SSL engine context. + * \param impl PRF implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_prf_sha384(br_ssl_engine_context *cc, br_tls_prf_impl impl) +{ + cc->prf_sha384 = impl; +} + +/** + * \brief Set the AES/CBC implementations. + * + * \param cc SSL engine context. + * \param impl_enc AES/CBC encryption implementation (or `NULL`). + * \param impl_dec AES/CBC decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_cbc(br_ssl_engine_context *cc, + const br_block_cbcenc_class *impl_enc, + const br_block_cbcdec_class *impl_dec) +{ + cc->iaes_cbcenc = impl_enc; + cc->iaes_cbcdec = impl_dec; +} + +/** + * \brief Set the "default" AES/CBC implementations. + * + * This function configures in the engine the AES implementations that + * should provide best runtime performance on the local system, while + * still being safe (in particular, constant-time). It also sets the + * handlers for CBC records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_cbc(br_ssl_engine_context *cc); + +/** + * \brief Set the AES/CTR implementation. + * + * \param cc SSL engine context. + * \param impl AES/CTR encryption/decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_ctr(br_ssl_engine_context *cc, + const br_block_ctr_class *impl) +{ + cc->iaes_ctr = impl; +} + +/** + * \brief Set the "default" implementations for AES/GCM (AES/CTR + GHASH). + * + * This function configures in the engine the AES/CTR and GHASH + * implementation that should provide best runtime performance on the local + * system, while still being safe (in particular, constant-time). It also + * sets the handlers for GCM records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_gcm(br_ssl_engine_context *cc); + +/** + * \brief Set the DES/CBC implementations. + * + * \param cc SSL engine context. + * \param impl_enc DES/CBC encryption implementation (or `NULL`). + * \param impl_dec DES/CBC decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_des_cbc(br_ssl_engine_context *cc, + const br_block_cbcenc_class *impl_enc, + const br_block_cbcdec_class *impl_dec) +{ + cc->ides_cbcenc = impl_enc; + cc->ides_cbcdec = impl_dec; +} + +/** + * \brief Set the "default" DES/CBC implementations. + * + * This function configures in the engine the DES implementations that + * should provide best runtime performance on the local system, while + * still being safe (in particular, constant-time). It also sets the + * handlers for CBC records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_des_cbc(br_ssl_engine_context *cc); + +/** + * \brief Set the GHASH implementation (used in GCM mode). + * + * \param cc SSL engine context. + * \param impl GHASH implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ghash(br_ssl_engine_context *cc, br_ghash impl) +{ + cc->ighash = impl; +} + +/** + * \brief Set the ChaCha20 implementation. + * + * \param cc SSL engine context. + * \param ichacha ChaCha20 implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_chacha20(br_ssl_engine_context *cc, + br_chacha20_run ichacha) +{ + cc->ichacha = ichacha; +} + +/** + * \brief Set the Poly1305 implementation. + * + * \param cc SSL engine context. + * \param ipoly Poly1305 implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_poly1305(br_ssl_engine_context *cc, + br_poly1305_run ipoly) +{ + cc->ipoly = ipoly; +} + +/** + * \brief Set the "default" ChaCha20 and Poly1305 implementations. + * + * This function configures in the engine the ChaCha20 and Poly1305 + * implementations that should provide best runtime performance on the + * local system, while still being safe (in particular, constant-time). + * It also sets the handlers for ChaCha20+Poly1305 records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_chapol(br_ssl_engine_context *cc); + +/** + * \brief Set the AES/CTR+CBC implementation. + * + * \param cc SSL engine context. + * \param impl AES/CTR+CBC encryption/decryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_aes_ctrcbc(br_ssl_engine_context *cc, + const br_block_ctrcbc_class *impl) +{ + cc->iaes_ctrcbc = impl; +} + +/** + * \brief Set the "default" implementations for AES/CCM. + * + * This function configures in the engine the AES/CTR+CBC + * implementation that should provide best runtime performance on the local + * system, while still being safe (in particular, constant-time). It also + * sets the handlers for CCM records. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_aes_ccm(br_ssl_engine_context *cc); + +/** + * \brief Set the record encryption and decryption engines for CBC + HMAC. + * + * \param cc SSL engine context. + * \param impl_in record CBC decryption implementation (or `NULL`). + * \param impl_out record CBC encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_cbc(br_ssl_engine_context *cc, + const br_sslrec_in_cbc_class *impl_in, + const br_sslrec_out_cbc_class *impl_out) +{ + cc->icbc_in = impl_in; + cc->icbc_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for GCM. + * + * \param cc SSL engine context. + * \param impl_in record GCM decryption implementation (or `NULL`). + * \param impl_out record GCM encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_gcm(br_ssl_engine_context *cc, + const br_sslrec_in_gcm_class *impl_in, + const br_sslrec_out_gcm_class *impl_out) +{ + cc->igcm_in = impl_in; + cc->igcm_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for CCM. + * + * \param cc SSL engine context. + * \param impl_in record CCM decryption implementation (or `NULL`). + * \param impl_out record CCM encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ccm(br_ssl_engine_context *cc, + const br_sslrec_in_ccm_class *impl_in, + const br_sslrec_out_ccm_class *impl_out) +{ + cc->iccm_in = impl_in; + cc->iccm_out = impl_out; +} + +/** + * \brief Set the record encryption and decryption engines for + * ChaCha20+Poly1305. + * + * \param cc SSL engine context. + * \param impl_in record ChaCha20 decryption implementation (or `NULL`). + * \param impl_out record ChaCha20 encryption implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_chapol(br_ssl_engine_context *cc, + const br_sslrec_in_chapol_class *impl_in, + const br_sslrec_out_chapol_class *impl_out) +{ + cc->ichapol_in = impl_in; + cc->ichapol_out = impl_out; +} + +/** + * \brief Set the EC implementation. + * + * The elliptic curve implementation will be used for ECDH and ECDHE + * cipher suites, and for ECDSA support. + * + * \param cc SSL engine context. + * \param iec EC implementation (or `NULL`). + */ +static inline void +br_ssl_engine_set_ec(br_ssl_engine_context *cc, const br_ec_impl *iec) +{ + cc->iec = iec; +} + +/** + * \brief Set the "default" EC implementation. + * + * This function sets the elliptic curve implementation for ECDH and + * ECDHE cipher suites, and for ECDSA support. It selects the fastest + * implementation on the current system. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_ec(br_ssl_engine_context *cc); + +/** + * \brief Get the EC implementation configured in the provided engine. + * + * \param cc SSL engine context. + * \return the EC implementation. + */ +static inline const br_ec_impl * +br_ssl_engine_get_ec(br_ssl_engine_context *cc) +{ + return cc->iec; +} + +/** + * \brief Set the RSA signature verification implementation. + * + * On the client, this is used to verify the server's signature on its + * ServerKeyExchange message (for ECDHE_RSA cipher suites). On the server, + * this is used to verify the client's CertificateVerify message (if a + * client certificate is requested, and that certificate contains a RSA key). + * + * \param cc SSL engine context. + * \param irsavrfy RSA signature verification implementation. + */ +static inline void +br_ssl_engine_set_rsavrfy(br_ssl_engine_context *cc, br_rsa_pkcs1_vrfy irsavrfy) +{ + cc->irsavrfy = irsavrfy; +} + +/** + * \brief Set the "default" RSA implementation (signature verification). + * + * This function sets the RSA implementation (signature verification) + * to the fastest implementation available on the current platform. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_rsavrfy(br_ssl_engine_context *cc); + +/** + * \brief Get the RSA implementation (signature verification) configured + * in the provided engine. + * + * \param cc SSL engine context. + * \return the RSA signature verification implementation. + */ +static inline br_rsa_pkcs1_vrfy +br_ssl_engine_get_rsavrfy(br_ssl_engine_context *cc) +{ + return cc->irsavrfy; +} + +/* + * \brief Set the ECDSA implementation (signature verification). + * + * On the client, this is used to verify the server's signature on its + * ServerKeyExchange message (for ECDHE_ECDSA cipher suites). On the server, + * this is used to verify the client's CertificateVerify message (if a + * client certificate is requested, that certificate contains an EC key, + * and full-static ECDH is not used). + * + * The ECDSA implementation will use the EC core implementation configured + * in the engine context. + * + * \param cc client context. + * \param iecdsa ECDSA verification implementation. + */ +static inline void +br_ssl_engine_set_ecdsa(br_ssl_engine_context *cc, br_ecdsa_vrfy iecdsa) +{ + cc->iecdsa = iecdsa; +} + +/** + * \brief Set the "default" ECDSA implementation (signature verification). + * + * This function sets the ECDSA implementation (signature verification) + * to the fastest implementation available on the current platform. This + * call also sets the elliptic curve implementation itself, there again + * to the fastest EC implementation available. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_set_default_ecdsa(br_ssl_engine_context *cc); + +/** + * \brief Get the ECDSA implementation (signature verification) configured + * in the provided engine. + * + * \param cc SSL engine context. + * \return the ECDSA signature verification implementation. + */ +static inline br_ecdsa_vrfy +br_ssl_engine_get_ecdsa(br_ssl_engine_context *cc) +{ + return cc->iecdsa; +} + +/** + * \brief Set the I/O buffer for the SSL engine. + * + * Once this call has been made, `br_ssl_client_reset()` or + * `br_ssl_server_reset()` MUST be called before using the context. + * + * The provided buffer will be used as long as the engine context is + * used. The caller is responsible for keeping it available. + * + * If `bidi` is 0, then the engine will operate in half-duplex mode + * (it won't be able to send data while there is unprocessed incoming + * data in the buffer, and it won't be able to receive data while there + * is unsent data in the buffer). The optimal buffer size in half-duplex + * mode is `BR_SSL_BUFSIZE_MONO`; if the buffer is larger, then extra + * bytes are ignored. If the buffer is smaller, then this limits the + * capacity of the engine to support all allowed record sizes. + * + * If `bidi` is 1, then the engine will split the buffer into two + * parts, for separate handling of outgoing and incoming data. This + * enables full-duplex processing, but requires more RAM. The optimal + * buffer size in full-duplex mode is `BR_SSL_BUFSIZE_BIDI`; if the + * buffer is larger, then extra bytes are ignored. If the buffer is + * smaller, then the split will favour the incoming part, so that + * interoperability is maximised. + * + * \param cc SSL engine context + * \param iobuf I/O buffer. + * \param iobuf_len I/O buffer length (in bytes). + * \param bidi non-zero for full-duplex mode. + */ +void br_ssl_engine_set_buffer(br_ssl_engine_context *cc, + void *iobuf, size_t iobuf_len, int bidi); + +/** + * \brief Set the I/O buffers for the SSL engine. + * + * Once this call has been made, `br_ssl_client_reset()` or + * `br_ssl_server_reset()` MUST be called before using the context. + * + * This function is similar to `br_ssl_engine_set_buffer()`, except + * that it enforces full-duplex mode, and the two I/O buffers are + * provided as separate chunks. + * + * The macros `BR_SSL_BUFSIZE_INPUT` and `BR_SSL_BUFSIZE_OUTPUT` + * evaluate to the optimal (maximum) sizes for the input and output + * buffer, respectively. + * + * \param cc SSL engine context + * \param ibuf input buffer. + * \param ibuf_len input buffer length (in bytes). + * \param obuf output buffer. + * \param obuf_len output buffer length (in bytes). + */ +void br_ssl_engine_set_buffers_bidi(br_ssl_engine_context *cc, + void *ibuf, size_t ibuf_len, void *obuf, size_t obuf_len); + +/** + * \brief Determine if MFLN negotiation was successful + * + * \param cc SSL engine context. + */ +static inline uint8_t +br_ssl_engine_get_mfln_negotiated(br_ssl_engine_context *cc) +{ + return cc->max_frag_len_negotiated; +} + +/** + * \brief Inject some "initial entropy" in the context. + * + * This entropy will be added to what can be obtained from the + * underlying operating system, if that OS is supported. + * + * This function may be called several times; all injected entropy chunks + * are cumulatively mixed. + * + * If entropy gathering from the OS is supported and compiled in, then this + * step is optional. Otherwise, it is mandatory to inject randomness, and + * the caller MUST take care to push (as one or several successive calls) + * enough entropy to achieve cryptographic resistance (at least 80 bits, + * preferably 128 or more). The engine will report an error if no entropy + * was provided and none can be obtained from the OS. + * + * Take care that this function cannot assess the cryptographic quality of + * the provided bytes. + * + * In all generality, "entropy" must here be considered to mean "that + * which the attacker cannot predict". If your OS/architecture does not + * have a suitable source of randomness, then you can make do with the + * combination of a large enough secret value (possibly a copy of an + * asymmetric private key that you also store on the system) AND a + * non-repeating value (e.g. current time, provided that the local clock + * cannot be reset or altered by the attacker). + * + * \param cc SSL engine context. + * \param data extra entropy to inject. + * \param len length of the extra data (in bytes). + */ +void br_ssl_engine_inject_entropy(br_ssl_engine_context *cc, + const void *data, size_t len); + +/** + * \brief Get the "server name" in this engine. + * + * For clients, this is the name provided with `br_ssl_client_reset()`; + * for servers, this is the name received from the client as part of the + * ClientHello message. If there is no such name (e.g. the client did + * not send an SNI extension) then the returned string is empty + * (returned pointer points to a byte of value 0). + * + * The returned pointer refers to a buffer inside the context, which may + * be overwritten as part of normal SSL activity (even within the same + * connection, if a renegotiation occurs). + * + * \param cc SSL engine context. + * \return the server name (possibly empty). + */ +static inline const char * +br_ssl_engine_get_server_name(const br_ssl_engine_context *cc) +{ + return cc->server_name; +} + +/** + * \brief Get the protocol version. + * + * This function returns the protocol version that is used by the + * engine. That value is set after sending (for a server) or receiving + * (for a client) the ServerHello message. + * + * \param cc SSL engine context. + * \return the protocol version. + */ +static inline unsigned +br_ssl_engine_get_version(const br_ssl_engine_context *cc) +{ + return cc->session.version; +} + +/** + * \brief Get a copy of the session parameters. + * + * The session parameters are filled during the handshake, so this + * function shall not be called before completion of the handshake. + * The initial handshake is completed when the context first allows + * application data to be injected. + * + * This function copies the current session parameters into the provided + * structure. Beware that the session parameters include the master + * secret, which is sensitive data, to handle with great care. + * + * \param cc SSL engine context. + * \param pp destination structure for the session parameters. + */ +static inline void +br_ssl_engine_get_session_parameters(const br_ssl_engine_context *cc, + br_ssl_session_parameters *pp) +{ + memcpy(pp, &cc->session, sizeof *pp); +} + +/** + * \brief Set the session parameters to the provided values. + * + * This function is meant to be used in the client, before doing a new + * handshake; a session resumption will be attempted with these + * parameters. In the server, this function has no effect. + * + * \param cc SSL engine context. + * \param pp source structure for the session parameters. + */ +static inline void +br_ssl_engine_set_session_parameters(br_ssl_engine_context *cc, + const br_ssl_session_parameters *pp) +{ + memcpy(&cc->session, pp, sizeof *pp); +} + +/** + * \brief Get identifier for the curve used for key exchange. + * + * If the cipher suite uses ECDHE, then this function returns the + * identifier for the curve used for transient parameters. This is + * defined during the course of the handshake, when the ServerKeyExchange + * is sent (on the server) or received (on the client). If the + * cipher suite does not use ECDHE (e.g. static ECDH, or RSA key + * exchange), then this value is indeterminate. + * + * @param cc SSL engine context. + * @return the ECDHE curve identifier. + */ +static inline int +br_ssl_engine_get_ecdhe_curve(br_ssl_engine_context *cc) +{ + return cc->ecdhe_curve; +} + +/** + * \brief Get the current engine state. + * + * An SSL engine (client or server) has, at any time, a state which is + * the combination of zero, one or more of these flags: + * + * - `BR_SSL_CLOSED` + * + * Engine is finished, no more I/O (until next reset). + * + * - `BR_SSL_SENDREC` + * + * Engine has some bytes to send to the peer. + * + * - `BR_SSL_RECVREC` + * + * Engine expects some bytes from the peer. + * + * - `BR_SSL_SENDAPP` + * + * Engine may receive application data to send (or flush). + * + * - `BR_SSL_RECVAPP` + * + * Engine has obtained some application data from the peer, + * that should be read by the caller. + * + * If no flag at all is set (state value is 0), then the engine is not + * fully initialised yet. + * + * The `BR_SSL_CLOSED` flag is exclusive; when it is set, no other flag + * is set. To distinguish between a normal closure and an error, use + * `br_ssl_engine_last_error()`. + * + * Generally speaking, `BR_SSL_SENDREC` and `BR_SSL_SENDAPP` are mutually + * exclusive: the input buffer, at any point, either accumulates + * plaintext data, or contains an assembled record that is being sent. + * Similarly, `BR_SSL_RECVREC` and `BR_SSL_RECVAPP` are mutually exclusive. + * This may change in a future library version. + * + * \param cc SSL engine context. + * \return the current engine state. + */ +unsigned br_ssl_engine_current_state(const br_ssl_engine_context *cc); + +/** \brief SSL engine state: closed or failed. */ +#define BR_SSL_CLOSED 0x0001 +/** \brief SSL engine state: record data is ready to be sent to the peer. */ +#define BR_SSL_SENDREC 0x0002 +/** \brief SSL engine state: engine may receive records from the peer. */ +#define BR_SSL_RECVREC 0x0004 +/** \brief SSL engine state: engine may accept application data to send. */ +#define BR_SSL_SENDAPP 0x0008 +/** \brief SSL engine state: engine has received application data. */ +#define BR_SSL_RECVAPP 0x0010 + +/** + * \brief Get the engine error indicator. + * + * The error indicator is `BR_ERR_OK` (0) if no error was encountered + * since the last call to `br_ssl_client_reset()` or + * `br_ssl_server_reset()`. Other status values are "sticky": they + * remain set, and prevent all I/O activity, until cleared. Only the + * reset calls clear the error indicator. + * + * \param cc SSL engine context. + * \return 0, or a non-zero error code. + */ +static inline int +br_ssl_engine_last_error(const br_ssl_engine_context *cc) +{ + return cc->err; +} + +/* + * There are four I/O operations, each identified by a symbolic name: + * + * sendapp inject application data in the engine + * recvapp retrieving application data from the engine + * sendrec sending records on the transport medium + * recvrec receiving records from the transport medium + * + * Terminology works thus: in a layered model where the SSL engine sits + * between the application and the network, "send" designates operations + * where bytes flow from application to network, and "recv" for the + * reverse operation. Application data (the plaintext that is to be + * conveyed through SSL) is "app", while encrypted records are "rec". + * Note that from the SSL engine point of view, "sendapp" and "recvrec" + * designate bytes that enter the engine ("inject" operation), while + * "recvapp" and "sendrec" designate bytes that exit the engine + * ("extract" operation). + * + * For the operation 'xxx', two functions are defined: + * + * br_ssl_engine_xxx_buf + * Returns a pointer and length to the buffer to use for that + * operation. '*len' is set to the number of bytes that may be read + * from the buffer (extract operation) or written to the buffer + * (inject operation). If no byte may be exchanged for that operation + * at that point, then '*len' is set to zero, and NULL is returned. + * The engine state is unmodified by this call. + * + * br_ssl_engine_xxx_ack + * Informs the engine that 'len' bytes have been read from the buffer + * (extract operation) or written to the buffer (inject operation). + * The 'len' value MUST NOT be zero. The 'len' value MUST NOT exceed + * that which was obtained from a preceding br_ssl_engine_xxx_buf() + * call. + */ + +/** + * \brief Get buffer for application data to send. + * + * If the engine is ready to accept application data to send to the + * peer, then this call returns a pointer to the buffer where such + * data shall be written, and its length is written in `*len`. + * Otherwise, `*len` is set to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the application data output buffer length, or 0. + * \return the application data output buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_sendapp_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Inform the engine of some new application data. + * + * After writing `len` bytes in the buffer returned by + * `br_ssl_engine_sendapp_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_sendapp_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes pushed (not zero). + */ +void br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for received application data. + * + * If the engine has received application data from the peer, then this + * call returns a pointer to the buffer from where such data shall be + * read, and its length is written in `*len`. Otherwise, `*len` is set + * to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the application data input buffer length, or 0. + * \return the application data input buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_recvapp_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Acknowledge some received application data. + * + * After reading `len` bytes from the buffer returned by + * `br_ssl_engine_recvapp_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_recvapp_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes read (not zero). + */ +void br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for record data to send. + * + * If the engine has prepared some records to send to the peer, then this + * call returns a pointer to the buffer from where such data shall be + * read, and its length is written in `*len`. Otherwise, `*len` is set + * to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the record data output buffer length, or 0. + * \return the record data output buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_sendrec_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Acknowledge some sent record data. + * + * After reading `len` bytes from the buffer returned by + * `br_ssl_engine_sendrec_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_sendrec_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes read (not zero). + */ +void br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Get buffer for incoming records. + * + * If the engine is ready to accept records from the peer, then this + * call returns a pointer to the buffer where such data shall be + * written, and its length is written in `*len`. Otherwise, `*len` is + * set to 0 and `NULL` is returned. + * + * \param cc SSL engine context. + * \param len receives the record data input buffer length, or 0. + * \return the record data input buffer, or `NULL`. + */ +unsigned char *br_ssl_engine_recvrec_buf( + const br_ssl_engine_context *cc, size_t *len); + +/** + * \brief Inform the engine of some new record data. + * + * After writing `len` bytes in the buffer returned by + * `br_ssl_engine_recvrec_buf()`, the application shall call this + * function to trigger any relevant processing. The `len` parameter + * MUST NOT be 0, and MUST NOT exceed the value obtained in the + * `br_ssl_engine_recvrec_buf()` call. + * + * \param cc SSL engine context. + * \param len number of bytes pushed (not zero). + */ +void br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); + +/** + * \brief Flush buffered application data. + * + * If some application data has been buffered in the engine, then wrap + * it into a record and mark it for sending. If no application data has + * been buffered but the engine would be ready to accept some, AND the + * `force` parameter is non-zero, then an empty record is assembled and + * marked for sending. In all other cases, this function does nothing. + * + * Empty records are technically legal, but not all existing SSL/TLS + * implementations support them. Empty records can be useful as a + * transparent "keep-alive" mechanism to maintain some low-level + * network activity. + * + * \param cc SSL engine context. + * \param force non-zero to force sending an empty record. + */ +void br_ssl_engine_flush(br_ssl_engine_context *cc, int force); + +/** + * \brief Initiate a closure. + * + * If, at that point, the context is open and in ready state, then a + * `close_notify` alert is assembled and marked for sending; this + * triggers the closure protocol. Otherwise, no such alert is assembled. + * + * \param cc SSL engine context. + */ +void br_ssl_engine_close(br_ssl_engine_context *cc); + +/** + * \brief Initiate a renegotiation. + * + * If the engine is failed or closed, or if the peer is known not to + * support secure renegotiation (RFC 5746), or if renegotiations have + * been disabled with the `BR_OPT_NO_RENEGOTIATION` flag, or if there + * is buffered incoming application data, then this function returns 0 + * and nothing else happens. + * + * Otherwise, this function returns 1, and a renegotiation attempt is + * triggered (if a handshake is already ongoing at that point, then + * no new handshake is triggered). + * + * \param cc SSL engine context. + * \return 1 on success, 0 on error. + */ +int br_ssl_engine_renegotiate(br_ssl_engine_context *cc); + +/** + * \brief Export key material from a connected SSL engine (RFC 5705). + * + * This calls compute a secret key of arbitrary length from the master + * secret of a connected SSL engine. If the provided context is not + * currently in "application data" state (initial handshake is not + * finished, another handshake is ongoing, or the connection failed or + * was closed), then this function returns 0. Otherwise, a secret key of + * length `len` bytes is computed and written in the buffer pointed to + * by `dst`, and 1 is returned. + * + * The computed key follows the specification described in RFC 5705. + * That RFC includes two key computations, with and without a "context + * value". If `context` is `NULL`, then the variant without context is + * used; otherwise, the `context_len` bytes located at the address + * pointed to by `context` are used in the computation. Note that it + * is possible to have a "with context" key with a context length of + * zero bytes, by setting `context` to a non-`NULL` value but + * `context_len` to 0. + * + * When context bytes are used, the context length MUST NOT exceed + * 65535 bytes. + * + * \param cc SSL engine context. + * \param dst destination buffer for exported key. + * \param len exported key length (in bytes). + * \param label disambiguation label. + * \param context context value (or `NULL`). + * \param context_len context length (in bytes). + * \return 1 on success, 0 on error. + */ +int br_ssl_key_export(br_ssl_engine_context *cc, + void *dst, size_t len, const char *label, + const void *context, size_t context_len); + +/* + * Pre-declaration for the SSL client context. + */ +typedef struct br_ssl_client_context_ br_ssl_client_context; + +/** + * \brief Type for the client certificate, if requested by the server. + */ +typedef struct { + /** + * \brief Authentication type. + * + * This is either `BR_AUTH_RSA` (RSA signature), `BR_AUTH_ECDSA` + * (ECDSA signature), or `BR_AUTH_ECDH` (static ECDH key exchange). + */ + int auth_type; + + /** + * \brief Hash function for computing the CertificateVerify. + * + * This is the symbolic identifier for the hash function that + * will be used to produce the hash of handshake messages, to + * be signed into the CertificateVerify. For full static ECDH + * (client and server certificates are both EC in the same + * curve, and static ECDH is used), this value is set to -1. + * + * Take care that with TLS 1.0 and 1.1, that value MUST match + * the protocol requirements: value must be 0 (MD5+SHA-1) for + * a RSA signature, or 2 (SHA-1) for an ECDSA signature. Only + * TLS 1.2 allows for other hash functions. + */ + int hash_id; + + /** + * \brief Certificate chain to send to the server. + * + * This is an array of `br_x509_certificate` objects, each + * normally containing a DER-encoded certificate. The client + * code does not try to decode these elements. If there is no + * chain to send to the server, then this pointer shall be + * set to `NULL`. + */ + const br_x509_certificate *chain; + + /** + * \brief Certificate chain length (number of certificates). + * + * If there is no chain to send to the server, then this value + * shall be set to 0. + */ + size_t chain_len; + +} br_ssl_client_certificate; + +/* + * Note: the constants below for signatures match the TLS constants. + */ + +/** \brief Client authentication type: static ECDH. */ +#define BR_AUTH_ECDH 0 +/** \brief Client authentication type: RSA signature. */ +#define BR_AUTH_RSA 1 +/** \brief Client authentication type: ECDSA signature. */ +#define BR_AUTH_ECDSA 3 + +/** + * \brief Class type for a certificate handler (client side). + * + * A certificate handler selects a client certificate chain to send to + * the server, upon explicit request from that server. It receives + * the list of trust anchor DN from the server, and supported types + * of certificates and signatures, and returns the chain to use. It + * is also invoked to perform the corresponding private key operation + * (a signature, or an ECDH computation). + * + * The SSL client engine will first push the trust anchor DN with + * `start_name_list()`, `start_name()`, `append_name()`, `end_name()` + * and `end_name_list()`. Then it will call `choose()`, to select the + * actual chain (and signature/hash algorithms). Finally, it will call + * either `do_sign()` or `do_keyx()`, depending on the algorithm choices. + */ +typedef struct br_ssl_client_certificate_class_ br_ssl_client_certificate_class; +struct br_ssl_client_certificate_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Begin reception of a list of trust anchor names. This + * is called while parsing the incoming CertificateRequest. + * + * \param pctx certificate handler context. + */ + void (*start_name_list)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief Begin reception of a new trust anchor name. + * + * The total encoded name length is provided; it is less than + * 65535 bytes. + * + * \param pctx certificate handler context. + * \param len encoded name length (in bytes). + */ + void (*start_name)(const br_ssl_client_certificate_class **pctx, + size_t len); + + /** + * \brief Receive some more bytes for the current trust anchor name. + * + * The provided reference (`data`) points to a transient buffer + * they may be reused as soon as this function returns. The chunk + * length (`len`) is never zero. + * + * \param pctx certificate handler context. + * \param data anchor name chunk. + * \param len anchor name chunk length (in bytes). + */ + void (*append_name)(const br_ssl_client_certificate_class **pctx, + const unsigned char *data, size_t len); + + /** + * \brief End current trust anchor name. + * + * This function is called when all the encoded anchor name data + * has been provided. + * + * \param pctx certificate handler context. + */ + void (*end_name)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief End list of trust anchor names. + * + * This function is called when all the anchor names in the + * CertificateRequest message have been obtained. + * + * \param pctx certificate handler context. + */ + void (*end_name_list)(const br_ssl_client_certificate_class **pctx); + + /** + * \brief Select client certificate and algorithms. + * + * This callback function shall fill the provided `choices` + * structure with the selected algorithms and certificate chain. + * The `hash_id`, `chain` and `chain_len` fields must be set. If + * the client cannot or does not wish to send a certificate, + * then it shall set `chain` to `NULL` and `chain_len` to 0. + * + * The `auth_types` parameter describes the authentication types, + * signature algorithms and hash functions that are supported by + * both the client context and the server, and compatible with + * the current protocol version. This is a bit field with the + * following contents: + * + * - If RSA signatures with hash function x are supported, then + * bit x is set. + * + * - If ECDSA signatures with hash function x are supported, + * then bit 8+x is set. + * + * - If static ECDH is supported, with a RSA-signed certificate, + * then bit 16 is set. + * + * - If static ECDH is supported, with an ECDSA-signed certificate, + * then bit 17 is set. + * + * Notes: + * + * - When using TLS 1.0 or 1.1, the hash function for RSA + * signatures is always the special MD5+SHA-1 (id 0), and the + * hash function for ECDSA signatures is always SHA-1 (id 2). + * + * - When using TLS 1.2, the list of hash functions is trimmed + * down to include only hash functions that the client context + * can support. The actual server list can be obtained with + * `br_ssl_client_get_server_hashes()`; that list may be used + * to select the certificate chain to send to the server. + * + * \param pctx certificate handler context. + * \param cc SSL client context. + * \param auth_types supported authentication types and algorithms. + * \param choices destination structure for the policy choices. + */ + void (*choose)(const br_ssl_client_certificate_class **pctx, + const br_ssl_client_context *cc, uint32_t auth_types, + br_ssl_client_certificate *choices); + + /** + * \brief Perform key exchange (client part). + * + * This callback is invoked in case of a full static ECDH key + * exchange: + * + * - the cipher suite uses `ECDH_RSA` or `ECDH_ECDSA`; + * + * - the server requests a client certificate; + * + * - the client has, and sends, a client certificate that + * uses an EC key in the same curve as the server's key, + * and chooses static ECDH (the `hash_id` field in the choice + * structure was set to -1). + * + * In that situation, this callback is invoked to compute the + * client-side ECDH: the provided `data` (of length `*len` bytes) + * is the server's public key point (as decoded from its + * certificate), and the client shall multiply that point with + * its own private key, and write back the X coordinate of the + * resulting point in the same buffer, starting at offset 0. + * The `*len` value shall be modified to designate the actual + * length of the X coordinate. + * + * The callback must uphold the following: + * + * - If the input array does not have the proper length for + * an encoded curve point, then an error (0) shall be reported. + * + * - If the input array has the proper length, then processing + * MUST be constant-time, even if the data is not a valid + * encoded point. + * + * - This callback MUST check that the input point is valid. + * + * Returned value is 1 on success, 0 on error. + * + * \param pctx certificate handler context. + * \param data server public key point. + * \param len public key point length / X coordinate length. + * \return 1 on success, 0 on error. + */ + uint32_t (*do_keyx)(const br_ssl_client_certificate_class **pctx, + unsigned char *data, size_t *len); + + /** + * \brief Perform a signature (client authentication). + * + * This callback is invoked when a client certificate was sent, + * and static ECDH is not used. It shall compute a signature, + * using the client's private key, over the provided hash value + * (which is the hash of all previous handshake messages). + * + * On input, the hash value to sign is in `data`, of size + * `hv_len`; the involved hash function is identified by + * `hash_id`. The signature shall be computed and written + * back into `data`; the total size of that buffer is `len` + * bytes. + * + * This callback shall verify that the signature length does not + * exceed `len` bytes, and abstain from writing the signature if + * it does not fit. + * + * For RSA signatures, the `hash_id` may be 0, in which case + * this is the special header-less signature specified in TLS 1.0 + * and 1.1, with a 36-byte hash value. Otherwise, normal PKCS#1 + * v1.5 signatures shall be computed. + * + * For ECDSA signatures, the signature value shall use the ASN.1 + * based encoding. + * + * Returned value is the signature length (in bytes), or 0 on error. + * + * \param pctx certificate handler context. + * \param hash_id hash function identifier. + * \param hv_len hash value length (in bytes). + * \param data input/output buffer (hash value, then signature). + * \param len total buffer length (in bytes). + * \return signature length (in bytes) on success, or 0 on error. + */ + size_t (*do_sign)(const br_ssl_client_certificate_class **pctx, + int hash_id, size_t hv_len, unsigned char *data, size_t len); +}; + +/** + * \brief A single-chain RSA client certificate handler. + * + * This handler uses a single certificate chain, with a RSA + * signature. The list of trust anchor DN is ignored. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_client_certificate_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_rsa_private_key *sk; + br_rsa_pkcs1_sign irsasign; +#endif +} br_ssl_client_certificate_rsa_context; + +/** + * \brief A single-chain EC client certificate handler. + * + * This handler uses a single certificate chain, with a RSA + * signature. The list of trust anchor DN is ignored. + * + * This handler may support both static ECDH, and ECDSA signatures + * (either usage may be selectively disabled). + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_client_certificate_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_ec_private_key *sk; + unsigned allowed_usages; + unsigned issuer_key_type; + const br_multihash_context *mhash; + const br_ec_impl *iec; + br_ecdsa_sign iecdsa; +#endif +} br_ssl_client_certificate_ec_context; + +/** + * \brief Context structure for a SSL client. + * + * The first field (called `eng`) is the SSL engine; all functions that + * work on a `br_ssl_engine_context` structure shall take as parameter + * a pointer to that field. The other structure fields are opaque and + * must not be accessed directly. + */ +struct br_ssl_client_context_ { + /** + * \brief The encapsulated engine context. + */ + br_ssl_engine_context eng; + +#ifndef BR_DOXYGEN_IGNORE + /* + * Minimum ClientHello length; padding with an extension (RFC + * 7685) is added if necessary to match at least that length. + * Such padding is nominally unnecessary, but it has been used + * to work around some server implementation bugs. + */ + uint16_t min_clienthello_len; + + /* + * Bit field for algoithms (hash + signature) supported by the + * server when requesting a client certificate. + */ + uint32_t hashes; + + /* + * Server's public key curve. + */ + int server_curve; + + /* + * Context for certificate handler. + */ + const br_ssl_client_certificate_class **client_auth_vtable; + + /* + * Client authentication type. + */ + unsigned char auth_type; + + /* + * Hash function to use for the client signature. This is 0xFF + * if static ECDH is used. + */ + unsigned char hash_id; + + /* + * For the core certificate handlers, thus avoiding (in most + * cases) the need for an externally provided policy context. + */ + union { + const br_ssl_client_certificate_class *vtable; + br_ssl_client_certificate_rsa_context single_rsa; + br_ssl_client_certificate_ec_context single_ec; + } client_auth; + + /* + * Implementations. + */ + br_rsa_public irsapub; +#endif +}; + +/** + * \brief Get the hash functions and signature algorithms supported by + * the server. + * + * This value is a bit field: + * + * - If RSA (PKCS#1 v1.5) is supported with hash function of ID `x`, + * then bit `x` is set (hash function ID is 0 for the special MD5+SHA-1, + * or 2 to 6 for the SHA family). + * + * - If ECDSA is supported with hash function of ID `x`, then bit `8+x` + * is set. + * + * - Newer algorithms are symbolic 16-bit identifiers that do not + * represent signature algorithm and hash function separately. If + * the TLS-level identifier is `0x0800+x` for a `x` in the 0..15 + * range, then bit `16+x` is set. + * + * "New algorithms" are currently defined only in draft documents, so + * this support is subject to possible change. Right now (early 2017), + * this maps ed25519 (EdDSA on Curve25519) to bit 23, and ed448 (EdDSA + * on Curve448) to bit 24. If the identifiers on the wire change in + * future document, then the decoding mechanism in BearSSL will be + * amended to keep mapping ed25519 and ed448 on bits 23 and 24, + * respectively. Mapping of other new algorithms (e.g. RSA/PSS) is not + * guaranteed yet. + * + * \param cc client context. + * \return the server-supported hash functions and signature algorithms. + */ +static inline uint32_t +br_ssl_client_get_server_hashes(const br_ssl_client_context *cc) +{ + return cc->hashes; +} + +/** + * \brief Get the server key curve. + * + * This function returns the ID for the curve used by the server's public + * key. This is set when the server's certificate chain is processed; + * this value is 0 if the server's key is not an EC key. + * + * \return the server's public key curve ID, or 0. + */ +static inline int +br_ssl_client_get_server_curve(const br_ssl_client_context *cc) +{ + return cc->server_curve; +} + +/* + * Each br_ssl_client_init_xxx() function sets the list of supported + * cipher suites and used implementations, as specified by the profile + * name 'xxx'. Defined profile names are: + * + * full all supported versions and suites; constant-time implementations + * TODO: add other profiles + */ + +/** + * \brief SSL client profile: full. + * + * This function initialises the provided SSL client context with + * all supported algorithms and cipher suites. It also initialises + * a companion X.509 validation engine with all supported algorithms, + * and the provided trust anchors; the X.509 engine will be used by + * the client context to validate the server's certificate. + * + * \param cc client context to initialise. + * \param xc X.509 validation context to initialise. + * \param trust_anchors trust anchors to use. + * \param trust_anchors_num number of trust anchors. + */ +void br_ssl_client_init_full(br_ssl_client_context *cc, + br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Clear the complete contents of a SSL client context. + * + * Everything is cleared, including the reference to the configured buffer, + * implementations, cipher suites and state. This is a preparatory step + * to assembling a custom profile. + * + * \param cc client context to clear. + */ +void br_ssl_client_zero(br_ssl_client_context *cc); + +/** + * \brief Set an externally provided client certificate handler context. + * + * The handler's methods are invoked when the server requests a client + * certificate. + * + * \param cc client context. + * \param pctx certificate handler context (pointer to its vtable field). + */ +static inline void +br_ssl_client_set_client_certificate(br_ssl_client_context *cc, + const br_ssl_client_certificate_class **pctx) +{ + cc->client_auth_vtable = pctx; +} + +/** + * \brief Set the RSA public-key operations implementation. + * + * This will be used to encrypt the pre-master secret with the server's + * RSA public key (RSA-encryption cipher suites only). + * + * \param cc client context. + * \param irsapub RSA public-key encryption implementation. + */ +static inline void +br_ssl_client_set_rsapub(br_ssl_client_context *cc, br_rsa_public irsapub) +{ + cc->irsapub = irsapub; +} + +/** + * \brief Set the "default" RSA implementation for public-key operations. + * + * This sets the RSA implementation in the client context (for encrypting + * the pre-master secret, in `TLS_RSA_*` cipher suites) to the fastest + * available on the current platform. + * + * \param cc client context. + */ +void br_ssl_client_set_default_rsapub(br_ssl_client_context *cc); + +/** + * \brief Set the minimum ClientHello length (RFC 7685 padding). + * + * If this value is set and the ClientHello would be shorter, then + * the Pad ClientHello extension will be added with enough padding bytes + * to reach the target size. Because of the extension header, the resulting + * size will sometimes be slightly more than `len` bytes if the target + * size cannot be exactly met. + * + * The target length relates to the _contents_ of the ClientHello, not + * counting its 4-byte header. For instance, if `len` is set to 512, + * then the padding will bring the ClientHello size to 516 bytes with its + * header, and 521 bytes when counting the 5-byte record header. + * + * \param cc client context. + * \param len minimum ClientHello length (in bytes). + */ +static inline void +br_ssl_client_set_min_clienthello_len(br_ssl_client_context *cc, uint16_t len) +{ + cc->min_clienthello_len = len; +} + +/** + * \brief Prepare or reset a client context for a new connection. + * + * The `server_name` parameter is used to fill the SNI extension; the + * X.509 "minimal" engine will also match that name against the server + * names included in the server's certificate. If the parameter is + * `NULL` then no SNI extension will be sent, and the X.509 "minimal" + * engine (if used for server certificate validation) will not check + * presence of any specific name in the received certificate. + * + * Therefore, setting the `server_name` to `NULL` shall be reserved + * to cases where alternate or additional methods are used to ascertain + * that the right server public key is used (e.g. a "known key" model). + * + * If `resume_session` is non-zero and the context was previously used + * then the session parameters may be reused (depending on whether the + * server previously sent a non-empty session ID, and accepts the session + * resumption). The session parameters for session resumption can also + * be set explicitly with `br_ssl_engine_set_session_parameters()`. + * + * On failure, the context is marked as failed, and this function + * returns 0. A possible failure condition is when no initial entropy + * was injected, and none could be obtained from the OS (either OS + * randomness gathering is not supported, or it failed). + * + * \param cc client context. + * \param server_name target server name, or `NULL`. + * \param resume_session non-zero to try session resumption. + * \return 0 on failure, 1 on success. + */ +int br_ssl_client_reset(br_ssl_client_context *cc, + const char *server_name, int resume_session); + +/** + * \brief Forget any session in the context. + * + * This means that the next handshake that uses this context will + * necessarily be a full handshake (this applies both to new connections + * and to renegotiations). + * + * \param cc client context. + */ +static inline void +br_ssl_client_forget_session(br_ssl_client_context *cc) +{ + cc->eng.session.session_id_len = 0; +} + +/** + * \brief Set client certificate chain and key (single RSA case). + * + * This function sets a client certificate chain, that the client will + * send to the server whenever a client certificate is requested. This + * certificate uses an RSA public key; the corresponding private key is + * invoked for authentication. Trust anchor names sent by the server are + * ignored. + * + * The provided chain and private key are linked in the client context; + * they must remain valid as long as they may be used, i.e. normally + * for the duration of the connection, since they might be invoked + * again upon renegotiations. + * + * \param cc SSL client context. + * \param chain client certificate chain (SSL order: EE comes first). + * \param chain_len client chain length (number of certificates). + * \param sk client private key. + * \param irsasign RSA signature implementation (PKCS#1 v1.5). + */ +void br_ssl_client_set_single_rsa(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, br_rsa_pkcs1_sign irsasign); + +/* + * \brief Set the client certificate chain and key (single EC case). + * + * This function sets a client certificate chain, that the client will + * send to the server whenever a client certificate is requested. This + * certificate uses an EC public key; the corresponding private key is + * invoked for authentication. Trust anchor names sent by the server are + * ignored. + * + * The provided chain and private key are linked in the client context; + * they must remain valid as long as they may be used, i.e. normally + * for the duration of the connection, since they might be invoked + * again upon renegotiations. + * + * The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`. The `BR_KEYTYPE_KEYX` + * value allows full static ECDH, while the `BR_KEYTYPE_SIGN` value + * allows ECDSA signatures. If ECDSA signatures are used, then an ECDSA + * signature implementation must be provided; otherwise, the `iecdsa` + * parameter may be 0. + * + * The `cert_issuer_key_type` value is either `BR_KEYTYPE_RSA` or + * `BR_KEYTYPE_EC`; it is the type of the public key used the the CA + * that issued (signed) the client certificate. That value is used with + * full static ECDH: support of the certificate by the server depends + * on how the certificate was signed. (Note: when using TLS 1.2, this + * parameter is ignored; but its value matters for TLS 1.0 and 1.1.) + * + * \param cc server context. + * \param chain server certificate chain to send. + * \param chain_len chain length (number of certificates). + * \param sk server private key (EC). + * \param allowed_usages allowed private key usages. + * \param cert_issuer_key_type issuing CA's key type. + * \param iec EC core implementation. + * \param iecdsa ECDSA signature implementation ("asn1" format). + */ +void br_ssl_client_set_single_ec(br_ssl_client_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa); + +/** + * \brief Type for a "translated cipher suite", as an array of two + * 16-bit integers. + * + * The first element is the cipher suite identifier (as used on the wire). + * The second element is the concatenation of four 4-bit elements which + * characterise the cipher suite contents. In most to least significant + * order, these 4-bit elements are: + * + * - Bits 12 to 15: key exchange + server key type + * + * | val | symbolic constant | suite type | details | + * | :-- | :----------------------- | :---------- | :----------------------------------------------- | + * | 0 | `BR_SSLKEYX_RSA` | RSA | RSA key exchange, key is RSA (encryption) | + * | 1 | `BR_SSLKEYX_ECDHE_RSA` | ECDHE_RSA | ECDHE key exchange, key is RSA (signature) | + * | 2 | `BR_SSLKEYX_ECDHE_ECDSA` | ECDHE_ECDSA | ECDHE key exchange, key is EC (signature) | + * | 3 | `BR_SSLKEYX_ECDH_RSA` | ECDH_RSA | Key is EC (key exchange), cert signed with RSA | + * | 4 | `BR_SSLKEYX_ECDH_ECDSA` | ECDH_ECDSA | Key is EC (key exchange), cert signed with ECDSA | + * + * - Bits 8 to 11: symmetric encryption algorithm + * + * | val | symbolic constant | symmetric encryption | key strength (bits) | + * | :-- | :--------------------- | :------------------- | :------------------ | + * | 0 | `BR_SSLENC_3DES_CBC` | 3DES/CBC | 168 | + * | 1 | `BR_SSLENC_AES128_CBC` | AES-128/CBC | 128 | + * | 2 | `BR_SSLENC_AES256_CBC` | AES-256/CBC | 256 | + * | 3 | `BR_SSLENC_AES128_GCM` | AES-128/GCM | 128 | + * | 4 | `BR_SSLENC_AES256_GCM` | AES-256/GCM | 256 | + * | 5 | `BR_SSLENC_CHACHA20` | ChaCha20/Poly1305 | 256 | + * + * - Bits 4 to 7: MAC algorithm + * + * | val | symbolic constant | MAC type | details | + * | :-- | :----------------- | :----------- | :------------------------------------ | + * | 0 | `BR_SSLMAC_AEAD` | AEAD | No dedicated MAC (encryption is AEAD) | + * | 2 | `BR_SSLMAC_SHA1` | HMAC/SHA-1 | Value matches `br_sha1_ID` | + * | 4 | `BR_SSLMAC_SHA256` | HMAC/SHA-256 | Value matches `br_sha256_ID` | + * | 5 | `BR_SSLMAC_SHA384` | HMAC/SHA-384 | Value matches `br_sha384_ID` | + * + * - Bits 0 to 3: hash function for PRF when used with TLS-1.2 + * + * | val | symbolic constant | hash function | details | + * | :-- | :----------------- | :------------ | :----------------------------------- | + * | 4 | `BR_SSLPRF_SHA256` | SHA-256 | Value matches `br_sha256_ID` | + * | 5 | `BR_SSLPRF_SHA384` | SHA-384 | Value matches `br_sha384_ID` | + * + * For instance, cipher suite `TLS_RSA_WITH_AES_128_GCM_SHA256` has + * standard identifier 0x009C, and is translated to 0x0304, for, in + * that order: RSA key exchange (0), AES-128/GCM (3), AEAD integrity (0), + * SHA-256 in the TLS PRF (4). + */ +typedef uint16_t br_suite_translated[2]; + +#ifndef BR_DOXYGEN_IGNORE +/* + * Constants are already documented in the br_suite_translated type. + */ + +#define BR_SSLKEYX_RSA 0 +#define BR_SSLKEYX_ECDHE_RSA 1 +#define BR_SSLKEYX_ECDHE_ECDSA 2 +#define BR_SSLKEYX_ECDH_RSA 3 +#define BR_SSLKEYX_ECDH_ECDSA 4 + +#define BR_SSLENC_3DES_CBC 0 +#define BR_SSLENC_AES128_CBC 1 +#define BR_SSLENC_AES256_CBC 2 +#define BR_SSLENC_AES128_GCM 3 +#define BR_SSLENC_AES256_GCM 4 +#define BR_SSLENC_CHACHA20 5 + +#define BR_SSLMAC_AEAD 0 +#define BR_SSLMAC_SHA1 br_sha1_ID +#define BR_SSLMAC_SHA256 br_sha256_ID +#define BR_SSLMAC_SHA384 br_sha384_ID + +#define BR_SSLPRF_SHA256 br_sha256_ID +#define BR_SSLPRF_SHA384 br_sha384_ID + +#endif + +/* + * Pre-declaration for the SSL server context. + */ +typedef struct br_ssl_server_context_ br_ssl_server_context; + +/** + * \brief Type for the server policy choices, taken after analysis of + * the client message (ClientHello). + */ +typedef struct { + /** + * \brief Cipher suite to use with that client. + */ + uint16_t cipher_suite; + + /** + * \brief Hash function or algorithm for signing the ServerKeyExchange. + * + * This parameter is ignored for `TLS_RSA_*` and `TLS_ECDH_*` + * cipher suites; it is used only for `TLS_ECDHE_*` suites, in + * which the server _signs_ the ephemeral EC Diffie-Hellman + * parameters sent to the client. + * + * This identifier must be one of the following values: + * + * - `0xFF00 + id`, where `id` is a hash function identifier + * (0 for MD5+SHA-1, or 2 to 6 for one of the SHA functions); + * + * - a full 16-bit identifier, lower than `0xFF00`. + * + * If the first option is used, then the SSL engine will + * compute the hash of the data that is to be signed, with the + * designated hash function. The `do_sign()` method will be + * invoked with that hash value provided in the the `data` + * buffer. + * + * If the second option is used, then the SSL engine will NOT + * compute a hash on the data; instead, it will provide the + * to-be-signed data itself in `data`, i.e. the concatenation of + * the client random, server random, and encoded ECDH + * parameters. Furthermore, with TLS-1.2 and later, the 16-bit + * identifier will be used "as is" in the protocol, in the + * SignatureAndHashAlgorithm; for instance, `0x0401` stands for + * RSA PKCS#1 v1.5 signature (the `01`) with SHA-256 as hash + * function (the `04`). + * + * Take care that with TLS 1.0 and 1.1, the hash function is + * constrainted by the protocol: RSA signature must use + * MD5+SHA-1 (so use `0xFF00`), while ECDSA must use SHA-1 + * (`0xFF02`). Since TLS 1.0 and 1.1 don't include a + * SignatureAndHashAlgorithm field in their ServerKeyExchange + * messages, any value below `0xFF00` will be usable to send the + * raw ServerKeyExchange data to the `do_sign()` callback, but + * that callback must still follow the protocol requirements + * when generating the signature. + */ + unsigned algo_id; + + /** + * \brief Certificate chain to send to the client. + * + * This is an array of `br_x509_certificate` objects, each + * normally containing a DER-encoded certificate. The server + * code does not try to decode these elements. + */ + const br_x509_certificate *chain; + + /** + * \brief Certificate chain length (number of certificates). + */ + size_t chain_len; + +} br_ssl_server_choices; + +/** + * \brief Class type for a policy handler (server side). + * + * A policy handler selects the policy parameters for a connection + * (cipher suite and other algorithms, and certificate chain to send to + * the client); it also performs the server-side computations involving + * its permanent private key. + * + * The SSL server engine will invoke first `choose()`, once the + * ClientHello message has been received, then either `do_keyx()` + * `do_sign()`, depending on the cipher suite. + */ +typedef struct br_ssl_server_policy_class_ br_ssl_server_policy_class; +struct br_ssl_server_policy_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Select algorithms and certificates for this connection. + * + * This callback function shall fill the provided `choices` + * structure with the policy choices for this connection. This + * entails selecting the cipher suite, hash function for signing + * the ServerKeyExchange (applicable only to ECDHE cipher suites), + * and certificate chain to send. + * + * The callback receives a pointer to the server context that + * contains the relevant data. In particular, the functions + * `br_ssl_server_get_client_suites()`, + * `br_ssl_server_get_client_hashes()` and + * `br_ssl_server_get_client_curves()` can be used to obtain + * the cipher suites, hash functions and elliptic curves + * supported by both the client and server, respectively. The + * `br_ssl_engine_get_version()` and `br_ssl_engine_get_server_name()` + * functions yield the protocol version and requested server name + * (SNI), respectively. + * + * This function may modify its context structure (`pctx`) in + * arbitrary ways to keep track of its own choices. + * + * This function shall return 1 if appropriate policy choices + * could be made, or 0 if this connection cannot be pursued. + * + * \param pctx policy context. + * \param cc SSL server context. + * \param choices destination structure for the policy choices. + * \return 1 on success, 0 on error. + */ + int (*choose)(const br_ssl_server_policy_class **pctx, + const br_ssl_server_context *cc, + br_ssl_server_choices *choices); + + /** + * \brief Perform key exchange (server part). + * + * This callback is invoked to perform the server-side cryptographic + * operation for a key exchange that is not ECDHE. This callback + * uses the private key. + * + * **For RSA key exchange**, the provided `data` (of length `*len` + * bytes) shall be decrypted with the server's private key, and + * the 48-byte premaster secret copied back to the first 48 bytes + * of `data`. + * + * - The caller makes sure that `*len` is at least 59 bytes. + * + * - This callback MUST check that the provided length matches + * that of the key modulus; it shall report an error otherwise. + * + * - If the length matches that of the RSA key modulus, then + * processing MUST be constant-time, even if decryption fails, + * or the padding is incorrect, or the plaintext message length + * is not exactly 48 bytes. + * + * - This callback needs not check the two first bytes of the + * obtained pre-master secret (the caller will do that). + * + * - If an error is reported (0), then what the callback put + * in the first 48 bytes of `data` is unimportant (the caller + * will use random bytes instead). + * + * **For ECDH key exchange**, the provided `data` (of length `*len` + * bytes) is the elliptic curve point from the client. The + * callback shall multiply it with its private key, and store + * the resulting X coordinate in `data`, starting at offset 0, + * and set `*len` to the length of the X coordinate. + * + * - If the input array does not have the proper length for + * an encoded curve point, then an error (0) shall be reported. + * + * - If the input array has the proper length, then processing + * MUST be constant-time, even if the data is not a valid + * encoded point. + * + * - This callback MUST check that the input point is valid. + * + * Returned value is 1 on success, 0 on error. + * + * \param pctx policy context. + * \param data key exchange data from the client. + * \param len key exchange data length (in bytes). + * \return 1 on success, 0 on error. + */ + uint32_t (*do_keyx)(const br_ssl_server_policy_class **pctx, + unsigned char *data, size_t *len); + + /** + * \brief Perform a signature (for a ServerKeyExchange message). + * + * This callback function is invoked for ECDHE cipher suites. On + * input, the hash value or message to sign is in `data`, of + * size `hv_len`; the involved hash function or algorithm is + * identified by `algo_id`. The signature shall be computed and + * written back into `data`; the total size of that buffer is + * `len` bytes. + * + * This callback shall verify that the signature length does not + * exceed `len` bytes, and abstain from writing the signature if + * it does not fit. + * + * The `algo_id` value matches that which was written in the + * `choices` structures by the `choose()` callback. This will be + * one of the following: + * + * - `0xFF00 + id` for a hash function identifier `id`. In + * that case, the `data` buffer contains a hash value + * already computed over the data that is to be signed, + * of length `hv_len`. The `id` may be 0 to designate the + * special MD5+SHA-1 concatenation (old-style RSA signing). + * + * - Another value, lower than `0xFF00`. The `data` buffer + * then contains the raw, non-hashed data to be signed + * (concatenation of the client and server randoms and + * ECDH parameters). The callback is responsible to apply + * any relevant hashing as part of the signing process. + * + * Returned value is the signature length (in bytes), or 0 on error. + * + * \param pctx policy context. + * \param algo_id hash function / algorithm identifier. + * \param data input/output buffer (message/hash, then signature). + * \param hv_len hash value or message length (in bytes). + * \param len total buffer length (in bytes). + * \return signature length (in bytes) on success, or 0 on error. + */ + size_t (*do_sign)(const br_ssl_server_policy_class **pctx, + unsigned algo_id, + unsigned char *data, size_t hv_len, size_t len); +}; + +/** + * \brief A single-chain RSA policy handler. + * + * This policy context uses a single certificate chain, and a RSA + * private key. The context can be restricted to only signatures or + * only key exchange. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_server_policy_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_rsa_private_key *sk; + unsigned allowed_usages; + br_rsa_private irsacore; + br_rsa_pkcs1_sign irsasign; +#endif +} br_ssl_server_policy_rsa_context; + +/** + * \brief A single-chain EC policy handler. + * + * This policy context uses a single certificate chain, and an EC + * private key. The context can be restricted to only signatures or + * only key exchange. + * + * Due to how TLS is defined, this context must be made aware whether + * the server certificate was itself signed with RSA or ECDSA. The code + * does not try to decode the certificate to obtain that information. + * + * Apart from the first field (vtable pointer), its contents are + * opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_server_policy_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + const br_x509_certificate *chain; + size_t chain_len; + const br_ec_private_key *sk; + unsigned allowed_usages; + unsigned cert_issuer_key_type; + const br_multihash_context *mhash; + const br_ec_impl *iec; + br_ecdsa_sign iecdsa; +#endif +} br_ssl_server_policy_ec_context; + +/** + * \brief Class type for a session parameter cache. + * + * Session parameters are saved in the cache with `save()`, and + * retrieved with `load()`. The cache implementation can apply any + * storage and eviction strategy that it sees fit. The SSL server + * context that performs the request is provided, so that its + * functionalities may be used by the implementation (e.g. hash + * functions or random number generation). + */ +typedef struct br_ssl_session_cache_class_ br_ssl_session_cache_class; +struct br_ssl_session_cache_class_ { + /** + * \brief Context size (in bytes). + */ + size_t context_size; + + /** + * \brief Record a session. + * + * This callback should record the provided session parameters. + * The `params` structure is transient, so its contents shall + * be copied into the cache. The session ID has been randomly + * generated and always has length exactly 32 bytes. + * + * \param ctx session cache context. + * \param server_ctx SSL server context. + * \param params session parameters to save. + */ + void (*save)(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + const br_ssl_session_parameters *params); + + /** + * \brief Lookup a session in the cache. + * + * The session ID to lookup is in `params` and always has length + * exactly 32 bytes. If the session parameters are found in the + * cache, then the parameters shall be copied into the `params` + * structure. Returned value is 1 on successful lookup, 0 + * otherwise. + * + * \param ctx session cache context. + * \param server_ctx SSL server context. + * \param params destination for session parameters. + * \return 1 if found, 0 otherwise. + */ + int (*load)(const br_ssl_session_cache_class **ctx, + br_ssl_server_context *server_ctx, + br_ssl_session_parameters *params); +}; + +/** + * \brief Context for a basic cache system. + * + * The system stores session parameters in a buffer provided at + * initialisation time. Each entry uses exactly 100 bytes, and + * buffer sizes up to 4294967295 bytes are supported. + * + * Entries are evicted with a LRU (Least Recently Used) policy. A + * search tree is maintained to keep lookups fast even with large + * caches. + * + * Apart from the first field (vtable pointer), the structure + * contents are opaque and shall not be accessed directly. + */ +typedef struct { + /** \brief Pointer to vtable. */ + const br_ssl_session_cache_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + unsigned char *store; + size_t store_len, store_ptr; + unsigned char index_key[32]; + const br_hash_class *hash; + int init_done; + uint32_t head, tail, root; +#endif +} br_ssl_session_cache_lru; + +/** + * \brief Initialise a LRU session cache with the provided storage space. + * + * The provided storage space must remain valid as long as the cache + * is used. Arbitrary lengths are supported, up to 4294967295 bytes; + * each entry uses up exactly 100 bytes. + * + * \param cc session cache context. + * \param store storage space for cached entries. + * \param store_len storage space length (in bytes). + */ +void br_ssl_session_cache_lru_init(br_ssl_session_cache_lru *cc, + unsigned char *store, size_t store_len); + +/** + * \brief Forget an entry in an LRU session cache. + * + * The session cache context must have been initialised. The entry + * with the provided session ID (of exactly 32 bytes) is looked for + * in the cache; if located, it is disabled. + * + * \param cc session cache context. + * \param id session ID to forget. + */ +void br_ssl_session_cache_lru_forget( + br_ssl_session_cache_lru *cc, const unsigned char *id); + +/** + * \brief Context structure for a SSL server. + * + * The first field (called `eng`) is the SSL engine; all functions that + * work on a `br_ssl_engine_context` structure shall take as parameter + * a pointer to that field. The other structure fields are opaque and + * must not be accessed directly. + */ +struct br_ssl_server_context_ { + /** + * \brief The encapsulated engine context. + */ + br_ssl_engine_context eng; + +#ifndef BR_DOXYGEN_IGNORE + /* + * Maximum version from the client. + */ + uint16_t client_max_version; + + /* + * Session cache. + */ + const br_ssl_session_cache_class **cache_vtable; + + /* + * Translated cipher suites supported by the client. The list + * is trimmed to include only the cipher suites that the + * server also supports; they are in the same order as in the + * client message. + */ + br_suite_translated client_suites[BR_MAX_CIPHER_SUITES]; + unsigned char client_suites_num; + + /* + * Hash functions supported by the client, with ECDSA and RSA + * (bit mask). For hash function with id 'x', set bit index is + * x for RSA, x+8 for ECDSA. For newer algorithms, with ID + * 0x08**, bit 16+k is set for algorithm 0x0800+k. + */ + uint32_t hashes; + + /* + * Curves supported by the client (bit mask, for named curves). + */ + uint32_t curves; + + /* + * Context for chain handler. + */ + const br_ssl_server_policy_class **policy_vtable; + uint16_t sign_hash_id; + + /* + * For the core handlers, thus avoiding (in most cases) the + * need for an externally provided policy context. + */ + union { + const br_ssl_server_policy_class *vtable; + br_ssl_server_policy_rsa_context single_rsa; + br_ssl_server_policy_ec_context single_ec; + } chain_handler; + + /* + * Buffer for the ECDHE private key. + */ + unsigned char ecdhe_key[70]; + size_t ecdhe_key_len; + + /* + * Trust anchor names for client authentication. "ta_names" and + * "tas" cannot be both non-NULL. + */ + const br_x500_name *ta_names; + const br_x509_trust_anchor *tas; + size_t num_tas; + size_t cur_dn_index; + const unsigned char *cur_dn; + size_t cur_dn_len; + + /* + * Buffer for the hash value computed over all handshake messages + * prior to CertificateVerify, and identifier for the hash function. + */ + unsigned char hash_CV[64]; + size_t hash_CV_len; + int hash_CV_id; + + /* + * Server-specific implementations. + * (none for now) + */ +#endif +}; + +/* + * Each br_ssl_server_init_xxx() function sets the list of supported + * cipher suites and used implementations, as specified by the profile + * name 'xxx'. Defined profile names are: + * + * full_rsa all supported algorithm, server key type is RSA + * full_ec all supported algorithm, server key type is EC + * TODO: add other profiles + * + * Naming scheme for "minimal" profiles: min123 + * + * -- character 1: key exchange + * r = RSA + * e = ECDHE_RSA + * f = ECDHE_ECDSA + * u = ECDH_RSA + * v = ECDH_ECDSA + * -- character 2: version / PRF + * 0 = TLS 1.0 / 1.1 with MD5+SHA-1 + * 2 = TLS 1.2 with SHA-256 + * 3 = TLS 1.2 with SHA-384 + * -- character 3: encryption + * a = AES/CBC + * d = 3DES/CBC + * g = AES/GCM + * c = ChaCha20+Poly1305 + */ + +/** + * \brief SSL server profile: full_rsa. + * + * This function initialises the provided SSL server context with + * all supported algorithms and cipher suites that rely on a RSA + * key pair. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_full_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: full_ec. + * + * This function initialises the provided SSL server context with + * all supported algorithms and cipher suites that rely on an EC + * key pair. + * + * The key type of the CA that issued the server's certificate must + * be provided, since it matters for ECDH cipher suites (ECDH_RSA + * suites require a RSA-powered CA). The key type is either + * `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len chain length (number of certificates). + * \param cert_issuer_key_type certificate issuer's key type. + * \param sk EC private key. + */ +void br_ssl_server_init_full_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + unsigned cert_issuer_key_type, const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minr2g. + * + * This profile uses only TLS_RSA_WITH_AES_128_GCM_SHA256. Server key is + * RSA, and RSA key exchange is used (not forward secure, but uses little + * CPU in the client). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_minr2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: mine2g. + * + * This profile uses only TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256. Server key + * is RSA, and ECDHE key exchange is used. This suite provides forward + * security, with a higher CPU expense on the client, and a somewhat + * larger code footprint (compared to "minr2g"). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_mine2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: minf2g. + * + * This profile uses only TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDHE key exchange is used. This suite provides + * forward security, with a higher CPU expense on the client and server + * (by a factor of about 3 to 4), and a somewhat larger code footprint + * (compared to "minu2g" and "minv2g"). + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minf2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minu2g. + * + * This profile uses only TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDH key exchange is used; the issuing CA used + * a RSA key. + * + * The "minu2g" and "minv2g" profiles do not provide forward secrecy, + * but are the lightest on the server (for CPU usage), and are rather + * inexpensive on the client as well. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minu2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: minv2g. + * + * This profile uses only TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256. + * Server key is EC, and ECDH key exchange is used; the issuing CA used + * an EC key. + * + * The "minu2g" and "minv2g" profiles do not provide forward secrecy, + * but are the lightest on the server (for CPU usage), and are rather + * inexpensive on the client as well. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minv2g(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief SSL server profile: mine2c. + * + * This profile uses only TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256. + * Server key is RSA, and ECDHE key exchange is used. This suite + * provides forward security. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk RSA private key. + */ +void br_ssl_server_init_mine2c(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk); + +/** + * \brief SSL server profile: minf2c. + * + * This profile uses only TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256. + * Server key is EC, and ECDHE key exchange is used. This suite provides + * forward security. + * + * \param cc server context to initialise. + * \param chain server certificate chain. + * \param chain_len certificate chain length (number of certificate). + * \param sk EC private key. + */ +void br_ssl_server_init_minf2c(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk); + +/** + * \brief Get the supported client suites. + * + * This function shall be called only after the ClientHello has been + * processed, typically from the policy engine. The returned array + * contains the cipher suites that are supported by both the client + * and the server; these suites are in client preference order, unless + * the `BR_OPT_ENFORCE_SERVER_PREFERENCES` flag was set, in which case + * they are in server preference order. + * + * The suites are _translated_, which means that each suite is given + * as two 16-bit integers: the standard suite identifier, and its + * translated version, broken down into its individual components, + * as explained with the `br_suite_translated` type. + * + * The returned array is allocated in the context and will be rewritten + * by each handshake. + * + * \param cc server context. + * \param num receives the array size (number of suites). + * \return the translated common cipher suites, in preference order. + */ +static inline const br_suite_translated * +br_ssl_server_get_client_suites(const br_ssl_server_context *cc, size_t *num) +{ + *num = cc->client_suites_num; + return cc->client_suites; +} + +/** + * \brief Get the hash functions and signature algorithms supported by + * the client. + * + * This value is a bit field: + * + * - If RSA (PKCS#1 v1.5) is supported with hash function of ID `x`, + * then bit `x` is set (hash function ID is 0 for the special MD5+SHA-1, + * or 2 to 6 for the SHA family). + * + * - If ECDSA is supported with hash function of ID `x`, then bit `8+x` + * is set. + * + * - Newer algorithms are symbolic 16-bit identifiers that do not + * represent signature algorithm and hash function separately. If + * the TLS-level identifier is `0x0800+x` for a `x` in the 0..15 + * range, then bit `16+x` is set. + * + * "New algorithms" are currently defined only in draft documents, so + * this support is subject to possible change. Right now (early 2017), + * this maps ed25519 (EdDSA on Curve25519) to bit 23, and ed448 (EdDSA + * on Curve448) to bit 24. If the identifiers on the wire change in + * future document, then the decoding mechanism in BearSSL will be + * amended to keep mapping ed25519 and ed448 on bits 23 and 24, + * respectively. Mapping of other new algorithms (e.g. RSA/PSS) is not + * guaranteed yet. + * + * \param cc server context. + * \return the client-supported hash functions and signature algorithms. + */ +static inline uint32_t +br_ssl_server_get_client_hashes(const br_ssl_server_context *cc) +{ + return cc->hashes; +} + +/** + * \brief Get the elliptic curves supported by the client. + * + * This is a bit field (bit x is set if curve of ID x is supported). + * + * \param cc server context. + * \return the client-supported elliptic curves. + */ +static inline uint32_t +br_ssl_server_get_client_curves(const br_ssl_server_context *cc) +{ + return cc->curves; +} + +/** + * \brief Clear the complete contents of a SSL server context. + * + * Everything is cleared, including the reference to the configured buffer, + * implementations, cipher suites and state. This is a preparatory step + * to assembling a custom profile. + * + * \param cc server context to clear. + */ +void br_ssl_server_zero(br_ssl_server_context *cc); + +/** + * \brief Set an externally provided policy context. + * + * The policy context's methods are invoked to decide the cipher suite + * and certificate chain, and to perform operations involving the server's + * private key. + * + * \param cc server context. + * \param pctx policy context (pointer to its vtable field). + */ +static inline void +br_ssl_server_set_policy(br_ssl_server_context *cc, + const br_ssl_server_policy_class **pctx) +{ + cc->policy_vtable = pctx; +} + +/** + * \brief Set the server certificate chain and key (single RSA case). + * + * This function uses a policy context included in the server context. + * It configures use of a single server certificate chain with a RSA + * private key. The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`; this enables or disables + * the corresponding cipher suites (i.e. `TLS_RSA_*` use the RSA key for + * key exchange, while `TLS_ECDHE_RSA_*` use the RSA key for signatures). + * + * \param cc server context. + * \param chain server certificate chain to send to the client. + * \param chain_len chain length (number of certificates). + * \param sk server private key (RSA). + * \param allowed_usages allowed private key usages. + * \param irsacore RSA core implementation. + * \param irsasign RSA signature implementation (PKCS#1 v1.5). + */ +void br_ssl_server_set_single_rsa(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_rsa_private_key *sk, unsigned allowed_usages, + br_rsa_private irsacore, br_rsa_pkcs1_sign irsasign); + +/** + * \brief Set the server certificate chain and key (single EC case). + * + * This function uses a policy context included in the server context. + * It configures use of a single server certificate chain with an EC + * private key. The `allowed_usages` is a combination of usages, namely + * `BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`; this enables or disables + * the corresponding cipher suites (i.e. `TLS_ECDH_*` use the EC key for + * key exchange, while `TLS_ECDHE_ECDSA_*` use the EC key for signatures). + * + * In order to support `TLS_ECDH_*` cipher suites (non-ephemeral ECDH), + * the algorithm type of the key used by the issuing CA to sign the + * server's certificate must be provided, as `cert_issuer_key_type` + * parameter (this value is either `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`). + * + * \param cc server context. + * \param chain server certificate chain to send. + * \param chain_len chain length (number of certificates). + * \param sk server private key (EC). + * \param allowed_usages allowed private key usages. + * \param cert_issuer_key_type issuing CA's key type. + * \param iec EC core implementation. + * \param iecdsa ECDSA signature implementation ("asn1" format). + */ +void br_ssl_server_set_single_ec(br_ssl_server_context *cc, + const br_x509_certificate *chain, size_t chain_len, + const br_ec_private_key *sk, unsigned allowed_usages, + unsigned cert_issuer_key_type, + const br_ec_impl *iec, br_ecdsa_sign iecdsa); + +/** + * \brief Activate client certificate authentication. + * + * The trust anchor encoded X.500 names (DN) to send to the client are + * provided. A client certificate will be requested and validated through + * the X.509 validator configured in the SSL engine. If `num` is 0, then + * client certificate authentication is disabled. + * + * If the client does not send a certificate, or on validation failure, + * the handshake aborts. Unauthenticated clients can be tolerated by + * setting the `BR_OPT_TOLERATE_NO_CLIENT_AUTH` flag. + * + * The provided array is linked in, not copied, so that pointer must + * remain valid as long as anchor names may be used. + * + * \param cc server context. + * \param ta_names encoded trust anchor names. + * \param num number of encoded trust anchor names. + */ +static inline void +br_ssl_server_set_trust_anchor_names(br_ssl_server_context *cc, + const br_x500_name *ta_names, size_t num) +{ + cc->ta_names = ta_names; + cc->tas = NULL; + cc->num_tas = num; +} + +/** + * \brief Activate client certificate authentication. + * + * This is a variant for `br_ssl_server_set_trust_anchor_names()`: the + * trust anchor names are provided not as an array of stand-alone names + * (`br_x500_name` structures), but as an array of trust anchors + * (`br_x509_trust_anchor` structures). The server engine itself will + * only use the `dn` field of each trust anchor. This is meant to allow + * defining a single array of trust anchors, to be used here and in the + * X.509 validation engine itself. + * + * The provided array is linked in, not copied, so that pointer must + * remain valid as long as anchor names may be used. + * + * \param cc server context. + * \param tas trust anchors (only names are used). + * \param num number of trust anchors. + */ +static inline void +br_ssl_server_set_trust_anchor_names_alt(br_ssl_server_context *cc, + const br_x509_trust_anchor *tas, size_t num) +{ + cc->ta_names = NULL; + cc->tas = tas; + cc->num_tas = num; +} + +/** + * \brief Configure the cache for session parameters. + * + * The cache context is provided as a pointer to its first field (vtable + * pointer). + * + * \param cc server context. + * \param vtable session cache context. + */ +static inline void +br_ssl_server_set_cache(br_ssl_server_context *cc, + const br_ssl_session_cache_class **vtable) +{ + cc->cache_vtable = vtable; +} + +/** + * \brief Prepare or reset a server context for handling an incoming client. + * + * \param cc server context. + * \return 1 on success, 0 on error. + */ +int br_ssl_server_reset(br_ssl_server_context *cc); + +/* ===================================================================== */ + +/* + * Context for the simplified I/O context. The transport medium is accessed + * through the low_read() and low_write() callback functions, each with + * its own opaque context pointer. + * + * low_read() read some bytes, at most 'len' bytes, into data[]. The + * returned value is the number of read bytes, or -1 on error. + * The 'len' parameter is guaranteed never to exceed 20000, + * so the length always fits in an 'int' on all platforms. + * + * low_write() write up to 'len' bytes, to be read from data[]. The + * returned value is the number of written bytes, or -1 on + * error. The 'len' parameter is guaranteed never to exceed + * 20000, so the length always fits in an 'int' on all + * parameters. + * + * A socket closure (if the transport medium is a socket) should be reported + * as an error (-1). The callbacks shall endeavour to block until at least + * one byte can be read or written; a callback returning 0 at times is + * acceptable, but this normally leads to the callback being immediately + * called again, so the callback should at least always try to block for + * some time if no I/O can take place. + * + * The SSL engine naturally applies some buffering, so the callbacks need + * not apply buffers of their own. + */ +/** + * \brief Context structure for the simplified SSL I/O wrapper. + * + * This structure is initialised with `br_sslio_init()`. Its contents + * are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + br_ssl_engine_context *engine; + int (*low_read)(void *read_context, + unsigned char *data, size_t len); + void *read_context; + int (*low_write)(void *write_context, + const unsigned char *data, size_t len); + void *write_context; +#endif +} br_sslio_context; + +/** + * \brief Initialise a simplified I/O wrapper context. + * + * The simplified I/O wrapper offers a simpler read/write API for a SSL + * engine (client or server), using the provided callback functions for + * reading data from, or writing data to, the transport medium. + * + * The callback functions have the following semantics: + * + * - Each callback receives an opaque context value (of type `void *`) + * that the callback may use arbitrarily (or possibly ignore). + * + * - `low_read()` reads at least one byte, at most `len` bytes, from + * the transport medium. Read bytes shall be written in `data`. + * + * - `low_write()` writes at least one byte, at most `len` bytes, unto + * the transport medium. The bytes to write are read from `data`. + * + * - The `len` parameter is never zero, and is always lower than 20000. + * + * - The number of processed bytes (read or written) is returned. Since + * that number is less than 20000, it always fits on an `int`. + * + * - On error, the callbacks return -1. Reaching end-of-stream is an + * error. Errors are permanent: the SSL connection is terminated. + * + * - Callbacks SHOULD NOT return 0. This is tolerated, as long as + * callbacks endeavour to block for some non-negligible amount of + * time until at least one byte can be sent or received (if a + * callback returns 0, then the wrapper invokes it again + * immediately). + * + * - Callbacks MAY return as soon as at least one byte is processed; + * they MAY also insist on reading or writing _all_ requested bytes. + * Since SSL is a self-terminated protocol (each record has a length + * header), this does not change semantics. + * + * - Callbacks need not apply any buffering (for performance) since SSL + * itself uses buffers. + * + * \param ctx wrapper context to initialise. + * \param engine SSL engine to wrap. + * \param low_read callback for reading data from the transport. + * \param read_context context pointer for `low_read()`. + * \param low_write callback for writing data on the transport. + * \param write_context context pointer for `low_write()`. + */ +void br_sslio_init(br_sslio_context *ctx, + br_ssl_engine_context *engine, + int (*low_read)(void *read_context, + unsigned char *data, size_t len), + void *read_context, + int (*low_write)(void *write_context, + const unsigned char *data, size_t len), + void *write_context); + +/** + * \brief Read some application data from a SSL connection. + * + * If `len` is zero, then this function returns 0 immediately. In + * all other cases, it never returns 0. + * + * This call returns only when at least one byte has been obtained. + * Returned value is the number of bytes read, or -1 on error. The + * number of bytes always fits on an 'int' (data from a single SSL/TLS + * record is returned). + * + * On error or SSL closure, this function returns -1. The caller should + * inspect the error status on the SSL engine to distinguish between + * normal closure and error. + * + * \param cc SSL wrapper context. + * \param dst destination buffer for application data. + * \param len maximum number of bytes to obtain. + * \return number of bytes obtained, or -1 on error. + */ +int br_sslio_read(br_sslio_context *cc, void *dst, size_t len); + +/** + * \brief Read application data from a SSL connection. + * + * This calls returns only when _all_ requested `len` bytes are read, + * or an error is reached. Returned value is 0 on success, -1 on error. + * A normal (verified) SSL closure before that many bytes are obtained + * is reported as an error by this function. + * + * \param cc SSL wrapper context. + * \param dst destination buffer for application data. + * \param len number of bytes to obtain. + * \return 0 on success, or -1 on error. + */ +int br_sslio_read_all(br_sslio_context *cc, void *dst, size_t len); + +/** + * \brief Write some application data unto a SSL connection. + * + * If `len` is zero, then this function returns 0 immediately. In + * all other cases, it never returns 0. + * + * This call returns only when at least one byte has been written. + * Returned value is the number of bytes written, or -1 on error. The + * number of bytes always fits on an 'int' (less than 20000). + * + * On error or SSL closure, this function returns -1. The caller should + * inspect the error status on the SSL engine to distinguish between + * normal closure and error. + * + * **Important:** SSL is buffered; a "written" byte is a byte that was + * injected into the wrapped SSL engine, but this does not necessarily mean + * that it has been scheduled for sending. Use `br_sslio_flush()` to + * ensure that all pending data has been sent to the transport medium. + * + * \param cc SSL wrapper context. + * \param src source buffer for application data. + * \param len maximum number of bytes to write. + * \return number of bytes written, or -1 on error. + */ +int br_sslio_write(br_sslio_context *cc, const void *src, size_t len); + +/** + * \brief Write application data unto a SSL connection. + * + * This calls returns only when _all_ requested `len` bytes have been + * written, or an error is reached. Returned value is 0 on success, -1 + * on error. A normal (verified) SSL closure before that many bytes are + * written is reported as an error by this function. + * + * **Important:** SSL is buffered; a "written" byte is a byte that was + * injected into the wrapped SSL engine, but this does not necessarily mean + * that it has been scheduled for sending. Use `br_sslio_flush()` to + * ensure that all pending data has been sent to the transport medium. + * + * \param cc SSL wrapper context. + * \param src source buffer for application data. + * \param len number of bytes to write. + * \return 0 on success, or -1 on error. + */ +int br_sslio_write_all(br_sslio_context *cc, const void *src, size_t len); + +/** + * \brief Flush pending data. + * + * This call makes sure that any buffered application data in the + * provided context (including the wrapped SSL engine) has been sent + * to the transport medium (i.e. accepted by the `low_write()` callback + * method). If there is no such pending data, then this function does + * nothing (and returns a success, i.e. 0). + * + * If the underlying transport medium has its own buffers, then it is + * up to the caller to ensure the corresponding flushing. + * + * Returned value is 0 on success, -1 on error. + * + * \param cc SSL wrapper context. + * \return 0 on success, or -1 on error. + */ +int br_sslio_flush(br_sslio_context *cc); + +/** + * \brief Close the SSL connection. + * + * This call runs the SSL closure protocol (sending a `close_notify`, + * receiving the response `close_notify`). When it returns, the SSL + * connection is finished. It is still up to the caller to manage the + * possible transport-level termination, if applicable (alternatively, + * the underlying transport stream may be reused for non-SSL messages). + * + * Returned value is 0 on success, -1 on error. A failure by the peer + * to process the complete closure protocol (i.e. sending back the + * `close_notify`) is an error. + * + * \param cc SSL wrapper context. + * \return 0 on success, or -1 on error. + */ +int br_sslio_close(br_sslio_context *cc); + +/* ===================================================================== */ + +/* + * Symbolic constants for cipher suites. + */ + +/* From RFC 5246 */ +#define BR_TLS_NULL_WITH_NULL_NULL 0x0000 +#define BR_TLS_RSA_WITH_NULL_MD5 0x0001 +#define BR_TLS_RSA_WITH_NULL_SHA 0x0002 +#define BR_TLS_RSA_WITH_NULL_SHA256 0x003B +#define BR_TLS_RSA_WITH_RC4_128_MD5 0x0004 +#define BR_TLS_RSA_WITH_RC4_128_SHA 0x0005 +#define BR_TLS_RSA_WITH_3DES_EDE_CBC_SHA 0x000A +#define BR_TLS_RSA_WITH_AES_128_CBC_SHA 0x002F +#define BR_TLS_RSA_WITH_AES_256_CBC_SHA 0x0035 +#define BR_TLS_RSA_WITH_AES_128_CBC_SHA256 0x003C +#define BR_TLS_RSA_WITH_AES_256_CBC_SHA256 0x003D +#define BR_TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA 0x000D +#define BR_TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA 0x0010 +#define BR_TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA 0x0013 +#define BR_TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA 0x0016 +#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA 0x0030 +#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA 0x0031 +#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA 0x0032 +#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA 0x0033 +#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA 0x0036 +#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA 0x0037 +#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA 0x0038 +#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA 0x0039 +#define BR_TLS_DH_DSS_WITH_AES_128_CBC_SHA256 0x003E +#define BR_TLS_DH_RSA_WITH_AES_128_CBC_SHA256 0x003F +#define BR_TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 0x0040 +#define BR_TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 0x0067 +#define BR_TLS_DH_DSS_WITH_AES_256_CBC_SHA256 0x0068 +#define BR_TLS_DH_RSA_WITH_AES_256_CBC_SHA256 0x0069 +#define BR_TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 0x006A +#define BR_TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 0x006B +#define BR_TLS_DH_anon_WITH_RC4_128_MD5 0x0018 +#define BR_TLS_DH_anon_WITH_3DES_EDE_CBC_SHA 0x001B +#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA 0x0034 +#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA 0x003A +#define BR_TLS_DH_anon_WITH_AES_128_CBC_SHA256 0x006C +#define BR_TLS_DH_anon_WITH_AES_256_CBC_SHA256 0x006D + +/* From RFC 4492 */ +#define BR_TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001 +#define BR_TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002 +#define BR_TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003 +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004 +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005 +#define BR_TLS_ECDHE_ECDSA_WITH_NULL_SHA 0xC006 +#define BR_TLS_ECDHE_ECDSA_WITH_RC4_128_SHA 0xC007 +#define BR_TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC008 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA 0xC009 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA 0xC00A +#define BR_TLS_ECDH_RSA_WITH_NULL_SHA 0xC00B +#define BR_TLS_ECDH_RSA_WITH_RC4_128_SHA 0xC00C +#define BR_TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA 0xC00D +#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA 0xC00E +#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA 0xC00F +#define BR_TLS_ECDHE_RSA_WITH_NULL_SHA 0xC010 +#define BR_TLS_ECDHE_RSA_WITH_RC4_128_SHA 0xC011 +#define BR_TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA 0xC012 +#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA 0xC013 +#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA 0xC014 +#define BR_TLS_ECDH_anon_WITH_NULL_SHA 0xC015 +#define BR_TLS_ECDH_anon_WITH_RC4_128_SHA 0xC016 +#define BR_TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA 0xC017 +#define BR_TLS_ECDH_anon_WITH_AES_128_CBC_SHA 0xC018 +#define BR_TLS_ECDH_anon_WITH_AES_256_CBC_SHA 0xC019 + +/* From RFC 5288 */ +#define BR_TLS_RSA_WITH_AES_128_GCM_SHA256 0x009C +#define BR_TLS_RSA_WITH_AES_256_GCM_SHA384 0x009D +#define BR_TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 0x009E +#define BR_TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 0x009F +#define BR_TLS_DH_RSA_WITH_AES_128_GCM_SHA256 0x00A0 +#define BR_TLS_DH_RSA_WITH_AES_256_GCM_SHA384 0x00A1 +#define BR_TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2 +#define BR_TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 0x00A3 +#define BR_TLS_DH_DSS_WITH_AES_128_GCM_SHA256 0x00A4 +#define BR_TLS_DH_DSS_WITH_AES_256_GCM_SHA384 0x00A5 +#define BR_TLS_DH_anon_WITH_AES_128_GCM_SHA256 0x00A6 +#define BR_TLS_DH_anon_WITH_AES_256_GCM_SHA384 0x00A7 + +/* From RFC 5289 */ +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 0xC023 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 0xC024 +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 0xC025 +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 0xC026 +#define BR_TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0xC027 +#define BR_TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 0xC028 +#define BR_TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 0xC029 +#define BR_TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 0xC02A +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 0xC02B +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 0xC02C +#define BR_TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 0xC02D +#define BR_TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 0xC02E +#define BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 0xC02F +#define BR_TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 0xC030 +#define BR_TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 0xC031 +#define BR_TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 0xC032 + +/* From RFC 6655 and 7251 */ +#define BR_TLS_RSA_WITH_AES_128_CCM 0xC09C +#define BR_TLS_RSA_WITH_AES_256_CCM 0xC09D +#define BR_TLS_RSA_WITH_AES_128_CCM_8 0xC0A0 +#define BR_TLS_RSA_WITH_AES_256_CCM_8 0xC0A1 +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM 0xC0AC +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM 0xC0AD +#define BR_TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 0xC0AE +#define BR_TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 0xC0AF + +/* From RFC 7905 */ +#define BR_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA8 +#define BR_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0xCCA9 +#define BR_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0xCCAA +#define BR_TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAB +#define BR_TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAC +#define BR_TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAD +#define BR_TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 0xCCAE + +/* From RFC 7507 */ +#define BR_TLS_FALLBACK_SCSV 0x5600 + +/* + * Symbolic constants for alerts. + */ +#define BR_ALERT_CLOSE_NOTIFY 0 +#define BR_ALERT_UNEXPECTED_MESSAGE 10 +#define BR_ALERT_BAD_RECORD_MAC 20 +#define BR_ALERT_RECORD_OVERFLOW 22 +#define BR_ALERT_DECOMPRESSION_FAILURE 30 +#define BR_ALERT_HANDSHAKE_FAILURE 40 +#define BR_ALERT_BAD_CERTIFICATE 42 +#define BR_ALERT_UNSUPPORTED_CERTIFICATE 43 +#define BR_ALERT_CERTIFICATE_REVOKED 44 +#define BR_ALERT_CERTIFICATE_EXPIRED 45 +#define BR_ALERT_CERTIFICATE_UNKNOWN 46 +#define BR_ALERT_ILLEGAL_PARAMETER 47 +#define BR_ALERT_UNKNOWN_CA 48 +#define BR_ALERT_ACCESS_DENIED 49 +#define BR_ALERT_DECODE_ERROR 50 +#define BR_ALERT_DECRYPT_ERROR 51 +#define BR_ALERT_PROTOCOL_VERSION 70 +#define BR_ALERT_INSUFFICIENT_SECURITY 71 +#define BR_ALERT_INTERNAL_ERROR 80 +#define BR_ALERT_USER_CANCELED 90 +#define BR_ALERT_NO_RENEGOTIATION 100 +#define BR_ALERT_UNSUPPORTED_EXTENSION 110 +#define BR_ALERT_NO_APPLICATION_PROTOCOL 120 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h new file mode 100644 index 0000000000..312e3d94f5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_tasmota_config.h @@ -0,0 +1,40 @@ +// do not delete + +#ifndef BEARSSL_TASMOTA_CONFIG +#define BEARSSL_TASMOTA_CONFIG + +#ifndef __ets__ +#define __ets__ +#endif + +#ifndef ESP32 +#ifndef ICACHE_FLASH +#define ICACHE_FLASH +#endif +#else +#ifndef PROGMEM +#define PROGMEM +#endif +#endif + +// #ifndef ESP8266 +// #define ESP8266 +// #endif + +#ifndef BR_SLOW_MUL15 +#define BR_SLOW_MUL15 1 // shrinks EC code by 8.5k +#endif + +#ifndef BR_MAX_RSA_SIZE +#ifdef USE_4K_RSA +#define BR_MAX_RSA_SIZE 4096 // max 4096 bits RSA keys +#else +#define BR_MAX_RSA_SIZE 2048 // max 2048 bits RSA keys +#endif +#endif + +#ifndef BR_MAX_EC_SIZE +#define BR_MAX_EC_SIZE 256 // max 256 bits EC keys +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h new file mode 100644 index 0000000000..d5a6b65544 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_bearssl_x509.h @@ -0,0 +1,1595 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef BR_BEARSSL_X509_H__ +#define BR_BEARSSL_X509_H__ + +#include +#include + +#include "t_bearssl_ec.h" +#include "t_bearssl_hash.h" +#include "t_bearssl_rsa.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** \file bearssl_x509.h + * + * # X.509 Certificate Chain Processing + * + * An X.509 processing engine receives an X.509 chain, chunk by chunk, + * as received from a SSL/TLS client or server (the client receives the + * server's certificate chain, and the server receives the client's + * certificate chain if it requested a client certificate). The chain + * is thus injected in the engine in SSL order (end-entity first). + * + * The engine's job is to return the public key to use for SSL/TLS. + * How exactly that key is obtained and verified is entirely up to the + * engine. + * + * **The "known key" engine** returns a public key which is already known + * from out-of-band information (e.g. the client _remembers_ the key from + * a previous connection, as in the usual SSH model). This is the simplest + * engine since it simply ignores the chain, thereby avoiding the need + * for any decoding logic. + * + * **The "minimal" engine** implements minimal X.509 decoding and chain + * validation: + * + * - The provided chain should validate "as is". There is no attempt + * at reordering, skipping or downloading extra certificates. + * + * - X.509 v1, v2 and v3 certificates are supported. + * + * - Trust anchors are a DN and a public key. Each anchor is either a + * "CA" anchor, or a non-CA. + * + * - If the end-entity certificate matches a non-CA anchor (subject DN + * is equal to the non-CA name, and public key is also identical to + * the anchor key), then this is a _direct trust_ case and the + * remaining certificates are ignored. + * + * - Unless direct trust is applied, the chain must be verifiable up to + * a certificate whose issuer DN matches the DN from a "CA" trust anchor, + * and whose signature is verifiable against that anchor's public key. + * Subsequent certificates in the chain are ignored. + * + * - The engine verifies subject/issuer DN matching, and enforces + * processing of Basic Constraints and Key Usage extensions. The + * Authority Key Identifier, Subject Key Identifier, Issuer Alt Name, + * Subject Directory Attribute, CRL Distribution Points, Freshest CRL, + * Authority Info Access and Subject Info Access extensions are + * ignored. The Subject Alt Name is decoded for the end-entity + * certificate under some conditions (see below). Other extensions + * are ignored if non-critical, or imply chain rejection if critical. + * + * - The Subject Alt Name extension is parsed for names of type `dNSName` + * when decoding the end-entity certificate, and only if there is a + * server name to match. If there is no SAN extension, then the + * Common Name from the subjectDN is used. That name matching is + * case-insensitive and honours a single starting wildcard (i.e. if + * the name in the certificate starts with "`*.`" then this matches + * any word as first element). Note: this name matching is performed + * also in the "direct trust" model. + * + * - DN matching is byte-to-byte equality (a future version might + * include some limited processing for case-insensitive matching and + * whitespace normalisation). + * + * - Successful validation produces a public key type but also a set + * of allowed usages (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * The caller is responsible for checking that the key type and + * usages are compatible with the expected values (e.g. with the + * selected cipher suite, when the client validates the server's + * certificate). + * + * **Important caveats:** + * + * - The "minimal" engine does not check revocation status. The relevant + * extensions are ignored, and CRL or OCSP responses are not gathered + * or checked. + * + * - The "minimal" engine does not currently support Name Constraints + * (some basic functionality to handle sub-domains may be added in a + * later version). + * + * - The decoder is not "validating" in the sense that it won't reject + * some certificates with invalid field values when these fields are + * not actually processed. + */ + +/* + * X.509 error codes are in the 32..63 range. + */ + +/** \brief X.509 status: validation was successful; this is not actually + an error. */ +#define BR_ERR_X509_OK 32 + +/** \brief X.509 status: invalid value in an ASN.1 structure. */ +#define BR_ERR_X509_INVALID_VALUE 33 + +/** \brief X.509 status: truncated certificate. */ +#define BR_ERR_X509_TRUNCATED 34 + +/** \brief X.509 status: empty certificate chain (no certificate at all). */ +#define BR_ERR_X509_EMPTY_CHAIN 35 + +/** \brief X.509 status: decoding error: inner element extends beyond + outer element size. */ +#define BR_ERR_X509_INNER_TRUNC 36 + +/** \brief X.509 status: decoding error: unsupported tag class (application + or private). */ +#define BR_ERR_X509_BAD_TAG_CLASS 37 + +/** \brief X.509 status: decoding error: unsupported tag value. */ +#define BR_ERR_X509_BAD_TAG_VALUE 38 + +/** \brief X.509 status: decoding error: indefinite length. */ +#define BR_ERR_X509_INDEFINITE_LENGTH 39 + +/** \brief X.509 status: decoding error: extraneous element. */ +#define BR_ERR_X509_EXTRA_ELEMENT 40 + +/** \brief X.509 status: decoding error: unexpected element. */ +#define BR_ERR_X509_UNEXPECTED 41 + +/** \brief X.509 status: decoding error: expected constructed element, but + is primitive. */ +#define BR_ERR_X509_NOT_CONSTRUCTED 42 + +/** \brief X.509 status: decoding error: expected primitive element, but + is constructed. */ +#define BR_ERR_X509_NOT_PRIMITIVE 43 + +/** \brief X.509 status: decoding error: BIT STRING length is not multiple + of 8. */ +#define BR_ERR_X509_PARTIAL_BYTE 44 + +/** \brief X.509 status: decoding error: BOOLEAN value has invalid length. */ +#define BR_ERR_X509_BAD_BOOLEAN 45 + +/** \brief X.509 status: decoding error: value is off-limits. */ +#define BR_ERR_X509_OVERFLOW 46 + +/** \brief X.509 status: invalid distinguished name. */ +#define BR_ERR_X509_BAD_DN 47 + +/** \brief X.509 status: invalid date/time representation. */ +#define BR_ERR_X509_BAD_TIME 48 + +/** \brief X.509 status: certificate contains unsupported features that + cannot be ignored. */ +#define BR_ERR_X509_UNSUPPORTED 49 + +/** \brief X.509 status: key or signature size exceeds internal limits. */ +#define BR_ERR_X509_LIMIT_EXCEEDED 50 + +/** \brief X.509 status: key type does not match that which was expected. */ +#define BR_ERR_X509_WRONG_KEY_TYPE 51 + +/** \brief X.509 status: signature is invalid. */ +#define BR_ERR_X509_BAD_SIGNATURE 52 + +/** \brief X.509 status: validation time is unknown. */ +#define BR_ERR_X509_TIME_UNKNOWN 53 + +/** \brief X.509 status: certificate is expired or not yet valid. */ +#define BR_ERR_X509_EXPIRED 54 + +/** \brief X.509 status: issuer/subject DN mismatch in the chain. */ +#define BR_ERR_X509_DN_MISMATCH 55 + +/** \brief X.509 status: expected server name was not found in the chain. */ +#define BR_ERR_X509_BAD_SERVER_NAME 56 + +/** \brief X.509 status: unknown critical extension in certificate. */ +#define BR_ERR_X509_CRITICAL_EXTENSION 57 + +/** \brief X.509 status: not a CA, or path length constraint violation */ +#define BR_ERR_X509_NOT_CA 58 + +/** \brief X.509 status: Key Usage extension prohibits intended usage. */ +#define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 + +/** \brief X.509 status: public key found in certificate is too small. */ +#define BR_ERR_X509_WEAK_PUBLIC_KEY 60 + +/** \brief X.509 status: chain could not be linked to a trust anchor. */ +#define BR_ERR_X509_NOT_TRUSTED 62 + +/** + * \brief Aggregate structure for public keys. + */ +typedef struct { + /** \brief Key type: `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC` */ + unsigned char key_type; + /** \brief Actual public key. */ + union { + /** \brief RSA public key. */ + br_rsa_public_key rsa; + /** \brief EC public key. */ + br_ec_public_key ec; + } key; +} br_x509_pkey; + +/** + * \brief Distinguished Name (X.500) structure. + * + * The DN is DER-encoded. + */ +typedef struct { + /** \brief Encoded DN data. */ + unsigned char *data; + /** \brief Encoded DN length (in bytes). */ + size_t len; +} br_x500_name; + +/** + * \brief Trust anchor structure. + */ +typedef struct { + /** \brief Encoded DN (X.500 name). */ + br_x500_name dn; + /** \brief Anchor flags (e.g. `BR_X509_TA_CA`). */ + unsigned flags; + /** \brief Anchor public key. */ + br_x509_pkey pkey; +} br_x509_trust_anchor; + +/** + * \brief Trust anchor flag: CA. + * + * A "CA" anchor is deemed fit to verify signatures on certificates. + * A "non-CA" anchor is accepted only for direct trust (server's + * certificate name and key match the anchor). + */ +#define BR_X509_TA_CA 0x0001 + +/* + * Key type: combination of a basic key type (low 4 bits) and some + * optional flags. + * + * For a public key, the basic key type only is set. + * + * For an expected key type, the flags indicate the intended purpose(s) + * for the key; the basic key type may be set to 0 to indicate that any + * key type compatible with the indicated purpose is acceptable. + */ +/** \brief Key type: algorithm is RSA. */ +#define BR_KEYTYPE_RSA 1 +/** \brief Key type: algorithm is EC. */ +#define BR_KEYTYPE_EC 2 + +/** + * \brief Key type: usage is "key exchange". + * + * This value is combined (with bitwise OR) with the algorithm + * (`BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`) when informing the X.509 + * validation engine that it should find a public key of that type, + * fit for key exchanges (e.g. `TLS_RSA_*` and `TLS_ECDH_*` cipher + * suites). + */ +#define BR_KEYTYPE_KEYX 0x10 + +/** + * \brief Key type: usage is "signature". + * + * This value is combined (with bitwise OR) with the algorithm + * (`BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`) when informing the X.509 + * validation engine that it should find a public key of that type, + * fit for signatures (e.g. `TLS_ECDHE_*` cipher suites). + */ +#define BR_KEYTYPE_SIGN 0x20 + +/* + * start_chain Called when a new chain is started. If 'server_name' + * is not NULL and non-empty, then it is a name that + * should be looked for in the EE certificate (in the + * SAN extension as dNSName, or in the subjectDN's CN + * if there is no SAN extension). + * The caller ensures that the provided 'server_name' + * pointer remains valid throughout validation. + * + * start_cert Begins a new certificate in the chain. The provided + * length is in bytes; this is the total certificate length. + * + * append Get some additional bytes for the current certificate. + * + * end_cert Ends the current certificate. + * + * end_chain Called at the end of the chain. Returned value is + * 0 on success, or a non-zero error code. + * + * get_pkey Returns the EE certificate public key. + * + * For a complete chain, start_chain() and end_chain() are always + * called. For each certificate, start_cert(), some append() calls, then + * end_cert() are called, in that order. There may be no append() call + * at all if the certificate is empty (which is not valid but may happen + * if the peer sends exactly that). + * + * get_pkey() shall return a pointer to a structure that is valid as + * long as a new chain is not started. This may be a sub-structure + * within the context for the engine. This function MAY return a valid + * pointer to a public key even in some cases of validation failure, + * depending on the validation engine. + */ + +/** + * \brief Class type for an X.509 engine. + * + * A certificate chain validation uses a caller-allocated context, which + * contains the running state for that validation. Methods are called + * in due order: + * + * - `start_chain()` is called at the start of the validation. + * - Certificates are processed one by one, in SSL order (end-entity + * comes first). For each certificate, the following methods are + * called: + * + * - `start_cert()` at the beginning of the certificate. + * - `append()` is called zero, one or more times, to provide + * the certificate (possibly in chunks). + * - `end_cert()` at the end of the certificate. + * + * - `end_chain()` is called when the last certificate in the chain + * was processed. + * - `get_pkey()` is called after chain processing, if the chain + * validation was successful. + * + * A context structure may be reused; the `start_chain()` method shall + * ensure (re)initialisation. + */ +typedef struct br_x509_class_ br_x509_class; +struct br_x509_class_ { + /** + * \brief X.509 context size, in bytes. + */ + size_t context_size; + + /** + * \brief Start a new chain. + * + * This method shall set the vtable (first field) of the context + * structure. + * + * The `server_name`, if not `NULL`, will be considered as a + * fully qualified domain name, to be matched against the `dNSName` + * elements of the end-entity certificate's SAN extension (if there + * is no SAN, then the Common Name from the subjectDN will be used). + * If `server_name` is `NULL` then no such matching is performed. + * + * \param ctx validation context. + * \param server_name server name to match (or `NULL`). + */ + void (*start_chain)(const br_x509_class **ctx, + const char *server_name); + + /** + * \brief Start a new certificate. + * + * \param ctx validation context. + * \param length new certificate length (in bytes). + */ + void (*start_cert)(const br_x509_class **ctx, uint32_t length); + + /** + * \brief Receive some bytes for the current certificate. + * + * This function may be called several times in succession for + * a given certificate. The caller guarantees that for each + * call, `len` is not zero, and the sum of all chunk lengths + * for a certificate matches the total certificate length which + * was provided in the previous `start_cert()` call. + * + * If the new certificate is empty (no byte at all) then this + * function won't be called at all. + * + * \param ctx validation context. + * \param buf certificate data chunk. + * \param len certificate data chunk length (in bytes). + */ + void (*append)(const br_x509_class **ctx, + const unsigned char *buf, size_t len); + + /** + * \brief Finish the current certificate. + * + * This function is called when the end of the current certificate + * is reached. + * + * \param ctx validation context. + */ + void (*end_cert)(const br_x509_class **ctx); + + /** + * \brief Finish the chain. + * + * This function is called at the end of the chain. It shall + * return either 0 if the validation was successful, or a + * non-zero error code. The `BR_ERR_X509_*` constants are + * error codes, though other values may be possible. + * + * \param ctx validation context. + * \return 0 on success, or a non-zero error code. + */ + unsigned (*end_chain)(const br_x509_class **ctx); + + /** + * \brief Get the resulting end-entity public key. + * + * The decoded public key is returned. The returned pointer + * may be valid only as long as the context structure is + * unmodified, i.e. it may cease to be valid if the context + * is released or reused. + * + * This function _may_ return `NULL` if the validation failed. + * However, returning a public key does not mean that the + * validation was wholly successful; some engines may return + * a decoded public key even if the chain did not end on a + * trusted anchor. + * + * If validation succeeded and `usage` is not `NULL`, then + * `*usage` is filled with a combination of `BR_KEYTYPE_SIGN` + * and/or `BR_KEYTYPE_KEYX` that specifies the validated key + * usage types. It is the caller's responsibility to check + * that value against the intended use of the public key. + * + * \param ctx validation context. + * \return the end-entity public key, or `NULL`. + */ + const br_x509_pkey *(*get_pkey)( + const br_x509_class *const *ctx, unsigned *usages); +}; + +/** + * \brief The "known key" X.509 engine structure. + * + * The structure contents are opaque (they shall not be accessed directly), + * except for the first field (the vtable). + * + * The "known key" engine returns an externally configured public key, + * and totally ignores the certificate contents. + */ +typedef struct { + /** \brief Reference to the context vtable. */ + const br_x509_class *vtable; +#ifndef BR_DOXYGEN_IGNORE + br_x509_pkey pkey; + unsigned usages; +#endif +} br_x509_knownkey_context; + +/** + * \brief Class instance for the "known key" X.509 engine. + */ +extern const br_x509_class br_x509_knownkey_vtable; + +/** + * \brief Initialize a "known key" X.509 engine with a known RSA public key. + * + * The `usages` parameter indicates the allowed key usages for that key + * (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * + * The provided pointers are linked in, not copied, so they must remain + * valid while the public key may be in usage. + * + * \param ctx context to initialise. + * \param pk known public key. + * \param usages allowed key usages. + */ +void br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx, + const br_rsa_public_key *pk, unsigned usages); + +/** + * \brief Initialize a "known key" X.509 engine with a known EC public key. + * + * The `usages` parameter indicates the allowed key usages for that key + * (`BR_KEYTYPE_KEYX` and/or `BR_KEYTYPE_SIGN`). + * + * The provided pointers are linked in, not copied, so they must remain + * valid while the public key may be in usage. + * + * \param ctx context to initialise. + * \param pk known public key. + * \param usages allowed key usages. + */ +void br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx, + const br_ec_public_key *pk, unsigned usages); + +#ifndef BR_DOXYGEN_IGNORE +/* + * The minimal X.509 engine has some state buffers which must be large + * enough to simultaneously accommodate: + * -- the public key extracted from the current certificate; + * -- the signature on the current certificate or on the previous + * certificate; + * -- the public key extracted from the EE certificate. + * + * We store public key elements in their raw unsigned big-endian + * encoding. We want to support up to RSA-4096 with a short (up to 64 + * bits) public exponent, thus a buffer for a public key must have + * length at least 520 bytes. Similarly, a RSA-4096 signature has length + * 512 bytes. + * + * Though RSA public exponents can formally be as large as the modulus + * (mathematically, even larger exponents would work, but PKCS#1 forbids + * them), exponents that do not fit on 32 bits are extremely rare, + * notably because some widespread implementations (e.g. Microsoft's + * CryptoAPI) don't support them. Moreover, large public exponent do not + * seem to imply any tangible security benefit, and they increase the + * cost of public key operations. The X.509 "minimal" engine will tolerate + * public exponents of arbitrary size as long as the modulus and the + * exponent can fit together in the dedicated buffer. + * + * EC public keys are shorter than RSA public keys; even with curve + * NIST P-521 (the largest curve we care to support), a public key is + * encoded over 133 bytes only. + */ +#define BR_X509_BUFSIZE_KEY 520 +#define BR_X509_BUFSIZE_SIG 512 +#endif + +/** + * \brief Type for receiving a name element. + * + * An array of such structures can be provided to the X.509 decoding + * engines. If the specified elements are found in the certificate + * subject DN or the SAN extension, then the name contents are copied + * as zero-terminated strings into the buffer. + * + * The decoder converts TeletexString and BMPString to UTF8String, and + * ensures that the resulting string is zero-terminated. If the string + * does not fit in the provided buffer, then the copy is aborted and an + * error is reported. + */ +typedef struct { + /** + * \brief Element OID. + * + * For X.500 name elements (to be extracted from the subject DN), + * this is the encoded OID for the requested name element; the + * first byte shall contain the length of the DER-encoded OID + * value, followed by the OID value (for instance, OID 2.5.4.3, + * for id-at-commonName, will be `03 55 04 03`). This is + * equivalent to full DER encoding with the length but without + * the tag. + * + * For SAN name elements, the first byte (`oid[0]`) has value 0, + * followed by another byte that matches the expected GeneralName + * tag. Allowed second byte values are then: + * + * - 1: `rfc822Name` + * + * - 2: `dNSName` + * + * - 6: `uniformResourceIdentifier` + * + * - 0: `otherName` + * + * If first and second byte are 0, then this is a SAN element of + * type `otherName`; the `oid[]` array should then contain, right + * after the two bytes of value 0, an encoded OID (with the same + * conventions as for X.500 name elements). If a match is found + * for that OID, then the corresponding name element will be + * extracted, as long as it is a supported string type. + */ + const unsigned char *oid; + + /** + * \brief Destination buffer. + */ + char *buf; + + /** + * \brief Length (in bytes) of the destination buffer. + * + * The buffer MUST NOT be smaller than 1 byte. + */ + size_t len; + + /** + * \brief Decoding status. + * + * Status is 0 if the name element was not found, 1 if it was + * found and decoded, or -1 on error. Error conditions include + * an unrecognised encoding, an invalid encoding, or a string + * too large for the destination buffer. + */ + int status; + +} br_name_element; + +/** + * \brief The "minimal" X.509 engine structure. + * + * The structure contents are opaque (they shall not be accessed directly), + * except for the first field (the vtable). + * + * The "minimal" engine performs a rudimentary but serviceable X.509 path + * validation. + */ +typedef struct { + const br_x509_class *vtable; + +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the EE public key. */ + br_x509_pkey pkey; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Server name to match with the SAN / CN of the EE certificate. */ + const char *server_name; + + /* Validated key usages. */ + unsigned char key_usages; + + /* Explicitly set date and time. */ + uint32_t days, seconds; + + /* Current certificate length (in bytes). Set to 0 when the + certificate has been fully processed. */ + uint32_t cert_length; + + /* Number of certificates processed so far in the current chain. + It is incremented at the end of the processing of a certificate, + so it is 0 for the EE. */ + uint32_t num_certs; + + /* Certificate data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Buffer for EE public key data. */ + unsigned char ee_pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Buffer for currently decoded public key. */ + unsigned char pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Signature type: signer key type, offset to the hash + function OID (in the T0 data block) and hash function + output length (TBS hash length). */ + unsigned char cert_signer_key_type; + uint16_t cert_sig_hash_oid; + unsigned char cert_sig_hash_len; + + /* Current/last certificate signature. */ + unsigned char cert_sig[BR_X509_BUFSIZE_SIG]; + uint16_t cert_sig_len; + + /* Minimum RSA key length (difference in bytes from 128). */ + int16_t min_rsa_size; + + /* Configured trust anchors. */ + const br_x509_trust_anchor *trust_anchors; + size_t trust_anchors_num; + + /* private context for dynamic callbacks */ + void *trust_anchor_dynamic_ctx; + /* Dynamic trust anchor, for on-the-fly loading of TAs */ + const br_x509_trust_anchor* (*trust_anchor_dynamic)(void *ctx, void *hashed_dn, size_t hashed_dn_len); + /* And a chance to free any dynamically allocated TA returned from above */ + void (*trust_anchor_dynamic_free)(void *ctx, const br_x509_trust_anchor *ta); + + /* + * Multi-hasher for the TBS. + */ + unsigned char do_mhash; + br_multihash_context mhash; + unsigned char tbs_hash[64]; + + /* + * Simple hasher for the subject/issuer DN. + */ + unsigned char do_dn_hash; + const br_hash_class *dn_hash_impl; + br_hash_compat_context dn_hash; + unsigned char current_dn_hash[64]; + unsigned char next_dn_hash[64]; + unsigned char saved_dn_hash[64]; + + /* + * Name elements to gather. + */ + br_name_element *name_elts; + size_t num_name_elts; + + /* + * Public key cryptography implementations (signature verification). + */ + br_rsa_pkcs1_vrfy irsa; + br_ecdsa_vrfy iecdsa; + const br_ec_impl *iec; +#endif + +} br_x509_minimal_context; + +/** + * \brief Class instance for the "minimal" X.509 engine. + */ +extern const br_x509_class br_x509_minimal_vtable; + +/** + * \brief Initialise a "minimal" X.509 engine. + * + * The `dn_hash_impl` parameter shall be a hash function internally used + * to match X.500 names (subject/issuer DN, and anchor names). Any standard + * hash function may be used, but a collision-resistant hash function is + * advised. + * + * After initialization, some implementations for signature verification + * (hash functions and signature algorithms) MUST be added. + * + * \param ctx context to initialise. + * \param dn_hash_impl hash function for DN comparisons. + * \param trust_anchors trust anchors. + * \param trust_anchors_num number of trust anchors. + */ +void br_x509_minimal_init(br_x509_minimal_context *ctx, + const br_hash_class *dn_hash_impl, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Set the optional dynamic trust anchor lookup callbacks + * + * The dynamic trust anchor lookup callbacks allow an application to implement + * a non-memory resident trust anchor store. This can be useful on embedded + * systems where RAM is at a premium, but there is an external stable store, + * such as embedded flash or SD card, to keep many CA certificates. Set or + * leave these functions as NULL to not use such a feature. + * + * The dynamic routine will be passed in the hashed DN in question using the + * dn_hash_impl, and should compare this DN to its set of hashed known DNs. + * Of course, the same dn_hash_impl needs to be used in the dynamic routine. + * After the trust_anchor* is used, the dynamic_free callback is given a + * chance to deallocate its memory, if needed. + * + * \param ctx context to initialise. + * \param dynamic_ctx private context for the dynamic callback + * \param trust_anchor_dynamic provides a trust_anchor* for a hashed_dn + * \param trust_anchor_dynamic_free allows deallocation of returned TA + */ +static inline void +br_x509_minimal_set_dynamic(br_x509_minimal_context *ctx, void *dynamic_ctx, + const br_x509_trust_anchor* (*dynamic)(void *ctx, void *hashed_dn, size_t hashed_dn_len), + void (*dynamic_free)(void *ctx, const br_x509_trust_anchor *ta)) +{ + ctx->trust_anchor_dynamic_ctx = dynamic_ctx; + ctx->trust_anchor_dynamic = dynamic; + ctx->trust_anchor_dynamic_free = dynamic_free; +} + +/** + * \brief Set a supported hash function in an X.509 "minimal" engine. + * + * Hash functions are used with signature verification algorithms. + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the hash functions it shall support for that + * purpose. The hash function identifier MUST be one of the standard + * hash function identifiers (1 to 6, for MD5, SHA-1, SHA-224, SHA-256, + * SHA-384 and SHA-512). + * + * If `impl` is `NULL`, this _removes_ support for the designated + * hash function. + * + * \param ctx validation context. + * \param id hash function identifier (from 1 to 6). + * \param impl hash function implementation (or `NULL`). + */ +static inline void +br_x509_minimal_set_hash(br_x509_minimal_context *ctx, + int id, const br_hash_class *impl) +{ + br_multihash_setimpl(&ctx->mhash, id, impl); +} + +/** + * \brief Set a RSA signature verification implementation in the X.509 + * "minimal" engine. + * + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the signature verification implementations that + * it is supposed to support. If `irsa` is `0`, then the RSA support + * is disabled. + * + * \param ctx validation context. + * \param irsa RSA signature verification implementation (or `0`). + */ +static inline void +br_x509_minimal_set_rsa(br_x509_minimal_context *ctx, + br_rsa_pkcs1_vrfy irsa) +{ + ctx->irsa = irsa; +} + +/** + * \brief Set a ECDSA signature verification implementation in the X.509 + * "minimal" engine. + * + * Once initialised (with `br_x509_minimal_init()`), the context must + * be configured with the signature verification implementations that + * it is supposed to support. + * + * If `iecdsa` is `0`, then this call disables ECDSA support; in that + * case, `iec` may be `NULL`. Otherwise, `iecdsa` MUST point to a function + * that verifies ECDSA signatures with format "asn1", and it will use + * `iec` as underlying elliptic curve support. + * + * \param ctx validation context. + * \param iec elliptic curve implementation (or `NULL`). + * \param iecdsa ECDSA implementation (or `0`). + */ +static inline void +br_x509_minimal_set_ecdsa(br_x509_minimal_context *ctx, + const br_ec_impl *iec, br_ecdsa_vrfy iecdsa) +{ + ctx->iecdsa = iecdsa; + ctx->iec = iec; +} + +/** + * \brief Initialise a "minimal" X.509 engine with default algorithms. + * + * This function performs the same job as `br_x509_minimal_init()`, but + * also sets implementations for RSA, ECDSA, and the standard hash + * functions. + * + * \param ctx context to initialise. + * \param trust_anchors trust anchors. + * \param trust_anchors_num number of trust anchors. + */ +void br_x509_minimal_init_full(br_x509_minimal_context *ctx, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num); + +/** + * \brief Set the validation time for the X.509 "minimal" engine. + * + * The validation time is set as two 32-bit integers, for days and + * seconds since a fixed epoch: + * + * - Days are counted in a proleptic Gregorian calendar since + * January 1st, 0 AD. Year "0 AD" is the one that preceded "1 AD"; + * it is also traditionally known as "1 BC". + * + * - Seconds are counted since midnight, from 0 to 86400 (a count of + * 86400 is possible only if a leap second happened). + * + * The validation date and time is understood in the UTC time zone. The + * "Unix Epoch" (January 1st, 1970, 00:00 UTC) corresponds to days=719528 + * and seconds=0; the "Windows Epoch" (January 1st, 1601, 00:00 UTC) is + * days=584754, seconds=0. + * + * If the validation date and time are not explicitly set, but BearSSL + * was compiled with support for the system clock on the underlying + * platform, then the current time will automatically be used. Otherwise, + * not setting the validation date and time implies a validation + * failure (except in case of direct trust of the EE key). + * + * \param ctx validation context. + * \param days days since January 1st, 0 AD (Gregorian calendar). + * \param seconds seconds since midnight (0 to 86400). + */ +static inline void +br_x509_minimal_set_time(br_x509_minimal_context *ctx, + uint32_t days, uint32_t seconds) +{ + ctx->days = days; + ctx->seconds = seconds; +} + +/** + * \brief Set the minimal acceptable length for RSA keys (X.509 "minimal" + * engine). + * + * The RSA key length is expressed in bytes. The default minimum key + * length is 128 bytes, corresponding to 1017 bits. RSA keys shorter + * than the configured length will be rejected, implying validation + * failure. This setting applies to keys extracted from certificates + * (both end-entity, and intermediate CA) but not to "CA" trust anchors. + * + * \param ctx validation context. + * \param byte_length minimum RSA key length, **in bytes** (not bits). + */ +static inline void +br_x509_minimal_set_minrsa(br_x509_minimal_context *ctx, int byte_length) +{ + ctx->min_rsa_size = (int16_t)(byte_length - 128); +} + +/** + * \brief Set the name elements to gather. + * + * The provided array is linked in the context. The elements are + * gathered from the EE certificate. If the same element type is + * requested several times, then the relevant structures will be filled + * in the order the matching values are encountered in the certificate. + * + * \param ctx validation context. + * \param elts array of name element structures to fill. + * \param num_elts number of name element structures to fill. + */ +static inline void +br_x509_minimal_set_name_elements(br_x509_minimal_context *ctx, + br_name_element *elts, size_t num_elts) +{ + ctx->name_elts = elts; + ctx->num_name_elts = num_elts; +} + +/** + * \brief X.509 decoder context. + * + * This structure is _not_ for X.509 validation, but for extracting + * names and public keys from encoded certificates. Intended usage is + * to use (self-signed) certificates as trust anchors. + * + * Contents are opaque and shall not be accessed directly. + */ +typedef struct { + +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the public key. */ + br_x509_pkey pkey; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Flag set when decoding succeeds. */ + unsigned char decoded; + + /* Validity dates. */ + uint32_t notbefore_days, notbefore_seconds; + uint32_t notafter_days, notafter_seconds; + + /* The "CA" flag. This is set to true if the certificate contains + a Basic Constraints extension that asserts CA status. */ + unsigned char isCA; + + /* DN processing: the subject DN is extracted and pushed to the + provided callback. */ + unsigned char copy_dn; + void *append_dn_ctx; + void (*append_dn)(void *ctx, const void *buf, size_t len); + + /* DN processing: the issuer DN is extracted and pushed to the + provided callback. */ + unsigned char copy_in; + void *append_in_ctx; + void (*append_in)(void *ctx, const void *buf, size_t len); + + /* Certificate data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* Buffer for decoded public key. */ + unsigned char pkey_data[BR_X509_BUFSIZE_KEY]; + + /* Type of key and hash function used in the certificate signature. */ + unsigned char signer_key_type; + unsigned char signer_hash_id; +#endif + +} br_x509_decoder_context; + +/** + * \brief Initialise an X.509 decoder context for processing a new + * certificate. + * + * The `append_dn()` callback (with opaque context `append_dn_ctx`) + * will be invoked to receive, chunk by chunk, the certificate's + * subject DN. If `append_dn` is `0` then the subject DN will be + * ignored. + * + * \param ctx X.509 decoder context to initialise. + * \param append_dn DN receiver callback (or `0`). + * \param append_dn_ctx context for the DN receiver callback. + * \param append_in issuer DN receiver callback (or `0`). + * \param append_in_ctx context for the issuer DN receiver callback. + */ +void br_x509_decoder_init(br_x509_decoder_context *ctx, + void (*append_dn)(void *ctx, const void *buf, size_t len), + void *append_dn_ctx, + void (*append_in)(void *ctx, const void *buf, size_t len), + void *append_in_ctx); + +/** + * \brief Push some certificate bytes into a decoder context. + * + * If `len` is non-zero, then that many bytes are pushed, from address + * `data`, into the provided decoder context. + * + * \param ctx X.509 decoder context. + * \param data certificate data chunk. + * \param len certificate data chunk length (in bytes). + */ +void br_x509_decoder_push(br_x509_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Obtain the decoded public key. + * + * Returned value is a pointer to a structure internal to the decoder + * context; releasing or reusing the decoder context invalidates that + * structure. + * + * If decoding was not finished, or failed, then `NULL` is returned. + * + * \param ctx X.509 decoder context. + * \return the public key, or `NULL` on unfinished/error. + */ +static inline br_x509_pkey * +br_x509_decoder_get_pkey(br_x509_decoder_context *ctx) +{ + if (ctx->decoded && ctx->err == 0) { + return &ctx->pkey; + } else { + return NULL; + } +} + +/** + * \brief Get decoder error status. + * + * If no error was reported yet but the certificate decoding is not + * finished, then the error code is `BR_ERR_X509_TRUNCATED`. If decoding + * was successful, then 0 is returned. + * + * \param ctx X.509 decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_x509_decoder_last_error(br_x509_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (!ctx->decoded) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the "isCA" flag from an X.509 decoder context. + * + * This flag is set if the decoded certificate claims to be a CA through + * a Basic Constraints extension. This flag should not be read before + * decoding completed successfully. + * + * \param ctx X.509 decoder context. + * \return the "isCA" flag. + */ +static inline int +br_x509_decoder_isCA(br_x509_decoder_context *ctx) +{ + return ctx->isCA; +} + +/** + * \brief Get the issuing CA key type (type of algorithm used to sign the + * decoded certificate). + * + * This is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. The value 0 is returned + * if the signature type was not recognised. + * + * \param ctx X.509 decoder context. + * \return the issuing CA key type. + */ +static inline int +br_x509_decoder_get_signer_key_type(br_x509_decoder_context *ctx) +{ + return ctx->signer_key_type; +} + +/** + * \brief Get the identifier for the hash function used to sign the decoded + * certificate. + * + * This is 0 if the hash function was not recognised. + * + * \param ctx X.509 decoder context. + * \return the signature hash function identifier. + */ +static inline int +br_x509_decoder_get_signer_hash_id(br_x509_decoder_context *ctx) +{ + return ctx->signer_hash_id; +} + +/** + * \brief Type for an X.509 certificate (DER-encoded). + */ +typedef struct { + /** \brief The DER-encoded certificate data. */ + unsigned char *data; + /** \brief The DER-encoded certificate length (in bytes). */ + size_t data_len; +} br_x509_certificate; + +/** + * \brief Private key decoder context. + * + * The private key decoder recognises RSA and EC private keys, either in + * their raw, DER-encoded format, or wrapped in an unencrypted PKCS#8 + * archive (again DER-encoded). + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the private key. */ + union { + br_rsa_private_key rsa; + br_ec_private_key ec; + } key; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Private key data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Decoded key type; 0 until decoding is complete. */ + unsigned char key_type; + + /* Buffer for the private key elements. It shall be large enough + to accommodate all elements for a RSA-4096 private key (roughly + five 2048-bit integers, possibly a bit more). */ + unsigned char key_data[3 * BR_X509_BUFSIZE_SIG]; +#endif +} br_skey_decoder_context; + +/** + * \brief Initialise a private key decoder context. + * + * \param ctx key decoder context to initialise. + */ +void br_skey_decoder_init(br_skey_decoder_context *ctx); + +/** + * \brief Push some data bytes into a private key decoder context. + * + * If `len` is non-zero, then that many data bytes, starting at address + * `data`, are pushed into the decoder. + * + * \param ctx key decoder context. + * \param data private key data chunk. + * \param len private key data chunk length (in bytes). + */ +void br_skey_decoder_push(br_skey_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Get the decoding status for a private key. + * + * Decoding status is 0 on success, or a non-zero error code. If the + * decoding is unfinished when this function is called, then the + * status code `BR_ERR_X509_TRUNCATED` is returned. + * + * \param ctx key decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_skey_decoder_last_error(const br_skey_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (ctx->key_type == 0) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the decoded private key type. + * + * Private key type is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. If decoding is + * not finished or failed, then 0 is returned. + * + * \param ctx key decoder context. + * \return decoded private key type, or 0. + */ +static inline int +br_skey_decoder_key_type(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0) { + return ctx->key_type; + } else { + return 0; + } +} + +/** + * \brief Get the decoded RSA private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not RSA. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded RSA private key, or `NULL`. + */ +static inline const br_rsa_private_key * +br_skey_decoder_get_rsa(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_RSA) { + return &ctx->key.rsa; + } else { + return NULL; + } +} + +/** + * \brief Get the decoded EC private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not EC. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded EC private key, or `NULL`. + */ +static inline const br_ec_private_key * +br_skey_decoder_get_ec(const br_skey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_EC) { + return &ctx->key.ec; + } else { + return NULL; + } +} + +/** + * \brief Public key decoder context. + * + * The public key decoder recognises RSA and EC private keys, either in + * their raw, DER-encoded format, or wrapped in an unencrypted PKCS#8 + * archive (again DER-encoded). + * + * Structure contents are opaque and shall not be accessed directly. + */ +typedef struct { +#ifndef BR_DOXYGEN_IGNORE + /* Structure for returning the private key. */ + union { + br_rsa_public_key rsa; + br_ec_public_key ec; + } key; + + /* CPU for the T0 virtual machine. */ + struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; + } cpu; + uint32_t dp_stack[32]; + uint32_t rp_stack[32]; + int err; + + /* Private key data chunk. */ + const unsigned char *hbuf; + size_t hlen; + + /* The pad serves as destination for various operations. */ + unsigned char pad[256]; + + /* Decoded key type; 0 until decoding is complete. */ + unsigned char key_type; + + /* Buffer for the private key elements. It shall be large enough + to accommodate all elements for a RSA-4096 private key (roughly + five 2048-bit integers, possibly a bit more). */ + unsigned char key_data[3 * BR_X509_BUFSIZE_SIG]; +#endif +} br_pkey_decoder_context; + + +/** + * \brief Initialise a public key decoder context. + * + * \param ctx key decoder context to initialise. + */ +void br_pkey_decoder_init(br_pkey_decoder_context *ctx); + +/** + * \brief Push some data bytes into a public key decoder context. + * + * If `len` is non-zero, then that many data bytes, starting at address + * `data`, are pushed into the decoder. + * + * \param ctx key decoder context. + * \param data private key data chunk. + * \param len private key data chunk length (in bytes). + */ +void br_pkey_decoder_push(br_pkey_decoder_context *ctx, + const void *data, size_t len); + +/** + * \brief Get the decoding status for a public key. + * + * Decoding status is 0 on success, or a non-zero error code. If the + * decoding is unfinished when this function is called, then the + * status code `BR_ERR_X509_TRUNCATED` is returned. + * + * \param ctx key decoder context. + * \return 0 on successful decoding, or a non-zero error code. + */ +static inline int +br_pkey_decoder_last_error(const br_pkey_decoder_context *ctx) +{ + if (ctx->err != 0) { + return ctx->err; + } + if (ctx->key_type == 0) { + return BR_ERR_X509_TRUNCATED; + } + return 0; +} + +/** + * \brief Get the decoded public key type. + * + * Public key type is `BR_KEYTYPE_RSA` or `BR_KEYTYPE_EC`. If decoding is + * not finished or failed, then 0 is returned. + * + * \param ctx key decoder context. + * \return decoded private key type, or 0. + */ +static inline int +br_pkey_decoder_key_type(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0) { + return ctx->key_type; + } else { + return 0; + } +} + +/** + * \brief Get the decoded RSA public key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not RSA. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded RSA public key, or `NULL`. + */ +static inline const br_rsa_public_key * +br_pkey_decoder_get_rsa(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_RSA) { + return &ctx->key.rsa; + } else { + return NULL; + } +} + +/** + * \brief Get the decoded EC private key. + * + * This function returns `NULL` if the decoding failed, or is not + * finished, or the key is not EC. The returned pointer references + * structures within the context that can become invalid if the context + * is reused or released. + * + * \param ctx key decoder context. + * \return decoded EC private key, or `NULL`. + */ +static inline const br_ec_public_key * +br_pkey_decoder_get_ec(const br_pkey_decoder_context *ctx) +{ + if (ctx->err == 0 && ctx->key_type == BR_KEYTYPE_EC) { + return &ctx->key.ec; + } else { + return NULL; + } +} + +/** + * \brief Encode an RSA private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in PKCS#1 (RFC 8017, Appendix C, type `RSAPrivateKey`), with DER + * encoding rules. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an RSA private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). It wraps around the "raw DER" + * format for the RSA key, as implemented by `br_encode_rsa_raw_der()`. + * + * The key elements are: + * + * - `sk`: the private key (`p`, `q`, `dp`, `dq` and `iq`) + * + * - `pk`: the public key (`n` and `e`) + * + * - `d` (size: `dlen` bytes): the private exponent + * + * The public key elements, and the private exponent `d`, can be + * recomputed from the private key (see `br_rsa_compute_modulus()`, + * `br_rsa_compute_pubexp()` and `br_rsa_compute_privexp()`). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the RSA private key. + * \param pk the RSA public key. + * \param d the RSA private exponent. + * \param dlen the RSA private exponent length (in bytes). + * \return the encoded key length (in bytes). + */ +size_t br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen); + +/** + * \brief Encode an EC private key (raw DER format). + * + * This function encodes the provided key into the "raw" format specified + * in RFC 5915 (type `ECPrivateKey`), with DER encoding rules. + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is `NULL`, then the encoded key will not include the public key + * in its `publicKey` field (which is nominally optional). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief Encode an EC private key (PKCS#8 DER format). + * + * This function encodes the provided key into the PKCS#8 format + * (RFC 5958, type `OneAsymmetricKey`). The curve is identified + * by an OID provided as parameters to the `privateKeyAlgorithm` + * field. The private key value (contents of the `privateKey` field) + * contains the DER encoding of the `ECPrivateKey` type defined in + * RFC 5915, without the `parameters` field (since they would be + * redundant with the information in `privateKeyAlgorithm`). + * + * The private key is provided in `sk`, the public key being `pk`. If + * `pk` is not `NULL`, then the encoded public key is included in the + * `publicKey` field of the private key value (but not in the `publicKey` + * field of the PKCS#8 `OneAsymmetricKey` wrapper). + * + * If `dest` is not `NULL`, then the encoded key is written at that + * address, and the encoded length (in bytes) is returned. If `dest` is + * `NULL`, then nothing is written, but the encoded length is still + * computed and returned. + * + * If the key cannot be encoded (e.g. because there is no known OBJECT + * IDENTIFIER for the used curve), then 0 is returned. + * + * \param dest the destination buffer (or `NULL`). + * \param sk the EC private key. + * \param pk the EC public key (or `NULL`). + * \return the encoded key length (in bytes), or 0. + */ +size_t br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk); + +/** + * \brief PEM banner for RSA private key (raw). + */ +#define BR_ENCODE_PEM_RSA_RAW "RSA PRIVATE KEY" + +/** + * \brief PEM banner for EC private key (raw). + */ +#define BR_ENCODE_PEM_EC_RAW "EC PRIVATE KEY" + +/** + * \brief PEM banner for an RSA or EC private key in PKCS#8 format. + */ +#define BR_ENCODE_PEM_PKCS8 "PRIVATE KEY" + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_config.h b/lib/lib_ssl/bearssl-esp8266/src/t_config.h new file mode 100644 index 0000000000..2b0e47b643 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_config.h @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_bearssl_tasmota_config.h" + +#ifndef CONFIG_H__ +#define CONFIG_H__ + +/* + * This file contains compile-time flags that can override the + * autodetection performed in relevant files. Each flag is a macro; it + * deactivates the feature if defined to 0, activates it if defined to a + * non-zero integer (normally 1). If the macro is not defined, then + * autodetection applies. + */ + +/* + * When BR_64 is enabled, 64-bit integer types are assumed to be + * efficient (i.e. the architecture has 64-bit registers and can + * do 64-bit operations as fast as 32-bit operations). + * +#define BR_64 1 + */ + +/* + * When BR_LOMUL is enabled, then multiplications of 32-bit values whose + * result are truncated to the low 32 bits are assumed to be + * substantially more efficient than 32-bit multiplications that yield + * 64-bit results. This is typically the case on low-end ARM Cortex M + * systems (M0, M0+, M1, and arguably M3 and M4 as well). + * +#define BR_LOMUL 1 + */ + +/* + * When BR_SLOW_MUL is enabled, multiplications are assumed to be + * substantially slow with regards to other integer operations, thus + * making it worth to make more operations for a given task if it allows + * using less multiplications. + * +#define BR_SLOW_MUL 1 + */ + +/* + * When BR_SLOW_MUL15 is enabled, short multplications (on 15-bit words) + * are assumed to be substantially slow with regards to other integer + * operations, thus making it worth to make more integer operations if + * it allows using less multiplications. + * +#define BR_SLOW_MUL15 1 + */ + +/* + * When BR_CT_MUL31 is enabled, multiplications of 31-bit values (used + * in the "i31" big integer implementation) use an alternate implementation + * which is slower and larger than the normal multiplication, but should + * ensure constant-time multiplications even on architectures where the + * multiplication opcode takes a variable number of cycles to complete. + * +#define BR_CT_MUL31 1 + */ + +/* + * When BR_CT_MUL15 is enabled, multiplications of 15-bit values (held + * in 32-bit words) use an alternate implementation which is slower and + * larger than the normal multiplication, but should ensure + * constant-time multiplications on most/all architectures where the + * basic multiplication is not constant-time. +#define BR_CT_MUL15 1 + */ + +/* + * When BR_NO_ARITH_SHIFT is enabled, arithmetic right shifts (with sign + * extension) are performed with a sequence of operations which is bigger + * and slower than a simple right shift on a signed value. This avoids + * relying on an implementation-defined behaviour. However, most if not + * all C compilers use sign extension for right shifts on signed values, + * so this alternate macro is disabled by default. +#define BR_NO_ARITH_SHIFT 1 + */ + +/* + * When BR_RDRAND is enabled, the SSL engine will use the RDRAND opcode + * to automatically obtain quality randomness for seeding its internal + * PRNG. Since that opcode is present only in recent x86 CPU, its + * support is dynamically tested; if the current CPU does not support + * it, then another random source will be used, such as /dev/urandom or + * CryptGenRandom(). + * +#define BR_RDRAND 1 + */ + +/* + * When BR_USE_URANDOM is enabled, the SSL engine will use /dev/urandom + * to automatically obtain quality randomness for seedings its internal + * PRNG. + * +#define BR_USE_URANDOM 1 + */ + +/* + * When BR_USE_ESP8266_RAND is enabled, use the phy_get_rand() SDK call + * on the ESP8266 as the entropy source, 32-bits at a time. + * +#define BR_USE_ESP8266_RAND 1 + */ + +/* + * When BR_USE_WIN32_RAND is enabled, the SSL engine will use the Win32 + * (CryptoAPI) functions (CryptAcquireContext(), CryptGenRandom()...) to + * automatically obtain quality randomness for seedings its internal PRNG. + * + * Note: if both BR_USE_URANDOM and BR_USE_WIN32_RAND are defined, the + * former takes precedence. + * +#define BR_USE_WIN32_RAND 1 + */ + +/* + * When BR_USE_UNIX_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling time(), and assuming that the + * returned value (a 'time_t') is an integer that counts time in seconds + * since the Unix Epoch (Jan 1st, 1970, 00:00 UTC). + * +#define BR_USE_UNIX_TIME 1 + */ + +/* + * When BR_USE_WIN32_TIME is enabled, the X.509 validation engine obtains + * the current time from the OS by calling the Win32 function + * GetSystemTimeAsFileTime(). + * + * Note: if both BR_USE_UNIX_TIME and BR_USE_WIN32_TIME are defined, the + * former takes precedence. + * +#define BR_USE_WIN32_TIME 1 + */ + +/* + * When BR_ARMEL_CORTEXM_GCC is enabled, some operations are replaced with + * inline assembly which is shorter and/or faster. This should be used + * only when all of the following are true: + * - target architecture is ARM in Thumb mode + * - target endianness is little-endian + * - compiler is GCC (or GCC-compatible for inline assembly syntax) + * + * This is meant for the low-end cores (Cortex M0, M0+, M1, M3). + * Note: if BR_LOMUL is not explicitly enabled or disabled, then + * enabling BR_ARMEL_CORTEXM_GCC also enables BR_LOMUL. + * +#define BR_ARMEL_CORTEXM_GCC 1 + */ + +/* + * When BR_AES_X86NI is enabled, the AES implementation using the x86 "NI" + * instructions (dedicated AES opcodes) will be compiled. If this is not + * enabled explicitly, then that AES implementation will be compiled only + * if a compatible compiler is detected. If set explicitly to 0, the + * implementation will not be compiled at all. + * +#define BR_AES_X86NI 1 + */ + +/* + * When BR_SSE2 is enabled, SSE2 intrinsics will be used for some + * algorithm implementations that use them (e.g. chacha20_sse2). If this + * is not enabled explicitly, then support for SSE2 intrinsics will be + * automatically detected. If set explicitly to 0, then SSE2 code will + * not be compiled at all. + * +#define BR_SSE2 1 + */ + +/* + * When BR_POWER8 is enabled, the AES implementation using the POWER ISA + * 2.07 opcodes (available on POWER8 processors and later) is compiled. + * If this is not enabled explicitly, then that implementation will be + * compiled only if a compatible compiler is detected, _and_ the target + * architecture is POWER8 or later. + * +#define BR_POWER8 1 + */ + +/* + * When BR_INT128 is enabled, then code using the 'unsigned __int64' + * and 'unsigned __int128' types will be used to leverage 64x64->128 + * unsigned multiplications. This should work with GCC and compatible + * compilers on 64-bit architectures. + * +#define BR_INT128 1 + */ + +/* + * When BR_UMUL128 is enabled, then code using the '_umul128()' and + * '_addcarry_u64()' intrinsics will be used to implement 64x64->128 + * unsigned multiplications. This should work on Visual C on x64 systems. + * +#define BR_UMUL128 1 + */ + +/* + * When BR_LE_UNALIGNED is enabled, then the current architecture is + * assumed to use little-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_LE_UNALIGNED 1 + */ + +/* + * When BR_BE_UNALIGNED is enabled, then the current architecture is + * assumed to use big-endian encoding for integers, and to tolerate + * unaligned accesses with no or minimal time penalty. + * +#define BR_BE_UNALIGNED 1 + */ + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/t_inner.h b/lib/lib_ssl/bearssl-esp8266/src/t_inner.h new file mode 100644 index 0000000000..de08990e5a --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/t_inner.h @@ -0,0 +1,2619 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef INNER_H__ +#define INNER_H__ + +#include +#include +#include + +#include "pgmspace_bearssl.h" + +#include "t_config.h" +#include "t_bearssl.h" + +/* + * On MSVC, disable the warning about applying unary minus on an + * unsigned type: it is standard, we do it all the time, and for + * good reasons. + */ +#if _MSC_VER +#pragma warning( disable : 4146 ) +#endif + +/* + * Maximum size for a RSA modulus (in bits). Allocated stack buffers + * depend on that size, so this value should be kept small. Currently, + * 2048-bit RSA keys offer adequate security, and should still do so for + * the next few decades; however, a number of widespread PKI have + * already set their root keys to RSA-4096, so we should be able to + * process such keys. + * + * This value MUST be a multiple of 64. This value MUST NOT exceed 47666 + * (some computations in RSA key generation rely on the factor size being + * no more than 23833 bits). RSA key sizes beyond 3072 bits don't make a + * lot of sense anyway. + */ +#ifndef BR_MAX_RSA_SIZE +#define BR_MAX_RSA_SIZE 4096 +#endif + +/* + * Minimum size for a RSA modulus (in bits); this value is used only to + * filter out invalid parameters for key pair generation. Normally, + * applications should not use RSA keys smaller than 2048 bits; but some + * specific cases might need shorter keys, for legacy or research + * purposes. + */ +#define BR_MIN_RSA_SIZE 512 + +/* + * Maximum size for a RSA factor (in bits). This is for RSA private-key + * operations. Default is to support factors up to a bit more than half + * the maximum modulus size. + * + * This value MUST be a multiple of 32. + */ +#define BR_MAX_RSA_FACTOR ((BR_MAX_RSA_SIZE + 64) >> 1) + +/* + * Maximum size for an EC curve (modulus or order), in bits. Size of + * stack buffers depends on that parameter. This size MUST be a multiple + * of 8 (so that decoding an integer with that many bytes does not + * overflow). + */ +#ifndef BR_MAX_EC_SIZE +#define BR_MAX_EC_SIZE 528 +#endif + +/* + * Some macros to recognize the current architecture. Right now, we are + * interested into automatically recognizing architecture with efficient + * 64-bit types so that we may automatically use implementations that + * use 64-bit registers in that case. Future versions may detect, e.g., + * availability of SSE2 intrinsics. + * + * If 'unsigned long' is a 64-bit type, then we assume that 64-bit types + * are efficient. Otherwise, we rely on macros that depend on compiler, + * OS and architecture. In any case, failure to detect the architecture + * as 64-bit means that the 32-bit code will be used, and that code + * works also on 64-bit architectures (the 64-bit code may simply be + * more efficient). + * + * The test on 'unsigned long' should already catch most cases, the one + * notable exception being Windows code where 'unsigned long' is kept to + * 32-bit for compatibility with all the legacy code that liberally uses + * the 'DWORD' type for 32-bit values. + * + * Macro names are taken from: http://nadeausoftware.com/articles/2012/02/c_c_tip_how_detect_processor_type_using_compiler_predefined_macros + */ +#ifndef BR_64 +#if ((ULONG_MAX >> 31) >> 31) == 3 +#define BR_64 1 +#elif defined(__ia64) || defined(__itanium__) || defined(_M_IA64) +#define BR_64 1 +#elif defined(__powerpc64__) || defined(__ppc64__) || defined(__PPC64__) \ + || defined(__64BIT__) || defined(_LP64) || defined(__LP64__) +#define BR_64 1 +#elif defined(__sparc64__) +#define BR_64 1 +#elif defined(__x86_64__) || defined(_M_X64) +#define BR_64 1 +#elif defined(__aarch64__) || defined(_M_ARM64) +#define BR_64 1 +#elif defined(__mips64) +#define BR_64 1 +#endif +#endif + +/* + * Set BR_LOMUL on platforms where it makes sense. + */ +#ifndef BR_LOMUL +#if BR_ARMEL_CORTEXM_GCC || ((defined(ESP8266) || defined(ESP32)) && !defined(ESP8266M32)) +#define BR_LOMUL 1 +#endif +#endif + +/* + * Architecture detection. + */ +#ifndef BR_i386 +#if __i386__ || _M_IX86 +#define BR_i386 1 +#endif +#endif + +#ifndef BR_amd64 +#if __x86_64__ || _M_X64 +#define BR_amd64 1 +#endif +#endif + +/* + * Compiler brand and version. + * + * Implementations that use intrinsics need to detect the compiler type + * and version because some specific actions may be needed to activate + * the corresponding opcodes, both for header inclusion, and when using + * them in a function. + * + * BR_GCC, BR_CLANG and BR_MSC will be set to 1 for, respectively, GCC, + * Clang and MS Visual C. For each of them, sub-macros will be defined + * for versions; each sub-macro is set whenever the compiler version is + * at least as recent as the one corresponding to the macro. + */ + +/* + * GCC thresholds are on versions 4.4 to 4.9 and 5.0. + */ +#ifndef BR_GCC +#if __GNUC__ && !__clang__ +#define BR_GCC 1 + +#if __GNUC__ > 4 +#define BR_GCC_5_0 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 9 +#define BR_GCC_4_9 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 8 +#define BR_GCC_4_8 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 7 +#define BR_GCC_4_7 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 6 +#define BR_GCC_4_6 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 5 +#define BR_GCC_4_5 1 +#elif __GNUC__ == 4 && __GNUC_MINOR__ >= 4 +#define BR_GCC_4_4 1 +#endif + +#if BR_GCC_5_0 +#define BR_GCC_4_9 1 +#endif +#if BR_GCC_4_9 +#define BR_GCC_4_8 1 +#endif +#if BR_GCC_4_8 +#define BR_GCC_4_7 1 +#endif +#if BR_GCC_4_7 +#define BR_GCC_4_6 1 +#endif +#if BR_GCC_4_6 +#define BR_GCC_4_5 1 +#endif +#if BR_GCC_4_5 +#define BR_GCC_4_4 1 +#endif + +#endif +#endif + +/* + * Clang thresholds are on versions 3.7.0 and 3.8.0. + */ +#ifndef BR_CLANG +#if __clang__ +#define BR_CLANG 1 + +#if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 8) +#define BR_CLANG_3_8 1 +#elif __clang_major__ == 3 && __clang_minor__ >= 7 +#define BR_CLANG_3_7 1 +#endif + +#if BR_CLANG_3_8 +#define BR_CLANG_3_7 1 +#endif + +#endif +#endif + +/* + * MS Visual C thresholds are on Visual Studio 2005 to 2015. + */ +#ifndef BR_MSC +#if _MSC_VER +#define BR_MSC 1 + +#if _MSC_VER >= 1900 +#define BR_MSC_2015 1 +#elif _MSC_VER >= 1800 +#define BR_MSC_2013 1 +#elif _MSC_VER >= 1700 +#define BR_MSC_2012 1 +#elif _MSC_VER >= 1600 +#define BR_MSC_2010 1 +#elif _MSC_VER >= 1500 +#define BR_MSC_2008 1 +#elif _MSC_VER >= 1400 +#define BR_MSC_2005 1 +#endif + +#if BR_MSC_2015 +#define BR_MSC_2013 1 +#endif +#if BR_MSC_2013 +#define BR_MSC_2012 1 +#endif +#if BR_MSC_2012 +#define BR_MSC_2010 1 +#endif +#if BR_MSC_2010 +#define BR_MSC_2008 1 +#endif +#if BR_MSC_2008 +#define BR_MSC_2005 1 +#endif + +#endif +#endif + +/* + * GCC 4.4+ and Clang 3.7+ allow tagging specific functions with a + * 'target' attribute that activates support for specific opcodes. + */ +#if BR_GCC_4_4 || BR_CLANG_3_7 +#define BR_TARGET(x) __attribute__((target(x))) +#else +#define BR_TARGET(x) +#endif + +/* + * AES-NI intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.8+, Clang 3.7+ and MSC 2012+. + */ +#ifndef BR_AES_X86NI +#if (BR_i386 || BR_amd64) && (BR_GCC_4_8 || BR_CLANG_3_7 || BR_MSC_2012) +#define BR_AES_X86NI 1 +#endif +#endif + +/* + * SSE2 intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.4+, Clang 3.7+ and MSC 2005+. + */ +#ifndef BR_SSE2 +#if (BR_i386 || BR_amd64) && (BR_GCC_4_4 || BR_CLANG_3_7 || BR_MSC_2005) +#define BR_SSE2 1 +#endif +#endif + +/* + * RDRAND intrinsics are available on x86 (32-bit and 64-bit) with + * GCC 4.6+, Clang 3.7+ and MSC 2012+. + */ +#ifndef BR_RDRAND +#if (BR_i386 || BR_amd64) && (BR_GCC_4_6 || BR_CLANG_3_7 || BR_MSC_2012) +#define BR_RDRAND 1 +#endif +#endif + +/* + * Use ESP8266 hardware random generator when possible. + */ +#ifndef BR_USE_ESP8266_RAND +#if (defined(ESP8266) || defined(ESP32)) +#define BR_USE_ESP8266_RAND 1 +#endif +#endif + +/* + * Determine type of OS for random number generation. Macro names and + * values are documented on: + * https://sourceforge.net/p/predef/wiki/OperatingSystems/ + * + * Win32's CryptGenRandom() should be available on Windows systems. + * + * /dev/urandom should work on all Unix-like systems (including macOS X). + * + * getentropy() is present on Linux (Glibc 2.25+), FreeBSD (12.0+) and + * OpenBSD (5.6+). For OpenBSD, there does not seem to be easy to use + * macros to test the minimum version, so we just assume that it is + * recent enough (last version without getentropy() has gone out of + * support in May 2015). + * + * Ideally we should use getentropy() on macOS (10.12+) too, but I don't + * know how to test the exact OS version with preprocessor macros. + * + * TODO: enrich the list of detected system. + */ + +#ifndef BR_USE_URANDOM +#if defined _AIX \ + || defined __ANDROID__ \ + || defined __FreeBSD__ \ + || defined __NetBSD__ \ + || defined __OpenBSD__ \ + || defined __DragonFly__ \ + || defined __linux__ \ + || (defined __sun && (defined __SVR4 || defined __svr4__)) \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_URANDOM 1 +#endif +#endif + +#ifndef BR_USE_GETENTROPY +#if (defined __linux__ \ + && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))) \ + || (defined __FreeBSD__ && __FreeBSD__ >= 12) \ + || defined __OpenBSD__ +#define BR_USE_GETENTROPY 1 +#endif +#endif + +#ifndef BR_USE_WIN32_RAND +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_RAND 1 +#endif +#endif + +/* + * POWER8 crypto support. We rely on compiler macros for the + * architecture, since we do not have a reliable, simple way to detect + * the required support at runtime (we could try running an opcode, and + * trapping the exception or signal on illegal instruction, but this + * induces some non-trivial OS dependencies that we would prefer to + * avoid if possible). + */ +#ifndef BR_POWER8 +#if __GNUC__ && ((_ARCH_PWR8 || _ARCH_PPC) && __CRYPTO__) +#define BR_POWER8 1 +#endif +#endif + +/* + * Detect endinanness on POWER8. + */ +#if BR_POWER8 +#if defined BR_POWER8_LE +#undef BR_POWER8_BE +#if BR_POWER8_LE +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_BE 1 +#endif +#elif defined BR_POWER8_BE +#undef BR_POWER8_LE +#if BR_POWER8_BE +#define BR_POWER8_LE 0 +#else +#define BR_POWER8_LE 1 +#endif +#else +#if __LITTLE_ENDIAN__ +#define BR_POWER8_LE 1 +#define BR_POWER8_BE 0 +#else +#define BR_POWER8_LE 0 +#define BR_POWER8_BE 1 +#endif +#endif +#endif + +/* + * Detect support for 128-bit integers. + */ +#if !defined BR_INT128 && !defined BR_UMUL128 +#ifdef __SIZEOF_INT128__ +#define BR_INT128 1 +#elif _M_X64 +#define BR_UMUL128 1 +#endif +#endif + +/* + * Detect support for unaligned accesses with known endianness. + * + * x86 (both 32-bit and 64-bit) is little-endian and allows unaligned + * accesses. + * + * POWER/PowerPC allows unaligned accesses when big-endian. POWER8 and + * later also allow unaligned accesses when little-endian. + */ +#if !defined BR_LE_UNALIGNED && !defined BR_BE_UNALIGNED + +#if __i386 || __i386__ || __x86_64__ || _M_IX86 || _M_X64 +#define BR_LE_UNALIGNED 1 +#elif BR_POWER8_BE +#define BR_BE_UNALIGNED 1 +#elif BR_POWER8_LE +#define BR_LE_UNALIGNED 1 +#elif (__powerpc__ || __powerpc64__ || _M_PPC || _ARCH_PPC || _ARCH_PPC64) \ + && __BIG_ENDIAN__ +#define BR_BE_UNALIGNED 1 +#endif + +#endif + +/* + * Detect support for an OS-provided time source. + */ + +#ifndef BR_USE_UNIX_TIME +/*#if defined __unix__ || defined __linux__ || defined ESP8266 || defined ESP32\*/ +// Tasmota now uses its own NTP client so we need to pass explicitly the current time +#if defined __unix__ || defined __linux__ || defined ESP32\ + || defined _POSIX_SOURCE || defined _POSIX_C_SOURCE \ + || (defined __APPLE__ && defined __MACH__) +#define BR_USE_UNIX_TIME 1 +#endif +#endif + +#ifndef BR_USE_WIN32_TIME +#if defined _WIN32 || defined _WIN64 +#define BR_USE_WIN32_TIME 1 +#endif +#endif + +/* ==================================================================== */ +/* + * Encoding/decoding functions. + * + * 32-bit and 64-bit decoding, both little-endian and big-endian, is + * implemented with the inline functions below. + * + * When allowed by some compile-time options (autodetected or provided), + * optimised code is used, to perform direct memory access when the + * underlying architecture supports it, both for endianness and + * alignment. This, however, may trigger strict aliasing issues; the + * code below uses unions to perform (supposedly) safe type punning. + * Since the C aliasing rules are relatively complex and were amended, + * or at least re-explained with different phrasing, in all successive + * versions of the C standard, it is always a bit risky to bet that any + * specific version of a C compiler got it right, for some notion of + * "right". + */ + +typedef union { + uint16_t u; + unsigned char b[sizeof(uint16_t)]; +} br_union_u16; + +typedef union { + uint32_t u; + unsigned char b[sizeof(uint32_t)]; +} br_union_u32; + +typedef union { + uint64_t u; + unsigned char b[sizeof(uint64_t)]; +} br_union_u64; + +static inline void +br_enc16le(void *dst, unsigned x) +{ +#if BR_LE_UNALIGNED + ((br_union_u16 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)x; + buf[1] = (unsigned char)(x >> 8); +#endif +} + +static inline void +br_enc16be(void *dst, unsigned x) +{ +#if BR_BE_UNALIGNED + ((br_union_u16 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)(x >> 8); + buf[1] = (unsigned char)x; +#endif +} + +static inline unsigned +br_dec16le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u16 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (unsigned)buf[0] | ((unsigned)buf[1] << 8); +#endif +} + +static inline unsigned +br_dec16be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u16 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((unsigned)buf[0] << 8) | (unsigned)buf[1]; +#endif +} + +static inline void +br_enc32le(void *dst, uint32_t x) +{ +#if BR_LE_UNALIGNED + ((br_union_u32 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)x; + buf[1] = (unsigned char)(x >> 8); + buf[2] = (unsigned char)(x >> 16); + buf[3] = (unsigned char)(x >> 24); +#endif +} + +static inline void +br_enc32be(void *dst, uint32_t x) +{ +#if BR_BE_UNALIGNED + ((br_union_u32 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + buf[0] = (unsigned char)(x >> 24); + buf[1] = (unsigned char)(x >> 16); + buf[2] = (unsigned char)(x >> 8); + buf[3] = (unsigned char)x; +#endif +} + +static inline uint32_t +br_dec32le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u32 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (uint32_t)buf[0] + | ((uint32_t)buf[1] << 8) + | ((uint32_t)buf[2] << 16) + | ((uint32_t)buf[3] << 24); +#endif +} + +static inline uint32_t +br_dec32be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u32 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((uint32_t)buf[0] << 24) + | ((uint32_t)buf[1] << 16) + | ((uint32_t)buf[2] << 8) + | (uint32_t)buf[3]; +#endif +} + +static inline void +br_enc64le(void *dst, uint64_t x) +{ +#if BR_LE_UNALIGNED + ((br_union_u64 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + br_enc32le(buf, (uint32_t)x); + br_enc32le(buf + 4, (uint32_t)(x >> 32)); +#endif +} + +static inline void +br_enc64be(void *dst, uint64_t x) +{ +#if BR_BE_UNALIGNED + ((br_union_u64 *)dst)->u = x; +#else + unsigned char *buf; + + buf = dst; + br_enc32be(buf, (uint32_t)(x >> 32)); + br_enc32be(buf + 4, (uint32_t)x); +#endif +} + +static inline uint64_t +br_dec64le(const void *src) +{ +#if BR_LE_UNALIGNED + return ((const br_union_u64 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return (uint64_t)br_dec32le(buf) + | ((uint64_t)br_dec32le(buf + 4) << 32); +#endif +} + +static inline uint64_t +br_dec64be(const void *src) +{ +#if BR_BE_UNALIGNED + return ((const br_union_u64 *)src)->u; +#else + const unsigned char *buf; + + buf = src; + return ((uint64_t)br_dec32be(buf) << 32) + | (uint64_t)br_dec32be(buf + 4); +#endif +} + +/* + * Range decoding and encoding (for several successive values). + */ +void br_range_dec16le(uint16_t *v, size_t num, const void *src); +void br_range_dec16be(uint16_t *v, size_t num, const void *src); +void br_range_enc16le(void *dst, const uint16_t *v, size_t num); +void br_range_enc16be(void *dst, const uint16_t *v, size_t num); + +void br_range_dec32le(uint32_t *v, size_t num, const void *src); +void br_range_dec32be(uint32_t *v, size_t num, const void *src); +void br_range_enc32le(void *dst, const uint32_t *v, size_t num); +void br_range_enc32be(void *dst, const uint32_t *v, size_t num); + +void br_range_dec64le(uint64_t *v, size_t num, const void *src); +void br_range_dec64be(uint64_t *v, size_t num, const void *src); +void br_range_enc64le(void *dst, const uint64_t *v, size_t num); +void br_range_enc64be(void *dst, const uint64_t *v, size_t num); + +/* + * Byte-swap a 32-bit integer. + */ +static inline uint32_t +br_swap32(uint32_t x) +{ + x = ((x & (uint32_t)0x00FF00FF) << 8) + | ((x >> 8) & (uint32_t)0x00FF00FF); + return (x << 16) | (x >> 16); +} + +/* ==================================================================== */ +/* + * Support code for hash functions. + */ + +/* + * IV for MD5, SHA-1, SHA-224 and SHA-256. + */ +extern const uint32_t br_md5_IV[]; +extern const uint32_t br_sha1_IV[]; +extern const uint32_t br_sha224_IV[]; +extern const uint32_t br_sha256_IV[]; + +/* + * Round functions for MD5, SHA-1, SHA-224 and SHA-256 (SHA-224 and + * SHA-256 use the same round function). + */ +void br_md5_round(const unsigned char *buf, uint32_t *val); +void br_sha1_round(const unsigned char *buf, uint32_t *val); +void br_sha2small_round(const unsigned char *buf, uint32_t *val); + +/* + * The core function for the TLS PRF. It computes + * P_hash(secret, label + seed), and XORs the result into the dst buffer. + */ +void br_tls_phash(void *dst, size_t len, + const br_hash_class *dig, + const void *secret, size_t secret_len, const char *label, + size_t seed_num, const br_tls_prf_seed_chunk *seed); + +/* + * Copy all configured hash implementations from a multihash context + * to another. + */ +static inline void +br_multihash_copyimpl(br_multihash_context *dst, + const br_multihash_context *src) +{ + memcpy((void *)dst->impl, src->impl, sizeof src->impl); +} + +/* ==================================================================== */ +/* + * Constant-time primitives. These functions manipulate 32-bit values in + * order to provide constant-time comparisons and multiplexers. + * + * Boolean values (the "ctl" bits) MUST have value 0 or 1. + * + * Implementation notes: + * ===================== + * + * The uintN_t types are unsigned and with width exactly N bits; the C + * standard guarantees that computations are performed modulo 2^N, and + * there can be no overflow. Negation (unary '-') works on unsigned types + * as well. + * + * The intN_t types are guaranteed to have width exactly N bits, with no + * padding bit, and using two's complement representation. Casting + * intN_t to uintN_t really is conversion modulo 2^N. Beware that intN_t + * types, being signed, trigger implementation-defined behaviour on + * overflow (including raising some signal): with GCC, while modular + * arithmetics are usually applied, the optimizer may assume that + * overflows don't occur (unless the -fwrapv command-line option is + * added); Clang has the additional -ftrapv option to explicitly trap on + * integer overflow or underflow. + */ + +/* + * Negate a boolean. + */ +static inline uint32_t +NOT(uint32_t ctl) +{ + return ctl ^ 1; +} + +/* + * Multiplexer: returns x if ctl == 1, y if ctl == 0. + */ +static inline uint32_t +MUX(uint32_t ctl, uint32_t x, uint32_t y) +{ + return y ^ (-ctl & (x ^ y)); +} + +/* + * Equality check: returns 1 if x == y, 0 otherwise. + */ +static inline uint32_t +EQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return NOT((q | -q) >> 31); +} + +/* + * Inequality check: returns 1 if x != y, 0 otherwise. + */ +static inline uint32_t +NEQ(uint32_t x, uint32_t y) +{ + uint32_t q; + + q = x ^ y; + return (q | -q) >> 31; +} + +/* + * Comparison: returns 1 if x > y, 0 otherwise. + */ +static inline uint32_t +GT(uint32_t x, uint32_t y) +{ + /* + * If both x < 2^31 and x < 2^31, then y-x will have its high + * bit set if x > y, cleared otherwise. + * + * If either x >= 2^31 or y >= 2^31 (but not both), then the + * result is the high bit of x. + * + * If both x >= 2^31 and y >= 2^31, then we can virtually + * subtract 2^31 from both, and we are back to the first case. + * Since (y-2^31)-(x-2^31) = y-x, the subtraction is already + * fine. + */ + uint32_t z; + + z = y - x; + return (z ^ ((x ^ y) & (x ^ z))) >> 31; +} + +/* + * Other comparisons (greater-or-equal, lower-than, lower-or-equal). + */ +#define GE(x, y) NOT(GT(y, x)) +#define LT(x, y) GT(y, x) +#define LE(x, y) NOT(GT(x, y)) + +/* + * General comparison: returned value is -1, 0 or 1, depending on + * whether x is lower than, equal to, or greater than y. + */ +static inline int32_t +CMP(uint32_t x, uint32_t y) +{ + return (int32_t)GT(x, y) | -(int32_t)GT(y, x); +} + +/* + * Returns 1 if x == 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +EQ0(int32_t x) +{ + uint32_t q; + + q = (uint32_t)x; + return ~(q | -q) >> 31; +} + +/* + * Returns 1 if x > 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +GT0(int32_t x) +{ + /* + * High bit of -x is 0 if x == 0, but 1 if x > 0. + */ + uint32_t q; + + q = (uint32_t)x; + return (~q & -q) >> 31; +} + +/* + * Returns 1 if x >= 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +GE0(int32_t x) +{ + return ~(uint32_t)x >> 31; +} + +/* + * Returns 1 if x < 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +LT0(int32_t x) +{ + return (uint32_t)x >> 31; +} + +/* + * Returns 1 if x <= 0, 0 otherwise. Take care that the operand is signed. + */ +static inline uint32_t +LE0(int32_t x) +{ + uint32_t q; + + /* + * ~-x has its high bit set if and only if -x is nonnegative (as + * a signed int), i.e. x is in the -(2^31-1) to 0 range. We must + * do an OR with x itself to account for x = -2^31. + */ + q = (uint32_t)x; + return (q | ~-q) >> 31; +} + +/* + * Conditional copy: src[] is copied into dst[] if and only if ctl is 1. + * dst[] and src[] may overlap completely (but not partially). + */ +void br_ccopy(uint32_t ctl, void *dst, const void *src, size_t len); + +#define CCOPY br_ccopy + +/* + * Compute the bit length of a 32-bit integer. Returned value is between 0 + * and 32 (inclusive). + */ +static inline uint32_t +BIT_LENGTH(uint32_t x) +{ + uint32_t k, c; + + k = NEQ(x, 0); + c = GT(x, 0xFFFF); x = MUX(c, x >> 16, x); k += c << 4; + c = GT(x, 0x00FF); x = MUX(c, x >> 8, x); k += c << 3; + c = GT(x, 0x000F); x = MUX(c, x >> 4, x); k += c << 2; + c = GT(x, 0x0003); x = MUX(c, x >> 2, x); k += c << 1; + k += GT(x, 0x0001); + return k; +} + +/* + * Compute the minimum of x and y. + */ +static inline uint32_t +MIN(uint32_t x, uint32_t y) +{ + return MUX(GT(x, y), y, x); +} + +/* + * Compute the maximum of x and y. + */ +static inline uint32_t +MAX(uint32_t x, uint32_t y) +{ + return MUX(GT(x, y), x, y); +} + +/* + * Multiply two 32-bit integers, with a 64-bit result. This default + * implementation assumes that the basic multiplication operator + * yields constant-time code. + */ +#define MUL(x, y) ((uint64_t)(x) * (uint64_t)(y)) + +#if BR_CT_MUL31 + +/* + * Alternate implementation of MUL31, that will be constant-time on some + * (old) platforms where the default MUL31 is not. Unfortunately, it is + * also substantially slower, and yields larger code, on more modern + * platforms, which is why it is deactivated by default. + * + * MUL31_lo() must do some extra work because on some platforms, the + * _signed_ multiplication may return early if the top bits are 1. + * Simply truncating (casting) the output of MUL31() would not be + * sufficient, because the compiler may notice that we keep only the low + * word, and then replace automatically the unsigned multiplication with + * a signed multiplication opcode. + */ +#define MUL31(x, y) ((uint64_t)((x) | (uint32_t)0x80000000) \ + * (uint64_t)((y) | (uint32_t)0x80000000) \ + - ((uint64_t)(x) << 31) - ((uint64_t)(y) << 31) \ + - ((uint64_t)1 << 62)) +static inline uint32_t +MUL31_lo(uint32_t x, uint32_t y) +{ + uint32_t xl, xh; + uint32_t yl, yh; + + xl = (x & 0xFFFF) | (uint32_t)0x80000000; + xh = (x >> 16) | (uint32_t)0x80000000; + yl = (y & 0xFFFF) | (uint32_t)0x80000000; + yh = (y >> 16) | (uint32_t)0x80000000; + return (xl * yl + ((xl * yh + xh * yl) << 16)) & (uint32_t)0x7FFFFFFF; +} + +#else + +/* + * Multiply two 31-bit integers, with a 62-bit result. This default + * implementation assumes that the basic multiplication operator + * yields constant-time code. + * The MUL31_lo() macro returns only the low 31 bits of the product. + */ +#define MUL31(x, y) ((uint64_t)(x) * (uint64_t)(y)) +#define MUL31_lo(x, y) (((uint32_t)(x) * (uint32_t)(y)) & (uint32_t)0x7FFFFFFF) + +#endif + +/* + * Multiply two words together; the sum of the lengths of the two + * operands must not exceed 31 (for instance, one operand may use 16 + * bits if the other fits on 15). If BR_CT_MUL15 is non-zero, then the + * macro will contain some extra operations that help in making the + * operation constant-time on some platforms, where the basic 32-bit + * multiplication is not constant-time. + */ +#if BR_CT_MUL15 +#define MUL15(x, y) (((uint32_t)(x) | (uint32_t)0x80000000) \ + * ((uint32_t)(y) | (uint32_t)0x80000000) \ + & (uint32_t)0x7FFFFFFF) +#else +#define MUL15(x, y) ((uint32_t)(x) * (uint32_t)(y)) +#endif + +/* + * Arithmetic right shift (sign bit is copied). What happens when + * right-shifting a negative value is _implementation-defined_, so it + * does not trigger undefined behaviour, but it is still up to each + * compiler to define (and document) what it does. Most/all compilers + * will do an arithmetic shift, the sign bit being used to fill the + * holes; this is a native operation on the underlying CPU, and it would + * make little sense for the compiler to do otherwise. GCC explicitly + * documents that it follows that convention. + * + * Still, if BR_NO_ARITH_SHIFT is defined (and non-zero), then an + * alternate version will be used, that does not rely on such + * implementation-defined behaviour. Unfortunately, it is also slower + * and yields bigger code, which is why it is deactivated by default. + */ +#if BR_NO_ARITH_SHIFT +#define ARSH(x, n) (((uint32_t)(x) >> (n)) \ + | ((-((uint32_t)(x) >> 31)) << (32 - (n)))) +#else +#define ARSH(x, n) ((*(int32_t *)&(x)) >> (n)) +#endif + +/* + * Constant-time division. The dividend hi:lo is divided by the + * divisor d; the quotient is returned and the remainder is written + * in *r. If hi == d, then the quotient does not fit on 32 bits; + * returned value is thus truncated. If hi > d, returned values are + * indeterminate. + */ +uint32_t br_divrem(uint32_t hi, uint32_t lo, uint32_t d, uint32_t *r); + +/* + * Wrapper for br_divrem(); the remainder is returned, and the quotient + * is discarded. + */ +static inline uint32_t +br_rem(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + br_divrem(hi, lo, d, &r); + return r; +} + +/* + * Wrapper for br_divrem(); the quotient is returned, and the remainder + * is discarded. + */ +static inline uint32_t +br_div(uint32_t hi, uint32_t lo, uint32_t d) +{ + uint32_t r; + + return br_divrem(hi, lo, d, &r); +} + +/* ==================================================================== */ + +/* + * Integers 'i32' + * -------------- + * + * The 'i32' functions implement computations on big integers using + * an internal representation as an array of 32-bit integers. For + * an array x[]: + * -- x[0] contains the "announced bit length" of the integer + * -- x[1], x[2]... contain the value in little-endian order (x[1] + * contains the least significant 32 bits) + * + * Multiplications rely on the elementary 32x32->64 multiplication. + * + * The announced bit length specifies the number of bits that are + * significant in the subsequent 32-bit words. Unused bits in the + * last (most significant) word are set to 0; subsequent words are + * uninitialized and need not exist at all. + * + * The execution time and memory access patterns of all computations + * depend on the announced bit length, but not on the actual word + * values. For modular integers, the announced bit length of any integer + * modulo n is equal to the actual bit length of n; thus, computations + * on modular integers are "constant-time" (only the modulus length may + * leak). + */ + +/* + * Compute the actual bit length of an integer. The argument x should + * point to the first (least significant) value word of the integer. + * The len 'xlen' contains the number of 32-bit words to access. + * + * CT: value or length of x does not leak. + */ +uint32_t br_i32_bit_length(uint32_t *x, size_t xlen); + +/* + * Decode an integer from its big-endian unsigned representation. The + * "true" bit length of the integer is computed, but all words of x[] + * corresponding to the full 'len' bytes of the source are set. + * + * CT: value or length of x does not leak. + */ +void br_i32_decode(uint32_t *x, const void *src, size_t len); + +/* + * Decode an integer from its big-endian unsigned representation. The + * integer MUST be lower than m[]; the announced bit length written in + * x[] will be equal to that of m[]. All 'len' bytes from the source are + * read. + * + * Returned value is 1 if the decode value fits within the modulus, 0 + * otherwise. In the latter case, the x[] buffer will be set to 0 (but + * still with the announced bit length of m[]). + * + * CT: value or length of x does not leak. Memory access pattern depends + * only of 'len' and the announced bit length of m. Whether x fits or + * not does not leak either. + */ +uint32_t br_i32_decode_mod(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Reduce an integer (a[]) modulo another (m[]). The result is written + * in x[] and its announced bit length is set to be equal to that of m[]. + * + * x[] MUST be distinct from a[] and m[]. + * + * CT: only announced bit lengths leak, not values of x, a or m. + */ +void br_i32_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m); + +/* + * Decode an integer from its big-endian unsigned representation, and + * reduce it modulo the provided modulus m[]. The announced bit length + * of the result is set to be equal to that of the modulus. + * + * x[] MUST be distinct from m[]. + */ +void br_i32_decode_reduce(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Encode an integer into its big-endian unsigned representation. The + * output length in bytes is provided (parameter 'len'); if the length + * is too short then the integer is appropriately truncated; if it is + * too long then the extra bytes are set to 0. + */ +void br_i32_encode(void *dst, size_t len, const uint32_t *x); + +/* + * Multiply x[] by 2^32 and then add integer z, modulo m[]. This + * function assumes that x[] and m[] have the same announced bit + * length, and the announced bit length of m[] matches its true + * bit length. + * + * x[] and m[] MUST be distinct arrays. + * + * CT: only the common announced bit length of x and m leaks, not + * the values of x, z or m. + */ +void br_i32_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); + +/* + * Extract one word from an integer. The offset is counted in bits. + * The word MUST entirely fit within the word elements corresponding + * to the announced bit length of a[]. + */ +static inline uint32_t +br_i32_word(const uint32_t *a, uint32_t off) +{ + size_t u; + unsigned j; + + u = (size_t)(off >> 5) + 1; + j = (unsigned)off & 31; + if (j == 0) { + return a[u]; + } else { + return (a[u] >> j) | (a[u + 1] << (32 - j)); + } +} + +/* + * Test whether an integer is zero. + */ +uint32_t br_i32_iszero(const uint32_t *x); + +/* + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] + * is unmodified, but the carry is still computed and returned. The + * arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i32_add(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, + * then a[] is unmodified, but the carry is still computed and returned. + * The arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i32_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Compute d+a*b, result in d. The initial announced bit length of d[] + * MUST match that of a[]. The d[] array MUST be large enough to + * accommodate the full result, plus (possibly) an extra word. The + * resulting announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[] (therefore, it may be larger than the actual + * bit length of the numerical result). + * + * a[] and b[] may be the same array. d[] must be disjoint from both a[] + * and b[]. + */ +void br_i32_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b); + +/* + * Zeroize an integer. The announced bit length is set to the provided + * value, and the corresponding words are set to 0. + */ +static inline void +br_i32_zero(uint32_t *x, uint32_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); +} + +/* + * Compute -(1/x) mod 2^32. If x is even, then this function returns 0. + */ +uint32_t br_i32_ninv32(uint32_t x); + +/* + * Convert a modular integer to Montgomery representation. The integer x[] + * MUST be lower than m[], but with the same announced bit length. + */ +void br_i32_to_monty(uint32_t *x, const uint32_t *m); + +/* + * Convert a modular integer back from Montgomery representation. The + * integer x[] MUST be lower than m[], but with the same announced bit + * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is + * the least significant value word of m[] (this works only if m[] is + * an odd integer). + */ +void br_i32_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular Montgomery multiplication. d[] is filled with the + * value of x*y/R modulo m[] (where R is the Montgomery factor). The + * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be + * numerically lower than m[]. x[] and y[] MAY be the same array. The + * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). + */ +void br_i32_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The t1[] and t2[] parameters must be temporary arrays, + * each large enough to accommodate an integer with the same size as m[]. + */ +void br_i32_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2); + +/* ==================================================================== */ + +/* + * Integers 'i31' + * -------------- + * + * The 'i31' functions implement computations on big integers using + * an internal representation as an array of 32-bit integers. For + * an array x[]: + * -- x[0] encodes the array length and the "announced bit length" + * of the integer: namely, if the announced bit length is k, + * then x[0] = ((k / 31) << 5) + (k % 31). + * -- x[1], x[2]... contain the value in little-endian order, 31 + * bits per word (x[1] contains the least significant 31 bits). + * The upper bit of each word is 0. + * + * Multiplications rely on the elementary 32x32->64 multiplication. + * + * The announced bit length specifies the number of bits that are + * significant in the subsequent 32-bit words. Unused bits in the + * last (most significant) word are set to 0; subsequent words are + * uninitialized and need not exist at all. + * + * The execution time and memory access patterns of all computations + * depend on the announced bit length, but not on the actual word + * values. For modular integers, the announced bit length of any integer + * modulo n is equal to the actual bit length of n; thus, computations + * on modular integers are "constant-time" (only the modulus length may + * leak). + */ + +/* + * Test whether an integer is zero. + */ +uint32_t br_i31_iszero(const uint32_t *x); + +/* + * Add b[] to a[] and return the carry (0 or 1). If ctl is 0, then a[] + * is unmodified, but the carry is still computed and returned. The + * arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i31_add(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Subtract b[] from a[] and return the carry (0 or 1). If ctl is 0, + * then a[] is unmodified, but the carry is still computed and returned. + * The arrays a[] and b[] MUST have the same announced bit length. + * + * a[] and b[] MAY be the same array, but partial overlap is not allowed. + */ +uint32_t br_i31_sub(uint32_t *a, const uint32_t *b, uint32_t ctl); + +/* + * Compute the ENCODED actual bit length of an integer. The argument x + * should point to the first (least significant) value word of the + * integer. The len 'xlen' contains the number of 32-bit words to + * access. The upper bit of each value word MUST be 0. + * Returned value is ((k / 31) << 5) + (k % 31) if the bit length is k. + * + * CT: value or length of x does not leak. + */ +uint32_t br_i31_bit_length(uint32_t *x, size_t xlen); + +/* + * Decode an integer from its big-endian unsigned representation. The + * "true" bit length of the integer is computed and set in the encoded + * announced bit length (x[0]), but all words of x[] corresponding to + * the full 'len' bytes of the source are set. + * + * CT: value or length of x does not leak. + */ +void br_i31_decode(uint32_t *x, const void *src, size_t len); + +/* + * Decode an integer from its big-endian unsigned representation. The + * integer MUST be lower than m[]; the (encoded) announced bit length + * written in x[] will be equal to that of m[]. All 'len' bytes from the + * source are read. + * + * Returned value is 1 if the decode value fits within the modulus, 0 + * otherwise. In the latter case, the x[] buffer will be set to 0 (but + * still with the announced bit length of m[]). + * + * CT: value or length of x does not leak. Memory access pattern depends + * only of 'len' and the announced bit length of m. Whether x fits or + * not does not leak either. + */ +uint32_t br_i31_decode_mod(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Zeroize an integer. The announced bit length is set to the provided + * value, and the corresponding words are set to 0. The ENCODED bit length + * is expected here. + */ +static inline void +br_i31_zero(uint32_t *x, uint32_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 31) >> 5) * sizeof *x); +} + +/* + * Right-shift an integer. The shift amount must be lower than 31 + * bits. + */ +void br_i31_rshift(uint32_t *x, int count); + +/* + * Reduce an integer (a[]) modulo another (m[]). The result is written + * in x[] and its announced bit length is set to be equal to that of m[]. + * + * x[] MUST be distinct from a[] and m[]. + * + * CT: only announced bit lengths leak, not values of x, a or m. + */ +void br_i31_reduce(uint32_t *x, const uint32_t *a, const uint32_t *m); + +/* + * Decode an integer from its big-endian unsigned representation, and + * reduce it modulo the provided modulus m[]. The announced bit length + * of the result is set to be equal to that of the modulus. + * + * x[] MUST be distinct from m[]. + */ +void br_i31_decode_reduce(uint32_t *x, + const void *src, size_t len, const uint32_t *m); + +/* + * Multiply x[] by 2^31 and then add integer z, modulo m[]. This + * function assumes that x[] and m[] have the same announced bit + * length, the announced bit length of m[] matches its true + * bit length. + * + * x[] and m[] MUST be distinct arrays. z MUST fit in 31 bits (upper + * bit set to 0). + * + * CT: only the common announced bit length of x and m leaks, not + * the values of x, z or m. + */ +void br_i31_muladd_small(uint32_t *x, uint32_t z, const uint32_t *m); + +/* + * Encode an integer into its big-endian unsigned representation. The + * output length in bytes is provided (parameter 'len'); if the length + * is too short then the integer is appropriately truncated; if it is + * too long then the extra bytes are set to 0. + */ +void br_i31_encode(void *dst, size_t len, const uint32_t *x); + +/* + * Compute -(1/x) mod 2^31. If x is even, then this function returns 0. + */ +uint32_t br_i31_ninv31(uint32_t x); + +/* + * Compute a modular Montgomery multiplication. d[] is filled with the + * value of x*y/R modulo m[] (where R is the Montgomery factor). The + * array d[] MUST be distinct from x[], y[] and m[]. x[] and y[] MUST be + * numerically lower than m[]. x[] and y[] MAY be the same array. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). + */ +void br_i31_montymul(uint32_t *d, const uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i); + +/* + * Convert a modular integer to Montgomery representation. The integer x[] + * MUST be lower than m[], but with the same announced bit length. + */ +void br_i31_to_monty(uint32_t *x, const uint32_t *m); + +/* + * Convert a modular integer back from Montgomery representation. The + * integer x[] MUST be lower than m[], but with the same announced bit + * length. The "m0i" parameter is equal to -(1/m0) mod 2^32, where m0 is + * the least significant value word of m[] (this works only if m[] is + * an odd integer). + */ +void br_i31_from_monty(uint32_t *x, const uint32_t *m, uint32_t m0i); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The t1[] and t2[] parameters must be temporary arrays, + * each large enough to accommodate an integer with the same size as m[]. + */ +void br_i31_modpow(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *t1, uint32_t *t2); + +/* + * Compute a modular exponentiation. x[] MUST be an integer modulo m[] + * (same announced bit length, lower value). m[] MUST be odd. The + * exponent is in big-endian unsigned notation, over 'elen' bytes. The + * "m0i" parameter is equal to -(1/m0) mod 2^31, where m0 is the least + * significant value word of m[] (this works only if m[] is an odd + * integer). The tmp[] array is used for temporaries, and has size + * 'twlen' words; it must be large enough to accommodate at least two + * temporary values with the same size as m[] (including the leading + * "bit length" word). If there is room for more temporaries, then this + * function may use the extra room for window-based optimisation, + * resulting in faster computations. + * + * Returned value is 1 on success, 0 on error. An error is reported if + * the provided tmp[] array is too short. + */ +uint32_t br_i31_modpow_opt(uint32_t *x, const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* + * Compute d+a*b, result in d. The initial announced bit length of d[] + * MUST match that of a[]. The d[] array MUST be large enough to + * accommodate the full result, plus (possibly) an extra word. The + * resulting announced bit length of d[] will be the sum of the announced + * bit lengths of a[] and b[] (therefore, it may be larger than the actual + * bit length of the numerical result). + * + * a[] and b[] may be the same array. d[] must be disjoint from both a[] + * and b[]. + */ +void br_i31_mulacc(uint32_t *d, const uint32_t *a, const uint32_t *b); + +/* + * Compute x/y mod m, result in x. Values x and y must be between 0 and + * m-1, and have the same announced bit length as m. Modulus m must be + * odd. The "m0i" parameter is equal to -1/m mod 2^31. The array 't' + * must point to a temporary area that can hold at least three integers + * of the size of m. + * + * m may not overlap x and y. x and y may overlap each other (this can + * be useful to test whether a value is invertible modulo m). t must be + * disjoint from all other arrays. + * + * Returned value is 1 on success, 0 otherwise. Success is attained if + * y is invertible modulo m. + */ +uint32_t br_i31_moddiv(uint32_t *x, const uint32_t *y, + const uint32_t *m, uint32_t m0i, uint32_t *t); + +/* ==================================================================== */ + +/* + * FIXME: document "i15" functions. + */ + +static inline void +br_i15_zero(uint16_t *x, uint16_t bit_len) +{ + *x ++ = bit_len; + memset(x, 0, ((bit_len + 15) >> 4) * sizeof *x); +} + +uint32_t br_i15_iszero(const uint16_t *x); + +uint16_t br_i15_ninv15(uint16_t x); + +uint32_t br_i15_add(uint16_t *a, const uint16_t *b, uint32_t ctl); + +uint32_t br_i15_sub(uint16_t *a, const uint16_t *b, uint32_t ctl); + +void br_i15_muladd_small(uint16_t *x, uint16_t z, const uint16_t *m); + +void br_i15_montymul(uint16_t *d, const uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i); + +void br_i15_to_monty(uint16_t *x, const uint16_t *m); + +void br_i15_modpow(uint16_t *x, const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *t1, uint16_t *t2); + +uint32_t br_i15_modpow_opt(uint16_t *x, const unsigned char *e, size_t elen, + const uint16_t *m, uint16_t m0i, uint16_t *tmp, size_t twlen); + +void br_i15_encode(void *dst, size_t len, const uint16_t *x); + +uint32_t br_i15_decode_mod(uint16_t *x, + const void *src, size_t len, const uint16_t *m); + +void br_i15_rshift(uint16_t *x, int count); + +uint32_t br_i15_bit_length(uint16_t *x, size_t xlen); + +void br_i15_decode(uint16_t *x, const void *src, size_t len); + +void br_i15_from_monty(uint16_t *x, const uint16_t *m, uint16_t m0i); + +void br_i15_decode_reduce(uint16_t *x, + const void *src, size_t len, const uint16_t *m); + +void br_i15_reduce(uint16_t *x, const uint16_t *a, const uint16_t *m); + +void br_i15_mulacc(uint16_t *d, const uint16_t *a, const uint16_t *b); + +uint32_t br_i15_moddiv(uint16_t *x, const uint16_t *y, + const uint16_t *m, uint16_t m0i, uint16_t *t); + +/* + * Variant of br_i31_modpow_opt() that internally uses 64x64->128 + * multiplications. It expects the same parameters as br_i31_modpow_opt(), + * except that the temporaries should be 64-bit integers, not 32-bit + * integers. + */ +uint32_t br_i62_modpow_opt(uint32_t *x31, const unsigned char *e, size_t elen, + const uint32_t *m31, uint32_t m0i31, uint64_t *tmp, size_t twlen); + +/* + * Type for a function with the same API as br_i31_modpow_opt() (some + * implementations of this type may have stricter alignment requirements + * on the temporaries). + */ +typedef uint32_t (*br_i31_modpow_opt_type)(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* + * Wrapper for br_i62_modpow_opt() that uses the same type as + * br_i31_modpow_opt(); however, it requires its 'tmp' argument to the + * 64-bit aligned. + */ +uint32_t br_i62_modpow_opt_as_i31(uint32_t *x, + const unsigned char *e, size_t elen, + const uint32_t *m, uint32_t m0i, uint32_t *tmp, size_t twlen); + +/* ==================================================================== */ + +static inline size_t +br_digest_size(const br_hash_class *digest_class) +{ + return (size_t)(digest_class->desc >> BR_HASHDESC_OUT_OFF) + & BR_HASHDESC_OUT_MASK; +} + +/* + * Get the output size (in bytes) of a hash function. + */ +size_t br_digest_size_by_ID(int digest_id); + +/* + * Get the OID (encoded OBJECT IDENTIFIER value, without tag and length) + * for a hash function. If digest_id is not a supported digest identifier + * (in particular if it is equal to 0, i.e. br_md5sha1_ID), then NULL is + * returned and *len is set to 0. + */ +const unsigned char *br_digest_OID(int digest_id, size_t *len); + +/* ==================================================================== */ +/* + * DES support functions. + */ + +/* + * Apply DES Initial Permutation. + */ +void br_des_do_IP(uint32_t *xl, uint32_t *xr); + +/* + * Apply DES Final Permutation (inverse of IP). + */ +void br_des_do_invIP(uint32_t *xl, uint32_t *xr); + +/* + * Key schedule unit: for a DES key (8 bytes), compute 16 subkeys. Each + * subkey is two 28-bit words represented as two 32-bit words; the PC-2 + * bit extration is NOT applied. + */ +void br_des_keysched_unit(uint32_t *skey, const void *key); + +/* + * Reversal of 16 DES sub-keys (for decryption). + */ +void br_des_rev_skey(uint32_t *skey); + +/* + * DES/3DES key schedule for 'des_tab' (encryption direction). Returned + * value is the number of rounds. + */ +unsigned br_des_tab_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * DES/3DES key schedule for 'des_ct' (encryption direction). Returned + * value is the number of rounds. + */ +unsigned br_des_ct_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * DES/3DES subkey decompression (from the compressed bitsliced subkeys). + */ +void br_des_ct_skey_expand(uint32_t *sk_exp, + unsigned num_rounds, const uint32_t *skey); + +/* + * DES/3DES block encryption/decryption ('des_tab'). + */ +void br_des_tab_process_block(unsigned num_rounds, + const uint32_t *skey, void *block); + +/* + * DES/3DES block encryption/decryption ('des_ct'). + */ +void br_des_ct_process_block(unsigned num_rounds, + const uint32_t *skey, void *block); + +/* ==================================================================== */ +/* + * AES support functions. + */ + +/* + * The AES S-box (256-byte table). + */ +extern const unsigned char br_aes_S[]; + +/* + * AES key schedule. skey[] is filled with n+1 128-bit subkeys, where n + * is the number of rounds (10 to 14, depending on key size). The number + * of rounds is returned. If the key size is invalid (not 16, 24 or 32), + * then 0 is returned. + * + * This implementation uses a 256-byte table and is NOT constant-time. + */ +unsigned br_aes_keysched(uint32_t *skey, const void *key, size_t key_len); + +/* + * AES key schedule for decryption ('aes_big' implementation). + */ +unsigned br_aes_big_keysched_inv(uint32_t *skey, + const void *key, size_t key_len); + +/* + * AES block encryption with the 'aes_big' implementation (fast, but + * not constant-time). This function encrypts a single block "in place". + */ +void br_aes_big_encrypt(unsigned num_rounds, const uint32_t *skey, void *data); + +/* + * AES block decryption with the 'aes_big' implementation (fast, but + * not constant-time). This function decrypts a single block "in place". + */ +void br_aes_big_decrypt(unsigned num_rounds, const uint32_t *skey, void *data); + +/* + * AES block encryption with the 'aes_small' implementation (small, but + * slow and not constant-time). This function encrypts a single block + * "in place". + */ +void br_aes_small_encrypt(unsigned num_rounds, + const uint32_t *skey, void *data); + +/* + * AES block decryption with the 'aes_small' implementation (small, but + * slow and not constant-time). This function decrypts a single block + * "in place". + */ +void br_aes_small_decrypt(unsigned num_rounds, + const uint32_t *skey, void *data); + +/* + * The constant-time implementation is "bitsliced": the 128-bit state is + * split over eight 32-bit words q* in the following way: + * + * -- Input block consists in 16 bytes: + * a00 a10 a20 a30 a01 a11 a21 a31 a02 a12 a22 a32 a03 a13 a23 a33 + * In the terminology of FIPS 197, this is a 4x4 matrix which is read + * column by column. + * + * -- Each byte is split into eight bits which are distributed over the + * eight words, at the same rank. Thus, for a byte x at rank k, bit 0 + * (least significant) of x will be at rank k in q0 (if that bit is b, + * then it contributes "b << k" to the value of q0), bit 1 of x will be + * at rank k in q1, and so on. + * + * -- Ranks given to bits are in "row order" and are either all even, or + * all odd. Two independent AES states are thus interleaved, one using + * the even ranks, the other the odd ranks. Row order means: + * a00 a01 a02 a03 a10 a11 a12 a13 a20 a21 a22 a23 a30 a31 a32 a33 + * + * Converting input bytes from two AES blocks to bitslice representation + * is done in the following way: + * -- Decode first block into the four words q0 q2 q4 q6, in that order, + * using little-endian convention. + * -- Decode second block into the four words q1 q3 q5 q7, in that order, + * using little-endian convention. + * -- Call br_aes_ct_ortho(). + * + * Converting back to bytes is done by using the reverse operations. Note + * that br_aes_ct_ortho() is its own inverse. + */ + +/* + * Perform bytewise orthogonalization of eight 32-bit words. Bytes + * of q0..q7 are spread over all words: for a byte x that occurs + * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit + * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j. + * + * This operation is an involution. + */ +void br_aes_ct_ortho(uint32_t *q); + +/* + * The AES S-box, as a bitsliced constant-time version. The input array + * consists in eight 32-bit words; 32 S-box instances are computed in + * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant) + * are spread over the words 0 to 7, at the same rank. + */ +void br_aes_ct_bitslice_Sbox(uint32_t *q); + +/* + * Like br_aes_bitslice_Sbox(), but for the inverse S-box. + */ +void br_aes_ct_bitslice_invSbox(uint32_t *q); + +/* + * Compute AES encryption on bitsliced data. Since input is stored on + * eight 32-bit words, two block encryptions are actually performed + * in parallel. + */ +void br_aes_ct_bitslice_encrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q); + +/* + * Compute AES decryption on bitsliced data. Since input is stored on + * eight 32-bit words, two block decryptions are actually performed + * in parallel. + */ +void br_aes_ct_bitslice_decrypt(unsigned num_rounds, + const uint32_t *skey, uint32_t *q); + +/* + * AES key schedule, constant-time version. skey[] is filled with n+1 + * 128-bit subkeys, where n is the number of rounds (10 to 14, depending + * on key size). The number of rounds is returned. If the key size is + * invalid (not 16, 24 or 32), then 0 is returned. + */ +unsigned br_aes_ct_keysched(uint32_t *comp_skey, + const void *key, size_t key_len); + +/* + * Expand AES subkeys as produced by br_aes_ct_keysched(), into + * a larger array suitable for br_aes_ct_bitslice_encrypt() and + * br_aes_ct_bitslice_decrypt(). + */ +void br_aes_ct_skey_expand(uint32_t *skey, + unsigned num_rounds, const uint32_t *comp_skey); + +/* + * For the ct64 implementation, the same bitslicing technique is used, + * but four instances are interleaved. First instance uses bits 0, 4, + * 8, 12,... of each word; second instance uses bits 1, 5, 9, 13,... + * and so on. + */ + +/* + * Perform bytewise orthogonalization of eight 64-bit words. Bytes + * of q0..q7 are spread over all words: for a byte x that occurs + * at rank i in q[j] (byte x uses bits 8*i to 8*i+7 in q[j]), the bit + * of rank k in x (0 <= k <= 7) goes to q[k] at rank 8*i+j. + * + * This operation is an involution. + */ +void br_aes_ct64_ortho(uint64_t *q); + +/* + * Interleave bytes for an AES input block. If input bytes are + * denoted 0123456789ABCDEF, and have been decoded with little-endian + * convention (w[0] contains 0123, with '3' being most significant; + * w[1] contains 4567, and so on), then output word q0 will be + * set to 08192A3B (again little-endian convention) and q1 will + * be set to 4C5D6E7F. + */ +void br_aes_ct64_interleave_in(uint64_t *q0, uint64_t *q1, const uint32_t *w); + +/* + * Perform the opposite of br_aes_ct64_interleave_in(). + */ +void br_aes_ct64_interleave_out(uint32_t *w, uint64_t q0, uint64_t q1); + +/* + * The AES S-box, as a bitsliced constant-time version. The input array + * consists in eight 64-bit words; 64 S-box instances are computed in + * parallel. Bits 0 to 7 of each S-box input (bit 0 is least significant) + * are spread over the words 0 to 7, at the same rank. + */ +void br_aes_ct64_bitslice_Sbox(uint64_t *q); + +/* + * Like br_aes_bitslice_Sbox(), but for the inverse S-box. + */ +void br_aes_ct64_bitslice_invSbox(uint64_t *q); + +/* + * Compute AES encryption on bitsliced data. Since input is stored on + * eight 64-bit words, four block encryptions are actually performed + * in parallel. + */ +void br_aes_ct64_bitslice_encrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q); + +/* + * Compute AES decryption on bitsliced data. Since input is stored on + * eight 64-bit words, four block decryptions are actually performed + * in parallel. + */ +void br_aes_ct64_bitslice_decrypt(unsigned num_rounds, + const uint64_t *skey, uint64_t *q); + +/* + * AES key schedule, constant-time version. skey[] is filled with n+1 + * 128-bit subkeys, where n is the number of rounds (10 to 14, depending + * on key size). The number of rounds is returned. If the key size is + * invalid (not 16, 24 or 32), then 0 is returned. + */ +unsigned br_aes_ct64_keysched(uint64_t *comp_skey, + const void *key, size_t key_len); + +/* + * Expand AES subkeys as produced by br_aes_ct64_keysched(), into + * a larger array suitable for br_aes_ct64_bitslice_encrypt() and + * br_aes_ct64_bitslice_decrypt(). + */ +void br_aes_ct64_skey_expand(uint64_t *skey, + unsigned num_rounds, const uint64_t *comp_skey); + +/* + * Test support for AES-NI opcodes. + */ +int br_aes_x86ni_supported(void); + +/* + * AES key schedule, using x86 AES-NI instructions. This yields the + * subkeys in the encryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_x86ni_keysched_enc(unsigned char *skni, + const void *key, size_t len); + +/* + * AES key schedule, using x86 AES-NI instructions. This yields the + * subkeys in the decryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_x86ni_keysched_dec(unsigned char *skni, + const void *key, size_t len); + +/* + * Test support for AES POWER8 opcodes. + */ +int br_aes_pwr8_supported(void); + +/* + * AES key schedule, using POWER8 instructions. This yields the + * subkeys in the encryption direction. Number of rounds is returned. + * Key size MUST be 16, 24 or 32 bytes; otherwise, 0 is returned. + */ +unsigned br_aes_pwr8_keysched(unsigned char *skni, + const void *key, size_t len); + +/* ==================================================================== */ +/* + * RSA. + */ + +/* + * Apply proper PKCS#1 v1.5 padding (for signatures). 'hash_oid' is + * the encoded hash function OID, or NULL. + */ +uint32_t br_rsa_pkcs1_sig_pad(const unsigned char *hash_oid, + const unsigned char *hash, size_t hash_len, + uint32_t n_bitlen, unsigned char *x); + +/* + * Check PKCS#1 v1.5 padding (for signatures). 'hash_oid' is the encoded + * hash function OID, or NULL. The provided 'sig' value is _after_ the + * modular exponentiation, i.e. it should be the padded hash. On + * success, the hashed message is extracted. + */ +uint32_t br_rsa_pkcs1_sig_unpad(const unsigned char *sig, size_t sig_len, + const unsigned char *hash_oid, size_t hash_len, + unsigned char *hash_out); + +/* + * Apply proper PSS padding. The 'x' buffer is output only: it + * receives the value that is to be exponentiated. + */ +uint32_t br_rsa_pss_sig_pad(const br_prng_class **rng, + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + uint32_t n_bitlen, unsigned char *x); + +/* + * Check PSS padding. The provided value is the one _after_ + * the modular exponentiation; it is modified by this function. + * This function infers the signature length from the public key + * size, i.e. it assumes that this has already been verified (as + * part of the exponentiation). + */ +uint32_t br_rsa_pss_sig_unpad( + const br_hash_class *hf_data, const br_hash_class *hf_mgf1, + const unsigned char *hash, size_t salt_len, + const br_rsa_public_key *pk, unsigned char *x); + +/* + * Apply OAEP padding. Returned value is the actual padded string length, + * or zero on error. + */ +size_t br_rsa_oaep_pad(const br_prng_class **rnd, const br_hash_class *dig, + const void *label, size_t label_len, const br_rsa_public_key *pk, + void *dst, size_t dst_nax_len, const void *src, size_t src_len); + +/* + * Unravel and check OAEP padding. If the padding is correct, then 1 is + * returned, '*len' is adjusted to the length of the message, and the + * data is moved to the start of the 'data' buffer. If the padding is + * incorrect, then 0 is returned and '*len' is untouched. Either way, + * the complete buffer contents are altered. + */ +uint32_t br_rsa_oaep_unpad(const br_hash_class *dig, + const void *label, size_t label_len, void *data, size_t *len); + +/* + * Compute MGF1 for a given seed, and XOR the output into the provided + * buffer. + */ +void br_mgf1_xor(void *data, size_t len, + const br_hash_class *dig, const void *seed, size_t seed_len); + +/* + * Inner function for RSA key generation; used by the "i31" and "i62" + * implementations. + */ +uint32_t br_rsa_i31_keygen_inner(const br_prng_class **rng, + br_rsa_private_key *sk, void *kbuf_priv, + br_rsa_public_key *pk, void *kbuf_pub, + unsigned size, uint32_t pubexp, br_i31_modpow_opt_type mp31); + +/* ==================================================================== */ +/* + * Elliptic curves. + */ + +/* + * Type for generic EC parameters: curve order (unsigned big-endian + * encoding) and encoded conventional generator. + */ +typedef struct { + int curve; + const unsigned char *order; + size_t order_len; + const unsigned char *generator; + size_t generator_len; +} br_ec_curve_def; + +extern const br_ec_curve_def br_secp256r1; +extern const br_ec_curve_def br_secp384r1; +extern const br_ec_curve_def br_secp521r1; + +/* + * For Curve25519, the advertised "order" really is 2^255-1, since the + * point multipliction function really works over arbitrary 255-bit + * scalars. This value is only meant as a hint for ECDH key generation; + * only ECDSA uses the exact curve order, and ECDSA is not used with + * that specific curve. + */ +extern const br_ec_curve_def br_curve25519; + +/* + * Decode some bytes as an i31 integer, with truncation (corresponding + * to the 'bits2int' operation in RFC 6979). The target ENCODED bit + * length is provided as last parameter. The resulting value will have + * this declared bit length, and consists the big-endian unsigned decoding + * of exactly that many bits in the source (capped at the source length). + */ +void br_ecdsa_i31_bits2int(uint32_t *x, + const void *src, size_t len, uint32_t ebitlen); + +/* + * Decode some bytes as an i15 integer, with truncation (corresponding + * to the 'bits2int' operation in RFC 6979). The target ENCODED bit + * length is provided as last parameter. The resulting value will have + * this declared bit length, and consists the big-endian unsigned decoding + * of exactly that many bits in the source (capped at the source length). + */ +void br_ecdsa_i15_bits2int(uint16_t *x, + const void *src, size_t len, uint32_t ebitlen); + +/* ==================================================================== */ +/* + * ASN.1 support functions. + */ + +/* + * A br_asn1_uint structure contains encoding information about an + * INTEGER nonnegative value: pointer to the integer contents (unsigned + * big-endian representation), length of the integer contents, + * and length of the encoded value. The data shall have minimal length: + * - If the integer value is zero, then 'len' must be zero. + * - If the integer value is not zero, then data[0] must be non-zero. + * + * Under these conditions, 'asn1len' is necessarily equal to either len + * or len+1. + */ +typedef struct { + const unsigned char *data; + size_t len; + size_t asn1len; +} br_asn1_uint; + +/* + * Given an encoded integer (unsigned big-endian, with possible leading + * bytes of value 0), returned the "prepared INTEGER" structure. + */ +br_asn1_uint br_asn1_uint_prepare(const void *xdata, size_t xlen); + +/* + * Encode an ASN.1 length. The length of the encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded length is still computed and returned. + */ +size_t br_asn1_encode_length(void *dest, size_t len); + +/* + * Convenient macro for computing lengths of lengths. + */ +#define len_of_len(len) br_asn1_encode_length(NULL, len) + +/* + * Encode a (prepared) ASN.1 INTEGER. The encoded length is returned. + * If 'dest' is NULL, then no encoding is performed, but the length of + * the encoded integer is still computed and returned. + */ +size_t br_asn1_encode_uint(void *dest, br_asn1_uint pp); + +/* + * Get the OID that identifies an elliptic curve. Returned value is + * the DER-encoded OID, with the length (always one byte) but without + * the tag. Thus, the first byte of the returned buffer contains the + * number of subsequent bytes in the value. If the curve is not + * recognised, NULL is returned. + */ +const unsigned char *br_get_curve_OID(int curve); + +/* + * Inner function for EC private key encoding. This is equivalent to + * the API function br_encode_ec_raw_der(), except for an extra + * parameter: if 'include_curve_oid' is zero, then the curve OID is + * _not_ included in the output blob (this is for PKCS#8 support). + */ +size_t br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid); + +/* ==================================================================== */ +/* + * SSL/TLS support functions. + */ + +/* + * Record types. + */ +#define BR_SSL_CHANGE_CIPHER_SPEC 20 +#define BR_SSL_ALERT 21 +#define BR_SSL_HANDSHAKE 22 +#define BR_SSL_APPLICATION_DATA 23 + +/* + * Handshake message types. + */ +#define BR_SSL_HELLO_REQUEST 0 +#define BR_SSL_CLIENT_HELLO 1 +#define BR_SSL_SERVER_HELLO 2 +#define BR_SSL_CERTIFICATE 11 +#define BR_SSL_SERVER_KEY_EXCHANGE 12 +#define BR_SSL_CERTIFICATE_REQUEST 13 +#define BR_SSL_SERVER_HELLO_DONE 14 +#define BR_SSL_CERTIFICATE_VERIFY 15 +#define BR_SSL_CLIENT_KEY_EXCHANGE 16 +#define BR_SSL_FINISHED 20 + +/* + * Alert levels. + */ +#define BR_LEVEL_WARNING 1 +#define BR_LEVEL_FATAL 2 + +/* + * Low-level I/O state. + */ +#define BR_IO_FAILED 0 +#define BR_IO_IN 1 +#define BR_IO_OUT 2 +#define BR_IO_INOUT 3 + +/* + * Mark a SSL engine as failed. The provided error code is recorded if + * the engine was not already marked as failed. If 'err' is 0, then the + * engine is marked as closed (without error). + */ +void br_ssl_engine_fail(br_ssl_engine_context *cc, int err); + +/* + * Test whether the engine is closed (normally or as a failure). + */ +static inline int +br_ssl_engine_closed(const br_ssl_engine_context *cc) +{ + return cc->iomode == BR_IO_FAILED; +} + +/* + * Configure a new maximum fragment length. If possible, the maximum + * length for outgoing records is immediately adjusted (if there are + * not already too many buffered bytes for that). + */ +void br_ssl_engine_new_max_frag_len( + br_ssl_engine_context *rc, unsigned max_frag_len); + +/* + * Test whether the current incoming record has been fully received + * or not. This functions returns 0 only if a complete record header + * has been received, but some of the (possibly encrypted) payload + * has not yet been obtained. + */ +int br_ssl_engine_recvrec_finished(const br_ssl_engine_context *rc); + +/* + * Flush the current record (if not empty). This is meant to be called + * from the handshake processor only. + */ +void br_ssl_engine_flush_record(br_ssl_engine_context *cc); + +/* + * Test whether there is some accumulated payload to send. + */ +static inline int +br_ssl_engine_has_pld_to_send(const br_ssl_engine_context *rc) +{ + return rc->oxa != rc->oxb && rc->oxa != rc->oxc; +} + +/* + * Initialize RNG in engine. Returned value is 1 on success, 0 on error. + * This function will try to use the OS-provided RNG, if available. If + * there is no OS-provided RNG, or if it failed, and no entropy was + * injected by the caller, then a failure will be reported. On error, + * the context error code is set. + */ +int br_ssl_engine_init_rand(br_ssl_engine_context *cc); + +/* + * Reset the handshake-related parts of the engine. + */ +void br_ssl_engine_hs_reset(br_ssl_engine_context *cc, + void (*hsinit)(void *), void (*hsrun)(void *)); + +/* + * Get the PRF to use for this context, for the provided PRF hash + * function ID. + */ +br_tls_prf_impl br_ssl_engine_get_PRF(br_ssl_engine_context *cc, int prf_id); + +/* + * Consume the provided pre-master secret and compute the corresponding + * master secret. The 'prf_id' is the ID of the hash function to use + * with the TLS 1.2 PRF (ignored if the version is TLS 1.0 or 1.1). + */ +void br_ssl_engine_compute_master(br_ssl_engine_context *cc, + int prf_id, const void *pms, size_t len); + +/* + * Switch to CBC decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF (ignored if not TLS 1.2+) + * mac_id id of hash function for HMAC + * bc_impl block cipher implementation (CBC decryption) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_cbc_in(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcdec_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to CBC encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF (ignored if not TLS 1.2+) + * mac_id id of hash function for HMAC + * bc_impl block cipher implementation (CBC encryption) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_cbc_out(br_ssl_engine_context *cc, + int is_client, int prf_id, int mac_id, + const br_block_cbcenc_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to GCM decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_gcm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to GCM encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR) + * cipher_key_len block cipher key length (in bytes) + */ +void br_ssl_engine_switch_gcm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctr_class *bc_impl, size_t cipher_key_len); + +/* + * Switch to ChaCha20+Poly1305 decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + */ +void br_ssl_engine_switch_chapol_in(br_ssl_engine_context *cc, + int is_client, int prf_id); + +/* + * Switch to ChaCha20+Poly1305 encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + */ +void br_ssl_engine_switch_chapol_out(br_ssl_engine_context *cc, + int is_client, int prf_id); + +/* + * Switch to CCM decryption for incoming records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR+CBC) + * cipher_key_len block cipher key length (in bytes) + * tag_len tag length (in bytes) + */ +void br_ssl_engine_switch_ccm_in(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len); + +/* + * Switch to GCM encryption for outgoing records. + * cc the engine context + * is_client non-zero for a client, zero for a server + * prf_id id of hash function for PRF + * bc_impl block cipher implementation (CTR+CBC) + * cipher_key_len block cipher key length (in bytes) + * tag_len tag length (in bytes) + */ +void br_ssl_engine_switch_ccm_out(br_ssl_engine_context *cc, + int is_client, int prf_id, + const br_block_ctrcbc_class *bc_impl, + size_t cipher_key_len, size_t tag_len); + +/* + * Calls to T0-generated code. + */ +void br_ssl_hs_client_init_main(void *ctx); +void br_ssl_hs_client_run(void *ctx); +void br_ssl_hs_server_init_main(void *ctx); +void br_ssl_hs_server_run(void *ctx); + +/* + * Get the hash function to use for signatures, given a bit mask of + * supported hash functions. This implements a strict choice order + * (namely SHA-256, SHA-384, SHA-512, SHA-224, SHA-1). If the mask + * does not document support of any of these hash functions, then this + * functions returns 0. + */ +int br_ssl_choose_hash(unsigned bf); + +/* ==================================================================== */ + +/* + * PowerPC / POWER assembly stuff. The special BR_POWER_ASM_MACROS macro + * must be defined before including this file; this is done by source + * files that use some inline assembly for PowerPC / POWER machines. + */ + +#if BR_POWER_ASM_MACROS + +#define lxvw4x(xt, ra, rb) lxvw4x_(xt, ra, rb) +#define stxvw4x(xt, ra, rb) stxvw4x_(xt, ra, rb) + +#define bdnz(foo) bdnz_(foo) +#define bdz(foo) bdz_(foo) +#define beq(foo) beq_(foo) + +#define li(rx, value) li_(rx, value) +#define addi(rx, ra, imm) addi_(rx, ra, imm) +#define cmpldi(rx, imm) cmpldi_(rx, imm) +#define mtctr(rx) mtctr_(rx) +#define vspltb(vrt, vrb, uim) vspltb_(vrt, vrb, uim) +#define vspltw(vrt, vrb, uim) vspltw_(vrt, vrb, uim) +#define vspltisb(vrt, imm) vspltisb_(vrt, imm) +#define vspltisw(vrt, imm) vspltisw_(vrt, imm) +#define vrlw(vrt, vra, vrb) vrlw_(vrt, vra, vrb) +#define vsbox(vrt, vra) vsbox_(vrt, vra) +#define vxor(vrt, vra, vrb) vxor_(vrt, vra, vrb) +#define vand(vrt, vra, vrb) vand_(vrt, vra, vrb) +#define vsro(vrt, vra, vrb) vsro_(vrt, vra, vrb) +#define vsl(vrt, vra, vrb) vsl_(vrt, vra, vrb) +#define vsldoi(vt, va, vb, sh) vsldoi_(vt, va, vb, sh) +#define vsr(vrt, vra, vrb) vsr_(vrt, vra, vrb) +#define vaddcuw(vrt, vra, vrb) vaddcuw_(vrt, vra, vrb) +#define vadduwm(vrt, vra, vrb) vadduwm_(vrt, vra, vrb) +#define vsububm(vrt, vra, vrb) vsububm_(vrt, vra, vrb) +#define vsubuwm(vrt, vra, vrb) vsubuwm_(vrt, vra, vrb) +#define vsrw(vrt, vra, vrb) vsrw_(vrt, vra, vrb) +#define vcipher(vt, va, vb) vcipher_(vt, va, vb) +#define vcipherlast(vt, va, vb) vcipherlast_(vt, va, vb) +#define vncipher(vt, va, vb) vncipher_(vt, va, vb) +#define vncipherlast(vt, va, vb) vncipherlast_(vt, va, vb) +#define vperm(vt, va, vb, vc) vperm_(vt, va, vb, vc) +#define vpmsumd(vt, va, vb) vpmsumd_(vt, va, vb) +#define xxpermdi(vt, va, vb, d) xxpermdi_(vt, va, vb, d) + +#define lxvw4x_(xt, ra, rb) "\tlxvw4x\t" #xt "," #ra "," #rb "\n" +#define stxvw4x_(xt, ra, rb) "\tstxvw4x\t" #xt "," #ra "," #rb "\n" + +#define label(foo) #foo "%=:\n" +#define bdnz_(foo) "\tbdnz\t" #foo "%=\n" +#define bdz_(foo) "\tbdz\t" #foo "%=\n" +#define beq_(foo) "\tbeq\t" #foo "%=\n" + +#define li_(rx, value) "\tli\t" #rx "," #value "\n" +#define addi_(rx, ra, imm) "\taddi\t" #rx "," #ra "," #imm "\n" +#define cmpldi_(rx, imm) "\tcmpldi\t" #rx "," #imm "\n" +#define mtctr_(rx) "\tmtctr\t" #rx "\n" +#define vspltb_(vrt, vrb, uim) "\tvspltb\t" #vrt "," #vrb "," #uim "\n" +#define vspltw_(vrt, vrb, uim) "\tvspltw\t" #vrt "," #vrb "," #uim "\n" +#define vspltisb_(vrt, imm) "\tvspltisb\t" #vrt "," #imm "\n" +#define vspltisw_(vrt, imm) "\tvspltisw\t" #vrt "," #imm "\n" +#define vrlw_(vrt, vra, vrb) "\tvrlw\t" #vrt "," #vra "," #vrb "\n" +#define vsbox_(vrt, vra) "\tvsbox\t" #vrt "," #vra "\n" +#define vxor_(vrt, vra, vrb) "\tvxor\t" #vrt "," #vra "," #vrb "\n" +#define vand_(vrt, vra, vrb) "\tvand\t" #vrt "," #vra "," #vrb "\n" +#define vsro_(vrt, vra, vrb) "\tvsro\t" #vrt "," #vra "," #vrb "\n" +#define vsl_(vrt, vra, vrb) "\tvsl\t" #vrt "," #vra "," #vrb "\n" +#define vsldoi_(vt, va, vb, sh) "\tvsldoi\t" #vt "," #va "," #vb "," #sh "\n" +#define vsr_(vrt, vra, vrb) "\tvsr\t" #vrt "," #vra "," #vrb "\n" +#define vaddcuw_(vrt, vra, vrb) "\tvaddcuw\t" #vrt "," #vra "," #vrb "\n" +#define vadduwm_(vrt, vra, vrb) "\tvadduwm\t" #vrt "," #vra "," #vrb "\n" +#define vsububm_(vrt, vra, vrb) "\tvsububm\t" #vrt "," #vra "," #vrb "\n" +#define vsubuwm_(vrt, vra, vrb) "\tvsubuwm\t" #vrt "," #vra "," #vrb "\n" +#define vsrw_(vrt, vra, vrb) "\tvsrw\t" #vrt "," #vra "," #vrb "\n" +#define vcipher_(vt, va, vb) "\tvcipher\t" #vt "," #va "," #vb "\n" +#define vcipherlast_(vt, va, vb) "\tvcipherlast\t" #vt "," #va "," #vb "\n" +#define vncipher_(vt, va, vb) "\tvncipher\t" #vt "," #va "," #vb "\n" +#define vncipherlast_(vt, va, vb) "\tvncipherlast\t" #vt "," #va "," #vb "\n" +#define vperm_(vt, va, vb, vc) "\tvperm\t" #vt "," #va "," #vb "," #vc "\n" +#define vpmsumd_(vt, va, vb) "\tvpmsumd\t" #vt "," #va "," #vb "\n" +#define xxpermdi_(vt, va, vb, d) "\txxpermdi\t" #vt "," #va "," #vb "," #d "\n" + +#endif + +/* ==================================================================== */ +/* + * Special "activate intrinsics" code, needed for some compiler versions. + * This is defined at the end of this file, so that it won't impact any + * of the inline functions defined previously; and it is controlled by + * a specific macro defined in the caller code. + * + * Calling code conventions: + * + * - Caller must define BR_ENABLE_INTRINSICS before including "t_inner.h". + * - Functions that use intrinsics must be enclosed in an "enabled" + * region (between BR_TARGETS_X86_UP and BR_TARGETS_X86_DOWN). + * - Functions that use intrinsics must be tagged with the appropriate + * BR_TARGET(). + */ + +#if BR_ENABLE_INTRINSICS && (BR_GCC_4_4 || BR_CLANG_3_7 || BR_MSC_2005) + +/* + * x86 intrinsics (both 32-bit and 64-bit). + */ +#if BR_i386 || BR_amd64 + +/* + * On GCC before version 5.0, we need to use the pragma to enable the + * target options globally, because the 'target' function attribute + * appears to be unreliable. Before 4.6 we must also avoid the + * push_options / pop_options mechanism, because it tends to trigger + * some internal compiler errors. + */ +#if BR_GCC && !BR_GCC_5_0 +#if BR_GCC_4_6 +#define BR_TARGETS_X86_UP \ + _Pragma("GCC push_options") \ + _Pragma("GCC target(\"sse2,ssse3,sse4.1,aes,pclmul,rdrnd\")") +#define BR_TARGETS_X86_DOWN \ + _Pragma("GCC pop_options") +#else +#define BR_TARGETS_X86_UP \ + _Pragma("GCC target(\"sse2,ssse3,sse4.1,aes,pclmul\")") +#define BR_TARGETS_X86_DOWN +#endif +#pragma GCC diagnostic ignored "-Wpsabi" +#endif + +#if BR_CLANG && !BR_CLANG_3_8 +#undef __SSE2__ +#undef __SSE3__ +#undef __SSSE3__ +#undef __SSE4_1__ +#undef __AES__ +#undef __PCLMUL__ +#undef __RDRND__ +#define __SSE2__ 1 +#define __SSE3__ 1 +#define __SSSE3__ 1 +#define __SSE4_1__ 1 +#define __AES__ 1 +#define __PCLMUL__ 1 +#define __RDRND__ 1 +#endif + +#ifndef BR_TARGETS_X86_UP +#define BR_TARGETS_X86_UP +#endif +#ifndef BR_TARGETS_X86_DOWN +#define BR_TARGETS_X86_DOWN +#endif + +#if BR_GCC || BR_CLANG +BR_TARGETS_X86_UP +#include +#include +#define br_bswap32 __builtin_bswap32 +BR_TARGETS_X86_DOWN +#endif + +#if BR_MSC +#include +#include +#include +#define br_bswap32 _byteswap_ulong +#endif + +static inline int +br_cpuid(uint32_t mask_eax, uint32_t mask_ebx, + uint32_t mask_ecx, uint32_t mask_edx) +{ +#if BR_GCC || BR_CLANG + unsigned eax, ebx, ecx, edx; + + if (__get_cpuid(1, &eax, &ebx, &ecx, &edx)) { + if ((eax & mask_eax) == mask_eax + && (ebx & mask_ebx) == mask_ebx + && (ecx & mask_ecx) == mask_ecx + && (edx & mask_edx) == mask_edx) + { + return 1; + } + } +#elif BR_MSC + int info[4]; + + __cpuid(info, 1); + if (((uint32_t)info[0] & mask_eax) == mask_eax + && ((uint32_t)info[1] & mask_ebx) == mask_ebx + && ((uint32_t)info[2] & mask_ecx) == mask_ecx + && ((uint32_t)info[3] & mask_edx) == mask_edx) + { + return 1; + } +#endif + return 0; +} + +#endif + +#endif + +#if (defined(ESP8266)|| defined(ESP32)) + + #ifdef __cplusplus + extern "C" { + #endif + + #define _debugBearSSL (0) + #ifdef ESP8266 + extern void stack_thunk_yield(void); + #else + #define stack_thunk_yield(ignored) + #endif + #ifdef __cplusplus + } + #endif + +#else + #define stack_thunk_yield(ignored) +#endif + +#ifdef ESP32 +#define memcpy_P memcpy +#endif + +/* ==================================================================== */ + +#endif diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c b/lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c new file mode 100644 index 0000000000..221bf9c1ec --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/asn1enc.c @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +br_asn1_uint +br_asn1_uint_prepare(const void *xdata, size_t xlen) +{ + const unsigned char *x; + br_asn1_uint t; + + x = xdata; + while (xlen > 0 && *x == 0) { + x ++; + xlen --; + } + t.data = x; + t.len = xlen; + t.asn1len = xlen; + if (xlen == 0 || x[0] >= 0x80) { + t.asn1len ++; + } + return t; +} + +/* see inner.h */ +size_t +br_asn1_encode_length(void *dest, size_t len) +{ + unsigned char *buf; + size_t z; + int i, j; + + buf = dest; + if (len < 0x80) { + if (buf != NULL) { + *buf = len; + } + return 1; + } + i = 0; + for (z = len; z != 0; z >>= 8) { + i ++; + } + if (buf != NULL) { + *buf ++ = 0x80 + i; + for (j = i - 1; j >= 0; j --) { + *buf ++ = len >> (j << 3); + } + } + return i + 1; +} + +/* see inner.h */ +size_t +br_asn1_encode_uint(void *dest, br_asn1_uint pp) +{ + unsigned char *buf; + size_t lenlen; + + if (dest == NULL) { + return 1 + br_asn1_encode_length(NULL, pp.asn1len) + pp.asn1len; + } + buf = dest; + *buf ++ = 0x02; + lenlen = br_asn1_encode_length(buf, pp.asn1len); + buf += lenlen; + *buf = 0x00; + memcpy(buf + pp.asn1len - pp.len, pp.data, pp.len); + return 1 + lenlen + pp.asn1len; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c new file mode 100644 index 0000000000..817a6d31bc --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_pk8der.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_ec_pkcs8_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key (the public key + * is included in the private key value instead). The + * 'version' field is an INTEGER that we will set to 0 + * (meaning 'v1', compatible with previous versions of PKCS#8). + * The 'privateKeyAlgorithm' structure is an AlgorithmIdentifier + * whose OID should be id-ecPublicKey, with, as parameters, the + * curve OID. The 'privateKey' is an OCTET STRING, whose value + * is the "raw DER" encoding of the key pair. + */ + + /* + * OID id-ecPublicKey (1.2.840.10045.2.1), DER-encoded (with + * the tag). + */ + static const unsigned char OID_ECPUBKEY[] = { + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01 + }; + + size_t len_version, len_privateKeyAlgorithm, len_privateKeyValue; + size_t len_privateKey, len_seq; + const unsigned char *oid; + + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + len_version = 3; + len_privateKeyAlgorithm = 2 + sizeof OID_ECPUBKEY + 2 + oid[0]; + len_privateKeyValue = br_encode_ec_raw_der_inner(NULL, sk, pk, 0); + len_privateKey = 1 + len_of_len(len_privateKeyValue) + + len_privateKeyValue; + len_seq = len_version + len_privateKeyAlgorithm + len_privateKey; + + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x00; + + /* privateKeyAlgorithm */ + *buf ++ = 0x30; + *buf ++ = (sizeof OID_ECPUBKEY) + 2 + oid[0]; + memcpy(buf, OID_ECPUBKEY, sizeof OID_ECPUBKEY); + buf += sizeof OID_ECPUBKEY; + *buf ++ = 0x06; + memcpy(buf, oid, 1 + oid[0]); + buf += 1 + oid[0]; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, len_privateKeyValue); + br_encode_ec_raw_der_inner(buf, sk, pk, 0); + + return 1 + lenlen + len_seq; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c new file mode 100644 index 0000000000..b84fd951c9 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_ec_rawder.c @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see inner.h */ +const unsigned char * +br_get_curve_OID(int curve) +{ + static const unsigned char OID_secp256r1[] = { + 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 + }; + static const unsigned char OID_secp384r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 + }; + static const unsigned char OID_secp521r1[] = { + 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 + }; + + switch (curve) { + case BR_EC_secp256r1: return OID_secp256r1; + case BR_EC_secp384r1: return OID_secp384r1; + case BR_EC_secp521r1: return OID_secp521r1; + default: + return NULL; + } +} + +/* see inner.h */ +size_t +br_encode_ec_raw_der_inner(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk, + int include_curve_oid) +{ + /* + * ASN.1 format: + * + * ECPrivateKey ::= SEQUENCE { + * version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), + * privateKey OCTET STRING, + * parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, + * publicKey [1] BIT STRING OPTIONAL + * } + * + * The tages '[0]' and '[1]' are explicit. The 'ECParameters' + * is a CHOICE; in our case, it will always be an OBJECT IDENTIFIER + * that identifies the curve. + * + * The value of the 'privateKey' field is the raw unsigned big-endian + * encoding of the private key (integer modulo the curve subgroup + * order); there is no INTEGER tag, and the leading bit may be 1. + * Also, leading bytes of value 0x00 are _not_ removed. + * + * The 'publicKey' contents are the raw encoded public key point, + * normally uncompressed (leading byte of value 0x04, followed + * by the unsigned big-endian encodings of the X and Y coordinates, + * padded to the full field length if necessary). + */ + + size_t len_version, len_privateKey, len_parameters, len_publicKey; + size_t len_publicKey_bits, len_seq; + const unsigned char *oid; + + if (include_curve_oid) { + oid = br_get_curve_OID(sk->curve); + if (oid == NULL) { + return 0; + } + } else { + oid = NULL; + } + len_version = 3; + len_privateKey = 1 + len_of_len(sk->xlen) + sk->xlen; + if (include_curve_oid) { + len_parameters = 4 + oid[0]; + } else { + len_parameters = 0; + } + if (pk == NULL) { + len_publicKey = 0; + len_publicKey_bits = 0; + } else { + len_publicKey_bits = 2 + len_of_len(pk->qlen) + pk->qlen; + len_publicKey = 1 + len_of_len(len_publicKey_bits) + + len_publicKey_bits; + } + len_seq = len_version + len_privateKey + len_parameters + len_publicKey; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version */ + *buf ++ = 0x02; + *buf ++ = 0x01; + *buf ++ = 0x01; + + /* privateKey */ + *buf ++ = 0x04; + buf += br_asn1_encode_length(buf, sk->xlen); + memcpy(buf, sk->x, sk->xlen); + buf += sk->xlen; + + /* parameters */ + if (include_curve_oid) { + *buf ++ = 0xA0; + *buf ++ = oid[0] + 2; + *buf ++ = 0x06; + memcpy(buf, oid, oid[0] + 1); + buf += oid[0] + 1; + } + + /* publicKey */ + if (pk != NULL) { + *buf ++ = 0xA1; + buf += br_asn1_encode_length(buf, len_publicKey_bits); + *buf ++ = 0x03; + buf += br_asn1_encode_length(buf, pk->qlen + 1); + *buf ++ = 0x00; + memcpy(buf, pk->q, pk->qlen); + /* buf += pk->qlen; */ + } + + return 1 + lenlen + len_seq; + } +} + +/* see bearssl_x509.h */ +size_t +br_encode_ec_raw_der(void *dest, + const br_ec_private_key *sk, const br_ec_public_key *pk) +{ + return br_encode_ec_raw_der_inner(dest, sk, pk, 1); +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c new file mode 100644 index 0000000000..da3e0223d6 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_pk8der.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_pkcs8_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * OneAsymmetricKey ::= SEQUENCE { + * version Version, + * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier, + * privateKey PrivateKey, + * attributes [0] Attributes OPTIONAL, + * ..., + * [[2: publicKey [1] PublicKey OPTIONAL ]], + * ... + * } + * + * We don't include attributes or public key. The 'version' field + * is an INTEGER that we will set to 0 (meaning 'v1', compatible + * with previous versions of PKCS#8). The 'privateKeyAlgorithm' + * structure is an AlgorithmIdentifier whose OID should be + * rsaEncryption, with NULL parameters. The 'privateKey' is an + * OCTET STRING, whose value is the "raw DER" encoding of the + * key pair. + * + * Since the private key value comes last, this function really + * adds a header, which is mostly fixed (only some lengths have + * to be modified. + */ + + /* + * Concatenation of: + * - DER encoding of an INTEGER of value 0 (the 'version' field) + * - DER encoding of a PrivateKeyAlgorithmIdentifier that uses + * the rsaEncryption OID, and NULL parameters + * - An OCTET STRING tag + */ + static const unsigned char PK8_HEAD[] = { + 0x02, 0x01, 0x00, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, + 0x04 + }; + + size_t len_raw, len_seq; + + len_raw = br_encode_rsa_raw_der(NULL, sk, pk, d, dlen); + len_seq = (sizeof PK8_HEAD) + len_of_len(len_raw) + len_raw; + if (dest == NULL) { + return 1 + len_of_len(len_seq) + len_seq; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, len_seq); + buf += lenlen; + + /* version, privateKeyAlgorithm, privateKey tag */ + memcpy(buf, PK8_HEAD, sizeof PK8_HEAD); + buf += sizeof PK8_HEAD; + + /* privateKey */ + buf += br_asn1_encode_length(buf, len_raw); + br_encode_rsa_raw_der(buf, sk, pk, d, dlen); + + return 1 + lenlen + len_seq; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c new file mode 100644 index 0000000000..31116d1f8f --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/encode_rsa_rawder.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +size_t +br_encode_rsa_raw_der(void *dest, const br_rsa_private_key *sk, + const br_rsa_public_key *pk, const void *d, size_t dlen) +{ + /* + * ASN.1 format: + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + * + * The 'version' field is an INTEGER of value 0 (meaning: there + * are exactly two prime factors), and 'otherPrimeInfos' will + * be absent (because there are exactly two prime factors). + */ + + br_asn1_uint num[9]; + size_t u, slen; + + /* + * For all INTEGER values, get the pointer and length for the + * data bytes. + */ + num[0] = br_asn1_uint_prepare(NULL, 0); + num[1] = br_asn1_uint_prepare(pk->n, pk->nlen); + num[2] = br_asn1_uint_prepare(pk->e, pk->elen); + num[3] = br_asn1_uint_prepare(d, dlen); + num[4] = br_asn1_uint_prepare(sk->p, sk->plen); + num[5] = br_asn1_uint_prepare(sk->q, sk->qlen); + num[6] = br_asn1_uint_prepare(sk->dp, sk->dplen); + num[7] = br_asn1_uint_prepare(sk->dq, sk->dqlen); + num[8] = br_asn1_uint_prepare(sk->iq, sk->iqlen); + + /* + * Get the length of the SEQUENCE contents. + */ + slen = 0; + for (u = 0; u < 9; u ++) { + uint32_t ilen; + + ilen = num[u].asn1len; + slen += 1 + len_of_len(ilen) + ilen; + } + + if (dest == NULL) { + return 1 + len_of_len(slen) + slen; + } else { + unsigned char *buf; + size_t lenlen; + + buf = dest; + *buf ++ = 0x30; /* SEQUENCE tag */ + lenlen = br_asn1_encode_length(buf, slen); + buf += lenlen; + for (u = 0; u < 9; u ++) { + buf += br_asn1_encode_uint(buf, num[u]); + } + return 1 + lenlen + slen; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c new file mode 100644 index 0000000000..84fa057aa5 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/pkey_decoder.c @@ -0,0 +1,587 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_pkey_decoder_init_main(void *t0ctx); + +void br_pkey_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_pkey_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_pkey_decoder_context, cpu))) +#define CONTEXT_NAME br_pkey_decoder_context + +/* see bearssl_x509.h */ +void +br_pkey_decoder_init(br_pkey_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_pkey_decoder_init_main(&ctx->cpu); + br_pkey_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_pkey_decoder_push(br_pkey_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_pkey_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x12, + 0x12, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, + 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_data)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_type)), 0x00, 0x00, + 0x2F, 0x43, 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, pad)), + 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x1C, 0x00, 0x00, 0x01, 0x22, + 0x00, 0x00, 0x05, 0x02, 0x28, 0x15, 0x00, 0x00, 0x06, 0x02, 0x29, 0x15, + 0x00, 0x00, 0x01, 0x10, 0x39, 0x00, 0x00, 0x0C, 0x05, 0x02, 0x2B, 0x15, + 0x36, 0x00, 0x00, 0x0C, 0x05, 0x02, 0x2B, 0x15, 0x37, 0x00, 0x00, 0x06, + 0x02, 0x24, 0x15, 0x00, 0x01, 0x03, 0x00, 0x51, 0x06, 0x02, 0x2C, 0x15, + 0x50, 0x01, 0x04, 0x3A, 0x02, 0x00, 0x3D, 0x00, 0x02, 0x03, 0x00, 0x13, + 0x03, 0x01, 0x02, 0x01, 0x43, 0x0D, 0x06, 0x02, 0x2C, 0x15, 0x02, 0x01, + 0x2F, 0x47, 0x12, 0x01, 0x00, 0x02, 0x00, 0x05, 0x02, 0x2C, 0x15, 0x02, + 0x00, 0x02, 0x01, 0x1B, 0x00, 0x02, 0x4D, 0x46, 0x05, 0x02, 0x2C, 0x15, + 0x53, 0x14, 0x06, 0x07, 0x55, 0x01, 0x7F, 0x03, 0x01, 0x04, 0x16, 0x42, + 0x14, 0x06, 0x10, 0x01, 0x00, 0x03, 0x01, 0x13, 0x06, 0x03, 0x48, 0x04, + 0x02, 0x01, 0x00, 0x03, 0x00, 0x04, 0x02, 0x2C, 0x15, 0x3B, 0x50, 0x01, + 0x03, 0x3A, 0x4D, 0x02, 0x01, 0x06, 0x03, 0x3F, 0x04, 0x03, 0x02, 0x00, + 0x3C, 0x3B, 0x55, 0x02, 0x01, 0x06, 0x03, 0x2E, 0x04, 0x01, 0x2D, 0x00, + 0x00, 0x51, 0x06, 0x02, 0x2C, 0x15, 0x4E, 0x40, 0x3B, 0x00, 0x03, 0x31, + 0x49, 0x13, 0x13, 0x03, 0x00, 0x03, 0x01, 0x4B, 0x03, 0x02, 0x02, 0x00, + 0x02, 0x02, 0x1C, 0x00, 0x00, 0x17, 0x17, 0x00, 0x00, 0x01, 0x0B, 0x00, + 0x00, 0x01, T0_INT2(3 * BR_X509_BUFSIZE_KEY), 0x00, 0x01, 0x01, 0x87, + 0xFF, 0xFF, 0x7F, 0x4E, 0x50, 0x01, 0x02, 0x17, 0x0C, 0x06, 0x06, 0x12, + 0x37, 0x40, 0x2E, 0x04, 0x1C, 0x01, 0x04, 0x17, 0x0C, 0x06, 0x08, 0x12, + 0x37, 0x01, 0x00, 0x3D, 0x2D, 0x04, 0x0E, 0x01, 0x10, 0x17, 0x0C, 0x06, + 0x05, 0x12, 0x36, 0x3E, 0x04, 0x03, 0x2C, 0x15, 0x12, 0x03, 0x00, 0x3B, + 0x02, 0x00, 0x30, 0x1D, 0x52, 0x24, 0x15, 0x00, 0x01, 0x41, 0x0A, 0x06, + 0x02, 0x26, 0x15, 0x13, 0x03, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x50, + 0x01, 0x06, 0x3A, 0x4F, 0x00, 0x00, 0x1E, 0x13, 0x06, 0x07, 0x18, 0x13, + 0x06, 0x01, 0x11, 0x04, 0x76, 0x21, 0x00, 0x00, 0x46, 0x05, 0x02, 0x2C, + 0x15, 0x33, 0x14, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x34, 0x14, 0x06, + 0x04, 0x01, 0x18, 0x04, 0x0A, 0x35, 0x14, 0x06, 0x04, 0x01, 0x19, 0x04, + 0x02, 0x2C, 0x15, 0x00, 0x00, 0x1A, 0x50, 0x01, 0x02, 0x3A, 0x09, 0x4A, + 0x00, 0x03, 0x13, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x4D, 0x51, 0x13, + 0x01, 0x81, 0x00, 0x0E, 0x06, 0x02, 0x2A, 0x15, 0x13, 0x01, 0x00, 0x0C, + 0x06, 0x0B, 0x12, 0x13, 0x05, 0x04, 0x12, 0x01, 0x00, 0x00, 0x51, 0x04, + 0x6F, 0x02, 0x01, 0x13, 0x05, 0x02, 0x27, 0x15, 0x20, 0x03, 0x01, 0x02, + 0x02, 0x1D, 0x02, 0x02, 0x1F, 0x03, 0x02, 0x13, 0x06, 0x03, 0x51, 0x04, + 0x68, 0x12, 0x02, 0x00, 0x02, 0x01, 0x08, 0x00, 0x00, 0x13, 0x31, 0x1A, + 0x08, 0x1E, 0x1A, 0x07, 0x1E, 0x49, 0x00, 0x01, 0x51, 0x13, 0x01, 0x81, + 0x00, 0x0A, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x08, 0x13, 0x05, 0x02, + 0x25, 0x15, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x0D, 0x06, + 0x19, 0x02, 0x00, 0x20, 0x03, 0x00, 0x13, 0x01, 0x83, 0xFF, 0xFF, 0x7F, + 0x0D, 0x06, 0x02, 0x26, 0x15, 0x01, 0x08, 0x0B, 0x1E, 0x51, 0x1A, 0x07, + 0x04, 0x60, 0x00, 0x00, 0x4C, 0x45, 0x00, 0x00, 0x50, 0x38, 0x4D, 0x00, + 0x00, 0x4D, 0x13, 0x01, 0x81, 0x7F, 0x0D, 0x06, 0x08, 0x54, 0x01, 0x00, + 0x32, 0x1D, 0x01, 0x00, 0x00, 0x13, 0x32, 0x1D, 0x32, 0x1F, 0x47, 0x01, + 0x7F, 0x00, 0x01, 0x51, 0x03, 0x00, 0x02, 0x00, 0x01, 0x05, 0x0F, 0x01, + 0x01, 0x10, 0x16, 0x02, 0x00, 0x01, 0x06, 0x0F, 0x13, 0x01, 0x01, 0x10, + 0x06, 0x02, 0x22, 0x15, 0x01, 0x04, 0x0B, 0x02, 0x00, 0x01, 0x1F, 0x10, + 0x13, 0x01, 0x1F, 0x0C, 0x06, 0x02, 0x23, 0x15, 0x07, 0x00, 0x00, 0x13, + 0x05, 0x02, 0x26, 0x15, 0x20, 0x52, 0x00, 0x00, 0x19, 0x13, 0x01, 0x00, + 0x0E, 0x06, 0x01, 0x00, 0x12, 0x11, 0x04, 0x74, 0x00, 0x01, 0x01, 0x00, + 0x00, 0x55, 0x12, 0x00, 0x00, 0x13, 0x06, 0x07, 0x56, 0x13, 0x06, 0x01, + 0x11, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x17, 0x18, 0x09, 0x21, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 14, + 18, + 22, + 26, + 30, + 34, + 38, + 42, + 46, + 50, + 54, + 58, + 62, + 66, + 71, + 76, + 80, + 85, + 89, + 93, + 97, + 103, + 109, + 114, + 122, + 130, + 136, + 152, + 185, + 252, + 262, + 280, + 284, + 288, + 293, + 352, + 366, + 373, + 387, + 420, + 429, + 496, + 507, + 563, + 567, + 572, + 598, + 642, + 651, + 664, + 668, + 672, + 684 +}; + +#define T0_INTERPRETED 31 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_pkey_decoder_init_main, 68) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_pkey_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* -rot */ + T0_NROT(); + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 13: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 14: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 15: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 16: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 17: { + /* co */ + T0_CO(); + } + break; + case 18: { + /* drop */ + (void)T0_POP(); + } + break; + case 19: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 20: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 21: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 22: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 23: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 24: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 25: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + CTX->hlen --; + T0_PUSH(pgm_read_byte(CTX->hbuf ++)); + } + + } + break; + case 26: { + /* rot */ + T0_ROT(); + } + break; + case 27: { + /* set-ec-key */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->key.ec.curve = curve; + CTX->key.ec.q = CTX->key_data; + CTX->key.ec.qlen = qlen; + + } + break; + case 28: { + /* set-rsa-key */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + + CTX->key.rsa.n = CTX->key_data; + CTX->key.rsa.nlen = nlen; + CTX->key.rsa.e = CTX->key_data + nlen; + CTX->key.rsa.elen = elen; + + } + break; + case 29: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 30: { + /* swap */ + T0_SWAP(); + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c new file mode 100644 index 0000000000..47adab0eb2 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/skey_decoder.c @@ -0,0 +1,654 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_skey_decoder_init_main(void *t0ctx); + +void br_skey_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_skey_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_skey_decoder_context, cpu))) +#define CONTEXT_NAME br_skey_decoder_context + +/* see bearssl_x509.h */ +void +br_skey_decoder_init(br_skey_decoder_context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_skey_decoder_init_main(&ctx->cpu); + br_skey_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_skey_decoder_push(br_skey_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_skey_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x01, 0x07, 0x00, 0x00, 0x01, 0x01, 0x08, 0x00, 0x00, 0x13, + 0x13, 0x00, 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, + 0x01, T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INVALID_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_data)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_type)), 0x00, 0x00, + 0x33, 0x48, 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, pad)), + 0x00, 0x00, 0x01, 0x13, 0x00, 0x00, 0x01, 0x1C, 0x00, 0x00, 0x01, 0x22, + 0x00, 0x00, 0x05, 0x02, 0x2C, 0x16, 0x00, 0x00, 0x06, 0x02, 0x2D, 0x16, + 0x00, 0x00, 0x01, 0x10, 0x3D, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16, + 0x3A, 0x00, 0x00, 0x0D, 0x05, 0x02, 0x2F, 0x16, 0x3B, 0x00, 0x00, 0x06, + 0x02, 0x27, 0x16, 0x00, 0x01, 0x03, 0x00, 0x54, 0x57, 0x01, 0x02, 0x3E, + 0x55, 0x23, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x04, 0x3E, 0x02, 0x00, + 0x41, 0x3F, 0x00, 0x02, 0x03, 0x00, 0x53, 0x14, 0x14, 0x03, 0x01, 0x48, + 0x0E, 0x06, 0x02, 0x30, 0x16, 0x33, 0x4C, 0x58, 0x01, 0x7F, 0x19, 0x0D, + 0x06, 0x04, 0x13, 0x13, 0x04, 0x29, 0x01, 0x20, 0x19, 0x0D, 0x06, 0x16, + 0x13, 0x3A, 0x53, 0x4D, 0x02, 0x00, 0x06, 0x09, 0x02, 0x00, 0x0C, 0x06, + 0x02, 0x2A, 0x16, 0x04, 0x02, 0x03, 0x00, 0x3F, 0x04, 0x0D, 0x01, 0x21, + 0x19, 0x0D, 0x06, 0x04, 0x13, 0x3A, 0x04, 0x03, 0x30, 0x16, 0x13, 0x5D, + 0x02, 0x00, 0x05, 0x02, 0x30, 0x16, 0x02, 0x00, 0x02, 0x01, 0x1D, 0x00, + 0x02, 0x53, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x5B, 0x15, 0x06, 0x07, 0x5D, + 0x01, 0x7F, 0x03, 0x01, 0x04, 0x16, 0x46, 0x15, 0x06, 0x10, 0x01, 0x00, + 0x03, 0x01, 0x14, 0x06, 0x03, 0x4D, 0x04, 0x02, 0x01, 0x00, 0x03, 0x00, + 0x04, 0x02, 0x30, 0x16, 0x3F, 0x57, 0x01, 0x04, 0x3E, 0x53, 0x02, 0x01, + 0x06, 0x03, 0x43, 0x04, 0x03, 0x02, 0x00, 0x40, 0x3F, 0x5D, 0x02, 0x01, + 0x06, 0x03, 0x32, 0x04, 0x01, 0x31, 0x00, 0x00, 0x54, 0x57, 0x01, 0x02, + 0x3E, 0x55, 0x06, 0x02, 0x30, 0x16, 0x57, 0x01, 0x02, 0x3E, 0x44, 0x3F, + 0x00, 0x07, 0x35, 0x50, 0x14, 0x05, 0x02, 0x2F, 0x16, 0x23, 0x01, 0x03, + 0x0B, 0x33, 0x17, 0x47, 0x07, 0x03, 0x00, 0x4F, 0x4F, 0x35, 0x4E, 0x14, + 0x14, 0x03, 0x01, 0x03, 0x02, 0x51, 0x14, 0x03, 0x03, 0x02, 0x02, 0x07, + 0x14, 0x03, 0x02, 0x51, 0x14, 0x03, 0x04, 0x02, 0x02, 0x07, 0x14, 0x03, + 0x02, 0x51, 0x14, 0x03, 0x05, 0x02, 0x02, 0x07, 0x14, 0x03, 0x02, 0x51, + 0x03, 0x06, 0x02, 0x00, 0x02, 0x01, 0x02, 0x03, 0x02, 0x04, 0x02, 0x05, + 0x02, 0x06, 0x1E, 0x00, 0x00, 0x19, 0x19, 0x00, 0x00, 0x01, 0x0B, 0x00, + 0x00, 0x01, 0x00, 0x20, 0x14, 0x06, 0x08, 0x01, 0x01, 0x21, 0x20, 0x22, + 0x20, 0x04, 0x75, 0x13, 0x00, 0x00, 0x01, + T0_INT2(3 * BR_X509_BUFSIZE_SIG), 0x00, 0x01, 0x01, 0x87, 0xFF, 0xFF, + 0x7F, 0x54, 0x57, 0x01, 0x02, 0x3E, 0x55, 0x01, 0x01, 0x0E, 0x06, 0x02, + 0x30, 0x16, 0x57, 0x01, 0x02, 0x19, 0x0D, 0x06, 0x06, 0x13, 0x3B, 0x44, + 0x32, 0x04, 0x1C, 0x01, 0x04, 0x19, 0x0D, 0x06, 0x08, 0x13, 0x3B, 0x01, + 0x00, 0x41, 0x31, 0x04, 0x0E, 0x01, 0x10, 0x19, 0x0D, 0x06, 0x05, 0x13, + 0x3A, 0x42, 0x04, 0x03, 0x30, 0x16, 0x13, 0x03, 0x00, 0x3F, 0x02, 0x00, + 0x34, 0x1F, 0x5A, 0x27, 0x16, 0x00, 0x01, 0x45, 0x0A, 0x06, 0x02, 0x29, + 0x16, 0x14, 0x03, 0x00, 0x08, 0x02, 0x00, 0x00, 0x00, 0x57, 0x01, 0x06, + 0x3E, 0x56, 0x00, 0x00, 0x20, 0x14, 0x06, 0x07, 0x1A, 0x14, 0x06, 0x01, + 0x12, 0x04, 0x76, 0x24, 0x00, 0x00, 0x4B, 0x05, 0x02, 0x30, 0x16, 0x37, + 0x15, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x38, 0x15, 0x06, 0x04, 0x01, + 0x18, 0x04, 0x0A, 0x39, 0x15, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, 0x30, + 0x16, 0x00, 0x00, 0x1C, 0x57, 0x01, 0x02, 0x3E, 0x09, 0x50, 0x00, 0x00, + 0x35, 0x4E, 0x13, 0x00, 0x03, 0x14, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, + 0x53, 0x59, 0x14, 0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x14, + 0x01, 0x00, 0x0D, 0x06, 0x0B, 0x13, 0x14, 0x05, 0x04, 0x13, 0x01, 0x00, + 0x00, 0x59, 0x04, 0x6F, 0x02, 0x01, 0x14, 0x05, 0x02, 0x2B, 0x16, 0x23, + 0x03, 0x01, 0x02, 0x02, 0x1F, 0x02, 0x02, 0x22, 0x03, 0x02, 0x14, 0x06, + 0x03, 0x59, 0x04, 0x68, 0x13, 0x02, 0x00, 0x02, 0x01, 0x08, 0x00, 0x00, + 0x14, 0x35, 0x1C, 0x08, 0x20, 0x1C, 0x07, 0x20, 0x4E, 0x00, 0x01, 0x59, + 0x14, 0x01, 0x81, 0x00, 0x0A, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x08, + 0x14, 0x05, 0x02, 0x28, 0x16, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x0E, 0x06, 0x19, 0x02, 0x00, 0x23, 0x03, 0x00, 0x14, 0x01, 0x83, + 0xFF, 0xFF, 0x7F, 0x0E, 0x06, 0x02, 0x29, 0x16, 0x01, 0x08, 0x0B, 0x20, + 0x59, 0x1C, 0x07, 0x04, 0x60, 0x00, 0x00, 0x52, 0x4A, 0x00, 0x00, 0x57, + 0x3C, 0x53, 0x00, 0x01, 0x53, 0x14, 0x05, 0x02, 0x2E, 0x16, 0x59, 0x14, + 0x01, 0x81, 0x00, 0x0F, 0x06, 0x02, 0x2E, 0x16, 0x03, 0x00, 0x14, 0x06, + 0x16, 0x59, 0x02, 0x00, 0x14, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x0F, 0x06, + 0x02, 0x2E, 0x16, 0x01, 0x08, 0x0B, 0x07, 0x03, 0x00, 0x04, 0x67, 0x13, + 0x02, 0x00, 0x00, 0x00, 0x53, 0x14, 0x01, 0x81, 0x7F, 0x0E, 0x06, 0x08, + 0x5C, 0x01, 0x00, 0x36, 0x1F, 0x01, 0x00, 0x00, 0x14, 0x36, 0x1F, 0x36, + 0x22, 0x4C, 0x01, 0x7F, 0x00, 0x01, 0x59, 0x03, 0x00, 0x02, 0x00, 0x01, + 0x05, 0x10, 0x01, 0x01, 0x11, 0x18, 0x02, 0x00, 0x01, 0x06, 0x10, 0x14, + 0x01, 0x01, 0x11, 0x06, 0x02, 0x25, 0x16, 0x01, 0x04, 0x0B, 0x02, 0x00, + 0x01, 0x1F, 0x11, 0x14, 0x01, 0x1F, 0x0D, 0x06, 0x02, 0x26, 0x16, 0x07, + 0x00, 0x00, 0x14, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x57, 0x00, + 0x00, 0x14, 0x05, 0x02, 0x29, 0x16, 0x23, 0x5A, 0x00, 0x00, 0x1B, 0x14, + 0x01, 0x00, 0x0F, 0x06, 0x01, 0x00, 0x13, 0x12, 0x04, 0x74, 0x00, 0x01, + 0x01, 0x00, 0x00, 0x5D, 0x13, 0x00, 0x00, 0x14, 0x06, 0x07, 0x5E, 0x14, + 0x06, 0x01, 0x12, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x19, 0x1A, 0x09, + 0x24, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 14, + 18, + 22, + 26, + 30, + 34, + 38, + 42, + 46, + 50, + 54, + 58, + 62, + 66, + 70, + 75, + 80, + 84, + 89, + 93, + 97, + 101, + 107, + 113, + 118, + 126, + 134, + 140, + 163, + 244, + 311, + 329, + 404, + 408, + 412, + 429, + 434, + 505, + 519, + 526, + 540, + 573, + 582, + 587, + 654, + 665, + 721, + 725, + 730, + 778, + 804, + 848, + 859, + 868, + 881, + 885, + 889, + 901 +}; + +#define T0_INTERPRETED 34 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_skey_decoder_init_main, 73) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_skey_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 8: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 9: { + /* -rot */ + T0_NROT(); + } + break; + case 10: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 11: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 12: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 13: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 14: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 15: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 16: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 17: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 18: { + /* co */ + T0_CO(); + } + break; + case 19: { + /* drop */ + (void)T0_POP(); + } + break; + case 20: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 21: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 22: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 23: { + /* get8 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*((unsigned char *)CTX + addr)); + + } + break; + case 24: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 25: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 26: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 27: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + CTX->hlen --; + T0_PUSH(pgm_read_byte(CTX->hbuf ++)); + } + + } + break; + case 28: { + /* rot */ + T0_ROT(); + } + break; + case 29: { + /* set-ec-key */ + + size_t xlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->key.ec.curve = curve; + CTX->key.ec.x = CTX->key_data; + CTX->key.ec.xlen = xlen; + + } + break; + case 30: { + /* set-rsa-key */ + + size_t iqlen = T0_POP(); + size_t dqlen = T0_POP(); + size_t dplen = T0_POP(); + size_t qlen = T0_POP(); + size_t plen = T0_POP(); + uint32_t n_bitlen = T0_POP(); + size_t off; + + CTX->key.rsa.n_bitlen = n_bitlen; + CTX->key.rsa.p = CTX->key_data; + CTX->key.rsa.plen = plen; + off = plen; + CTX->key.rsa.q = CTX->key_data + off; + CTX->key.rsa.qlen = qlen; + off += qlen; + CTX->key.rsa.dp = CTX->key_data + off; + CTX->key.rsa.dplen = dplen; + off += dplen; + CTX->key.rsa.dq = CTX->key_data + off; + CTX->key.rsa.dqlen = dqlen; + off += dqlen; + CTX->key.rsa.iq = CTX->key_data + off; + CTX->key.rsa.iqlen = iqlen; + + } + break; + case 31: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 32: { + /* swap */ + T0_SWAP(); + } + break; + case 33: { + /* u>> */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x >> c); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c new file mode 100644 index 0000000000..b738755f5c --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_decoder.c @@ -0,0 +1,790 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_x509_decoder_init_main(void *t0ctx); + +void br_x509_decoder_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +#define CTX ((br_x509_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_x509_decoder_context, cpu))) +#define CONTEXT_NAME br_x509_decoder_context + +/* see bearssl_x509.h */ +void +br_x509_decoder_init(br_x509_decoder_context *ctx, + void (*append_dn)(void *ctx, const void *buf, size_t len), + void *append_dn_ctx, + void (*append_in)(void *ctx, const void *buf, size_t len), + void *append_in_ctx) +{ + memset(ctx, 0, sizeof *ctx); + /* obsolete + ctx->err = 0; + ctx->hbuf = NULL; + ctx->hlen = 0; + */ + ctx->append_dn = append_dn; + ctx->append_dn_ctx = append_dn_ctx; + ctx->append_in = append_in; + ctx->append_in_ctx = append_in_ctx; + ctx->cpu.dp = &ctx->dp_stack[0]; + ctx->cpu.rp = &ctx->rp_stack[0]; + br_x509_decoder_init_main(&ctx->cpu); + br_x509_decoder_run(&ctx->cpu); +} + +/* see bearssl_x509.h */ +void +br_x509_decoder_push(br_x509_decoder_context *ctx, + const void *data, size_t len) +{ + ctx->hbuf = data; + ctx->hlen = len; + br_x509_decoder_run(&ctx->cpu); +} + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, + 0x0D, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, + 0x05, 0x2B, 0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x04, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48, + 0xCE, 0x3D, 0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, + 0x03, 0x04, 0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, 0x0F, 0x1F, + 0x12, 0xFE, 0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, 0x26, 0x1E, + 0x29, 0xDF, 0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, 0x0F, 0x3F, + 0x13, 0x1E, 0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, 0x26, 0x3E, + 0x29, 0xFF, 0x03, 0x55, 0x1D, 0x13 +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, 0x00, 0x11, 0x00, 0x00, 0x01, + 0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, 0x00, 0x00, 0x1A, 0x1A, 0x00, + 0x00, 0x01, T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, copy_dn)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, copy_in)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, decoded)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, isCA)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_x509_decoder_context, pkey_data)), 0x01, + T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notafter_days)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notafter_seconds)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notbefore_days)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, notbefore_seconds)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, signer_hash_id)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, signer_key_type)), 0x00, 0x00, 0x01, + 0x80, 0x45, 0x00, 0x00, 0x01, 0x80, 0x4E, 0x00, 0x00, 0x01, 0x80, 0x54, + 0x00, 0x00, 0x01, 0x81, 0x36, 0x00, 0x02, 0x03, 0x00, 0x03, 0x01, 0x1B, + 0x02, 0x01, 0x13, 0x26, 0x02, 0x00, 0x0F, 0x15, 0x00, 0x00, 0x05, 0x02, + 0x34, 0x1D, 0x00, 0x00, 0x06, 0x02, 0x35, 0x1D, 0x00, 0x00, 0x01, 0x10, + 0x50, 0x00, 0x00, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x4D, 0x00, 0x00, 0x11, + 0x05, 0x02, 0x38, 0x1D, 0x4E, 0x00, 0x00, 0x06, 0x02, 0x30, 0x1D, 0x00, + 0x00, 0x1B, 0x19, 0x01, 0x08, 0x0E, 0x26, 0x29, 0x19, 0x09, 0x00, 0x00, + 0x01, 0x30, 0x0A, 0x1B, 0x01, 0x00, 0x01, 0x09, 0x4C, 0x05, 0x02, 0x2F, + 0x1D, 0x00, 0x00, 0x20, 0x20, 0x00, 0x00, 0x01, 0x80, 0x5A, 0x00, 0x00, + 0x01, 0x80, 0x62, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, 0x01, 0x80, + 0x74, 0x00, 0x00, 0x01, 0x80, 0x7D, 0x00, 0x00, 0x01, 0x3D, 0x00, 0x00, + 0x20, 0x11, 0x06, 0x04, 0x2B, 0x6C, 0x7B, 0x72, 0x00, 0x04, 0x01, 0x00, + 0x3E, 0x25, 0x01, 0x00, 0x3C, 0x25, 0x01, 0x00, 0x3D, 0x25, 0x01, 0x87, + 0xFF, 0xFF, 0x7F, 0x6E, 0x6E, 0x71, 0x1B, 0x01, 0x20, 0x11, 0x06, 0x11, + 0x1A, 0x4D, 0x6C, 0x71, 0x01, 0x02, 0x51, 0x6F, 0x01, 0x02, 0x12, 0x06, + 0x02, 0x39, 0x1D, 0x52, 0x71, 0x01, 0x02, 0x51, 0x6D, 0x6E, 0x7B, 0x01, + 0x01, 0x3D, 0x25, 0x6E, 0x7B, 0x01, 0x00, 0x3D, 0x25, 0x6E, 0x66, 0x44, + 0x24, 0x43, 0x24, 0x66, 0x42, 0x24, 0x41, 0x24, 0x52, 0x01, 0x01, 0x3C, + 0x25, 0x6E, 0x7B, 0x01, 0x00, 0x3C, 0x25, 0x6E, 0x6E, 0x61, 0x05, 0x02, + 0x39, 0x1D, 0x75, 0x1C, 0x06, 0x1C, 0x7B, 0x62, 0x6E, 0x40, 0x69, 0x03, + 0x00, 0x40, 0x26, 0x02, 0x00, 0x09, 0x26, 0x02, 0x00, 0x0A, 0x69, 0x03, + 0x01, 0x52, 0x52, 0x02, 0x00, 0x02, 0x01, 0x18, 0x04, 0x1E, 0x5B, 0x1C, + 0x06, 0x18, 0x65, 0x03, 0x02, 0x52, 0x62, 0x1B, 0x03, 0x03, 0x1B, 0x40, + 0x23, 0x0D, 0x06, 0x02, 0x33, 0x1D, 0x63, 0x02, 0x02, 0x02, 0x03, 0x17, + 0x04, 0x02, 0x39, 0x1D, 0x52, 0x01, 0x00, 0x3F, 0x25, 0x72, 0x01, 0x21, + 0x5C, 0x01, 0x22, 0x5C, 0x1B, 0x01, 0x23, 0x11, 0x06, 0x28, 0x1A, 0x4D, + 0x6C, 0x6E, 0x1B, 0x06, 0x1D, 0x6E, 0x61, 0x1A, 0x71, 0x1B, 0x01, 0x01, + 0x11, 0x06, 0x03, 0x64, 0x1A, 0x71, 0x01, 0x04, 0x51, 0x6C, 0x4B, 0x1C, + 0x06, 0x03, 0x60, 0x04, 0x01, 0x7C, 0x52, 0x52, 0x04, 0x60, 0x52, 0x52, + 0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02, 0x38, 0x1D, 0x1A, 0x52, 0x6E, + 0x61, 0x06, 0x80, 0x63, 0x76, 0x1C, 0x06, 0x06, 0x01, 0x02, 0x3B, 0x04, + 0x80, 0x57, 0x77, 0x1C, 0x06, 0x06, 0x01, 0x03, 0x3B, 0x04, 0x80, 0x4D, + 0x78, 0x1C, 0x06, 0x06, 0x01, 0x04, 0x3B, 0x04, 0x80, 0x43, 0x79, 0x1C, + 0x06, 0x05, 0x01, 0x05, 0x3B, 0x04, 0x3A, 0x7A, 0x1C, 0x06, 0x05, 0x01, + 0x06, 0x3B, 0x04, 0x31, 0x56, 0x1C, 0x06, 0x05, 0x01, 0x02, 0x3A, 0x04, + 0x28, 0x57, 0x1C, 0x06, 0x05, 0x01, 0x03, 0x3A, 0x04, 0x1F, 0x58, 0x1C, + 0x06, 0x05, 0x01, 0x04, 0x3A, 0x04, 0x16, 0x59, 0x1C, 0x06, 0x05, 0x01, + 0x05, 0x3A, 0x04, 0x0D, 0x5A, 0x1C, 0x06, 0x05, 0x01, 0x06, 0x3A, 0x04, + 0x04, 0x01, 0x00, 0x01, 0x00, 0x04, 0x04, 0x01, 0x00, 0x01, 0x00, 0x47, + 0x25, 0x46, 0x25, 0x7B, 0x62, 0x7B, 0x52, 0x1A, 0x01, 0x01, 0x3E, 0x25, + 0x74, 0x30, 0x1D, 0x00, 0x00, 0x01, 0x81, 0x06, 0x00, 0x01, 0x55, 0x0D, + 0x06, 0x02, 0x32, 0x1D, 0x1B, 0x03, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x00, + 0x6E, 0x72, 0x1B, 0x01, 0x01, 0x11, 0x06, 0x08, 0x64, 0x01, 0x01, 0x15, + 0x3F, 0x25, 0x04, 0x01, 0x2B, 0x7B, 0x00, 0x00, 0x71, 0x01, 0x06, 0x51, + 0x70, 0x00, 0x00, 0x71, 0x01, 0x03, 0x51, 0x6C, 0x73, 0x06, 0x02, 0x37, + 0x1D, 0x00, 0x00, 0x26, 0x1B, 0x06, 0x07, 0x21, 0x1B, 0x06, 0x01, 0x16, + 0x04, 0x76, 0x2B, 0x00, 0x00, 0x01, 0x01, 0x51, 0x6B, 0x01, 0x01, 0x10, + 0x06, 0x02, 0x2C, 0x1D, 0x73, 0x27, 0x00, 0x00, 0x61, 0x05, 0x02, 0x39, + 0x1D, 0x48, 0x1C, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x49, 0x1C, 0x06, + 0x04, 0x01, 0x18, 0x04, 0x0A, 0x4A, 0x1C, 0x06, 0x04, 0x01, 0x19, 0x04, + 0x02, 0x39, 0x1D, 0x00, 0x04, 0x71, 0x1B, 0x01, 0x17, 0x01, 0x18, 0x4C, + 0x05, 0x02, 0x2F, 0x1D, 0x01, 0x18, 0x11, 0x03, 0x00, 0x4E, 0x6C, 0x67, + 0x02, 0x00, 0x06, 0x0C, 0x01, 0x80, 0x64, 0x08, 0x03, 0x01, 0x67, 0x02, + 0x01, 0x09, 0x04, 0x0E, 0x1B, 0x01, 0x32, 0x0D, 0x06, 0x04, 0x01, 0x80, + 0x64, 0x09, 0x01, 0x8E, 0x6C, 0x09, 0x03, 0x01, 0x02, 0x01, 0x01, 0x82, + 0x6D, 0x08, 0x02, 0x01, 0x01, 0x03, 0x09, 0x01, 0x04, 0x0C, 0x09, 0x02, + 0x01, 0x01, 0x80, 0x63, 0x09, 0x01, 0x80, 0x64, 0x0C, 0x0A, 0x02, 0x01, + 0x01, 0x83, 0x0F, 0x09, 0x01, 0x83, 0x10, 0x0C, 0x09, 0x03, 0x03, 0x01, + 0x01, 0x01, 0x0C, 0x68, 0x2A, 0x01, 0x01, 0x0E, 0x02, 0x01, 0x01, 0x04, + 0x07, 0x28, 0x02, 0x01, 0x01, 0x80, 0x64, 0x07, 0x27, 0x02, 0x01, 0x01, + 0x83, 0x10, 0x07, 0x28, 0x1F, 0x15, 0x06, 0x03, 0x01, 0x18, 0x09, 0x5E, + 0x09, 0x53, 0x1B, 0x01, 0x05, 0x14, 0x02, 0x03, 0x09, 0x03, 0x03, 0x01, + 0x1F, 0x15, 0x01, 0x01, 0x26, 0x68, 0x02, 0x03, 0x09, 0x2A, 0x03, 0x03, + 0x01, 0x00, 0x01, 0x17, 0x68, 0x01, 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01, + 0x00, 0x01, 0x3B, 0x68, 0x01, 0x3C, 0x08, 0x02, 0x02, 0x09, 0x03, 0x02, + 0x01, 0x00, 0x01, 0x3C, 0x68, 0x02, 0x02, 0x09, 0x03, 0x02, 0x73, 0x1B, + 0x01, 0x2E, 0x11, 0x06, 0x0D, 0x1A, 0x73, 0x1B, 0x01, 0x30, 0x01, 0x39, + 0x4C, 0x06, 0x03, 0x1A, 0x04, 0x74, 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02, + 0x2F, 0x1D, 0x52, 0x02, 0x03, 0x02, 0x02, 0x00, 0x01, 0x73, 0x54, 0x01, + 0x0A, 0x08, 0x03, 0x00, 0x73, 0x54, 0x02, 0x00, 0x09, 0x00, 0x02, 0x03, + 0x00, 0x03, 0x01, 0x67, 0x1B, 0x02, 0x01, 0x02, 0x00, 0x4C, 0x05, 0x02, + 0x2F, 0x1D, 0x00, 0x00, 0x23, 0x71, 0x01, 0x02, 0x51, 0x0B, 0x6A, 0x00, + 0x03, 0x1B, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0x6C, 0x73, 0x1B, 0x01, + 0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x1B, 0x01, 0x00, 0x11, 0x06, + 0x0B, 0x1A, 0x1B, 0x05, 0x04, 0x1A, 0x01, 0x00, 0x00, 0x73, 0x04, 0x6F, + 0x02, 0x01, 0x1B, 0x05, 0x02, 0x33, 0x1D, 0x2A, 0x03, 0x01, 0x02, 0x02, + 0x25, 0x02, 0x02, 0x29, 0x03, 0x02, 0x1B, 0x06, 0x03, 0x73, 0x04, 0x68, + 0x1A, 0x02, 0x00, 0x02, 0x01, 0x0A, 0x00, 0x01, 0x73, 0x1B, 0x01, 0x81, + 0x00, 0x0D, 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x0A, 0x1B, 0x05, 0x02, + 0x31, 0x1D, 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x12, 0x06, + 0x19, 0x02, 0x00, 0x2A, 0x03, 0x00, 0x1B, 0x01, 0x83, 0xFF, 0xFF, 0x7F, + 0x12, 0x06, 0x02, 0x32, 0x1D, 0x01, 0x08, 0x0E, 0x26, 0x73, 0x23, 0x09, + 0x04, 0x60, 0x00, 0x00, 0x6B, 0x5F, 0x00, 0x00, 0x6C, 0x7B, 0x00, 0x00, + 0x71, 0x4F, 0x6C, 0x00, 0x01, 0x6C, 0x1B, 0x05, 0x02, 0x36, 0x1D, 0x73, + 0x1B, 0x01, 0x81, 0x00, 0x13, 0x06, 0x02, 0x36, 0x1D, 0x03, 0x00, 0x1B, + 0x06, 0x16, 0x73, 0x02, 0x00, 0x1B, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13, + 0x06, 0x02, 0x36, 0x1D, 0x01, 0x08, 0x0E, 0x09, 0x03, 0x00, 0x04, 0x67, + 0x1A, 0x02, 0x00, 0x00, 0x00, 0x6C, 0x1B, 0x01, 0x81, 0x7F, 0x12, 0x06, + 0x08, 0x7B, 0x01, 0x00, 0x45, 0x25, 0x01, 0x00, 0x00, 0x1B, 0x45, 0x25, + 0x45, 0x29, 0x63, 0x01, 0x7F, 0x00, 0x01, 0x73, 0x03, 0x00, 0x02, 0x00, + 0x01, 0x05, 0x14, 0x01, 0x01, 0x15, 0x1E, 0x02, 0x00, 0x01, 0x06, 0x14, + 0x1B, 0x01, 0x01, 0x15, 0x06, 0x02, 0x2D, 0x1D, 0x01, 0x04, 0x0E, 0x02, + 0x00, 0x01, 0x1F, 0x15, 0x1B, 0x01, 0x1F, 0x11, 0x06, 0x02, 0x2E, 0x1D, + 0x09, 0x00, 0x00, 0x1B, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x71, + 0x00, 0x00, 0x1B, 0x05, 0x02, 0x32, 0x1D, 0x2A, 0x74, 0x00, 0x00, 0x22, + 0x1B, 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x1A, 0x16, 0x04, 0x74, 0x00, + 0x01, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, 0x15, 0x00, 0x00, + 0x01, 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, 0x33, 0x00, 0x00, + 0x7C, 0x1A, 0x00, 0x00, 0x1B, 0x06, 0x07, 0x7D, 0x1B, 0x06, 0x01, 0x16, + 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x20, 0x21, 0x0B, 0x2B, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 24, + 28, + 32, + 36, + 40, + 44, + 48, + 52, + 56, + 60, + 64, + 68, + 72, + 76, + 80, + 84, + 88, + 93, + 98, + 103, + 108, + 116, + 121, + 126, + 131, + 136, + 141, + 146, + 151, + 156, + 161, + 166, + 171, + 186, + 192, + 198, + 203, + 211, + 219, + 225, + 236, + 251, + 255, + 260, + 265, + 270, + 275, + 280, + 284, + 294, + 637, + 642, + 656, + 676, + 683, + 695, + 709, + 724, + 757, + 977, + 991, + 1008, + 1017, + 1084, + 1140, + 1144, + 1148, + 1153, + 1201, + 1227, + 1271, + 1282, + 1291, + 1304, + 1308, + 1312, + 1316, + 1320, + 1324, + 1328, + 1332, + 1344 +}; + +#define T0_INTERPRETED 39 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_x509_decoder_init_main, 93) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_x509_decoder_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* %25 */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a % b); + + } + break; + case 8: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 9: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 10: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 11: { + /* -rot */ + T0_NROT(); + } + break; + case 12: { + /* / */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a / b); + + } + break; + case 13: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 14: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 15: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 16: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 17: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 18: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 19: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 20: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 21: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 22: { + /* co */ + T0_CO(); + } + break; + case 23: { + /* copy-ec-pkey */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + CTX->pkey.key_type = BR_KEYTYPE_EC; + CTX->pkey.key.ec.curve = curve; + CTX->pkey.key.ec.q = CTX->pkey_data; + CTX->pkey.key.ec.qlen = qlen; + + } + break; + case 24: { + /* copy-rsa-pkey */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + CTX->pkey.key_type = BR_KEYTYPE_RSA; + CTX->pkey.key.rsa.n = CTX->pkey_data; + CTX->pkey.key.rsa.nlen = nlen; + CTX->pkey.key.rsa.e = CTX->pkey_data + nlen; + CTX->pkey.key.rsa.elen = elen; + + } + break; + case 25: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 26: { + /* drop */ + (void)T0_POP(); + } + break; + case 27: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 28: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 29: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 30: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 31: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 32: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 33: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy_P((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + if (CTX->copy_dn && CTX->append_dn) { + CTX->append_dn(CTX->append_dn_ctx, CTX->hbuf, clen); + } + if (CTX->copy_in && CTX->append_in) { + CTX->append_in(CTX->append_in_ctx, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 34: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + unsigned char x = pgm_read_byte(CTX->hbuf ++); + if (CTX->copy_dn && CTX->append_dn) { + CTX->append_dn(CTX->append_dn_ctx, &x, 1); + } + if (CTX->copy_in && CTX->append_in) { + CTX->append_in(CTX->append_in_ctx, &x, 1); + } + CTX->hlen --; + T0_PUSH(x); + } + + } + break; + case 35: { + /* rot */ + T0_ROT(); + } + break; + case 36: { + /* set32 */ + + uint32_t addr = T0_POP(); + *(uint32_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 37: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 38: { + /* swap */ + T0_SWAP(); + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c new file mode 100644 index 0000000000..b4443baf83 --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_knownkey.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +void +br_x509_knownkey_init_rsa(br_x509_knownkey_context *ctx, + const br_rsa_public_key *pk, unsigned usages) +{ + ctx->vtable = &br_x509_knownkey_vtable; + ctx->pkey.key_type = BR_KEYTYPE_RSA; + ctx->pkey.key.rsa = *pk; + ctx->usages = usages; +} + +/* see bearssl_x509.h */ +void +br_x509_knownkey_init_ec(br_x509_knownkey_context *ctx, + const br_ec_public_key *pk, unsigned usages) +{ + ctx->vtable = &br_x509_knownkey_vtable; + ctx->pkey.key_type = BR_KEYTYPE_EC; + ctx->pkey.key.ec = *pk; + ctx->usages = usages; +} + +static void +kk_start_chain(const br_x509_class **ctx, const char *server_name) +{ + (void)ctx; + (void)server_name; +} + +static void +kk_start_cert(const br_x509_class **ctx, uint32_t length) +{ + (void)ctx; + (void)length; +} + +static void +kk_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) +{ + (void)ctx; + (void)buf; + (void)len; +} + +static void +kk_end_cert(const br_x509_class **ctx) +{ + (void)ctx; +} + +static unsigned +kk_end_chain(const br_x509_class **ctx) +{ + (void)ctx; + return 0; +} + +static const br_x509_pkey * +kk_get_pkey(const br_x509_class *const *ctx, unsigned *usages) +{ + const br_x509_knownkey_context *xc; + + xc = (const br_x509_knownkey_context *)ctx; + if (usages != NULL) { + *usages = xc->usages; + } + return &xc->pkey; +} + +/* see bearssl_x509.h */ +const br_x509_class br_x509_knownkey_vtable PROGMEM = { + sizeof(br_x509_knownkey_context), + kk_start_chain, + kk_start_cert, + kk_append, + kk_end_cert, + kk_end_chain, + kk_get_pkey +}; diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c new file mode 100644 index 0000000000..0d92e7b25d --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal.c @@ -0,0 +1,1777 @@ +/* Automatically generated code; do not modify directly. */ + +#include +#include +#include + +typedef struct { + uint32_t *dp; + uint32_t *rp; + const unsigned char *ip; +} t0_context; + +static uint32_t +t0_parse7E_unsigned(const unsigned char **p) +{ + uint32_t x; + + x = 0; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + return x; + } + } +} + +static int32_t +t0_parse7E_signed(const unsigned char **p) +{ + int neg; + uint32_t x; + + neg = (pgm_read_byte(*p) >> 6) & 1; + x = (uint32_t)-neg; + for (;;) { + unsigned y; + + y = pgm_read_byte((*p)++); + x = (x << 7) | (uint32_t)(y & 0x7F); + if (y < 0x80) { + if (neg) { + return -(int32_t)~x - 1; + } else { + return (int32_t)x; + } + } + } +} + +#define T0_VBYTE(x, n) (unsigned char)((((uint32_t)(x) >> (n)) & 0x7F) | 0x80) +#define T0_FBYTE(x, n) (unsigned char)(((uint32_t)(x) >> (n)) & 0x7F) +#define T0_SBYTE(x) (unsigned char)((((uint32_t)(x) >> 28) + 0xF8) ^ 0xF8) +#define T0_INT1(x) T0_FBYTE(x, 0) +#define T0_INT2(x) T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT3(x) T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT4(x) T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) +#define T0_INT5(x) T0_SBYTE(x), T0_VBYTE(x, 21), T0_VBYTE(x, 14), T0_VBYTE(x, 7), T0_FBYTE(x, 0) + +/* static const unsigned char t0_datablock[]; */ + + +void br_x509_minimal_init_main(void *t0ctx); + +void br_x509_minimal_run(void *t0ctx); + + + +#include "t_inner.h" + + + + + +#include "t_inner.h" + +/* + * Implementation Notes + * -------------------- + * + * The C code pushes the data by chunks; all decoding is done in the + * T0 code. The cert_length value is set to the certificate length when + * a new certificate is started; the T0 code picks it up as outer limit, + * and decoding functions use it to ensure that no attempt is made at + * reading past it. The T0 code also checks that once the certificate is + * decoded, there are no trailing bytes. + * + * The T0 code sets cert_length to 0 when the certificate is fully + * decoded. + * + * The C code must still perform two checks: + * + * -- If the certificate length is 0, then the T0 code will not be + * invoked at all. This invalid condition must thus be reported by the + * C code. + * + * -- When reaching the end of certificate, the C code must verify that + * the certificate length has been set to 0, thereby signaling that + * the T0 code properly decoded a certificate. + * + * Processing of a chain works in the following way: + * + * -- The error flag is set to a non-zero value when validation is + * finished. The value is either BR_ERR_X509_OK (validation is + * successful) or another non-zero error code. When a non-zero error + * code is obtained, the remaining bytes in the current certificate and + * the subsequent certificates (if any) are completely ignored. + * + * -- Each certificate is decoded in due course, with the following + * "interesting points": + * + * -- Start of the TBS: the multihash engine is reset and activated. + * + * -- Start of the issuer DN: the secondary hash engine is started, + * to process the encoded issuer DN. + * + * -- End of the issuer DN: the secondary hash engine is stopped. The + * resulting hash value is computed and then copied into the + * next_dn_hash[] buffer. + * + * -- Start of the subject DN: the secondary hash engine is started, + * to process the encoded subject DN. + * + * -- For the EE certificate only: the Common Name, if any, is matched + * against the expected server name. + * + * -- End of the subject DN: the secondary hash engine is stopped. The + * resulting hash value is computed into the pad. It is then processed: + * + * -- If this is the EE certificate, then the hash is ignored + * (except for direct trust processing, see later; the hash is + * simply left in current_dn_hash[]). + * + * -- Otherwise, the hashed subject DN is compared with the saved + * hash value (in saved_dn_hash[]). They must match. + * + * Either way, the next_dn_hash[] value is then copied into the + * saved_dn_hash[] value. Thus, at that point, saved_dn_hash[] + * contains the hash of the issuer DN for the current certificate, + * and current_dn_hash[] contains the hash of the subject DN for the + * current certificate. + * + * -- Public key: it is decoded into the cert_pkey[] buffer. Unknown + * key types are reported at that point. + * + * -- If this is the EE certificate, then the key type is compared + * with the expected key type (initialization parameter). The public + * key data is copied to ee_pkey_data[]. The key and hashed subject + * DN are also compared with the "direct trust" keys; if the key + * and DN are matched, then validation ends with a success. + * + * -- Otherwise, the saved signature (cert_sig[]) is verified + * against the saved TBS hash (tbs_hash[]) and that freshly + * decoded public key. Failure here ends validation with an error. + * + * -- Extensions: extension values are processed in due order. + * + * -- Basic Constraints: for all certificates except EE, must be + * present, indicate a CA, and have a path length compatible with + * the chain length so far. + * + * -- Key Usage: for the EE, if present, must allow signatures + * or encryption/key exchange, as required for the cipher suite. + * For non-EE, if present, must have the "certificate sign" bit. + * + * -- Subject Alt Name: for the EE, dNSName names are matched + * against the server name. Ignored for non-EE. + * + * -- Authority Key Identifier, Subject Key Identifier, Issuer + * Alt Name, Subject Directory Attributes, CRL Distribution Points + * Freshest CRL, Authority Info Access and Subject Info Access + * extensions are always ignored: they either contain only + * informative data, or they relate to revocation processing, which + * we explicitly do not support. + * + * -- All other extensions are ignored if non-critical. If a + * critical extension other than the ones above is encountered, + * then a failure is reported. + * + * -- End of the TBS: the multihash engine is stopped. + * + * -- Signature algorithm: the signature algorithm on the + * certificate is decoded. A failure is reported if that algorithm + * is unknown. The hashed TBS corresponding to the signature hash + * function is computed and stored in tbs_hash[] (if not supported, + * then a failure is reported). The hash OID and length are stored + * in cert_sig_hash_oid and cert_sig_hash_len. + * + * -- Signature value: the signature value is copied into the + * cert_sig[] array. + * + * -- Certificate end: the hashed issuer DN (saved_dn_hash[]) is + * looked up in the trust store (CA trust anchors only); for all + * that match, the signature (cert_sig[]) is verified against the + * anchor public key (hashed TBS is in tbs_hash[]). If one of these + * signatures is valid, then validation ends with a success. + * + * -- If the chain end is reached without obtaining a validation success, + * then validation is reported as failed. + */ + +#if BR_USE_UNIX_TIME +#include +#endif + +#if BR_USE_WIN32_TIME +#include +#endif + +/* + * The T0 compiler will produce these prototypes declarations in the + * header. + * +void br_x509_minimal_init_main(void *ctx); +void br_x509_minimal_run(void *ctx); + */ + +/* see bearssl_x509.h */ +void +br_x509_minimal_init(br_x509_minimal_context *ctx, + const br_hash_class *dn_hash_impl, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + memset(ctx, 0, sizeof *ctx); + ctx->vtable = &br_x509_minimal_vtable; + ctx->dn_hash_impl = dn_hash_impl; + ctx->trust_anchors = trust_anchors; + ctx->trust_anchors_num = trust_anchors_num; +} + +static void +xm_start_chain(const br_x509_class **ctx, const char *server_name) +{ + br_x509_minimal_context *cc; + size_t u; + + cc = (br_x509_minimal_context *)(void *)ctx; + for (u = 0; u < cc->num_name_elts; u ++) { + cc->name_elts[u].status = 0; + cc->name_elts[u].buf[0] = 0; + } + memset(&cc->pkey, 0, sizeof cc->pkey); + cc->num_certs = 0; + cc->err = 0; + cc->cpu.dp = cc->dp_stack; + cc->cpu.rp = cc->rp_stack; + br_x509_minimal_init_main(&cc->cpu); + if (server_name == NULL || *server_name == 0) { + cc->server_name = NULL; + } else { + cc->server_name = server_name; + } +} + +static void +xm_start_cert(const br_x509_class **ctx, uint32_t length) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err != 0) { + return; + } + if (length == 0) { + cc->err = BR_ERR_X509_TRUNCATED; + return; + } + cc->cert_length = length; +} + +static void +xm_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err != 0) { + return; + } + cc->hbuf = buf; + cc->hlen = len; + br_x509_minimal_run(&cc->cpu); +} + +static void +xm_end_cert(const br_x509_class **ctx) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == 0 && cc->cert_length != 0) { + cc->err = BR_ERR_X509_TRUNCATED; + } + cc->num_certs ++; +} + +static unsigned +xm_end_chain(const br_x509_class **ctx) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == 0) { + if (cc->num_certs == 0) { + cc->err = BR_ERR_X509_EMPTY_CHAIN; + } else { + cc->err = BR_ERR_X509_NOT_TRUSTED; + } + } else if (cc->err == BR_ERR_X509_OK) { + return 0; + } + return (unsigned)cc->err; +} + +static const br_x509_pkey * +xm_get_pkey(const br_x509_class *const *ctx, unsigned *usages) +{ + br_x509_minimal_context *cc; + + cc = (br_x509_minimal_context *)(void *)ctx; + if (cc->err == BR_ERR_X509_OK + || cc->err == BR_ERR_X509_NOT_TRUSTED) + { + if (usages != NULL) { + *usages = cc->key_usages; + } + return &((br_x509_minimal_context *)(void *)ctx)->pkey; + } else { + return NULL; + } +} + +/* see bearssl_x509.h */ +const br_x509_class br_x509_minimal_vtable PROGMEM = { + sizeof(br_x509_minimal_context), + xm_start_chain, + xm_start_cert, + xm_append, + xm_end_cert, + xm_end_chain, + xm_get_pkey +}; + +#define CTX ((br_x509_minimal_context *)(void *)((unsigned char *)t0ctx - offsetof(br_x509_minimal_context, cpu))) +#define CONTEXT_NAME br_x509_minimal_context + +#define DNHASH_LEN ((CTX->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK) +#define dnhash_len ((ctx->dn_hash_impl->desc >> BR_HASHDESC_OUT_OFF) & BR_HASHDESC_OUT_MASK) + +/* + * Hash a DN (from a trust anchor) into the provided buffer. This uses the + * DN hash implementation and context structure from the X.509 engine + * context. + */ +static void +hash_dn(br_x509_minimal_context *ctx, const void *dn, size_t len, + unsigned char *out) +{ + ctx->dn_hash_impl->init(&ctx->dn_hash.vtable); + ctx->dn_hash_impl->update(&ctx->dn_hash.vtable, dn, len); + ctx->dn_hash_impl->out(&ctx->dn_hash.vtable, out); +} + +/* + * Compare two big integers for equality. The integers use unsigned big-endian + * encoding; extra leading bytes (of value 0) are allowed. + */ +static int +eqbigint(const unsigned char *b1, size_t len1, + const unsigned char *b2, size_t len2) +{ + while (len1 > 0 && *b1 == 0) { + b1 ++; + len1 --; + } + while (len2 > 0 && pgm_read_byte(b2) == 0) { + b2 ++; + len2 --; + } + if (len1 != len2) { + return 0; + } + return memcmp_P(b1, b2, len1) == 0; +} + +/* + * Compare two strings for equality, in a case-insensitive way. This + * function handles casing only for ASCII letters. + */ +static int +eqnocase(const void *s1, const void *s2, size_t len) +{ + const unsigned char *buf1, *buf2; + + buf1 = s1; + buf2 = s2; + while (len -- > 0) { + int x1, x2; + + x1 = *buf1 ++; + x2 = *buf2 ++; + if (x1 >= 'A' && x1 <= 'Z') { + x1 += 'a' - 'A'; + } + if (x2 >= 'A' && x2 <= 'Z') { + x2 += 'a' - 'A'; + } + if (x1 != x2) { + return 0; + } + } + return 1; +} + +static int verify_signature(br_x509_minimal_context *ctx, + const br_x509_pkey *pk); + +/* + * Check whether the current certificate (EE) is directly trusted against + * a single trust anchor + */ +static int check_single_direct_trust(br_x509_minimal_context *ctx, + unsigned char hashed_DN[64], + const br_x509_trust_anchor *ta) +{ + int kt; + + if (ta->flags & BR_X509_TA_CA) { + return 0; + } + if (memcmp(hashed_DN, ctx->current_dn_hash, dnhash_len)) { + return 0; + } + kt = ctx->pkey.key_type; + if ((pgm_read_byte(&ta->pkey.key_type) & 0x0F) != kt) { + return 0; + } + switch (kt) { + case BR_KEYTYPE_RSA: + if (!eqbigint(ctx->pkey.key.rsa.n, + ctx->pkey.key.rsa.nlen, + ta->pkey.key.rsa.n, + ta->pkey.key.rsa.nlen) + || !eqbigint(ctx->pkey.key.rsa.e, + ctx->pkey.key.rsa.elen, + ta->pkey.key.rsa.e, + ta->pkey.key.rsa.elen)) + { + return 0; + } + return 1; + case BR_KEYTYPE_EC: + if (ctx->pkey.key.ec.curve != ta->pkey.key.ec.curve + || ctx->pkey.key.ec.qlen != ta->pkey.key.ec.qlen + || memcmp_P(ctx->pkey.key.ec.q, + ta->pkey.key.ec.q, + ta->pkey.key.ec.qlen) != 0) + { + return 0; + } + return 1; + default: + return 0; + } + return 0; /* Should not get here */ +} + + +/* + * Check whether the current certificate (EE) is directly trusted against + * a single CA trust anchor. We use the issuer hash (in saved_dn_hash[]) + * as the CA identifier. + */ +static int check_single_trust_anchor_CA(br_x509_minimal_context *ctx, + unsigned char hashed_DN[64], + const br_x509_trust_anchor *ta) +{ + if (!(ta->flags & BR_X509_TA_CA)) { + return 0; + } + if (memcmp(hashed_DN, ctx->saved_dn_hash, dnhash_len)) { + return 0; + } + if (verify_signature(ctx, &ta->pkey) == 0) { + return 1; + } + return 0; +} + + + + +static const unsigned char t0_datablock[] PROGMEM = { + + 0x00, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x09, + 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x09, 0x2A, 0x86, + 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x0E, 0x09, 0x2A, 0x86, 0x48, 0x86, + 0xF7, 0x0D, 0x01, 0x01, 0x0B, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x01, 0x0C, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, + 0x0D, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x09, 0x60, 0x86, 0x48, 0x01, + 0x65, 0x03, 0x04, 0x02, 0x04, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, + 0x02, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x07, + 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01, 0x08, 0x2A, 0x86, 0x48, 0xCE, + 0x3D, 0x03, 0x01, 0x07, 0x05, 0x2B, 0x81, 0x04, 0x00, 0x22, 0x05, 0x2B, + 0x81, 0x04, 0x00, 0x23, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x01, + 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x01, 0x08, 0x2A, 0x86, + 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x02, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, + 0x04, 0x03, 0x03, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x04, 0x03, 0x04, + 0x03, 0x55, 0x04, 0x03, 0x00, 0x1F, 0x03, 0xFC, 0x07, 0x7F, 0x0B, 0x5E, + 0x0F, 0x1F, 0x12, 0xFE, 0x16, 0xBF, 0x1A, 0x9F, 0x1E, 0x7E, 0x22, 0x3F, + 0x26, 0x1E, 0x29, 0xDF, 0x00, 0x1F, 0x03, 0xFD, 0x07, 0x9F, 0x0B, 0x7E, + 0x0F, 0x3F, 0x13, 0x1E, 0x16, 0xDF, 0x1A, 0xBF, 0x1E, 0x9E, 0x22, 0x5F, + 0x26, 0x3E, 0x29, 0xFF, 0x03, 0x55, 0x1D, 0x13, 0x03, 0x55, 0x1D, 0x0F, + 0x03, 0x55, 0x1D, 0x11, 0x03, 0x55, 0x1D, 0x20, 0x08, 0x2B, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x02, 0x01, 0x03, 0x55, 0x1D, 0x23, 0x03, 0x55, 0x1D, + 0x0E, 0x03, 0x55, 0x1D, 0x12, 0x03, 0x55, 0x1D, 0x09, 0x03, 0x55, 0x1D, + 0x1F, 0x03, 0x55, 0x1D, 0x2E, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x01, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x0B +}; + +static const unsigned char t0_codeblock[] PROGMEM = { + + 0x00, 0x01, 0x00, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x00, 0x01, + 0x00, 0x11, 0x00, 0x00, 0x01, 0x01, 0x09, 0x00, 0x00, 0x01, 0x01, 0x0A, + 0x00, 0x00, 0x25, 0x25, 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_BOOLEAN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_DN), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_SERVER_NAME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_CLASS), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TAG_VALUE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_BAD_TIME), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_CRITICAL_EXTENSION), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_DN_MISMATCH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXPIRED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_EXTRA_ELEMENT), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_FORBIDDEN_KEY_USAGE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INDEFINITE_LENGTH), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_INNER_TRUNC), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_LIMIT_EXCEEDED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CA), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_CONSTRUCTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_NOT_PRIMITIVE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_OVERFLOW), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_PARTIAL_BYTE), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNEXPECTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_UNSUPPORTED), 0x00, 0x00, 0x01, + T0_INT1(BR_ERR_X509_WEAK_PUBLIC_KEY), 0x00, 0x00, 0x01, + T0_INT1(BR_KEYTYPE_EC), 0x00, 0x00, 0x01, T0_INT1(BR_KEYTYPE_RSA), + 0x00, 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_length)), 0x00, + 0x00, 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_len)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_hash_oid)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, cert_sig_len)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, cert_signer_key_type)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, current_dn_hash)), 0x00, 0x00, + 0x01, T0_INT2(offsetof(CONTEXT_NAME, key_usages)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(br_x509_minimal_context, pkey_data)), 0x01, + T0_INT2(BR_X509_BUFSIZE_KEY), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, min_rsa_size)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, next_dn_hash)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, num_certs)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, pad)), 0x00, 0x00, 0x01, + T0_INT2(offsetof(CONTEXT_NAME, saved_dn_hash)), 0x00, 0x00, 0x01, 0x80, + 0x73, 0x00, 0x00, 0x01, 0x80, 0x7C, 0x00, 0x00, 0x01, 0x81, 0x02, 0x00, + 0x00, 0x8F, 0x05, 0x05, 0x33, 0x41, 0x01, 0x00, 0x00, 0x33, 0x01, 0x0A, + 0x0E, 0x09, 0x01, 0x9A, 0xFF, 0xB8, 0x00, 0x0A, 0x00, 0x00, 0x01, 0x82, + 0x19, 0x00, 0x00, 0x01, 0x82, 0x01, 0x00, 0x00, 0x01, 0x81, 0x68, 0x00, + 0x02, 0x03, 0x00, 0x03, 0x01, 0x26, 0x02, 0x01, 0x13, 0x3A, 0x02, 0x00, + 0x0F, 0x15, 0x00, 0x00, 0x01, 0x81, 0x74, 0x00, 0x00, 0x05, 0x02, 0x51, + 0x29, 0x00, 0x00, 0x06, 0x02, 0x52, 0x29, 0x00, 0x00, 0x01, 0x10, 0x74, + 0x00, 0x00, 0x11, 0x05, 0x02, 0x55, 0x29, 0x71, 0x00, 0x00, 0x11, 0x05, + 0x02, 0x55, 0x29, 0x72, 0x00, 0x00, 0x06, 0x02, 0x4B, 0x29, 0x00, 0x00, + 0x01, 0x82, 0x11, 0x00, 0x00, 0x26, 0x21, 0x01, 0x08, 0x0E, 0x3A, 0x3F, + 0x21, 0x09, 0x00, 0x0B, 0x03, 0x00, 0x5A, 0x2B, 0xAC, 0x38, 0xAC, 0xB0, + 0x26, 0x01, 0x20, 0x11, 0x06, 0x11, 0x25, 0x71, 0xAA, 0xB0, 0x01, 0x02, + 0x75, 0xAD, 0x01, 0x02, 0x12, 0x06, 0x02, 0x56, 0x29, 0x76, 0xB0, 0x01, + 0x02, 0x75, 0xAB, 0xAC, 0xBF, 0x99, 0x64, 0x60, 0x22, 0x16, 0xAC, 0xA4, + 0x03, 0x01, 0x03, 0x02, 0xA4, 0x02, 0x02, 0x02, 0x01, 0x19, 0x06, 0x02, + 0x4A, 0x29, 0x76, 0x02, 0x00, 0x06, 0x05, 0x9A, 0x03, 0x03, 0x04, 0x09, + 0x99, 0x60, 0x67, 0x22, 0x28, 0x05, 0x02, 0x49, 0x29, 0x67, 0x64, 0x22, + 0x16, 0xAC, 0xAC, 0x9B, 0x05, 0x02, 0x56, 0x29, 0xB9, 0x27, 0x06, 0x27, + 0xBF, 0xA1, 0xAC, 0x62, 0xA7, 0x03, 0x05, 0x62, 0x3A, 0x02, 0x05, 0x09, + 0x3A, 0x02, 0x05, 0x0A, 0xA7, 0x03, 0x06, 0x76, 0x63, 0x2A, 0x01, 0x81, + 0x00, 0x09, 0x02, 0x05, 0x12, 0x06, 0x02, 0x57, 0x29, 0x76, 0x59, 0x03, + 0x04, 0x04, 0x3A, 0x85, 0x27, 0x06, 0x34, 0x9B, 0x05, 0x02, 0x56, 0x29, + 0x68, 0x27, 0x06, 0x04, 0x01, 0x17, 0x04, 0x12, 0x69, 0x27, 0x06, 0x04, + 0x01, 0x18, 0x04, 0x0A, 0x6A, 0x27, 0x06, 0x04, 0x01, 0x19, 0x04, 0x02, + 0x56, 0x29, 0x03, 0x07, 0x76, 0xA1, 0x26, 0x03, 0x08, 0x26, 0x62, 0x33, + 0x0D, 0x06, 0x02, 0x4F, 0x29, 0xA2, 0x58, 0x03, 0x04, 0x04, 0x02, 0x56, + 0x29, 0x76, 0x02, 0x00, 0x06, 0x21, 0x02, 0x04, 0x59, 0x30, 0x11, 0x06, + 0x08, 0x25, 0x02, 0x05, 0x02, 0x06, 0x1E, 0x04, 0x10, 0x58, 0x30, 0x11, + 0x06, 0x08, 0x25, 0x02, 0x07, 0x02, 0x08, 0x1D, 0x04, 0x03, 0x56, 0x29, + 0x25, 0x04, 0x24, 0x02, 0x04, 0x59, 0x30, 0x11, 0x06, 0x08, 0x25, 0x02, + 0x05, 0x02, 0x06, 0x24, 0x04, 0x10, 0x58, 0x30, 0x11, 0x06, 0x08, 0x25, + 0x02, 0x07, 0x02, 0x08, 0x23, 0x04, 0x03, 0x56, 0x29, 0x25, 0x26, 0x06, + 0x01, 0x29, 0x25, 0x01, 0x00, 0x03, 0x09, 0xB1, 0x01, 0x21, 0x8C, 0x01, + 0x22, 0x8C, 0x26, 0x01, 0x23, 0x11, 0x06, 0x81, 0x26, 0x25, 0x71, 0xAA, + 0xAC, 0x26, 0x06, 0x81, 0x1A, 0x01, 0x00, 0x03, 0x0A, 0xAC, 0x9B, 0x25, + 0xB0, 0x26, 0x01, 0x01, 0x11, 0x06, 0x04, 0xA3, 0x03, 0x0A, 0xB0, 0x01, + 0x04, 0x75, 0xAA, 0x6E, 0x27, 0x06, 0x0F, 0x02, 0x00, 0x06, 0x03, 0xC0, + 0x04, 0x05, 0x96, 0x01, 0x7F, 0x03, 0x09, 0x04, 0x80, 0x6C, 0x8E, 0x27, + 0x06, 0x06, 0x02, 0x00, 0x98, 0x04, 0x80, 0x62, 0xC2, 0x27, 0x06, 0x11, + 0x02, 0x00, 0x06, 0x09, 0x01, 0x00, 0x03, 0x03, 0x95, 0x03, 0x03, 0x04, + 0x01, 0xC0, 0x04, 0x80, 0x4D, 0x70, 0x27, 0x06, 0x0A, 0x02, 0x0A, 0x06, + 0x03, 0x97, 0x04, 0x01, 0xC0, 0x04, 0x3F, 0x6D, 0x27, 0x06, 0x03, 0xC0, + 0x04, 0x38, 0xC5, 0x27, 0x06, 0x03, 0xC0, 0x04, 0x31, 0x8D, 0x27, 0x06, + 0x03, 0xC0, 0x04, 0x2A, 0xC3, 0x27, 0x06, 0x03, 0xC0, 0x04, 0x23, 0x77, + 0x27, 0x06, 0x03, 0xC0, 0x04, 0x1C, 0x82, 0x27, 0x06, 0x03, 0xC0, 0x04, + 0x15, 0x6C, 0x27, 0x06, 0x03, 0xC0, 0x04, 0x0E, 0xC4, 0x27, 0x06, 0x03, + 0xC0, 0x04, 0x07, 0x02, 0x0A, 0x06, 0x02, 0x48, 0x29, 0xC0, 0x76, 0x76, + 0x04, 0xFE, 0x62, 0x76, 0x76, 0x04, 0x08, 0x01, 0x7F, 0x11, 0x05, 0x02, + 0x55, 0x29, 0x25, 0x76, 0x39, 0x02, 0x00, 0x06, 0x08, 0x02, 0x03, 0x3B, + 0x2F, 0x05, 0x02, 0x44, 0x29, 0x02, 0x00, 0x06, 0x01, 0x17, 0x02, 0x00, + 0x02, 0x09, 0x2F, 0x05, 0x02, 0x50, 0x29, 0xB0, 0x73, 0xAA, 0x9B, 0x06, + 0x80, 0x77, 0xBA, 0x27, 0x06, 0x07, 0x01, 0x02, 0x59, 0x87, 0x04, 0x80, + 0x5E, 0xBB, 0x27, 0x06, 0x07, 0x01, 0x03, 0x59, 0x88, 0x04, 0x80, 0x53, + 0xBC, 0x27, 0x06, 0x07, 0x01, 0x04, 0x59, 0x89, 0x04, 0x80, 0x48, 0xBD, + 0x27, 0x06, 0x06, 0x01, 0x05, 0x59, 0x8A, 0x04, 0x3E, 0xBE, 0x27, 0x06, + 0x06, 0x01, 0x06, 0x59, 0x8B, 0x04, 0x34, 0x7C, 0x27, 0x06, 0x06, 0x01, + 0x02, 0x58, 0x87, 0x04, 0x2A, 0x7D, 0x27, 0x06, 0x06, 0x01, 0x03, 0x58, + 0x88, 0x04, 0x20, 0x7E, 0x27, 0x06, 0x06, 0x01, 0x04, 0x58, 0x89, 0x04, + 0x16, 0x7F, 0x27, 0x06, 0x06, 0x01, 0x05, 0x58, 0x8A, 0x04, 0x0C, 0x80, + 0x27, 0x06, 0x06, 0x01, 0x06, 0x58, 0x8B, 0x04, 0x02, 0x56, 0x29, 0x5D, + 0x34, 0x5F, 0x36, 0x1C, 0x26, 0x05, 0x02, 0x56, 0x29, 0x5C, 0x36, 0x04, + 0x02, 0x56, 0x29, 0xBF, 0xA1, 0x26, 0x01, T0_INT2(BR_X509_BUFSIZE_SIG), + 0x12, 0x06, 0x02, 0x4F, 0x29, 0x26, 0x5E, 0x34, 0x5B, 0xA2, 0x76, 0x76, + 0x01, 0x00, 0x5A, 0x35, 0x18, 0x00, 0x00, 0x01, 0x30, 0x0A, 0x26, 0x01, + 0x00, 0x01, 0x09, 0x6F, 0x05, 0x02, 0x47, 0x29, 0x00, 0x00, 0x30, 0x30, + 0x00, 0x00, 0x01, 0x81, 0x08, 0x00, 0x00, 0x01, 0x81, 0x10, 0x00, 0x00, + 0x01, 0x81, 0x19, 0x00, 0x00, 0x01, 0x81, 0x22, 0x00, 0x00, 0x01, 0x81, + 0x2B, 0x00, 0x01, 0x7B, 0x01, 0x01, 0x11, 0x3A, 0x01, 0x83, 0xFD, 0x7F, + 0x11, 0x15, 0x06, 0x03, 0x3A, 0x25, 0x00, 0x3A, 0x26, 0x03, 0x00, 0x26, + 0xC6, 0x05, 0x04, 0x41, 0x01, 0x00, 0x00, 0x26, 0x01, 0x81, 0x00, 0x0D, + 0x06, 0x04, 0x93, 0x04, 0x80, 0x49, 0x26, 0x01, 0x90, 0x00, 0x0D, 0x06, + 0x0F, 0x01, 0x06, 0x14, 0x01, 0x81, 0x40, 0x2F, 0x93, 0x02, 0x00, 0x01, + 0x00, 0x94, 0x04, 0x33, 0x26, 0x01, 0x83, 0xFF, 0x7F, 0x0D, 0x06, 0x14, + 0x01, 0x0C, 0x14, 0x01, 0x81, 0x60, 0x2F, 0x93, 0x02, 0x00, 0x01, 0x06, + 0x94, 0x02, 0x00, 0x01, 0x00, 0x94, 0x04, 0x17, 0x01, 0x12, 0x14, 0x01, + 0x81, 0x70, 0x2F, 0x93, 0x02, 0x00, 0x01, 0x0C, 0x94, 0x02, 0x00, 0x01, + 0x06, 0x94, 0x02, 0x00, 0x01, 0x00, 0x94, 0x00, 0x00, 0x01, 0x82, 0x15, + 0x00, 0x00, 0x26, 0x01, 0x83, 0xB0, 0x00, 0x01, 0x83, 0xB7, 0x7F, 0x6F, + 0x00, 0x00, 0x01, 0x81, 0x34, 0x00, 0x00, 0x01, 0x80, 0x6B, 0x00, 0x00, + 0x01, 0x81, 0x78, 0x00, 0x00, 0x01, 0x3D, 0x00, 0x00, 0x01, 0x80, 0x43, + 0x00, 0x00, 0x01, 0x80, 0x4D, 0x00, 0x00, 0x01, 0x80, 0x57, 0x00, 0x00, + 0x01, 0x80, 0x61, 0x00, 0x00, 0x30, 0x11, 0x06, 0x04, 0x41, 0xAA, 0xBF, + 0xB1, 0x00, 0x00, 0x01, 0x82, 0x09, 0x00, 0x00, 0x01, 0x81, 0x6C, 0x00, + 0x00, 0x26, 0x01, 0x83, 0xB8, 0x00, 0x01, 0x83, 0xBF, 0x7F, 0x6F, 0x00, + 0x00, 0x01, 0x30, 0x61, 0x36, 0x01, 0x7F, 0x79, 0x1A, 0x01, 0x00, 0x79, + 0x1A, 0x04, 0x7A, 0x00, 0x01, 0x81, 0x38, 0x00, 0x01, 0x7B, 0x0D, 0x06, + 0x02, 0x4E, 0x29, 0x26, 0x03, 0x00, 0x0A, 0x02, 0x00, 0x00, 0x00, 0x30, + 0x26, 0x3E, 0x3A, 0x01, 0x82, 0x00, 0x13, 0x2F, 0x06, 0x04, 0x41, 0x01, + 0x00, 0x00, 0x30, 0x66, 0x09, 0x36, 0x3F, 0x00, 0x00, 0x14, 0x01, 0x3F, + 0x15, 0x01, 0x81, 0x00, 0x2F, 0x93, 0x00, 0x02, 0x01, 0x00, 0x03, 0x00, + 0xAC, 0x26, 0x06, 0x80, 0x59, 0xB0, 0x01, 0x20, 0x30, 0x11, 0x06, 0x17, + 0x25, 0x71, 0xAA, 0x9B, 0x25, 0x01, 0x7F, 0x2E, 0x03, 0x01, 0xB0, 0x01, + 0x20, 0x74, 0xAA, 0xAF, 0x02, 0x01, 0x20, 0x76, 0x76, 0x04, 0x38, 0x01, + 0x21, 0x30, 0x11, 0x06, 0x08, 0x25, 0x72, 0xB3, 0x01, 0x01, 0x1F, 0x04, + 0x2A, 0x01, 0x22, 0x30, 0x11, 0x06, 0x11, 0x25, 0x72, 0xB3, 0x26, 0x06, + 0x06, 0x2C, 0x02, 0x00, 0x2F, 0x03, 0x00, 0x01, 0x02, 0x1F, 0x04, 0x13, + 0x01, 0x26, 0x30, 0x11, 0x06, 0x08, 0x25, 0x72, 0xB3, 0x01, 0x06, 0x1F, + 0x04, 0x05, 0x41, 0xAB, 0x01, 0x00, 0x25, 0x04, 0xFF, 0x23, 0x76, 0x02, + 0x00, 0x00, 0x00, 0xAC, 0xB1, 0x26, 0x01, 0x01, 0x11, 0x06, 0x08, 0xA3, + 0x05, 0x02, 0x50, 0x29, 0xB1, 0x04, 0x02, 0x50, 0x29, 0x26, 0x01, 0x02, + 0x11, 0x06, 0x0C, 0x25, 0x72, 0xAD, 0x65, 0x2B, 0x40, 0x0D, 0x06, 0x02, + 0x50, 0x29, 0xB1, 0x01, 0x7F, 0x10, 0x06, 0x02, 0x55, 0x29, 0x25, 0x76, + 0x00, 0x00, 0xAC, 0x26, 0x06, 0x1A, 0xAC, 0x9B, 0x25, 0x26, 0x06, 0x11, + 0xAC, 0x26, 0x06, 0x0C, 0xAC, 0x9B, 0x25, 0x86, 0x27, 0x05, 0x02, 0x48, + 0x29, 0xBF, 0x04, 0x71, 0x76, 0x76, 0x04, 0x63, 0x76, 0x00, 0x02, 0x03, + 0x00, 0xB0, 0x01, 0x03, 0x75, 0xAA, 0xB7, 0x03, 0x01, 0x02, 0x01, 0x01, + 0x07, 0x12, 0x06, 0x02, 0x55, 0x29, 0x26, 0x01, 0x00, 0x30, 0x11, 0x06, + 0x05, 0x25, 0x4C, 0x29, 0x04, 0x15, 0x01, 0x01, 0x30, 0x11, 0x06, 0x0A, + 0x25, 0xB7, 0x02, 0x01, 0x14, 0x02, 0x01, 0x0E, 0x04, 0x05, 0x25, 0xB7, + 0x01, 0x00, 0x25, 0x02, 0x00, 0x06, 0x19, 0x01, 0x00, 0x30, 0x01, 0x38, + 0x15, 0x06, 0x03, 0x01, 0x10, 0x2F, 0x3A, 0x01, 0x81, 0x40, 0x15, 0x06, + 0x03, 0x01, 0x20, 0x2F, 0x61, 0x36, 0x04, 0x07, 0x01, 0x04, 0x15, 0x05, + 0x02, 0x4C, 0x29, 0xBF, 0x00, 0x00, 0x37, 0xAC, 0xBF, 0x1B, 0x00, 0x03, + 0x01, 0x00, 0x03, 0x00, 0x37, 0xAC, 0x26, 0x06, 0x30, 0xB0, 0x01, 0x11, + 0x74, 0xAA, 0x26, 0x05, 0x02, 0x43, 0x29, 0x26, 0x06, 0x20, 0xAC, 0x9B, + 0x25, 0x84, 0x27, 0x03, 0x01, 0x01, 0x00, 0x2E, 0x03, 0x02, 0xAF, 0x26, + 0x02, 0x01, 0x15, 0x06, 0x07, 0x2C, 0x06, 0x04, 0x01, 0x7F, 0x03, 0x00, + 0x02, 0x02, 0x20, 0x76, 0x04, 0x5D, 0x76, 0x04, 0x4D, 0x76, 0x1B, 0x02, + 0x00, 0x00, 0x00, 0xB0, 0x01, 0x06, 0x75, 0xAE, 0x00, 0x00, 0xB5, 0x83, + 0x06, 0x0E, 0x3A, 0x26, 0x05, 0x06, 0x41, 0x01, 0x00, 0x01, 0x00, 0x00, + 0xB5, 0x6B, 0x04, 0x08, 0x8F, 0x06, 0x05, 0x25, 0x01, 0x00, 0x04, 0x00, + 0x00, 0x00, 0xB6, 0x83, 0x06, 0x0E, 0x3A, 0x26, 0x05, 0x06, 0x41, 0x01, + 0x00, 0x01, 0x00, 0x00, 0xB6, 0x6B, 0x04, 0x08, 0x8F, 0x06, 0x05, 0x25, + 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 0xB7, 0x26, 0x01, 0x81, 0x00, 0x0D, + 0x06, 0x04, 0x00, 0x04, 0x80, 0x55, 0x26, 0x01, 0x81, 0x40, 0x0D, 0x06, + 0x07, 0x25, 0x01, 0x00, 0x00, 0x04, 0x80, 0x47, 0x26, 0x01, 0x81, 0x60, + 0x0D, 0x06, 0x0E, 0x01, 0x1F, 0x15, 0x01, 0x01, 0xA0, 0x01, 0x81, 0x00, + 0x01, 0x8F, 0x7F, 0x04, 0x32, 0x26, 0x01, 0x81, 0x70, 0x0D, 0x06, 0x0F, + 0x01, 0x0F, 0x15, 0x01, 0x02, 0xA0, 0x01, 0x90, 0x00, 0x01, 0x83, 0xFF, + 0x7F, 0x04, 0x1C, 0x26, 0x01, 0x81, 0x78, 0x0D, 0x06, 0x11, 0x01, 0x07, + 0x15, 0x01, 0x03, 0xA0, 0x01, 0x84, 0x80, 0x00, 0x01, 0x80, 0xC3, 0xFF, + 0x7F, 0x04, 0x04, 0x25, 0x01, 0x00, 0x00, 0x6F, 0x05, 0x03, 0x25, 0x01, + 0x00, 0x00, 0x00, 0x3A, 0x26, 0x05, 0x06, 0x41, 0x01, 0x00, 0x01, 0x7F, + 0x00, 0xB7, 0x33, 0x26, 0x3C, 0x06, 0x03, 0x3A, 0x25, 0x00, 0x01, 0x06, + 0x0E, 0x3A, 0x26, 0x01, 0x06, 0x14, 0x01, 0x02, 0x10, 0x06, 0x04, 0x41, + 0x01, 0x7F, 0x00, 0x01, 0x3F, 0x15, 0x09, 0x00, 0x00, 0x26, 0x06, 0x06, + 0x0B, 0x9F, 0x33, 0x40, 0x04, 0x77, 0x25, 0x26, 0x00, 0x00, 0xB0, 0x01, + 0x03, 0x75, 0xAA, 0xB7, 0x06, 0x02, 0x54, 0x29, 0x00, 0x00, 0x3A, 0x26, + 0x06, 0x07, 0x31, 0x26, 0x06, 0x01, 0x1A, 0x04, 0x76, 0x41, 0x00, 0x00, + 0x01, 0x01, 0x75, 0xA9, 0x01, 0x01, 0x10, 0x06, 0x02, 0x42, 0x29, 0xB7, + 0x3D, 0x00, 0x04, 0xB0, 0x26, 0x01, 0x17, 0x01, 0x18, 0x6F, 0x05, 0x02, + 0x47, 0x29, 0x01, 0x18, 0x11, 0x03, 0x00, 0x72, 0xAA, 0xA5, 0x02, 0x00, + 0x06, 0x0C, 0x01, 0x80, 0x64, 0x08, 0x03, 0x01, 0xA5, 0x02, 0x01, 0x09, + 0x04, 0x0E, 0x26, 0x01, 0x32, 0x0D, 0x06, 0x04, 0x01, 0x80, 0x64, 0x09, + 0x01, 0x8E, 0x6C, 0x09, 0x03, 0x01, 0x02, 0x01, 0x01, 0x82, 0x6D, 0x08, + 0x02, 0x01, 0x01, 0x03, 0x09, 0x01, 0x04, 0x0C, 0x09, 0x02, 0x01, 0x01, + 0x80, 0x63, 0x09, 0x01, 0x80, 0x64, 0x0C, 0x0A, 0x02, 0x01, 0x01, 0x83, + 0x0F, 0x09, 0x01, 0x83, 0x10, 0x0C, 0x09, 0x03, 0x03, 0x01, 0x01, 0x01, + 0x0C, 0xA6, 0x40, 0x01, 0x01, 0x0E, 0x02, 0x01, 0x01, 0x04, 0x07, 0x3E, + 0x02, 0x01, 0x01, 0x80, 0x64, 0x07, 0x3D, 0x02, 0x01, 0x01, 0x83, 0x10, + 0x07, 0x3E, 0x2F, 0x15, 0x06, 0x03, 0x01, 0x18, 0x09, 0x91, 0x09, 0x78, + 0x26, 0x01, 0x05, 0x14, 0x02, 0x03, 0x09, 0x03, 0x03, 0x01, 0x1F, 0x15, + 0x01, 0x01, 0x3A, 0xA6, 0x02, 0x03, 0x09, 0x40, 0x03, 0x03, 0x01, 0x00, + 0x01, 0x17, 0xA6, 0x01, 0x9C, 0x10, 0x08, 0x03, 0x02, 0x01, 0x00, 0x01, + 0x3B, 0xA6, 0x01, 0x3C, 0x08, 0x02, 0x02, 0x09, 0x03, 0x02, 0x01, 0x00, + 0x01, 0x3C, 0xA6, 0x02, 0x02, 0x09, 0x03, 0x02, 0xB7, 0x26, 0x01, 0x2E, + 0x11, 0x06, 0x0D, 0x25, 0xB7, 0x26, 0x01, 0x30, 0x01, 0x39, 0x6F, 0x06, + 0x03, 0x25, 0x04, 0x74, 0x01, 0x80, 0x5A, 0x10, 0x06, 0x02, 0x47, 0x29, + 0x76, 0x02, 0x03, 0x02, 0x02, 0x00, 0x01, 0xB7, 0x7A, 0x01, 0x0A, 0x08, + 0x03, 0x00, 0xB7, 0x7A, 0x02, 0x00, 0x09, 0x00, 0x02, 0x03, 0x00, 0x03, + 0x01, 0xA5, 0x26, 0x02, 0x01, 0x02, 0x00, 0x6F, 0x05, 0x02, 0x47, 0x29, + 0x00, 0x00, 0x33, 0xB0, 0x01, 0x02, 0x75, 0x0B, 0xA8, 0x00, 0x03, 0x26, + 0x03, 0x00, 0x03, 0x01, 0x03, 0x02, 0xAA, 0xB7, 0x26, 0x01, 0x81, 0x00, + 0x13, 0x06, 0x02, 0x53, 0x29, 0x26, 0x01, 0x00, 0x11, 0x06, 0x0B, 0x25, + 0x26, 0x05, 0x04, 0x25, 0x01, 0x00, 0x00, 0xB7, 0x04, 0x6F, 0x02, 0x01, + 0x26, 0x05, 0x02, 0x4F, 0x29, 0x40, 0x03, 0x01, 0x02, 0x02, 0x36, 0x02, + 0x02, 0x3F, 0x03, 0x02, 0x26, 0x06, 0x03, 0xB7, 0x04, 0x68, 0x25, 0x02, + 0x00, 0x02, 0x01, 0x0A, 0x00, 0x01, 0xB7, 0x26, 0x01, 0x81, 0x00, 0x0D, + 0x06, 0x01, 0x00, 0x01, 0x81, 0x00, 0x0A, 0x26, 0x05, 0x02, 0x4D, 0x29, + 0x03, 0x00, 0x01, 0x00, 0x02, 0x00, 0x01, 0x00, 0x12, 0x06, 0x19, 0x02, + 0x00, 0x40, 0x03, 0x00, 0x26, 0x01, 0x83, 0xFF, 0xFF, 0x7F, 0x12, 0x06, + 0x02, 0x4E, 0x29, 0x01, 0x08, 0x0E, 0x3A, 0xB7, 0x33, 0x09, 0x04, 0x60, + 0x00, 0x00, 0xA9, 0x92, 0x00, 0x00, 0xAA, 0xBF, 0x00, 0x00, 0xB0, 0x73, + 0xAA, 0x00, 0x01, 0xAA, 0x26, 0x05, 0x02, 0x53, 0x29, 0xB7, 0x26, 0x01, + 0x81, 0x00, 0x13, 0x06, 0x02, 0x53, 0x29, 0x03, 0x00, 0x26, 0x06, 0x16, + 0xB7, 0x02, 0x00, 0x26, 0x01, 0x87, 0xFF, 0xFF, 0x7F, 0x13, 0x06, 0x02, + 0x53, 0x29, 0x01, 0x08, 0x0E, 0x09, 0x03, 0x00, 0x04, 0x67, 0x25, 0x02, + 0x00, 0x00, 0x00, 0xAA, 0x26, 0x01, 0x81, 0x7F, 0x12, 0x06, 0x08, 0xBF, + 0x01, 0x00, 0x66, 0x36, 0x01, 0x00, 0x00, 0x26, 0x66, 0x36, 0x66, 0x3F, + 0xA2, 0x01, 0x7F, 0x00, 0x00, 0xB0, 0x01, 0x0C, 0x30, 0x11, 0x06, 0x05, + 0x25, 0x72, 0xB3, 0x04, 0x3E, 0x01, 0x12, 0x30, 0x11, 0x06, 0x05, 0x25, + 0x72, 0xB4, 0x04, 0x33, 0x01, 0x13, 0x30, 0x11, 0x06, 0x05, 0x25, 0x72, + 0xB4, 0x04, 0x28, 0x01, 0x14, 0x30, 0x11, 0x06, 0x05, 0x25, 0x72, 0xB4, + 0x04, 0x1D, 0x01, 0x16, 0x30, 0x11, 0x06, 0x05, 0x25, 0x72, 0xB4, 0x04, + 0x12, 0x01, 0x1E, 0x30, 0x11, 0x06, 0x05, 0x25, 0x72, 0xB2, 0x04, 0x07, + 0x41, 0xAB, 0x01, 0x00, 0x01, 0x00, 0x25, 0x00, 0x01, 0xB7, 0x03, 0x00, + 0x02, 0x00, 0x01, 0x05, 0x14, 0x01, 0x01, 0x15, 0x2D, 0x02, 0x00, 0x01, + 0x06, 0x14, 0x26, 0x01, 0x01, 0x15, 0x06, 0x02, 0x45, 0x29, 0x01, 0x04, + 0x0E, 0x02, 0x00, 0x01, 0x1F, 0x15, 0x26, 0x01, 0x1F, 0x11, 0x06, 0x02, + 0x46, 0x29, 0x09, 0x00, 0x00, 0x26, 0x05, 0x05, 0x01, 0x00, 0x01, 0x7F, + 0x00, 0xB0, 0x00, 0x01, 0xAA, 0x26, 0x05, 0x05, 0x66, 0x36, 0x01, 0x7F, + 0x00, 0x01, 0x01, 0x03, 0x00, 0x9C, 0x26, 0x01, 0x83, 0xFF, 0x7E, 0x11, + 0x06, 0x16, 0x25, 0x26, 0x06, 0x10, 0x9D, 0x26, 0x05, 0x05, 0x25, 0xBF, + 0x01, 0x00, 0x00, 0x02, 0x00, 0x81, 0x03, 0x00, 0x04, 0x6D, 0x04, 0x1B, + 0x26, 0x05, 0x05, 0x25, 0xBF, 0x01, 0x00, 0x00, 0x02, 0x00, 0x81, 0x03, + 0x00, 0x26, 0x06, 0x0B, 0x9C, 0x26, 0x05, 0x05, 0x25, 0xBF, 0x01, 0x00, + 0x00, 0x04, 0x6D, 0x25, 0x02, 0x00, 0x26, 0x05, 0x01, 0x00, 0x40, 0x66, + 0x36, 0x01, 0x7F, 0x00, 0x01, 0xAA, 0x01, 0x01, 0x03, 0x00, 0x26, 0x06, + 0x10, 0x9E, 0x26, 0x05, 0x05, 0x25, 0xBF, 0x01, 0x00, 0x00, 0x02, 0x00, + 0x81, 0x03, 0x00, 0x04, 0x6D, 0x25, 0x02, 0x00, 0x26, 0x05, 0x01, 0x00, + 0x40, 0x66, 0x36, 0x01, 0x7F, 0x00, 0x01, 0xAA, 0x01, 0x01, 0x03, 0x00, + 0x26, 0x06, 0x10, 0xB7, 0x26, 0x05, 0x05, 0x25, 0xBF, 0x01, 0x00, 0x00, + 0x02, 0x00, 0x81, 0x03, 0x00, 0x04, 0x6D, 0x25, 0x02, 0x00, 0x26, 0x05, + 0x01, 0x00, 0x40, 0x66, 0x36, 0x01, 0x7F, 0x00, 0x00, 0xB7, 0x01, 0x08, + 0x0E, 0x3A, 0xB7, 0x33, 0x09, 0x00, 0x00, 0xB7, 0x3A, 0xB7, 0x01, 0x08, + 0x0E, 0x33, 0x09, 0x00, 0x00, 0x26, 0x05, 0x02, 0x4E, 0x29, 0x40, 0xB8, + 0x00, 0x00, 0x32, 0x26, 0x01, 0x00, 0x13, 0x06, 0x01, 0x00, 0x25, 0x1A, + 0x04, 0x74, 0x00, 0x01, 0x01, 0x00, 0x00, 0x01, 0x0B, 0x00, 0x00, 0x01, + 0x15, 0x00, 0x00, 0x01, 0x1F, 0x00, 0x00, 0x01, 0x29, 0x00, 0x00, 0x01, + 0x33, 0x00, 0x00, 0xC0, 0x25, 0x00, 0x00, 0x26, 0x06, 0x07, 0xC1, 0x26, + 0x06, 0x01, 0x1A, 0x04, 0x76, 0x00, 0x00, 0x01, 0x00, 0x30, 0x31, 0x0B, + 0x41, 0x00, 0x00, 0x01, 0x81, 0x70, 0x00, 0x00, 0x01, 0x82, 0x0D, 0x00, + 0x00, 0x01, 0x82, 0x22, 0x00, 0x00, 0x01, 0x82, 0x05, 0x00, 0x00, 0x26, + 0x01, 0x83, 0xFB, 0x50, 0x01, 0x83, 0xFB, 0x6F, 0x6F, 0x06, 0x04, 0x25, + 0x01, 0x00, 0x00, 0x26, 0x01, 0x83, 0xB0, 0x00, 0x01, 0x83, 0xBF, 0x7F, + 0x6F, 0x06, 0x04, 0x25, 0x01, 0x00, 0x00, 0x01, 0x83, 0xFF, 0x7F, 0x15, + 0x01, 0x83, 0xFF, 0x7E, 0x0D, 0x00 +}; + +static const uint16_t t0_caddr[] PROGMEM = { + + 0, + 5, + 10, + 15, + 20, + 25, + 29, + 33, + 37, + 41, + 45, + 49, + 53, + 57, + 61, + 65, + 69, + 73, + 77, + 81, + 85, + 89, + 93, + 97, + 101, + 105, + 109, + 113, + 117, + 121, + 125, + 130, + 135, + 140, + 145, + 150, + 155, + 160, + 165, + 173, + 178, + 183, + 188, + 193, + 198, + 203, + 208, + 213, + 234, + 239, + 244, + 249, + 264, + 269, + 275, + 281, + 286, + 294, + 302, + 308, + 313, + 324, + 960, + 975, + 979, + 984, + 989, + 994, + 999, + 1004, + 1118, + 1123, + 1135, + 1140, + 1145, + 1150, + 1154, + 1159, + 1164, + 1169, + 1174, + 1184, + 1189, + 1194, + 1206, + 1221, + 1226, + 1240, + 1262, + 1273, + 1376, + 1423, + 1456, + 1547, + 1553, + 1616, + 1623, + 1651, + 1679, + 1784, + 1826, + 1839, + 1851, + 1865, + 1880, + 2100, + 2114, + 2131, + 2140, + 2207, + 2263, + 2267, + 2271, + 2276, + 2324, + 2350, + 2426, + 2470, + 2481, + 2566, + 2604, + 2642, + 2652, + 2662, + 2671, + 2684, + 2688, + 2692, + 2696, + 2700, + 2704, + 2708, + 2712, + 2724, + 2732, + 2737, + 2742, + 2747, + 2752 +}; + +#define T0_INTERPRETED 60 + +#define T0_ENTER(ip, rp, slot) do { \ + const unsigned char *t0_newip; \ + uint32_t t0_lnum; \ + t0_newip = &t0_codeblock[pgm_read_word(&t0_caddr[(slot) - T0_INTERPRETED])]; \ + t0_lnum = t0_parse7E_unsigned(&t0_newip); \ + (rp) += t0_lnum; \ + *((rp) ++) = (uint32_t)((ip) - &t0_codeblock[0]) + (t0_lnum << 16); \ + (ip) = t0_newip; \ + } while (0) + +#define T0_DEFENTRY(name, slot) \ +void \ +name(void *ctx) \ +{ \ + t0_context *t0ctx = ctx; \ + t0ctx->ip = &t0_codeblock[0]; \ + T0_ENTER(t0ctx->ip, t0ctx->rp, slot); \ +} + +T0_DEFENTRY(br_x509_minimal_init_main, 144) + +#define T0_NEXT(t0ipp) (pgm_read_byte((*t0ipp)++)) + +void +br_x509_minimal_run(void *t0ctx) +{ + uint32_t *dp, *rp; + const unsigned char *ip; + +#define T0_LOCAL(x) (*(rp - 2 - (x))) +#define T0_POP() (*-- dp) +#define T0_POPi() (*(int32_t *)(-- dp)) +#define T0_PEEK(x) (*(dp - 1 - (x))) +#define T0_PEEKi(x) (*(int32_t *)(dp - 1 - (x))) +#define T0_PUSH(v) do { *dp = (v); dp ++; } while (0) +#define T0_PUSHi(v) do { *(int32_t *)dp = (v); dp ++; } while (0) +#define T0_RPOP() (*-- rp) +#define T0_RPOPi() (*(int32_t *)(-- rp)) +#define T0_RPUSH(v) do { *rp = (v); rp ++; } while (0) +#define T0_RPUSHi(v) do { *(int32_t *)rp = (v); rp ++; } while (0) +#define T0_ROLL(x) do { \ + size_t t0len = (size_t)(x); \ + uint32_t t0tmp = *(dp - 1 - t0len); \ + memmove(dp - t0len - 1, dp - t0len, t0len * sizeof *dp); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_SWAP() do { \ + uint32_t t0tmp = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_ROT() do { \ + uint32_t t0tmp = *(dp - 3); \ + *(dp - 3) = *(dp - 2); \ + *(dp - 2) = *(dp - 1); \ + *(dp - 1) = t0tmp; \ +} while (0) +#define T0_NROT() do { \ + uint32_t t0tmp = *(dp - 1); \ + *(dp - 1) = *(dp - 2); \ + *(dp - 2) = *(dp - 3); \ + *(dp - 3) = t0tmp; \ +} while (0) +#define T0_PICK(x) do { \ + uint32_t t0depth = (x); \ + T0_PUSH(T0_PEEK(t0depth)); \ +} while (0) +#define T0_CO() do { \ + goto t0_exit; \ +} while (0) +#define T0_RET() goto t0_next + + dp = ((t0_context *)t0ctx)->dp; + rp = ((t0_context *)t0ctx)->rp; + ip = ((t0_context *)t0ctx)->ip; + goto t0_next; + for (;;) { + uint32_t t0x; + + t0_next: + t0x = T0_NEXT(&ip); + if (t0x < T0_INTERPRETED) { + switch (t0x) { + int32_t t0off; + + case 0: /* ret */ + t0x = T0_RPOP(); + rp -= (t0x >> 16); + t0x &= 0xFFFF; + if (t0x == 0) { + ip = NULL; + goto t0_exit; + } + ip = &t0_codeblock[t0x]; + break; + case 1: /* literal constant */ + T0_PUSHi(t0_parse7E_signed(&ip)); + break; + case 2: /* read local */ + T0_PUSH(T0_LOCAL(t0_parse7E_unsigned(&ip))); + break; + case 3: /* write local */ + T0_LOCAL(t0_parse7E_unsigned(&ip)) = T0_POP(); + break; + case 4: /* jump */ + t0off = t0_parse7E_signed(&ip); + ip += t0off; + break; + case 5: /* jump if */ + t0off = t0_parse7E_signed(&ip); + if (T0_POP()) { + ip += t0off; + } + break; + case 6: /* jump if not */ + t0off = t0_parse7E_signed(&ip); + if (!T0_POP()) { + ip += t0off; + } + break; + case 7: { + /* %25 */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a % b); + + } + break; + case 8: { + /* * */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a * b); + + } + break; + case 9: { + /* + */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a + b); + + } + break; + case 10: { + /* - */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a - b); + + } + break; + case 11: { + /* -rot */ + T0_NROT(); + } + break; + case 12: { + /* / */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSHi(a / b); + + } + break; + case 13: { + /* < */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a < b)); + + } + break; + case 14: { + /* << */ + + int c = (int)T0_POPi(); + uint32_t x = T0_POP(); + T0_PUSH(x << c); + + } + break; + case 15: { + /* <= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a <= b)); + + } + break; + case 16: { + /* <> */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a != b)); + + } + break; + case 17: { + /* = */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(-(uint32_t)(a == b)); + + } + break; + case 18: { + /* > */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a > b)); + + } + break; + case 19: { + /* >= */ + + int32_t b = T0_POPi(); + int32_t a = T0_POPi(); + T0_PUSH(-(uint32_t)(a >= b)); + + } + break; + case 20: { + /* >> */ + + int c = (int)T0_POPi(); + int32_t x = T0_POPi(); + T0_PUSHi(x >> c); + + } + break; + case 21: { + /* and */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a & b); + + } + break; + case 22: { + /* blobcopy */ + + size_t len = T0_POP(); + unsigned char *src = (unsigned char *)CTX + T0_POP(); + unsigned char *dst = (unsigned char *)CTX + T0_POP(); + memcpy(dst, src, len); + + } + break; + case 23: { + /* check-direct-trust */ + + size_t u; + const br_x509_trust_anchor *ta; + unsigned char hashed_DN[64]; + + for (u = 0; u < CTX->trust_anchors_num; u ++) { + ta = &CTX->trust_anchors[u]; + hash_dn(CTX, ta->dn.data, ta->dn.len, hashed_DN); + if (check_single_direct_trust(CTX, hashed_DN, ta)) { + /* + * Direct trust match! + */ + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + if (CTX->err != BR_ERR_X509_OK && CTX->trust_anchor_dynamic) { + ta = CTX->trust_anchor_dynamic(CTX->trust_anchor_dynamic_ctx, CTX->current_dn_hash, DNHASH_LEN); + if (ta) { + memcpy(hashed_DN, ta->dn.data, DNHASH_LEN); + int ret = check_single_direct_trust(CTX, hashed_DN, ta); + if (CTX->trust_anchor_dynamic_free) { + CTX->trust_anchor_dynamic_free(CTX->trust_anchor_dynamic_ctx, ta); + } + if (ret) { + /* + * Direct trust match! + */ + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + } + + } + break; + case 24: { + /* check-trust-anchor-CA */ + + size_t u; + const br_x509_trust_anchor *ta; + unsigned char hashed_DN[64]; + + for (u = 0; u < CTX->trust_anchors_num; u ++) { + ta = &CTX->trust_anchors[u]; + hash_dn(CTX, ta->dn.data, ta->dn.len, hashed_DN); + if (check_single_trust_anchor_CA(CTX, hashed_DN, ta)) { + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + if (CTX->err != BR_ERR_X509_OK && CTX->trust_anchor_dynamic) { + ta = CTX->trust_anchor_dynamic(CTX->trust_anchor_dynamic_ctx, CTX->saved_dn_hash, DNHASH_LEN); + if (ta) { + memcpy(hashed_DN, ta->dn.data, DNHASH_LEN); + int ret; + ret = check_single_trust_anchor_CA(CTX, hashed_DN, ta); + if (CTX->trust_anchor_dynamic_free) { + CTX->trust_anchor_dynamic_free(CTX->trust_anchor_dynamic_ctx, ta); + } + if (ret) { + CTX->err = BR_ERR_X509_OK; + T0_CO(); + } + } + + } + + } + break; + case 25: { + /* check-validity-range */ + + uint32_t nbs = T0_POP(); + uint32_t nbd = T0_POP(); + uint32_t nas = T0_POP(); + uint32_t nad = T0_POP(); + int r; + uint32_t vd = CTX->days; + uint32_t vs = CTX->seconds; + if (vd == 0 && vs == 0) { +#if BR_USE_UNIX_TIME + time_t x = time(NULL); + + vd = (uint32_t)(x / 86400) + 719528; + vs = (uint32_t)(x % 86400); +#elif BR_USE_WIN32_TIME + FILETIME ft; + uint64_t x; + + GetSystemTimeAsFileTime(&ft); + x = ((uint64_t)ft.dwHighDateTime << 32) + + (uint64_t)ft.dwLowDateTime; + x = (x / 10000000); + vd = (uint32_t)(x / 86400) + 584754; + vs = (uint32_t)(x % 86400); +#else + CTX->err = BR_ERR_X509_TIME_UNKNOWN; + T0_CO(); +#endif + } + if (vd < nbd || (vd == nbd && vs < nbs)) { + r = -1; + } else if (vd > nad || (vd == nad && vs > nas)) { + r = 1; + } else { + r = 0; + } + T0_PUSHi(r); + + } + break; + case 26: { + /* co */ + T0_CO(); + } + break; + case 27: { + /* compute-dn-hash */ + + CTX->dn_hash_impl->out(&CTX->dn_hash.vtable, CTX->current_dn_hash); + CTX->do_dn_hash = 0; + + } + break; + case 28: { + /* compute-tbs-hash */ + + int id = T0_POPi(); + size_t len; + len = br_multihash_out(&CTX->mhash, id, CTX->tbs_hash); + T0_PUSH(len); + + } + break; + case 29: { + /* copy-ee-ec-pkey */ + + size_t qlen = T0_POP(); + uint32_t curve = T0_POP(); + memcpy(CTX->ee_pkey_data, CTX->pkey_data, qlen); + CTX->pkey.key_type = BR_KEYTYPE_EC; + CTX->pkey.key.ec.curve = curve; + CTX->pkey.key.ec.q = CTX->ee_pkey_data; + CTX->pkey.key.ec.qlen = qlen; + + } + break; + case 30: { + /* copy-ee-rsa-pkey */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + memcpy(CTX->ee_pkey_data, CTX->pkey_data, nlen + elen); + CTX->pkey.key_type = BR_KEYTYPE_RSA; + CTX->pkey.key.rsa.n = CTX->ee_pkey_data; + CTX->pkey.key.rsa.nlen = nlen; + CTX->pkey.key.rsa.e = CTX->ee_pkey_data + nlen; + CTX->pkey.key.rsa.elen = elen; + + } + break; + case 31: { + /* copy-name-SAN */ + + unsigned tag = T0_POP(); + unsigned ok = T0_POP(); + size_t u, len; + + len = CTX->pad[0]; + for (u = 0; u < CTX->num_name_elts; u ++) { + br_name_element *ne; + + ne = &CTX->name_elts[u]; + if (ne->status == 0 && ne->oid[0] == 0 && ne->oid[1] == tag) { + if (ok && ne->len > len) { + memcpy(ne->buf, CTX->pad + 1, len); + ne->buf[len] = 0; + ne->status = 1; + } else { + ne->status = -1; + } + break; + } + } + + } + break; + case 32: { + /* copy-name-element */ + + size_t len; + int32_t off = T0_POPi(); + int ok = T0_POPi(); + + if (off >= 0) { + br_name_element *ne = &CTX->name_elts[off]; + + if (ok) { + len = CTX->pad[0]; + if (len < ne->len) { + memcpy(ne->buf, CTX->pad + 1, len); + ne->buf[len] = 0; + ne->status = 1; + } else { + ne->status = -1; + } + } else { + ne->status = -1; + } + } + + } + break; + case 33: { + /* data-get8 */ + + size_t addr = T0_POP(); + T0_PUSH(pgm_read_byte(&t0_datablock[addr])); + + } + break; + case 34: { + /* dn-hash-length */ + + T0_PUSH(DNHASH_LEN); + + } + break; + case 35: { + /* do-ecdsa-vrfy */ + + size_t qlen = T0_POP(); + int curve = T0_POP(); + br_x509_pkey pk; + + pk.key_type = BR_KEYTYPE_EC; + pk.key.ec.curve = curve; + pk.key.ec.q = CTX->pkey_data; + pk.key.ec.qlen = qlen; + T0_PUSH(verify_signature(CTX, &pk)); + + } + break; + case 36: { + /* do-rsa-vrfy */ + + size_t elen = T0_POP(); + size_t nlen = T0_POP(); + br_x509_pkey pk; + + pk.key_type = BR_KEYTYPE_RSA; + pk.key.rsa.n = CTX->pkey_data; + pk.key.rsa.nlen = nlen; + pk.key.rsa.e = CTX->pkey_data + nlen; + pk.key.rsa.elen = elen; + T0_PUSH(verify_signature(CTX, &pk)); + + } + break; + case 37: { + /* drop */ + (void)T0_POP(); + } + break; + case 38: { + /* dup */ + T0_PUSH(T0_PEEK(0)); + } + break; + case 39: { + /* eqOID */ + + const unsigned char *a2 = &t0_datablock[T0_POP()]; + const unsigned char *a1 = &CTX->pad[0]; + size_t len = a1[0]; + int x; + if (len == pgm_read_byte(&a2[0])) { + x = -(memcmp_P(a1 + 1, a2 + 1, len) == 0); + } else { + x = 0; + } + T0_PUSH((uint32_t)x); + + } + break; + case 40: { + /* eqblob */ + + size_t len = T0_POP(); + const unsigned char *a2 = (const unsigned char *)CTX + T0_POP(); + const unsigned char *a1 = (const unsigned char *)CTX + T0_POP(); + T0_PUSHi(-(memcmp(a1, a2, len) == 0)); + + } + break; + case 41: { + /* fail */ + + CTX->err = T0_POPi(); + T0_CO(); + + } + break; + case 42: { + /* get16 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*(uint16_t *)(void *)((unsigned char *)CTX + addr)); + + } + break; + case 43: { + /* get32 */ + + uint32_t addr = T0_POP(); + T0_PUSH(*(uint32_t *)(void *)((unsigned char *)CTX + addr)); + + } + break; + case 44: { + /* match-server-name */ + + size_t n1, n2; + + if (CTX->server_name == NULL) { + T0_PUSH(0); + T0_RET(); + } + n1 = strlen(CTX->server_name); + n2 = CTX->pad[0]; + if (n1 == n2 && eqnocase(&CTX->pad[1], CTX->server_name, n1)) { + T0_PUSHi(-1); + T0_RET(); + } + if (n2 >= 2 && CTX->pad[1] == '*' && CTX->pad[2] == '.') { + size_t u; + + u = 0; + while (u < n1 && CTX->server_name[u] != '.') { + u ++; + } + u ++; + n1 -= u; + if ((n2 - 2) == n1 + && eqnocase(&CTX->pad[3], CTX->server_name + u, n1)) + { + T0_PUSHi(-1); + T0_RET(); + } + } + T0_PUSH(0); + + } + break; + case 45: { + /* neg */ + + uint32_t a = T0_POP(); + T0_PUSH(-a); + + } + break; + case 46: { + /* offset-name-element */ + + unsigned san = T0_POP(); + size_t u; + + for (u = 0; u < CTX->num_name_elts; u ++) { + if (CTX->name_elts[u].status == 0) { + const unsigned char *oid; + size_t len, off; + + oid = CTX->name_elts[u].oid; + if (san) { + if (oid[0] != 0 || oid[1] != 0) { + continue; + } + off = 2; + } else { + off = 0; + } + len = oid[off]; + if (len != 0 && len == CTX->pad[0] + && memcmp(oid + off + 1, + CTX->pad + 1, len) == 0) + { + T0_PUSH(u); + T0_RET(); + } + } + } + T0_PUSHi(-1); + + } + break; + case 47: { + /* or */ + + uint32_t b = T0_POP(); + uint32_t a = T0_POP(); + T0_PUSH(a | b); + + } + break; + case 48: { + /* over */ + T0_PUSH(T0_PEEK(1)); + } + break; + case 49: { + /* read-blob-inner */ + + uint32_t len = T0_POP(); + uint32_t addr = T0_POP(); + size_t clen = CTX->hlen; + if (clen > len) { + clen = (size_t)len; + } + if (addr != 0) { + memcpy((unsigned char *)CTX + addr, CTX->hbuf, clen); + } + if (CTX->do_mhash) { + br_multihash_update(&CTX->mhash, CTX->hbuf, clen); + } + if (CTX->do_dn_hash) { + CTX->dn_hash_impl->update( + &CTX->dn_hash.vtable, CTX->hbuf, clen); + } + CTX->hbuf += clen; + CTX->hlen -= clen; + T0_PUSH(addr + clen); + T0_PUSH(len - clen); + + } + break; + case 50: { + /* read8-low */ + + if (CTX->hlen == 0) { + T0_PUSHi(-1); + } else { + unsigned char x = *CTX->hbuf ++; + if (CTX->do_mhash) { + br_multihash_update(&CTX->mhash, &x, 1); + } + if (CTX->do_dn_hash) { + CTX->dn_hash_impl->update(&CTX->dn_hash.vtable, &x, 1); + } + CTX->hlen --; + T0_PUSH(x); + } + + } + break; + case 51: { + /* rot */ + T0_ROT(); + } + break; + case 52: { + /* set16 */ + + uint32_t addr = T0_POP(); + *(uint16_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 53: { + /* set32 */ + + uint32_t addr = T0_POP(); + *(uint32_t *)(void *)((unsigned char *)CTX + addr) = T0_POP(); + + } + break; + case 54: { + /* set8 */ + + uint32_t addr = T0_POP(); + *((unsigned char *)CTX + addr) = (unsigned char)T0_POP(); + + } + break; + case 55: { + /* start-dn-hash */ + + CTX->dn_hash_impl->init(&CTX->dn_hash.vtable); + CTX->do_dn_hash = 1; + + } + break; + case 56: { + /* start-tbs-hash */ + + br_multihash_init(&CTX->mhash); + CTX->do_mhash = 1; + + } + break; + case 57: { + /* stop-tbs-hash */ + + CTX->do_mhash = 0; + + } + break; + case 58: { + /* swap */ + T0_SWAP(); + } + break; + case 59: { + /* zero-server-name */ + + T0_PUSHi(-(CTX->server_name == NULL)); + + } + break; + } + + } else { + T0_ENTER(ip, rp, t0x); + } + } +t0_exit: + ((t0_context *)t0ctx)->dp = dp; + ((t0_context *)t0ctx)->rp = rp; + ((t0_context *)t0ctx)->ip = ip; +} + + + +/* + * Verify the signature on the certificate with the provided public key. + * This function checks the public key type with regards to the expected + * type. Returned value is either 0 on success, or a non-zero error code. + */ +static int +verify_signature(br_x509_minimal_context *ctx, const br_x509_pkey *pk) +{ + unsigned char tmp[64]; + unsigned char tmp2[64]; + int kt; + + kt = ctx->cert_signer_key_type; + if ((pgm_read_byte(&pk->key_type) & 0x0F) != kt) { + return BR_ERR_X509_WRONG_KEY_TYPE; + } + switch (kt) { + + case BR_KEYTYPE_RSA: + if (ctx->irsa == 0) { + return BR_ERR_X509_UNSUPPORTED; + } + memcpy_P(tmp2, &t0_datablock[ctx->cert_sig_hash_oid], ctx->cert_sig_hash_len); + if (!ctx->irsa(ctx->cert_sig, ctx->cert_sig_len, + tmp2, //&t0_datablock[ctx->cert_sig_hash_oid], + ctx->cert_sig_hash_len, &pk->key.rsa, tmp)) + { + return BR_ERR_X509_BAD_SIGNATURE; + } + if (memcmp(ctx->tbs_hash, tmp, ctx->cert_sig_hash_len) != 0) { + return BR_ERR_X509_BAD_SIGNATURE; + } + return 0; + + case BR_KEYTYPE_EC: + if (ctx->iecdsa == 0) { + return BR_ERR_X509_UNSUPPORTED; + } + if (!ctx->iecdsa(ctx->iec, ctx->tbs_hash, + ctx->cert_sig_hash_len, &pk->key.ec, + ctx->cert_sig, ctx->cert_sig_len)) + { + return BR_ERR_X509_BAD_SIGNATURE; + } + return 0; + + default: + return BR_ERR_X509_UNSUPPORTED; + } +} diff --git a/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c new file mode 100644 index 0000000000..89352672af --- /dev/null +++ b/lib/lib_ssl/bearssl-esp8266/src/x509/x509_minimal_full.c @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016 Thomas Pornin + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "t_inner.h" + +/* see bearssl_x509.h */ +void +br_x509_minimal_init_full(br_x509_minimal_context *xc, + const br_x509_trust_anchor *trust_anchors, size_t trust_anchors_num) +{ + /* + * All hash functions are activated. + * Note: the X.509 validation engine will nonetheless refuse to + * validate signatures that use MD5 as hash function. + */ + static const br_hash_class *hashes[] = { + &br_md5_vtable, + &br_sha1_vtable, + &br_sha224_vtable, + &br_sha256_vtable, + &br_sha384_vtable, + &br_sha512_vtable + }; + + int id; + + br_x509_minimal_init(xc, &br_sha256_vtable, + trust_anchors, trust_anchors_num); + br_x509_minimal_set_rsa(xc, &br_rsa_i31_pkcs1_vrfy); + br_x509_minimal_set_ecdsa(xc, + &br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1); + for (id = br_md5_ID; id <= br_sha512_ID; id ++) { + const br_hash_class *hc; + + hc = hashes[id - 1]; + br_x509_minimal_set_hash(xc, id, hc); + } +} diff --git a/lib/lib_ssl/tls_mini/library.json b/lib/lib_ssl/tls_mini/library.json new file mode 100644 index 0000000000..d93dfd353b --- /dev/null +++ b/lib/lib_ssl/tls_mini/library.json @@ -0,0 +1,17 @@ +{ + "name": "TLS mini client derived from Arduino", + "version": "1.0", + "description": "TLS mini client derived from Arduino", + "license": "MIT", + "homepage": "https://github.com/arendst/Tasmota", + "frameworks": "arduino", + "platforms": "espressif32, espressif8266", + "authors": + { + "name": "Stephan Hadinger", + "maintainer": true + }, + "build": { + "flags": [ "-I$PROJECT_DIR/include" ] + } +} \ No newline at end of file diff --git a/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp new file mode 100644 index 0000000000..098752c889 --- /dev/null +++ b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp @@ -0,0 +1,169 @@ +/* + StackThunk_light.c - Allow use second stack for BearSSL calls + Light version with reduced Stack size due to Tasmota optimizations. + + BearSSL uses a significant amount of stack space, much larger than + the default Arduino core stack. These routines handle swapping + between a secondary, user-allocated stack on the heap and the real + stack. + + Copyright (C) 2021 Earle F. Philhower, III. All rights reserved. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) +*/ + +#include "tasmota_options.h" + +#if defined(ESP8266) && defined(USE_TLS) +#include +#include +#include "StackThunk_light.h" +#include + +extern "C" { + +extern void yield(); +extern bool can_yield(); + +uint32_t *stack_thunk_light_ptr = NULL; +uint32_t *stack_thunk_light_top = NULL; +uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ +uint32_t stack_thunk_light_refcnt = 0; + +//#define _stackSize (5600/4) +#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) || defined(USE_MQTT_AZURE_IOT) + #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes +#else + #define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300 +#endif +#define _stackPaint 0xdeadbeef + +void stack_thunk_yield() +{ + if (can_yield()) { + uint32_t tmp; + register uint32_t* save __asm__("a3") = stack_thunk_light_save; + + __asm__ __volatile__ ( + "mov.n %0, a1\n\t" + "mov.n a1, %1\n\t" + : "=r"(tmp) : "r"(save) : "memory"); + + yield(); + + __asm__ __volatile__ ( + "mov.n a1, %0\n\t" + :: "r"(tmp) : "memory"); + } +} + +/* Add a reference, and allocate the stack if necessary */ +void stack_thunk_light_add_ref() +{ + stack_thunk_light_refcnt++; + if (stack_thunk_light_refcnt == 1) { + stack_thunk_light_ptr = (uint32_t *)malloc(_stackSize * sizeof(uint32_t)); + stack_thunk_light_top = stack_thunk_light_ptr + _stackSize - 1; + stack_thunk_light_save = NULL; + stack_thunk_light_repaint(); + } +} + +/* Drop a reference, and free stack if no more in use */ +void stack_thunk_light_del_ref() +{ + if (stack_thunk_light_refcnt == 0) { + /* Error! */ + return; + } + stack_thunk_light_refcnt--; + if (!stack_thunk_light_refcnt) { + free(stack_thunk_light_ptr); + stack_thunk_light_ptr = NULL; + stack_thunk_light_top = NULL; + stack_thunk_light_save = NULL; + } +} + +void stack_thunk_light_repaint() +{ + if (stack_thunk_light_ptr) { + for (int i=0; i < _stackSize; i++) { + stack_thunk_light_ptr[i] = _stackPaint; + } + } +} + +/* Simple accessor functions used by postmortem */ +uint32_t stack_thunk_light_get_refcnt() { + return stack_thunk_light_refcnt; +} + +uint32_t stack_thunk_light_get_stack_top() { + return (uint32_t)stack_thunk_light_top; +} + +uint32_t stack_thunk_light_get_stack_bot() { + return (uint32_t)stack_thunk_light_ptr; +} + +uint32_t stack_thunk_light_get_cont_sp() { + return (uint32_t)stack_thunk_light_save; +} + +/* Return the number of bytes ever used since the stack was created */ +uint32_t stack_thunk_light_get_max_usage() +{ + uint32_t cnt = 0; + + /* No stack == no usage by definition! */ + if (!stack_thunk_light_ptr) { + return 0; + } + + for (cnt=0; (cnt < _stackSize) && (stack_thunk_light_ptr[cnt] == _stackPaint); cnt++) { + /* Noop, all work done in for() */ + } + return 4 * (_stackSize - cnt); +} + +/* Print the stack from the first used 16-byte chunk to the top, decodable by the exception decoder */ +// void stack_thunk_light_dump_stack() +// { +// uint32_t *pos = stack_thunk_light_top; +// while (pos < stack_thunk_light_ptr) { +// if ((pos[0] != _stackPaint) || (pos[1] != _stackPaint) || (pos[2] != _stackPaint) || (pos[3] != _stackPaint)) +// break; +// pos += 4; +// } +// ets_printf(">>>stack>>>\n"); +// while (pos < stack_thunk_light_ptr) { +// ets_printf("%08x: %08x %08x %08x %08x\n", (int32_t)pos, pos[0], pos[1], pos[2], pos[3]); +// pos += 4; +// } +// ets_printf("<< +#include +#include + +#include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers +#include "ESP8266WiFi.h" +#include "WiFiHelper.h" +#include "WiFiClient.h" +#include "StackThunk_light.h" +#include "lwip/opt.h" +#include "lwip/ip.h" +#include "lwip/tcp.h" +#include "lwip/inet.h" +#include "lwip/netif.h" +#ifdef ESP8266 + #include + #include "c_types.h" +#endif + +#include +#undef DEBUG_TLS + +#ifdef DEBUG_TLS +#include "coredecls.h" +#define LOG_HEAP_SIZE(a) _Log_heap_size(a) +void _Log_heap_size(const char *msg) { +#ifdef ESP8266 + register uint32_t *sp asm("a1"); + int freestack = 4 * (sp - g_pcont->stack); + Serial.printf("%s %d, Fragmentation=%d, Thunkstack=%d, Free stack=%d, FreeContStack=%d\n", + msg, ESP.getFreeHeap(), ESP.getHeapFragmentation(), stack_thunk_light_get_max_usage(), + freestack, ESP.getFreeContStack()); +#elif defined(ESP32) + Serial.printf("> Heap %s = %d\n", msg, uxTaskGetStackHighWaterMark(nullptr)); +#endif +} +#else +#define LOG_HEAP_SIZE(a) +#endif + +// get UTC time from Tasmota +extern uint32_t UtcTime(void); +extern uint32_t CfgTime(void); + +#ifdef ESP8266 // Stack thunk is not needed with ESP32 +// Stack thunked versions of calls +// Initially in BearSSLHelpers.h +extern "C" { +extern unsigned char *thunk_light_br_ssl_engine_recvapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_recvrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendapp_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len); +extern unsigned char *thunk_light_br_ssl_engine_sendrec_buf( const br_ssl_engine_context *cc, size_t *len); +extern void thunk_light_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len); +}; + +// Second stack thunked helpers +make_stack_thunk_light(br_ssl_engine_recvapp_ack); +make_stack_thunk_light(br_ssl_engine_recvapp_buf); +make_stack_thunk_light(br_ssl_engine_recvrec_ack); +make_stack_thunk_light(br_ssl_engine_recvrec_buf); +make_stack_thunk_light(br_ssl_engine_sendapp_ack); +make_stack_thunk_light(br_ssl_engine_sendapp_buf); +make_stack_thunk_light(br_ssl_engine_sendrec_ack); +make_stack_thunk_light(br_ssl_engine_sendrec_buf); + +// create new version of Thunk function to store on SYS stack +// unless the Thunk was initialized. Thanks to AES128 GCM, we can keep +// symetric processing on the stack +void min_br_ssl_engine_recvapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_ack(cc, len); + } else { + return br_ssl_engine_recvapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvapp_buf(cc, len); + } else { + return br_ssl_engine_recvapp_buf(cc, len); + } +} +void min_br_ssl_engine_recvrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_ack(cc, len); + } else { + return br_ssl_engine_recvrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_recvrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_recvrec_buf(cc, len); + } else { + return br_ssl_engine_recvrec_buf(cc, len); + } +} +void min_br_ssl_engine_sendapp_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_ack(cc, len); + } else { + return br_ssl_engine_sendapp_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendapp_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendapp_buf(cc, len); + } else { + return br_ssl_engine_sendapp_buf(cc, len); + } +} +void min_br_ssl_engine_sendrec_ack(br_ssl_engine_context *cc, size_t len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_ack(cc, len); + } else { + return br_ssl_engine_sendrec_ack(cc, len); + } +} +unsigned char *min_br_ssl_engine_sendrec_buf(const br_ssl_engine_context *cc, size_t *len) { + if (stack_thunk_light_get_refcnt()) { + return thunk_light_br_ssl_engine_sendrec_buf(cc, len); + } else { + return br_ssl_engine_sendrec_buf(cc, len); + } +} + +// Use min_ instead of original thunk_ +#define br_ssl_engine_recvapp_ack min_br_ssl_engine_recvapp_ack +#define br_ssl_engine_recvapp_buf min_br_ssl_engine_recvapp_buf +#define br_ssl_engine_recvrec_ack min_br_ssl_engine_recvrec_ack +#define br_ssl_engine_recvrec_buf min_br_ssl_engine_recvrec_buf +#define br_ssl_engine_sendapp_ack min_br_ssl_engine_sendapp_ack +#define br_ssl_engine_sendapp_buf min_br_ssl_engine_sendapp_buf +#define br_ssl_engine_sendrec_ack min_br_ssl_engine_sendrec_ack +#define br_ssl_engine_sendrec_buf min_br_ssl_engine_sendrec_buf + +#endif // ESP8266 + +//#define DEBUG_ESP_SSL +#ifdef DEBUG_ESP_SSL +//#define DEBUG_BSSL(fmt, ...) DEBUG_ESP_PORT.printf_P((PGM_P)PSTR( "BSSL:" fmt), ## __VA_ARGS__) +#define DEBUG_BSSL(fmt, ...) Serial.printf(fmt, ## __VA_ARGS__) +#else +#define DEBUG_BSSL(...) +#endif + +namespace BearSSL { + +void WiFiClientSecure_light::_clear() { + // TLS handshake may take more than the 5 second default timeout + _timeout = 10000; // 10 seconds max, it should never go over 6 seconds + + _sc = nullptr; + _ctx_present = false; + _eng = nullptr; + _iobuf_in = nullptr; + _iobuf_out = nullptr; + setBufferSizes(1024, 1024); // reasonable minimum + _handshake_done = false; + _last_error = 0; + _recvapp_buf = nullptr; + _recvapp_len = 0; + _insecure = false; // set to true when calling setPubKeyFingerprint() + _fingerprint_any = true; // by default accept all fingerprints + _fingerprint1 = nullptr; + _fingerprint2 = nullptr; + _chain_P = nullptr; + _sk_ec_P = nullptr; + _ta_P = nullptr; + _ta_size = 0; + _max_thunkstack_use = 0; + _alpn_names = nullptr; + _alpn_num = 0; +} + +// Constructor +WiFiClientSecure_light::WiFiClientSecure_light(int recv, int xmit) : WiFiClient() { + _clear(); + // LOG_HEAP_SIZE("StackThunk before"); + //stack_thunk_light_add_ref(); + // LOG_HEAP_SIZE("StackThunk after"); + // now finish the setup + setBufferSizes(recv, xmit); // reasonable minimum + allocateBuffers(); +} + +WiFiClientSecure_light::~WiFiClientSecure_light() { +#ifdef ESP8266 + if (_client) { + _client->unref(); + _client = nullptr; + } +#elif defined(ESP32) + stop(); +#endif + //_cipher_list = nullptr; // std::shared will free if last reference + _freeSSL(); +} + +void WiFiClientSecure_light::allocateBuffers(void) { + // We prefer to allocate all buffers at start, rather than lazy allocation and deallocation + // in the long run it avoids heap fragmentation and improves stability + LOG_HEAP_SIZE("allocateBuffers before"); + _sc = std::make_shared(); + LOG_HEAP_SIZE("allocateBuffers ClientContext"); + _iobuf_in = std::shared_ptr(new unsigned char[_iobuf_in_size], std::default_delete()); + _iobuf_out = std::shared_ptr(new unsigned char[_iobuf_out_size], std::default_delete()); + LOG_HEAP_SIZE("allocateBuffers after"); +} + +void WiFiClientSecure_light::setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type) { + _chain_P = cert; + _sk_ec_P = sk; + _allowed_usages = allowed_usages; + _cert_issuer_key_type = cert_issuer_key_type; +} + +void WiFiClientSecure_light::setTrustAnchor(const br_x509_trust_anchor *ta, size_t ta_size) { + _ta_P = ta; + _ta_size = ta_size; +} + +void WiFiClientSecure_light::setBufferSizes(int recv, int xmit) { + // Following constants taken from bearssl/src/ssl/ssl_engine.c (not exported unfortunately) + const int MAX_OUT_OVERHEAD = 85; + const int MAX_IN_OVERHEAD = 325; + + // The data buffers must be between 512B and 16KB + recv = std::max(512, std::min(16384, recv)); + xmit = std::max(512, std::min(16384, xmit)); + + // Add in overhead for SSL protocol + recv += MAX_IN_OVERHEAD; + xmit += MAX_OUT_OVERHEAD; + _iobuf_in_size = recv; + _iobuf_out_size = xmit; +} + +#ifdef ESP8266 +bool WiFiClientSecure_light::stop(unsigned int maxWaitMs) { + bool ret = WiFiClient::stop(maxWaitMs); // calls our virtual flush() + _freeSSL(); + return ret; +} + +bool WiFiClientSecure_light::flush(unsigned int maxWaitMs) { + (void) _run_until(BR_SSL_SENDAPP); + return WiFiClient::flush(maxWaitMs); +} +#elif defined(ESP32) +void WiFiClientSecure_light::stop(void) { + WiFiClient::stop(); // calls our virtual flush() + _freeSSL(); +} + +void WiFiClientSecure_light::flush(void) { + (void) _run_until(BR_SSL_SENDAPP); + // don't call flush on ESP32 - its behavior is different and empties the receive buffer - which we don't want +} +#endif + +#ifdef ESP32 +int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port, int32_t timeout) { + DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port); + clearLastError(); + if (!WiFiClient::connect(ip, port, timeout)) { + setLastError(ERR_TCP_CONNECT); + return 0; + } + return _connectSSL(_domain.isEmpty() ? nullptr : _domain.c_str()); +} +#else // ESP32 +int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) { + DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port); + clearLastError(); + if (!WiFiClient::connect(ip, port)) { + setLastError(ERR_TCP_CONNECT); + return 0; + } + return _connectSSL(_domain.isEmpty() ? nullptr : _domain.c_str()); +} +#endif + +#ifdef ESP32 +int WiFiClientSecure_light::connect(const char* name, uint16_t port, int32_t timeout) { + DEBUG_BSSL("connect(%s,%d)\n", name, port); + IPAddress remote_addr; + clearLastError(); + if (!WiFiHelper::hostByName(name, remote_addr)) { + DEBUG_BSSL("connect: Name loopup failure\n"); + setLastError(ERR_CANT_RESOLVE_IP); + return 0; + } + DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port); + if (!WiFiClient::connect(remote_addr, port, timeout)) { + DEBUG_BSSL("connect: Unable to connect TCP socket\n"); + _last_error = ERR_TCP_CONNECT; + return 0; + } + LOG_HEAP_SIZE("Before calling _connectSSL"); + return _connectSSL(name); +} +#else // ESP32 +int WiFiClientSecure_light::connect(const char* name, uint16_t port) { + DEBUG_BSSL("connect(%s,%d)\n", name, port); + IPAddress remote_addr; + clearLastError(); + if (!WiFiHelper::hostByName(name, remote_addr)) { + DEBUG_BSSL("connect: Name loopup failure\n"); + setLastError(ERR_CANT_RESOLVE_IP); + return 0; + } + DEBUG_BSSL("connect(%s,%d)\n", remote_addr.toString().c_str(), port); + if (!WiFiClient::connect(remote_addr, port)) { + DEBUG_BSSL("connect: Unable to connect TCP socket\n"); + _last_error = ERR_TCP_CONNECT; + return 0; + } + LOG_HEAP_SIZE("Before calling _connectSSL"); + return _connectSSL(name); +} +#endif + +void WiFiClientSecure_light::_freeSSL() { + _ctx_present = false; + _recvapp_buf = nullptr; + _recvapp_len = 0; + // This connection is toast + _handshake_done = false; +} + +bool WiFiClientSecure_light::_clientConnected() { +#ifdef ESP8266 + return (_client && _client->state() == ESTABLISHED); +#elif defined(ESP32) + return WiFiClient::connected(); +#endif +} + +uint8_t WiFiClientSecure_light::connected() { + if (available() || (_clientConnected() && _handshake_done)) { + return true; + } + return false; +} + +size_t WiFiClientSecure_light::_write(const uint8_t *buf, size_t size, bool pmem) { + size_t sent_bytes = 0; + + if (!connected() || !size || !_handshake_done) { + return 0; + } + + do { + // Ensure we yield if we need multiple fragments to avoid WDT + if (sent_bytes) { + optimistic_yield(1000); + } + + // Get BearSSL to a state where we can send + if (_run_until(BR_SSL_SENDAPP) < 0) { + break; + } + + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + size_t sendapp_len; + unsigned char *sendapp_buf = br_ssl_engine_sendapp_buf(_eng, &sendapp_len); + int to_send = size > sendapp_len ? sendapp_len : size; + if (pmem) { + memcpy_P(sendapp_buf, buf, to_send); + } else { + memcpy(sendapp_buf, buf, to_send); + } + br_ssl_engine_sendapp_ack(_eng, to_send); + br_ssl_engine_flush(_eng, 0); + flush(); + buf += to_send; + sent_bytes += to_send; + size -= to_send; + } else { + break; + } + } while (size); + + LOG_HEAP_SIZE("_write"); + return sent_bytes; +} + +void WiFiClientSecure_light::setInsecure() { + _insecure = true; +} + +size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) { + return _write(buf, size, false); +} + +size_t WiFiClientSecure_light::write_P(PGM_P buf, size_t size) { + return _write((const uint8_t *)buf, size, true); +} + +// We have to manually read and send individual chunks. +size_t WiFiClientSecure_light::write(Stream& stream) { + size_t totalSent = 0; + size_t countRead; + size_t countSent; + + if (!connected() || !_handshake_done) { + DEBUG_BSSL("write: Connect/handshake not completed yet\n"); + return 0; + } + + do { + uint8_t temp[256]; // Temporary chunk size same as ClientContext + countSent = 0; + countRead = stream.readBytes(temp, sizeof(temp)); + if (countRead) { + countSent = _write((const uint8_t*)temp, countRead, true); + totalSent += countSent; + } + yield(); // Feed the WDT + } while ((countSent == countRead) && (countSent > 0)); + return totalSent; +} + +int WiFiClientSecure_light::read(uint8_t *buf, size_t size) { + if (!ctx_present() || !_handshake_done) { + return -1; + } + + int avail = available(); + bool conn = connected(); + if (!avail && conn) { + return 0; // We're still connected, but nothing to read + } + if (!avail && !conn) { + DEBUG_BSSL("read: Not connected, none left available\n"); + return -1; + } + + if (avail) { + // Take data from the recvapp buffer + int to_copy = _recvapp_len < size ? _recvapp_len : size; + memcpy(buf, _recvapp_buf, to_copy); + br_ssl_engine_recvapp_ack(_eng, to_copy); + _recvapp_buf = nullptr; + _recvapp_len = 0; + return to_copy; + } + + if (!conn) { + DEBUG_BSSL("read: Not connected\n"); + return -1; + } + return 0; // If we're connected, no error but no read. +} + +int WiFiClientSecure_light::read() { + uint8_t c; + if (1 == read(&c, 1)) { + return c; + } + DEBUG_BSSL("read: failed\n"); + return -1; +} + +int WiFiClientSecure_light::available() { + if (_recvapp_buf) { + return _recvapp_len; // Anything from last call? + } + _recvapp_buf = nullptr; + _recvapp_len = 0; + if (!ctx_present() || _run_until(BR_SSL_RECVAPP, false) < 0) { + return 0; + } + int st = br_ssl_engine_current_state(_eng); + if (st == BR_SSL_CLOSED) { + return 0; // Nothing leftover, SSL is closed + } + if (st & BR_SSL_RECVAPP) { + _recvapp_buf = br_ssl_engine_recvapp_buf(_eng, &_recvapp_len); + return _recvapp_len; + } + + return 0; +} + +int WiFiClientSecure_light::peek() { + if (!ctx_present() || !available()) { + DEBUG_BSSL("peek: Not connected, none left available\n"); + return -1; + } + if (_recvapp_buf && _recvapp_len) { + return _recvapp_buf[0]; + } + DEBUG_BSSL("peek: No data left\n"); + return -1; +} + +size_t WiFiClientSecure_light::peekBytes(uint8_t *buffer, size_t length) { + size_t to_copy = 0; + if (!ctx_present()) { + DEBUG_BSSL("peekBytes: Not connected\n"); + return 0; + } + + _startMillis = millis(); + while ((available() < (int) length) && ((millis() - _startMillis) < 5000)) { + yield(); + } + + to_copy = _recvapp_len < length ? _recvapp_len : length; + memcpy(buffer, _recvapp_buf, to_copy); + return to_copy; +} + +/* --- Copied almost verbatim from BEARSSL SSL_IO.C --- + Run the engine, until the specified target state is achieved, or + an error occurs. The target state is SENDAPP, RECVAPP, or the + combination of both (the combination matches either). When a match is + achieved, this function returns 0. On error, it returns -1. +*/ +int WiFiClientSecure_light::_run_until(unsigned target, bool blocking) { + //LOG_HEAP_SIZE("_run_until 1"); + if (!ctx_present()) { + DEBUG_BSSL("_run_until: Not connected\n"); + return -1; + } + uint32_t t = millis(); + for (int no_work = 0; blocking || no_work < 2;) { + if (blocking) { + // Only for blocking operations can we afford to yield() + optimistic_yield(100); + } + + if (((int32_t)(millis() - (t + this->_loopTimeout)) >= 0)){ + DEBUG_BSSL("_run_until: Timeout\n"); + return -1; + } + + int state; + state = br_ssl_engine_current_state(_eng); + if (state & BR_SSL_CLOSED) { + return -1; + } + +#ifdef ESP8266 + if (!(_client->state() == ESTABLISHED) && !WiFiClient::available()) { + return (state & target) ? 0 : -1; + } +#elif defined(ESP32) + if (!_clientConnected() && !WiFiClient::available()) { + return (state & target) ? 0 : -1; + } +#endif + + /* + If there is some record data to send, do it. This takes + precedence over everything else. + */ + if (state & BR_SSL_SENDREC) { + unsigned char *buf; + size_t len; + int wlen; + + buf = br_ssl_engine_sendrec_buf(_eng, &len); + wlen = WiFiClient::write(buf, len); + if (wlen <= 0) { + /* + If we received a close_notify and we + still send something, then we have our + own response close_notify to send, and + the peer is allowed by RFC 5246 not to + wait for it. + */ + return -1; + } + if (wlen > 0) { + br_ssl_engine_sendrec_ack(_eng, wlen); + } + no_work = 0; + continue; + } + + /* + If we reached our target, then we are finished. + */ + if (state & target) { + return 0; + } + /* + If some application data must be read, and we did not + exit, then this means that we are trying to write data, + and that's not possible until the application data is + read. This may happen if using a shared in/out buffer, + and the underlying protocol is not strictly half-duplex. + This is unrecoverable here, so we report an error. + */ + if (state & BR_SSL_RECVAPP) { + DEBUG_BSSL("_run_until: Fatal protocol state\n"); + return -1; + } + /* + If we reached that point, then either we are trying + to read data and there is some, or the engine is stuck + until a new record is obtained. + */ + if (state & BR_SSL_RECVREC) { + if (WiFiClient::available()) { + unsigned char *buf; + size_t len; + int rlen; + + buf = br_ssl_engine_recvrec_buf(_eng, &len); + rlen = WiFiClient::read(buf, len); + if (rlen < 0) { + return -1; + } + if (rlen > 0) { + br_ssl_engine_recvrec_ack(_eng, rlen); + } + no_work = 0; + continue; + } + } + /* + We can reach that point if the target RECVAPP, and + the state contains SENDAPP only. This may happen with + a shared in/out buffer. In that case, we must flush + the buffered data to "make room" for a new incoming + record. + */ + br_ssl_engine_flush(_eng, 0); + + no_work++; // We didn't actually advance here + } + // We only get here if we ran through the loop without getting anything done + return -1; +} + +bool WiFiClientSecure_light::_wait_for_handshake() { + _handshake_done = false; + while (!_handshake_done && _clientConnected()) { + int ret = _run_until(BR_SSL_SENDAPP); + if (ret < 0) { + DEBUG_BSSL("_wait_for_handshake: failed\n"); + break; + } + if (br_ssl_engine_current_state(_eng) & BR_SSL_SENDAPP) { + _handshake_done = true; + } + optimistic_yield(1000); + } + return _handshake_done; +} + +static uint8_t htoi (unsigned char c) +{ + if (c>='0' && c <='9') return c - '0'; + else if (c>='A' && c<='F') return 10 + c - 'A'; + else if (c>='a' && c<='f') return 10 + c - 'a'; + else return 255; +} + +extern "C" { + + // see https://stackoverflow.com/questions/6357031/how-do-you-convert-a-byte-array-to-a-hexadecimal-string-in-c + void tohex(unsigned char * in, size_t insz, char * out, size_t outsz) { + unsigned char * pin = in; + static const char * hex = "0123456789ABCDEF"; + char * pout = out; + for(; pin < in+insz; pout +=3, pin++){ + pout[0] = hex[(*pin>>4) & 0xF]; + pout[1] = hex[ *pin & 0xF]; + pout[2] = ':'; + if (pout + 3 - out > outsz){ + /* Better to truncate output string than overflow buffer */ + /* it would be still better to either return a status */ + /* or ensure the target buffer is large enough and it never happen */ + break; + } + } + pout[-1] = 0; + } + + + // BearSSL doesn't define a true insecure decoder, so we make one ourselves + // from the simple parser. It generates the issuer and subject hashes and + // the SHA1 fingerprint, only one (or none!) of which will be used to + // "verify" the certificate. + + // Private x509 decoder state + struct br_x509_pubkeyfingerprint_context { + const br_x509_class *vtable; + bool done_cert; // did we parse the first cert already? + bool fingerprint_all; + uint8_t *pubkey_recv_fingerprint; + const uint8_t *fingerprint1; + const uint8_t *fingerprint2; + unsigned usages; // pubkey usage + br_x509_decoder_context ctx; // defined in BearSSL + }; + + // Callback on the first byte of any certificate + static void pubkeyfingerprint_start_chain(const br_x509_class **ctx, const char *server_name) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_init(&xc->ctx, nullptr, nullptr, nullptr, nullptr); + } + (void)server_name; // ignore server name + } + + // Callback for each certificate present in the chain (but only operates + // on the first one by design). + static void pubkeyfingerprint_start_cert(const br_x509_class **ctx, uint32_t length) { + (void) ctx; // do nothing + (void) length; + } + + // Callback for each byte stream in the chain. Only process first cert. + static void pubkeyfingerprint_append(const br_x509_class **ctx, const unsigned char *buf, size_t len) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // Don't process anything but the first certificate in the chain + if (!xc->done_cert) { + br_x509_decoder_push(&xc->ctx, (const void*)buf, len); + } + } + + // Callback on individual cert end. + static void pubkeyfingerprint_end_cert(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + xc->done_cert = true; // first cert already processed + } + +// **** Start patch Castellucci +/* + static void pubkeyfingerprint_pubkey_fingerprint(br_sha1_context *shactx, br_rsa_public_key rsakey) { + br_sha1_init(shactx); + br_sha1_update(shactx, "ssh-rsa", 7); // tag + br_sha1_update(shactx, rsakey.e, rsakey.elen); // exponent + br_sha1_update(shactx, rsakey.n, rsakey.nlen); // modulus + } +*/ + // If `compat` id false, adds a u32be length prefixed value to the sha1 state. + // If `compat` is true, the length will be omitted for compatibility with + // data from older versions of Tasmota. + static void sha1_update_len(br_sha1_context *shactx, const void *msg, uint32_t len, bool compat) { + uint8_t buf[] = {0, 0, 0, 0}; + + if (!compat) { + buf[0] = (len >> 24) & 0xff; + buf[1] = (len >> 16) & 0xff; + buf[2] = (len >> 8) & 0xff; + buf[3] = (len >> 0) & 0xff; + br_sha1_update(shactx, buf, 4); // length + } + br_sha1_update(shactx, msg, len); // message + } + + // Update the received fingerprint based on the certificate's public key. + // If `compat` is true, an insecure version of the fingerprint will be + // calcualted for compatibility with older versions of Tasmota. Normally, + // `compat` should be false. + static void pubkeyfingerprint_pubkey_fingerprint(br_x509_pubkeyfingerprint_context *xc, bool compat) { + br_rsa_public_key rsakey = xc->ctx.pkey.key.rsa; + + br_sha1_context shactx; + + br_sha1_init(&shactx); + + sha1_update_len(&shactx, "ssh-rsa", 7, compat); // tag + sha1_update_len(&shactx, rsakey.e, rsakey.elen, compat); // exponent + sha1_update_len(&shactx, rsakey.n, rsakey.nlen, compat); // modulus + + br_sha1_out(&shactx, xc->pubkey_recv_fingerprint); // copy to fingerprint + } +// **** End patch Castellucci + + // Callback when complete chain has been parsed. + // Return 0 on validation success, !0 on validation error + static unsigned pubkeyfingerprint_end_chain(const br_x509_class **ctx) { + br_x509_pubkeyfingerprint_context *xc = (br_x509_pubkeyfingerprint_context *)ctx; + // set fingerprint status byte to zero + // FIXME: find a better way to pass this information + xc->pubkey_recv_fingerprint[20] = 0; + // Try matching using the the new fingerprint algorithm + pubkeyfingerprint_pubkey_fingerprint(xc, false); + if (!xc->fingerprint_all) { + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint1, 20)) { + return 0; + } + if (0 == memcmp_P(xc->pubkey_recv_fingerprint, xc->fingerprint2, 20)) { + return 0; + } + return 1; // no match, error + } else { + // Default (no validation at all) or no errors in prior checks = success. + return 0; + } +// **** End patch Castellucci + } + + // Return the public key from the validator (set by x509_minimal) + static const br_x509_pkey *pubkeyfingerprint_get_pkey(const br_x509_class *const *ctx, unsigned *usages) { + const br_x509_pubkeyfingerprint_context *xc = (const br_x509_pubkeyfingerprint_context *)ctx; + + if (usages != NULL) { + *usages = BR_KEYTYPE_KEYX | BR_KEYTYPE_SIGN; // I said we were insecure! + } + return &xc->ctx.pkey; + } + + // Set up the x509 insecure data structures for BearSSL core to use. + void br_x509_pubkeyfingerprint_init(br_x509_pubkeyfingerprint_context *ctx, + const uint8_t *fingerprint1, const uint8_t *fingerprint2, + uint8_t *recv_fingerprint, + bool fingerprint_all) { + static const br_x509_class br_x509_pubkeyfingerprint_vtable PROGMEM = { + sizeof(br_x509_pubkeyfingerprint_context), + pubkeyfingerprint_start_chain, + pubkeyfingerprint_start_cert, + pubkeyfingerprint_append, + pubkeyfingerprint_end_cert, + pubkeyfingerprint_end_chain, + pubkeyfingerprint_get_pkey + }; + + memset(ctx, 0, sizeof * ctx); + ctx->vtable = &br_x509_pubkeyfingerprint_vtable; + ctx->done_cert = false; + ctx->fingerprint1 = fingerprint1; + ctx->fingerprint2 = fingerprint2; + ctx->pubkey_recv_fingerprint = recv_fingerprint; + ctx->fingerprint_all = fingerprint_all; + } + + // We limit to a single cipher to reduce footprint + // we reference it, don't put in PROGMEM + static const uint16_t suites[] = { + BR_TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + }; + + // Default initializion for our SSL clients + static void br_ssl_client_base_init(br_ssl_client_context *cc) { + br_ssl_client_zero(cc); + // forbid SSL renegotiation, as we free the Private Key after handshake + br_ssl_engine_add_flags(&cc->eng, BR_OPT_NO_RENEGOTIATION); + + br_ssl_engine_set_versions(&cc->eng, BR_TLS12, BR_TLS12); + br_ssl_engine_set_suites(&cc->eng, suites, (sizeof suites) / (sizeof suites[0])); + br_ssl_client_set_default_rsapub(cc); + br_ssl_engine_set_default_rsavrfy(&cc->eng); + + // install hashes + br_ssl_engine_set_hash(&cc->eng, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_prf_sha256(&cc->eng, &br_tls12_sha256_prf); + + // AES CTR/GCM small version, not contstant time (we don't really care here as there is no TPM anyways) + br_ssl_engine_set_gcm(&cc->eng, &br_sslrec_in_gcm_vtable, &br_sslrec_out_gcm_vtable); + br_ssl_engine_set_aes_ctr(&cc->eng, &br_aes_small_ctr_vtable); + br_ssl_engine_set_ghash(&cc->eng, &br_ghash_ctmul32); + + // we support only P256 EC curve for AWS IoT, no EC curve for Letsencrypt unless forced + br_ssl_engine_set_ec(&cc->eng, &br_ec_p256_m15); // TODO + } +} + +// Called by connect() to do the actual SSL setup and handshake. +// Returns if the SSL handshake succeeded. +bool WiFiClientSecure_light::_connectSSL(const char* hostName) { + // Validation context, either full CA validation or checking only fingerprints + + br_x509_minimal_context *x509_minimal = nullptr; + br_x509_pubkeyfingerprint_context *x509_insecure = nullptr; + + LOG_HEAP_SIZE("_connectSSL.start"); + + do { // used to exit on Out of Memory error and keep all cleanup code at the same place + // ============================================================ + // allocate Thunk stack, move to alternate stack and initialize +#ifdef ESP8266 + stack_thunk_light_add_ref(); +#endif // ESP8266 + LOG_HEAP_SIZE("Thunk allocated"); + DEBUG_BSSL("_connectSSL: start connection\n"); + _freeSSL(); + clearLastError(); +#ifdef ESP8266 + if (!stack_thunk_light_get_stack_bot()) break; +#endif // ESP8266 + + _ctx_present = true; + _eng = &_sc->eng; // Allocation/deallocation taken care of by the _sc shared_ptr + + br_ssl_client_base_init(_sc.get()); + if (_alpn_names && _alpn_num > 0) { + br_ssl_engine_set_protocol_names(_eng, _alpn_names, _alpn_num); + } + + // ============================================================ + // Allocatte and initialize Decoder Context + LOG_HEAP_SIZE("_connectSSL before DecoderContext allocation"); + // Only failure possible in the installation is OOM + + x509_insecure = (br_x509_pubkeyfingerprint_context*) malloc(sizeof(br_x509_pubkeyfingerprint_context)); + //x509_insecure = std::unique_ptr(new br_x509_pubkeyfingerprint_context); + if (!x509_insecure) break; + br_x509_pubkeyfingerprint_init(x509_insecure, _fingerprint1, _fingerprint2, _recv_fingerprint, _fingerprint_any); + br_ssl_engine_set_x509(_eng, &x509_insecure->vtable); + + if (!_insecure) { + x509_minimal = (br_x509_minimal_context*) malloc(sizeof(br_x509_minimal_context)); + if (!x509_minimal) break; + br_x509_minimal_init(x509_minimal, &br_sha256_vtable, _ta_P, _ta_size); + br_x509_minimal_set_rsa(x509_minimal, br_ssl_engine_get_rsavrfy(_eng)); + br_x509_minimal_set_hash(x509_minimal, br_sha256_ID, &br_sha256_vtable); + br_ssl_engine_set_x509(_eng, &x509_minimal->vtable); + uint32_t now = UtcTime(); + uint32_t cfg_time = CfgTime(); + if (cfg_time > now) { now = cfg_time; } + br_x509_minimal_set_time(x509_minimal, now / 86400 + 719528, now % 86400); + } + LOG_HEAP_SIZE("_connectSSL after DecoderContext allocation"); + + // ============================================================ + // Set send/receive buffers + br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); + + // ============================================================ + // allocate Private key if needed, only if USE_MQTT_AWS_IOT + LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); + #ifdef USE_MQTT_AWS_IOT + // ============================================================ + // Set the EC Private Key, only USE_MQTT_AWS_IOT + // limited to P256 curve + br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, + _sk_ec_P, _allowed_usages, + _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); + #endif // USE_MQTT_AWS_IOT + + // ============================================================ + // Start TLS connection, ALL + if (!br_ssl_client_reset(_sc.get(), hostName, 0)) break; + + auto ret = _wait_for_handshake(); + #ifdef DEBUG_ESP_SSL + if (!ret) { + DEBUG_BSSL("Couldn't connect. Error = %d\n", getLastError()); + } else { + DEBUG_BSSL("Connected! MFLNStatus = %d\n", getMFLNStatus()); + } + #endif + LOG_HEAP_SIZE("_connectSSL.end"); +#ifdef ESP8266 + _max_thunkstack_use = stack_thunk_light_get_max_usage(); + stack_thunk_light_del_ref(); + //stack_thunk_light_repaint(); + LOG_HEAP_SIZE("_connectSSL.end, freeing StackThunk"); +#endif // ESP8266 + + free(x509_minimal); // safe to call if nullptr + free(x509_insecure); + LOG_HEAP_SIZE("_connectSSL after release of Priv Key"); + return ret; + } while (0); + + // ============================================================ + // if we arrived here, this means we had an OOM error, cleaning up + setLastError(ERR_OOM); + DEBUG_BSSL("_connectSSL: Out of memory\n"); +#ifdef ESP8266 + stack_thunk_light_del_ref(); +#endif + free(x509_minimal); // safe to call if nullptr + free(x509_insecure); + LOG_HEAP_SIZE("_connectSSL clean_on_error"); + return false; +} + +}; + +#include "t_bearssl_tasmota_config.h" + +#endif // USE_TLS diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h new file mode 100644 index 0000000000..3f0a892a5f --- /dev/null +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h @@ -0,0 +1,300 @@ +/* + WiFiClientBearSSL- SSL client/server for esp8266 using BearSSL libraries + - Mostly compatible with Arduino WiFi shield library and standard + WiFiClient/ServerSecure (except for certificate handling). + + Copyright (C) 2021 Earle F. Philhower, III + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +#ifndef wificlientlightbearssl_h +#define wificlientlightbearssl_h +#ifdef USE_TLS +#include +#include "WiFiClient.h" +#include + +namespace BearSSL { + +class WiFiClientSecure_light : public WiFiClient { + public: + WiFiClientSecure_light(int recv, int xmit); + ~WiFiClientSecure_light() override; + + void allocateBuffers(void); + + #ifdef ESP32 // the method to override in ESP32 has timeout argument default #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) + int connect(IPAddress ip, uint16_t port, int32_t timeout = 3000) override; + int connect(const char* name, uint16_t port, int32_t timeout = 3000) override; + #else + int connect(IPAddress ip, uint16_t port) override; + int connect(const char* name, uint16_t port) override; + #endif + + uint8_t connected() override; + size_t write(const uint8_t *buf, size_t size) override; + #ifdef ESP8266 + size_t write_P(PGM_P buf, size_t size) override; + #else + size_t write_P(PGM_P buf, size_t size); + #endif + size_t write(const char *buf) { + return write((const uint8_t*)buf, strlen(buf)); + } + size_t write_P(const char *buf) { + return write_P((PGM_P)buf, strlen_P(buf)); + } + size_t write(Stream& stream); // Note this is not virtual + int read(uint8_t *buf, size_t size) override; + int available() override; + int read() override; + int peek() override; + #ifdef ESP8266 + size_t peekBytes(uint8_t *buffer, size_t length) override; + bool flush(unsigned int maxWaitMs); + bool stop(unsigned int maxWaitMs); + void flush() override { (void)flush(0); } + void stop() override { (void)stop(0); } + #else + size_t peekBytes(uint8_t *buffer, size_t length); + void flush() override; + void stop() override; + #endif + + // Only check SHA1 fingerprint of public key + void setPubKeyFingerprint(const uint8_t *f1, const uint8_t *f2, + bool f_any = false) { + _fingerprint1 = f1; + _fingerprint2 = f2; + _fingerprint_any = f_any; + _insecure = true; + } + const uint8_t * getRecvPubKeyFingerprint(void) { + return _recv_fingerprint; + } + + void setClientECCert(const br_x509_certificate *cert, const br_ec_private_key *sk, + unsigned allowed_usages, unsigned cert_issuer_key_type); + + void setTrustAnchor(const br_x509_trust_anchor *ta, size_t ta_size); + + void setALPN(const char **names, size_t num) { + // set ALPN extensions, used mostly by AWS IoT on port 443. Need to be static pointers + _alpn_names = names; + _alpn_num = num; + } + + // Sets the requested buffer size for transmit and receive + void setBufferSizes(int recv, int xmit); + + // Returns whether MFLN negotiation for the above buffer sizes succeeded (after connection) + int getMFLNStatus() { + return connected() && br_ssl_engine_get_mfln_negotiated(_eng); + } + + int32_t getLastError(void) { + if (_last_error) { + return _last_error; + } else { + return br_ssl_engine_last_error(_eng); + } + } + inline void setLastError(int32_t err) { + _last_error = err; + } + inline void clearLastError(void) { + _last_error = 0; + } + inline size_t getMaxThunkStackUse(void) { + return _max_thunkstack_use; + } + + void setInsecure(); + + void setDomainName(const char * domain) { + _domain = domain; + } + + private: + uint32_t _loopTimeout=5000; + void _clear(); + bool _ctx_present; + std::shared_ptr _sc; + inline bool ctx_present() { + return _ctx_present; + } + br_ssl_engine_context *_eng; // &_sc->eng, to allow for client or server contexts + std::shared_ptr _iobuf_in; + std::shared_ptr _iobuf_out; + int _iobuf_in_size; + int _iobuf_out_size; + bool _handshake_done; + uint64_t _last_error; + + bool _fingerprint_any; // accept all fingerprints + bool _insecure; // force fingerprint + const uint8_t *_fingerprint1; // fingerprint1 to be checked against + const uint8_t *_fingerprint2; // fingerprint2 to be checked against +// **** Start patch Castellucci +/* + uint8_t _recv_fingerprint[20]; // fingerprint received +*/ + uint8_t _recv_fingerprint[21]; // fingerprint received +// **** End patch Castellucci + + unsigned char *_recvapp_buf; + size_t _recvapp_len; + bool _clientConnected(); // Is the underlying socket alive? + bool _connectSSL(const char *hostName); // Do initial SSL handshake + void _freeSSL(); + int _run_until(unsigned target, bool blocking = true); + size_t _write(const uint8_t *buf, size_t size, bool pmem); + bool _wait_for_handshake(); // Sets and return the _handshake_done after connecting + + // Optional client certificate + const br_x509_certificate *_chain_P; // PROGMEM certificate + const br_ec_private_key *_sk_ec_P; // PROGMEM private key + const br_x509_trust_anchor *_ta_P; // PROGMEM server CA + size_t _ta_size; + unsigned _allowed_usages; + unsigned _cert_issuer_key_type; + + // record the maximum use of ThunkStack for monitoring + size_t _max_thunkstack_use; + + // domain name (string) that will be used with SNI when the address provided is already resolved + String _domain; + + // ALPN + const char ** _alpn_names; + size_t _alpn_num; + +}; + +#define ERR_OOM -1000 +#define ERR_CANT_RESOLVE_IP -1001 +#define ERR_TCP_CONNECT -1002 +// #define ERR_MISSING_EC_KEY -1003 // deprecated, AWS IoT is not called if the private key is not present +#define ERR_MISSING_CA -1004 + +// For reference, BearSSL error codes: +// #define BR_ERR_OK 0 +// #define BR_ERR_BAD_PARAM 1 +// #define BR_ERR_BAD_STATE 2 +// #define BR_ERR_UNSUPPORTED_VERSION 3 +// #define BR_ERR_BAD_VERSION 4 +// #define BR_ERR_BAD_LENGTH 5 +// #define BR_ERR_TOO_LARGE 6 +// #define BR_ERR_BAD_MAC 7 +// #define BR_ERR_NO_RANDOM 8 +// #define BR_ERR_UNKNOWN_TYPE 9 +// #define BR_ERR_UNEXPECTED 10 +// #define BR_ERR_BAD_CCS 12 +// #define BR_ERR_BAD_ALERT 13 +// #define BR_ERR_BAD_HANDSHAKE 14 +// #define BR_ERR_OVERSIZED_ID 15 +// #define BR_ERR_BAD_CIPHER_SUITE 16 +// #define BR_ERR_BAD_COMPRESSION 17 +// #define BR_ERR_BAD_FRAGLEN 18 +// #define BR_ERR_BAD_SECRENEG 19 +// #define BR_ERR_EXTRA_EXTENSION 20 +// #define BR_ERR_BAD_SNI 21 +// #define BR_ERR_BAD_HELLO_DONE 22 +// #define BR_ERR_LIMIT_EXCEEDED 23 +// #define BR_ERR_BAD_FINISHED 24 +// #define BR_ERR_RESUME_MISMATCH 25 +// #define BR_ERR_INVALID_ALGORITHM 26 +// #define BR_ERR_BAD_SIGNATURE 27 +// #define BR_ERR_WRONG_KEY_USAGE 28 +// #define BR_ERR_NO_CLIENT_AUTH 29 +// #define BR_ERR_IO 31 +// #define BR_ERR_RECV_FATAL_ALERT 256 +// #define BR_ERR_SEND_FATAL_ALERT 512 +// #define BR_ERR_X509_OK 32 +// #define BR_ERR_X509_INVALID_VALUE 33 +// #define BR_ERR_X509_TRUNCATED 34 +// #define BR_ERR_X509_EMPTY_CHAIN 35 +// #define BR_ERR_X509_INNER_TRUNC 36 +// #define BR_ERR_X509_BAD_TAG_CLASS 37 +// #define BR_ERR_X509_BAD_TAG_VALUE 38 +// #define BR_ERR_X509_INDEFINITE_LENGTH 39 +// #define BR_ERR_X509_EXTRA_ELEMENT 40 +// #define BR_ERR_X509_UNEXPECTED 41 +// #define BR_ERR_X509_NOT_CONSTRUCTED 42 +// #define BR_ERR_X509_NOT_PRIMITIVE 43 +// #define BR_ERR_X509_PARTIAL_BYTE 44 +// #define BR_ERR_X509_BAD_BOOLEAN 45 +// #define BR_ERR_X509_OVERFLOW 46 +// #define BR_ERR_X509_BAD_DN 47 +// #define BR_ERR_X509_BAD_TIME 48 +// #define BR_ERR_X509_UNSUPPORTED 49 +// #define BR_ERR_X509_LIMIT_EXCEEDED 50 +// #define BR_ERR_X509_WRONG_KEY_TYPE 51 +// #define BR_ERR_X509_BAD_SIGNATURE 52 +// #define BR_ERR_X509_TIME_UNKNOWN 53 +// #define BR_ERR_X509_EXPIRED 54 +// #define BR_ERR_X509_DN_MISMATCH 55 +// #define BR_ERR_X509_BAD_SERVER_NAME 56 +// #define BR_ERR_X509_CRITICAL_EXTENSION 57 +// #define BR_ERR_X509_NOT_CA 58 +// #define BR_ERR_X509_FORBIDDEN_KEY_USAGE 59 +// #define BR_ERR_X509_WEAK_PUBLIC_KEY 60 +// #define BR_ERR_X509_NOT_TRUSTED 62 + +// Alert types for TLSContentType.ALERT messages +// See RFC 8466, section B.2 + +// CLOSE_NOTIFY = 0 +// UNEXPECTED_MESSAGE = 10 +// BAD_RECORD_MAC = 20 +// DECRYPTION_FAILED = 21 +// RECORD_OVERFLOW = 22 +// DECOMPRESSION_FAILURE = 30 +// HANDSHAKE_FAILURE = 40 +// NO_CERTIFICATE = 41 +// BAD_CERTIFICATE = 42 +// UNSUPPORTED_CERTIFICATE = 43 +// CERTIFICATE_REVOKED = 44 +// CERTIFICATE_EXPIRED = 45 +// CERTIFICATE_UNKNOWN = 46 +// ILLEGAL_PARAMETER = 47 +// UNKNOWN_CA = 48 +// ACCESS_DENIED = 49 +// DECODE_ERROR = 50 +// DECRYPT_ERROR = 51 +// EXPORT_RESTRICTION = 60 +// PROTOCOL_VERSION = 70 +// INSUFFICIENT_SECURITY = 71 +// INTERNAL_ERROR = 80 +// INAPPROPRIATE_FALLBACK = 86 +// USER_CANCELED = 90 +// NO_RENEGOTIATION = 100 +// MISSING_EXTENSION = 109 +// UNSUPPORTED_EXTENSION = 110 +// CERTIFICATE_UNOBTAINABLE = 111 +// UNRECOGNIZED_NAME = 112 +// BAD_CERTIFICATE_STATUS_RESPONSE = 113 +// BAD_CERTIFICATE_HASH_VALUE = 114 +// UNKNOWN_PSK_IDENTITY = 115 +// CERTIFICATE_REQUIRED = 116 +// NO_APPLICATION_PROTOCOL = 120 + +}; + +#endif // USE_TLS +#endif // wificlientlightbearssl_h From c83e415a0f14f7bbea94123b98f4bc2f0d0bb765 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 29 Sep 2024 21:03:15 +0200 Subject: [PATCH 293/367] [MQTT-TLS] Switch to Tasmota's Light-Weight BearSSL lib --- lib/lib_ssl/tls_mini/src/StackThunk_light.cpp | 3 +- .../src/WiFiClientSecureLightBearSSL.cpp | 42 +- .../src/WiFiClientSecureLightBearSSL.h | 17 +- platformio.ini | 4 + src/src/CustomBuild/Certificate_CA.h | 202 ++++++ src/src/CustomBuild/define_plugin_sets.h | 9 + src/src/ESPEasyCore/Controller.cpp | 81 ++- src/src/Globals/ESPEasy_time.cpp | 6 +- src/src/Globals/ESPEasy_time.h | 2 + src/src/Globals/MQTT.cpp | 2 +- src/src/Globals/MQTT.h | 16 +- src/src/Helpers/ESPEasy_WiFiClientSecure.cpp | 441 -------------- src/src/Helpers/ESPEasy_WiFiClientSecure.h | 125 ---- src/src/Helpers/ESPEasy_ssl_client.cpp | 575 ------------------ src/src/Helpers/ESPEasy_ssl_client.h | 51 -- src/src/WebServer/ControllerPage.cpp | 10 +- 16 files changed, 320 insertions(+), 1266 deletions(-) create mode 100644 src/src/CustomBuild/Certificate_CA.h delete mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.cpp delete mode 100644 src/src/Helpers/ESPEasy_WiFiClientSecure.h delete mode 100644 src/src/Helpers/ESPEasy_ssl_client.cpp delete mode 100644 src/src/Helpers/ESPEasy_ssl_client.h diff --git a/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp index 098752c889..daaed2d765 100644 --- a/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp +++ b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp @@ -25,9 +25,8 @@ Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling) */ -#include "tasmota_options.h" -#if defined(ESP8266) && defined(USE_TLS) +#if defined(ESP8266) && FEATURE_TLS #include #include #include "StackThunk_light.h" diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp index 540d37420f..27b2cdc24f 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp @@ -20,8 +20,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "tasmota_options.h" -#ifdef USE_TLS + +#if FEATURE_TLS // #define DEBUG_TLS // #define DEBUG_ESP_SSL @@ -33,8 +33,15 @@ #include #include "WiFiClientSecureLightBearSSL.h" // needs to be before "ESP8266WiFi.h" to avoid conflict with Arduino headers -#include "ESP8266WiFi.h" -#include "WiFiHelper.h" + +#ifdef ESP32 + #include +#endif + +#ifdef ESP8266 + #include +#endif + #include "WiFiClient.h" #include "StackThunk_light.h" #include "lwip/opt.h" @@ -68,9 +75,6 @@ void _Log_heap_size(const char *msg) { #define LOG_HEAP_SIZE(a) #endif -// get UTC time from Tasmota -extern uint32_t UtcTime(void); -extern uint32_t CfgTime(void); #ifdef ESP8266 // Stack thunk is not needed with ESP32 // Stack thunked versions of calls @@ -319,7 +323,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port, int32_t tim DEBUG_BSSL("connect(%s,%d)\n", name, port); IPAddress remote_addr; clearLastError(); - if (!WiFiHelper::hostByName(name, remote_addr)) { + if (WiFi.hostByName(name, remote_addr) != 1) { DEBUG_BSSL("connect: Name loopup failure\n"); setLastError(ERR_CANT_RESOLVE_IP); return 0; @@ -338,7 +342,7 @@ int WiFiClientSecure_light::connect(const char* name, uint16_t port) { DEBUG_BSSL("connect(%s,%d)\n", name, port); IPAddress remote_addr; clearLastError(); - if (!WiFiHelper::hostByName(name, remote_addr)) { + if (WiFi.hostByName(name, remote_addr, 1000) != 1) { DEBUG_BSSL("connect: Name loopup failure\n"); setLastError(ERR_CANT_RESOLVE_IP); return 0; @@ -1010,8 +1014,26 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) { return false; } +uint32_t WiFiClientSecure_light::UtcTime(void) const +{ + if (_UtcTime == nullptr) { + return 0u; + } + return _UtcTime(); +} + +uint32_t WiFiClientSecure_light::CfgTime(void) const +{ + if (_CfgTime == nullptr) { + return 0u; + } + return _CfgTime(); +} + + }; #include "t_bearssl_tasmota_config.h" -#endif // USE_TLS + +#endif // FEATURE_MQTT diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h index 3f0a892a5f..f62b5fdb0f 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h @@ -24,7 +24,7 @@ #ifndef wificlientlightbearssl_h #define wificlientlightbearssl_h -#ifdef USE_TLS +#if FEATURE_TLS #include #include "WiFiClient.h" #include @@ -33,9 +33,15 @@ namespace BearSSL { class WiFiClientSecure_light : public WiFiClient { public: + typedef std::function UtcTime_fcn; + typedef std::function CfgTime_fcn; + WiFiClientSecure_light(int recv, int xmit); ~WiFiClientSecure_light() override; + void setUtcTime_fcn(UtcTime_fcn fcn) { _UtcTime = fcn; } + void setCfgTime_fcn(CfgTime_fcn fcn) { _CfgTime = fcn; } + void allocateBuffers(void); #ifdef ESP32 // the method to override in ESP32 has timeout argument default #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) @@ -184,6 +190,13 @@ class WiFiClientSecure_light : public WiFiClient { const char ** _alpn_names; size_t _alpn_num; + uint32_t UtcTime(void) const; + uint32_t CfgTime(void) const; + + + UtcTime_fcn _UtcTime{nullptr}; + CfgTime_fcn _CfgTime{nullptr}; + }; #define ERR_OOM -1000 @@ -296,5 +309,5 @@ class WiFiClientSecure_light : public WiFiClient { }; -#endif // USE_TLS +#endif // FEATURE_MQTT #endif // wificlientlightbearssl_h diff --git a/platformio.ini b/platformio.ini index aa5f95c359..e7d4770853 100644 --- a/platformio.ini +++ b/platformio.ini @@ -28,6 +28,7 @@ extra_configs = platformio_esp32c2_envs.ini platformio_esp32c6_envs.ini + ;default_envs = normal_ESP32_4M default_envs = max_ESP32_16M8M_LittleFS_ETH ;default_envs = normal_ESP32c6_4M316k_LittleFS_CDC @@ -107,3 +108,6 @@ src_filter = +<*> -<.git/> -<.svn/> - - - + +#if __has_include("../../Custom_CERTS.h") +#include "../../Custom_CERTS.h" +#else + +// Root Certificates taken from: +// https://github.com/arendst/Tasmota/blob/b2b839fa83b622c3d28324ac4d6db7ab133741c6/tasmota/tasmota_support/tasmota_ca.ino#L177 + + +#if ! defined(OMIT_LETS_ENCRYPT_CERT) + +/*********************************************************************************************\ + * LetsEncrypt ISRG Root X1 certificate, RSA 4096 bits SHA 256, valid until 20350604 + * + * https://letsencrypt.org/certificates/ + * Downloaded from https://letsencrypt.org/certs/isrgrootx1.pem + * + * to convert do: `bearssl ta isrgrootx1.pem` + * then copy and paste below, chain the generic names to the same as below + * remove "static" and add "PROGMEM" +\*********************************************************************************************/ + +static const unsigned char PROGMEM LetsEncrypt_ISRG_Root_X1_DN[] = { + 0x30, 0x4F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x20, 0x49, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x20, 0x53, + 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x20, 0x52, 0x65, 0x73, 0x65, + 0x61, 0x72, 0x63, 0x68, 0x20, 0x47, 0x72, 0x6F, 0x75, 0x70, 0x31, 0x15, + 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, 0x49, 0x53, 0x52, + 0x47, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x58, 0x31 +}; + +static const unsigned char PROGMEM LetsEncrypt_ISRG_Root_X1_RSA_N[] = { + 0xAD, 0xE8, 0x24, 0x73, 0xF4, 0x14, 0x37, 0xF3, 0x9B, 0x9E, 0x2B, 0x57, + 0x28, 0x1C, 0x87, 0xBE, 0xDC, 0xB7, 0xDF, 0x38, 0x90, 0x8C, 0x6E, 0x3C, + 0xE6, 0x57, 0xA0, 0x78, 0xF7, 0x75, 0xC2, 0xA2, 0xFE, 0xF5, 0x6A, 0x6E, + 0xF6, 0x00, 0x4F, 0x28, 0xDB, 0xDE, 0x68, 0x86, 0x6C, 0x44, 0x93, 0xB6, + 0xB1, 0x63, 0xFD, 0x14, 0x12, 0x6B, 0xBF, 0x1F, 0xD2, 0xEA, 0x31, 0x9B, + 0x21, 0x7E, 0xD1, 0x33, 0x3C, 0xBA, 0x48, 0xF5, 0xDD, 0x79, 0xDF, 0xB3, + 0xB8, 0xFF, 0x12, 0xF1, 0x21, 0x9A, 0x4B, 0xC1, 0x8A, 0x86, 0x71, 0x69, + 0x4A, 0x66, 0x66, 0x6C, 0x8F, 0x7E, 0x3C, 0x70, 0xBF, 0xAD, 0x29, 0x22, + 0x06, 0xF3, 0xE4, 0xC0, 0xE6, 0x80, 0xAE, 0xE2, 0x4B, 0x8F, 0xB7, 0x99, + 0x7E, 0x94, 0x03, 0x9F, 0xD3, 0x47, 0x97, 0x7C, 0x99, 0x48, 0x23, 0x53, + 0xE8, 0x38, 0xAE, 0x4F, 0x0A, 0x6F, 0x83, 0x2E, 0xD1, 0x49, 0x57, 0x8C, + 0x80, 0x74, 0xB6, 0xDA, 0x2F, 0xD0, 0x38, 0x8D, 0x7B, 0x03, 0x70, 0x21, + 0x1B, 0x75, 0xF2, 0x30, 0x3C, 0xFA, 0x8F, 0xAE, 0xDD, 0xDA, 0x63, 0xAB, + 0xEB, 0x16, 0x4F, 0xC2, 0x8E, 0x11, 0x4B, 0x7E, 0xCF, 0x0B, 0xE8, 0xFF, + 0xB5, 0x77, 0x2E, 0xF4, 0xB2, 0x7B, 0x4A, 0xE0, 0x4C, 0x12, 0x25, 0x0C, + 0x70, 0x8D, 0x03, 0x29, 0xA0, 0xE1, 0x53, 0x24, 0xEC, 0x13, 0xD9, 0xEE, + 0x19, 0xBF, 0x10, 0xB3, 0x4A, 0x8C, 0x3F, 0x89, 0xA3, 0x61, 0x51, 0xDE, + 0xAC, 0x87, 0x07, 0x94, 0xF4, 0x63, 0x71, 0xEC, 0x2E, 0xE2, 0x6F, 0x5B, + 0x98, 0x81, 0xE1, 0x89, 0x5C, 0x34, 0x79, 0x6C, 0x76, 0xEF, 0x3B, 0x90, + 0x62, 0x79, 0xE6, 0xDB, 0xA4, 0x9A, 0x2F, 0x26, 0xC5, 0xD0, 0x10, 0xE1, + 0x0E, 0xDE, 0xD9, 0x10, 0x8E, 0x16, 0xFB, 0xB7, 0xF7, 0xA8, 0xF7, 0xC7, + 0xE5, 0x02, 0x07, 0x98, 0x8F, 0x36, 0x08, 0x95, 0xE7, 0xE2, 0x37, 0x96, + 0x0D, 0x36, 0x75, 0x9E, 0xFB, 0x0E, 0x72, 0xB1, 0x1D, 0x9B, 0xBC, 0x03, + 0xF9, 0x49, 0x05, 0xD8, 0x81, 0xDD, 0x05, 0xB4, 0x2A, 0xD6, 0x41, 0xE9, + 0xAC, 0x01, 0x76, 0x95, 0x0A, 0x0F, 0xD8, 0xDF, 0xD5, 0xBD, 0x12, 0x1F, + 0x35, 0x2F, 0x28, 0x17, 0x6C, 0xD2, 0x98, 0xC1, 0xA8, 0x09, 0x64, 0x77, + 0x6E, 0x47, 0x37, 0xBA, 0xCE, 0xAC, 0x59, 0x5E, 0x68, 0x9D, 0x7F, 0x72, + 0xD6, 0x89, 0xC5, 0x06, 0x41, 0x29, 0x3E, 0x59, 0x3E, 0xDD, 0x26, 0xF5, + 0x24, 0xC9, 0x11, 0xA7, 0x5A, 0xA3, 0x4C, 0x40, 0x1F, 0x46, 0xA1, 0x99, + 0xB5, 0xA7, 0x3A, 0x51, 0x6E, 0x86, 0x3B, 0x9E, 0x7D, 0x72, 0xA7, 0x12, + 0x05, 0x78, 0x59, 0xED, 0x3E, 0x51, 0x78, 0x15, 0x0B, 0x03, 0x8F, 0x8D, + 0xD0, 0x2F, 0x05, 0xB2, 0x3E, 0x7B, 0x4A, 0x1C, 0x4B, 0x73, 0x05, 0x12, + 0xFC, 0xC6, 0xEA, 0xE0, 0x50, 0x13, 0x7C, 0x43, 0x93, 0x74, 0xB3, 0xCA, + 0x74, 0xE7, 0x8E, 0x1F, 0x01, 0x08, 0xD0, 0x30, 0xD4, 0x5B, 0x71, 0x36, + 0xB4, 0x07, 0xBA, 0xC1, 0x30, 0x30, 0x5C, 0x48, 0xB7, 0x82, 0x3B, 0x98, + 0xA6, 0x7D, 0x60, 0x8A, 0xA2, 0xA3, 0x29, 0x82, 0xCC, 0xBA, 0xBD, 0x83, + 0x04, 0x1B, 0xA2, 0x83, 0x03, 0x41, 0xA1, 0xD6, 0x05, 0xF1, 0x1B, 0xC2, + 0xB6, 0xF0, 0xA8, 0x7C, 0x86, 0x3B, 0x46, 0xA8, 0x48, 0x2A, 0x88, 0xDC, + 0x76, 0x9A, 0x76, 0xBF, 0x1F, 0x6A, 0xA5, 0x3D, 0x19, 0x8F, 0xEB, 0x38, + 0xF3, 0x64, 0xDE, 0xC8, 0x2B, 0x0D, 0x0A, 0x28, 0xFF, 0xF7, 0xDB, 0xE2, + 0x15, 0x42, 0xD4, 0x22, 0xD0, 0x27, 0x5D, 0xE1, 0x79, 0xFE, 0x18, 0xE7, + 0x70, 0x88, 0xAD, 0x4E, 0xE6, 0xD9, 0x8B, 0x3A, 0xC6, 0xDD, 0x27, 0x51, + 0x6E, 0xFF, 0xBC, 0x64, 0xF5, 0x33, 0x43, 0x4F +}; + +static const unsigned char LetsEncrypt_ISRG_Root_X1_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +#endif + + +#if ! defined(OMIT_AWS_CERT) +/*********************************************************************************************\ + * Amazon Root CA, RSA 2048 bits SHA 256, valid until 20380117 + * + * https://www.amazontrust.com/repository/ + * Downloaded from https://www.amazontrust.com/repository/AmazonRootCA1.pem + * + * to convert do: "bearssl ta AmazonRootCA1.pem" + * then copy and paste below, chain the generic names to the same as below + * remove "static" and add "PROGMEM" +\*********************************************************************************************/ + + +const unsigned char PROGMEM AmazonRootCA1_DN[] = { + 0x30, 0x39, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, + 0x02, 0x55, 0x53, 0x31, 0x0F, 0x30, 0x0D, 0x06, 0x03, 0x55, 0x04, 0x0A, + 0x13, 0x06, 0x41, 0x6D, 0x61, 0x7A, 0x6F, 0x6E, 0x31, 0x19, 0x30, 0x17, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x10, 0x41, 0x6D, 0x61, 0x7A, 0x6F, + 0x6E, 0x20, 0x52, 0x6F, 0x6F, 0x74, 0x20, 0x43, 0x41, 0x20, 0x31 +}; + +const unsigned char PROGMEM AmazonRootCA1_RSA_N[] = { + 0xB2, 0x78, 0x80, 0x71, 0xCA, 0x78, 0xD5, 0xE3, 0x71, 0xAF, 0x47, 0x80, + 0x50, 0x74, 0x7D, 0x6E, 0xD8, 0xD7, 0x88, 0x76, 0xF4, 0x99, 0x68, 0xF7, + 0x58, 0x21, 0x60, 0xF9, 0x74, 0x84, 0x01, 0x2F, 0xAC, 0x02, 0x2D, 0x86, + 0xD3, 0xA0, 0x43, 0x7A, 0x4E, 0xB2, 0xA4, 0xD0, 0x36, 0xBA, 0x01, 0xBE, + 0x8D, 0xDB, 0x48, 0xC8, 0x07, 0x17, 0x36, 0x4C, 0xF4, 0xEE, 0x88, 0x23, + 0xC7, 0x3E, 0xEB, 0x37, 0xF5, 0xB5, 0x19, 0xF8, 0x49, 0x68, 0xB0, 0xDE, + 0xD7, 0xB9, 0x76, 0x38, 0x1D, 0x61, 0x9E, 0xA4, 0xFE, 0x82, 0x36, 0xA5, + 0xE5, 0x4A, 0x56, 0xE4, 0x45, 0xE1, 0xF9, 0xFD, 0xB4, 0x16, 0xFA, 0x74, + 0xDA, 0x9C, 0x9B, 0x35, 0x39, 0x2F, 0xFA, 0xB0, 0x20, 0x50, 0x06, 0x6C, + 0x7A, 0xD0, 0x80, 0xB2, 0xA6, 0xF9, 0xAF, 0xEC, 0x47, 0x19, 0x8F, 0x50, + 0x38, 0x07, 0xDC, 0xA2, 0x87, 0x39, 0x58, 0xF8, 0xBA, 0xD5, 0xA9, 0xF9, + 0x48, 0x67, 0x30, 0x96, 0xEE, 0x94, 0x78, 0x5E, 0x6F, 0x89, 0xA3, 0x51, + 0xC0, 0x30, 0x86, 0x66, 0xA1, 0x45, 0x66, 0xBA, 0x54, 0xEB, 0xA3, 0xC3, + 0x91, 0xF9, 0x48, 0xDC, 0xFF, 0xD1, 0xE8, 0x30, 0x2D, 0x7D, 0x2D, 0x74, + 0x70, 0x35, 0xD7, 0x88, 0x24, 0xF7, 0x9E, 0xC4, 0x59, 0x6E, 0xBB, 0x73, + 0x87, 0x17, 0xF2, 0x32, 0x46, 0x28, 0xB8, 0x43, 0xFA, 0xB7, 0x1D, 0xAA, + 0xCA, 0xB4, 0xF2, 0x9F, 0x24, 0x0E, 0x2D, 0x4B, 0xF7, 0x71, 0x5C, 0x5E, + 0x69, 0xFF, 0xEA, 0x95, 0x02, 0xCB, 0x38, 0x8A, 0xAE, 0x50, 0x38, 0x6F, + 0xDB, 0xFB, 0x2D, 0x62, 0x1B, 0xC5, 0xC7, 0x1E, 0x54, 0xE1, 0x77, 0xE0, + 0x67, 0xC8, 0x0F, 0x9C, 0x87, 0x23, 0xD6, 0x3F, 0x40, 0x20, 0x7F, 0x20, + 0x80, 0xC4, 0x80, 0x4C, 0x3E, 0x3B, 0x24, 0x26, 0x8E, 0x04, 0xAE, 0x6C, + 0x9A, 0xC8, 0xAA, 0x0D +}; + +static const unsigned char PROGMEM AmazonRootCA1_RSA_E[] = { + 0x01, 0x00, 0x01 +}; + +#endif + + +// +// ========== cumulative CA ================= +// +const br_x509_trust_anchor PROGMEM Tasmota_TA[] = { +#if ! defined(OMIT_LETS_ENCRYPT_CERT) + { + { (unsigned char *)LetsEncrypt_ISRG_Root_X1_DN, sizeof LetsEncrypt_ISRG_Root_X1_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)LetsEncrypt_ISRG_Root_X1_RSA_N, sizeof LetsEncrypt_ISRG_Root_X1_RSA_N, + (unsigned char *)LetsEncrypt_ISRG_Root_X1_RSA_E, sizeof LetsEncrypt_ISRG_Root_X1_RSA_E, + } } + } + } + +#if ! defined(OMIT_AWS_CERT) || defined(INCLUDE_LOCAL_CERT) + , +#endif +#endif + +#if ! defined(OMIT_AWS_CERT) + { + { (unsigned char *)AmazonRootCA1_DN, sizeof AmazonRootCA1_DN }, + BR_X509_TA_CA, + { + BR_KEYTYPE_RSA, + { .rsa = { + (unsigned char *)AmazonRootCA1_RSA_N, sizeof AmazonRootCA1_RSA_N, + (unsigned char *)AmazonRootCA1_RSA_E, sizeof AmazonRootCA1_RSA_E, + } } + } + } + +#if defined(INCLUDE_LOCAL_CERT) + , +#endif +#endif + + +#if defined(INCLUDE_LOCAL_CERT) +#include +#endif + +}; + +constexpr size_t Tasmota_TA_size = sizeof(Tasmota_TA) / sizeof(br_x509_trust_anchor); + + +#endif + + +#endif + +#endif \ No newline at end of file diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index de01f7af73..14f6b5e813 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -2970,6 +2970,15 @@ To create/register a plugin, you have to : #endif #endif +#if FEATURE_MQTT_TLS + #if defined(FEATURE_TLS) && !FEATURE_TLS + #undef FEATURE_TLS + #endif + #ifndef FEATURE_TLS + #define FEATURE_TLS 1 + #endif +#endif + #ifdef USES_ESPEASY_NOW #if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) || (defined(ESP8266) && defined(PLUGIN_BUILD_IR)) // Will not fit on ESP8266 along with IR plugins included diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 923add2dea..5ecba1cd5c 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -7,6 +7,8 @@ #include "../ControllerQueue/MQTT_queue_element.h" +#include "../CustomBuild/Certificate_CA.h" + #include "../DataStructs/ControllerSettingsStruct.h" #include "../DataStructs/ESPEasy_EventStruct.h" @@ -210,18 +212,24 @@ bool MQTTConnect(controllerIndex_t controller_idx) const TLS_types TLS_type = ControllerSettings->TLStype(); if ((TLS_type != TLS_types::NoTLS) && (nullptr == mqtt_tls)) { - # ifdef ESP32 - mqtt_tls = new ESPEasy_WiFiClientSecure; - # endif // ifdef ESP32 - # ifdef ESP8266 - mqtt_tls = new BearSSL::WiFiClientSecure; - # endif // ifdef ESP8266 +#ifdef ESP32 + #if MQTT_MAX_PACKET_SIZE > 2000 + mqtt_tls = new BearSSL::WiFiClientSecure_light(4096,4096); + #else + mqtt_tls = new BearSSL::WiFiClientSecure_light(2048,2048); + #endif +#else // ESP32 - ESP8266 + mqtt_tls = new BearSSL::WiFiClientSecure_light(1024,1024); +#endif mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { mqtt_tls_last_errorstr = F("MQTT : Could not create TLS client, out of memory"); addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); return false; + } else { + mqtt_tls->setUtcTime_fcn(getUnixTime); + mqtt_tls->setCfgTime_fcn(get_build_unixtime); } } @@ -257,24 +265,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); - /* - { - static int previousFree = FreeMem(); - const int freemem = FreeMem(); - - String analyse = F(" free memory: "); - analyse += freemem; - analyse += F(" largest free block: "); - analyse += getMaxFreeBlock(); - - analyse += F(" Difference: "); - analyse += previousFree - freemem; - - addLog(LOG_LEVEL_INFO, analyse); - previousFree = freemem; - } - */ + // FIXME TD-er: Must convert rootCA from file to format accepted by bearSSL if (mqtt_rootCA.isEmpty() && (mqtt_tls != nullptr)) { LoadCertificate(ControllerSettings->getCertificateFilename(), mqtt_rootCA); @@ -286,13 +278,14 @@ bool MQTTConnect(controllerIndex_t controller_idx) return false; } - # ifdef ESP32 - mqtt_tls->setCACert(mqtt_rootCA.c_str()); - # endif // ifdef ESP32 - # ifdef ESP8266 - mqtt_X509List.append(mqtt_rootCA.c_str()); - mqtt_tls->setTrustAnchors(&mqtt_X509List); - # endif // ifdef ESP8266 + + + //mqtt_X509List.append(mqtt_rootCA.c_str()); +// mqtt_tls->setTrustAnchors(&mqtt_X509List); + } + */ + if (mqtt_tls != nullptr) { + mqtt_tls->setTrustAnchor(Tasmota_TA, Tasmota_TA_size); } break; } @@ -451,15 +444,11 @@ bool MQTTConnect(controllerIndex_t controller_idx) if (mqtt_tls != nullptr) { - char buf[128] = { 0 }; - # ifdef ESP8266 - mqtt_tls_last_error = mqtt_tls->getLastSSLError(buf, 128); - # endif // ifdef ESP8266 # ifdef ESP32 - mqtt_tls_last_error = mqtt_tls->lastError(buf, 128); + mqtt_tls_last_error = mqtt_tls->getLastError(); mqtt_tls->clearLastError(); # endif // ifdef ESP32 - mqtt_tls_last_errorstr = buf; + //mqtt_tls_last_errorstr = buf; } # ifdef ESP32 @@ -489,6 +478,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) dn.trim(); } + // FIXME TD-er: Must implement fingerprint verification + /* if (mqtt_tls != nullptr) { if (!mqtt_tls->verify( fp.c_str(), @@ -499,6 +490,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) MQTTresult = false; } } + */ } } # endif // ifdef ESP32 @@ -538,6 +530,9 @@ bool MQTTConnect(controllerIndex_t controller_idx) # if FEATURE_MQTT_TLS # ifdef ESP32 + // FIXME TD-er: Must get certificate info + /* + if ((mqtt_tls != nullptr) && loglevelActiveFor(LOG_LEVEL_INFO)) { String log = F("MQTT : Peer certificate info: "); @@ -546,6 +541,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) log += mqtt_tls->getPeerCertificateInfo(); addLogMove(LOG_LEVEL_INFO, log); } + */ # endif // ifdef ESP32 # endif // if FEATURE_MQTT_TLS @@ -902,13 +898,12 @@ bool GetTLSfingerprint(String& fp) # ifdef ESP32 if (MQTTclient_connected && (mqtt_tls != nullptr)) { - uint8_t sha256_result[32] = { 0 }; - - if (mqtt_tls->getFingerprintSHA256(sha256_result)) { + const uint8_t *recv_fingerprint = mqtt_tls->getRecvPubKeyFingerprint(); + if (recv_fingerprint != nullptr) { fp.reserve(64); - for (size_t i = 0; i < 32; ++i) { - const String tmp(sha256_result[i], HEX); + for (size_t i = 0; i < 21; ++i) { + const String tmp(recv_fingerprint[i], HEX); switch (tmp.length()) { case 0: @@ -933,6 +928,9 @@ bool GetTLS_Certificate(String& cert, bool caRoot) { # ifdef ESP32 + // FIXME TD-er: Implement retrieval of certificate + /* + if (MQTTclient_connected && (mqtt_tls != nullptr)) { String subject; @@ -940,6 +938,7 @@ bool GetTLS_Certificate(String& cert, bool caRoot) return true; } } + */ # endif // ifdef ESP32 return false; } diff --git a/src/src/Globals/ESPEasy_time.cpp b/src/src/Globals/ESPEasy_time.cpp index 67b8b78e7a..2c528cbf26 100644 --- a/src/src/Globals/ESPEasy_time.cpp +++ b/src/src/Globals/ESPEasy_time.cpp @@ -1,3 +1,7 @@ #include "../Globals/ESPEasy_time.h" -ESPEasy_time node_time; \ No newline at end of file +ESPEasy_time node_time; + +uint32_t getUnixTime() { + return node_time.getUnixTime(); +} \ No newline at end of file diff --git a/src/src/Globals/ESPEasy_time.h b/src/src/Globals/ESPEasy_time.h index 74d1715ae7..57bef991de 100644 --- a/src/src/Globals/ESPEasy_time.h +++ b/src/src/Globals/ESPEasy_time.h @@ -5,6 +5,8 @@ extern ESPEasy_time node_time; +// Global function needed for SSL +uint32_t getUnixTime(); #endif // GLOBALS_ESPEASY_TIME_H \ No newline at end of file diff --git a/src/src/Globals/MQTT.cpp b/src/src/Globals/MQTT.cpp index 65e752e75f..672f135000 100644 --- a/src/src/Globals/MQTT.cpp +++ b/src/src/Globals/MQTT.cpp @@ -12,7 +12,7 @@ String mqtt_tls_last_errorstr; int32_t mqtt_tls_last_error = 0; # ifdef ESP32 -ESPEasy_WiFiClientSecure* mqtt_tls; +BearSSL::WiFiClientSecure_light* mqtt_tls; # endif // ifdef ESP32 # ifdef ESP8266 BearSSL::WiFiClientSecure* mqtt_tls; diff --git a/src/src/Globals/MQTT.h b/src/src/Globals/MQTT.h index 4422fdfe63..4ac2500f2d 100644 --- a/src/src/Globals/MQTT.h +++ b/src/src/Globals/MQTT.h @@ -11,11 +11,7 @@ # include # if FEATURE_MQTT_TLS -# ifdef ESP32 -# include "../Helpers/ESPEasy_WiFiClientSecure.h" -# else -# include -# endif + #include # endif // if FEATURE_MQTT_TLS // MQTT client @@ -23,14 +19,8 @@ extern WiFiClient mqtt; # if FEATURE_MQTT_TLS extern String mqtt_tls_last_errorstr; extern int32_t mqtt_tls_last_error; -# ifdef ESP32 -extern ESPEasy_WiFiClientSecure* mqtt_tls; -# endif // ifdef ESP32 -# ifdef ESP8266 -extern BearSSL::WiFiClientSecure* mqtt_tls; -extern BearSSL::X509List mqtt_X509List; - -# endif // ifdef ESP8266 +extern BearSSL::WiFiClientSecure_light* mqtt_tls; +//extern BearSSL::X509List mqtt_X509List; extern String mqtt_rootCA; extern String mqtt_fingerprint; diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp b/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp deleted file mode 100644 index 0388398295..0000000000 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.cpp +++ /dev/null @@ -1,441 +0,0 @@ -#include "../Helpers/ESPEasy_WiFiClientSecure.h" - -/* - ESPEasy_WiFiClientSecure.cpp - Client Secure class for ESP32 - Copyright (c) 2016 Hristo Gochkov All right reserved. - Additions Copyright (C) 2017 Evandro Luis Copercini. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library 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 - Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifdef ESP32 -#include -#include -#include - - -// FIXME TD-er: Feels wrong this needs to be included here to use mbedtls_pem_write_buffer -#include -#include - -#include - -#undef connect -#undef write -#undef read - - -ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure() -{ - _connected = false; - - sslclient = new ESPEasy_sslclient_context; - ssl_init(sslclient); - sslclient->socket = -1; - sslclient->handshake_timeout = 120000; - _use_insecure = false; - _CA_cert = NULL; - _cert = NULL; - _private_key = NULL; - _pskIdent = NULL; - _psKey = NULL; - next = NULL; - _alpn_protos = NULL; -} - - -ESPEasy_WiFiClientSecure::ESPEasy_WiFiClientSecure(int sock) -{ - _connected = false; - _timeout = 0; - - sslclient = new ESPEasy_sslclient_context; - ssl_init(sslclient); - sslclient->socket = sock; - sslclient->handshake_timeout = 120000; - - if (sock >= 0) { - _connected = true; - } - - _CA_cert = NULL; - _cert = NULL; - _private_key = NULL; - _pskIdent = NULL; - _psKey = NULL; - next = NULL; - _alpn_protos = NULL; -} - -ESPEasy_WiFiClientSecure::~ESPEasy_WiFiClientSecure() -{ - stop(); - delete sslclient; -} - -ESPEasy_WiFiClientSecure &ESPEasy_WiFiClientSecure::operator=(const ESPEasy_WiFiClientSecure &other) -{ - stop(); - sslclient->socket = other.sslclient->socket; - _connected = other._connected; - return *this; -} - -void ESPEasy_WiFiClientSecure::stop() -{ - if (sslclient->socket >= 0) { - close(sslclient->socket); - sslclient->socket = -1; - _connected = false; - _peek = -1; - } - stop_ssl_socket(sslclient, _CA_cert, _cert, _private_key); -} - -int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port) -{ - if (_pskIdent && _psKey) - return connect(ip, port, _pskIdent, _psKey); - return connect(ip, port, _CA_cert, _cert, _private_key); -} - -int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, int32_t timeout){ - _timeout = timeout; - return connect(ip, port); -} - -int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port) -{ - if (_pskIdent && _psKey) - return connect(host, port, _pskIdent, _psKey); - return connect(host, port, _CA_cert, _cert, _private_key); -} - -int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, int32_t timeout){ - _timeout = timeout; - return connect(host, port); -} - -int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) -{ - return connect(ip.toString().c_str(), port, CA_cert, cert, private_key); -} - -int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *CA_cert, const char *cert, const char *private_key) -{ - if(_timeout > 0){ - sslclient->handshake_timeout = _timeout; - } - int ret = start_ssl_client(sslclient, host, port, _timeout, CA_cert, cert, private_key, NULL, NULL, _use_insecure, _alpn_protos); - _lastError = ret; - if (ret < 0) { - log_e("start_ssl_client: %d", ret); - stop(); - return 0; - } - _connected = true; - return 1; -} - -int ESPEasy_WiFiClientSecure::connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey) { - return connect(ip.toString().c_str(), port, pskIdent, psKey); -} - -int ESPEasy_WiFiClientSecure::connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey) { - log_v("start_ssl_client with PSK"); - if(_timeout > 0){ - sslclient->handshake_timeout = _timeout; - } - int ret = start_ssl_client(sslclient, host, port, _timeout, NULL, NULL, NULL, pskIdent, psKey, _use_insecure, _alpn_protos); - _lastError = ret; - if (ret < 0) { - log_e("start_ssl_client: %d", ret); - stop(); - return 0; - } - _connected = true; - return 1; -} - -int ESPEasy_WiFiClientSecure::peek(){ - if(_peek >= 0){ - return _peek; - } - _peek = timedRead(); - return _peek; -} - -size_t ESPEasy_WiFiClientSecure::write(uint8_t data) -{ - return write(&data, 1); -} - -int ESPEasy_WiFiClientSecure::read() -{ - uint8_t data = -1; - int res = read(&data, 1); - if (res < 0) { - return res; - } - return data; -} - -size_t ESPEasy_WiFiClientSecure::write(const uint8_t *buf, size_t size) -{ - if (!_connected) { - return 0; - } - int res = send_ssl_data(sslclient, buf, size); - if (res < 0) { - stop(); - res = 0; - } - return res; -} - -int ESPEasy_WiFiClientSecure::read(uint8_t *buf, size_t size) -{ - int peeked = 0; - int avail = available(); - if ((!buf && size) || avail <= 0) { - return -1; - } - if(!size){ - return 0; - } - if(_peek >= 0){ - buf[0] = _peek; - _peek = -1; - size--; - avail--; - if(!size || !avail){ - return 1; - } - buf++; - peeked = 1; - } - - int res = get_ssl_receive(sslclient, buf, size); - if (res < 0) { - stop(); - return peeked?peeked:res; - } - return res + peeked; -} - -int ESPEasy_WiFiClientSecure::available() -{ - int peeked = (_peek >= 0); - if (!_connected) { - return peeked; - } - int res = data_to_read(sslclient); - if (res < 0) { - stop(); - return peeked?peeked:res; - } - return res+peeked; -} - -uint8_t ESPEasy_WiFiClientSecure::connected() -{ - uint8_t dummy = 0; - read(&dummy, 0); - - return _connected; -} - -void ESPEasy_WiFiClientSecure::setInsecure() -{ - _CA_cert = NULL; - _cert = NULL; - _private_key = NULL; - _pskIdent = NULL; - _psKey = NULL; - _use_insecure = true; -} - -void ESPEasy_WiFiClientSecure::setCACert (const char *rootCA) -{ - _CA_cert = rootCA; -} - -void ESPEasy_WiFiClientSecure::setCertificate (const char *client_ca) -{ - _cert = client_ca; -} - -void ESPEasy_WiFiClientSecure::setPrivateKey (const char *private_key) -{ - _private_key = private_key; -} - -void ESPEasy_WiFiClientSecure::setPreSharedKey(const char *pskIdent, const char *psKey) { - _pskIdent = pskIdent; - _psKey = psKey; -} - -bool ESPEasy_WiFiClientSecure::verify(const char* fp, const char* domain_name) -{ - if (!sslclient) - return false; - - return verify_ssl_fingerprint(sslclient, fp, domain_name); -} - -char *ESPEasy_WiFiClientSecure::_streamLoad(Stream& stream, size_t size) { - char *dest = (char*)malloc(size+1); - if (!dest) { - return nullptr; - } - if (size != stream.readBytes(dest, size)) { - free(dest); - dest = nullptr; - return nullptr; - } - dest[size] = '\0'; - return dest; -} - -bool ESPEasy_WiFiClientSecure::loadCACert(Stream& stream, size_t size) { - char *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { - setCACert(dest); - ret = true; - } - return ret; -} - -bool ESPEasy_WiFiClientSecure::loadCertificate(Stream& stream, size_t size) { - char *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { - setCertificate(dest); - ret = true; - } - return ret; -} - -bool ESPEasy_WiFiClientSecure::loadPrivateKey(Stream& stream, size_t size) { - char *dest = _streamLoad(stream, size); - bool ret = false; - if (dest) { - setPrivateKey(dest); - ret = true; - } - return ret; -} - -int ESPEasy_WiFiClientSecure::lastError(char *buf, const size_t size) -{ - if (!_lastError) { - return 0; - } - mbedtls_strerror(_lastError, buf, size); - return _lastError; -} - -void ESPEasy_WiFiClientSecure::setHandshakeTimeout(unsigned long handshake_timeout) -{ - sslclient->handshake_timeout = handshake_timeout * 1000; -} - -void ESPEasy_WiFiClientSecure::setAlpnProtocols(const char **alpn_protos) -{ - _alpn_protos = alpn_protos; -} - -String ESPEasy_WiFiClientSecure::getPeerCertificateInfo(const mbedtls_x509_crt* peer) -{ - if (peer == nullptr) { - peer = getPeerCertificate(); - } - String res; - if (peer != nullptr) { - char buf[1024] = {0}; - int l = mbedtls_x509_crt_info (buf, sizeof(buf), "", peer); - if (l > 0) { - if (res.reserve(l)) { - for (int i = 0; i < l; ++i) { - res += buf[i]; - } - } - } - } - return res; -} - -int ESPEasy_WiFiClientSecure::getPeerCertificate(String& pem, String& subject, bool caRoot) -{ - const mbedtls_x509_crt *chain; - - chain = getPeerCertificate(); - - int error {0}; - bool done = false; - while (chain != nullptr && error == 0 && !done) { - if (!caRoot || (chain->ca_istrue && chain->next == nullptr)) { - done = true; - error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); - } - chain = chain->next; - } - return error; -} - -int ESPEasy_WiFiClientSecure::cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject) -{ - const String pem_begin_crt = F("-----BEGIN CERTIFICATE-----\n"); - const String pem_end_crt = F("-----END CERTIFICATE-----"); - pem.clear(); - subject.clear(); - - const mbedtls_asn1_named_data* common_name = &crt->subject; - while (common_name != nullptr) { - // While iterating through DN objects, check for CN object - if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) - { - - subject.reserve(common_name->val.len); - const unsigned char* p = common_name->val.p; - for (auto i = 0; i < common_name->val.len; ++i, ++p) { - subject += static_cast(*p); - } - } - - // Fetch next DN object - common_name = common_name->next; - } - - size_t written{}; - - const size_t buffer_size = - pem_begin_crt.length() + - pem_end_crt.length() + - 2* crt->raw.len; - - std::vector pem_buf; - pem_buf.resize(buffer_size, 0u); - int ret = mbedtls_pem_write_buffer( - pem_begin_crt.c_str(), pem_end_crt.c_str(), - crt->raw.p, crt->raw.len, - &pem_buf[0], buffer_size, &written); - if (ret == 0) { - pem.reserve(written); - for (auto i = 0; i < written; ++i) { - pem += static_cast(pem_buf[i]); - } - } - return ret; -} -#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_WiFiClientSecure.h b/src/src/Helpers/ESPEasy_WiFiClientSecure.h deleted file mode 100644 index aa0bcc625e..0000000000 --- a/src/src/Helpers/ESPEasy_WiFiClientSecure.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - ESPEasy_WiFiClientSecure.h - Base class that provides Client SSL to ESP32 - Copyright (c) 2011 Adrian McEwen. All right reserved. - Additions Copyright (C) 2017 Evandro Luis Copercini. - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - This library 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 - Lesser General Public License for more details. - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -/* - Almost literal copy of https://github.com/brokentoaster/arduino-esp32/blob/master/libraries/WiFiClientSecure/src/WiFiClientSecure.h - Changed into "ESPEasy" version to incorporate some fixes - for memory leaks not yet present in the "older" core versions. -*/ - -#ifdef ESP32 - -#ifndef ESPEASY_WiFiClientSecure_h -#define ESPEASY_WiFiClientSecure_h -#include -#include -#include -#include "../Helpers/ESPEasy_ssl_client.h" - -class ESPEasy_WiFiClientSecure : public WiFiClient -{ -protected: - ESPEasy_sslclient_context *sslclient; - - int _lastError = 0; - int _peek = -1; - int _timeout = 0; - bool _use_insecure; - const char *_CA_cert; - const char *_cert; - const char *_private_key; - const char *_pskIdent; // identity for PSK cipher suites - const char *_psKey; // key in hex for PSK cipher suites - const char **_alpn_protos; - -public: - ESPEasy_WiFiClientSecure *next; - ESPEasy_WiFiClientSecure(); - ESPEasy_WiFiClientSecure(int socket); - ~ESPEasy_WiFiClientSecure(); - int connect(IPAddress ip, uint16_t port); - int connect(IPAddress ip, uint16_t port, int32_t timeout); - int connect(const char *host, uint16_t port); - int connect(const char *host, uint16_t port, int32_t timeout); - int connect(IPAddress ip, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); - int connect(const char *host, uint16_t port, const char *rootCABuff, const char *cli_cert, const char *cli_key); - int connect(IPAddress ip, uint16_t port, const char *pskIdent, const char *psKey); - int connect(const char *host, uint16_t port, const char *pskIdent, const char *psKey); - int peek(); - size_t write(uint8_t data); - size_t write(const uint8_t *buf, size_t size); - int available(); - int read(); - int read(uint8_t *buf, size_t size); - void flush() {} - void stop(); - uint8_t connected(); - int lastError(char *buf, const size_t size); - void clearLastError() { _lastError = 0; } - void setInsecure(); // Don't validate the chain, just accept whatever is given. VERY INSECURE! - void setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex - void setCACert(const char *rootCA); - void setCertificate(const char *client_ca); - void setPrivateKey (const char *private_key); - bool loadCACert(Stream& stream, size_t size); - bool loadCertificate(Stream& stream, size_t size); - bool loadPrivateKey(Stream& stream, size_t size); - bool verify(const char* fingerprint, const char* domain_name); - void setHandshakeTimeout(unsigned long handshake_timeout); - void setAlpnProtocols(const char **alpn_protos); - String getPeerCertificateInfo(const mbedtls_x509_crt* crt = nullptr); - const mbedtls_x509_crt* getPeerCertificate() { return mbedtls_ssl_get_peer_cert(&sslclient->ssl_ctx); }; - bool getFingerprintSHA256(uint8_t sha256_result[32]) { return get_peer_fingerprint(sslclient, sha256_result); }; - - int getPeerCertificate(String& pem, String& subject, bool caRoot); - - // See: https://stackoverflow.com/a/63730321/8708166 - static int cert_to_pem(const mbedtls_x509_crt *crt, String& pem, String& subject); - - operator bool() - { - return connected(); - } - ESPEasy_WiFiClientSecure &operator=(const ESPEasy_WiFiClientSecure &other); - bool operator==(const bool value) - { - return bool() == value; - } - bool operator!=(const bool value) - { - return bool() != value; - } - bool operator==(const ESPEasy_WiFiClientSecure &); - bool operator!=(const ESPEasy_WiFiClientSecure &rhs) - { - return !this->operator==(rhs); - }; - - int socket() - { - return sslclient->socket = -1; - } - -private: - char *_streamLoad(Stream& stream, size_t size); - - //friend class WiFiServer; - using Print::write; -}; - -#endif -#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.cpp b/src/src/Helpers/ESPEasy_ssl_client.cpp deleted file mode 100644 index 7e20438a52..0000000000 --- a/src/src/Helpers/ESPEasy_ssl_client.cpp +++ /dev/null @@ -1,575 +0,0 @@ -#include "../Helpers/ESPEasy_ssl_client.h" - -/* Provide SSL/TLS functions to ESP32 with Arduino IDE -* -* Adapted from the ssl_client1 example of mbedtls. -* -* Original Copyright (C) 2006-2015, ARM Limited, All Rights Reserved, Apache 2.0 License. -* Additions Copyright (C) 2017 Evandro Luis Copercini, Apache 2.0 License. -*/ - -#ifdef ESP32 - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if !defined(MBEDTLS_KEY_EXCHANGE__SOME__PSK_ENABLED) && !defined(MBEDTLS_KEY_EXCHANGE_SOME_PSK_ENABLED) -# warning "Please configure IDF framework to include mbedTLS -> Enable pre-shared-key ciphersuites and activate at least one cipher" -#else - - -const char *ESPEasy_pers = "esp32-tls"; - -static int _handle_error(int err, const char * function, int line) -{ - if(err == -30848){ - return err; - } -#ifdef MBEDTLS_ERROR_C - char error_buf[100]; - mbedtls_strerror(err, error_buf, 100); - log_e("[%s():%d]: (%d) %s", function, line, err, error_buf); -#else - log_e("[%s():%d]: code %d", function, line, err); -#endif - return err; -} - -#define handle_error(e) _handle_error(e, __FUNCTION__, __LINE__) - - -ESPEasy_sslclient_context::ESPEasy_sslclient_context() -{ - mbedtls_ssl_init(&ssl_ctx); - mbedtls_ssl_config_init(&ssl_conf); - mbedtls_ctr_drbg_init(&drbg_ctx); - - mbedtls_entropy_init(&entropy_ctx); - mbedtls_x509_crt_init(&ca_cert); - mbedtls_x509_crt_init(&client_cert); - mbedtls_pk_init(&client_key); -} - - -ESPEasy_sslclient_context::~ESPEasy_sslclient_context() -{ - free_ca_cert(); - free_client_cert(); - free_client_key(); -} - - -void ESPEasy_sslclient_context::free_ca_cert() -{ - mbedtls_x509_crt_free(&ca_cert); -} - -void ESPEasy_sslclient_context::free_client_cert() -{ - mbedtls_x509_crt_free(&client_cert); -} - -void ESPEasy_sslclient_context::free_client_key() -{ - mbedtls_pk_free(&client_key); -} - - -void ssl_init(ESPEasy_sslclient_context *ssl_client) -{ - mbedtls_ssl_free(&ssl_client->ssl_ctx); - mbedtls_ssl_config_free(&ssl_client->ssl_conf); - mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); - - mbedtls_ssl_init(&ssl_client->ssl_ctx); - mbedtls_ssl_config_init(&ssl_client->ssl_conf); - mbedtls_ctr_drbg_init(&ssl_client->drbg_ctx); -} - - -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos) -{ - char buf[512]; - int ret, flags; - int enable = 1; - log_v("Free internal heap before TLS %u", ESP.getFreeHeap()); - - if (rootCABuff == NULL && pskIdent == NULL && psKey == NULL && !insecure) { - return -1; - } - - log_v("Starting socket"); - ssl_client->socket = -1; - - ssl_client->socket = lwip_socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (ssl_client->socket < 0) { - log_e("ERROR opening socket"); - return ssl_client->socket; - } - - IPAddress srv((uint32_t)0); - if(!WiFiGenericClass::hostByName(host, srv)){ - return -1; - } - - fcntl( ssl_client->socket, F_SETFL, fcntl( ssl_client->socket, F_GETFL, 0 ) | O_NONBLOCK ); - struct sockaddr_in serv_addr; - memset(&serv_addr, 0, sizeof(serv_addr)); - serv_addr.sin_family = AF_INET; - serv_addr.sin_addr.s_addr = srv; - serv_addr.sin_port = htons(port); - - if(timeout <= 0){ - timeout = 30000; // Milli seconds. - } - - fd_set fdset; - struct timeval tv; - FD_ZERO(&fdset); - FD_SET(ssl_client->socket, &fdset); - tv.tv_sec = timeout / 1000; - tv.tv_usec = (timeout % 1000) * 1000; - - int res = lwip_connect(ssl_client->socket, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); - if (res < 0 && errno != EINPROGRESS) { - log_e("connect on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); - close(ssl_client->socket); - return -1; - } - - res = select(ssl_client->socket + 1, nullptr, &fdset, nullptr, timeout<0 ? nullptr : &tv); - if (res < 0) { - log_e("select on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); - close(ssl_client->socket); - return -1; - } else if (res == 0) { - log_i("select returned due to timeout %d ms for fd %d", timeout, ssl_client->socket); - close(ssl_client->socket); - return -1; - } else { - int sockerr; - socklen_t len = (socklen_t)sizeof(int); - res = getsockopt(ssl_client->socket, SOL_SOCKET, SO_ERROR, &sockerr, &len); - - if (res < 0) { - log_e("getsockopt on fd %d, errno: %d, \"%s\"", ssl_client->socket, errno, strerror(errno)); - close(ssl_client->socket); - return -1; - } - - if (sockerr != 0) { - log_e("socket error on fd %d, errno: %d, \"%s\"", ssl_client->socket, sockerr, strerror(sockerr)); - close(ssl_client->socket); - return -1; - } - } - - -#define ROE(x,msg) { if (((x)<0)) { log_e("LWIP Socket config of " msg " failed."); return -1; }} - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)),"SO_RCVTIMEO"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)),"SO_SNDTIMEO"); - - ROE(lwip_setsockopt(ssl_client->socket, IPPROTO_TCP, TCP_NODELAY, &enable, sizeof(enable)),"TCP_NODELAY"); - ROE(lwip_setsockopt(ssl_client->socket, SOL_SOCKET, SO_KEEPALIVE, &enable, sizeof(enable)),"SO_KEEPALIVE"); - - - - log_v("Seeding the random number generator"); - mbedtls_entropy_free(&ssl_client->entropy_ctx); - mbedtls_entropy_init(&ssl_client->entropy_ctx); - - ret = mbedtls_ctr_drbg_seed(&ssl_client->drbg_ctx, mbedtls_entropy_func, - &ssl_client->entropy_ctx, (const unsigned char *) ESPEasy_pers, strlen(ESPEasy_pers)); - if (ret < 0) { - return handle_error(ret); - } - - log_v("Setting up the SSL/TLS structure..."); - - if ((ret = mbedtls_ssl_config_defaults(&ssl_client->ssl_conf, - MBEDTLS_SSL_IS_CLIENT, - MBEDTLS_SSL_TRANSPORT_STREAM, - MBEDTLS_SSL_PRESET_DEFAULT)) != 0) { - return handle_error(ret); - } - - if (alpn_protos != NULL) { - log_v("Setting ALPN protocols"); - if ((ret = mbedtls_ssl_conf_alpn_protocols(&ssl_client->ssl_conf, alpn_protos) ) != 0) { - return handle_error(ret); - } - } - - // MBEDTLS_SSL_VERIFY_REQUIRED if a CA certificate is defined on Arduino IDE and - // MBEDTLS_SSL_VERIFY_NONE if not. - - if (insecure) { - mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_NONE); - log_i("WARNING: Skipping SSL Verification. INSECURE!"); - } else if (rootCABuff != NULL) { - log_v("Loading CA cert"); - mbedtls_x509_crt_init(&ssl_client->ca_cert); - mbedtls_ssl_conf_authmode(&ssl_client->ssl_conf, MBEDTLS_SSL_VERIFY_REQUIRED); - ret = mbedtls_x509_crt_parse(&ssl_client->ca_cert, (const unsigned char *)rootCABuff, strlen(rootCABuff) + 1); - mbedtls_ssl_conf_ca_chain(&ssl_client->ssl_conf, &ssl_client->ca_cert, NULL); - //mbedtls_ssl_conf_verify(&ssl_client->ssl_ctx, my_verify, NULL ); - if (ret < 0) { - // free the ca_cert in the case parse failed, otherwise, the old ca_cert still in the heap memory, that lead to "out of memory" crash. - ssl_client->free_ca_cert(); - return handle_error(ret); - } - } else if (pskIdent != NULL && psKey != NULL) { - log_v("Setting up PSK"); - // convert PSK from hex to binary - if ((strlen(psKey) & 1) != 0 || strlen(psKey) > 2*MBEDTLS_PSK_MAX_LEN) { - log_e("pre-shared key not valid hex or too long"); - return -1; - } - unsigned char psk[MBEDTLS_PSK_MAX_LEN]; - size_t psk_len = strlen(psKey)/2; - for (int j=0; j= '0' && c <= '9') c -= '0'; - else if (c >= 'A' && c <= 'F') c -= 'A' - 10; - else if (c >= 'a' && c <= 'f') c -= 'a' - 10; - else return -1; - psk[j/2] = c<<4; - c = psKey[j+1]; - if (c >= '0' && c <= '9') c -= '0'; - else if (c >= 'A' && c <= 'F') c -= 'A' - 10; - else if (c >= 'a' && c <= 'f') c -= 'a' - 10; - else return -1; - psk[j/2] |= c; - } - // set mbedtls config - ret = mbedtls_ssl_conf_psk(&ssl_client->ssl_conf, psk, psk_len, - (const unsigned char *)pskIdent, strlen(pskIdent)); - if (ret != 0) { - log_e("mbedtls_ssl_conf_psk returned %d", ret); - return handle_error(ret); - } - } else { - return -1; - } - - if (!insecure && cli_cert != NULL && cli_key != NULL) { - mbedtls_x509_crt_init(&ssl_client->client_cert); - mbedtls_pk_init(&ssl_client->client_key); - - log_v("Loading CRT cert"); - - ret = mbedtls_x509_crt_parse(&ssl_client->client_cert, (const unsigned char *)cli_cert, strlen(cli_cert) + 1); - if (ret < 0) { - // free the client_cert in the case parse failed, otherwise, the old client_cert still in the heap memory, that lead to "out of memory" crash. - ssl_client->free_client_cert(); - return handle_error(ret); - } - - log_v("Loading private key"); - ret = mbedtls_pk_parse_key(&ssl_client->client_key, (const unsigned char *)cli_key, strlen(cli_key) + 1, NULL, 0); - - if (ret != 0) { - return handle_error(ret); - } - - mbedtls_ssl_conf_own_cert(&ssl_client->ssl_conf, &ssl_client->client_cert, &ssl_client->client_key); - } - - log_v("Setting hostname for TLS session..."); - - // Hostname set here should match CN in server certificate - if((ret = mbedtls_ssl_set_hostname(&ssl_client->ssl_ctx, host)) != 0){ - return handle_error(ret); - } - - mbedtls_ssl_conf_rng(&ssl_client->ssl_conf, mbedtls_ctr_drbg_random, &ssl_client->drbg_ctx); - - if ((ret = mbedtls_ssl_setup(&ssl_client->ssl_ctx, &ssl_client->ssl_conf)) != 0) { - return handle_error(ret); - } - - mbedtls_ssl_set_bio(&ssl_client->ssl_ctx, &ssl_client->socket, mbedtls_net_send, mbedtls_net_recv, NULL ); - - log_v("Performing the SSL/TLS handshake..."); - unsigned long handshake_start_time=millis(); - while ((ret = mbedtls_ssl_handshake(&ssl_client->ssl_ctx)) != 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE) { - // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ - ssl_client->free_client_key(); - ssl_client->free_ca_cert(); - ssl_client->free_client_cert(); - // ++++++++++ END ++++++++++ - return handle_error(ret); - } - if ((millis()-handshake_start_time) > ssl_client->handshake_timeout) { - // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ - ssl_client->free_client_key(); - ssl_client->free_ca_cert(); - ssl_client->free_client_cert(); - // ++++++++++ END ++++++++++ - return -1; - } - vTaskDelay(2);//2 ticks - } - - - if (cli_cert != NULL && cli_key != NULL) { - log_d("Protocol is %s Ciphersuite is %s", mbedtls_ssl_get_version(&ssl_client->ssl_ctx), mbedtls_ssl_get_ciphersuite(&ssl_client->ssl_ctx)); - if ((ret = mbedtls_ssl_get_record_expansion(&ssl_client->ssl_ctx)) >= 0) { - log_d("Record expansion is %d", ret); - } else { - log_w("Record expansion is unknown (compression)"); - } - } - - log_v("Verifying peer X.509 certificate..."); - - if ((flags = mbedtls_ssl_get_verify_result(&ssl_client->ssl_ctx)) != 0) { - memset(buf, 0, sizeof(buf)); - mbedtls_x509_crt_verify_info(buf, sizeof(buf), " ! ", flags); - log_e("Failed to verify peer certificate! verification info: %s", buf); - stop_ssl_socket(ssl_client, rootCABuff, cli_cert, cli_key); //It's not safe continue. - // ++++++++++ ADDED TO FIX MEMORY LEAK ON FAILED CONNECTION ++++++++++ - ssl_client->free_client_key(); - ssl_client->free_ca_cert(); - ssl_client->free_client_cert(); - // ++++++++++ END ++++++++++ - - return handle_error(ret); - } else { - log_v("Certificate verified."); - } - - ssl_client->free_ca_cert(); - ssl_client->free_client_cert(); - ssl_client->free_client_key(); - - log_v("Free internal heap after TLS %u", ESP.getFreeHeap()); - - return ssl_client->socket; -} - - -void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key) -{ - log_v("Cleaning SSL connection."); - - if (ssl_client->socket >= 0) { - close(ssl_client->socket); - ssl_client->socket = -1; - } - - mbedtls_ssl_free(&ssl_client->ssl_ctx); - mbedtls_ssl_config_free(&ssl_client->ssl_conf); - mbedtls_ctr_drbg_free(&ssl_client->drbg_ctx); - mbedtls_entropy_free(&ssl_client->entropy_ctx); - ssl_client->free_ca_cert(); - ssl_client->free_client_cert(); - ssl_client->free_client_key(); -} - - -int data_to_read(ESPEasy_sslclient_context *ssl_client) -{ - int ret, res; - ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, NULL, 0); - //log_e("RET: %i",ret); //for low level debug - res = mbedtls_ssl_get_bytes_avail(&ssl_client->ssl_ctx); - //log_e("RES: %i",res); //for low level debug - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { - return handle_error(ret); - } - - return res; -} - -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len) -{ - log_v("Writing HTTP request with %d bytes...", len); //for low level debug - int ret = -1; - - while ((ret = mbedtls_ssl_write(&ssl_client->ssl_ctx, data, len)) <= 0) { - if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE && ret < 0) { - log_v("Handling error %d", ret); //for low level debug - return handle_error(ret); - } - //wait for space to become available - vTaskDelay(2); - } - - return ret; -} - -int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length) -{ - //log_d( "Reading HTTP response..."); //for low level debug - int ret = -1; - - ret = mbedtls_ssl_read(&ssl_client->ssl_ctx, data, length); - - //log_v( "%d bytes read", ret); //for low level debug - return ret; -} - -static bool parseHexNibble(char pb, uint8_t* res) -{ - if (pb >= '0' && pb <= '9') { - *res = (uint8_t) (pb - '0'); return true; - } else if (pb >= 'a' && pb <= 'f') { - *res = (uint8_t) (pb - 'a' + 10); return true; - } else if (pb >= 'A' && pb <= 'F') { - *res = (uint8_t) (pb - 'A' + 10); return true; - } - return false; -} - -// Compare a name from certificate and domain name, return true if they match -static bool matchName(const std::string& name, const std::string& domainName) -{ - size_t wildcardPos = name.find('*'); - if (wildcardPos == std::string::npos) { - // Not a wildcard, expect an exact match - return name == domainName; - } - - size_t firstDotPos = name.find('.'); - if (wildcardPos > firstDotPos) { - // Wildcard is not part of leftmost component of domain name - // Do not attempt to match (rfc6125 6.4.3.1) - return false; - } - if (wildcardPos != 0 || firstDotPos != 1) { - // Matching of wildcards such as baz*.example.com and b*z.example.com - // is optional. Maybe implement this in the future? - return false; - } - size_t domainNameFirstDotPos = domainName.find('.'); - if (domainNameFirstDotPos == std::string::npos) { - return false; - } - return domainName.substr(domainNameFirstDotPos) == name.substr(firstDotPos); -} - -// Verifies certificate provided by the peer to match specified SHA256 fingerprint -bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name) -{ - // Convert hex string to byte array - uint8_t fingerprint_local[32]; - int len = strlen(fp); - int pos = 0; - for (size_t i = 0; i < sizeof(fingerprint_local); ++i) { - while (pos < len && ((fp[pos] == ' ') || (fp[pos] == ':'))) { - ++pos; - } - if (pos > len - 2) { - log_d("pos:%d len:%d fingerprint too short", pos, len); - return false; - } - uint8_t high, low; - if (!parseHexNibble(fp[pos], &high) || !parseHexNibble(fp[pos+1], &low)) { - log_d("pos:%d len:%d invalid hex sequence: %c%c", pos, len, fp[pos], fp[pos+1]); - return false; - } - pos += 2; - fingerprint_local[i] = low | (high << 4); - } - - // Calculate certificate's SHA256 fingerprint - uint8_t fingerprint_remote[32]; - if(!get_peer_fingerprint(ssl_client, fingerprint_remote)) - return false; - - // Check if fingerprints match - if (memcmp(fingerprint_local, fingerprint_remote, 32)) - { - log_d("fingerprint doesn't match"); - return false; - } - - // Additionally check if certificate has domain name if provided - if (domain_name) - return verify_ssl_dn(ssl_client, domain_name); - else - return true; -} - -bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]) -{ - if (!ssl_client) { - log_d("Invalid ssl_client pointer"); - return false; - }; - - const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); - if (!crt) { - log_d("Failed to get peer cert."); - return false; - }; - - mbedtls_sha256_context sha256_ctx; - mbedtls_sha256_init(&sha256_ctx); - mbedtls_sha256_starts(&sha256_ctx, false); - mbedtls_sha256_update(&sha256_ctx, crt->raw.p, crt->raw.len); - mbedtls_sha256_finish(&sha256_ctx, sha256); - - return true; -} - -// Checks if peer certificate has specified domain in CN or SANs -bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name) -{ - log_d("domain name: '%s'", (domain_name)?domain_name:"(null)"); - std::string domain_name_str(domain_name); - std::transform(domain_name_str.begin(), domain_name_str.end(), domain_name_str.begin(), ::tolower); - - // Get certificate provided by the peer - const mbedtls_x509_crt* crt = mbedtls_ssl_get_peer_cert(&ssl_client->ssl_ctx); - - // Check for domain name in SANs - const mbedtls_x509_sequence* san = &crt->subject_alt_names; - while (san != nullptr) - { - std::string san_str((const char*)san->buf.p, san->buf.len); - std::transform(san_str.begin(), san_str.end(), san_str.begin(), ::tolower); - - if (matchName(san_str, domain_name_str)) - return true; - - log_d("SAN '%s': no match", san_str.c_str()); - - // Fetch next SAN - san = san->next; - } - - // Check for domain name in CN - const mbedtls_asn1_named_data* common_name = &crt->subject; - while (common_name != nullptr) - { - // While iterating through DN objects, check for CN object - if (!MBEDTLS_OID_CMP(MBEDTLS_OID_AT_CN, &common_name->oid)) - { - std::string common_name_str((const char*)common_name->val.p, common_name->val.len); - - if (matchName(common_name_str, domain_name_str)) - return true; - - log_d("CN '%s': not match", common_name_str.c_str()); - } - - // Fetch next DN object - common_name = common_name->next; - } - - return false; -} -#endif -#endif \ No newline at end of file diff --git a/src/src/Helpers/ESPEasy_ssl_client.h b/src/src/Helpers/ESPEasy_ssl_client.h deleted file mode 100644 index 7f6eebf034..0000000000 --- a/src/src/Helpers/ESPEasy_ssl_client.h +++ /dev/null @@ -1,51 +0,0 @@ -/* Provide SSL/TLS functions to ESP32 with Arduino IDE - * by Evandro Copercini - 2017 - Apache 2.0 License - */ -#ifdef ESP32 -#ifndef ESPEASY_ARD_SSL_H -#define ESPEASY_ARD_SSL_H -#include -#include -#include -#include -#include -#include -#include - -typedef struct ESPEasy_sslclient_context { - - ESPEasy_sslclient_context(); - - ~ESPEasy_sslclient_context(); - - void free_ca_cert(); - void free_client_cert(); - void free_client_key(); - - int socket = -1; - mbedtls_ssl_context ssl_ctx; - mbedtls_ssl_config ssl_conf; - - mbedtls_ctr_drbg_context drbg_ctx; - mbedtls_entropy_context entropy_ctx; - - mbedtls_x509_crt ca_cert; - mbedtls_x509_crt client_cert; - mbedtls_pk_context client_key; - - unsigned long handshake_timeout = 120000; -} ESPEasy_sslclient_context; - - -void ssl_init(ESPEasy_sslclient_context *ssl_client); -int start_ssl_client(ESPEasy_sslclient_context *ssl_client, const char *host, uint32_t port, int timeout, const char *rootCABuff, const char *cli_cert, const char *cli_key, const char *pskIdent, const char *psKey, bool insecure, const char **alpn_protos); -void stop_ssl_socket(ESPEasy_sslclient_context *ssl_client, const char *rootCABuff, const char *cli_cert, const char *cli_key); -int data_to_read(ESPEasy_sslclient_context *ssl_client); -int send_ssl_data(ESPEasy_sslclient_context *ssl_client, const uint8_t *data, size_t len); -int get_ssl_receive(ESPEasy_sslclient_context *ssl_client, uint8_t *data, int length); -bool verify_ssl_fingerprint(ESPEasy_sslclient_context *ssl_client, const char* fp, const char* domain_name); -bool verify_ssl_dn(ESPEasy_sslclient_context *ssl_client, const char* domain_name); -bool get_peer_fingerprint(ESPEasy_sslclient_context *ssl_client, uint8_t sha256[32]); - -#endif -#endif \ No newline at end of file diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 096a5cb069..50e769c793 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -490,6 +490,9 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex } else { LoadControllerSettings(controllerindex, *ControllerSettings); + // FIXME TD-er: Implement retrieval of certificate + /* + addFormSubHeader(F("Peer Certificate")); { @@ -522,10 +525,8 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex int error {0}; while (chain != nullptr && error == 0) { - /* - const bool mustShow = !chain->ca_istrue || chain->next == nullptr; - if (mustShow) { - */ +// const bool mustShow = !chain->ca_istrue || chain->next == nullptr; +// if (mustShow) { String pem, subject; error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); { @@ -567,6 +568,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex chain = chain->next; } } + */ } } #endif From 0843f4dc0132f7d8fb153758b0f949878c844fdf Mon Sep 17 00:00:00 2001 From: Ton Huisman Date: Sun, 29 Sep 2024 21:05:50 +0200 Subject: [PATCH 294/367] [P054] Add documentation, small UI improvement --- docs/source/Plugin/P054.rst | 60 ++++++++++++++++-- .../Plugin/P054_DeviceConfiguration.png | Bin 0 -> 25860 bytes docs/source/Plugin/P054_SerialPortOptions.png | Bin 0 -> 43457 bytes docs/source/Plugin/P054_commands.repl | 39 ++++++++++++ .../Plugin/_plugin_substitutions_p05x.repl | 2 +- src/_P054_DMX512.ino | 11 +++- 6 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 docs/source/Plugin/P054_DeviceConfiguration.png create mode 100644 docs/source/Plugin/P054_SerialPortOptions.png create mode 100644 docs/source/Plugin/P054_commands.repl diff --git a/docs/source/Plugin/P054.rst b/docs/source/Plugin/P054.rst index ad5de6617e..e1cf1f9061 100644 --- a/docs/source/Plugin/P054.rst +++ b/docs/source/Plugin/P054.rst @@ -1,4 +1,4 @@ -.. include:: ../Plugin/_plugin_substitutions_p05x.repl +.. include:: ../Plugin/_plugin_substitutions_p05x.repl .. _P054_page: |P054_typename| @@ -21,15 +21,59 @@ Maintainer: |P054_maintainer| Used libraries: |P054_usedlibraries| -Supported hardware ------------------- - |P054_usedby| -.. Commands available -.. ^^^^^^^^^^^^^^^^^^ +Description +----------- + +For controlling DMX512 equipment, this plugin can be used for sending commands to a DMX bus. As an ESP only has a 5V compatible serial interface, usually a RS485 or DMX converter is needed to connect to the DMX bus. + +This plugin can only send out commands to the DMX bus, no receiving or recording features are available. + +Configuration +------------- + +.. image:: P054_DeviceConfiguration.png + +* **Name** In the Name field a unique name should be entered. + +* **Enabled** When unchecked the plugin is not enabled. + +Actuator +^^^^^^^^ + +See: :ref:`SerialHelper_page` + +.. warning:: The selected serial port **must** use on-ESP GPIO pins, as the DMX protocol has some specific coding that is not supported by regular serial chips, and is implemented by directly interacting with the ESP TX pin. + +Example selection list (taken from an ESP32-C6 configuration): + +.. image:: P054_SerialPortOptions.png -.. .. include:: P054_commands.repl +* *HW Serial0*: First serial port, usually also in use for the serial log, so should best be avoided. + +* *HW Serial1*: Secondary serial port. Selected as the default, and required to be used on ESP8266. + +* *HW Serial2*: (Not available on an ESP32-C6, but is available on some other ESP32 models) Can be used. + +* *SW Serial*: Can be tried, and used if working as intended, but should better be avoided because of the high serial speed (250000 baud). + +* *USB HWCDC*: Can not be used. + +* *USB CDC*: Can not be used. + +* *I2C Serial*: Can not be used. + +Device Settings +^^^^^^^^^^^^^^^ + +* **Channels**: The number of DMX channels to be used. Determines the size of a memory buffer. Keep as small as possible, especially on ESP8266, where available memory can be limited. + + +Commands available +^^^^^^^^^^^^^^^^^^ + +.. include:: P054_commands.repl .. Events .. ~~~~~~ @@ -42,6 +86,8 @@ Change log .. versionchanged:: 2.0 ... + |added| 2024-09-29: Add selection for Serial port and GPIO on ESP32. + |added| Major overhaul for 2.0 release. diff --git a/docs/source/Plugin/P054_DeviceConfiguration.png b/docs/source/Plugin/P054_DeviceConfiguration.png new file mode 100644 index 0000000000000000000000000000000000000000..05154a12d5ca128045efc8be586acab0d32ac5f8 GIT binary patch literal 25860 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[,[,=]]`` + + | ``channel``: Channel number from 1 .. **Channels** set in the configuration. + + | ``value``: Numeric value between 0 and 255 (inclusive) to set for the channel. + "," + | Fills the send-buffer given value(s) for the channel and sends that to the connected devices. + + | If no channel is used with a value, it is set for the next channel. The channel is initially set to 1. + + | Multiple values, optionally with a channel prefixed, can be provided and will be processed until no more data is found. + + | Also, other subcommands (not including ``dmx``) can be used, f.e. setting up some channel data and adding a ``,log`` at the end to list the current buffer content to the log output. + " diff --git a/docs/source/Plugin/_plugin_substitutions_p05x.repl b/docs/source/Plugin/_plugin_substitutions_p05x.repl index 5baecc6bbc..22dee3255d 100644 --- a/docs/source/Plugin/_plugin_substitutions_p05x.repl +++ b/docs/source/Plugin/_plugin_substitutions_p05x.repl @@ -53,7 +53,7 @@ .. |P054_name| replace:: :cyan:`DMX512 TX` .. |P054_type| replace:: :cyan:`Communication` .. |P054_typename| replace:: :cyan:`Communication - DMX512 TX` -.. |P054_porttype| replace:: `.` +.. |P054_porttype| replace:: `Serial` .. |P054_status| replace:: :yellow:`COLLECTION` .. |P054_github| replace:: P054_DMX512.ino .. _P054_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P054_DMX512.ino diff --git a/src/_P054_DMX512.ino b/src/_P054_DMX512.ino index 93671097f0..09d4483c96 100644 --- a/src/_P054_DMX512.ino +++ b/src/_P054_DMX512.ino @@ -133,9 +133,17 @@ boolean Plugin_054(uint8_t function, struct EventStruct *event, String& string) } break; } + case PLUGIN_WEBFORM_SHOW_SERIAL_PARAMS: { - addFormNote(F("An on-chip ESP Serial port must be selected!")); + addFormNote(F("An on-chip ESP Serial port" + #if USES_USBCDC + " (not USB CDC)" + #endif // if USES_USBCDC + #if USES_HWCDC + " (not USB HWCDC)" + #endif // if USES_HWCDC + " must be selected!")); break; } @@ -217,6 +225,7 @@ boolean Plugin_054(uint8_t function, struct EventStruct *event, String& string) { if (Plugin_054_DMXBuffer) { delete[] Plugin_054_DMXBuffer; + Plugin_054_DMXBuffer = nullptr; } delete Plugin_054_Serial; Plugin_054_Serial = nullptr; From 059479f87f183426b7913c4c66c4fd52e6217171 Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 29 Sep 2024 22:59:57 +0200 Subject: [PATCH 295/367] [MQTT TLS] Fix build hidden write(uint8_t) --- lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp | 4 ++++ lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp index 27b2cdc24f..137a3f2061 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp @@ -427,6 +427,10 @@ void WiFiClientSecure_light::setInsecure() { _insecure = true; } +size_t WiFiClientSecure_light::write(uint8_t data) { + return _write(&data, 1, false); +} + size_t WiFiClientSecure_light::write(const uint8_t *buf, size_t size) { return _write(buf, size, false); } diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h index f62b5fdb0f..ae192cfd0b 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h @@ -26,12 +26,12 @@ #define wificlientlightbearssl_h #if FEATURE_TLS #include -#include "WiFiClient.h" +#include "NetworkClient.h" #include namespace BearSSL { -class WiFiClientSecure_light : public WiFiClient { +class WiFiClientSecure_light : public NetworkClient { public: typedef std::function UtcTime_fcn; typedef std::function CfgTime_fcn; @@ -53,6 +53,7 @@ class WiFiClientSecure_light : public WiFiClient { #endif uint8_t connected() override; + size_t write(uint8_t data) override; size_t write(const uint8_t *buf, size_t size) override; #ifdef ESP8266 size_t write_P(PGM_P buf, size_t size) override; From e28505badafce030fdf795dc0a3e2f250e7c689c Mon Sep 17 00:00:00 2001 From: TD-er Date: Sun, 29 Sep 2024 23:27:04 +0200 Subject: [PATCH 296/367] [MQTT-TLS] Enable TLS for MQTT on all ESP32-xx Custom builds --- tools/pio/pre_custom_esp32_IR.py | 1 + tools/pio/pre_custom_esp32c2.py | 1 + tools/pio/pre_custom_esp32c6.py | 1 + 3 files changed, 3 insertions(+) diff --git a/tools/pio/pre_custom_esp32_IR.py b/tools/pio/pre_custom_esp32_IR.py index 4daf68ca07..eae182217b 100644 --- a/tools/pio/pre_custom_esp32_IR.py +++ b/tools/pio/pre_custom_esp32_IR.py @@ -60,6 +60,7 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp32c2.py b/tools/pio/pre_custom_esp32c2.py index b69afaf130..16af0041c4 100644 --- a/tools/pio/pre_custom_esp32c2.py +++ b/tools/pio/pre_custom_esp32c2.py @@ -59,6 +59,7 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp32c6.py b/tools/pio/pre_custom_esp32c6.py index 5a1f9b9eb6..f951e5ec48 100644 --- a/tools/pio/pre_custom_esp32c6.py +++ b/tools/pio/pre_custom_esp32c6.py @@ -61,6 +61,7 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", From 9bca93b3580e2e5d8651d66e8341bdd2cd5ccf39 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 30 Sep 2024 09:14:55 +0200 Subject: [PATCH 297/367] [MQTT TLS] Disable PSK option --- .../DataStructs/ControllerSettingsStruct.cpp | 6 +++-- src/src/DataTypes/TLS_types.cpp | 2 +- src/src/DataTypes/TLS_types.h | 2 +- src/src/ESPEasyCore/Controller.cpp | 2 ++ src/src/Helpers/_CPlugin_Helper_webform.cpp | 25 +++++++------------ 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/src/src/DataStructs/ControllerSettingsStruct.cpp b/src/src/DataStructs/ControllerSettingsStruct.cpp index 0a379cba52..c9f2ce6c0e 100644 --- a/src/src/DataStructs/ControllerSettingsStruct.cpp +++ b/src/src/DataStructs/ControllerSettingsStruct.cpp @@ -335,14 +335,16 @@ String ControllerSettingsStruct::getCertificateFilename(TLS_types tls_type) cons case TLS_types::NoTLS: case TLS_types::TLS_insecure: return EMPTY_STRING; +/* case TLS_types::TLS_PSK: certFile += F(".psk"); break; - /* +*/ +/* case TLS_types::TLS_CA_CLI_CERT: certFile += F(".caclicert"); break; - */ +*/ case TLS_types::TLS_CA_CERT: certFile += F(".cacert"); break; diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp index fb8f43e839..5e4f65d2ac 100644 --- a/src/src/DataTypes/TLS_types.cpp +++ b/src/src/DataTypes/TLS_types.cpp @@ -5,7 +5,7 @@ const __FlashStringHelper* toString(TLS_types tls_type) { switch (tls_type) { case TLS_types::NoTLS: break; - case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); +// case TLS_types::TLS_PSK: return F("TLS PreSharedKey"); case TLS_types::TLS_CA_CERT: return F("TLS CA Cert"); case TLS_types::TLS_insecure: return F("TLS No Checks (insecure)"); case TLS_types::TLS_FINGERPRINT: return F("TLS Certficate Fingerprint"); diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index 01fa2731bc..e21bc2cb52 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -10,7 +10,7 @@ // Value is stored, so do not change assigned integer values. enum class TLS_types { NoTLS = 0, // Do not use encryption - TLS_PSK = 1, // Pre-Shared-Key +// TLS_PSK = 1, // Pre-Shared-Key TLS_CA_CERT = 2, // Validate server certificate against known CA //TLS_CA_CLI_CERT = 3, // TLS_CA_CERT + supply client certificate for authentication TLS_FINGERPRINT = 4, // Use certificate fingerprint diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 5ecba1cd5c..28b88c67f6 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -256,12 +256,14 @@ bool MQTTConnect(controllerIndex_t controller_idx) MQTTclient.setClient(mqtt); break; } + /* case TLS_types::TLS_PSK: { // if (mqtt_tls != nullptr) // mqtt_tls->setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex break; } + */ case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index e8e224221f..250f71121a 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -187,43 +187,37 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin #if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - #if FEATURE_MQTT_TLS const int choice = static_cast(ControllerSettings.TLStype()); - #define NR_MQTT_TLS_TYPES 4 - const __FlashStringHelper * options[NR_MQTT_TLS_TYPES] = { - toString(TLS_types::NoTLS), + const __FlashStringHelper * options[] = { + toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), - toString(TLS_types::TLS_CA_CERT), - toString(TLS_types::TLS_FINGERPRINT), - toString(TLS_types::TLS_insecure) + toString(TLS_types::TLS_CA_CERT), + toString(TLS_types::TLS_FINGERPRINT), + toString(TLS_types::TLS_insecure) }; - const int indices[NR_MQTT_TLS_TYPES] = { + const int indices[] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), static_cast(TLS_types::TLS_CA_CERT), static_cast(TLS_types::TLS_FINGERPRINT), static_cast(TLS_types::TLS_insecure) }; - addFormSelector(displayName, internalName, NR_MQTT_TLS_TYPES, options, indices, choice, true); - #undef NR_MQTT_TLS_TYPES - addCertificateFileNote(ControllerSettings, F("Certificate or PSK must be stored on the filesystem in"), ControllerSettings.TLStype()); - #endif + constexpr int nrOptions = NR_ELEMENTS(indices); + addFormSelector(displayName, internalName, nrOptions, options, indices, choice, true); + addCertificateFileNote(ControllerSettings, F("Certificate or FingerPrint must be stored on the filesystem in"), ControllerSettings.TLStype()); break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { - #if FEATURE_MQTT_TLS const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT)); addFormCheckBox(displayName, internalName, false, saveDisabled); addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); - #endif break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - #if FEATURE_MQTT_TLS /* const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; @@ -234,7 +228,6 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin addUnit(F("File Exists")); } addCertificateFileNote(ControllerSettings, F("Store CA Certificate in"), TLS_types::TLS_CA_CERT); - #endif break; } #endif From 695e30e9b0e5c38ec029943bc91b02c7c8f912b3 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 30 Sep 2024 09:15:21 +0200 Subject: [PATCH 298/367] [MQTT TLS] Uncrustify controller source files --- src/src/ESPEasyCore/Controller.cpp | 130 +++++----- src/src/Helpers/_CPlugin_Helper_webform.cpp | 185 +++++++------- src/src/WebServer/ControllerPage.cpp | 258 +++++++++++--------- 3 files changed, 308 insertions(+), 265 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 28b88c67f6..04ac2b61a9 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -212,15 +212,15 @@ bool MQTTConnect(controllerIndex_t controller_idx) const TLS_types TLS_type = ControllerSettings->TLStype(); if ((TLS_type != TLS_types::NoTLS) && (nullptr == mqtt_tls)) { -#ifdef ESP32 - #if MQTT_MAX_PACKET_SIZE > 2000 - mqtt_tls = new BearSSL::WiFiClientSecure_light(4096,4096); - #else - mqtt_tls = new BearSSL::WiFiClientSecure_light(2048,2048); - #endif -#else // ESP32 - ESP8266 - mqtt_tls = new BearSSL::WiFiClientSecure_light(1024,1024); -#endif +# ifdef ESP32 + # if MQTT_MAX_PACKET_SIZE > 2000 + mqtt_tls = new BearSSL::WiFiClientSecure_light(4096, 4096); + # else // if MQTT_MAX_PACKET_SIZE > 2000 + mqtt_tls = new BearSSL::WiFiClientSecure_light(2048, 2048); + # endif // if MQTT_MAX_PACKET_SIZE > 2000 +# else // ESP32 - ESP8266 + mqtt_tls = new BearSSL::WiFiClientSecure_light(1024, 1024); +# endif // ifdef ESP32 mqtt_rootCA.clear(); if (mqtt_tls == nullptr) { @@ -256,36 +256,38 @@ bool MQTTConnect(controllerIndex_t controller_idx) MQTTclient.setClient(mqtt); break; } + /* - case TLS_types::TLS_PSK: - { - // if (mqtt_tls != nullptr) - // mqtt_tls->setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex - break; - } - */ + case TLS_types::TLS_PSK: + { + // if (mqtt_tls != nullptr) + // mqtt_tls->setPreSharedKey(const char *pskIdent, const char *psKey); // psKey in Hex + break; + } + */ case TLS_types::TLS_CA_CERT: { mqtt_rootCA.clear(); + /* - // FIXME TD-er: Must convert rootCA from file to format accepted by bearSSL + // FIXME TD-er: Must convert rootCA from file to format accepted by bearSSL - if (mqtt_rootCA.isEmpty() && (mqtt_tls != nullptr)) { - LoadCertificate(ControllerSettings->getCertificateFilename(), mqtt_rootCA); + if (mqtt_rootCA.isEmpty() && (mqtt_tls != nullptr)) { + LoadCertificate(ControllerSettings->getCertificateFilename(), mqtt_rootCA); - if (mqtt_rootCA.isEmpty()) { + if (mqtt_rootCA.isEmpty()) { // Fingerprint must be of some minimal length to continue. mqtt_tls_last_errorstr = F("MQTT : No TLS root CA"); addLog(LOG_LEVEL_ERROR, mqtt_tls_last_errorstr); return false; - } + } - //mqtt_X509List.append(mqtt_rootCA.c_str()); -// mqtt_tls->setTrustAnchors(&mqtt_X509List); - } - */ + //mqtt_X509List.append(mqtt_rootCA.c_str()); + // mqtt_tls->setTrustAnchors(&mqtt_X509List); + } + */ if (mqtt_tls != nullptr) { mqtt_tls->setTrustAnchor(Tasmota_TA, Tasmota_TA_size); } @@ -388,7 +390,7 @@ bool MQTTConnect(controllerIndex_t controller_idx) MQTTclient.setClient(mqtt); MQTTclient.setKeepAlive(10); MQTTclient.setSocketTimeout(timeout); -#endif +# endif // if FEATURE_MQTT_TLS if (ControllerSettings->UseDNS) { MQTTclient.setServer(ControllerSettings->getHost().c_str(), ControllerSettings->Port); @@ -442,17 +444,17 @@ bool MQTTConnect(controllerIndex_t controller_idx) Settings.Protocol[controller_idx], statisticsTimerStart); - # if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS if (mqtt_tls != nullptr) { - # ifdef ESP32 + # ifdef ESP32 mqtt_tls_last_error = mqtt_tls->getLastError(); mqtt_tls->clearLastError(); - # endif // ifdef ESP32 - //mqtt_tls_last_errorstr = buf; + # endif // ifdef ESP32 + // mqtt_tls_last_errorstr = buf; } - # ifdef ESP32 + # ifdef ESP32 // FIXME TD-er: There seems to be no verify function in BearSSL used on ESP8266 if (TLS_type == TLS_types::TLS_FINGERPRINT) @@ -481,26 +483,27 @@ bool MQTTConnect(controllerIndex_t controller_idx) } // FIXME TD-er: Must implement fingerprint verification + /* - if (mqtt_tls != nullptr) { - if (!mqtt_tls->verify( + if (mqtt_tls != nullptr) { + if (!mqtt_tls->verify( fp.c_str(), dn.isEmpty() ? nullptr : dn.c_str())) - { + { mqtt_tls_last_errorstr += F("TLS Fingerprint does not match"); addLog(LOG_LEVEL_INFO, mqtt_fingerprint); MQTTresult = false; - } - } - */ + } + } + */ } } - # endif // ifdef ESP32 + # endif // ifdef ESP32 - # endif // if FEATURE_MQTT_TLS + # endif // if FEATURE_MQTT_TLS if (!MQTTresult) { - # if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS if ((mqtt_tls_last_error != 0) && loglevelActiveFor(LOG_LEVEL_ERROR)) { String log = F("MQTT : TLS error code: "); @@ -509,15 +512,15 @@ bool MQTTConnect(controllerIndex_t controller_idx) log += mqtt_tls_last_errorstr; addLog(LOG_LEVEL_ERROR, log); } - # endif // if FEATURE_MQTT_TLS + # endif // if FEATURE_MQTT_TLS MQTTclient.disconnect(); - # if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS if (mqtt_tls != nullptr) { mqtt_tls->stop(); } - # endif // if FEATURE_MQTT_TLS + # endif // if FEATURE_MQTT_TLS updateMQTTclient_connected(); @@ -529,23 +532,24 @@ bool MQTTConnect(controllerIndex_t controller_idx) addLogMove(LOG_LEVEL_INFO, concat(F("MQTT : Connected to broker with client ID: "), clientid)); } - # if FEATURE_MQTT_TLS - # ifdef ESP32 + # if FEATURE_MQTT_TLS + # ifdef ESP32 // FIXME TD-er: Must get certificate info + /* - if ((mqtt_tls != nullptr) && loglevelActiveFor(LOG_LEVEL_INFO)) - { - String log = F("MQTT : Peer certificate info: "); - log += ControllerSettings->getHost(); - log += ' '; - log += mqtt_tls->getPeerCertificateInfo(); - addLogMove(LOG_LEVEL_INFO, log); - } - */ - # endif // ifdef ESP32 - # endif // if FEATURE_MQTT_TLS + if ((mqtt_tls != nullptr) && loglevelActiveFor(LOG_LEVEL_INFO)) + { + String log = F("MQTT : Peer certificate info: "); + log += ControllerSettings->getHost(); + log += ' '; + log += mqtt_tls->getPeerCertificateInfo(); + addLogMove(LOG_LEVEL_INFO, log); + } + */ + # endif // ifdef ESP32 + # endif // if FEATURE_MQTT_TLS String subscribeTo = ControllerSettings->Subscribe; @@ -901,6 +905,7 @@ bool GetTLSfingerprint(String& fp) if (MQTTclient_connected && (mqtt_tls != nullptr)) { const uint8_t *recv_fingerprint = mqtt_tls->getRecvPubKeyFingerprint(); + if (recv_fingerprint != nullptr) { fp.reserve(64); @@ -931,16 +936,17 @@ bool GetTLS_Certificate(String& cert, bool caRoot) # ifdef ESP32 // FIXME TD-er: Implement retrieval of certificate + /* - if (MQTTclient_connected && (mqtt_tls != nullptr)) { - String subject; + if (MQTTclient_connected && (mqtt_tls != nullptr)) { + String subject; - if (mqtt_tls->getPeerCertificate(cert, subject, caRoot) == 0) { + if (mqtt_tls->getPeerCertificate(cert, subject, caRoot) == 0) { return true; - } - } - */ + } + } + */ # endif // ifdef ESP32 return false; } diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 250f71121a..4192552134 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -16,61 +16,61 @@ /*********************************************************************************************\ * Functions to load and store controller settings on the web page. \*********************************************************************************************/ -const __FlashStringHelper * toString(ControllerSettingsStruct::VarType parameterIdx, bool displayName) +const __FlashStringHelper* toString(ControllerSettingsStruct::VarType parameterIdx, bool displayName) { switch (parameterIdx) { - case ControllerSettingsStruct::CONTROLLER_USE_DNS: return F("Locate Controller"); - case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); - case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); - case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); + case ControllerSettingsStruct::CONTROLLER_USE_DNS: return F("Locate Controller"); + case ControllerSettingsStruct::CONTROLLER_HOSTNAME: return F("Controller Hostname"); + case ControllerSettingsStruct::CONTROLLER_IP: return F("Controller IP"); + case ControllerSettingsStruct::CONTROLLER_PORT: return F("Controller Port"); #if FEATURE_MQTT_TLS - case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: return F("Use TLS"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: return F("Store Fingerprint"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: return F("Store Certificate"); case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: return F("Store CA Certificate"); -#endif - - case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); - case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); - - case ControllerSettingsStruct::CONTROLLER_MIN_SEND_INTERVAL: return F("Minimum Send Interval"); - case ControllerSettingsStruct::CONTROLLER_MAX_QUEUE_DEPTH: return F("Max Queue Depth"); - case ControllerSettingsStruct::CONTROLLER_MAX_RETRIES: return F("Max Retries"); - case ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION: return F("Full Queue Action"); - case ControllerSettingsStruct::CONTROLLER_ALLOW_EXPIRE: return F("Allow Expire"); - case ControllerSettingsStruct::CONTROLLER_DEDUPLICATE: return F("De-duplicate"); - case ControllerSettingsStruct::CONTROLLER_USE_LOCAL_SYSTEM_TIME: return F("Use Local System Time"); - - case ControllerSettingsStruct::CONTROLLER_CHECK_REPLY: return F("Check Reply"); - - case ControllerSettingsStruct::CONTROLLER_CLIENT_ID: return F("Controller Client ID"); +#endif // if FEATURE_MQTT_TLS + + case ControllerSettingsStruct::CONTROLLER_USER: return F("Controller User"); + case ControllerSettingsStruct::CONTROLLER_PASS: return F("Controller Password"); + + case ControllerSettingsStruct::CONTROLLER_MIN_SEND_INTERVAL: return F("Minimum Send Interval"); + case ControllerSettingsStruct::CONTROLLER_MAX_QUEUE_DEPTH: return F("Max Queue Depth"); + case ControllerSettingsStruct::CONTROLLER_MAX_RETRIES: return F("Max Retries"); + case ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION: return F("Full Queue Action"); + case ControllerSettingsStruct::CONTROLLER_ALLOW_EXPIRE: return F("Allow Expire"); + case ControllerSettingsStruct::CONTROLLER_DEDUPLICATE: return F("De-duplicate"); + case ControllerSettingsStruct::CONTROLLER_USE_LOCAL_SYSTEM_TIME: return F("Use Local System Time"); + + case ControllerSettingsStruct::CONTROLLER_CHECK_REPLY: return F("Check Reply"); + + case ControllerSettingsStruct::CONTROLLER_CLIENT_ID: return F("Controller Client ID"); #if FEATURE_MQTT - case ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT: return F("Unique Client ID on Reconnect"); - case ControllerSettingsStruct::CONTROLLER_RETAINFLAG: return F("Publish Retain Flag"); -#endif - case ControllerSettingsStruct::CONTROLLER_SUBSCRIBE: return F("Controller Subscribe"); - case ControllerSettingsStruct::CONTROLLER_PUBLISH: return F("Controller Publish"); + case ControllerSettingsStruct::CONTROLLER_UNIQUE_CLIENT_ID_RECONNECT: return F("Unique Client ID on Reconnect"); + case ControllerSettingsStruct::CONTROLLER_RETAINFLAG: return F("Publish Retain Flag"); +#endif // if FEATURE_MQTT + case ControllerSettingsStruct::CONTROLLER_SUBSCRIBE: return F("Controller Subscribe"); + case ControllerSettingsStruct::CONTROLLER_PUBLISH: return F("Controller Publish"); #if FEATURE_MQTT - case ControllerSettingsStruct::CONTROLLER_LWT_TOPIC: return F("Controller LWT Topic"); - case ControllerSettingsStruct::CONTROLLER_LWT_CONNECT_MESSAGE: return F("LWT Connect Message"); - case ControllerSettingsStruct::CONTROLLER_LWT_DISCONNECT_MESSAGE: return F("LWT Disconnect Message"); - case ControllerSettingsStruct::CONTROLLER_SEND_LWT: return F("Send LWT to broker"); - case ControllerSettingsStruct::CONTROLLER_WILL_RETAIN: return F("Will Retain"); - case ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION: return F("Clean Session"); -#endif - case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: return F("Use Extended Credentials"); - case ControllerSettingsStruct::CONTROLLER_SEND_BINARY: return F("Send Binary"); - case ControllerSettingsStruct::CONTROLLER_TIMEOUT: return F("Client Timeout"); - case ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR: return F("Sample Set Initiator"); + case ControllerSettingsStruct::CONTROLLER_LWT_TOPIC: return F("Controller LWT Topic"); + case ControllerSettingsStruct::CONTROLLER_LWT_CONNECT_MESSAGE: return F("LWT Connect Message"); + case ControllerSettingsStruct::CONTROLLER_LWT_DISCONNECT_MESSAGE: return F("LWT Disconnect Message"); + case ControllerSettingsStruct::CONTROLLER_SEND_LWT: return F("Send LWT to broker"); + case ControllerSettingsStruct::CONTROLLER_WILL_RETAIN: return F("Will Retain"); + case ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION: return F("Clean Session"); +#endif // if FEATURE_MQTT + case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: return F("Use Extended Credentials"); + case ControllerSettingsStruct::CONTROLLER_SEND_BINARY: return F("Send Binary"); + case ControllerSettingsStruct::CONTROLLER_TIMEOUT: return F("Client Timeout"); + case ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR: return F("Sample Set Initiator"); case ControllerSettingsStruct::CONTROLLER_ENABLED: - if (displayName) { return F("Enabled"); } - else { return F("controllerenabled"); } - + if (displayName) { return F("Enabled"); } + else { return F("controllerenabled"); } + default: - return F("Undefined"); + return F("Undefined"); } } @@ -124,21 +124,23 @@ void addControllerEnabledForm(controllerIndex_t controllerindex) { ControllerSettingsStruct::VarType varType = ControllerSettingsStruct::CONTROLLER_ENABLED; - bool isAlternativeDisplayName = false; - const String displayName = getControllerParameterDisplayName(ProtocolIndex, varType, isAlternativeDisplayName); - const String internalName = getControllerParameterInternalName(ProtocolIndex, varType); + bool isAlternativeDisplayName = false; + const String displayName = getControllerParameterDisplayName(ProtocolIndex, varType, isAlternativeDisplayName); + const String internalName = getControllerParameterInternalName(ProtocolIndex, varType); addFormCheckBox(displayName, internalName, Settings.ControllerEnabled[controllerindex]); } #if FEATURE_MQTT_TLS void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, const String& description, TLS_types tls_type) { const String certFile = ControllerSettings.getCertificateFilename(tls_type); + if (!certFile.isEmpty()) { String note = description; note += F(" "); note += certFile; note += F(" "); + if (fileExists(certFile)) { note += F("(File exists)"); } else { @@ -147,22 +149,26 @@ void addCertificateFileNote(const ControllerSettingsStruct& ControllerSettings, addFormNote(note); } } -#endif -void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { - protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); +#endif // if FEATURE_MQTT_TLS + +void addControllerParameterForm(const ControllerSettingsStruct & ControllerSettings, + controllerIndex_t controllerindex, + ControllerSettingsStruct::VarType varType) { + protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); + if (!validProtocolIndex(ProtocolIndex)) { return; } - bool isAlternativeDisplayName = false; - const String displayName = getControllerParameterDisplayName(ProtocolIndex, varType, isAlternativeDisplayName); - const String internalName = getControllerParameterInternalName(ProtocolIndex, varType); + bool isAlternativeDisplayName = false; + const String displayName = getControllerParameterDisplayName(ProtocolIndex, varType, isAlternativeDisplayName); + const String internalName = getControllerParameterInternalName(ProtocolIndex, varType); switch (varType) { case ControllerSettingsStruct::CONTROLLER_USE_DNS: { - const __FlashStringHelper * options[2] = { + const __FlashStringHelper *options[2] = { F("Use IP address"), F("Use Hostname") }; @@ -187,24 +193,28 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin #if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - const int choice = static_cast(ControllerSettings.TLStype()); - const __FlashStringHelper * options[] = { + const int choice = static_cast(ControllerSettings.TLStype()); + const __FlashStringHelper *options[] = { toString(TLS_types::NoTLS), -// toString(TLS_types::TLS_PSK), + + // toString(TLS_types::TLS_PSK), toString(TLS_types::TLS_CA_CERT), toString(TLS_types::TLS_FINGERPRINT), toString(TLS_types::TLS_insecure) }; const int indices[] = { static_cast(TLS_types::NoTLS), -// static_cast(TLS_types::TLS_PSK), + + // static_cast(TLS_types::TLS_PSK), static_cast(TLS_types::TLS_CA_CERT), static_cast(TLS_types::TLS_FINGERPRINT), static_cast(TLS_types::TLS_insecure) }; constexpr int nrOptions = NR_ELEMENTS(indices); addFormSelector(displayName, internalName, nrOptions, options, indices, choice, true); - addCertificateFileNote(ControllerSettings, F("Certificate or FingerPrint must be stored on the filesystem in"), ControllerSettings.TLStype()); + addCertificateFileNote(ControllerSettings, + F("Certificate or FingerPrint must be stored on the filesystem in"), + ControllerSettings.TLStype()); break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: @@ -214,23 +224,24 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); break; } - case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: - // fall through + case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: + // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { /* - const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? + const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; - */ + */ const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT)); addFormCheckBox(displayName, internalName, false, saveDisabled); + if (saveDisabled) { addUnit(F("File Exists")); } addCertificateFileNote(ControllerSettings, F("Store CA Certificate in"), TLS_types::TLS_CA_CERT); break; } -#endif +#endif // if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_USER: { const size_t fieldMaxLength = @@ -243,10 +254,12 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } case ControllerSettingsStruct::CONTROLLER_PASS: { - const size_t fieldMaxLength = ControllerSettings.useExtendedCredentials() ? EXT_SECURITY_MAX_PASS_LENGTH : sizeof(SecuritySettings.ControllerPassword[0]) - 1; + const size_t fieldMaxLength = + ControllerSettings.useExtendedCredentials() ? EXT_SECURITY_MAX_PASS_LENGTH : sizeof(SecuritySettings.ControllerPassword[0]) - 1; + if (isAlternativeDisplayName) { // It is not a regular password, thus use normal text field. - addFormTextBox(displayName, internalName, + addFormTextBox(displayName, internalName, getControllerPass(controllerindex, ControllerSettings), fieldMaxLength); } else { @@ -274,7 +287,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin } case ControllerSettingsStruct::CONTROLLER_FULL_QUEUE_ACTION: { - const __FlashStringHelper * options[2] { + const __FlashStringHelper *options[2] { F("Ignore New"), F("Delete Oldest") }; @@ -289,10 +302,10 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin break; case ControllerSettingsStruct::CONTROLLER_USE_LOCAL_SYSTEM_TIME: addFormCheckBox(displayName, internalName, ControllerSettings.useLocalSystemTime()); - break; + break; case ControllerSettingsStruct::CONTROLLER_CHECK_REPLY: { - const __FlashStringHelper * options[2] = { + const __FlashStringHelper *options[2] = { F("Ignore Acknowledgement"), F("Check Acknowledgement") }; @@ -309,12 +322,12 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin case ControllerSettingsStruct::CONTROLLER_RETAINFLAG: addFormCheckBox(displayName, internalName, ControllerSettings.mqtt_retainFlag()); break; -#endif +#endif // if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_SUBSCRIBE: - addFormTextBox(displayName, internalName, ControllerSettings.Subscribe, sizeof(ControllerSettings.Subscribe) - 1); + addFormTextBox(displayName, internalName, ControllerSettings.Subscribe, sizeof(ControllerSettings.Subscribe) - 1); break; case ControllerSettingsStruct::CONTROLLER_PUBLISH: - addFormTextBox(displayName, internalName, ControllerSettings.Publish, sizeof(ControllerSettings.Publish) - 1); + addFormTextBox(displayName, internalName, ControllerSettings.Publish, sizeof(ControllerSettings.Publish) - 1); break; #if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_LWT_TOPIC: @@ -335,7 +348,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin case ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION: addFormCheckBox(displayName, internalName, ControllerSettings.mqtt_cleanSession()); break; -#endif +#endif // if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: addFormCheckBox(displayName, internalName, ControllerSettings.useExtendedCredentials()); break; @@ -358,7 +371,7 @@ void addControllerParameterForm(const ControllerSettingsStruct& ControllerSettin void saveControllerParameterForm(ControllerSettingsStruct & ControllerSettings, controllerIndex_t controllerindex, ControllerSettingsStruct::VarType varType) { - const protocolIndex_t ProtocolIndex = + const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); if (!validProtocolIndex(ProtocolIndex)) { @@ -394,19 +407,21 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet #if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE: { - #if FEATURE_MQTT_TLS - const int current = static_cast(ControllerSettings.TLStype()); + # if FEATURE_MQTT_TLS + const int current = static_cast(ControllerSettings.TLStype()); const TLS_types tls_type = static_cast(getFormItemInt(internalName, current)); ControllerSettings.TLStype(tls_type); - #endif + # endif // if FEATURE_MQTT_TLS break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { - #if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS + if (isFormItemChecked(internalName)) { String fingerprint; + if (GetTLSfingerprint(fingerprint)) { if (ControllerSettings.UseDNS) { fingerprint += '\n'; @@ -415,7 +430,7 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT), fingerprint); } } - #endif + # endif // if FEATURE_MQTT_TLS break; } @@ -423,17 +438,19 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - #if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS + if (isFormItemChecked(internalName)) { String cacert; + if (GetTLS_Certificate(cacert, true)) { SaveCertificate(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT), cacert); } } - #endif + # endif // if FEATURE_MQTT_TLS break; } -#endif +#endif // if FEATURE_MQTT_TLS case ControllerSettingsStruct::CONTROLLER_USER: setControllerUser(controllerindex, ControllerSettings, webArg(internalName)); break; @@ -481,12 +498,12 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet case ControllerSettingsStruct::CONTROLLER_RETAINFLAG: ControllerSettings.mqtt_retainFlag(isFormItemChecked(internalName)); break; -#endif +#endif // if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_SUBSCRIBE: - strncpy_webserver_arg(ControllerSettings.Subscribe, internalName); + strncpy_webserver_arg(ControllerSettings.Subscribe, internalName); break; case ControllerSettingsStruct::CONTROLLER_PUBLISH: - strncpy_webserver_arg(ControllerSettings.Publish, internalName); + strncpy_webserver_arg(ControllerSettings.Publish, internalName); break; #if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_LWT_TOPIC: @@ -507,7 +524,7 @@ void saveControllerParameterForm(ControllerSettingsStruct & ControllerSet case ControllerSettingsStruct::CONTROLLER_CLEAN_SESSION: ControllerSettings.mqtt_cleanSession(isFormItemChecked(internalName)); break; -#endif +#endif // if FEATURE_MQTT case ControllerSettingsStruct::CONTROLLER_USE_EXTENDED_CREDENTIALS: ControllerSettings.useExtendedCredentials(isFormItemChecked(internalName)); break; diff --git a/src/src/WebServer/ControllerPage.cpp b/src/src/WebServer/ControllerPage.cpp index 50e769c793..2c36b140c5 100644 --- a/src/src/WebServer/ControllerPage.cpp +++ b/src/src/WebServer/ControllerPage.cpp @@ -18,7 +18,7 @@ # if FEATURE_MQTT # include "../Globals/MQTT.h" -# endif +# endif // if FEATURE_MQTT # include "../Helpers/_CPlugin_init.h" # include "../Helpers/_CPlugin_Helper_webform.h" @@ -55,11 +55,11 @@ void handle_controllers() { if ((protocol_webarg_value != -1) && !controllerNotSet) { const protocolIndex_t protocolIndex = protocol_webarg_value; - bool mustInit = false; - bool mustCallCpluginSave = false; + bool mustInit = false; + bool mustCallCpluginSave = false; { // Place in a scope to free ControllerSettings memory ASAP - MakeControllerSettings(ControllerSettings); //-V522 + MakeControllerSettings(ControllerSettings); // -V522 if (!AllocatedControllerSettings()) { addHtmlError(F("Not enough free memory to save settings")); @@ -152,9 +152,9 @@ void handle_controllers_clearLoadDefaults(uint8_t controllerindex, ControllerSet const ProtocolStruct& proto = getProtocolStruct(ProtocolIndex); ControllerSettings.reset(); -#if FEATURE_MQTT_TLS +# if FEATURE_MQTT_TLS ControllerSettings.TLStype(TLS_types::NoTLS); -#endif +# endif // if FEATURE_MQTT_TLS ControllerSettings.Port = proto.defaultPort; // Load some templates from the controller. @@ -222,7 +222,7 @@ void handle_controllers_ShowAllControllersTable() html_table_header(F("Host")); html_table_header(F("Port")); - MakeControllerSettings(ControllerSettings); //-V522 + MakeControllerSettings(ControllerSettings); // -V522 if (AllocatedControllerSettings()) { for (controllerIndex_t x = 0; x < CONTROLLER_MAX; x++) @@ -274,6 +274,7 @@ void handle_controllers_ShowAllControllersTable() html_TD(); const ProtocolStruct& proto = getProtocolStruct(ProtocolIndex); + if ((INVALID_PROTOCOL_INDEX == ProtocolIndex) || proto.usesPort) { addHtmlInt(13 == Settings.Protocol[x] ? Settings.UDPPort : ControllerSettings->Port); // P2P/C013 exception } @@ -306,10 +307,11 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addSelector_Item(F("- Standalone -"), 0, false, false, EMPTY_STRING); protocolIndex_t protocolIndex = 0; + while (validProtocolIndex(protocolIndex)) { const cpluginID_t number = getCPluginID_from_ProtocolIndex(protocolIndex); - boolean disabled = false; // !((controllerindex == 0) || !Protocol[x].usesMQTT); + boolean disabled = false; // !((controllerindex == 0) || !Protocol[x].usesMQTT); addSelector_Item(getCPluginNameFromProtocolIndex(protocolIndex), number, choice == number, @@ -321,7 +323,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addHelpButton(F("EasyProtocols")); const protocolIndex_t ProtocolIndex = getProtocolIndex_from_ControllerIndex(controllerindex); - const ProtocolStruct& proto = getProtocolStruct(ProtocolIndex); + const ProtocolStruct& proto = getProtocolStruct(ProtocolIndex); # ifndef LIMIT_BUILD_SIZE addRTDControllerButton(getCPluginID_from_ProtocolIndex(ProtocolIndex)); @@ -330,7 +332,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (Settings.Protocol[controllerindex]) { { - MakeControllerSettings(ControllerSettings); //-V522 + MakeControllerSettings(ControllerSettings); // -V522 if (!AllocatedControllerSettings()) { addHtmlError(F("Out of memory, cannot load page")); @@ -351,20 +353,23 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_IP); } } + if (proto.usesPort) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PORT); } - #if FEATURE_MQTT_TLS + # if FEATURE_MQTT_TLS + if (proto.usesMQTT && proto.usesTLS) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_TYPE); addFormNote(F("Default ports: MQTT: 1883 / MQTT TLS: 8883")); } - #endif + # endif // if FEATURE_MQTT_TLS # ifdef USES_ESPEASY_NOW if (proto.usesMQTT) { // FIXME TD-er: Currently only enabled for MQTT protocols, later for more - addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_ENABLE_ESPEASY_NOW_FALLBACK); + addControllerParameterForm(*ControllerSettings, controllerindex, + ControllerSettingsStruct::CONTROLLER_ENABLE_ESPEASY_NOW_FALLBACK); } # endif // ifdef USES_ESPEASY_NOW @@ -387,6 +392,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (proto.usesTimeout) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_TIMEOUT); + if (proto.usesHost) { addFormNote(F("Typical timeout: 100...300 msec for local host, >500 msec for internet hosts")); } @@ -395,6 +401,7 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex if (proto.usesSampleSets) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SAMPLE_SET_INITIATOR); } + if (proto.allowLocalSystemTime) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_USE_LOCAL_SYSTEM_TIME); } @@ -417,7 +424,8 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PASS); } - #if FEATURE_MQTT + # if FEATURE_MQTT + if (proto.usesMQTT) { addTableSeparator(F("MQTT"), 2, 3); @@ -431,16 +439,17 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex # endif // if FEATURE_MQTT - if (proto.usesTemplate - #if FEATURE_MQTT + if (proto.usesTemplate + # if FEATURE_MQTT || proto.usesMQTT - #endif - ) + # endif // if FEATURE_MQTT + ) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_SUBSCRIBE); addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_PUBLISH); } - #if FEATURE_MQTT + # if FEATURE_MQTT + if (proto.usesMQTT) { addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_LWT_TOPIC); @@ -469,119 +478,130 @@ void handle_controllers_ControllerSettingsPage(controllerIndex_t controllerindex } } { -#if FEATURE_MQTT - if (proto.usesMQTT) { - addFormSubHeader(F("Connection Status")); - addRowLabel(F("MQTT Client Connected")); - addEnabled(MQTTclient_connected); - -#if FEATURE_MQTT_TLS - if (proto.usesTLS) { - addRowLabel(F("Last Error")); - addHtmlInt(mqtt_tls_last_error); - addHtml(F(": ")); - addHtml(mqtt_tls_last_errorstr); - - #ifdef ESP32 - if (MQTTclient_connected && mqtt_tls != nullptr) { - MakeControllerSettings(ControllerSettings); //-V522 - if (!AllocatedControllerSettings()) { - addHtmlError(F("Out of memory, cannot load page")); - } else { - LoadControllerSettings(controllerindex, *ControllerSettings); - - // FIXME TD-er: Implement retrieval of certificate - /* - - addFormSubHeader(F("Peer Certificate")); - - { - addFormTextArea( - F("Certificate Info"), - F("certinfo"), - mqtt_tls->getPeerCertificateInfo(), - -1, - -1, - -1, - true); +# if FEATURE_MQTT + + if (proto.usesMQTT) { + addFormSubHeader(F("Connection Status")); + addRowLabel(F("MQTT Client Connected")); + addEnabled(MQTTclient_connected); + +# if FEATURE_MQTT_TLS + + if (proto.usesTLS) { + addRowLabel(F("Last Error")); + addHtmlInt(mqtt_tls_last_error); + addHtml(F(": ")); + addHtml(mqtt_tls_last_errorstr); + + # ifdef ESP32 + + if (MQTTclient_connected && (mqtt_tls != nullptr)) { + MakeControllerSettings(ControllerSettings); // -V522 + + if (!AllocatedControllerSettings()) { + addHtmlError(F("Out of memory, cannot load page")); + } else { + LoadControllerSettings(controllerindex, *ControllerSettings); + + // FIXME TD-er: Implement retrieval of certificate +/* + + addFormSubHeader(F("Peer Certificate")); + + { + addFormTextArea( + F("Certificate Info"), + F("certinfo"), + mqtt_tls->getPeerCertificateInfo(), + -1, + -1, + -1, + true); + } + { + String fingerprint; + + if (GetTLSfingerprint(fingerprint)) { + addFormTextBox(F("Certificate Fingerprint"), + F("fingerprint"), + fingerprint, + 64, + true); // ReadOnly + addControllerParameterForm(*ControllerSettings, controllerindex, + ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); } - { - String fingerprint; - if (GetTLSfingerprint(fingerprint)) { - addFormTextBox(F("Certificate Fingerprint"), - F("fingerprint"), - fingerprint, - 64, - true); // ReadOnly - addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT); + } + addFormSubHeader(F("Peer Certificate Chain")); + { + // FIXME TD-er: Must wrap this in divs to be able to fold it by default. + const mbedtls_x509_crt *chain; + + chain = mqtt_tls->getPeerCertificate(); + + int error { 0 }; + + while (chain != nullptr && error == 0) { + // const bool mustShow = !chain->ca_istrue || chain->next == nullptr; + // if (mustShow) { + String pem, subject; + error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); + { + String label; + + if (chain->ca_istrue) { + label = F("CA "); + } + label += F("Certificate "); + label += subject; + label += F(""); + addRowLabel(label); } - } - addFormSubHeader(F("Peer Certificate Chain")); - { - // FIXME TD-er: Must wrap this in divs to be able to fold it by default. - const mbedtls_x509_crt *chain; - - chain = mqtt_tls->getPeerCertificate(); - - int error {0}; - while (chain != nullptr && error == 0) { -// const bool mustShow = !chain->ca_istrue || chain->next == nullptr; -// if (mustShow) { - String pem, subject; - error = ESPEasy_WiFiClientSecure::cert_to_pem(chain, pem, subject); - { - String label; - if (chain->ca_istrue) { - label = F("CA "); - } - label += F("Certificate "); - label += subject; - label += F(""); - addRowLabel(label); - } - if (error == 0) { - addTextArea( - F("peerCertInfo"), - mqtt_tls->getPeerCertificateInfo(chain), - -1, - -1, - -1, - true, - false); - - addTextArea( - F("pem"), - pem, - -1, - -1, - -1, - true, - false); - } else { - addHtmlInt(error); - } - if (chain->ca_istrue && chain->next == nullptr) { - // Add checkbox to store CA cert - addControllerParameterForm(*ControllerSettings, controllerindex, ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT); - } -// } - chain = chain->next; + + if (error == 0) { + addTextArea( + F("peerCertInfo"), + mqtt_tls->getPeerCertificateInfo(chain), + -1, + -1, + -1, + true, + false); + + addTextArea( + F("pem"), + pem, + -1, + -1, + -1, + true, + false); + } else { + addHtmlInt(error); + } + + if (chain->ca_istrue && (chain->next == nullptr)) { + // Add checkbox to store CA cert + addControllerParameterForm(*ControllerSettings, controllerindex, + ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT); } + + // } + chain = chain->next; } - */ } +*/ } - #endif } -#endif + # endif // ifdef ESP32 } -#endif +# endif // if FEATURE_MQTT_TLS + } +# endif // if FEATURE_MQTT } // Separate enabled checkbox as it doesn't need to use the ControllerSettings. // So ControllerSettings object can be destructed before controller specific settings are loaded. addControllerEnabledForm(controllerindex); - } addFormSeparator(2); From 4917ff92e3c03151ded2ba18e4e0d66de6f49ea9 Mon Sep 17 00:00:00 2001 From: TD-er Date: Mon, 30 Sep 2024 23:01:11 +0200 Subject: [PATCH 299/367] [MQTT-TLS] Improve stability of MQTT connection while serving JSON pages --- .../tls_mini/src/WiFiClientSecureLightBearSSL.cpp | 12 ++++++++++++ .../tls_mini/src/WiFiClientSecureLightBearSSL.h | 6 ++++-- lib/pubsubclient/src/PubSubClient.cpp | 1 + src/ESPEasy-Globals.cpp | 2 +- src/src/ESPEasyCore/Controller.cpp | 5 +++++ src/src/ESPEasyCore/ESPEasy_setup.cpp | 2 +- src/src/Helpers/PeriodicalActions.cpp | 4 ++-- 7 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp index 137a3f2061..e2bc99089e 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp @@ -297,6 +297,18 @@ void WiFiClientSecure_light::flush(void) { #endif #ifdef ESP32 + +int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port) +{ + return connect(ip, port, _timeout); +} + +int WiFiClientSecure_light::connect(const char* name, uint16_t port) +{ + return connect(name, port, _timeout); +} + + int WiFiClientSecure_light::connect(IPAddress ip, uint16_t port, int32_t timeout) { DEBUG_BSSL("connect(%s,%d)", ip.toString().c_str(), port); clearLastError(); diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h index ae192cfd0b..60c32fb04f 100644 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.h @@ -45,8 +45,10 @@ class WiFiClientSecure_light : public NetworkClient { void allocateBuffers(void); #ifdef ESP32 // the method to override in ESP32 has timeout argument default #define WIFI_CLIENT_DEF_CONN_TIMEOUT_MS (3000) - int connect(IPAddress ip, uint16_t port, int32_t timeout = 3000) override; - int connect(const char* name, uint16_t port, int32_t timeout = 3000) override; + int connect(IPAddress ip, uint16_t port) override; + int connect(const char* name, uint16_t port) override; + int connect(IPAddress ip, uint16_t port, int32_t timeout); + int connect(const char* name, uint16_t port, int32_t timeout); #else int connect(IPAddress ip, uint16_t port) override; int connect(const char* name, uint16_t port) override; diff --git a/lib/pubsubclient/src/PubSubClient.cpp b/lib/pubsubclient/src/PubSubClient.cpp index 30948c57f9..34a8cab7b8 100644 --- a/lib/pubsubclient/src/PubSubClient.cpp +++ b/lib/pubsubclient/src/PubSubClient.cpp @@ -458,6 +458,7 @@ bool PubSubClient::loop_read() { } boolean PubSubClient::loop() { + delay(0); loop_read(); if (connected()) { unsigned long t = millis(); diff --git a/src/ESPEasy-Globals.cpp b/src/ESPEasy-Globals.cpp index 74bf20a961..ef6bef37e1 100644 --- a/src/ESPEasy-Globals.cpp +++ b/src/ESPEasy-Globals.cpp @@ -11,7 +11,7 @@ boolean printToWebJSON = false; -unsigned long timermqtt_interval = 250; +unsigned long timermqtt_interval = 100; unsigned long lastSend = 0; unsigned long lastWeb = 0; diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 04ac2b61a9..9b19c827d0 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -254,6 +254,8 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt.setTimeout(timeout); // in msec as it should be! # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS MQTTclient.setClient(mqtt); + MQTTclient.setKeepAlive(10); + MQTTclient.setSocketTimeout(timeout); break; } @@ -357,6 +359,9 @@ bool MQTTConnect(controllerIndex_t controller_idx) mqtt_tls->setBufferSizes(1024, 1024); # endif // ifdef ESP8266 MQTTclient.setClient(*mqtt_tls); + MQTTclient.setKeepAlive(10); + MQTTclient.setSocketTimeout(timeout); + if (mqttPort == 1883) { mqttPort = 8883; diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 8130acc603..10c4c71146 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -459,7 +459,7 @@ void ESPEasy_setup() } #endif - timermqtt_interval = 250; // Interval for checking MQTT + timermqtt_interval = 100; // Interval for checking MQTT timerAwakeFromDeepSleep = millis(); CPluginInit(); #ifndef BUILD_NO_RAM_TRACKER diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index 6e1f90b967..9f037cf0d6 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -365,10 +365,10 @@ void updateMQTTclient_connected() { if (!MQTTclient_connected) { // As suggested here: https://github.com/letscontrolit/ESPEasy/issues/1356 if (timermqtt_interval < 30000) { - timermqtt_interval += 5000; + timermqtt_interval += 500; } } else { - timermqtt_interval = 250; + timermqtt_interval = 100; } Scheduler.setIntervalTimer(SchedulerIntervalTimer_e::TIMER_MQTT); scheduleNextMQTTdelayQueue(); From f8ffe8e1641ecf43a07c2491f2421011d4b0ae19 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 1 Oct 2024 10:52:20 +0200 Subject: [PATCH 300/367] [PVS-Studio] Add fixes suggested by PVS-Studio --- src/src/DataStructs/PluginStats_Config.cpp | 4 ++++ src/src/DataStructs/PluginStats_Config.h | 2 ++ src/src/DataStructs/SettingsStruct.h | 4 ++-- src/src/DataStructs/WiFi_AP_Candidate.cpp | 12 +++++++++++- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/src/DataStructs/PluginStats_Config.cpp b/src/src/DataStructs/PluginStats_Config.cpp index 2e8fa78bdb..9f66f21c87 100644 --- a/src/src/DataStructs/PluginStats_Config.cpp +++ b/src/src/DataStructs/PluginStats_Config.cpp @@ -3,6 +3,10 @@ #if FEATURE_PLUGIN_STATS +PluginStats_Config_t::PluginStats_Config_t(const PluginStats_Config_t& other) +{ + setStored(other.getStored()); +} PluginStats_Config_t & PluginStats_Config_t::operator=(const PluginStats_Config_t& other) { diff --git a/src/src/DataStructs/PluginStats_Config.h b/src/src/DataStructs/PluginStats_Config.h index 16dcff1d01..d8aa71c80d 100644 --- a/src/src/DataStructs/PluginStats_Config.h +++ b/src/src/DataStructs/PluginStats_Config.h @@ -20,6 +20,8 @@ struct PluginStats_Config_t { setStored(stored_value); } + PluginStats_Config_t(const PluginStats_Config_t& other); + PluginStats_Config_t& operator=(const PluginStats_Config_t& other); AxisPosition getAxisPosition() const { diff --git a/src/src/DataStructs/SettingsStruct.h b/src/src/DataStructs/SettingsStruct.h index 45fade5f86..8cd707dff2 100644 --- a/src/src/DataStructs/SettingsStruct.h +++ b/src/src/DataStructs/SettingsStruct.h @@ -486,7 +486,7 @@ class SettingsStruct_tmpl uint32_t CheckI2Cdevice : 1; // Bit 30 Inverted uint32_t DoNotUse_31 : 1; // Bit 31 Was used to detect whether various bits were even set - } VariousBits_1; + } VariousBits_1; //-V730 uint32_t ResetFactoryDefaultPreference = 0; // Do not clear this one in the clearAll() uint32_t I2C_clockSpeed = 400000; @@ -561,7 +561,7 @@ class SettingsStruct_tmpl uint32_t unused_30 : 1; // Bit 30 uint32_t unused_31 : 1; // Bit 31 - } VariousBits_2; + } VariousBits_2; //-V730 uint8_t console_serial_port = DEFAULT_CONSOLE_PORT; int8_t console_serial_rxpin = DEFAULT_CONSOLE_PORT_RXPIN; diff --git a/src/src/DataStructs/WiFi_AP_Candidate.cpp b/src/src/DataStructs/WiFi_AP_Candidate.cpp index 2ed36251d8..bf37383631 100644 --- a/src/src/DataStructs/WiFi_AP_Candidate.cpp +++ b/src/src/DataStructs/WiFi_AP_Candidate.cpp @@ -25,7 +25,7 @@ WiFi_AP_Candidate::WiFi_AP_Candidate() : country({ .cc = "01", .schan = 1, - .nchan = 11, + .nchan = 14, .policy = WIFI_COUNTRY_POLICY_AUTO, }), #endif @@ -47,6 +47,16 @@ WiFi_AP_Candidate::WiFi_AP_Candidate(const WiFi_AP_Candidate& other) } WiFi_AP_Candidate::WiFi_AP_Candidate(uint8_t index_c, const String& ssid_c) : +#ifdef ESP32 +# if ESP_IDF_VERSION_MAJOR >= 5 +country({ + .cc = "01", + .schan = 1, + .nchan = 14, + .policy = WIFI_COUNTRY_POLICY_AUTO, +}), +#endif +#endif last_seen(0), rssi(0), channel(0), index(index_c), enc_type(0) { memset(&bits, 0, sizeof(bits)); From f6c0a4b5a652e2c3b15429e67c187f2326cafa40 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 1 Oct 2024 16:23:32 +0200 Subject: [PATCH 301/367] [MQTT-TLS] Make connection to broker more stable --- src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp | 2 ++ src/src/ESPEasyCore/ESPEasy_setup.cpp | 2 +- src/src/Helpers/PeriodicalActions.cpp | 11 +++++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp index ca21573cb3..db80122b21 100644 --- a/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp +++ b/src/src/ESPEasyCore/ESPEasy_backgroundtasks.cpp @@ -79,11 +79,13 @@ void backgroundtasks() serial(); // if (webserverRunning) { +/* { START_TIMER web_server.handleClient(); STOP_TIMER(WEBSERVER_HANDLE_CLIENT); } +*/ #if FEATURE_ESPEASY_P2P if (networkConnected) { checkUDP(); diff --git a/src/src/ESPEasyCore/ESPEasy_setup.cpp b/src/src/ESPEasyCore/ESPEasy_setup.cpp index 10c4c71146..84a6384a30 100644 --- a/src/src/ESPEasyCore/ESPEasy_setup.cpp +++ b/src/src/ESPEasyCore/ESPEasy_setup.cpp @@ -592,8 +592,8 @@ void ESPEasy_setup() #endif - UseRTOSMultitasking = Settings.UseRTOSMultitasking; #ifdef USE_RTOS_MULTITASKING + UseRTOSMultitasking = Settings.UseRTOSMultitasking; if (UseRTOSMultitasking) { if (loglevelActiveFor(LOG_LEVEL_INFO)) { diff --git a/src/src/Helpers/PeriodicalActions.cpp b/src/src/Helpers/PeriodicalActions.cpp index 9f037cf0d6..4d444567d8 100644 --- a/src/src/Helpers/PeriodicalActions.cpp +++ b/src/src/Helpers/PeriodicalActions.cpp @@ -96,12 +96,15 @@ void run10TimesPerSecond() { } #ifdef USES_C015 - if (NetworkConnected()) - Blynk_Run_c015(); + if (NetworkConnected()) { + Blynk_Run_c015(); + } #endif - #ifndef USE_RTOS_MULTITASKING + if (!UseRTOSMultitasking) { + START_TIMER web_server.handleClient(); - #endif + STOP_TIMER(WEBSERVER_HANDLE_CLIENT); + } } From acc8b78eaf3f7c6c15a8fd8ecaf2c3604dfdc45a Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 1 Oct 2024 23:14:01 +0200 Subject: [PATCH 302/367] [MQTT TLS] Disable certificate validation options for now --- src/src/Helpers/_CPlugin_Helper_webform.cpp | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/src/Helpers/_CPlugin_Helper_webform.cpp b/src/src/Helpers/_CPlugin_Helper_webform.cpp index 4192552134..de0c767088 100644 --- a/src/src/Helpers/_CPlugin_Helper_webform.cpp +++ b/src/src/Helpers/_CPlugin_Helper_webform.cpp @@ -198,16 +198,24 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett toString(TLS_types::NoTLS), // toString(TLS_types::TLS_PSK), + +/* + // FIXME TD-er: Disabled for now, will add selection later toString(TLS_types::TLS_CA_CERT), toString(TLS_types::TLS_FINGERPRINT), +*/ toString(TLS_types::TLS_insecure) }; const int indices[] = { static_cast(TLS_types::NoTLS), // static_cast(TLS_types::TLS_PSK), + + /* + // FIXME TD-er: Disabled for now, will add selection later static_cast(TLS_types::TLS_CA_CERT), static_cast(TLS_types::TLS_FINGERPRINT), +*/ static_cast(TLS_types::TLS_insecure) }; constexpr int nrOptions = NR_ELEMENTS(indices); @@ -219,19 +227,22 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_FINGERPRINT: { +/* + // FIXME TD-er: Disabled for now, will add selection later const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_FINGERPRINT)); addFormCheckBox(displayName, internalName, false, saveDisabled); addCertificateFileNote(ControllerSettings, F("Store fingerprint in"), TLS_types::TLS_FINGERPRINT); +*/ break; } case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CERT: // fall through case ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT: { - /* - const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? - TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; - */ + /* + // FIXME TD-er: Disabled for now, will add selection later + // const TLS_types tls_type = (varType == ControllerSettingsStruct::CONTROLLER_MQTT_TLS_STORE_CACERT) ? + // TLS_types::TLS_CA_CERT : TLS_types::TLS_CERT; const bool saveDisabled = fileExists(ControllerSettings.getCertificateFilename(TLS_types::TLS_CA_CERT)); addFormCheckBox(displayName, internalName, false, saveDisabled); @@ -239,6 +250,7 @@ void addControllerParameterForm(const ControllerSettingsStruct & ControllerSett addUnit(F("File Exists")); } addCertificateFileNote(ControllerSettings, F("Store CA Certificate in"), TLS_types::TLS_CA_CERT); +*/ break; } #endif // if FEATURE_MQTT_TLS From 7c77b5baeb07ce617c9c2d65ad787a1fe5a69628 Mon Sep 17 00:00:00 2001 From: TD-er Date: Tue, 1 Oct 2024 23:14:54 +0200 Subject: [PATCH 303/367] [MQTT TLS] Add some documentation about using TLS for MQTT --- docs/source/Controller/_Controller.rst | 79 +++++++------------------- 1 file changed, 19 insertions(+), 60 deletions(-) diff --git a/docs/source/Controller/_Controller.rst b/docs/source/Controller/_Controller.rst index db1f1c7290..8019561481 100644 --- a/docs/source/Controller/_Controller.rst +++ b/docs/source/Controller/_Controller.rst @@ -79,66 +79,25 @@ before WiFi connection is made or during lost connection. TLS configuration ----------------- -Added: 2021-09-26 - -Some protocols like MQTT may use TLS to provide a secure connection to the broker. - - -Still under development. -Notes: - -BearSSL::WiFiClientSecure net; - -Retrieve CA root certificate: -net.setCACert(local_root_ca); -BearSSL::X509List cert(digicert); -net.setTrustAnchors(&cert); - - -Retrieve public key of a specific certificate: ``openssl x509 -pubkey -noout -in ca.crt`` -BearSSL::PublicKey key(pubkey); -net.setKnownKey(&key); - - -Use certificate fingerprint (HEX checksum of certificate): -openssl x509 -fingerrint -in ca.crt - -net.setFingerprint(fp); - -Self Signed certificate Mosquitto: http://www.steves-internet-guide.com/mosquitto-tls/ -Let's encrypt Mosquitto: https://www.digitalocean.com/community/tutorials/how-to-install-and-secure-the-mosquitto-mqtt-messaging-broker-on-ubuntu-18-04-quickstart - -See: https://www.youtube.com/watch?v=ytQUbyab4es - -https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem.txt - ------BEGIN CERTIFICATE----- -MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/ -MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT -DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow -SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT -GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF -q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8 -SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0 -Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA -a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj -/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T -AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG -CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv -bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k -c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw -VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC -ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz -MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu -Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF -AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo -uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/ -wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu -X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG -PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6 -KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg== ------END CERTIFICATE----- +Added: 2024-10-02 + +Some protocols like MQTT may use TLS to provide a secure connection to the host. +Where the default port for not encrypted connections to a MQTT broker is port 1883, its TLS counterpart is by default using port 8883. + +.. note:: The current (2024-10-02) implementation does only allow to set to use TLS for MQTT controllers. There is not yet a proper validation of the used certificate. + +Future implementations will add various ways to validate the used certificates using: + +- Root CA, allowing to validate whether a certificate was signed by a known certificate authority (CA). +- Fingerprint, check whether a certificate is still the same as before. +- Check whether a certificate has expired. + +To summarize, the current implementation does allow to encrypt the connection to the MQTT broker. +However a man-in-the-middle attack is still perfectly possible as the used certificates are not validated. + +This does make using it extremely simple as even self-signed certificates can be used. +However do not consider this to be a 'secure' method since some attacker can redirect to another host and serve some false certificate. + Sample ThingSpeak configuration From dd360f77d3e47eafa025eacb568519789d413b01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 2 Oct 2024 12:37:12 +0000 Subject: [PATCH 304/367] Bump cryptography from 38.0.3 to 43.0.1 Bumps [cryptography](https://github.com/pyca/cryptography) from 38.0.3 to 43.0.1. - [Changelog](https://github.com/pyca/cryptography/blob/main/CHANGELOG.rst) - [Commits](https://github.com/pyca/cryptography/compare/38.0.3...43.0.1) --- updated-dependencies: - dependency-name: cryptography dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 58f057ee7c..76a5e7ba5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ platformio>=6.1.9 pygit2>=1.10.1 -cryptography==38.0.3 +cryptography==43.0.1 setuptools \ No newline at end of file From 632e16b2477474aa81e80e0d16fd050b80fed71b Mon Sep 17 00:00:00 2001 From: TD-er Date: Wed, 2 Oct 2024 21:05:02 +0200 Subject: [PATCH 305/367] [Controller] Fix really strange merge issue (return statement got lost) --- src/src/ESPEasyCore/Controller.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/src/ESPEasyCore/Controller.cpp b/src/src/ESPEasyCore/Controller.cpp index 9b19c827d0..6d2c085779 100644 --- a/src/src/ESPEasyCore/Controller.cpp +++ b/src/src/ESPEasyCore/Controller.cpp @@ -759,7 +759,7 @@ void SendStatus(struct EventStruct *event, const __FlashStringHelper *status) void SendStatus(struct EventStruct *event, const String& status) { - if (status.isEmpty()) {} + if (status.isEmpty()) { return; } switch (event->Source) { @@ -870,6 +870,7 @@ void MQTTStatus(struct EventStruct *event, const String& status) if (DomoticzMQTT_controllerIndex == enabledMqttController) { // Do not send MQTT status updates to Domoticz + return; } String pubname; bool mqtt_retainFlag; @@ -971,7 +972,7 @@ void SensorSendTask(struct EventStruct *event, unsigned long timestampUnixTime) void SensorSendTask(struct EventStruct *event, unsigned long timestampUnixTime, unsigned long lasttimer) { - if (!validTaskIndex(event->TaskIndex)) {} + if (!validTaskIndex(event->TaskIndex)) { return; } // FIXME TD-er: Should a 'disabled' task be rescheduled? // If not, then it should be rescheduled after the check to see if it is enabled. From d40279b5b1344668a82d8755cde7076f3bd5259a Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 10:05:06 +0200 Subject: [PATCH 306/367] [HeatpumpIR] Rename lib folder to make it compile on Windows --- lib/{HeatpumpIR => Hp_IR}/AIRWAYHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/AIRWAYHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/AUXHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/AUXHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/BGHHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/BGHHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/BalluHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/BalluHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/CarrierHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/CarrierHeatpumpIR.h | 0 .../DaikinHeatpumpARC417IR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpARC417IR.h | 0 .../DaikinHeatpumpARC480A14IR.cpp | 0 .../DaikinHeatpumpARC480A14IR.h | 0 lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/ElectroluxHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/ElectroluxHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/FuegoHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/FuegoHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/FujitsuHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/FujitsuHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/GreeHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/GreeHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/HeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/HeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/HeatpumpIRFactory.cpp | 0 lib/{HeatpumpIR => Hp_IR}/HeatpumpIRFactory.h | 0 lib/{HeatpumpIR => Hp_IR}/HisenseHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/HisenseHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/HitachiHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/HitachiHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/HyundaiHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/HyundaiHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/IRSender.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSender.h | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderBitBang.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderBlaster.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderESP32.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderESP8266.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderESP8266Alt.cpp | 0 .../IRSenderIRremoteESP8266.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IRSenderPWM.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IVTHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/IVTHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/KY26HeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/KY26HeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/LICENSE | 0 lib/{HeatpumpIR => Hp_IR}/MideaHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/MideaHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/MitsubishiHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/MitsubishiHeatpumpIR.h | 0 .../MitsubishiHeavyFDTCHeatpumpIR.cpp | 0 .../MitsubishiHeavyFDTCHeatpumpIR.h | 0 .../MitsubishiHeavyHeatpumpIR.cpp | 0 .../MitsubishiHeavyHeatpumpIR.h | 0 .../MitsubishiMSCHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/MitsubishiMSCHeatpumpIR.h | 0 .../MitsubishiSEZKDXXHeatpumpIR.cpp | 0 .../MitsubishiSEZKDXXHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/NibeHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/NibeHeatpumpIR.h | 0 .../PanasonicAltDKEHeatpumpIR.cpp | 0 .../PanasonicAltDKEHeatpumpIR.h | 0 .../PanasonicCKPHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/PanasonicCKPHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/PanasonicHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/PanasonicHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/PhilcoPHS32HeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/PhilcoPHS32HeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/R51MHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/R51MHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/README.md | 0 lib/{HeatpumpIR => Hp_IR}/SamsungHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/SamsungHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/SharpHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/SharpHeatpumpIR.h | 0 .../ToshibaDaiseikaiHeatpumpIR.cpp | 0 .../ToshibaDaiseikaiHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/ToshibaHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/ToshibaHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/VaillantHeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/VaillantHeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/ZHJG01HeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/ZHJG01HeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/ZHLT01HeatpumpIR.cpp | 0 lib/{HeatpumpIR => Hp_IR}/ZHLT01HeatpumpIR.h | 0 lib/{HeatpumpIR => Hp_IR}/arduino_irsender.png | Bin .../examples/AUXTest_MQTT/AUXTest_MQTT.ino | 0 .../ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png | Bin .../AUXTest_MQTT/home_assistant_climate.yaml | 0 .../examples/CarrierTest/CarrierTest.ino | 0 .../examples/DaikinTest_MQTT/DaikinTest_MQTT.ino | 0 .../examples/DaiseikaiTest/DaiseikaiTest.ino | 0 .../examples/GreeTest/GreeYACTest/GreeYACTest.ino | 0 .../examples/HisenseTest/HisenseTest.ino | 0 .../examples/KY26Test_Menu/KY26Test_Menu.ino | 0 .../MideaRelayControl/MideaRelayControl.ino | 0 .../PanasonicCKPTimer/PanasonicCKPTimer.ino | 0 .../PanasonicRelayControl/PanasonicRelayControl.ino | 0 .../examples/rawsender/rawsender.ino | 0 .../examples/simple/simple.ino | 0 lib/{HeatpumpIR => Hp_IR}/keywords.txt | 0 lib/{HeatpumpIR => Hp_IR}/library.json | 0 lib/{HeatpumpIR => Hp_IR}/library.properties | 0 105 files changed, 0 insertions(+), 0 deletions(-) rename lib/{HeatpumpIR => Hp_IR}/AIRWAYHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/AIRWAYHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/AUXHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/AUXHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/BGHHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/BGHHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/BalluHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/BalluHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/CarrierHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/CarrierHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpARC417IR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpARC417IR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpARC480A14IR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpARC480A14IR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/DaikinHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/ElectroluxHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/ElectroluxHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/FuegoHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/FuegoHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/FujitsuHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/FujitsuHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/GreeHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/GreeHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/HeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/HeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/HeatpumpIRFactory.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/HeatpumpIRFactory.h (100%) rename lib/{HeatpumpIR => Hp_IR}/HisenseHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/HisenseHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/HitachiHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/HitachiHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/HyundaiHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/HyundaiHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSender.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSender.h (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderBitBang.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderBlaster.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderESP32.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderESP8266.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderESP8266Alt.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderIRremoteESP8266.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IRSenderPWM.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IVTHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/IVTHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/KY26HeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/KY26HeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/LICENSE (100%) rename lib/{HeatpumpIR => Hp_IR}/MideaHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MideaHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeavyFDTCHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeavyFDTCHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeavyHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiHeavyHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiMSCHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiMSCHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiSEZKDXXHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/MitsubishiSEZKDXXHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/NibeHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/NibeHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicAltDKEHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicAltDKEHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicCKPHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicCKPHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/PanasonicHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/PhilcoPHS32HeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/PhilcoPHS32HeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/R51MHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/R51MHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/README.md (100%) rename lib/{HeatpumpIR => Hp_IR}/SamsungHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/SamsungHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/SharpHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/SharpHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/ToshibaDaiseikaiHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/ToshibaDaiseikaiHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/ToshibaHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/ToshibaHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/VaillantHeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/VaillantHeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/ZHJG01HeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/ZHJG01HeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/ZHLT01HeatpumpIR.cpp (100%) rename lib/{HeatpumpIR => Hp_IR}/ZHLT01HeatpumpIR.h (100%) rename lib/{HeatpumpIR => Hp_IR}/arduino_irsender.png (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/AUXTest_MQTT/AUXTest_MQTT.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/AUXTest_MQTT/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/AUXTest_MQTT/home_assistant_climate.yaml (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/CarrierTest/CarrierTest.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/DaikinTest_MQTT/DaikinTest_MQTT.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/DaiseikaiTest/DaiseikaiTest.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/GreeTest/GreeYACTest/GreeYACTest.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/HisenseTest/HisenseTest.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/KY26Test_Menu/KY26Test_Menu.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/MideaRelayControl/MideaRelayControl.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/PanasonicRelayControl/PanasonicRelayControl.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/rawsender/rawsender.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/examples/simple/simple.ino (100%) rename lib/{HeatpumpIR => Hp_IR}/keywords.txt (100%) rename lib/{HeatpumpIR => Hp_IR}/library.json (100%) rename lib/{HeatpumpIR => Hp_IR}/library.properties (100%) diff --git a/lib/HeatpumpIR/AIRWAYHeatpumpIR.cpp b/lib/Hp_IR/AIRWAYHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/AIRWAYHeatpumpIR.cpp rename to lib/Hp_IR/AIRWAYHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/AIRWAYHeatpumpIR.h b/lib/Hp_IR/AIRWAYHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/AIRWAYHeatpumpIR.h rename to lib/Hp_IR/AIRWAYHeatpumpIR.h diff --git a/lib/HeatpumpIR/AUXHeatpumpIR.cpp b/lib/Hp_IR/AUXHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/AUXHeatpumpIR.cpp rename to lib/Hp_IR/AUXHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/AUXHeatpumpIR.h b/lib/Hp_IR/AUXHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/AUXHeatpumpIR.h rename to lib/Hp_IR/AUXHeatpumpIR.h diff --git a/lib/HeatpumpIR/BGHHeatpumpIR.cpp b/lib/Hp_IR/BGHHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/BGHHeatpumpIR.cpp rename to lib/Hp_IR/BGHHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/BGHHeatpumpIR.h b/lib/Hp_IR/BGHHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/BGHHeatpumpIR.h rename to lib/Hp_IR/BGHHeatpumpIR.h diff --git a/lib/HeatpumpIR/BalluHeatpumpIR.cpp b/lib/Hp_IR/BalluHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/BalluHeatpumpIR.cpp rename to lib/Hp_IR/BalluHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/BalluHeatpumpIR.h b/lib/Hp_IR/BalluHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/BalluHeatpumpIR.h rename to lib/Hp_IR/BalluHeatpumpIR.h diff --git a/lib/HeatpumpIR/CarrierHeatpumpIR.cpp b/lib/Hp_IR/CarrierHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/CarrierHeatpumpIR.cpp rename to lib/Hp_IR/CarrierHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/CarrierHeatpumpIR.h b/lib/Hp_IR/CarrierHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/CarrierHeatpumpIR.h rename to lib/Hp_IR/CarrierHeatpumpIR.h diff --git a/lib/HeatpumpIR/DaikinHeatpumpARC417IR.cpp b/lib/Hp_IR/DaikinHeatpumpARC417IR.cpp similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpARC417IR.cpp rename to lib/Hp_IR/DaikinHeatpumpARC417IR.cpp diff --git a/lib/HeatpumpIR/DaikinHeatpumpARC417IR.h b/lib/Hp_IR/DaikinHeatpumpARC417IR.h similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpARC417IR.h rename to lib/Hp_IR/DaikinHeatpumpARC417IR.h diff --git a/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.cpp b/lib/Hp_IR/DaikinHeatpumpARC480A14IR.cpp similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.cpp rename to lib/Hp_IR/DaikinHeatpumpARC480A14IR.cpp diff --git a/lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h b/lib/Hp_IR/DaikinHeatpumpARC480A14IR.h similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpARC480A14IR.h rename to lib/Hp_IR/DaikinHeatpumpARC480A14IR.h diff --git a/lib/HeatpumpIR/DaikinHeatpumpIR.cpp b/lib/Hp_IR/DaikinHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpIR.cpp rename to lib/Hp_IR/DaikinHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/DaikinHeatpumpIR.h b/lib/Hp_IR/DaikinHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/DaikinHeatpumpIR.h rename to lib/Hp_IR/DaikinHeatpumpIR.h diff --git a/lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp b/lib/Hp_IR/ElectroluxHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/ElectroluxHeatpumpIR.cpp rename to lib/Hp_IR/ElectroluxHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/ElectroluxHeatpumpIR.h b/lib/Hp_IR/ElectroluxHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/ElectroluxHeatpumpIR.h rename to lib/Hp_IR/ElectroluxHeatpumpIR.h diff --git a/lib/HeatpumpIR/FuegoHeatpumpIR.cpp b/lib/Hp_IR/FuegoHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/FuegoHeatpumpIR.cpp rename to lib/Hp_IR/FuegoHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/FuegoHeatpumpIR.h b/lib/Hp_IR/FuegoHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/FuegoHeatpumpIR.h rename to lib/Hp_IR/FuegoHeatpumpIR.h diff --git a/lib/HeatpumpIR/FujitsuHeatpumpIR.cpp b/lib/Hp_IR/FujitsuHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/FujitsuHeatpumpIR.cpp rename to lib/Hp_IR/FujitsuHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/FujitsuHeatpumpIR.h b/lib/Hp_IR/FujitsuHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/FujitsuHeatpumpIR.h rename to lib/Hp_IR/FujitsuHeatpumpIR.h diff --git a/lib/HeatpumpIR/GreeHeatpumpIR.cpp b/lib/Hp_IR/GreeHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/GreeHeatpumpIR.cpp rename to lib/Hp_IR/GreeHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/GreeHeatpumpIR.h b/lib/Hp_IR/GreeHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/GreeHeatpumpIR.h rename to lib/Hp_IR/GreeHeatpumpIR.h diff --git a/lib/HeatpumpIR/HeatpumpIR.cpp b/lib/Hp_IR/HeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/HeatpumpIR.cpp rename to lib/Hp_IR/HeatpumpIR.cpp diff --git a/lib/HeatpumpIR/HeatpumpIR.h b/lib/Hp_IR/HeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/HeatpumpIR.h rename to lib/Hp_IR/HeatpumpIR.h diff --git a/lib/HeatpumpIR/HeatpumpIRFactory.cpp b/lib/Hp_IR/HeatpumpIRFactory.cpp similarity index 100% rename from lib/HeatpumpIR/HeatpumpIRFactory.cpp rename to lib/Hp_IR/HeatpumpIRFactory.cpp diff --git a/lib/HeatpumpIR/HeatpumpIRFactory.h b/lib/Hp_IR/HeatpumpIRFactory.h similarity index 100% rename from lib/HeatpumpIR/HeatpumpIRFactory.h rename to lib/Hp_IR/HeatpumpIRFactory.h diff --git a/lib/HeatpumpIR/HisenseHeatpumpIR.cpp b/lib/Hp_IR/HisenseHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/HisenseHeatpumpIR.cpp rename to lib/Hp_IR/HisenseHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/HisenseHeatpumpIR.h b/lib/Hp_IR/HisenseHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/HisenseHeatpumpIR.h rename to lib/Hp_IR/HisenseHeatpumpIR.h diff --git a/lib/HeatpumpIR/HitachiHeatpumpIR.cpp b/lib/Hp_IR/HitachiHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/HitachiHeatpumpIR.cpp rename to lib/Hp_IR/HitachiHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/HitachiHeatpumpIR.h b/lib/Hp_IR/HitachiHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/HitachiHeatpumpIR.h rename to lib/Hp_IR/HitachiHeatpumpIR.h diff --git a/lib/HeatpumpIR/HyundaiHeatpumpIR.cpp b/lib/Hp_IR/HyundaiHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/HyundaiHeatpumpIR.cpp rename to lib/Hp_IR/HyundaiHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/HyundaiHeatpumpIR.h b/lib/Hp_IR/HyundaiHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/HyundaiHeatpumpIR.h rename to lib/Hp_IR/HyundaiHeatpumpIR.h diff --git a/lib/HeatpumpIR/IRSender.cpp b/lib/Hp_IR/IRSender.cpp similarity index 100% rename from lib/HeatpumpIR/IRSender.cpp rename to lib/Hp_IR/IRSender.cpp diff --git a/lib/HeatpumpIR/IRSender.h b/lib/Hp_IR/IRSender.h similarity index 100% rename from lib/HeatpumpIR/IRSender.h rename to lib/Hp_IR/IRSender.h diff --git a/lib/HeatpumpIR/IRSenderBitBang.cpp b/lib/Hp_IR/IRSenderBitBang.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderBitBang.cpp rename to lib/Hp_IR/IRSenderBitBang.cpp diff --git a/lib/HeatpumpIR/IRSenderBlaster.cpp b/lib/Hp_IR/IRSenderBlaster.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderBlaster.cpp rename to lib/Hp_IR/IRSenderBlaster.cpp diff --git a/lib/HeatpumpIR/IRSenderESP32.cpp b/lib/Hp_IR/IRSenderESP32.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderESP32.cpp rename to lib/Hp_IR/IRSenderESP32.cpp diff --git a/lib/HeatpumpIR/IRSenderESP8266.cpp b/lib/Hp_IR/IRSenderESP8266.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderESP8266.cpp rename to lib/Hp_IR/IRSenderESP8266.cpp diff --git a/lib/HeatpumpIR/IRSenderESP8266Alt.cpp b/lib/Hp_IR/IRSenderESP8266Alt.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderESP8266Alt.cpp rename to lib/Hp_IR/IRSenderESP8266Alt.cpp diff --git a/lib/HeatpumpIR/IRSenderIRremoteESP8266.cpp b/lib/Hp_IR/IRSenderIRremoteESP8266.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderIRremoteESP8266.cpp rename to lib/Hp_IR/IRSenderIRremoteESP8266.cpp diff --git a/lib/HeatpumpIR/IRSenderPWM.cpp b/lib/Hp_IR/IRSenderPWM.cpp similarity index 100% rename from lib/HeatpumpIR/IRSenderPWM.cpp rename to lib/Hp_IR/IRSenderPWM.cpp diff --git a/lib/HeatpumpIR/IVTHeatpumpIR.cpp b/lib/Hp_IR/IVTHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/IVTHeatpumpIR.cpp rename to lib/Hp_IR/IVTHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/IVTHeatpumpIR.h b/lib/Hp_IR/IVTHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/IVTHeatpumpIR.h rename to lib/Hp_IR/IVTHeatpumpIR.h diff --git a/lib/HeatpumpIR/KY26HeatpumpIR.cpp b/lib/Hp_IR/KY26HeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/KY26HeatpumpIR.cpp rename to lib/Hp_IR/KY26HeatpumpIR.cpp diff --git a/lib/HeatpumpIR/KY26HeatpumpIR.h b/lib/Hp_IR/KY26HeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/KY26HeatpumpIR.h rename to lib/Hp_IR/KY26HeatpumpIR.h diff --git a/lib/HeatpumpIR/LICENSE b/lib/Hp_IR/LICENSE similarity index 100% rename from lib/HeatpumpIR/LICENSE rename to lib/Hp_IR/LICENSE diff --git a/lib/HeatpumpIR/MideaHeatpumpIR.cpp b/lib/Hp_IR/MideaHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MideaHeatpumpIR.cpp rename to lib/Hp_IR/MideaHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MideaHeatpumpIR.h b/lib/Hp_IR/MideaHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MideaHeatpumpIR.h rename to lib/Hp_IR/MideaHeatpumpIR.h diff --git a/lib/HeatpumpIR/MitsubishiHeatpumpIR.cpp b/lib/Hp_IR/MitsubishiHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeatpumpIR.cpp rename to lib/Hp_IR/MitsubishiHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MitsubishiHeatpumpIR.h b/lib/Hp_IR/MitsubishiHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeatpumpIR.h rename to lib/Hp_IR/MitsubishiHeatpumpIR.h diff --git a/lib/HeatpumpIR/MitsubishiHeavyFDTCHeatpumpIR.cpp b/lib/Hp_IR/MitsubishiHeavyFDTCHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeavyFDTCHeatpumpIR.cpp rename to lib/Hp_IR/MitsubishiHeavyFDTCHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MitsubishiHeavyFDTCHeatpumpIR.h b/lib/Hp_IR/MitsubishiHeavyFDTCHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeavyFDTCHeatpumpIR.h rename to lib/Hp_IR/MitsubishiHeavyFDTCHeatpumpIR.h diff --git a/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.cpp b/lib/Hp_IR/MitsubishiHeavyHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.cpp rename to lib/Hp_IR/MitsubishiHeavyHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h b/lib/Hp_IR/MitsubishiHeavyHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MitsubishiHeavyHeatpumpIR.h rename to lib/Hp_IR/MitsubishiHeavyHeatpumpIR.h diff --git a/lib/HeatpumpIR/MitsubishiMSCHeatpumpIR.cpp b/lib/Hp_IR/MitsubishiMSCHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MitsubishiMSCHeatpumpIR.cpp rename to lib/Hp_IR/MitsubishiMSCHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MitsubishiMSCHeatpumpIR.h b/lib/Hp_IR/MitsubishiMSCHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MitsubishiMSCHeatpumpIR.h rename to lib/Hp_IR/MitsubishiMSCHeatpumpIR.h diff --git a/lib/HeatpumpIR/MitsubishiSEZKDXXHeatpumpIR.cpp b/lib/Hp_IR/MitsubishiSEZKDXXHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/MitsubishiSEZKDXXHeatpumpIR.cpp rename to lib/Hp_IR/MitsubishiSEZKDXXHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/MitsubishiSEZKDXXHeatpumpIR.h b/lib/Hp_IR/MitsubishiSEZKDXXHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/MitsubishiSEZKDXXHeatpumpIR.h rename to lib/Hp_IR/MitsubishiSEZKDXXHeatpumpIR.h diff --git a/lib/HeatpumpIR/NibeHeatpumpIR.cpp b/lib/Hp_IR/NibeHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/NibeHeatpumpIR.cpp rename to lib/Hp_IR/NibeHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/NibeHeatpumpIR.h b/lib/Hp_IR/NibeHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/NibeHeatpumpIR.h rename to lib/Hp_IR/NibeHeatpumpIR.h diff --git a/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.cpp b/lib/Hp_IR/PanasonicAltDKEHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.cpp rename to lib/Hp_IR/PanasonicAltDKEHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h b/lib/Hp_IR/PanasonicAltDKEHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/PanasonicAltDKEHeatpumpIR.h rename to lib/Hp_IR/PanasonicAltDKEHeatpumpIR.h diff --git a/lib/HeatpumpIR/PanasonicCKPHeatpumpIR.cpp b/lib/Hp_IR/PanasonicCKPHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/PanasonicCKPHeatpumpIR.cpp rename to lib/Hp_IR/PanasonicCKPHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/PanasonicCKPHeatpumpIR.h b/lib/Hp_IR/PanasonicCKPHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/PanasonicCKPHeatpumpIR.h rename to lib/Hp_IR/PanasonicCKPHeatpumpIR.h diff --git a/lib/HeatpumpIR/PanasonicHeatpumpIR.cpp b/lib/Hp_IR/PanasonicHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/PanasonicHeatpumpIR.cpp rename to lib/Hp_IR/PanasonicHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/PanasonicHeatpumpIR.h b/lib/Hp_IR/PanasonicHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/PanasonicHeatpumpIR.h rename to lib/Hp_IR/PanasonicHeatpumpIR.h diff --git a/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp b/lib/Hp_IR/PhilcoPHS32HeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.cpp rename to lib/Hp_IR/PhilcoPHS32HeatpumpIR.cpp diff --git a/lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h b/lib/Hp_IR/PhilcoPHS32HeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/PhilcoPHS32HeatpumpIR.h rename to lib/Hp_IR/PhilcoPHS32HeatpumpIR.h diff --git a/lib/HeatpumpIR/R51MHeatpumpIR.cpp b/lib/Hp_IR/R51MHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/R51MHeatpumpIR.cpp rename to lib/Hp_IR/R51MHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/R51MHeatpumpIR.h b/lib/Hp_IR/R51MHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/R51MHeatpumpIR.h rename to lib/Hp_IR/R51MHeatpumpIR.h diff --git a/lib/HeatpumpIR/README.md b/lib/Hp_IR/README.md similarity index 100% rename from lib/HeatpumpIR/README.md rename to lib/Hp_IR/README.md diff --git a/lib/HeatpumpIR/SamsungHeatpumpIR.cpp b/lib/Hp_IR/SamsungHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/SamsungHeatpumpIR.cpp rename to lib/Hp_IR/SamsungHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/SamsungHeatpumpIR.h b/lib/Hp_IR/SamsungHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/SamsungHeatpumpIR.h rename to lib/Hp_IR/SamsungHeatpumpIR.h diff --git a/lib/HeatpumpIR/SharpHeatpumpIR.cpp b/lib/Hp_IR/SharpHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/SharpHeatpumpIR.cpp rename to lib/Hp_IR/SharpHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/SharpHeatpumpIR.h b/lib/Hp_IR/SharpHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/SharpHeatpumpIR.h rename to lib/Hp_IR/SharpHeatpumpIR.h diff --git a/lib/HeatpumpIR/ToshibaDaiseikaiHeatpumpIR.cpp b/lib/Hp_IR/ToshibaDaiseikaiHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/ToshibaDaiseikaiHeatpumpIR.cpp rename to lib/Hp_IR/ToshibaDaiseikaiHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/ToshibaDaiseikaiHeatpumpIR.h b/lib/Hp_IR/ToshibaDaiseikaiHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/ToshibaDaiseikaiHeatpumpIR.h rename to lib/Hp_IR/ToshibaDaiseikaiHeatpumpIR.h diff --git a/lib/HeatpumpIR/ToshibaHeatpumpIR.cpp b/lib/Hp_IR/ToshibaHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/ToshibaHeatpumpIR.cpp rename to lib/Hp_IR/ToshibaHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/ToshibaHeatpumpIR.h b/lib/Hp_IR/ToshibaHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/ToshibaHeatpumpIR.h rename to lib/Hp_IR/ToshibaHeatpumpIR.h diff --git a/lib/HeatpumpIR/VaillantHeatpumpIR.cpp b/lib/Hp_IR/VaillantHeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/VaillantHeatpumpIR.cpp rename to lib/Hp_IR/VaillantHeatpumpIR.cpp diff --git a/lib/HeatpumpIR/VaillantHeatpumpIR.h b/lib/Hp_IR/VaillantHeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/VaillantHeatpumpIR.h rename to lib/Hp_IR/VaillantHeatpumpIR.h diff --git a/lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp b/lib/Hp_IR/ZHJG01HeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/ZHJG01HeatpumpIR.cpp rename to lib/Hp_IR/ZHJG01HeatpumpIR.cpp diff --git a/lib/HeatpumpIR/ZHJG01HeatpumpIR.h b/lib/Hp_IR/ZHJG01HeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/ZHJG01HeatpumpIR.h rename to lib/Hp_IR/ZHJG01HeatpumpIR.h diff --git a/lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp b/lib/Hp_IR/ZHLT01HeatpumpIR.cpp similarity index 100% rename from lib/HeatpumpIR/ZHLT01HeatpumpIR.cpp rename to lib/Hp_IR/ZHLT01HeatpumpIR.cpp diff --git a/lib/HeatpumpIR/ZHLT01HeatpumpIR.h b/lib/Hp_IR/ZHLT01HeatpumpIR.h similarity index 100% rename from lib/HeatpumpIR/ZHLT01HeatpumpIR.h rename to lib/Hp_IR/ZHLT01HeatpumpIR.h diff --git a/lib/HeatpumpIR/arduino_irsender.png b/lib/Hp_IR/arduino_irsender.png similarity index 100% rename from lib/HeatpumpIR/arduino_irsender.png rename to lib/Hp_IR/arduino_irsender.png diff --git a/lib/HeatpumpIR/examples/AUXTest_MQTT/AUXTest_MQTT.ino b/lib/Hp_IR/examples/AUXTest_MQTT/AUXTest_MQTT.ino similarity index 100% rename from lib/HeatpumpIR/examples/AUXTest_MQTT/AUXTest_MQTT.ino rename to lib/Hp_IR/examples/AUXTest_MQTT/AUXTest_MQTT.ino diff --git a/lib/HeatpumpIR/examples/AUXTest_MQTT/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png b/lib/Hp_IR/examples/AUXTest_MQTT/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png similarity index 100% rename from lib/HeatpumpIR/examples/AUXTest_MQTT/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png rename to lib/Hp_IR/examples/AUXTest_MQTT/ESP8266-WeMos-D1-Mini-pinout-gpio-pin.png diff --git a/lib/HeatpumpIR/examples/AUXTest_MQTT/home_assistant_climate.yaml b/lib/Hp_IR/examples/AUXTest_MQTT/home_assistant_climate.yaml similarity index 100% rename from lib/HeatpumpIR/examples/AUXTest_MQTT/home_assistant_climate.yaml rename to lib/Hp_IR/examples/AUXTest_MQTT/home_assistant_climate.yaml diff --git a/lib/HeatpumpIR/examples/CarrierTest/CarrierTest.ino b/lib/Hp_IR/examples/CarrierTest/CarrierTest.ino similarity index 100% rename from lib/HeatpumpIR/examples/CarrierTest/CarrierTest.ino rename to lib/Hp_IR/examples/CarrierTest/CarrierTest.ino diff --git a/lib/HeatpumpIR/examples/DaikinTest_MQTT/DaikinTest_MQTT.ino b/lib/Hp_IR/examples/DaikinTest_MQTT/DaikinTest_MQTT.ino similarity index 100% rename from lib/HeatpumpIR/examples/DaikinTest_MQTT/DaikinTest_MQTT.ino rename to lib/Hp_IR/examples/DaikinTest_MQTT/DaikinTest_MQTT.ino diff --git a/lib/HeatpumpIR/examples/DaiseikaiTest/DaiseikaiTest.ino b/lib/Hp_IR/examples/DaiseikaiTest/DaiseikaiTest.ino similarity index 100% rename from lib/HeatpumpIR/examples/DaiseikaiTest/DaiseikaiTest.ino rename to lib/Hp_IR/examples/DaiseikaiTest/DaiseikaiTest.ino diff --git a/lib/HeatpumpIR/examples/GreeTest/GreeYACTest/GreeYACTest.ino b/lib/Hp_IR/examples/GreeTest/GreeYACTest/GreeYACTest.ino similarity index 100% rename from lib/HeatpumpIR/examples/GreeTest/GreeYACTest/GreeYACTest.ino rename to lib/Hp_IR/examples/GreeTest/GreeYACTest/GreeYACTest.ino diff --git a/lib/HeatpumpIR/examples/HisenseTest/HisenseTest.ino b/lib/Hp_IR/examples/HisenseTest/HisenseTest.ino similarity index 100% rename from lib/HeatpumpIR/examples/HisenseTest/HisenseTest.ino rename to lib/Hp_IR/examples/HisenseTest/HisenseTest.ino diff --git a/lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino b/lib/Hp_IR/examples/KY26Test_Menu/KY26Test_Menu.ino similarity index 100% rename from lib/HeatpumpIR/examples/KY26Test_Menu/KY26Test_Menu.ino rename to lib/Hp_IR/examples/KY26Test_Menu/KY26Test_Menu.ino diff --git a/lib/HeatpumpIR/examples/MideaRelayControl/MideaRelayControl.ino b/lib/Hp_IR/examples/MideaRelayControl/MideaRelayControl.ino similarity index 100% rename from lib/HeatpumpIR/examples/MideaRelayControl/MideaRelayControl.ino rename to lib/Hp_IR/examples/MideaRelayControl/MideaRelayControl.ino diff --git a/lib/HeatpumpIR/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino b/lib/Hp_IR/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino similarity index 100% rename from lib/HeatpumpIR/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino rename to lib/Hp_IR/examples/PanasonicCKPTimer/PanasonicCKPTimer.ino diff --git a/lib/HeatpumpIR/examples/PanasonicRelayControl/PanasonicRelayControl.ino b/lib/Hp_IR/examples/PanasonicRelayControl/PanasonicRelayControl.ino similarity index 100% rename from lib/HeatpumpIR/examples/PanasonicRelayControl/PanasonicRelayControl.ino rename to lib/Hp_IR/examples/PanasonicRelayControl/PanasonicRelayControl.ino diff --git a/lib/HeatpumpIR/examples/rawsender/rawsender.ino b/lib/Hp_IR/examples/rawsender/rawsender.ino similarity index 100% rename from lib/HeatpumpIR/examples/rawsender/rawsender.ino rename to lib/Hp_IR/examples/rawsender/rawsender.ino diff --git a/lib/HeatpumpIR/examples/simple/simple.ino b/lib/Hp_IR/examples/simple/simple.ino similarity index 100% rename from lib/HeatpumpIR/examples/simple/simple.ino rename to lib/Hp_IR/examples/simple/simple.ino diff --git a/lib/HeatpumpIR/keywords.txt b/lib/Hp_IR/keywords.txt similarity index 100% rename from lib/HeatpumpIR/keywords.txt rename to lib/Hp_IR/keywords.txt diff --git a/lib/HeatpumpIR/library.json b/lib/Hp_IR/library.json similarity index 100% rename from lib/HeatpumpIR/library.json rename to lib/Hp_IR/library.json diff --git a/lib/HeatpumpIR/library.properties b/lib/Hp_IR/library.properties similarity index 100% rename from lib/HeatpumpIR/library.properties rename to lib/Hp_IR/library.properties From 6b44fadf2958dc4c3c509008452e9bf51b022433 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 10:05:41 +0200 Subject: [PATCH 307/367] [HeatpumpIR] Make IRext builds fit again on ESP8266 --- platformio.ini | 2 - platformio_core_defs.ini | 449 ++++++++++++----------- platformio_esp82xx_envs.ini | 2 +- src/src/CustomBuild/define_plugin_sets.h | 20 + 4 files changed, 248 insertions(+), 225 deletions(-) diff --git a/platformio.ini b/platformio.ini index e7d4770853..3a029770a4 100644 --- a/platformio.ini +++ b/platformio.ini @@ -108,6 +108,4 @@ src_filter = +<*> -<.git/> -<.svn/> - - - QOUT > DIO > DOUT -; for lib_ldf_mode, see http://docs.platformio.org/en/latest/librarymanager/ldf.html;ldf - -; **** Frequently used build flags: -; Use custom.h file to override default settings for ESPeasy: -D USE_CUSTOM_H -; Set VCC mode to measure Vcc of ESP chip : -D FEATURE_ADC_VCC=1 - -; Build Flags: -; -DUSE_CONFIG_OVERRIDE -; lwIP 1.4 (Default) -; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH -; lwIP 2 - Low Memory -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY -; lwIP 2 - Higher Bandwitdh -; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH -; VTABLES in Flash (default) -; -DVTABLES_IN_FLASH -; VTABLES in Heap -; -DVTABLES_IN_DRAM -; VTABLES in IRAM -; -DVTABLES_IN_IRAM -; NO_EXTRA_4K_HEAP - this forces the default NONOS-SDK user's heap location -; Default currently overlaps cont stack (Arduino) with sys stack (System) -; to save up-to 4 kB of heap. (starting core_2.4.2) -; ESP8266_DISABLE_EXTRA4K - Calls disable_extra4k_at_link_time() from setup -; to force the linker keep user's stack in user ram. -; CONT_STACKSIZE to set the 'cont' (Arduino) stack size. Default = 4096 -; -mtarget-align see: https://github.com/arendst/Sonoff-Tasmota/issues/3678#issuecomment-419712437 - -[esp82xx_defaults] -build_flags = -D NDEBUG - -lstdc++ -lsupc++ - -mtarget-align - -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY - -DVTABLES_IN_FLASH - -DPUYA_SUPPORT=1 - -DDISABLE_SC16IS752_SPI - -DCRON_USE_LOCAL_TIME - -fno-strict-aliasing - -I$PROJECT_DIR/src/include - -include "ESPEasy_config.h" - -lib_ignore = ESP32_ping - ESP32WebServer - ESP32HTTPUpdateServer - ServoESP32 - IRremoteESP8266 - HeatpumpIR - TinyWireM - ESP8266SdFat - SD(esp8266) - SD - SDFS - LittleFS(esp8266) - LittleFS - ArduinoOTA - ESP8266mDNS - I2C AXP192 Power management -; EspSoftwareSerial - - - -; Keep optimization flag to -O2 -; See: https://github.com/platformio/platform-espressif8266/issues/288 -; For "-fno-strict-aliasing" -; See: https://github.com/esp8266/Arduino/issues/8261 -[esp82xx_2_7_x] -build_flags = -DNDEBUG - -mtarget-align - -DVTABLES_IN_FLASH - -fno-exceptions - -lstdc++ - -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH - -DPUYA_SUPPORT=1 - -DCORE_POST_2_5_0 - -DDISABLE_SC16IS752_SPI - -DCRON_USE_LOCAL_TIME - -fno-strict-aliasing - -DLIBRARIES_NO_LOG=1 - -DNO_GLOBAL_I2S - -I$PROJECT_DIR/src/include - -include "ESPEasy_config.h" - -O2 - -s - -DBEARSSL_SSL_BASIC - -DCORE_POST_2_6_0 - -DPR_9453_FLUSH_TO_CLEAR=flush - ; remove the 4-bytes alignment for PSTR() - -DPSTR_ALIGN=1 - -Werror=return-type -build_unflags = ${esp82xx_common.build_unflags} -lib_ignore = ${esp82xx_defaults.lib_ignore} - EspSoftwareSerial - - -[esp82xx_3_0_x] -build_flags = ${esp82xx_2_7_x.build_flags} - -DCORE_POST_3_0_0 - -Wno-deprecated-declarations -; -flto=auto -; -Wl,-flto -build_unflags = -DDEBUG_ESP_PORT - -fexceptions - -Wall -; -fno-lto -lib_ignore = ${esp82xx_defaults.lib_ignore} -extra_scripts = pre:tools/pio/pre_custom_esp8266_toolchain.py - - - -; See for SDK flags: https://github.com/esp8266/Arduino/blob/master/tools/platformio-build.py - -[core_2_7_4] -extends = esp82xx_2_7_x -platform = espressif8266@2.6.3 -platform_packages = - framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#2.7.4 -build_flags = ${esp82xx_2_7_x.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 - -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=0 - -Wno-deprecated-declarations - -DLIBRARIES_NO_LOG=1 -lib_ignore = ${esp82xx_2_7_x.lib_ignore} -build_unflags = ${esp82xx_2_7_x.build_unflags} -extra_scripts = ${esp82xx_common.extra_scripts} - - -[core_stage] -extends = esp82xx_3_0_x -platform = espressif8266@4.2.1 -platform_packages = -build_flags = ${esp82xx_3_0_x.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 - -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1 - -DLIBRARIES_NO_LOG=1 - -DFP_IN_IRAM - -DPHASE_LOCKED_WAVEFORM -build_unflags = ${esp82xx_3_0_x.build_unflags} -lib_ignore = ${esp82xx_defaults.lib_ignore} -extra_scripts = ${esp82xx_common.extra_scripts} - - - -; See: https://arduino-esp8266.readthedocs.io/en/latest/mmu.html -[core_stage_2ndheap] -extends = esp82xx_3_0_x -platform = espressif8266@4.2.1 -platform_packages = -build_flags = ${esp82xx_3_0_x.build_flags} - -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 - -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1 - -DLIBRARIES_NO_LOG=1 - -DPHASE_LOCKED_WAVEFORM - -DPIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED - -DUSE_SECOND_HEAP -build_unflags = ${esp82xx_3_0_x.build_unflags} -lib_ignore = ${core_stage.lib_ignore} -extra_scripts = ${esp82xx_common.extra_scripts} - - - -; Updated ESP-IDF to the latest stable 4.0.1 -; See: https://github.com/platformio/platform-espressif32/releases -; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4 -; Just for those who lost track of the extremely confusing numbering schema. -; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 -[core_esp32_IDF5_1_4__3_0_5_SPIFFS] -platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2962/framework-arduinoespressif32-all-release_v5.1-33fbade.zip -build_flags = -DESP32_STAGE - -DESP_IDF_VERSION_MAJOR=5 - -DLIBRARIES_NO_LOG=1 - -DDISABLE_SC16IS752_SPI - -DCONFIG_PM_ENABLE - -DESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED - -DPR_9453_FLUSH_TO_CLEAR=clear - - ;-DETH_TYPE_JL1101_SUPPORTED -; -DCONFIG_LWIP_L2_TO_L3_COPY -; -DETH_SPI_SUPPORTS_NO_IRQ=1 - -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 - -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 - -DNEOPIXEL_ESP32_RMT_DEFAULT - -DCRON_USE_LOCAL_TIME - -I$PROJECT_DIR/src/include - -include "sdkconfig.h" - -include "ESPEasy_config.h" - -include "esp32x_fixes.h" - -Wnull-dereference -lib_ignore = - -; ESP_IDF 5.3.1 -[core_esp32_IDF5_3_1__3_0_5_LittleFS] -platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2996/framework-arduinoespressif32-all-release_v5.3-22a3b096.zip -build_flags = -DESP32_STAGE - -DESP_IDF_VERSION_MAJOR=5 - -DLIBRARIES_NO_LOG=1 - -DDISABLE_SC16IS752_SPI - -DCONFIG_PM_ENABLE - -DETH_TYPE_JL1101_SUPPORTED - ; PR_9453_FLUSH_TO_CLEAR_REVERTED - -DPR_9453_FLUSH_TO_CLEAR=clear -; -DCONFIG_LWIP_L2_TO_L3_COPY -; -DETH_SPI_SUPPORTS_NO_IRQ=1 - -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 - -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 - -DNEOPIXEL_ESP32_RMT_DEFAULT - -DCRON_USE_LOCAL_TIME - -I$PROJECT_DIR/src/include - -include "sdkconfig.h" - -include "ESPEasy_config.h" - -include "esp32x_fixes.h" - -Wnull-dereference -lib_ignore = - -; Regarding the define PR_9453_FLUSH_TO_CLEAR -; See: -; https://github.com/espressif/arduino-esp32/pull/8871/files +; ********************************************************************* + +; **** Definition cheat sheet: +; board_build.flash_mode in terms of performance: QIO > QOUT > DIO > DOUT +; for lib_ldf_mode, see http://docs.platformio.org/en/latest/librarymanager/ldf.html;ldf + +; **** Frequently used build flags: +; Use custom.h file to override default settings for ESPeasy: -D USE_CUSTOM_H +; Set VCC mode to measure Vcc of ESP chip : -D FEATURE_ADC_VCC=1 + +; Build Flags: +; -DUSE_CONFIG_OVERRIDE +; lwIP 1.4 (Default) +; -DPIO_FRAMEWORK_ARDUINO_LWIP_HIGHER_BANDWIDTH +; lwIP 2 - Low Memory +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY +; lwIP 2 - Higher Bandwitdh +; -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH +; VTABLES in Flash (default) +; -DVTABLES_IN_FLASH +; VTABLES in Heap +; -DVTABLES_IN_DRAM +; VTABLES in IRAM +; -DVTABLES_IN_IRAM +; NO_EXTRA_4K_HEAP - this forces the default NONOS-SDK user's heap location +; Default currently overlaps cont stack (Arduino) with sys stack (System) +; to save up-to 4 kB of heap. (starting core_2.4.2) +; ESP8266_DISABLE_EXTRA4K - Calls disable_extra4k_at_link_time() from setup +; to force the linker keep user's stack in user ram. +; CONT_STACKSIZE to set the 'cont' (Arduino) stack size. Default = 4096 +; -mtarget-align see: https://github.com/arendst/Sonoff-Tasmota/issues/3678#issuecomment-419712437 + +[esp82xx_defaults] +build_flags = -D NDEBUG + -lstdc++ -lsupc++ + -mtarget-align + -DPIO_FRAMEWORK_ARDUINO_LWIP2_LOW_MEMORY + -DVTABLES_IN_FLASH + -DPUYA_SUPPORT=1 + -DDISABLE_SC16IS752_SPI + -DCRON_USE_LOCAL_TIME + -fno-strict-aliasing + -I$PROJECT_DIR/src/include + -include "ESPEasy_config.h" + +lib_ignore = ESP32_ping + ESP32WebServer + ESP32HTTPUpdateServer + ServoESP32 + IRremoteESP8266 + HeatpumpIR + TinyWireM + ESP8266SdFat + SD(esp8266) + SD + SDFS + LittleFS(esp8266) + LittleFS + ArduinoOTA + ESP8266mDNS + I2C AXP192 Power management +; EspSoftwareSerial + + + +; Keep optimization flag to -O2 +; See: https://github.com/platformio/platform-espressif8266/issues/288 +; For "-fno-strict-aliasing" +; See: https://github.com/esp8266/Arduino/issues/8261 +[esp82xx_2_7_x] +build_flags = -DNDEBUG + -mtarget-align + -DVTABLES_IN_FLASH + -fno-exceptions + -lstdc++ + -DPIO_FRAMEWORK_ARDUINO_LWIP2_HIGHER_BANDWIDTH_LOW_FLASH + -DPUYA_SUPPORT=1 + -DCORE_POST_2_5_0 + -DDISABLE_SC16IS752_SPI + -DCRON_USE_LOCAL_TIME + -fno-strict-aliasing + -DLIBRARIES_NO_LOG=1 + -DNO_GLOBAL_I2S + -I$PROJECT_DIR/src/include + -include "ESPEasy_config.h" + -O2 + -s + -DBEARSSL_SSL_BASIC + -DCORE_POST_2_6_0 + -DPR_9453_FLUSH_TO_CLEAR=flush + ; remove the 4-bytes alignment for PSTR() + -DPSTR_ALIGN=1 + -Werror=return-type +build_unflags = ${esp82xx_common.build_unflags} +lib_ignore = ${esp82xx_defaults.lib_ignore} + EspSoftwareSerial + + +[esp82xx_3_0_x] +build_flags = ${esp82xx_2_7_x.build_flags} + -DCORE_POST_3_0_0 + -Wno-deprecated-declarations +; -flto=auto +; -Wl,-flto +build_unflags = -DDEBUG_ESP_PORT + -fexceptions + -Wall +; -fno-lto +lib_ignore = ${esp82xx_defaults.lib_ignore} +extra_scripts = pre:tools/pio/pre_custom_esp8266_toolchain.py + + + +; See for SDK flags: https://github.com/esp8266/Arduino/blob/master/tools/platformio-build.py + +[core_2_7_4] +extends = esp82xx_2_7_x +platform = espressif8266@2.6.3 +platform_packages = + framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#2.7.4 +build_flags = ${esp82xx_2_7_x.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK22x_190703 + -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=0 + -Wno-deprecated-declarations + -DLIBRARIES_NO_LOG=1 +lib_ignore = ${esp82xx_2_7_x.lib_ignore} +build_unflags = ${esp82xx_2_7_x.build_unflags} +extra_scripts = ${esp82xx_common.extra_scripts} + + +[core_stage] +extends = esp82xx_3_0_x +platform = espressif8266@4.2.1 +platform_packages = +build_flags = ${esp82xx_3_0_x.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 + -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1 + -DLIBRARIES_NO_LOG=1 + -DFP_IN_IRAM + -DPHASE_LOCKED_WAVEFORM +build_unflags = ${esp82xx_3_0_x.build_unflags} +lib_ignore = ${esp82xx_defaults.lib_ignore} +extra_scripts = ${esp82xx_common.extra_scripts} + + + +; See: https://arduino-esp8266.readthedocs.io/en/latest/mmu.html +[core_stage_2ndheap] +extends = esp82xx_3_0_x +platform = espressif8266@4.2.1 +platform_packages = +build_flags = ${esp82xx_3_0_x.build_flags} + -DPIO_FRAMEWORK_ARDUINO_ESPRESSIF_SDK3 + -DUSES_LATEST_SOFTWARE_SERIAL_LIBRARY=1 + -DLIBRARIES_NO_LOG=1 + -DPHASE_LOCKED_WAVEFORM + -DPIO_FRAMEWORK_ARDUINO_MMU_CACHE16_IRAM48_SECHEAP_SHARED + -DUSE_SECOND_HEAP +build_unflags = ${esp82xx_3_0_x.build_unflags} +lib_ignore = ${core_stage.lib_ignore} +extra_scripts = ${esp82xx_common.extra_scripts} + + + +; Updated ESP-IDF to the latest stable 4.0.1 +; See: https://github.com/platformio/platform-espressif32/releases +; IDF 4.4 = platform-espressif32 3.4.x = espressif/arduino-esp32 tag 2.0.4 +; Just for those who lost track of the extremely confusing numbering schema. +; For MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS See: https://github.com/espressif/arduino-esp32/pull/6676 +[core_esp32_IDF5_1_4__3_0_5_SPIFFS] +platform = https://github.com/tasmota/platform-espressif32/releases/download/2024.09.10/platform-espressif32.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2962/framework-arduinoespressif32-all-release_v5.1-33fbade.zip +build_flags = -DESP32_STAGE + -DESP_IDF_VERSION_MAJOR=5 + -DLIBRARIES_NO_LOG=1 + -DDISABLE_SC16IS752_SPI + -DCONFIG_PM_ENABLE + -DESP_IDF_STILL_NEEDS_SPI_REGISTERS_FIXED + -DPR_9453_FLUSH_TO_CLEAR=clear + + ;-DETH_TYPE_JL1101_SUPPORTED +; -DCONFIG_LWIP_L2_TO_L3_COPY +; -DETH_SPI_SUPPORTS_NO_IRQ=1 + -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 + -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 + -DNEOPIXEL_ESP32_RMT_DEFAULT + -DCRON_USE_LOCAL_TIME + -I$PROJECT_DIR/src/include + -include "sdkconfig.h" + -include "ESPEasy_config.h" + -include "esp32x_fixes.h" + -Wnull-dereference +lib_ignore = +lib_extra_dirs = + lib/lib_ssl + + +; ESP_IDF 5.3.1 +[core_esp32_IDF5_3_1__3_0_5_LittleFS] +platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2996/framework-arduinoespressif32-all-release_v5.3-22a3b096.zip +build_flags = -DESP32_STAGE + -DESP_IDF_VERSION_MAJOR=5 + -DLIBRARIES_NO_LOG=1 + -DDISABLE_SC16IS752_SPI + -DCONFIG_PM_ENABLE + -DETH_TYPE_JL1101_SUPPORTED + ; PR_9453_FLUSH_TO_CLEAR_REVERTED + -DPR_9453_FLUSH_TO_CLEAR=clear +; -DCONFIG_LWIP_L2_TO_L3_COPY +; -DETH_SPI_SUPPORTS_NO_IRQ=1 + -DCONFIG_FREERTOS_USE_TICKLESS_IDLE=1 + -DCONFIG_FREERTOS_IDLE_TIME_BEFORE_SLEEP=3 + -DNEOPIXEL_ESP32_RMT_DEFAULT + -DCRON_USE_LOCAL_TIME + -I$PROJECT_DIR/src/include + -include "sdkconfig.h" + -include "ESPEasy_config.h" + -include "esp32x_fixes.h" + -Wnull-dereference +lib_ignore = +lib_extra_dirs = + lib/lib_ssl + +; Regarding the define PR_9453_FLUSH_TO_CLEAR +; See: +; https://github.com/espressif/arduino-esp32/pull/8871/files ; https://github.com/espressif/arduino-esp32/pull/9453/files \ No newline at end of file diff --git a/platformio_esp82xx_envs.ini b/platformio_esp82xx_envs.ini index d459c27b27..3b1f1cb064 100644 --- a/platformio_esp82xx_envs.ini +++ b/platformio_esp82xx_envs.ini @@ -444,7 +444,7 @@ extra_scripts = ${esp8266_4M2M.extra_scripts} extends = esp8266_4M2M platform = ${ir.platform} platform_packages = ${ir.platform_packages} -lib_ignore = ${ir.lib_ignore} +lib_ignore = ${ir.lib_ignore} build_flags = ${normal_ir_extended_no_rx.build_flags} ${esp8266_4M2M.build_flags} ${limited_build_size.build_flags} diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 14f6b5e813..7e1b4481c2 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -667,6 +667,17 @@ To create/register a plugin, you have to : #define NOTIFIER_SET_STABLE #define USES_P029 // Output - Domoticz MQTT Helper #define PLUGIN_SET_ONLY_TEMP_HUM + + #ifdef USES_P038 + #undef USES_P038 // NeoPixel + #endif + #ifdef USES_P043 + #undef USES_P043 // ClkOutput + #endif + #ifdef USES_P079 + #undef USES_P079 // Wemos Motoshield + #endif + #endif #ifdef PLUGIN_BUILD_IR_EXTENDED_NO_RX @@ -2097,12 +2108,21 @@ To create/register a plugin, you have to : // Disable few plugin(s) to make the build fit :/ #ifdef PLUGIN_BUILD_IR_EXTENDED_NO_RX + #ifdef USES_P038 + #undef USES_P038 // NeoPixel + #endif #ifdef USES_P039 #undef USES_P039 // Environment - Thermocouple #endif #ifdef USES_P040 #undef USES_P040 // RFID - ID12LA/RDM6300 #endif + #ifdef USES_P043 + #undef USES_P043 // ClkOutput + #endif + #ifdef USES_P079 + #undef USES_P079 // Wemos Motoshield + #endif #endif // ifdef PLUGIN_BUILD_IR_EXTENDED_NO_RX // EXPERIMENTAL (playground) ####################### From 0097f7f9c7168fb6ba235799abc26195e9afe26c Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 10:11:11 +0200 Subject: [PATCH 308/367] [Build] Add already a shorter folder name for core_dir for future builds --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a7d3d15ffb..b682fe50c1 100644 --- a/.gitignore +++ b/.gitignore @@ -71,3 +71,4 @@ docs/source/Plugin/_plugin_sets_overview.repl .platformio/ +.pio/ From bc56a2eafc3daee89fe4d41d385edabe56e6f116 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 15:27:10 +0200 Subject: [PATCH 309/367] [Email] Split N001_Email.ino into N001_data_struct --- src/_N001_Email.cpp | 425 +------------------ src/src/NotifierStructs/N001_data_struct.cpp | 407 ++++++++++++++++++ src/src/NotifierStructs/N001_data_struct.h | 38 ++ 3 files changed, 446 insertions(+), 424 deletions(-) create mode 100644 src/src/NotifierStructs/N001_data_struct.cpp create mode 100644 src/src/NotifierStructs/N001_data_struct.h diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index 36bf82b3dd..d57808f522 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -24,37 +24,7 @@ # define NPLUGIN_001_PKT_SZ 256 -# include "src/DataStructs/ESPEasy_EventStruct.h" -# include "src/DataStructs/NotificationSettingsStruct.h" -# include "src/ESPEasyCore/ESPEasy_Log.h" -# include "src/ESPEasyCore/ESPEasy_backgroundtasks.h" -# include "src/Globals/NPlugins.h" -# include "src/Globals/Settings.h" -# include "src/Helpers/ESPEasy_Storage.h" -# include "src/Helpers/ESPEasy_time_calc.h" -# include "src/Helpers/Networking.h" -# include "src/Helpers/StringGenerator_System.h" -# include "src/Helpers/StringParser.h" -# include "src/Helpers/_CPlugin_Helper.h" // safeReadStringUntil -# include "src/Helpers/_NPlugin_init.h" - -# include - -// Forward declaration -bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, - const String & aSub, - String & aMesg); -bool NPlugin_001_Auth(WiFiClient & client, - const String& user, - const String& pass, - uint16_t timeout); -bool NPlugin_001_MTA(WiFiClient & client, - const String& aStr, - uint16_t aWaitForPattern, - uint16_t timeout); -bool getNextMailAddress(const String& data, - String & address, - int index); +# include "src/NotifierStructs/N001_data_struct.h" // The message body is included in event->String1 @@ -122,397 +92,4 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String& return success; } -bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg) -{ - // String& aDomain , String aTo, String aFrom, String aSub, String aMesg, String aHost, int aPort) - bool myStatus = false; - bool failFlag = false; - - // Use WiFiClient class to create TCP connections - WiFiClient client; - - # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - - // See: https://github.com/espressif/arduino-esp32/pull/6676 - client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! - Client *pClient = &client; - pClient->setTimeout(notificationsettings.Timeout_ms); - # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! - # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat( - F("Email: Connecting to %s:%d"), - notificationsettings.Server, - notificationsettings.Port)); - } - # endif // ifndef BUILD_NO_DEBUG - - if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, notificationsettings.Timeout_ms)) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLog(LOG_LEVEL_ERROR, strformat( - F("Email: Error connecting to %s:%d"), - notificationsettings.Server, - notificationsettings.Port)); - } - myStatus = false; - failFlag = true; - } else { - uint16_t clientTimeout = notificationsettings.Timeout_ms; - - if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { - clientTimeout = NPLUGIN_001_DEF_TM; - } - - String email_address(notificationsettings.Sender); - int pos_less = email_address.indexOf('<'); - String senderName = Settings.getHostname(); - - if (pos_less > -1) { - senderName = email_address.substring(0, pos_less); - removeChar(senderName, '"'); // Remove quotes - email_address = email_address.substring(pos_less + 1); - removeChar(email_address, '<'); - removeChar(email_address, '>'); - email_address.trim(); - senderName.trim(); - } - - - // Use Notify Command's destination email address(s) if provided in Command rules. - // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
    How are you?
    Have a good day.
    " - String subAddr; - String tmp_ato; - int pos_brace1 = aMesg.indexOf('{'); - int pos_amper = aMesg.indexOf('@'); - int pos_brace2 = aMesg.indexOf('}'); - - if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { - subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); - subAddr.trim(); - tmp_ato = subAddr; - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); - } - # endif // ifndef BUILD_NO_DEBUG - - String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. - subMsg.trim(); - - if (subMsg.indexOf(',') == 0) { - subMsg = subMsg.substring(1); // Remove leading comma. - subMsg.trim(); - } - - if (!subMsg.length()) { - subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; - } - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); - } - # endif // ifndef BUILD_NO_DEBUG - aMesg = subMsg; - } - else { - tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. - } - - // Clean up receiver address. - tmp_ato.replace(";", ","); - tmp_ato.replace(" ", ""); - - String mailheader = F( - "From: $nodename <$emailfrom>\r\n" - "To: $ato\r\n" - "Subject: $subject\r\n" - "Reply-To: $nodename <$emailfrom>\r\n" - "Date: $date\r\n" - "MIME-VERSION: 1.0\r\n" - "Content-type: text/html; charset=UTF-8\r\n" - "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" - ); - - - mailheader.replace(F("$nodename"), senderName); - mailheader.replace(F("$emailfrom"), email_address); - mailheader.replace(F("$ato"), tmp_ato); - mailheader.replace(F("$subject"), aSub); - String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); - mailheader.replace(F("$date"), parseTemplate(dateFmtHdr)); - mailheader.replace(F("$espeasyversion"), getSystemBuildString()); - - // Make sure to replace the char '\r' and not the string "\r" - // See: https://github.com/letscontrolit/ESPEasy/issues/4967 - removeChar(aMesg, '\r'); - aMesg.replace(String('\n'), F("
    ")); // re-write line breaks for Content-type: text/html - - // Wait for Client to Start Sending - // The MTA Exchange - - if (!failFlag) { - addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); - - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); - # endif // ifndef BUILD_NO_DEBUG - - while (true) { // FIXME TD-er: Use of while here can be useful so you can - // exit using break; - // However this is way too complex using both a failFlag and break - // and not even consistently. - if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); - } - # endif // ifndef BUILD_NO_DEBUG - failFlag = true; - break; - } - - if (!failFlag) { - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); - # endif // ifndef BUILD_NO_DEBUG - - const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); - - if (!NPlugin_001_MTA( - client, - astr, - 250, - clientTimeout)) { - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); - # endif // ifndef BUILD_NO_DEBUG - failFlag = true; - } - } - - // Must retrieve SMTP Reply Packet. Data not used, ignored. - if (!failFlag) { - const unsigned long timer = millis() + clientTimeout; - String replyStr; - String catStr; - - bool done = false; - - while (client.available() && !done) { - if (timeOutReached(timer)) { - failFlag = true; - break; - } - done = safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ); - catStr += replyStr; - } - - if (!catStr.length()) { - catStr = F("Empty!"); - } - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); - addLogMove(LOG_LEVEL_DEBUG, log); - } - # endif // ifndef BUILD_NO_DEBUG - } - - if (!failFlag) { - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); - } - # endif // ifndef BUILD_NO_DEBUG - - if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { - # ifndef BUILD_NO_DEBUG - - addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); - # endif // ifndef BUILD_NO_DEBUG - failFlag = true; - break; - } - } - - if (!failFlag) { - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); - # endif // ifndef BUILD_NO_DEBUG - - const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); - - if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { - # ifndef BUILD_NO_DEBUG - addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); - # endif // ifndef BUILD_NO_DEBUG - failFlag = true; - break; - } - } - - if (!failFlag) { - bool nextAddressAvailable = true; - int i = 0; - String emailTo; - const String receiver(tmp_ato); - - addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"), receiver.c_str())); - - if (!getNextMailAddress(receiver, emailTo, i)) { - addLog(LOG_LEVEL_ERROR, F("Email: Receiver missing!")); - break; - } - - while (nextAddressAvailable) { - if (loglevelActiveFor(LOG_LEVEL_INFO)) { - addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); - } - - if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } - ++i; - nextAddressAvailable = getNextMailAddress(receiver, emailTo, i); - } - } - - if (!failFlag) { - if (!NPlugin_001_MTA(client, F("DATA"), 354, clientTimeout)) { - failFlag = true; - break; - } - } - - if (!failFlag) { - if (!NPlugin_001_MTA(client, strformat(F("%s%s\r\n.\r\n"), mailheader.c_str(), aMesg.c_str()), 250, clientTimeout)) { - failFlag = true; - break; - } - } - - // Email Sent. Do some final housekeeping, tell server we're leaving. - if (!failFlag) { - myStatus = true; - } - - NPlugin_001_MTA(client, F("QUIT"), 221, clientTimeout); // Sent successfully, close SMTP protocol, ignore failure - break; - } - } - client.PR_9453_FLUSH_TO_CLEAR(); - client.stop(); - - if (myStatus == true) { - addLog(LOG_LEVEL_INFO, F("Email: Connection Closed Successfully")); - } else { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLogMove(LOG_LEVEL_ERROR, concat(F("Email: Connection Closed With Error. Used header: "), mailheader)); - } - } - } - return myStatus; -} - -bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass, uint16_t timeout) -{ - if (user.isEmpty() || pass.isEmpty()) { - // No user/password given. - return true; - } - base64 encoder; - - if (NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout) && - NPlugin_001_MTA(client, encoder.encode(user), 334, timeout) && - NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { - addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); - return true; - } - return false; -} - -bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) -{ - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, aStr); - } - # endif // ifndef BUILD_NO_DEBUG - - if (aStr.length()) { - client.PR_9453_FLUSH_TO_CLEAR(); // have to send msg to server so flush data first - client.println(aStr); - } - - // Wait For Response - unsigned long timer = millis() + timeout; - - backgroundtasks(); - - while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once - if (timeOutReached(timer)) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLogMove(LOG_LEVEL_ERROR, - concat(F("NPlugin_001_MTA: timeout. "), aStr)); - } - break; - } - - delay(0); - - String line; - safeReadStringUntil(client, line, '\n', 1024, timeout); - - // response could be like: '220 domain', '220-domain','220+domain' - const String pattern_str_space = strformat(F("%d "), aWaitForPattern); - const String pattern_str_minus = strformat(F("%d-"), aWaitForPattern); - const String pattern_str_plus = strformat(F("%d+"), aWaitForPattern); - - const bool patternFound = line.indexOf(pattern_str_space) >= 0 - || line.indexOf(pattern_str_minus) >= 0 - || line.indexOf(pattern_str_plus) >= 0; - - # ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLogMove(LOG_LEVEL_DEBUG, line); - } - # endif // ifndef BUILD_NO_DEBUG - - return patternFound; - } - - return false; -} - -bool getNextMailAddress(const String& data, String& address, int index) -{ - int found = 0; - int strIndex[] = { 0, -1 }; - const int maxIndex = data.length() - 1; - - for (int i = 0; i <= maxIndex && found <= index; i++) { - if ((data.charAt(i) == ',') || (i == maxIndex)) { - found++; - strIndex[0] = strIndex[1] + 1; - strIndex[1] = (i == maxIndex) ? i + 1 : i; - } - } - - if (found > index) { - address = data.substring(strIndex[0], strIndex[1]); - return true; - } - return false; -} - #endif // ifdef USES_N001 diff --git a/src/src/NotifierStructs/N001_data_struct.cpp b/src/src/NotifierStructs/N001_data_struct.cpp new file mode 100644 index 0000000000..bdf86177de --- /dev/null +++ b/src/src/NotifierStructs/N001_data_struct.cpp @@ -0,0 +1,407 @@ +#include "../NotifierStructs/N001_data_struct.h" + +#include "../DataStructs/ESPEasy_EventStruct.h" +#include "../ESPEasyCore/ESPEasy_Log.h" +#include "../ESPEasyCore/ESPEasy_backgroundtasks.h" +#include "../Globals/Settings.h" +#include "../Helpers/ESPEasy_Storage.h" +#include "../Helpers/ESPEasy_time_calc.h" +#include "../Helpers/Networking.h" +#include "../Helpers/StringGenerator_System.h" +#include "../Helpers/StringParser.h" +#include "../Helpers/_CPlugin_Helper.h" // safeReadStringUntil +#include "../Helpers/_NPlugin_init.h" + + +bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg) +{ + // String& aDomain , String aTo, String aFrom, String aSub, String aMesg, String aHost, int aPort) + bool myStatus = false; + bool failFlag = false; + + // Use WiFiClient class to create TCP connections + WiFiClient client; + + #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + + // See: https://github.com/espressif/arduino-esp32/pull/6676 + client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! + Client *pClient = &client; + pClient->setTimeout(notificationsettings.Timeout_ms); + #else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! + #endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("Email: Connecting to %s:%d"), + notificationsettings.Server, + notificationsettings.Port)); + } + #endif // ifndef BUILD_NO_DEBUG + + if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, notificationsettings.Timeout_ms)) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLog(LOG_LEVEL_ERROR, strformat( + F("Email: Error connecting to %s:%d"), + notificationsettings.Server, + notificationsettings.Port)); + } + myStatus = false; + failFlag = true; + } else { + uint16_t clientTimeout = notificationsettings.Timeout_ms; + + if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { + clientTimeout = NPLUGIN_001_DEF_TM; + } + + String email_address(notificationsettings.Sender); + int pos_less = email_address.indexOf('<'); + String senderName = Settings.getHostname(); + + if (pos_less > -1) { + senderName = email_address.substring(0, pos_less); + removeChar(senderName, '"'); // Remove quotes + email_address = email_address.substring(pos_less + 1); + removeChar(email_address, '<'); + removeChar(email_address, '>'); + email_address.trim(); + senderName.trim(); + } + + + // Use Notify Command's destination email address(s) if provided in Command rules. + // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
    How are you?
    Have a good day.
    " + String subAddr; + String tmp_ato; + int pos_brace1 = aMesg.indexOf('{'); + int pos_amper = aMesg.indexOf('@'); + int pos_brace2 = aMesg.indexOf('}'); + + if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { + subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); + subAddr.trim(); + tmp_ato = subAddr; + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); + } + #endif // ifndef BUILD_NO_DEBUG + + String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. + subMsg.trim(); + + if (subMsg.indexOf(',') == 0) { + subMsg = subMsg.substring(1); // Remove leading comma. + subMsg.trim(); + } + + if (!subMsg.length()) { + subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; + } + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); + } + #endif // ifndef BUILD_NO_DEBUG + aMesg = subMsg; + } + else { + tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. + } + + // Clean up receiver address. + tmp_ato.replace(";", ","); + tmp_ato.replace(" ", ""); + + String mailheader = F( + "From: $nodename <$emailfrom>\r\n" + "To: $ato\r\n" + "Subject: $subject\r\n" + "Reply-To: $nodename <$emailfrom>\r\n" + "Date: $date\r\n" + "MIME-VERSION: 1.0\r\n" + "Content-type: text/html; charset=UTF-8\r\n" + "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" + ); + + + mailheader.replace(F("$nodename"), senderName); + mailheader.replace(F("$emailfrom"), email_address); + mailheader.replace(F("$ato"), tmp_ato); + mailheader.replace(F("$subject"), aSub); + String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); + mailheader.replace(F("$date"), parseTemplate(dateFmtHdr)); + mailheader.replace(F("$espeasyversion"), getSystemBuildString()); + + // Make sure to replace the char '\r' and not the string "\r" + // See: https://github.com/letscontrolit/ESPEasy/issues/4967 + removeChar(aMesg, '\r'); + aMesg.replace(String('\n'), F("
    ")); // re-write line breaks for Content-type: text/html + + // Wait for Client to Start Sending + // The MTA Exchange + + if (!failFlag) { + addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); + + #ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); + #endif // ifndef BUILD_NO_DEBUG + + while (true) { // FIXME TD-er: Use of while here can be useful so you can + // exit using break; + // However this is way too complex using both a failFlag and break + // and not even consistently. + if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); + } + #endif // ifndef BUILD_NO_DEBUG + failFlag = true; + break; + } + + if (!failFlag) { + #ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); + #endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); + + if (!NPlugin_001_MTA( + client, + astr, + 250, + clientTimeout)) { + #ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); + #endif // ifndef BUILD_NO_DEBUG + failFlag = true; + } + } + + // Must retrieve SMTP Reply Packet. Data not used, ignored. + if (!failFlag) { + const unsigned long timer = millis() + clientTimeout; + String replyStr; + String catStr; + + bool done = false; + + while (client.available() && !done) { + if (timeOutReached(timer)) { + failFlag = true; + break; + } + done = safeReadStringUntil(client, replyStr, '\n', NPLUGIN_001_PKT_SZ); + catStr += replyStr; + } + + if (!catStr.length()) { + catStr = F("Empty!"); + } + + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); + addLogMove(LOG_LEVEL_DEBUG, log); + } + #endif // ifndef BUILD_NO_DEBUG + } + + if (!failFlag) { + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); + } + #endif // ifndef BUILD_NO_DEBUG + + if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { + #ifndef BUILD_NO_DEBUG + + addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); + #endif // ifndef BUILD_NO_DEBUG + failFlag = true; + break; + } + } + + if (!failFlag) { + #ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); + #endif // ifndef BUILD_NO_DEBUG + + const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); + + if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { + #ifndef BUILD_NO_DEBUG + addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); + #endif // ifndef BUILD_NO_DEBUG + failFlag = true; + break; + } + } + + if (!failFlag) { + bool nextAddressAvailable = true; + int i = 0; + String emailTo; + const String receiver(tmp_ato); + + addLog(LOG_LEVEL_INFO, strformat(F("Email: Receiver(s): %s"), receiver.c_str())); + + if (!getNextMailAddress(receiver, emailTo, i)) { + addLog(LOG_LEVEL_ERROR, F("Email: Receiver missing!")); + break; + } + + while (nextAddressAvailable) { + if (loglevelActiveFor(LOG_LEVEL_INFO)) { + addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); + } + + if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } + ++i; + nextAddressAvailable = getNextMailAddress(receiver, emailTo, i); + } + } + + if (!failFlag) { + if (!NPlugin_001_MTA(client, F("DATA"), 354, clientTimeout)) { + failFlag = true; + break; + } + } + + if (!failFlag) { + if (!NPlugin_001_MTA(client, strformat(F("%s%s\r\n.\r\n"), mailheader.c_str(), aMesg.c_str()), 250, clientTimeout)) { + failFlag = true; + break; + } + } + + // Email Sent. Do some final housekeeping, tell server we're leaving. + if (!failFlag) { + myStatus = true; + } + + NPlugin_001_MTA(client, F("QUIT"), 221, clientTimeout); // Sent successfully, close SMTP protocol, ignore failure + break; + } + } + client.PR_9453_FLUSH_TO_CLEAR(); + client.stop(); + + if (myStatus == true) { + addLog(LOG_LEVEL_INFO, F("Email: Connection Closed Successfully")); + } else { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLogMove(LOG_LEVEL_ERROR, concat(F("Email: Connection Closed With Error. Used header: "), mailheader)); + } + } + } + return myStatus; +} + +bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass, uint16_t timeout) +{ + if (user.isEmpty() || pass.isEmpty()) { + // No user/password given. + return true; + } + base64 encoder; + + if (NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(user), 334, timeout) && + NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { + addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); + return true; + } + return false; +} + +bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) +{ + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, aStr); + } + #endif // ifndef BUILD_NO_DEBUG + + if (aStr.length()) { + client.PR_9453_FLUSH_TO_CLEAR(); // have to send msg to server so flush data first + client.println(aStr); + } + + // Wait For Response + unsigned long timer = millis() + timeout; + + backgroundtasks(); + + while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once + if (timeOutReached(timer)) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLogMove(LOG_LEVEL_ERROR, + concat(F("NPlugin_001_MTA: timeout. "), aStr)); + } + break; + } + + delay(0); + + String line; + safeReadStringUntil(client, line, '\n', 1024, timeout); + + // response could be like: '220 domain', '220-domain','220+domain' + const String pattern_str_space = strformat(F("%d "), aWaitForPattern); + const String pattern_str_minus = strformat(F("%d-"), aWaitForPattern); + const String pattern_str_plus = strformat(F("%d+"), aWaitForPattern); + + const bool patternFound = line.indexOf(pattern_str_space) >= 0 + || line.indexOf(pattern_str_minus) >= 0 + || line.indexOf(pattern_str_plus) >= 0; + + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLogMove(LOG_LEVEL_DEBUG, line); + } + #endif // ifndef BUILD_NO_DEBUG + + return patternFound; + } + + return false; +} + +bool getNextMailAddress(const String& data, String& address, int index) +{ + int found = 0; + int strIndex[] = { 0, -1 }; + const int maxIndex = data.length() - 1; + + for (int i = 0; i <= maxIndex && found <= index; i++) { + if ((data.charAt(i) == ',') || (i == maxIndex)) { + found++; + strIndex[0] = strIndex[1] + 1; + strIndex[1] = (i == maxIndex) ? i + 1 : i; + } + } + + if (found > index) { + address = data.substring(strIndex[0], strIndex[1]); + return true; + } + return false; +} diff --git a/src/src/NotifierStructs/N001_data_struct.h b/src/src/NotifierStructs/N001_data_struct.h new file mode 100644 index 0000000000..4cddc6e299 --- /dev/null +++ b/src/src/NotifierStructs/N001_data_struct.h @@ -0,0 +1,38 @@ +#ifndef NOTIFIERSTRUCTS_N001_DATA_STRUCT_H +#define NOTIFIERSTRUCTS_N001_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" + +#ifdef USES_N001 + +// FIXME TD-er: Must we add a complete inheritance structure like done with pluginTaskData? +// We do not have multiple instances of these notifiers, so let's keep it simple. + + +# define NPLUGIN_001_PKT_SZ 256 + +# include + +# include "../DataStructs/NotificationSettingsStruct.h" +# include "../Globals/NPlugins.h" + + +bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, + const String & aSub, + String & aMesg); +bool NPlugin_001_Auth(WiFiClient & client, + const String& user, + const String& pass, + uint16_t timeout); +bool NPlugin_001_MTA(WiFiClient & client, + const String& aStr, + uint16_t aWaitForPattern, + uint16_t timeout); +bool getNextMailAddress(const String& data, + String & address, + int index); + + +#endif // ifdef USES_N001 + +#endif // ifndef NOTIFIERSTRUCTS_N001_DATA_STRUCT_H From d9d5c1b71497f31e55e54bb9c90830033166e90c Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 23:16:32 +0200 Subject: [PATCH 310/367] [Email] Add SSL support for email --- src/src/CustomBuild/define_plugin_sets.h | 24 ++++ src/src/DataTypes/TLS_types.cpp | 2 +- src/src/DataTypes/TLS_types.h | 2 +- src/src/Helpers/ESPEasy_Storage.cpp | 2 +- src/src/Helpers/ESPEasy_Storage.h | 2 +- src/src/Helpers/Networking.cpp | 17 +++ src/src/NotifierStructs/N001_data_struct.cpp | 128 ++++++++++++------- src/src/WebServer/NotificationPage.cpp | 11 +- tools/pio/pre_custom_esp32.py | 1 + tools/pio/pre_custom_esp32_IR.py | 1 + tools/pio/pre_custom_esp32c2.py | 1 + tools/pio/pre_custom_esp32c6.py | 1 + tools/pio/pre_custom_esp82xx.py | 1 + 13 files changed, 139 insertions(+), 54 deletions(-) diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index 7e1b4481c2..b71e4b24b5 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1129,6 +1129,7 @@ To create/register a plugin, you have to : #define FEATURE_RULES_EASY_COLOR_CODE 1 #endif #define FEATURE_MQTT_TLS 1 + #define FEATURE_EMAIL_TLS 1 #ifdef FEATURE_CUSTOM_PROVISIONING #undef FEATURE_CUSTOM_PROVISIONING @@ -2238,6 +2239,9 @@ To create/register a plugin, you have to : #ifndef FEATURE_MQTT_TLS #define FEATURE_MQTT_TLS 1 #endif + #ifndef FEATURE_EMAIL_TLS + #define FEATURE_EMAIL_TLS 1 + #endif // Plugins #ifndef USES_P016 @@ -2981,6 +2985,11 @@ To create/register a plugin, you have to : #define FEATURE_MQTT_TLS 0 #endif +#ifndef FEATURE_EMAIL_TLS +#define FEATURE_EMAIL_TLS 0 +#endif + + #ifdef ESP8266 // It just doesn't work on ESP8266, too slow, too high memory requirements //#if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) @@ -2988,6 +2997,10 @@ To create/register a plugin, you have to : #undef FEATURE_MQTT_TLS #define FEATURE_MQTT_TLS 0 #endif + #if FEATURE_EMAIL_TLS + #undef FEATURE_EMAIL_TLS + #define FEATURE_EMAIL_TLS 0 + #endif #endif #if FEATURE_MQTT_TLS @@ -2999,6 +3012,17 @@ To create/register a plugin, you have to : #endif #endif + +#if FEATURE_EMAIL_TLS + #if defined(FEATURE_TLS) && !FEATURE_TLS + #undef FEATURE_TLS + #endif + #ifndef FEATURE_TLS + #define FEATURE_TLS 1 + #endif +#endif + + #ifdef USES_ESPEASY_NOW #if defined(LIMIT_BUILD_SIZE) || defined(ESP8266_1M) || (defined(ESP8266) && defined(PLUGIN_BUILD_IR)) // Will not fit on ESP8266 along with IR plugins included diff --git a/src/src/DataTypes/TLS_types.cpp b/src/src/DataTypes/TLS_types.cpp index 5e4f65d2ac..fd1abfb182 100644 --- a/src/src/DataTypes/TLS_types.cpp +++ b/src/src/DataTypes/TLS_types.cpp @@ -1,6 +1,6 @@ #include "../DataTypes/TLS_types.h" -#if FEATURE_MQTT_TLS +#if FEATURE_TLS const __FlashStringHelper* toString(TLS_types tls_type) { switch (tls_type) { diff --git a/src/src/DataTypes/TLS_types.h b/src/src/DataTypes/TLS_types.h index e21bc2cb52..4739d85061 100644 --- a/src/src/DataTypes/TLS_types.h +++ b/src/src/DataTypes/TLS_types.h @@ -3,7 +3,7 @@ #include "../../ESPEasy_common.h" -#if FEATURE_MQTT_TLS +#if FEATURE_TLS #include #include diff --git a/src/src/Helpers/ESPEasy_Storage.cpp b/src/src/Helpers/ESPEasy_Storage.cpp index 18e620a160..77603adc1f 100644 --- a/src/src/Helpers/ESPEasy_Storage.cpp +++ b/src/src/Helpers/ESPEasy_Storage.cpp @@ -1556,7 +1556,7 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int Handle certificate files on the file system. The content will be stripped from unusable character like quotes, spaces etc. \*********************************************************************************************/ -#if FEATURE_MQTT_TLS +#if FEATURE_TLS static inline bool is_base64(char c) { return (isalnum(c) || (c == '+') || (c == '/')); } diff --git a/src/src/Helpers/ESPEasy_Storage.h b/src/src/Helpers/ESPEasy_Storage.h index 48dbbc31a7..8450168808 100644 --- a/src/src/Helpers/ESPEasy_Storage.h +++ b/src/src/Helpers/ESPEasy_Storage.h @@ -273,7 +273,7 @@ String LoadNotificationSettings(int NotificationIndex, uint8_t *memAddress, int Handle certificate files on the file system. The content will be stripped from unusable character like quotes, spaces etc. \*********************************************************************************************/ -#if FEATURE_MQTT_TLS +#if FEATURE_TLS String SaveCertificate(const String& fname, const String& certificate); String LoadCertificate(const String& fname, String& certificate, bool cleanup = true); #endif diff --git a/src/src/Helpers/Networking.cpp b/src/src/Helpers/Networking.cpp index 426b977a8e..d18cb7c81a 100644 --- a/src/src/Helpers/Networking.cpp +++ b/src/src/Helpers/Networking.cpp @@ -1090,6 +1090,14 @@ bool connectClient(WiFiClient& client, IPAddress ip, uint16_t port, uint32_t tim client.stop(); return false; } +#ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_DEBUG, strformat( + F("connectClient: '%s' port: %u"), + ip.toString().c_str(), + port)); + } +#endif // In case of domain name resolution error result can be negative. // https://github.com/esp8266/Arduino/blob/18f643c7e2d6a0da9d26ff2b14c94e6536ab78c1/libraries/Ethernet/src/Dns.cpp#L44 @@ -1099,6 +1107,15 @@ bool connectClient(WiFiClient& client, IPAddress ip, uint16_t port, uint32_t tim delay(0); if (!connected) { +#ifndef BUILD_NO_DEBUG + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLog(LOG_LEVEL_ERROR, strformat( + F("connectClient: connect failed to '%s' port: %u"), + ip.toString().c_str(), + port)); + } +#endif + Scheduler.sendGratuitousARP_now(); client.stop(); // Make sure to start over without some stale connection } diff --git a/src/src/NotifierStructs/N001_data_struct.cpp b/src/src/NotifierStructs/N001_data_struct.cpp index bdf86177de..57d05dcde3 100644 --- a/src/src/NotifierStructs/N001_data_struct.cpp +++ b/src/src/NotifierStructs/N001_data_struct.cpp @@ -12,6 +12,12 @@ #include "../Helpers/_CPlugin_Helper.h" // safeReadStringUntil #include "../Helpers/_NPlugin_init.h" +#if FEATURE_EMAIL_TLS + + # include + # include "../CustomBuild/Certificate_CA.h" + +#endif // if FEATURE_EMAIL_TLS bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg) { @@ -19,8 +25,21 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co bool myStatus = false; bool failFlag = false; +#if FEATURE_EMAIL_TLS + + // values are based on the NPLUGIN_001_PKT_SZ + BearSSL::WiFiClientSecure_light client(4096, 4096); + client.setUtcTime_fcn(getUnixTime); + client.setCfgTime_fcn(get_build_unixtime); + client.setTrustAnchor(Tasmota_TA, Tasmota_TA_size); + + client.setInsecure(); + +#else // if FEATURE_EMAIL_TLS + // Use WiFiClient class to create TCP connections WiFiClient client; +#endif // if FEATURE_EMAIL_TLS #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS @@ -58,6 +77,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co clientTimeout = NPLUGIN_001_DEF_TM; } + addLog(LOG_LEVEL_DEBUG, concat(F("NPlugin_001_send: timeout: "), clientTimeout)); + String email_address(notificationsettings.Sender); int pos_less = email_address.indexOf('<'); String senderName = Settings.getHostname(); @@ -119,25 +140,28 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co tmp_ato.replace(";", ","); tmp_ato.replace(" ", ""); - String mailheader = F( - "From: $nodename <$emailfrom>\r\n" - "To: $ato\r\n" - "Subject: $subject\r\n" - "Reply-To: $nodename <$emailfrom>\r\n" - "Date: $date\r\n" - "MIME-VERSION: 1.0\r\n" - "Content-type: text/html; charset=UTF-8\r\n" - "X-Mailer: EspEasy v$espeasyversion\r\n\r\n" - ); - - - mailheader.replace(F("$nodename"), senderName); - mailheader.replace(F("$emailfrom"), email_address); - mailheader.replace(F("$ato"), tmp_ato); - mailheader.replace(F("$subject"), aSub); - String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); - mailheader.replace(F("$date"), parseTemplate(dateFmtHdr)); - mailheader.replace(F("$espeasyversion"), getSystemBuildString()); + + const String nodename_emailfrom = strformat(F("%s <%s>"), senderName.c_str(), email_address.c_str()); + String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); + + const String mailheader = strformat( + F( + "From: %s\r\n" + "To: %s\r\n" + "Subject: %s\r\n" + "Reply-To: %s\r\n" + "Date: %s\r\n" + "MIME-VERSION: 1.0\r\n" + "Content-type: text/html; charset=UTF-8\r\n" + "X-Mailer: EspEasy v%s\r\n\r\n" + ), + nodename_emailfrom.c_str(), + tmp_ato.c_str(), + aSub.c_str(), + nodename_emailfrom.c_str(), + parseTemplate(dateFmtHdr).c_str(), + getSystemBuildString().c_str()); + // Make sure to replace the char '\r' and not the string "\r" // See: https://github.com/letscontrolit/ESPEasy/issues/4967 @@ -270,7 +294,10 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co addLog(LOG_LEVEL_INFO, concat(F("Email: To "), emailTo)); } - if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) { break; } + if (!NPlugin_001_MTA(client, strformat(F("RCPT TO:<%s>"), emailTo.c_str()), 250, clientTimeout)) + { + break; + } ++i; nextAddressAvailable = getNextMailAddress(receiver, emailTo, i); } @@ -299,7 +326,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co break; } } - client.PR_9453_FLUSH_TO_CLEAR(); + + // client.PR_9453_FLUSH_TO_CLEAR(); client.stop(); if (myStatus == true) { @@ -321,13 +349,18 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass } base64 encoder; - if (NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout) && - NPlugin_001_MTA(client, encoder.encode(user), 334, timeout) && - NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { + bool success = true; + + if (!NPlugin_001_MTA(client, F("AUTH LOGIN"), 334, timeout)) { success = false; } + + if (!NPlugin_001_MTA(client, encoder.encode(user), 334, timeout)) { success = false; } + + if (!NPlugin_001_MTA(client, encoder.encode(pass), 235, timeout)) { success = false; } + + if (success) { addLog(LOG_LEVEL_INFO, F("Email: Credentials Accepted")); - return true; } - return false; + return success; } bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) @@ -335,7 +368,11 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa #ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLog(LOG_LEVEL_DEBUG, aStr); + addLog(LOG_LEVEL_DEBUG, strformat( + F("NPlugin_001_MTA: Waitfor: %u Timeout: %u ms Send: '%s'"), + aWaitForPattern, + timeout, + aStr.c_str())); } #endif // ifndef BUILD_NO_DEBUG @@ -345,23 +382,22 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa } // Wait For Response - unsigned long timer = millis() + timeout; - + const unsigned long timer = millis() + timeout; backgroundtasks(); - while (true) { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once - if (timeOutReached(timer)) { - if (loglevelActiveFor(LOG_LEVEL_ERROR)) { - addLogMove(LOG_LEVEL_ERROR, - concat(F("NPlugin_001_MTA: timeout. "), aStr)); - } - break; - } - + do { // FIXME TD-er: Why this while loop??? makes no sense as it will only be run once delay(0); String line; - safeReadStringUntil(client, line, '\n', 1024, timeout); + safeReadStringUntil(client, line, '\n', 1024); // , timeout); + + #ifndef BUILD_NO_DEBUG + + if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { + addLogMove(LOG_LEVEL_DEBUG, concat(F("NPlugin_001_MTA: read line: "), line)); + } + #endif // ifndef BUILD_NO_DEBUG + // response could be like: '220 domain', '220-domain','220+domain' const String pattern_str_space = strformat(F("%d "), aWaitForPattern); @@ -372,14 +408,16 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa || line.indexOf(pattern_str_minus) >= 0 || line.indexOf(pattern_str_plus) >= 0; - #ifndef BUILD_NO_DEBUG - - if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { - addLogMove(LOG_LEVEL_DEBUG, line); + if (patternFound) { + return true; } - #endif // ifndef BUILD_NO_DEBUG + } while (!timeOutReached(timer)); - return patternFound; + if (timeOutReached(timer)) { + if (loglevelActiveFor(LOG_LEVEL_ERROR)) { + addLogMove(LOG_LEVEL_ERROR, + concat(F("NPlugin_001_MTA: timeout. "), aStr)); + } } return false; diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index e76cc74948..9c9f3bd5c4 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -224,11 +224,12 @@ void handle_notifications() { F("Port"), F("port"), NotificationSettings.Port, 1, - 65535 -# if FEATURE_TOOLTIPS - , F("NOTE: SSL/TLS servers NOT supported!") -# endif // if FEATURE_TOOLTIPS - ); + 65535); +#if FEATURE_EMAIL_TLS + addFormNote(F("default port SSL: 465, TLS: 587")); +#else + addFormNote(F("SSL/TLS servers NOT supported!")); +#endif if ((NotificationSettings.Timeout_ms < NPLUGIN_001_MIN_TM) || (NotificationSettings.Timeout_ms > NPLUGIN_001_MAX_TM)) diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index 51e4a639cc..b56c7a5cdc 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -64,6 +64,7 @@ # "-DUSES_C015", # Blynk "-DFEATURE_MQTT_TLS=1", + "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp32_IR.py b/tools/pio/pre_custom_esp32_IR.py index eae182217b..615cf41e46 100644 --- a/tools/pio/pre_custom_esp32_IR.py +++ b/tools/pio/pre_custom_esp32_IR.py @@ -61,6 +61,7 @@ # "-DUSES_C015", # Blynk "-DFEATURE_MQTT_TLS=1", + "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp32c2.py b/tools/pio/pre_custom_esp32c2.py index 16af0041c4..b9957757ec 100644 --- a/tools/pio/pre_custom_esp32c2.py +++ b/tools/pio/pre_custom_esp32c2.py @@ -60,6 +60,7 @@ # "-DUSES_C015", # Blynk "-DFEATURE_MQTT_TLS=1", + "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp32c6.py b/tools/pio/pre_custom_esp32c6.py index f951e5ec48..9e77508ed6 100644 --- a/tools/pio/pre_custom_esp32c6.py +++ b/tools/pio/pre_custom_esp32c6.py @@ -62,6 +62,7 @@ # "-DUSES_C015", # Blynk "-DFEATURE_MQTT_TLS=1", + "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", "-DFEATURE_SD=1", "-DFEATURE_I2CMULTIPLEXER=1", diff --git a/tools/pio/pre_custom_esp82xx.py b/tools/pio/pre_custom_esp82xx.py index 8d5dde7cdb..39600161b1 100644 --- a/tools/pio/pre_custom_esp82xx.py +++ b/tools/pio/pre_custom_esp82xx.py @@ -60,6 +60,7 @@ # "-DUSES_C015", # Blynk "-DFEATURE_MQTT_TLS=0", + "-DFEATURE_EMAIL_TLS=0", # "-DFEATURE_MDNS=1", # "-DFEATURE_SD=1", "-DFEATURE_EXT_RTC=1", From 0ca00d2c8b95f1d93752b919419a49fd3c30d974 Mon Sep 17 00:00:00 2001 From: TD-er Date: Thu, 3 Oct 2024 23:46:56 +0200 Subject: [PATCH 311/367] [Email] Fix builds + include N001 in all ESP32-xx custom builds --- src/src/NotifierStructs/N001_data_struct.cpp | 3 +++ tools/pio/pre_custom_esp32.py | 2 ++ tools/pio/pre_custom_esp32_IR.py | 2 ++ tools/pio/pre_custom_esp32c2.py | 2 ++ tools/pio/pre_custom_esp32c6.py | 2 ++ 5 files changed, 11 insertions(+) diff --git a/src/src/NotifierStructs/N001_data_struct.cpp b/src/src/NotifierStructs/N001_data_struct.cpp index 57d05dcde3..b7b3ef0250 100644 --- a/src/src/NotifierStructs/N001_data_struct.cpp +++ b/src/src/NotifierStructs/N001_data_struct.cpp @@ -1,5 +1,6 @@ #include "../NotifierStructs/N001_data_struct.h" +#ifdef USES_N001 #include "../DataStructs/ESPEasy_EventStruct.h" #include "../ESPEasyCore/ESPEasy_Log.h" #include "../ESPEasyCore/ESPEasy_backgroundtasks.h" @@ -443,3 +444,5 @@ bool getNextMailAddress(const String& data, String& address, int index) } return false; } + +#endif \ No newline at end of file diff --git a/tools/pio/pre_custom_esp32.py b/tools/pio/pre_custom_esp32.py index b56c7a5cdc..0061629ce7 100644 --- a/tools/pio/pre_custom_esp32.py +++ b/tools/pio/pre_custom_esp32.py @@ -63,6 +63,8 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DUSES_N001", # E-mail + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", diff --git a/tools/pio/pre_custom_esp32_IR.py b/tools/pio/pre_custom_esp32_IR.py index 615cf41e46..6bf934431e 100644 --- a/tools/pio/pre_custom_esp32_IR.py +++ b/tools/pio/pre_custom_esp32_IR.py @@ -60,6 +60,8 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DUSES_N001", # E-mail + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", diff --git a/tools/pio/pre_custom_esp32c2.py b/tools/pio/pre_custom_esp32c2.py index b9957757ec..cc470b7179 100644 --- a/tools/pio/pre_custom_esp32c2.py +++ b/tools/pio/pre_custom_esp32c2.py @@ -59,6 +59,8 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DUSES_N001", # E-mail + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", diff --git a/tools/pio/pre_custom_esp32c6.py b/tools/pio/pre_custom_esp32c6.py index 9e77508ed6..17b0a96325 100644 --- a/tools/pio/pre_custom_esp32c6.py +++ b/tools/pio/pre_custom_esp32c6.py @@ -61,6 +61,8 @@ "-DUSES_C018", # TTN/RN2483 # "-DUSES_C015", # Blynk + "-DUSES_N001", # E-mail + "-DFEATURE_MQTT_TLS=1", "-DFEATURE_EMAIL_TLS=1", "-DFEATURE_EXT_RTC=1", From 048fb8bd0999d6cf47ee64868ce5ec05143ed683 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 4 Oct 2024 00:43:33 +0200 Subject: [PATCH 312/367] [Build] Fix builds which do not have DEBUG log level included --- src/src/NotifierStructs/N001_data_struct.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/src/NotifierStructs/N001_data_struct.cpp b/src/src/NotifierStructs/N001_data_struct.cpp index b7b3ef0250..4350636503 100644 --- a/src/src/NotifierStructs/N001_data_struct.cpp +++ b/src/src/NotifierStructs/N001_data_struct.cpp @@ -77,8 +77,9 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; } - +#ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, concat(F("NPlugin_001_send: timeout: "), clientTimeout)); +#endif String email_address(notificationsettings.Sender); int pos_less = email_address.indexOf('<'); From 7e04a8cec65827b63e52e6dc4c22b59d6cfa2dc6 Mon Sep 17 00:00:00 2001 From: TD-er Date: Fri, 4 Oct 2024 14:15:06 +0200 Subject: [PATCH 313/367] [Email] Clean-up code --- platformio_core_defs.ini | 2 +- platformio_esp32_solo1.ini | 2 +- src/_N001_Email.cpp | 2 - src/src/NotifierStructs/N001_data_struct.cpp | 190 ++++++++++--------- src/src/NotifierStructs/N001_data_struct.h | 6 +- src/src/WebServer/Markup_Forms.cpp | 2 + src/src/WebServer/NotificationPage.cpp | 33 ++-- 7 files changed, 128 insertions(+), 109 deletions(-) diff --git a/platformio_core_defs.ini b/platformio_core_defs.ini index e755edbbc9..3d54acfd68 100644 --- a/platformio_core_defs.ini +++ b/platformio_core_defs.ini @@ -198,7 +198,7 @@ lib_extra_dirs = ; ESP_IDF 5.3.1 [core_esp32_IDF5_3_1__3_0_5_LittleFS] platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2996/framework-arduinoespressif32-all-release_v5.3-22a3b096.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/3019/framework-arduinoespressif32-all-release_v5.3-98aecc7e.zip build_flags = -DESP32_STAGE -DESP_IDF_VERSION_MAJOR=5 -DLIBRARIES_NO_LOG=1 diff --git a/platformio_esp32_solo1.ini b/platformio_esp32_solo1.ini index 4250b93411..9854619dc8 100644 --- a/platformio_esp32_solo1.ini +++ b/platformio_esp32_solo1.ini @@ -3,7 +3,7 @@ [esp32_solo1_common_LittleFS] extends = esp32_base_idf5 platform = https://github.com/Jason2866/platform-espressif32.git#Arduino/IDF53 -platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/2997/framework-arduinoespressif32-solo1-release_v5.3-22a3b096.zip +platform_packages = framework-arduinoespressif32 @ https://github.com/Jason2866/esp32-arduino-lib-builder/releases/download/3020/framework-arduinoespressif32-solo1-release_v5.3-98aecc7e.zip build_flags = ${esp32_base_idf5.build_flags} -DFEATURE_ARDUINO_OTA=1 -DUSE_LITTLEFS diff --git a/src/_N001_Email.cpp b/src/_N001_Email.cpp index d57808f522..a3080ae7df 100644 --- a/src/_N001_Email.cpp +++ b/src/_N001_Email.cpp @@ -79,8 +79,6 @@ bool NPlugin_001(NPlugin::Function function, struct EventStruct *event, String& if (!event->String2.isEmpty()) { subject = event->String2; } - subject = parseTemplate(subject); - body = parseTemplate(body); NPlugin_001_send(NotificationSettings, subject, body); success = true; break; diff --git a/src/src/NotifierStructs/N001_data_struct.cpp b/src/src/NotifierStructs/N001_data_struct.cpp index 4350636503..66399bdf51 100644 --- a/src/src/NotifierStructs/N001_data_struct.cpp +++ b/src/src/NotifierStructs/N001_data_struct.cpp @@ -1,32 +1,31 @@ #include "../NotifierStructs/N001_data_struct.h" #ifdef USES_N001 -#include "../DataStructs/ESPEasy_EventStruct.h" -#include "../ESPEasyCore/ESPEasy_Log.h" -#include "../ESPEasyCore/ESPEasy_backgroundtasks.h" -#include "../Globals/Settings.h" -#include "../Helpers/ESPEasy_Storage.h" -#include "../Helpers/ESPEasy_time_calc.h" -#include "../Helpers/Networking.h" -#include "../Helpers/StringGenerator_System.h" -#include "../Helpers/StringParser.h" -#include "../Helpers/_CPlugin_Helper.h" // safeReadStringUntil -#include "../Helpers/_NPlugin_init.h" - -#if FEATURE_EMAIL_TLS - - # include - # include "../CustomBuild/Certificate_CA.h" - -#endif // if FEATURE_EMAIL_TLS - -bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, const String& aSub, String& aMesg) +# include "../DataStructs/ESPEasy_EventStruct.h" +# include "../ESPEasyCore/ESPEasy_Log.h" +# include "../ESPEasyCore/ESPEasy_backgroundtasks.h" +# include "../Globals/Settings.h" +# include "../Helpers/ESPEasy_Storage.h" +# include "../Helpers/ESPEasy_time_calc.h" +# include "../Helpers/Networking.h" +# include "../Helpers/StringGenerator_System.h" +# include "../Helpers/StringParser.h" +# include "../Helpers/_CPlugin_Helper.h" // safeReadStringUntil +# include "../Helpers/_NPlugin_init.h" + +# if FEATURE_EMAIL_TLS + + # include + # include "../CustomBuild/Certificate_CA.h" + +# endif // if FEATURE_EMAIL_TLS + +bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, String& subject, String& body) { - // String& aDomain , String aTo, String aFrom, String aSub, String aMesg, String aHost, int aPort) bool myStatus = false; bool failFlag = false; -#if FEATURE_EMAIL_TLS +# if FEATURE_EMAIL_TLS // values are based on the NPLUGIN_001_PKT_SZ BearSSL::WiFiClientSecure_light client(4096, 4096); @@ -36,23 +35,23 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co client.setInsecure(); -#else // if FEATURE_EMAIL_TLS +# else // if FEATURE_EMAIL_TLS // Use WiFiClient class to create TCP connections WiFiClient client; -#endif // if FEATURE_EMAIL_TLS +# endif // if FEATURE_EMAIL_TLS - #ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS // See: https://github.com/espressif/arduino-esp32/pull/6676 client.setTimeout((notificationsettings.Timeout_ms + 500) / 1000); // in seconds!!!! Client *pClient = &client; pClient->setTimeout(notificationsettings.Timeout_ms); - #else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # else // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS client.setTimeout(notificationsettings.Timeout_ms); // in msec as it should be! - #endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS + # endif // ifdef MUSTFIX_CLIENT_TIMEOUT_IN_SECONDS - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat( @@ -60,14 +59,23 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co notificationsettings.Server, notificationsettings.Port)); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG if (!connectClient(client, notificationsettings.Server, notificationsettings.Port, notificationsettings.Timeout_ms)) { if (loglevelActiveFor(LOG_LEVEL_ERROR)) { +# if FEATURE_EMAIL_TLS addLog(LOG_LEVEL_ERROR, strformat( - F("Email: Error connecting to %s:%d"), + F("Email: Error connecting to %s:%u Error code: %d"), + notificationsettings.Server, + notificationsettings.Port, + client.getLastError())); + +#else + addLog(LOG_LEVEL_ERROR, strformat( + F("Email: Error connecting to %s:%u"), notificationsettings.Server, notificationsettings.Port)); +#endif } myStatus = false; failFlag = true; @@ -77,9 +85,9 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if ((clientTimeout < NPLUGIN_001_MIN_TM) || (clientTimeout > NPLUGIN_001_MAX_TM)) { clientTimeout = NPLUGIN_001_DEF_TM; } -#ifndef BUILD_NO_DEBUG +# ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, concat(F("NPlugin_001_send: timeout: "), clientTimeout)); -#endif +# endif // ifndef BUILD_NO_DEBUG String email_address(notificationsettings.Sender); int pos_less = email_address.indexOf('<'); @@ -100,22 +108,22 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co // Sample Rule: Notify 1, "{email1@domain.com;email2@domain.net}Test email from %sysname%.
    How are you?
    Have a good day.
    " String subAddr; String tmp_ato; - int pos_brace1 = aMesg.indexOf('{'); - int pos_amper = aMesg.indexOf('@'); - int pos_brace2 = aMesg.indexOf('}'); + int pos_brace1 = body.indexOf('{'); + int pos_amper = body.indexOf('@'); + int pos_brace2 = body.indexOf('}'); if ((pos_brace1 == 0) && (pos_amper > pos_brace1) && (pos_brace2 > pos_amper)) { - subAddr = aMesg.substring(pos_brace1 + 1, pos_brace2); + subAddr = body.substring(pos_brace1 + 1, pos_brace2); subAddr.trim(); tmp_ato = subAddr; - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Receiver (ato): %s"), subAddr.c_str())); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG - String subMsg = aMesg.substring(pos_brace2 + 1); // Remove substitute email address from subject line. + String subMsg = body.substring(pos_brace2 + 1); // Remove substitute email address from subject line. subMsg.trim(); if (subMsg.indexOf(',') == 0) { @@ -126,13 +134,13 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if (!subMsg.length()) { subMsg = "ERROR: ESPEasy Notify Rule missing the message text. Please correct the rule."; } - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat(F("Email: Substitute Message: %s"), subMsg.c_str())); } - #endif // ifndef BUILD_NO_DEBUG - aMesg = subMsg; + # endif // ifndef BUILD_NO_DEBUG + body = subMsg; } else { tmp_ato = notificationsettings.Receiver; // Use plugin's receiver. @@ -143,32 +151,36 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co tmp_ato.replace(" ", ""); - const String nodename_emailfrom = strformat(F("%s <%s>"), senderName.c_str(), email_address.c_str()); - String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); - - const String mailheader = strformat( - F( - "From: %s\r\n" - "To: %s\r\n" - "Subject: %s\r\n" - "Reply-To: %s\r\n" - "Date: %s\r\n" - "MIME-VERSION: 1.0\r\n" - "Content-type: text/html; charset=UTF-8\r\n" - "X-Mailer: EspEasy v%s\r\n\r\n" - ), - nodename_emailfrom.c_str(), - tmp_ato.c_str(), - aSub.c_str(), - nodename_emailfrom.c_str(), - parseTemplate(dateFmtHdr).c_str(), - getSystemBuildString().c_str()); - + String mailheader; + { + const String nodename_emailfrom = strformat(F("%s <%s>"), senderName.c_str(), email_address.c_str()); + String dateFmtHdr = F("%sysweekday_s%, %sysday_0% %sysmonth_s% %sysyear% %systime% %systzoffset%"); + + mailheader = strformat( + F( + "From: %s\r\n" + "To: %s\r\n" + "Subject: %s\r\n" + "Reply-To: %s\r\n" + "Date: %s\r\n" + "MIME-VERSION: 1.0\r\n" + "Content-type: text/html; charset=UTF-8\r\n" + "X-Mailer: EspEasy v%s\r\n\r\n" + ), + nodename_emailfrom.c_str(), + tmp_ato.c_str(), + parseTemplate(subject).c_str(), + nodename_emailfrom.c_str(), + parseTemplate(dateFmtHdr).c_str(), + getSystemBuildString().c_str()); + } // Make sure to replace the char '\r' and not the string "\r" // See: https://github.com/letscontrolit/ESPEasy/issues/4967 - removeChar(aMesg, '\r'); - aMesg.replace(String('\n'), F("
    ")); // re-write line breaks for Content-type: text/html + removeChar(body, '\r'); + body.replace(String('\n'), F("
    ")); // re-write line breaks for Content-type: text/html + + body = parseTemplate(body); // Wait for Client to Start Sending // The MTA Exchange @@ -176,29 +188,29 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co if (!failFlag) { addLog(LOG_LEVEL_INFO, F("Email: Initializing ...")); - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_INFO, strformat(F("Email: Max Allowed Timeout is %d secs"), clientTimeout / 1000)); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG while (true) { // FIXME TD-er: Use of while here can be useful so you can // exit using break; // However this is way too complex using both a failFlag and break // and not even consistently. if (!NPlugin_001_MTA(client, EMPTY_STRING, 220, clientTimeout)) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Initialization Fail")); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } if (!failFlag) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Sending EHLO domain")); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG const String astr = strformat(F("EHLO %s"), notificationsettings.Domain); @@ -207,9 +219,9 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co astr, 250, clientTimeout)) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: EHLO Domain Fail")); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG failFlag = true; } } @@ -235,44 +247,44 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co catStr = F("Empty!"); } - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { String log = strformat(F("Email: Packet Rcvd is: > %s <"), catStr.c_str()); addLogMove(LOG_LEVEL_DEBUG, log); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG } if (!failFlag) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, F("Email: Sending User/Pass")); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG if (!NPlugin_001_Auth(client, notificationsettings.User, notificationsettings.Pass, clientTimeout)) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: User/Pass Fail")); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } } if (!failFlag) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Sending email Addr")); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG const String astr = strformat(F("MAIL FROM:<%s>"), email_address.c_str()); if (!NPlugin_001_MTA(client, astr, 250, clientTimeout)) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG addLog(LOG_LEVEL_DEBUG, F("Email: Addr Fail")); - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG failFlag = true; break; } @@ -313,7 +325,7 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, co } if (!failFlag) { - if (!NPlugin_001_MTA(client, strformat(F("%s%s\r\n.\r\n"), mailheader.c_str(), aMesg.c_str()), 250, clientTimeout)) { + if (!NPlugin_001_MTA(client, strformat(F("%s%s\r\n.\r\n"), mailheader.c_str(), body.c_str()), 250, clientTimeout)) { failFlag = true; break; } @@ -367,7 +379,7 @@ bool NPlugin_001_Auth(WiFiClient& client, const String& user, const String& pass bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPattern, uint16_t timeout) { - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLog(LOG_LEVEL_DEBUG, strformat( @@ -376,7 +388,7 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa timeout, aStr.c_str())); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG if (aStr.length()) { client.PR_9453_FLUSH_TO_CLEAR(); // have to send msg to server so flush data first @@ -393,12 +405,12 @@ bool NPlugin_001_MTA(WiFiClient& client, const String& aStr, uint16_t aWaitForPa String line; safeReadStringUntil(client, line, '\n', 1024); // , timeout); - #ifndef BUILD_NO_DEBUG + # ifndef BUILD_NO_DEBUG if (loglevelActiveFor(LOG_LEVEL_DEBUG)) { addLogMove(LOG_LEVEL_DEBUG, concat(F("NPlugin_001_MTA: read line: "), line)); } - #endif // ifndef BUILD_NO_DEBUG + # endif // ifndef BUILD_NO_DEBUG // response could be like: '220 domain', '220-domain','220+domain' @@ -446,4 +458,4 @@ bool getNextMailAddress(const String& data, String& address, int index) return false; } -#endif \ No newline at end of file +#endif // ifdef USES_N001 diff --git a/src/src/NotifierStructs/N001_data_struct.h b/src/src/NotifierStructs/N001_data_struct.h index 4cddc6e299..9ab778e7c0 100644 --- a/src/src/NotifierStructs/N001_data_struct.h +++ b/src/src/NotifierStructs/N001_data_struct.h @@ -6,7 +6,7 @@ #ifdef USES_N001 // FIXME TD-er: Must we add a complete inheritance structure like done with pluginTaskData? -// We do not have multiple instances of these notifiers, so let's keep it simple. +// We do not have multiple instances of these notifiers that need to keep track of run-time members, so let's keep it simple. # define NPLUGIN_001_PKT_SZ 256 @@ -18,8 +18,8 @@ bool NPlugin_001_send(const NotificationSettingsStruct& notificationsettings, - const String & aSub, - String & aMesg); + String & subject, + String & body); bool NPlugin_001_Auth(WiFiClient & client, const String& user, const String& pass, diff --git a/src/src/WebServer/Markup_Forms.cpp b/src/src/WebServer/Markup_Forms.cpp index ac47d98e1b..0174857106 100644 --- a/src/src/WebServer/Markup_Forms.cpp +++ b/src/src/WebServer/Markup_Forms.cpp @@ -351,6 +351,8 @@ void addFormPasswordBox(const String& label, const String& id, const String& pas bool getFormPassword(const String& id, String& password) { password = webArg(id); + + addLog(LOG_LEVEL_INFO, concat(F("getFormPassword: "), password)); return !equals(password, F("*****")); } diff --git a/src/src/WebServer/NotificationPage.cpp b/src/src/WebServer/NotificationPage.cpp index 9c9f3bd5c4..0f1f60899d 100644 --- a/src/src/WebServer/NotificationPage.cpp +++ b/src/src/WebServer/NotificationPage.cpp @@ -83,8 +83,7 @@ void handle_notifications() { } NotificationSettings.Port = getFormItemInt(F("port"), 0); - // FIXME TD-er: Must convert this to msec in the user interface as every other timeout in ESPEasy is in msec. - NotificationSettings.Timeout_ms = 1000 * getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM / 1000); + NotificationSettings.Timeout_ms = getFormItemInt(F("timeout"), NPLUGIN_001_DEF_TM); NotificationSettings.Pin1 = getFormItemInt(F("pin1"), -1); NotificationSettings.Pin2 = getFormItemInt(F("pin2"), -1); Settings.NotificationEnabled[notificationindex] = isFormItemChecked(F("notificationenabled")); @@ -93,9 +92,10 @@ void handle_notifications() { strncpy_webserver_arg(NotificationSettings.Sender, F("sender")); strncpy_webserver_arg(NotificationSettings.Receiver, F("receiver")); strncpy_webserver_arg(NotificationSettings.Subject, F("subject")); - strncpy_webserver_arg(NotificationSettings.User, F("user")); - strncpy_webserver_arg(NotificationSettings.Pass, F("pass")); + strncpy_webserver_arg(NotificationSettings.User, F("username")); + strncpy_webserver_arg(NotificationSettings.Pass, F("password")); strncpy_webserver_arg(NotificationSettings.Body, F("body")); +// copyFormPassword(F("password"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass)); } } addHtmlError(SaveNotificationSettings(notificationindex, reinterpret_cast(&NotificationSettings), @@ -218,6 +218,7 @@ void handle_notifications() { { if (Notification[NotificationProtocolIndex].usesMessaging) { + addFormSubHeader(F("SMTP Server Settings")); addFormTextBox(F("Domain"), F("domain"), NotificationSettings.Domain, sizeof(NotificationSettings.Domain) - 1); addFormTextBox(F("Server"), F("server"), NotificationSettings.Server, sizeof(NotificationSettings.Server) - 1); addFormNumericBox( @@ -240,22 +241,28 @@ void handle_notifications() { // FIXME TD-er: Must convert to msec as every other timeout used/configured in ESPEasy is in msec addFormNumericBox( F("Timeout"), F("timeout"), - NotificationSettings.Timeout_ms / 1000, - NPLUGIN_001_MIN_TM / 1000, - NPLUGIN_001_MAX_TM / 1000 + NotificationSettings.Timeout_ms, + NPLUGIN_001_MIN_TM, + NPLUGIN_001_MAX_TM # if FEATURE_TOOLTIPS , F("Maximum Server Response Time") # endif // if FEATURE_TOOLTIPS ); - addUnit(F("Seconds")); + addUnit(F("ms")); - addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1); - addFormTextBox(F("Receiver"), F("receiver"), NotificationSettings.Receiver, sizeof(NotificationSettings.Receiver) - 1); - addFormTextBox(F("Subject"), F("subject"), NotificationSettings.Subject, sizeof(NotificationSettings.Subject) - 1); + ZERO_TERMINATE(NotificationSettings.Pass); + addFormSubHeader(F("Credentials")); - addFormTextBox(F("User"), F("user"), NotificationSettings.User, sizeof(NotificationSettings.User) - 1); - addFormTextBox(F("Pass"), F("pass"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass) - 1); + addFormTextBox(F("Username"), F("username"), NotificationSettings.User, sizeof(NotificationSettings.User) - 1); + addFormTextBox(F("Password"), F("password"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass) - 1); +// addFormPasswordBox(F("Password"), F("password"), NotificationSettings.Pass, sizeof(NotificationSettings.Pass) - 1); + + addFormSubHeader(F("Email Attributes")); + + addFormTextBox(F("Sender"), F("sender"), NotificationSettings.Sender, sizeof(NotificationSettings.Sender) - 1); + addFormTextBox(F("Receiver"), F("receiver"), NotificationSettings.Receiver, sizeof(NotificationSettings.Receiver) - 1); + addFormTextBox(F("Subject"), F("subject"), NotificationSettings.Subject, sizeof(NotificationSettings.Subject) - 1); addRowLabel(F("Body")); addHtml(F("