espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.83k stars 7.32k forks source link

ESP32 (IDF v5.1) SSL Handshake Failure After Server Certificate Renewal (IDFGH-13314) #14239

Closed samuelclay closed 4 months ago

samuelclay commented 4 months ago

Answers checklist.

IDF version.

v5.1

Espressif SoC revision.

ESP32-S3

Operating System used.

macOS

How did you build your project?

Command line with idf.py

If you are using Windows, please specify command line type.

None

Development Kit.

Custom Board

Power Supply used.

USB

What is the expected behavior?

I'm encountering an SSL handshake failure with my ESP32 devices (using ESP-IDF v5.1 with mbedTLS 3.3.0) when connecting to my server. This issue started occurring after the server's Let's Encrypt SSL certificate was renewed. When I upgrade to ESP-IDF v5.1.4, which uses mbedTLS 3.5.2, everything works as expected. But the ESP-IDF v5.1 devices are deployed in the field, so I can't easily update their firmware.

What is the actual behavior?

The ESP32 devices were able to connect successfully until a recent renewal of the SSL certificate on the server. The renewed certificate now causes the ESP32 devices to fail the connection with the following error:

E (80654) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x2700
I (80660) esp-tls-mbedtls: (FFFFD900): X509 - Certificate verification failed, e.g. CRL, CA or signature check failed
E (80671) esp-tls: Failed to open new connection
E (80677) transport_base: Failed to open a new connection
E (80688) HTTP_CLIENT: Connection failed, sock < 0
E (80688) Sol: HTTP_EVENT_ERROR

The SSL certificates have an A rating on Qualys SSL Server Test and work fine on web browsers and IDF v5.1.4, just not on IDF v5.1. The server is serving a TLS 1.2 certificate with many ciphers, most of which IDF 5.1 should be able to handle.

I've confirmed that the ESP32 devices are successfully connecting to WiFi. And I verified that upgrading to a later version of mbedTLS on a test device resolves the issue (not feasible for deployed devices). I also reviewed Caddy configuration and SSL settings and switched to nginx with similar configurations, just to rule out the web server. Both Caddy and nginx provide A ratings for the SSL certificate, and work in a browser and on IDF 5.1.4.

Steps to reproduce.

  1. Set up an ESP32 device with ESP-IDF v5.1 and mbedTLS 3.3.0.
  2. Configure the device to connect to a server using HTTPS.
  3. Set up the server with Caddy as a reverse proxy and obtain an SSL certificate from Let's Encrypt.
  4. Attempt to connect to the server from the ESP32 device.

Debug Logs.

ESP32 Log:

