Skip to content
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

HTTPS Server works on raspberrypi but not on espressif #8268

Closed
ams1 opened this issue Aug 2, 2023 · 29 comments · Fixed by #8926
Closed

HTTPS Server works on raspberrypi but not on espressif #8268

ams1 opened this issue Aug 2, 2023 · 29 comments · Fixed by #8926
Labels
bug espressif applies to multiple Espressif chips network
Milestone

Comments

@ams1
Copy link

ams1 commented Aug 2, 2023

Hi,

Thank you for this AWESOME initiative!

Any plan to have an example for SSL http server?

@michalpokusa
Copy link

Hi, thanks for kind word.

Unfortunately right now implementing SSL is problematic, due to the amount of compute power necessary, it is very slow and in my testing does not work at all on some microcontrollers.

Related issue on CircuitPython itself:
#7657

Attempt to make a HTTPS server:
https://github.com/ide/circuitpython-https-server

If you want to play with it, I would appreciate you sharing your results.

But if you really need a HTTPS, one way is to use e.g. nginx on Raspberry Pi (Zero/4), but at this point you can simply host a website on it too.

@zap8600
Copy link

zap8600 commented Aug 7, 2023

I've tried using the example on CircuitPython 8.2.0, using both version 4.2.0 and 2.3.0, on an Adafruit Feather ESP32-S2. The error I get is the same on both HTTP server versions. The error below is from my attempt to use version 2.3.0, modified to print the socket type on server start. Here is the error I'm getting.

Traceback (most recent call last):
  File "code.py", line 79, in <module>
  File "code.py", line 49, in main
  File "/lib/adafruit_httpserver/server.py", line 137, in poll
RuntimeError: Invalid socket for TLS

Here is the line the error is happening at.

            conn, client_address = self._sock.accept()

Hope this helps!

@anecdata
Copy link
Member

anecdata commented Aug 7, 2023

What are you using for a client? curl -v might yield some clues.

@michalpokusa
Copy link

During my testing I was getting the exact same error. I was testing using curl, VSCode extension Thunder Client, a script in Python with requests, Google Chrome and even other microcontroller (ESP32-S2 TFT) with adafruit_requests.

I believe it is something in the CP itself. The interesting thing is that adafruit_requests as far as I know can make requests to HTTPS endpoints, so the capability is there.

@zap8600
Copy link

zap8600 commented Aug 7, 2023

What are you using for a client? curl -v might yield some clues.

I'm using curl. curl will stall, then throw an error once the microcontroller resets.

@anecdata
Copy link
Member

anecdata commented Aug 7, 2023

The curl switch -v gives detail about the SSL transaction, I thought it might give a clue where in the process it breaks.

@zap8600
Copy link

zap8600 commented Aug 7, 2023

The curl switch -v gives detail about the SSL transaction, I thought it might give a clue where in the process it breaks.

I haven't looked much into it, but I think it only has to do with CP. Both curl and the browser don't get anything back from the microcontroller. They'll throw a connection reset error when the microcontroller resets, though.

@anecdata
Copy link
Member

anecdata commented Aug 7, 2023

I loaded up a Pico W and a QT Py S2 and a QT Py S3 (all on CP 8.2.0, all freshly reset).

It works on Pico W:

