-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Pass Client parameter to httpUpdate to use any BearSSL security method for a firmware update (with example) #4832
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pass Client parameter to httpUpdate to use any BearSSL security method for a firmware update (with example) #4832
Conversation
…ure/httpclient_client_parameter
…com/Jeroen88/Arduino into feature/httpclient_client_parameter
…eBearSSL example using MFLN
Travis build failed because of macro redefinitions not within my scope, e.g.: |
Travis build passed :) |
…ure/httpupdate_client_parameter
…ture/httpupdate_client_parameter pull origin master
…com/Jeroen88/Arduino into feature/httpupdate_client_parameter update with remote master
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice work! Some concerns about types and the new example, but nothing major. Shouldn't be an issue getting it into 2.5.0.
HTTPClient http; | ||
|
||
USE_SERIAL.print("[HTTP] begin...\n"); | ||
// configure traged server and url | ||
//http.begin("https://192.168.1.12/test.html", "7a 9c f4 db 40 d3 62 5a 6e 21 bc 5c cc 66 c8 3e a1 45 59 38"); //HTTPS | ||
http.begin("http://192.168.1.12/test.html"); //HTTP | ||
if (http.begin((Client &)client, "http://tls.mbed.org/")) { // HTTP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any reason this one picked? Isn't tls.mbed.org https-only?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, thanks for checking return codes!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will change this in http://jigsaw.w3.org/HTTP/connection.html
HTTPClient http; | ||
|
||
USE_SERIAL.print("[HTTP] begin...\n"); | ||
// configure traged server and url | ||
|
||
|
||
http.begin("http://user:[email protected]/test.html"); | ||
http.begin((Client&)client, "http://guest:[email protected]/HTTP/Basic/"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't there no need for the cast (Client&)
here and in the other examples? Your prototype will make the C++ compiler pass the pointer no matter what, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You will be right. I'll check it and then change it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The cast has been removed
@@ -65,7 +67,7 @@ void loop() { | |||
uint8_t buff[128] = { 0 }; | |||
|
|||
// get tcp stream |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we get rid of the stream
temp variable completely since it's no longer needed with your fix? i.e. there is no getStreamPtr()
needed anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I see later on that you still have a "getStreamPtr()" method with the new one, too. Any reason not to just keep using it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not quite understand you here.
The getStreamPtr() is embedded in the #ifdef KEEP_PRESENT_API, so it's just there for backward compatibility.
I added the streaming example on purpose, because a server response may be too large to fit into a String, making it impossible to use getString(), but still possible to stream it. Moreover, since it is String getString() instead of String &getString() the response might even be copied (allthough the compiler may optimize this, I don't know)
Could you explain what you are suggesting?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand you now. The getStreamPtr() is kept as part of the existing API, however in the examples the unneeded call to getStreamPtr() has been removed.
uint8_t buff[128] = { 0 }; | ||
|
||
// get tcp stream | ||
WiFiClient * stream = &client; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above, no more "get tcp stream" with your change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
getStreamPtr() has been removed from the example
} | ||
|
||
_port = (protocol == "https" ? 443 : 80); | ||
return beginInternal(url, protocol.c_str()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There shouldn't be any need for the protocol anymore, no? The WiFiCilent*
passed in should already either be encrypted or not, and the code paths for both are the same, no?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it is still needed. The ESP8266HTTPClient is setting up (and maintaining with Keep-Alive) the connection, and it should know what port to use. The idea (also in the original version) of this begin() method , is to pass all the needed information to set up the connection through a fully qualified URL.
_host = host; | ||
_port = port; | ||
_uri = uri; | ||
_protocol = (https ? "https" : "http"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question as above about the protocol, is it needed anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _port is needed, see in connect(): if(!_tcp->connect(_host.c_str(), _port)) { ...
If you mean that that _protocol can be dropped as class variable, I think you are right. I'll check that and wrap it into a #ifdef KEEP_PRESENT_API if possible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The _protocol may be dropped in the new class, but beginInternal() is still used, and this function uses _protocol as well. In this function _protocol could be changed into a variable local to that function. I did not do this to stay as close as possible to the original code.
_tcp->setNoDelay(true); | ||
#ifdef KEEP_PRESENT_API | ||
if(_tcpDeprecated) | ||
_tcpDeprecated->setNoDelay(true); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't you want to set it on _tcp
as well (outside this #define
, of course)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Client::setNoDelay() does not exist. If I change Client in WiFiClient its is possible, see my remark later
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now setNoDelay() is alway called
#include <WiFiClient.h> | ||
#else | ||
#include <Client.h> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why move to abstract Client
from WiFiClient
? I don't remember ever seeing anything else take this root class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea was that this class could be used for other Clients as well, eg an EthernetClient as defined here (https://github.com/esp8266/Arduino/blob/master/libraries/Ethernet/src/EthernetClient.h). But because most people will use the WiFiClient, I will change Client to WiFiClient
@@ -0,0 +1,138 @@ | |||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I'm happy to see someone using CertStore, for ESP8266 updates that's probably not what we want people to do since it's saying you'd accept an update from anyone on the internet.
What most people would want to do is have a single, private certificate that they generate and keep secure on their own company's system. It would make this new example shorter and simpler, too, and focus it on the new way of doing the update vs. the overhead for making a CertStore...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand:
You will not accept an update from anyone on the internet because the URL determines where you fetch the update, and the Client identifies the host. Also, there is no need to populate the cert store with all root certificates, just your company's one is possible too.
Do you want an extra, simpler example just using a cert? Could you provide it?
@@ -276,7 +304,7 @@ HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate(HTTPClient& http, const String& | |||
ret = HTTP_UPDATE_FAILED; | |||
} else { | |||
|
|||
WiFiClient * tcp = http.getStreamPtr(); | |||
WiFiClient * tcp = (WiFiClient *) http.getStreamPtr(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm fine with casts like this, but I think @devyte will probably ask for static_cast<>. No change from me, just a warning...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will try the static_cast
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because http.getStreamPtr() returns a WiFiClient now, casting is no longer necessary
@earlephilhower thnx for the review! I will make updates and give answers to your questions / remarks soon! |
Requested review changes are made and posted as a new PR #4980. |
Added two new update() and one updateSpiffs() function that uses the ESP8266HTTPClient adapted by me in PR #4825. With these adaptations, it is possible to do an encrypted and authenticated server firmware or SPIFFS update, using any of the supported security options of the WiFiClientSecureBearSSL library. httpUpdateSecure.ino is a complete example.
To try this, you need either a sketch with enough free memory to allocate the full TLS/SSL-buffer (16k), or a, currently not very widely supported Maximum Fragment Length Negotiation enabled, server (or do the same trick as I do, using a nodeJS server and set maxSendFragmentLength) that sends the correct response headers and serves the binary. You need a certificate store in SPIFFs that includes your root CA, or a self signed certificate, or a certificate fingerprint. In the latter two cases you need to edit the example to use allowSelfSignedCerts() or setFingerprint(). It is also possible to use encryption without server authentication. In this case use setInsecure(). Of course any of the other supported WiFiClientSecureBearSSL functions may be used as well.
I have a question: in ESP8266httpUpdate.cpp with these new functions, a Client& is passed into the update() functions. After the headers have been checked in handleUpdate(), a WiFiClient * is retrieved from the http client :
WiFiClient * tcp = (WiFiClient *) http.getStreamPtr();
This works fine, however: in PR #4825 I designed httpClient as a generic client for any Client. There is no fundamental reason not to use e.g. EthernetClient. But in handleUpdate() a WiFiClient * is necessary to do the actual update. Is it possible to use a base class Client instead?