I (16171) Sol: Connected, IP address: 192.168.1.70
D (16183) Sol: Free heap: 56k/290k Free PSRAM: 7971k/8668k 
D (16195) esp-tls: handshake in progress...
I (16197) mbedtls: ssl_tls.c:3939 => handshake
I (16201) mbedtls: ssl_msg.c:2124 => flush output
I (16207) mbedtls: ssl_msg.c:2133 <= flush output
I (16213) mbedtls: ssl_tls.c:3859 client state: MBEDTLS_SSL_HELLO_REQUEST
I (16221) mbedtls: ssl_msg.c:2124 => flush output
I (16227) mbedtls: ssl_msg.c:2133 <= flush output
I (16231) mbedtls: ssl_tls.c:3859 client state: MBEDTLS_SSL_CLIENT_HELLO
I (16239) mbedtls: ssl_client.c:906 => write client hello
D (16239) Sol: Powering off EinkET013TT1
D (16245) mbedtls: ssl_client.c:709 client hello, current time: 1721763899
D (16257) mbedtls: ssl_client.c:480 dumping 'client hello, random bytes' (32 bytes)
D (16265) mbedtls: ssl_client.c:480 0000:  66 a0 08 3b 55 8e 18 7b df c7 23 3e f0 f7 a6 68  f..;U..{..#>...h
D (16275) mbedtls: ssl_client.c:480 0010:  db ca 5e 97 e0 53 80 a3 3f 10 9d 61 a7 2e e1 9f  ..^..S..?..a....
D (16285) mbedtls: ssl_client.c:505 dumping 'session id' (0 bytes)
D (16291) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0ad, TLS-ECDHE-ECDSA-WITH-AES-256-CCM
D (16301) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c00a, TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA
D (16311) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c014, TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA
D (16321) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0af, TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8
D (16331) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c02b, TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
D (16341) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c02f, TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256
D (16352) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0ac, TLS-ECDHE-ECDSA-WITH-AES-128-CCM
D (16362) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c023, TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256
D (16371) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c027, TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256
D (16383) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c009, TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA
D (16393) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c013, TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA
D (16399) Sol: EinkET013TT1 powered off
D (16402) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0ae, TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8
D (16416) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c05c, TLS-ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256
D (16426) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c060, TLS-ECDHE-RSA-WITH-ARIA-128-GCM-SHA256
D (16438) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c048, TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256
D (16448) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c04c, TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256
D (16457) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c09d, TLS-RSA-WITH-AES-256-CCM
D (16467) mbedtls: ssl_client.c:363 client hello, add ciphersuite: 003d, TLS-RSA-WITH-AES-256-CBC-SHA256
D (16477) mbedtls: ssl_client.c:363 client hello, add ciphersuite: 0035, TLS-RSA-WITH-AES-256-CBC-SHA
D (16487) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c00f, TLS-ECDH-RSA-WITH-AES-256-CBC-SHA
D (16497) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c005, TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA
D (16507) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0a1, TLS-RSA-WITH-AES-256-CCM-8
D (16515) mbedtls: ssl_client.c:363 client hello, add ciphersuite: 009c, TLS-RSA-WITH-AES-128-GCM-SHA256
D (16525) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c09c, TLS-RSA-WITH-AES-128-CCM
D (16535) mbedtls: ssl_client.c:363 client hello, add ciphersuite: 003c, TLS-RSA-WITH-AES-128-CBC-SHA256
D (16545) mbedtls: ssl_client.c:363 client hello, add ciphersuite: 002f, TLS-RSA-WITH-AES-128-CBC-SHA
D (16553) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c031, TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256
D (16563) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c029, TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256
D (16573) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c00e, TLS-ECDH-RSA-WITH-AES-128-CBC-SHA
D (16583) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c02d, TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256
D (16595) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c025, TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256
D (16605) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c004, TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA
D (16615) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c0a0, TLS-RSA-WITH-AES-128-CCM-8
D (16623) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c05e, TLS-ECDH-ECDSA-WITH-ARIA-128-GCM-SHA256
D (16635) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c062, TLS-ECDH-RSA-WITH-ARIA-128-GCM-SHA256
D (16645) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c050, TLS-RSA-WITH-ARIA-128-GCM-SHA256
D (16655) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c04a, TLS-ECDH-ECDSA-WITH-ARIA-128-CBC-SHA256
D (16665) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c04e, TLS-ECDH-RSA-WITH-ARIA-128-CBC-SHA256
D (16675) mbedtls: ssl_client.c:363 client hello, add ciphersuite: c03c, TLS-RSA-WITH-ARIA-128-CBC-SHA256
D (16685) mbedtls: ssl_client.c:381 adding EMPTY_RENEGOTIATION_INFO_SCSV
D (16691) mbedtls: ssl_client.c:390 client hello, got 40 cipher suites
D (16699) mbedtls: ssl_client.c:54 client hello, adding server name extension: prod5.sindarin.com
D (16708) mbedtls: ssl_client.c:241 client hello, adding supported_groups extension
W (16716) mbedtls: ssl_client.c:258 got supported group(001d)
D (16722) mbedtls: ssl_client.c:272 NamedGroup: x25519 ( 1d )
W (16728) mbedtls: ssl_client.c:258 got supported group(0017)
D (16734) mbedtls: ssl_client.c:272 NamedGroup: secp256r1 ( 17 )
W (16740) mbedtls: ssl_client.c:258 got supported group(0018)
D (16748) mbedtls: ssl_client.c:272 NamedGroup: secp384r1 ( 18 )
W (16754) mbedtls: ssl_client.c:258 got supported group(0019)
D (16761) mbedtls: ssl_client.c:272 NamedGroup: secp521r1 ( 19 )
W (16767) mbedtls: ssl_client.c:258 got supported group(001a)
D (16773) mbedtls: ssl_client.c:272 NamedGroup: brainpoolP256r1 ( 1a )
W (16779) mbedtls: ssl_client.c:258 got supported group(001b)
D (16787) mbedtls: ssl_client.c:272 NamedGroup: brainpoolP384r1 ( 1b )
W (16793) mbedtls: ssl_client.c:258 got supported group(001c)
D (16799) mbedtls: ssl_client.c:272 NamedGroup: brainpoolP512r1 ( 1c )
D (16807) mbedtls: ssl_client.c:295 dumping 'Supported groups extension' (16 bytes)
D (16815) mbedtls: ssl_client.c:295 0000:  00 0e 00 1d 00 17 00 18 00 19 00 1a 00 1b 00 1c  ................
D (16824) mbedtls: ssl_tls.c:9397 adding signature_algorithms extension
D (16832) mbedtls: ssl_tls.c:9417 got signature scheme [403] ecdsa_secp256r1_sha256
D (16840) mbedtls: ssl_tls.c:9426 sent signature scheme [403] ecdsa_secp256r1_sha256
D (16848) mbedtls: ssl_tls.c:9417 got signature scheme [804] rsa_pss_rsae_sha256
D (16854) mbedtls: ssl_tls.c:9417 got signature scheme [401] rsa_pkcs1_sha256
D (16863) mbedtls: ssl_tls.c:9426 sent signature scheme [401] rsa_pkcs1_sha256
D (16869) mbedtls: ssl_tls12_client.c:110 client hello, adding supported_point_formats extension
D (16879) mbedtls: ssl_tls12_client.c:310 client hello, adding encrypt_then_mac extension
D (16887) mbedtls: ssl_tls12_client.c:342 client hello, adding extended_master_secret extension
D (16897) mbedtls: ssl_tls12_client.c:375 client hello, adding session ticket extension
D (16905) mbedtls: ssl_client.c:669 client hello, total extension length: 75
D (16911) mbedtls: ssl_client.c:671 dumping 'client hello extensions' (75 bytes)
D (16919) mbedtls: ssl_client.c:671 0000:  00 4b 00 00 00 17 00 15 00 00 12 70 72 6f 64 35  .K.........prod5
D (16929) mbedtls: ssl_client.c:671 0010:  2e 73 69 6e 64 61 72 69 6e 2e 63 6f 6d 00 0a 00  .sindarin.com...
D (16940) mbedtls: ssl_client.c:671 0020:  10 00 0e 00 1d 00 17 00 18 00 19 00 1a 00 1b 00  ................
D (16950) mbedtls: ssl_client.c:671 0030:  1c 00 0d 00 06 00 04 04 03 04 01 00 0b 00 02 01  ................
D (16960) mbedtls: ssl_client.c:671 0040:  00 00 16 00 00 00 17 00 00 00 23                 ..........#
I (16969) mbedtls: ssl_msg.c:2554 => write handshake message
I (16975) mbedtls: ssl_msg.c:2714 => write record
D (16981) mbedtls: ssl_msg.c:2798 output record: msgtype = 22, version = [3:3], msglen = 200
I (16993) mbedtls: ssl_msg.c:2851 <= write record
I (16995) mbedtls: ssl_msg.c:2675 <= write handshake message
I (17003) mbedtls: ssl_client.c:994 <= write client hello
I (17009) mbedtls: ssl_msg.c:2124 => flush output
I (17013) mbedtls: ssl_msg.c:2138 message length: 205, out_left: 205
I (17022) mbedtls: ssl_msg.c:2145 ssl->f_send() returned 205 (-0xffffff33)
I (17028) mbedtls: ssl_msg.c:2172 <= flush output
I (17034) mbedtls: ssl_tls.c:3859 client state: MBEDTLS_SSL_SERVER_HELLO
I (17042) mbedtls: ssl_tls12_client.c:1195 => parse server hello
I (17049) mbedtls: ssl_msg.c:3887 => read record
I (17055) mbedtls: ssl_msg.c:1926 => fetch input
I (17059) mbedtls: ssl_msg.c:2066 in_left: 0, nb_want: 5
D (17194) ledc: Set to target duty: 151
D (17194) ledc: Set to target duty: 151
I (17477) mbedtls: ssl_msg.c:2086 in_left: 0, nb_want: 5
I (17478) mbedtls: ssl_msg.c:2089 ssl->f_recv(_timeout)() returned 5 (-0xfffffffb)
I (17480) mbedtls: ssl_msg.c:2111 <= fetch input
D (17486) mbedtls: ssl_msg.c:3634 input record: msgtype = 22, version = [0x303], msglen = 69
I (17495) mbedtls: ssl_msg.c:1926 => fetch input
I (17501) mbedtls: ssl_msg.c:2066 in_left: 5, nb_want: 74
I (17508) mbedtls: ssl_msg.c:2086 in_left: 5, nb_want: 74
I (17513) mbedtls: ssl_msg.c:2089 ssl->f_recv(_timeout)() returned 69 (-0xffffffbb)
I (17522) mbedtls: ssl_msg.c:2111 <= fetch input
D (17528) mbedtls: ssl_msg.c:3008 handshake message: msglen = 69, type = 2, hslen = 69
I (17536) mbedtls: ssl_msg.c:3959 <= read record
D (17540) mbedtls: ssl_tls12_client.c:1269 dumping 'server hello, version' (2 bytes)
D (17548) mbedtls: ssl_tls12_client.c:1269 0000:  03 03                                            ..
D (17558) mbedtls: ssl_tls12_client.c:1288 server hello, current time: 1439200979
D (17566) mbedtls: ssl_tls12_client.c:1298 dumping 'server hello, random bytes' (32 bytes)
D (17574) mbedtls: ssl_tls12_client.c:1298 0000:  55 c8 76 d3 a4 e8 72 eb f3 84 ce a6 29 75 da 60  U.v...r.....)u.`
D (17585) mbedtls: ssl_tls12_client.c:1298 0010:  8b 6c 46 e1 b4 d9 82 b2 02 04 ba bc 6f 8f c8 74  .lF.........o..t
D (17595) mbedtls: ssl_tls12_client.c:1361 server hello, session id len.: 0
D (17603) mbedtls: ssl_tls12_client.c:1362 dumping 'server hello, session id' (0 bytes)
D (17611) mbedtls: ssl_tls12_client.c:1386 no session has been resumed
D (17617) mbedtls: ssl_tls12_client.c:1389 server hello, chosen ciphersuite: c02b
D (17625) mbedtls: ssl_tls12_client.c:1390 server hello, compress alg.: 0
D (17633) mbedtls: ssl_tls12_client.c:1425 server hello, chosen ciphersuite: TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256
I (17643) mbedtls: ssl_tls12_client.c:1446 server hello, total extension length: 25
D (17651) mbedtls: ssl_tls12_client.c:1466 found renegotiation extension
D (17659) mbedtls: ssl_tls12_client.c:1591 unknown extension found: 0 (ignoring)
D (17667) mbedtls: ssl_tls12_client.c:1544 found supported_point_formats extension
D (17675) mbedtls: ssl_tls12_client.c:1531 found session_ticket extension
D (17681) mbedtls: ssl_tls12_client.c:1518 found extended_master_secret extension
I (17690) mbedtls: ssl_tls12_client.c:1660 <= parse server hello
I (17696) mbedtls: ssl_msg.c:2124 => flush output
I (17702) mbedtls: ssl_msg.c:2133 <= flush output
I (17708) mbedtls: ssl_tls.c:3859 client state: MBEDTLS_SSL_SERVER_CERTIFICATE
I (17716) mbedtls: ssl_tls.c:7559 => parse certificate
I (17721) mbedtls: ssl_msg.c:3887 => read record
I (17727) mbedtls: ssl_msg.c:1926 => fetch input
I (17733) mbedtls: ssl_msg.c:2066 in_left: 0, nb_want: 5
I (17740) mbedtls: ssl_msg.c:2086 in_left: 0, nb_want: 5
I (17744) mbedtls: ssl_msg.c:2089 ssl->f_recv(_timeout)() returned 5 (-0xfffffffb)
I (17752) mbedtls: ssl_msg.c:2111 <= fetch input
D (17758) mbedtls: ssl_msg.c:3634 input record: msgtype = 22, version = [0x303], msglen = 2466
I (17768) mbedtls: ssl_msg.c:1926 => fetch input
I (17772) mbedtls: ssl_msg.c:2066 in_left: 5, nb_want: 2471
I (17780) mbedtls: ssl_msg.c:2086 in_left: 5, nb_want: 2471
I (17785) mbedtls: ssl_msg.c:2089 ssl->f_recv(_timeout)() returned 2466 (-0xfffff65e)
I (17795) mbedtls: ssl_msg.c:2111 <= fetch input
D (17825) mbedtls: ssl_msg.c:3008 handshake message: msglen = 2466, type = 11, hslen = 2466
I (17827) mbedtls: ssl_msg.c:3959 <= read record
D (17829) mbedtls: ssl_tls.c:7241 peer certificate #1:
D (17834) mbedtls: ssl_tls.c:7241 cert. version     : 3
D (17840) mbedtls: ssl_tls.c:7241 serial number     : 44:AF:B0:80:D6:A3:27:BA:89:30:39:86:2E:F8:40:6B
D (17848) mbedtls: ssl_tls.c:7241 issuer name       : O=Digital Signature Trust Co., CN=DST Root CA X3
D (17858) mbedtls: ssl_tls.c:7241 subject name      : O=Digital Signature Trust Co., CN=DST Root CA X3
D (17868) mbedtls: ssl_tls.c:7241 issued  on        : 2000-09-30 21:12:19
D (17874) mbedtls: ssl_tls.c:7241 expires on        : 2021-09-30 14:01:15
D (17882) mbedtls: ssl_tls.c:7241 signed using      : RSA with SHA1
D (17888) mbedtls: ssl_tls.c:7241 RSA key size      : 2048 bits
D (17894) mbedtls: ssl_tls.c:7241 basic constraints : CA=true
D (17900) mbedtls: ssl_tls.c:7241 key usage         : Key Cert Sign, CRL Sign
D (17908) mbedtls: ssl_tls.c:7241 value of 'crt->rsa.N' (2048 bits) is:
D (17914) mbedtls: ssl_tls.c:7241  df af e9 97 50 08 83 57 b4 cc 62 65 f6 90 82 ec
D (17922) mbedtls: ssl_tls.c:7241  c7 d3 2c 6b 30 ca 5b ec d9 c3 7d c7 40 c1 18 14
D (17930) mbedtls: ssl_tls.c:7241  8b e0 e8 33 76 49 2a e3 3f 21 49 93 ac 4e 0e af
D (17938) mbedtls: ssl_tls.c:7241  3e 48 cb 65 ee fc d3 21 0f 65 d2 2a d9 32 8f 8c
D (17947) mbedtls: ssl_tls.c:7241  e5 f7 77 b0 12 7b b5 95 c0 89 a3 a9 ba ed 73 2e
D (17953) mbedtls: ssl_tls.c:7241  7a 0c 06 32 83 a2 7e 8a 14 30 cd 11 a0 e1 2a 38
D (17961) mbedtls: ssl_tls.c:7241  b9 79 0a 31 fd 50 bd 80 65 df b7 51 63 83 c8 e2
D (17969) mbedtls: ssl_tls.c:7241  88 61 ea 4b 61 81 ec 52 6b b9 a2 e2 4b 1a 28 9f
D (17977) mbedtls: ssl_tls.c:7241  48 a3 9e 0c da 09 8e 3e 17 2e 1e dd 20 df 5b c6
D (17986) mbedtls: ssl_tls.c:7241  2a 8a ab 2e bd 70 ad c5 0b 1a 25 90 74 72 c5 7b
D (17993) mbedtls: ssl_tls.c:7241  6a ab 34 d6 30 89 ff e5 68 13 7b 54 0b c8 d6 ae
D (18001) mbedtls: ssl_tls.c:7241  ec 5a 9c 92 1e 3d 64 b3 8c c6 df bf c9 41 70 ec
D (18009) mbedtls: ssl_tls.c:7241  16 72 d5 26 ec 38 55 39 43 d0 fc fd 18 5c 40 f1
D (18017) mbedtls: ssl_tls.c:7241  97 eb d5 9a 9b 8d 1d ba da 25 b9 c6 d8 df c1 15
D (18025) mbedtls: ssl_tls.c:7241  02 3a ab da 6e f1 3e 2e f5 5c 08 9c 3c d6 83 69
D (18033) mbedtls: ssl_tls.c:7241  e4 10 9b 19 2a b6 29 57 e3 e5 3d 9b 9f f0 02 5d
D (18039) mbedtls: ssl_tls.c:7241 value of 'crt->rsa.E' (17 bits) is:
D (18047) mbedtls: ssl_tls.c:7241  01 00 01
D (18051) mbedtls: ssl_tls.c:7325 Use configuration-specific verification callback
W (18058) mbedtls: ssl_tls.c:7378 x509_verify_cert() returned -9984 (-0x2700)
W (18066) mbedtls: ssl_tls.c:7425 bad certificate (usage extensions)
I (18074) mbedtls: ssl_msg.c:4868 => send alert message
D (18080) mbedtls: ssl_msg.c:4869 send alert level=2 message=42
I (18086) mbedtls: ssl_msg.c:2714 => write record
D (18092) mbedtls: ssl_msg.c:2798 output record: msgtype = 21, version = [3:3], msglen = 2
I (18100) mbedtls: ssl_msg.c:2124 => flush output
I (18106) mbedtls: ssl_msg.c:2138 message length: 7, out_left: 7
I (18114) mbedtls: ssl_msg.c:2145 ssl->f_send() returned 7 (-0xfffffff9)
I (18120) mbedtls: ssl_msg.c:2172 <= flush output
I (18126) mbedtls: ssl_msg.c:2851 <= write record
I (18131) mbedtls: ssl_msg.c:4880 <= send alert message
D (18137) mbedtls: ssl_tls.c:7483 ! Certificate verification flags 0000480c
I (18144) mbedtls: ssl_tls.c:3950 <= handshake
E (18149) esp-tls-mbedtls: mbedtls_ssl_handshake returned -0x2700
I (18156) esp-tls-mbedtls: (FFFFD900): X509 - Certificate verification failed, e.g. CRL, CA or signature check failed
E (18169) esp-tls: Failed to open new connection
E (18173) transport_base: Failed to open a new connection
E (18184) HTTP_CLIENT: Connection failed, sock < 0
E (18184) Sol: HTTP_EVENT_ERROR
D (18188) event: no handlers have been registered for event ESP_HTTP_CLIENT_EVENT:0 posted to loop 0x3fcddb28
E (18199) Sol: esp_http_client_open err=ESP_ERR_HTTP_CONNECT

Server Log (Caddy):

2024/07/23 13:53:58.495 DEBUG   events  event   {"name": "tls_get_certificate", "id": "4c4ca4fc-5227-468e-809d-19ae48f54687", "origin": "tls", "data": {"client_hello":{"CipherSuites":[49325,49162,49172,49327,49195,49199,49324,49187,49191,49161,49171,49326,49244,49248,49224,49228,49309,61,53,49167,49157,49313,156,49308,60,47,49201,49193,49166,49197,49189,49156,49312,49246,49250,49232,49226,49230,49212,255],"ServerName":"prod5.sindarin.com","SupportedCurves":[29,23,24,25,26,27,28],"SupportedPoints":"AA==","SignatureSchemes":[1027,1025],"SupportedProtos":null,"SupportedVersions":[771,770,769],"RemoteAddr":{"IP":"130.51.142.53","Port":62167,"Zone":""},"LocalAddr":{"IP":"64.225.15.171","Port":443,"Zone":""}}}}
2024/07/23 13:53:58.495 DEBUG   tls.handshake   choosing certificate    {"identifier": "prod5.sindarin.com", "num_choices": 1}
2024/07/23 13:53:58.495 DEBUG   tls.handshake   default certificate selection results   {"identifier": "prod5.sindarin.com", "subjects": ["prod5.sindarin.com"], "managed": true, "issuer_key": "acme-v02.api.letsencrypt.org-directory", "hash": "eb22767b8203303d93f0bc1727b42a77200fec476d4d8690ceb9c4897f4832d3"}
2024/07/23 13:53:58.495 DEBUG   tls.handshake   matched certificate in cache    {"remote_ip": "130.51.142.53", "remote_port": "62167", "subjects": ["prod5.sindarin.com"], "managed": true, "expiration": "2024/10/21 01:37:02.000", "hash": "eb22767b8203303d93f0bc1727b42a77200fec476d4d8690ceb9c4897f4832d3"}
2024/07/23 13:53:59.184 DEBUG   http.stdlib http: TLS handshake error from 130.51.142.53:62167: remote error: tls: bad certificate


### More Information.

I switched from Caddy to Nginx and configured the server to use both the "ISRG Root X1" and "ISRG Root X2" certificates. The X1 is the current valid root certificate for Let's Encrypt, but the X2 should also work. 

1. Has anyone encountered similar issues with ESP32 and SSL certificate renewals?
2. Are there known compatibility issues between mbedTLS 3.3.0 and recent Let's Encrypt certificates?
3. What server-side changes can I make to resolve this issue without updating the ESP32 firmware?
4. Are there any specific ESP-IDF or mbedTLS configurations that might help mitigate this problem?

Any insights or suggestions would be greatly appreciated. Thank you in advance for your help!
KaeLL commented 4 months ago

I had this issue too back in March. Ended up switching the esp crt bundle from minimal to full. The emergency update server bring-up was done by a coworker, and crisis was averted.

samuelclay commented 4 months ago

We're already using the full default certificate bundle, but what was the emergency update server bring-up? Does that mean you added certificates to the chain on your server? Which ones and how did you know which ones?

KaeLL commented 4 months ago

@windol

samuelclay commented 4 months ago

Solved the problem. I'd like to pay it forward here to anybody else who may come across this issue in the future. Here's what I did.

I built a minimal test case that (included below):

  1. Connected to wifi
  2. Fetched an https endpoint and return the status code
  3. Scanning for wifi networks if wifi failed

That allowed me to narrow down the issue. At first it worked. Then I imported my project's sdkconfig.defaults and adjusted a few of the configuration options so that it compiled (mainly about partition maps needing to change to address the larger flash size). And then I was able to reproduce the issue.

There were about 250 differences between the two sdkconfig settings. I used a binary search to narrow it down to a single setting: CONFIG_MBEDTLS_SHA512_C: Enable the SHA-384 and SHA-512 cryptographic hash algorithms

We had that turned off in our shipping code, while it's turned on by default. Switching it on got everything working again. Obviously I can't switch it on because these devices are out in the field and I can't change their code. But that led me down the path of figuring out what changes were needed in the SSL certificate.

I learned that our certificate was using a signature algorithm of ECDSA SHA-384. By creating a RSA 2048-bit certificate signing request (CSR) and using that CSR when applying for a SSL certificate, I could generate a certification that used RSA SHA-256, which would be supported by my devices using # CONFIG_MBEDTLS_SHA512_C is not set.

samuelclay commented 4 months ago

Here's esp32_https_test.c, my minimal test case:

#include "esp_crt_bundle.h"
#include "esp_event.h"
#include "esp_http_client.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "esp_tls.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include <stdio.h>

static const char *TAG = "HTTPS_TEST";
#define WIFI_POWER_19_5dBm 78
#define DEFAULT_SCAN_LIST_SIZE 10

#define WIFI_SSID "<snip>"
#define WIFI_PASS "<snip>"

esp_err_t _http_event_handler(esp_http_client_event_t *evt) {
  switch (evt->event_id) {
  case HTTP_EVENT_ERROR:
    ESP_LOGI(TAG, "HTTP_EVENT_ERROR");
    break;
  case HTTP_EVENT_ON_CONNECTED:
    ESP_LOGI(TAG, "HTTP_EVENT_ON_CONNECTED");
    break;
  case HTTP_EVENT_HEADER_SENT:
    ESP_LOGI(TAG, "HTTP_EVENT_HEADER_SENT");
    break;
  case HTTP_EVENT_ON_HEADER:
    ESP_LOGI(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key,
             evt->header_value);
    break;
  case HTTP_EVENT_ON_DATA:
    ESP_LOGI(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
    if (!esp_http_client_is_chunked_response(evt->client)) {
      printf("%.*s", evt->data_len, (char *)evt->data);
    }
    break;
  case HTTP_EVENT_ON_FINISH:
    ESP_LOGI(TAG, "HTTP_EVENT_ON_FINISH");
    break;
  case HTTP_EVENT_DISCONNECTED:
    ESP_LOGI(TAG, "HTTP_EVENT_DISCONNECTED");
    break;
  default:
    ESP_LOGI(TAG, "HTTP_EVENT_UNKNOWN, event=%d", evt->event_id);
    break;
  }
  return ESP_OK;
}

void create_client_and_test_website() {
  esp_http_client_config_t config = {
      .url = "https://example.com",
      .event_handler = _http_event_handler,
      .method = HTTP_METHOD_GET,
      .timeout_ms = 30000,
      .cert_pem = NULL,
      .crt_bundle_attach = esp_crt_bundle_attach,
  };

  esp_http_client_handle_t client = esp_http_client_init(&config);

  // Perform the HTTP GET request
  esp_err_t err = esp_http_client_perform(client);

  if (err == ESP_OK) {
    ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %" PRIi64,
             esp_http_client_get_status_code(client),
             esp_http_client_get_content_length(client));
  } else {
    ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
  }

  esp_http_client_cleanup(client);
}

void wifi_scan() {
  uint16_t number = DEFAULT_SCAN_LIST_SIZE;
  wifi_ap_record_t ap_info[DEFAULT_SCAN_LIST_SIZE];
  uint16_t ap_count = 0;
  memset(ap_info, 0, sizeof(ap_info));

  ESP_ERROR_CHECK(esp_wifi_scan_start(NULL, true));
  ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&number, ap_info));
  ESP_ERROR_CHECK(esp_wifi_scan_get_ap_num(&ap_count));
  ESP_LOGI(TAG, "Total APs scanned = %u", ap_count);
  for (int i = 0; (i < DEFAULT_SCAN_LIST_SIZE) && (i < ap_count); i++) {
    ESP_LOGI(TAG, "SSID \t\t%s", ap_info[i].ssid);
    ESP_LOGI(TAG, "RSSI \t\t%d", ap_info[i].rssi);
    ESP_LOGI(TAG, "Channel \t\t%d\n", ap_info[i].primary);
  }
}

static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data) {
  if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
    int8_t power = 0;
    esp_wifi_get_max_tx_power(&power);
    ESP_LOGI(TAG, "WiFi TX Power was %d", power);
    esp_wifi_set_max_tx_power(WIFI_POWER_19_5dBm);
    esp_wifi_get_max_tx_power(&power);
    ESP_LOGI(TAG, "WiFi TX Power is now %d", power);

    ESP_LOGI(TAG, "Connecting to WiFi...");
    esp_wifi_connect();
  } else if (event_base == WIFI_EVENT &&
             event_id == WIFI_EVENT_STA_DISCONNECTED) {
    wifi_event_sta_disconnected_t *disconnected =
        (wifi_event_sta_disconnected_t *)event_data;
    ESP_LOGI(TAG, "Disconnected from WiFi, reason: %d", disconnected->reason);
    switch (disconnected->reason) {
    case WIFI_REASON_AUTH_EXPIRE:
      ESP_LOGI(TAG, "Auth Expire");
      break;
    case WIFI_REASON_AUTH_FAIL:
      ESP_LOGI(TAG, "Authentication Failed, Check your SSID and Password");
      break;
    case WIFI_REASON_NO_AP_FOUND:
      ESP_LOGI(TAG, "AP not found, Check if the AP is available");
      break;
    case WIFI_REASON_BEACON_TIMEOUT:
      ESP_LOGI(TAG, "Beacon Timeout");
      break;
    case WIFI_REASON_ASSOC_LEAVE:
      ESP_LOGI(TAG, "Association Leave");
      break;
    case WIFI_REASON_HANDSHAKE_TIMEOUT:
      ESP_LOGI(TAG, "Handshake Timeout");
      break;
    default:
      ESP_LOGI(TAG, "WiFi disconnected, reconnecting...");
      break;
    }
    // Sleep 5 seconds before retrying
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    wifi_scan();
    esp_wifi_connect();
  } else {
    ESP_LOGI(TAG, "Unknown WiFi event: %ld", event_id);
  }
}

static void ip_event_handler(void *arg, esp_event_base_t event_base,
                             int32_t event_id, void *event_data) {
  if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
    create_client_and_test_website();
  } else if (event_base == WIFI_EVENT &&
             event_id == WIFI_EVENT_STA_DISCONNECTED) {
    wifi_event_sta_disconnected_t *disconnected =
        (wifi_event_sta_disconnected_t *)event_data;
    ESP_LOGI(TAG, "Disconnected from WiFi, reason: %d", disconnected->reason);
    switch (disconnected->reason) {
    case WIFI_REASON_AUTH_EXPIRE:
      ESP_LOGI(TAG, "Auth Expire");
      break;
    case WIFI_REASON_AUTH_FAIL:
      ESP_LOGI(TAG, "Authentication Failed, Check your SSID and Password");
      break;
    case WIFI_REASON_NO_AP_FOUND:
      ESP_LOGI(TAG, "AP not found, Check if the AP is available");
      break;
    case WIFI_REASON_BEACON_TIMEOUT:
      ESP_LOGI(TAG, "Beacon Timeout");
      break;
    case WIFI_REASON_ASSOC_LEAVE:
      ESP_LOGI(TAG, "Association Leave");
      break;
    case WIFI_REASON_HANDSHAKE_TIMEOUT:
      ESP_LOGI(TAG, "Handshake Timeout");
      break;
    default:
      ESP_LOGI(TAG, "WiFi disconnected, reconnecting...");
      break;
    }
    // Sleep 5 seconds before retrying
    vTaskDelay(5000 / portTICK_PERIOD_MS);
    wifi_scan();
    esp_wifi_connect();
  } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    ESP_LOGI(TAG, "Got IP: " IPSTR, IP2STR(&event->ip_info.ip));
    create_client_and_test_website();
  } else {
    ESP_LOGI(TAG, "Unknown IP event: %ld", event_id);
  }
}

void wifi_init() {
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());
  esp_netif_create_default_wifi_sta();
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  cfg.static_tx_buf_num = 0;
  cfg.dynamic_tx_buf_num = 32;
  cfg.tx_buf_type = 1;
  cfg.cache_tx_buf_num = 1;
  cfg.static_rx_buf_num = 4;
  cfg.dynamic_rx_buf_num = 32;
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  esp_event_handler_instance_t instance_any_id;
  esp_event_handler_instance_t instance_got_ip;
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL,
      &instance_any_id));
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      IP_EVENT, ESP_EVENT_ANY_ID, &ip_event_handler, NULL, &instance_got_ip));
  wifi_config_t wifi_config = {
      .sta =
          {
              .ssid = WIFI_SSID,
              .password = WIFI_PASS,
              .threshold.authmode = WIFI_AUTH_WPA2_PSK,
              .sae_pwe_h2e = WPA3_SAE_PWE_BOTH,
              .sae_h2e_identifier = "",
              .pmf_cfg = {.capable = true, .required = false},
          },
  };
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  esp_wifi_set_protocol(WIFI_IF_STA, WIFI_PROTOCOL_11B | WIFI_PROTOCOL_11G |
                                         WIFI_PROTOCOL_11N);

  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  ESP_ERROR_CHECK(esp_wifi_start());
  vTaskDelay(1000 / portTICK_PERIOD_MS);
  ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
}

void app_main(void) {
  esp_log_level_set("wifi", ESP_LOG_VERBOSE);
  esp_log_level_set("wifi_init", ESP_LOG_VERBOSE);

  esp_err_t ret = nvs_flash_init();
  if (ret == ESP_ERR_NVS_NO_FREE_PAGES ||
      ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
  }
  ESP_ERROR_CHECK(ret);
  wifi_init();
}