% curl -v --insecure https://192.168.6.198
*   Trying 192.168.6.198:443...
* Connected to 192.168.6.198 (192.168.6.198) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* TLSv1.2 (IN), TLS handshake, Certificate (11):
* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
* TLSv1.2 (IN), TLS handshake, Server finished (14):
* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (OUT), TLS handshake, Finished (20):
* TLSv1.2 (IN), TLS change cipher, Change cipher spec (1):
* TLSv1.2 (IN), TLS handshake, Finished (20):
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server did not agree on a protocol. Uses default.
* Server certificate:
*  subject: CN=picow.local
*  start date: Feb 28 05:42:56 2023 GMT
*  expire date: Feb 28 05:42:56 2024 GMT
*  issuer: CN=picow.local
*  SSL certificate verify result: self signed certificate (18), continuing anyway.
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 192.168.6.198
> User-Agent: curl/7.88.1
> Accept: */*
> 
< HTTP/1.1 200 OK
< Content-Length: 117
< Content-Type: text/html
< Connection: close
< 
<!DOCTYPE html>
<title>Hello world &middot; Raspberry Pi Pico W</title>
<p>Hello world from Raspberry Pi Pico W!</p>
* Closing connection 0
* TLSv1.2 (OUT), TLS alert, close notify (256):

It breaks early in the TLS handshake on S2 and S3:

% curl -v --insecure https://192.168.6.210
*   Trying 192.168.6.210:443...
* Connected to 192.168.6.210 (192.168.6.210) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* Recv failure: Connection reset by peer
* LibreSSL/3.3.6: error:02FFF036:system library:func(4095):Connection reset by peer
* Closing connection 0
curl: (35) Recv failure: Connection reset by peer

Same behavior with CP 8.2.2 on S2.

Probably some difference in common-hal, or esp-idf. Perhaps we should move this issue to the circuitpython repo.

@anecdata
Copy link
Member

anecdata commented Aug 7, 2023

code.py passes in server_side=True in line 74: return self._ssl_context.wrap_socket(socket, server_side=True)
https://docs.circuitpython.org/en/latest/shared-bindings/ssl/index.html#ssl.SSLContext.wrap_socket
espressif does nothing with it:

ssl_sslsocket_obj_t *common_hal_ssl_sslcontext_wrap_socket(ssl_sslcontext_obj_t *self,

compared to raspberrypi:
ret = mbedtls_ssl_config_defaults(&o->conf,

@anecdata anecdata transferred this issue from adafruit/Adafruit_CircuitPython_HTTPServer Aug 7, 2023
@anecdata anecdata changed the title Example for SSL HTTPS Server works on raspberrypi but not on espressif Aug 7, 2023
@anecdata anecdata added bug network espressif applies to multiple Espressif chips labels Aug 7, 2023
@zap8600
Copy link

zap8600 commented Aug 7, 2023

So we just need to modify the espressif port to check for the argument?

@anecdata
Copy link
Member

anecdata commented Aug 8, 2023

There's presumably some lwip logic needed, predicated on server_side=True. But it is odd that the exception is Invalid socket for TLS, which I think indicates the socket isn't SOCK_STREAM, though the library sets it when it creates the socket to listen to.

@dhalbert dhalbert added this to the 9.0.0 milestone Aug 11, 2023
@dhalbert
Copy link
Collaborator

We could backport this to 8.2.x if it gets figured out soon.

@michalpokusa
Copy link

Is there any progress on fixing/updating ESPs to allow HTTPS? I would like to make an update to adafruit_httpserver that allows HTTPS, but currently for all I know only RPi Pico W supports what is needed for it to work.

@zap8600
Copy link

zap8600 commented Nov 29, 2023

Why don't we just add

    ret = mbedtls_ssl_config_defaults(&o->conf,
        server_side ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
        MBEDTLS_SSL_TRANSPORT_STREAM,
        MBEDTLS_SSL_PRESET_DEFAULT);
    if (ret != 0) {
        goto cleanup;
    }

to common_hal_ssl_sslcontext_wrap_socket? Is there more that's supposed to be done? I'll make a fork and test this.

@tannewt
Copy link
Member

tannewt commented Feb 14, 2024

@michalpokusa or @anecdata did you try @zap8600's suggestion?

@jepler
Copy link
Member

jepler commented Feb 15, 2024

I don't think calling mbedtls APIs here will work, the espressif SSLContext is using ep-idf calls like esp_tls_init.

It's tempted to think we could/should use mbedtls API calls (not esp-idf calls) for both espressif & raspberrypi rp2 (I think this is what micropython does!) but that would be a big change.

@anecdata
Copy link
Member

anecdata commented Feb 16, 2024

Does anyone have working test code for HTTPS Server for espressif with this PR? I'm getting RuntimeError: Invalid socket for TLS using code from https://github.com/ide/circuitpython-https-server updated for latest adafruit_httpserver. (I may be doing something wrong)

i.e., how do we know this is completed?

@michalpokusa
Copy link

Does anyone have working test code for HTTPS Server for espressif with this PR? I'm getting RuntimeError: Invalid socket for TLS using code from https://github.com/ide/circuitpython-https-server updated for latest adafruit_httpserver. (I may be doing something wrong)

i.e., how do we know this is completed?

I also started trying HTTPS with adafruit_httpserver on ESP32-S2 TFT Feather. I get exactly the same error, specifically it gets raised by Server._sock.accept() in Server.poll().

@anecdata
Copy link
Member

client side (essentially same output for ESP32-S3 and Pico W:

% curl -v --insecure https://192.168.6.243
*   Trying 192.168.6.243:443...
* Connected to 192.168.6.243 (192.168.6.243) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* Recv failure: Connection reset by peer
* LibreSSL/3.3.6: error:02FFF036:system library:func(4095):Connection reset by peer
* Closing connection
curl: (35) Recv failure: Connection reset by peer

@jepler
Copy link
Member

jepler commented Feb 16, 2024

@anecdata please re-test with #8932, which fixes the "Invalid Socket for TLS" message. I tested on esp32-s3 (matrixportal s3) with ide/circuitpython-https-server#2

(however if it's not working on pico w either there's something else going on as well. That port did already set the socket type on an accepted socket, as far as I could tell from reading the source code)

@anecdata
Copy link
Member

anecdata commented Feb 16, 2024

@jepler works for me now on espressif, and it's plenty fast (QT Py S3)... Thanks!

@michalpokusa
Copy link

michalpokusa commented Feb 16, 2024

@jepler I also tested on my MatrixPortal S3, works very fast, I also tested with Websockets and wss://.

But I was unable to make it work with ESP32-S2 Feather TFT, I can start the server but when trying to connect I get an empty MemoryError:. Seems strange considering that both have 2MB of RAM, although MatrixPortal has SRAM and Feather has PSRAM, would that make any difference?

@anecdata Could you maybe test on the Feather if you have one on hand?

EDIT1: MemoryError: is present even with .mpy version of the adafruit_httpserver

@anecdata
Copy link
Member

anecdata commented Feb 17, 2024

I get the same on QT Py ESP32-S2 (N4R2), no dual http/https servers (just https):

server:

Traceback (most recent call last):
  File "adafruit_httpserver/server.py", line 404, in poll
MemoryError: 
Traceback (most recent call last):
  File "code.py", line 61, in <module>
  File "adafruit_httpserver/server.py", line 450, in poll
  File "adafruit_httpserver/server.py", line 404, in poll
MemoryError: 

client:

$ curl -iL -v --insecure https://192.168.6.180?HTTPS
*   Trying 192.168.6.180:443...
* Connected to 192.168.6.180 (192.168.6.180) port 443 (#0)
* ALPN: offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
* Recv failure: Operation timed out
* LibreSSL/3.3.6: error:02FFF03C:system library:func(4095):Operation timed out
* Closing connection 0
curl: (35) Recv failure: Operation timed out

edit: yes, EESP32-S3 has more internal SRAM than ESP32-S2, but maybe there could be build differences or something else to account for it? I suspect there's some difference of SRAM available to esp-idf.

@michalpokusa
Copy link

If RPicoW with 264kB can handle HTTPS, maybe there is something else here that needs to be fixed? It is definitely better but still not completely working.

Would it be worth re-opening this issue?

@anecdata
Copy link
Member

anecdata commented Feb 17, 2024

I think it needs an issue, either re-open or new. Some difference in S2 vs. S3, and also "https:// PicoW" breaks with latest everything (unless you've gotten it to work?)

iirc S2 has 320KB SRAM, and S3 has 540KB 512KB SRAM, with various claims to that memory from esp-idf/rtos/etc, and maybe some things from CP core too. I don't fully understand the new CP 9 memory model.

@michalpokusa
Copy link

I double checked and you are right, RPicoW now has the same problem as Feather, which is MemoryError:.

I assumed that considering that only the espressif port changed, code would still work on raspberry.

@michalpokusa
Copy link

michalpokusa commented Feb 18, 2024

After updating to CP 9.0.0-beta.1 I get a different error:
image

This does not mean that the MemoryError is gone, as it might appear later, but something else changed that unables even ESP32-S3 boards like MatrixPortal S3 to start a HTTPS server.

@anecdata
Copy link
Member

Pico W HTTPS Server seems to have broken with the MemoryError somewhere between 8.0.0 and 8.1.0.

@tannewt
Copy link
Member

tannewt commented Feb 20, 2024

iirc S2 has 320KB SRAM, and S3 has 540KB 512KB SRAM, with various claims to that memory from esp-idf/rtos/etc, and maybe some things from CP core too. I don't fully understand the new CP 9 memory model.

CP will try to allocate to PSRAM first now in beta.1+. (Not exactly sure when I changed it.) The IDF can allocate to PSRAM now too by default but needs to request it explicitly. Without an explicit request, then internal memory will be used first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug espressif applies to multiple Espressif chips network
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants