matherp Guru Joined: 11/12/2012 Location: United KingdomPosts: 11442
Posted: 09:05am 07 Jun 2026
Copy link to clipboard
Print this post
Me in red My commentary in magenta The rest is Claude At numerous points I had to agree its actions but... Quote another web site reporting TLS client error -15 Dim buff%(16384/8) Const CRLF$ = Chr$(13)+Chr$(10) 'Const SITE$ = "www.elprisetjustnu.se" Const SITE$ = "se.elpris.eu" WEB NTP 2 idag$="20"+Mid$(Date$,9,2)+"/"+Mid$(Date$,4,2)+"-"+LEFT$(Date$,2) ' ' ------------ Build HTTP/1.1 Request ---------------------------- ' reg$ = "GET /api/v1/prices/"+idag$+"_SE4.json HTTP/1.1" + CRLF$ Cat reg$, "Host: " + site$ + CRLF$ Cat reg$, "User-Agent: WebMite" + CRLF$ Cat reg$, "Accept: application/json" + CRLF$ Cat reg$, "Connection: close" + CRLF$ + CRLF$ ' ' ----------- End Build HTTP/1.1 Request ------------------------- Print reg$ WEB open tls client SITE$ ,443 WEB tcp CLIENT REQUEST reg$,buff%(),10000 Pause 1000 WEB close tcp client I'll investigate what TLS client error -15 means in this firmware. Let me search the codebase. Grep "client error" 14 lines of output Grep "tls client" 10 lines of output Read d:\Dropbox\PicoMite\PicoMite\MMTCPclient.c (lines 61-180) Error -15 is the lwIP err_t code ERR_CLSD (connection closed). Let me confirm and look at how the TLS handshake/SNI is set up. Grep "ERR_CLSD|ERR_ABRT|ERR_RST" (in d:\Dropbox\PicoMite\PicoMite) 5 lines of output Grep "set_hostname|set_session|mbedtls_ssl_set_hostname|sni|SNI|altcp_tls_new|altcp_tls_alloc" (in d:\Dropbox\PicoMite\PicoMite\MMTCPclient.c) 7 lines of output Read d:\Dropbox\PicoMite\PicoMite\MMTCPclient.c (lines 196-315) Read d:\Dropbox\PicoMite\PicoMite\MMTCPclient.c (lines 315-444) SNI is being set correctly. Error -15 = ERR_CLSD. The server is closing the connection during the TLS handshake — that's a negotiation mismatch (TLS version, cipher suite, or curve the server requires but mbedtls isn't offering). Let me look at the TLS config. Grep "picomite_tls_get_client_config" (in d:\Dropbox\PicoMite\PicoMite) 5 lines of output Globpattern: "**/mbedtls_config.h" Found 7 files Grep "picomite_tls_get_client_config" (in d:\Dropbox\PicoMite\PicoMite\PicoMite.c) 61 lines of output Read d:\Dropbox\PicoMite\PicoMite\mbedtls_config.h Now I understand the TLS config. Notably: only TLS 1.2 is enabled (no MBEDTLS_SSL_PROTO_TLS1_3), and no ChaCha20. Let me probe what se.elpris.eu actually requires so we diagnose this definitively rather than guess. Bash Probe TLS handshake details for se.elpris.eu IN echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu 2>&1 | grep -iE "protocol|cipher|verify|new," | head -20 OUT New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384 Protocol: TLSv1.3 Verify return code: 0 (ok) Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Verify return code: 0 (ok) Protocol : TLSv1.3 Cipher : TLS_AES_256_GCM_SHA384 Verify return code: 0 (ok) verify return:1 verify return:1 verify return:1 The default handshake lands on TLS 1.3. The key question: does the server accept TLS 1.2 at all? Let me force it. Bash Force TLS 1.2 handshake to test support IN echo "=== Force TLS 1.2 ==="; echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 2>&1 | grep -iE "protocol|cipher|alert|handshake failure|error" | head; echo "=== TLS1.2 ciphers offered (if any) ==="; echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 2>&1 | grep -iE "Cipher :" | head OUT === Force TLS 1.2 === New, TLSv1.2, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384 Protocol: TLSv1.2 Protocol : TLSv1.2 Cipher : ECDHE-ECDSA-AES256-GCM-SHA384 === TLS1.2 ciphers offered (if any) === Cipher : ECDHE-ECDSA-AES256-GCM-SHA384 Important finding: the server does support TLS 1.2, negotiating ECDHE-ECDSA-AES256-GCM-SHA384 — a cipher the firmware's config supports. So the problem is something more specific. The cert is ECDSA. Let me check the cert chain (size and curve), since the config caps the TLS input buffer at 8 KB. Bash Examine cert chain size, count and details IN echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 -showcerts 2>/dev/null > /tmp/elpris_chain.txt; echo "=== chain raw size (bytes of PEM) ==="; wc -c < /tmp/elpris_chain.txt; echo "=== number of certs ==="; grep -c "BEGIN CERTIFICATE" /tmp/elpris_chain.txt; echo "=== per-cert subject / sig alg / key ==="; awk '/BEGIN CERT/,/END CERT/' /tmp/elpris_chain.txt | openssl crl2pkcs7 -nocrl -certfile /dev/stdin 2>/dev/null | openssl pkcs7 -print_certs -noout 2>/dev/null OUT Exit code 1 === chain raw size (bytes of PEM) === 4391 === number of certs === 2 === per-cert subject / sig alg / key === Bash Show leaf cert signature and key algorithm IN cd /tmp; echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 2>/dev/null | openssl x509 -noout -subject -issuer -text 2>/dev/null | grep -iE "subject:|issuer:|Signature Algorithm|Public Key Algorithm|ASN1 OID|NIST CURVE|Public-Key" | head -20 OUT Signature Algorithm: ecdsa-with-SHA384 Issuer: C=US, O=Let's Encrypt, CN=E8 Subject: CN=se.elpris.eu Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) ASN1 OID: prime256v1 NIST CURVE: P-256 Signature Algorithm: ecdsa-with-SHA384 Leaf is ECDSA P-256 (secp256r1, enabled), signed ecdsa-with-SHA384. Chain is only ~4.4 KB so the 8 KB buffer is fine. All primitives appear present. But one subtle thing stands out: the config defines MBEDTLS_SHA512_C but not MBEDTLS_SHA384_C. In recent mbedtls these are separate. Let me check the mbedtls version and whether SHA-384 needs its own define. Bash Locate mbedtls version header IN cd /d/Dropbox/PicoMite/PicoMite 2>/dev/null; f=$(find . -path ./.venv -prune -o -name "build_info.h" -print 2>/dev/null | head -1); echo "build_info: $f"; find . -path '*/mbedtls/*' -name "version.h" 2>/dev/null | grep -v venv | head; find . -name "mbedtls" -type d 2>/dev/null | grep -v venv | head OUT build_info: ./build/CMakeFiles/PicoMite.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls ./build/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls ./buildRP2040L/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls ./_tmp/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls Bash Check mbedtls version and SHA384_C presence IN v=$(find /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls -name "build_info.h" -o -name "version.h" 2>/dev/null | head -2); echo "$v"; grep -rE "MBEDTLS_VERSION_STRING\b" /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include 2>/dev/null | head; echo "=== Does this mbedtls have a separate SHA384_C? ==="; grep -rn "MBEDTLS_SHA384_C" /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/*.h 2>/dev/null | head OUT /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/build_info.h /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/version.h /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/build_info.h:#define MBEDTLS_VERSION_STRING "3.6.2" === Does this mbedtls have a separate SHA384_C? === /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h:180:#if defined(MBEDTLS_SHA384_C) /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_from_psa.h:608:#define MBEDTLS_SHA384_C /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_psa_from_legacy.h:202:#if defined(MBEDTLS_SHA384_C) /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_psa_superset_legacy.h:56:#if defined(MBEDTLS_SHA384_C) /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/mbedtls_config.h:1776: * (MBEDTLS_SHA256_C or MBEDTLS_SHA384_C or /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/mbedtls_config.h:2925: * MBEDTLS_SHA224_C, MBEDTLS_SHA256_C, MBEDTLS_SHA384_C, /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/mbedtls_config.h:3434: * \def MBEDTLS_SHA384_C /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/mbedtls_config.h:3447:#define MBEDTLS_SHA384_C /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/sha512.h:43:#if defined(MBEDTLS_SHA384_C) /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/sha512.h:90: * MBEDTLS_SHA384_C/MBEDTLS_SHA512_C symbols otherwise the mbedtls is 3.6.2, which has a separate MBEDTLS_SHA384_C. Let me check whether SHA512_C auto-enables SHA384_C (the legacy-crypto adjustment), because if it doesn't, SHA-384 is missing. Bash Check if SHA512_C implies SHA384_C in 3.6.2 IN grep -nE "SHA384_C|SHA512_C" /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h; echo "=== check.h relationship ==="; grep -n "SHA384" /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/check_config.h 2>/dev/null | head; echo "=== config doc for SHA384_C ==="; sed -n '3430,3450p' /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/mbedtls_config.h OUT 180:#if defined(MBEDTLS_SHA384_C) 184:#if defined(MBEDTLS_SHA512_C) === check.h relationship === 404: !defined(MBEDTLS_MD_CAN_SHA384) && \ 892: !(defined(MBEDTLS_MD_CAN_SHA256) || defined(MBEDTLS_MD_CAN_SHA384)) === config doc for SHA384_C === */ //#define MBEDTLS_SHA256_USE_A64_CRYPTO_ONLY /** * \def MBEDTLS_SHA384_C * * Enable the SHA-384 cryptographic hash algorithm. * * Module: library/sha512.c * Caller: library/md.c * library/psa_crypto_hash.c * library/ssl_tls.c * library/ssl*_client.c * library/ssl*_server.c * * Comment to disable SHA-384 */ #define MBEDTLS_SHA384_C /** * \def MBEDTLS_SHA512_C Bash Inspect MD_CAN_SHA384 derivation IN sed -n '175,195p' /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h; echo "=== MD_CAN_SHA384 definition ==="; grep -rn "MBEDTLS_MD_CAN_SHA384" /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h OUT #endif #if defined(MBEDTLS_SHA256_C) #define MBEDTLS_MD_CAN_SHA256 #define MBEDTLS_MD_SOME_LEGACY #endif #if defined(MBEDTLS_SHA384_C) #define MBEDTLS_MD_CAN_SHA384 #define MBEDTLS_MD_SOME_LEGACY #endif #if defined(MBEDTLS_SHA512_C) #define MBEDTLS_MD_CAN_SHA512 #define MBEDTLS_MD_SOME_LEGACY #endif #if defined(MBEDTLS_SHA3_C) #define MBEDTLS_MD_CAN_SHA3_224 #define MBEDTLS_MD_CAN_SHA3_256 #define MBEDTLS_MD_CAN_SHA3_384 #define MBEDTLS_MD_CAN_SHA3_512 #define MBEDTLS_MD_SOME_LEGACY #endif #if defined(MBEDTLS_RIPEMD160_C) === MD_CAN_SHA384 definition === 127:#define MBEDTLS_MD_CAN_SHA384 181:#define MBEDTLS_MD_CAN_SHA384 Confirmed the root cause mechanism. Let me verify line 127's context to be sure SHA-384 truly ends up unavailable with this config. Bash Context around line 127 MD_CAN_SHA384 IN sed -n '110,135p' /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/config_adjust_legacy_crypto.h OUT #endif #if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_1) #define MBEDTLS_MD_CAN_SHA1 #define MBEDTLS_MD_SHA1_VIA_PSA #define MBEDTLS_MD_SOME_PSA #endif #if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_224) #define MBEDTLS_MD_CAN_SHA224 #define MBEDTLS_MD_SHA224_VIA_PSA #define MBEDTLS_MD_SOME_PSA #endif #if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_256) #define MBEDTLS_MD_CAN_SHA256 #define MBEDTLS_MD_SHA256_VIA_PSA #define MBEDTLS_MD_SOME_PSA #endif #if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_384) #define MBEDTLS_MD_CAN_SHA384 #define MBEDTLS_MD_SHA384_VIA_PSA #define MBEDTLS_MD_SOME_PSA #endif #if defined(MBEDTLS_PSA_ACCEL_ALG_SHA_512) #define MBEDTLS_MD_CAN_SHA512 #define MBEDTLS_MD_SHA512_VIA_PSA #define MBEDTLS_MD_SOME_PSA #endif Line 127 is gated on PSA hardware accel (not enabled). Confirmed: with SHA512_C but no SHA384_C, MBEDTLS_MD_CAN_SHA384 is undefined → SHA-384 is unavailable in this firmware. That removes every *-SHA384 ciphersuite and the SHA-384 signature algorithms from mbedtls's ClientHello. The decisive test: does this server accept any SHA-256 TLS 1.2 cipher, or only SHA-384? Bash Test which TLS1.2 ciphers the server accepts IN for c in ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 ECDHE-ECDSA-CHACHA20-POLY1305; do r=$(echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 -cipher "$c" 2>&1 | grep -iE "Cipher :|alert|handshake failure|no ciphers" | head -1); printf "%-35s %s\n" "$c" "$r"; done OUT ECDHE-ECDSA-AES128-GCM-SHA256 Cipher : ECDHE-ECDSA-AES128-GCM-SHA256 ECDHE-ECDSA-AES256-GCM-SHA384 Cipher : ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-SHA256 Cipher : 0000 ECDHE-ECDSA-CHACHA20-POLY1305 Cipher : ECDHE-ECDSA-CHACHA20-POLY1305 Server accepts ECDHE-ECDSA-AES128-GCM-SHA256 (SHA-256, which mbedtls can do), so cipher availability alone isn't fatal. But the ServerKeyExchange signature and the cert signature matter. Let me emulate mbedtls's ClientHello more precisely: SHA-256 ciphers only and signature_algorithms without SHA-384. Bash Test handshake without SHA384 signature algorithms IN echo "=== emulate mbedtls: SHA256 cipher + sigalgs WITHOUT sha384 ==="; echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 \ -cipher "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256" \ -sigalgs "ECDSA+SHA256:RSA+SHA256:ECDSA+SHA512:RSA+SHA512" 2>&1 | grep -iE "Cipher :|Protocol :|alert|handshake failure|Peer signature|verify error|return code" | head; \ echo; echo "=== same but sigalgs WITH sha384 (control) ==="; echo | openssl s_client -connect se.elpris.eu:443 -servername se.elpris.eu -tls1_2 \ -cipher "ECDHE-ECDSA-AES128-GCM-SHA256" \ -sigalgs "ECDSA+SHA384:ECDSA+SHA256" 2>&1 | grep -iE "Cipher :|Peer signature|return code" | head OUT === emulate mbedtls: SHA256 cipher + sigalgs WITHOUT sha384 === Peer signature type: ecdsa_secp256r1_sha256 Protocol : TLSv1.2 Cipher : ECDHE-ECDSA-AES128-GCM-SHA256 Verify return code: 0 (ok) === same but sigalgs WITH sha384 (control) === Peer signature type: ecdsa_secp384r1_sha384 Cipher : ECDHE-ECDSA-AES128-GCM-SHA256 Verify return code: 0 (ok) With SHA-256-only sigalgs the handshake still completes (ecdsa_secp256r1_sha256), so negotiation isn't the wall. That points the finger at certificate parsing/verification: the chain is signed ecdsa-with-SHA384, and mbedtls must hash the cert with SHA-384 even just to parse/verify it. Let me confirm whether mbedtls 3.6.2 fails cert parsing when the signature MD is unavailable. Bash Find sig-alg handling in x509_crt parse IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/x509_crt.c; grep -nE "UNKNOWN_SIG_ALG|x509_get_sig_alg|sig_md|md_info_from_type|MBEDTLS_ERR_X509_INVALID_SIGNATURE" $f | head -30 OUT 1168: if ((ret = mbedtls_x509_get_sig_alg(&crt->sig_oid, &sig_params1, 1169: &crt->sig_md, &crt->sig_pk, 1807: crt->sig_md, crt->sig_opts); 2047: if (x509_profile_check_md_alg(profile, crl_list->sig_md) != 0) { 2056: psa_algorithm = mbedtls_md_psa_alg_from_type(crl_list->sig_md); 2068: md_info = mbedtls_md_info_from_type(crl_list->sig_md); 2085: crl_list->sig_md, hash, hash_length, 2132: md_info = mbedtls_md_info_from_type(child->sig_md); 2140: psa_algorithm_t hash_alg = mbedtls_md_psa_alg_from_type(child->sig_md); 2162: child->sig_md, hash, hash_len, 2170: child->sig_md, hash, hash_len, 2582: if (x509_profile_check_md_alg(profile, child->sig_md) != 0) { Bash Inspect x509_get_sig_alg behavior on unknown MD IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/x509.c; grep -n "mbedtls_x509_get_sig_alg" $f; echo "---"; sed -n '/int mbedtls_x509_get_sig_alg/,/^}/p' $f | head -60 OUT 718:int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, --- int mbedtls_x509_get_sig_alg(const mbedtls_x509_buf *sig_oid, const mbedtls_x509_buf *sig_params, mbedtls_md_type_t *md_alg, mbedtls_pk_type_t *pk_alg, void **sig_opts) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; if (*sig_opts != NULL) { return MBEDTLS_ERR_X509_BAD_INPUT_DATA; } if ((ret = mbedtls_oid_get_sig_alg(sig_oid, md_alg, pk_alg)) != 0) { return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG, ret); } #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) if (*pk_alg == MBEDTLS_PK_RSASSA_PSS) { mbedtls_pk_rsassa_pss_options *pss_opts; pss_opts = mbedtls_calloc(1, sizeof(mbedtls_pk_rsassa_pss_options)); if (pss_opts == NULL) { return MBEDTLS_ERR_X509_ALLOC_FAILED; } ret = mbedtls_x509_get_rsassa_pss_params(sig_params, md_alg, &pss_opts->mgf1_hash_id, &pss_opts->expected_salt_len); if (ret != 0) { mbedtls_free(pss_opts); return ret; } *sig_opts = (void *) pss_opts; } else #endif /* MBEDTLS_X509_RSASSA_PSS_SUPPORT */ { /* Make sure parameters are absent or NULL */ if ((sig_params->tag != MBEDTLS_ASN1_NULL && sig_params->tag != 0) || sig_params->len != 0) { return MBEDTLS_ERR_X509_INVALID_ALG; } } return 0; } mbedtls_oid_get_sig_alg drives this. In mbedtls 3.6 the OID sig-alg table entries are conditionally compiled on the hash being available. Let me check whether the ecdsa-with-SHA384 entry is gated on MBEDTLS_MD_CAN_SHA384. Bash Check OID sig-alg table gating for SHA384 IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/oid.c; grep -n "oid_sig_alg\|SHA384\|MD_CAN_SHA384\|ecdsa" $f | head -40 OUT 377:} oid_sig_alg_t; 379:static const oid_sig_alg_t oid_sig_alg[] = 408:#if defined(MBEDTLS_MD_CAN_SHA384) 410: OID_DESCRIPTOR(MBEDTLS_OID_PKCS1_SHA384, "sha384WithRSAEncryption", 412: MBEDTLS_MD_SHA384, MBEDTLS_PK_RSA, 414:#endif /* MBEDTLS_MD_CAN_SHA384 */ 432: OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA1, "ecdsa-with-SHA1", "ECDSA with SHA1"), 438: OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA224, "ecdsa-with-SHA224", "ECDSA with SHA224"), 444: OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA256, "ecdsa-with-SHA256", "ECDSA with SHA256"), 448:#if defined(MBEDTLS_MD_CAN_SHA384) 450: OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA384, "ecdsa-with-SHA384", "ECDSA with SHA384"), 451: MBEDTLS_MD_SHA384, MBEDTLS_PK_ECDSA, 453:#endif /* MBEDTLS_MD_CAN_SHA384 */ 456: OID_DESCRIPTOR(MBEDTLS_OID_ECDSA_SHA512, "ecdsa-with-SHA512", "ECDSA with SHA512"), 473:FN_OID_TYPED_FROM_ASN1(oid_sig_alg_t, sig_alg, oid_sig_alg) 477: oid_sig_alg_t, 484: oid_sig_alg_t, 491: oid_sig_alg_t, 492: oid_sig_alg, 746:#if defined(MBEDTLS_MD_CAN_SHA384) 748: OID_DESCRIPTOR(MBEDTLS_OID_DIGEST_ALG_SHA384, "id-sha384", "SHA-384"), 749: MBEDTLS_MD_SHA384, 830:#if defined(MBEDTLS_MD_CAN_SHA384) 832: OID_DESCRIPTOR(MBEDTLS_OID_HMAC_SHA384, "hmacSHA384", "HMAC-SHA-384"), 833: MBEDTLS_MD_SHA384, 835:#endif /* MBEDTLS_MD_CAN_SHA384 */ Decisive. oid.c:448-453: the ecdsa-with-SHA384 OID descriptor is compiled only #if defined(MBEDTLS_MD_CAN_SHA384). Let me confirm the parse aborts (not just degrades) when the OID lookup misses. Bash Confirm cert parse aborts on unknown sig alg IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/x509_crt.c; sed -n '1160,1185p' $f OUT if (crt->version < 0 || crt->version > 2) { mbedtls_x509_crt_free(crt); return MBEDTLS_ERR_X509_UNKNOWN_VERSION; } crt->version++; if ((ret = mbedtls_x509_get_sig_alg(&crt->sig_oid, &sig_params1, &crt->sig_md, &crt->sig_pk, &crt->sig_opts)) != 0) { mbedtls_x509_crt_free(crt); return ret; } /* * issuer Name */ crt->issuer_raw.p = p; if ((ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) { mbedtls_x509_crt_free(crt); return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_X509_INVALID_FORMAT, ret); } Root cause confirmed. Here's the complete chain: The firmware's mbedtls_config.h:89 enables MBEDTLS_SHA512_C but not MBEDTLS_SHA384_C. In mbedtls 3.6.2 these are separate, and MBEDTLS_MD_CAN_SHA384 is only derived from SHA384_C (or PSA accel, which you don't use). So SHA-384 is entirely absent — which knocks out the ecdsa-with-SHA384 OID descriptor at oid.c:448-453. se.elpris.eu (and the other failing sites) serve ECDSA P-256 leaf certs signed ecdsa-with-SHA384 — the standard for every Let's Encrypt ECDSA intermediate (E5–E8). When mbedtls parses that certificate during the handshake, x509_crt.c:1167 calls mbedtls_x509_get_sig_alg → mbedtls_oid_get_sig_alg, which can't find the OID → returns MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG → the cert parse aborts. This happens even with verification disabled — the cert can't be parsed, let alone verified. The handshake dies and altcp surfaces it as ERR_CLSD = -15. That's why it's site-class-specific: it isn't cipher negotiation (I confirmed the server happily does ECDHE-ECDSA-AES128-GCM-SHA256 and will even sign with ecdsa_secp256r1_sha256). The wall is the SHA-384-signed cert chain itself. RSA-cert sites (your earlier ECDHE-RSA fix) use sha256WithRSAEncryption leaves, so they parse fine. The fix Add MBEDTLS_SHA384_C. SHA-384 lives in sha512.c, which is already compiled because SHA512_C is on — so this is a near-zero-flash enable, same pattern as your [[project_tls_ecdhe_rsa_zerocost]] note. Edit d:\Dropbox\PicoMite\PicoMite\mbedtls_config.h Added 10 lines Now two follow-ups: the build dependency trap, and a note on the docs. Rebuild caveat: Per your [[project_tls_ecdhe_rsa_zerocost]] note, the build system doesn't always track mbedtls_config.h as a dependency of the prebuilt mbedtls objects (it's pulled in via -DMBEDTLS_CONFIG_FILE). To be sure oid.c, x509_crt.c and the cipher-suite table actually pick up the change, delete the mbedtls build objects (or clean-rebuild) rather than relying on an incremental build: Bash Locate compiled mbedtls oid object to confirm rebuild target IN ls -d /d/Dropbox/PicoMite/PicoMite/build/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir 2>/dev/null && find /d/Dropbox/PicoMite/PicoMite/build -path '*mbedtls*' -name "oid.c.obj" 2>/dev/null | head OUT /d/Dropbox/PicoMite/PicoMite/build/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir /d/Dropbox/PicoMite/PicoMite/build/CMakeFiles/PicoMite.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/oid.c.obj /d/Dropbox/PicoMite/PicoMite/build/_deps/picotool-build/lib/CMakeFiles/mbedtls.dir/D_/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/oid.c.obj The firmware compiles mbedtls into its own target (build/CMakeFiles/PicoMite.dir/.../library/). Force those objects to rebuild before your next build: Remove-Item -Recurse -Force "d:\Dropbox\PicoMite\PicoMite\build\CMakeFiles\PicoMite.dir\D_\Dropbox\PicoMite\pico-sdk\lib\mbedtls\library\*.obj" (or just delete the whole build dir for a clean rebuild). After rebuilding and flashing, se.elpris.eu should complete the handshake. Let me record this so the next ECDSA-site report is instant, and update the docs that currently only mention -13. Read d:\Dropbox\PicoMite\PicoMite\web.txt (lines 319-340) I'll leave the docs as-is (the -13 section is about CA-verification, a different path) and instead save the diagnosis to memory. Write C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\project_tls_sha384_required.md 13 lines --- name: project_tls_sha384_required description: TLS client error -15 on ECDSA-cert sites = mbedtls_config.h missing MBEDTLS_SHA384_C metadata: type: project --- "TLS client error -15" (lwIP ERR_CLSD) at `WEB OPEN TLS CLIENT` for sites like se.elpris.eu was caused by mbedtls_config.h enabling `MBEDTLS_SHA512_C` but NOT `MBEDTLS_SHA384_C`. In mbedtls 3.6.2 they are separate switches; `MBEDTLS_MD_CAN_SHA384` is derived only from SHA384_C (or PSA accel, unused here). Without SHA-384 the `ecdsa-with-SHA384` / `sha384WithRSA` OID descriptors are `#if`'d out in oid.c, so `mbedtls_x509_crt_parse_der` returns `MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG` and the cert parse fails — BEFORE verification, so VERIFY_NONE doesn't help. Handshake aborts → ERR_CLSD = -15. **Why:** Every Let's Encrypt ECDSA leaf (E5-E8 intermediates) is signed ecdsa-with-SHA384, so all such sites fail as a class. It is NOT cipher negotiation — the server will happily do ECDHE-ECDSA-AES128-GCM-SHA256 and sign SKE with ecdsa_secp256r1_sha256; the wall is parsing the SHA384-signed cert chain itself. RSA-cert sites (the [[project_tls_ecdhe_rsa_zerocost]] fix) use sha256WithRSAEncryption leaves so they parsed fine. **How to apply:** Add `#define MBEDTLS_SHA384_C` to mbedtls_config.h. Near-zero flash — SHA-384 lives in sha512.c, already compiled via SHA512_C. Same dep-tracking trap as [[project_tls_ecdhe_rsa_zerocost]]: the firmware compiles mbedtls into the PicoMite target with -DMBEDTLS_CONFIG_FILE, which isn't tracked as a dependency, so delete build/CMakeFiles/PicoMite.dir/.../mbedtls/library/*.obj (or clean build) to force recompile. Diagnose -15 vs -13: -13 (ERR_ABRT) is the CA-verification-failure path (see web.txt); -15 (ERR_CLSD) is server-closed-mid-handshake, of which unparseable cert is one cause. Read C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\MEMORY.md (lines 15-16) Edit C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\MEMORY.md Added 1 line Summary Diagnosis: TLS client error -15 is lwIP's ERR_CLSD. For se.elpris.eu (and the other failing site) the real cause is that mbedtls_config.h defined MBEDTLS_SHA512_C but not the separate MBEDTLS_SHA384_C that mbedtls 3.6.2 requires. Without SHA-384, the ecdsa-with-SHA384 OID descriptor is compiled out, so mbedtls can't even parse the site's SHA-384-signed ECDSA certificate — the handshake aborts before verification, and altcp reports it as -15. I verified each link in the chain against the live server and the mbedtls source: Server supports TLS 1.2 with ECDHE-ECDSA-AES256-GCM-SHA384; leaf is ECDSA P-256, signed ecdsa-with-SHA384 (Let's Encrypt E8). It's not negotiation: the server also accepts ECDHE-ECDSA-AES128-GCM-SHA256 and will sign with ecdsa_secp256r1_sha256 — both within mbedtls's reach. The blocker is cert parsing: oid.c:448-453 gates the OID on MBEDTLS_MD_CAN_SHA384, and x509_crt.c:1167 hard-fails when the lookup misses. Fix applied: Added #define MBEDTLS_SHA384_C to mbedtls_config.h. Near-zero flash cost (SHA-384 shares sha512.c, already compiled). This fixes the whole class of Let's Encrypt ECDSA sites, not just this one. Action you still need to take: Force the mbedtls objects to recompile (config file isn't dependency-tracked) by deleting build\CMakeFiles\PicoMite.dir\...\mbedtls\library\*.obj or doing a clean build, then reflash. I haven't run the build (it needs your board-specific config) — say the word if you'd like me to kick one off.At this point Claude has analysed the Webmite build in detail to understand which protocols are already included. It has then gone out to the external site and tried multiple protocol variations to understand what is missing and identified the changes needed in MMbasic to accomodate this site. I build the updated version which works first time and test against the site giving the problems which now works - all very impressive but the next bit is, to me, incredible Are there other protocols which we are not including that are in common use? Good question — the SHA-384 gap is one instance of a broader pattern, so let me audit the config against what public HTTPS actually uses in 2026. Let me verify which relevant modules are present/absent and what each would cost to add. Bash Audit firmware mbedtls config for common protocol switches IN cfg=/d/Dropbox/PicoMite/PicoMite/mbedtls_config.h echo "=== What's in the firmware config (grep key switches) ===" for s in MBEDTLS_SSL_PROTO_TLS1_3 MBEDTLS_SSL_PROTO_TLS1_2 MBEDTLS_CHACHA20_C MBEDTLS_POLY1305_C MBEDTLS_CHACHAPOLY_C MBEDTLS_X509_RSASSA_PSS_SUPPORT MBEDTLS_DHM_C MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED MBEDTLS_PSA_CRYPTO_C MBEDTLS_SSL_SESSION_TICKETS MBEDTLS_SSL_ENCRYPT_THEN_MAC MBEDTLS_SSL_EXTENDED_MASTER_SECRET; do if grep -q "^#define $s\b" "$cfg"; then echo " PRESENT : $s"; else echo " ABSENT : $s"; fi done OUT === What's in the firmware config (grep key switches) === ABSENT : MBEDTLS_SSL_PROTO_TLS1_3 PRESENT : MBEDTLS_SSL_PROTO_TLS1_2 ABSENT : MBEDTLS_CHACHA20_C ABSENT : MBEDTLS_POLY1305_C ABSENT : MBEDTLS_CHACHAPOLY_C ABSENT : MBEDTLS_X509_RSASSA_PSS_SUPPORT ABSENT : MBEDTLS_DHM_C ABSENT : MBEDTLS_KEY_EXCHANGE_DHE_RSA_ENABLED ABSENT : MBEDTLS_PSA_CRYPTO_C ABSENT : MBEDTLS_SSL_SESSION_TICKETS ABSENT : MBEDTLS_SSL_ENCRYPT_THEN_MAC ABSENT : MBEDTLS_SSL_EXTENDED_MASTER_SECRET Let me check the dependency cost of TLS 1.3 in this mbedtls version (3.6 ties it to PSA crypto, which is a big addition). Bash Check TLS1.3 and ChaChaPoly prerequisites IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/check_config.h echo "=== TLS 1.3 prerequisites enforced by check_config.h ===" grep -n "SSL_PROTO_TLS1_3" $f | head echo "---" awk '/MBEDTLS_SSL_PROTO_TLS1_3/{print NR": "$0}' $f | head echo echo "=== ChaChaPoly dependency (CHACHAPOLY needs CHACHA20+POLY1305) ===" grep -n "CHACHAPOLY_C" $f | head echo echo "=== are chacha/poly source files even in the tree? ===" ls /d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/library/ | grep -iE "chacha|poly1305|hkdf" OUT === TLS 1.3 prerequisites enforced by check_config.h === 807:#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ 812:#error "MBEDTLS_SSL_PROTO_TLS1_3 defined, but not all prerequisites" 832:#if defined(MBEDTLS_SSL_PROTO_TLS1_3) && !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) 833:#error "MBEDTLS_SSL_PROTO_TLS1_3 defined without MBEDTLS_SSL_KEEP_PEER_CERTIFICATE" 903: !( defined(MBEDTLS_SSL_PROTO_TLS1_2) || defined(MBEDTLS_SSL_PROTO_TLS1_3) ) 1068:#if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) && ( !defined(MBEDTLS_SSL_PROTO_TLS1_3) ) --- 807: #if defined(MBEDTLS_SSL_PROTO_TLS1_3) && \ 812: #error "MBEDTLS_SSL_PROTO_TLS1_3 defined, but not all prerequisites" 832: #if defined(MBEDTLS_SSL_PROTO_TLS1_3) && !defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) 833: #error "MBEDTLS_SSL_PROTO_TLS1_3 defined without MBEDTLS_SSL_KEEP_PEER_CERTIFICATE" 903: !( defined(MBEDTLS_SSL_PROTO_TLS1_2) || defined(MBEDTLS_SSL_PROTO_TLS1_3) ) 1068: #if defined(MBEDTLS_SSL_RECORD_SIZE_LIMIT) && ( !defined(MBEDTLS_SSL_PROTO_TLS1_3) ) === ChaChaPoly dependency (CHACHAPOLY needs CHACHA20+POLY1305) === 264:#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_CHACHA20_C) 265:#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites" 268:#if defined(MBEDTLS_CHACHAPOLY_C) && !defined(MBEDTLS_POLY1305_C) 269:#error "MBEDTLS_CHACHAPOLY_C defined, but not all prerequisites" === are chacha/poly source files even in the tree? === chacha20.c chachapoly.c hkdf.c poly1305.c Read d:\Dropbox\PicoMite\pico-sdk\lib\mbedtls\include\mbedtls\check_config.h (lines 806-835) I have what I need. Here's the honest assessment of what's missing versus what public HTTPS actually uses in 2026. Gaps, ranked by real-world impact 1. RSA-PSS certificate signatures — MBEDTLS_X509_RSASSA_PSS_SUPPORT (+ MBEDTLS_PKCS1_V21) This is the exact same failure mode you just hit, not a hypothetical. The code path we read in mbedtls_x509_get_sig_alg has a #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) block — without it, any certificate in a chain signed with rsassa-pss fails to parse, aborting the handshake with the same ERR_CLSD -15 before verification even runs. You currently have only MBEDTLS_PKCS1_V15; PSS needs MBEDTLS_PKCS1_V21. RSA-PSS leaf certs are still a minority of the public web but a growing one (and mandatory for RSA under TLS 1.3). Cost: small. This is the highest value-for-money fix and the natural companion to today's change. 2. ChaCha20-Poly1305 — MBEDTLS_CHACHA20_C + MBEDTLS_POLY1305_C + MBEDTLS_CHACHAPOLY_C Not an interop blocker — every server that offers ChaCha20 also offers AES-GCM, which you have. But two reasons it's worth it: (a) the source files are already in the tree, and (b) the RP2350 has no AES hardware, so ChaCha20-Poly1305 is faster in software than AES-GCM — a throughput win on exactly your platform. Cost: low-moderate flash. 3. TLS 1.3 — MBEDTLS_SSL_PROTO_TLS1_3 This is your one genuine protocol-level gap (you're TLS 1.2-only). But in mbedtls 3.6.2 it's expensive: check_config.h:807 requires the whole PSA crypto subsystem (MBEDTLS_PSA_CRYPTO_C, HKDF, the TLS 1.3 key-exchange modes, SSL_KEEP_PEER_CERTIFICATE). That's a large flash/RAM addition, and given your [[project_heap_bss_overlap]] and [[project_flash_target_offset_alignment]] constraints, non-trivial. Practically, ~all public HTTPS still accepts TLS 1.2, so this is future-proofing, not a current blocker. I'd hold off unless you start hitting TLS-1.3-only servers. Not worth adding DHE-RSA (MBEDTLS_DHM_C) — legacy finite-field DH, effectively dead on the public web; ECDHE covers everything. Session tickets / resumption, Encrypt-then-MAC, Extended Master Secret — optimizations/hardening, not interop requirements for a client. My recommendation Add #1 (RSA-PSS) now alongside the SHA-384 fix — same bug class, cheap, and closes the cert-parse-failure hole properly. Consider #2 (ChaCha20) for the RP2350 performance benefit. Defer #3 (TLS 1.3) until something actually needs it. Want me to add RSA-PSS (and optionally ChaCha20-Poly1305) to mbedtls_config.h? I'd verify the PKCS1_V21 prerequisite against check_config.h before writing it in.I think we have RSA compiled in is that not the case? You're right that RSA itself is compiled in — but that's a different thing from RSA-PSS. Let me be precise about the distinction and verify. Bash Confirm RSA vs PSS padding switches IN cfg=/d/Dropbox/PicoMite/PicoMite/mbedtls_config.h for s in MBEDTLS_RSA_C MBEDTLS_PKCS1_V15 MBEDTLS_PKCS1_V21 MBEDTLS_X509_RSASSA_PSS_SUPPORT MBEDTLS_KEY_EXCHANGE_RSA_ENABLED MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED; do if grep -q "^#define $s\b" "$cfg"; then echo " PRESENT : $s"; else echo " ABSENT : $s"; fi done OUT PRESENT : MBEDTLS_RSA_C PRESENT : MBEDTLS_PKCS1_V15 ABSENT : MBEDTLS_PKCS1_V21 ABSENT : MBEDTLS_X509_RSASSA_PSS_SUPPORT PRESENT : MBEDTLS_KEY_EXCHANGE_RSA_ENABLED PRESENT : MBEDTLS_KEY_EXCHANGE_ECDHE_RSA_ENABLED Yes — RSA is compiled in, but that's not the same thing. The result above shows the precise gap: MBEDTLS_RSA_C ✓ — the RSA algorithm (the modular-exponentiation math on RSA keys) MBEDTLS_PKCS1_V15 ✓ — the PKCS#1 v1.5 signature padding scheme MBEDTLS_PKCS1_V21 ✗ — the PSS padding scheme MBEDTLS_X509_RSASSA_PSS_SUPPORT ✗ — parsing PSS parameters out of a certificate The thing to separate is the key type vs. the signature scheme. An RSA key can sign with two completely different, non-interchangeable encodings: Scheme OID in the cert mbedtls switch RSASSA-PKCS1-v1_5 sha256WithRSAEncryption PKCS1_V15 ✓ you have RSASSA-PSS rsassa-pss (own params) PKCS1_V21 ✗ missing So you can verify a v1.5-padded RSA signature, but you cannot parse or verify a PSS-padded one — even though it's "the same RSA." It's exactly parallel to the SHA-384 situation: the math module is present, but the specific signatureAlgorithm OID the cert advertises isn't in your table, so mbedtls_x509_crt_parse_der bails out with UNKNOWN_SIG_ALG and the handshake dies with the same -15. That's why I listed RSA-PSS as a real (if smaller) hole rather than something you already cover. It's only the leaf/chain signature encoding that matters for parsing — and a growing minority of RSA certs (and all RSA certs under TLS 1.3) use PSS. So: do you want me to add MBEDTLS_PKCS1_V21 + MBEDTLS_X509_RSASSA_PSS_SUPPORT? It's a small addition that closes the cert-parse hole for PSS the same way SHA384_C did for SHA-384.Add it Let me verify the exact prerequisites in check_config.h before editing, so I don't introduce a build break. Bash Check PSS prerequisites in check_config IN f=/d/Dropbox/PicoMite/pico-sdk/lib/mbedtls/include/mbedtls/check_config.h echo "=== RSASSA_PSS_SUPPORT prerequisites ===" grep -n "RSASSA_PSS_SUPPORT" $f echo "---" awk '/RSASSA_PSS_SUPPORT/{c=3} c>0{print NR": "$0; c--}' $f echo echo "=== PKCS1_V21 references ===" grep -n "PKCS1_V21" $f OUT === RSASSA_PSS_SUPPORT prerequisites === 756:#if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ 758:#error "MBEDTLS_X509_RSASSA_PSS_SUPPORT defined, but not all prerequisites" --- 756: #if defined(MBEDTLS_X509_RSASSA_PSS_SUPPORT) && \ 757: ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_PKCS1_V21) ) 758: #error "MBEDTLS_X509_RSASSA_PSS_SUPPORT defined, but not all prerequisites" 759: #endif 760: === PKCS1_V21 references === 751:#if defined(MBEDTLS_RSA_C) && ( !defined(MBEDTLS_PKCS1_V21) && \ 757: ( !defined(MBEDTLS_RSA_C) || !defined(MBEDTLS_PKCS1_V21) ) 818: ( defined(MBEDTLS_PK_CAN_ECDSA_SIGN) || defined(MBEDTLS_PKCS1_V21) ) ) Prerequisites confirmed: X509_RSASSA_PSS_SUPPORT needs only MBEDTLS_RSA_C (present) + MBEDTLS_PKCS1_V21. Adding both together. Edit d:\Dropbox\PicoMite\PicoMite\mbedtls_config.h Added 10 lines Done. Added MBEDTLS_PKCS1_V21 + MBEDTLS_X509_RSASSA_PSS_SUPPORT to mbedtls_config.h, with a comment tying it to the same parse-trap class as the SHA-384 fix. Same rebuild caveat applies — the config isn't dependency-tracked against the prebuilt mbedtls objects, so force the recompile before your next build: Remove-Item -Recurse -Force "d:\Dropbox\PicoMite\PicoMite\build\CMakeFiles\PicoMite.dir\D_\Dropbox\PicoMite\pico-sdk\lib\mbedtls\library\*.obj" Let me fold this into the existing SHA-384 memory so the cert-parse-failure pattern is recorded as one entry rather than two. Edit C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\project_tls_sha384_required.md Edit failed Let me re-read the memory file and rewrite it. Read C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\project_tls_sha384_required.md Write C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\project_tls_sha384_required.md 18 lines --- name: project_tls_sha384_required description: TLS client error -15 = mbedtls_config.h can't PARSE the cert's signatureAlgorithm OID (missing SHA384_C and/or RSA-PSS) metadata: node_type: memory type: project originSessionId: f15499a2-da26-4211-8e1c-6354904098d9 --- "TLS client error -15" (lwIP ERR_CLSD) at `WEB OPEN TLS CLIENT` is a cert-PARSE failure: `mbedtls_x509_crt_parse_der` → `mbedtls_x509_get_sig_alg` → `mbedtls_oid_get_sig_alg` can't find the cert's signatureAlgorithm OID (descriptor `#if`'d out in oid.c), returns `MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG`, and x509_crt.c:~1167 frees+returns BEFORE verification — so VERIFY_NONE does NOT help. Handshake aborts = ERR_CLSD = -15. Two distinct OIDs hit this, both fixed (2026-06): 1. **SHA-384** (se.elpris.eu): config had `MBEDTLS_SHA512_C` but NOT `MBEDTLS_SHA384_C`. In mbedtls 3.6.2 they're separate; `MBEDTLS_MD_CAN_SHA384` derives only from SHA384_C (or PSA accel, unused), so the `ecdsa-with-SHA384`/`sha384WithRSA` descriptors vanish. Every Let's Encrypt ECDSA leaf (E5-E8 intermediates) is ecdsa-with-SHA384 → whole class fails. Fix: `#define MBEDTLS_SHA384_C` (near-zero flash; SHA-384 lives in sha512.c, already compiled via SHA512_C). 2. **RSA-PSS**: RSA_C + PKCS1_V15 only cover RSASSA-PKCS1-v1_5 (sha256WithRSAEncryption). A cert signed RSASSA-PSS uses OID rsassa-pss, compiled out without PKCS1_V21. Fix: `#define MBEDTLS_PKCS1_V21` + `#define MBEDTLS_X509_RSASSA_PSS_SUPPORT` (check_config.h:756 requires RSA_C + PKCS1_V21). All RSA certs under TLS 1.3 use PSS; a growing minority do in 1.2. **Why:** "RSA is compiled in" ≠ covers PSS — RSA the key/math (RSA_C) is separate from the signature padding scheme (V15 vs V21/PSS). Same trap as the missing SHA384: the primitive's math module is present, but the specific signatureAlgorithm OID isn't in the table. It is NOT cipher negotiation — servers will downgrade SKE to a hash/scheme the client offers; the wall is parsing the served cert CHAIN's own signatures. **How to apply / rebuild trap:** firmware compiles mbedtls INTO the PicoMite target via -DMBEDTLS_CONFIG_FILE, NOT dependency-tracked, so editing mbedtls_config.h alone won't recompile oid.c/x509_crt.c — delete build/CMakeFiles/PicoMite.dir/.../mbedtls/library/*.obj (or clean build). Same trap noted in [[project_tls_ecdhe_rsa_zerocost]]. Diagnose -15 vs -13: -13 (ERR_ABRT) = CA-verification-failure path (see web.txt); -15 (ERR_CLSD) = server closed mid-handshake, of which an unparseable cert sig OID is the main self-inflicted cause. Still ABSENT by choice (2026-06): TLS 1.3 (needs whole PSA crypto subsystem per check_config.h:807 — expensive, deferred) and ChaCha20-Poly1305 (faster than AES-GCM on RP2350's no-AES-hw, sources present, not yet enabled). Edit C:\Users\peter\.claude\projects\d--Dropbox-PicoMite-PicoMite\memory\MEMORY.md Modified Added and recorded. Summary of this change: mbedtls_config.h: added MBEDTLS_PKCS1_V21 + MBEDTLS_X509_RSASSA_PSS_SUPPORT (prerequisites checked against check_config.h:756 — only needs RSA_C, already present), with a comment tying it to the same parse-trap class as the SHA-384 fix. Folded both fixes into the memory entry as one "unparseable cert signatureAlgorithm OID → -15" pattern, and noted TLS 1.3 / ChaCha20 are deliberately deferred. Don't forget the recompile step (config isn't dependency-tracked) before your next build, then flash and retest an RSA-PSS or ECDSA site.I build the new version and check it hasn't caused RAM or FLASH allocation issues and still works on the various test sites Can you suggest a site to test RSA-PSS against RSA-PSS leaf certs are genuinely uncommon, so rather than guess I'll probe candidates and report which actually serve an rsassa-pss-signed cert in the chain. Let me scan some likely ones (German/EU government and TeleSec-CA sites tend to use PSS). Bash Probe candidate sites for RSA-PSS cert signatures IN for h in www.bsi.bund.de www.bund.de www.telekom.de www.t-systems.com www.deutschland.de service.bund.de www.gematik.de; do out=$(echo | timeout 12 openssl s_client -connect $h:443 -servername $h -showcerts 2>/dev/null) # extract each cert's signature algorithm algs=$(echo "$out" | awk '/BEGIN CERT/{c=1;b=""} c{b=b $0 ORS} /END CERT/{c=0; print b}' | while read -r -d '' _; do :; done; \ echo "$out" | awk 'BEGIN{RS="-----END CERTIFICATE-----"} /BEGIN CERTIFICATE/{print $0 "-----END CERTIFICATE-----"}' > /tmp/c.pem 2>/dev/null csplit -z -f /tmp/cc -b %02d.pem /tmp/c.pem '/BEGIN CERTIFICATE/' '{*}' >/dev/null 2>&1 for f in /tmp/cc*.pem; do [ -f "$f" ] && openssl x509 -in "$f" -noout -subject -text 2>/dev/null | grep -m1 -i "Signature Algorithm" | sed 's/^ *//'; done; rm -f /tmp/cc*.pem /tmp/c.pem) printf "=== %s ===\n%s\n" "$h" "${algs:-no response}" doneClaude is now testing remote sites to look for this specific protocol === www.bsi.bund.de === Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption === www.bund.de === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === www.telekom.de === Signature Algorithm: ecdsa-with-SHA256 Signature Algorithm: ecdsa-with-SHA256 Signature Algorithm: sha256WithRSAEncryption === www.t-systems.com === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === www.deutschland.de === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === service.bund.de === Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption === www.gematik.de === Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption None use PSS (bsi.bund.de uses sha512WithRSAEncryption — still PKCS#1 v1.5, but a useful SHA-512 test). PSS issuance is dominated by a few CAs like D-TRUST/Bundesdruckerei. Let me probe that cluster. Bash Probe D-TRUST / German bank cluster for PSS certs IN for h in www.d-trust.net www.bundesdruckerei.de www.bundesbank.de www.sparkasse.de www.dkb.de www.bafin.de www.bundestag.de www.admin.ch; do out=$(echo | timeout 12 openssl s_client -connect $h:443 -servername $h -showcerts 2>/dev/null) echo "$out" | awk 'BEGIN{RS="-----END CERTIFICATE-----"} /BEGIN CERTIFICATE/{print $0 "-----END CERTIFICATE-----"}' > /tmp/c.pem 2>/dev/null csplit -z -f /tmp/cc -b %02d.pem /tmp/c.pem '/BEGIN CERTIFICATE/' '{*}' >/dev/null 2>&1 algs="" for f in /tmp/cc*.pem; do [ -f "$f" ] && algs="$algs$(openssl x509 -in "$f" -noout -text 2>/dev/null | grep -m1 -i "Signature Algorithm" | sed 's/^ *//')"$'\n'; done rm -f /tmp/cc*.pem /tmp/c.pem printf "=== %s ===\n%s\n" "$h" "${algs:-no response}" done OUT === www.d-trust.net === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === www.bundesdruckerei.de === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === www.bundesbank.de === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha256WithRSAEncryption === www.sparkasse.de === Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption === www.dkb.de === Signature Algorithm: sha256WithRSAEncryption Signature Algorithm: sha384WithRSAEncryption === www.bafin.de === Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption Signature Algorithm: sha512WithRSAEncryption === www.bundestag.de === Signature Algorithm: ecdsa-with-SHA384 Signature Algorithm: sha256WithRSAEncryption === www.admin.ch === Signature Algorithm: ecdsa-with-SHA384 Signature Algorithm: ecdsa-with-SHA384 Signature Algorithm: ecdsa-with-SHA384 Signature Algorithm: sha256WithRSAEncryption Confirmed empirically that public RSA-PSS leaf certs are very rare — that whole cluster is PKCS#1 v1.5 (sha256/384/512WithRSAEncryption) or ECDSA. Hunting for one is unreliable. The robust way to test the PSS parse path is a local test server with a cert you sign using PSS — verification is off by default so a self-signed cert is fine; the point is purely to exercise parsing of the rsassa-pss signatureAlgorithm. Let me generate one and confirm openssl actually stamps it PSS.Now this is the impressive bit. It is going to create a web server on my PC with this very specific protocol in order to test the firmware Bash Generate and verify a self-signed RSASSA-PSS cert IN cd /tmp && rm -f pss.key pss.crt # Normal RSA key, but SIGN the cert with PSS padding -> signatureAlgorithm = rsassa-pss openssl req -x509 -newkey rsa:2048 -keyout pss.key -out pss.crt -days 365 -nodes \ -subj "/CN=picomite-pss-test" \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:-1 \ -sha256 2>/dev/null echo "=== signatureAlgorithm of generated cert ===" openssl x509 -in pss.crt -noout -text 2>/dev/null | grep -iA1 "Signature Algorithm" | head -4 echo "=== files ==="; ls -l /tmp/pss.key /tmp/pss.crt 2>/dev/null | awk '{print $5, $9}' OUT === signatureAlgorithm of generated cert === === files === 1732 /tmp/pss.key Cert generation failed silently — let me see the actual error. Bash Retry PSS cert generation showing errors IN cd /tmp && rm -f pss.key pss.crt openssl req -x509 -newkey rsa:2048 -keyout pss.key -out pss.crt -days 365 -nodes \ -subj "/CN=picomite-pss-test" \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest \ -sha256 2>&1 | head -20 echo "exit context done"; ls -l pss.crt 2>/dev/null OUT Exit code 2 .+......+....+.....+++++++++++++++++++++++++++++++++++++++*...+.+++++++++++++++++++++++++++++++++++++++*..........+.........+....+.....+.+......+...+..+...+.......++++++ .....+.........+......+...+++++++++++++++++++++++++++++++++++++++*..+.....+.+...+.....+..................+............+...+.............+++++++++++++++++++++++++++++++++++++++*.............+........+.+..............+.+........+...+.......+..+......+.......+......+......+...+..+....+.....+.++++++ ----- req: subject name is expected to be in the format /type0=value0/type1=value1/type2=... where characters may be escaped by \. This name is not in that format: 'C:/Program Files/Git/CN=picomite-pss-test' exit context done MSYS path mangling ate the -subj. Disabling path conversion: Bash Generate PSS cert with path conversion disabled IN cd /tmp && rm -f pss.key pss.crt MSYS_NO_PATHCONV=1 openssl req -x509 -newkey rsa:2048 -keyout pss.key -out pss.crt -days 365 -nodes \ -subj "/CN=picomite-pss-test" \ -sigopt rsa_padding_mode:pss -sigopt rsa_pss_saltlen:digest -sha256 2>/dev/null echo "=== signatureAlgorithm ===" openssl x509 -in pss.crt -noout -text 2>/dev/null | grep -iA1 "Signature Algorithm" | grep -iv "public key" | head -4 echo "=== files (bytes) ==="; ls -l pss.key pss.crt 2>/dev/null | awk '{print $5, $9}' OUT === signatureAlgorithm === Signature Algorithm: rsassaPss Hash Algorithm: sha256 -- Signature Algorithm: rsassaPss === files (bytes) === 1298 pss.crt 1732 pss.key Now I have a cert with Signature Algorithm: rsassaPss — exactly the OID that fails to parse without your new PKCS1_V21 + X509_RSASSA_PSS_SUPPORT. Let me get this PC's LAN IP so you can point the WebMite at it. PowerShell IN { "command": "Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' } | Select-Object IPAddress, InterfaceAlias | Format-Table -AutoSize", "description": "Find this PC's LAN IPv4 address" } OUT IPAddress InterfaceAlias --------- -------------- 10.5.0.2 NordLynx 192.168.1.117 WiFi 192.168.1.195 Ethernet LAN IPs: 192.168.1.117 (WiFi) / 192.168.1.195 (Ethernet) — use whichever subnet your WebMite is on. Let me put the test cert somewhere with a clean Windows path so the serve command is copy-pasteable. Bash Copy PSS test cert to project test folder IN mkdir -p /d/Dropbox/PicoMite/PicoMite/tls_pss_test && cp /tmp/pss.crt /tmp/pss.key /d/Dropbox/PicoMite/PicoMite/tls_pss_test/ && echo "Windows path: d:\\Dropbox\\PicoMite\\PicoMite\\tls_pss_test\\" && ls -l /d/Dropbox/PicoMite/PicoMite/tls_pss_test/ OUT Windows path: d:\Dropbox\PicoMite\PicoMite\tls_pss_test\ total 8 -rw-r--r-- 1 peter 197609 1298 Jun 6 19:45 pss.crt -rw-r--r-- 1 peter 197609 1732 Jun 6 19:45 pss.key My recommendation: don't hunt for a public RSA-PSS site — they're rare and you can't control them. The cluster I probed (German government, D-TRUST, banks, admin.ch) is entirely PKCS#1 v1.5 or ECDSA. Instead, run a local PSS test server with a cert I just generated whose signatureAlgorithm is rsassaPss — the exact OID that hard-fails to parse without your new switches. How to test 1. On this PC, start an HTTPS server with the PSS-signed cert (foreground; Ctrl-C to stop): & "C:\Program Files\Git\usr\bin\openssl.exe" s_server ` -cert "d:\Dropbox\PicoMite\PicoMite\tls_pss_test\pss.crt" ` -key "d:\Dropbox\PicoMite\PicoMite\tls_pss_test\pss.key" ` -accept 4433 -www -tls1_2 (Adjust the openssl path if yours differs — (Get-Command openssl).Source to find it. Allow it through the Windows firewall if prompted.) 2. On the WebMite (same LAN), point at this PC: Dim buff%(2048/8) WEB OPEN TLS CLIENT "192.168.1.117", 4433 ' .195 if WebMite is on the wired subnet WEB TCP CLIENT REQUEST "GET / HTTP/1.0" + Chr$(13)+Chr$(10) + Chr$(13)+Chr$(10), buff%() Pause 1000 Print LGetStr$(buff%(), 0, 400) WEB CLOSE TCP CLIENT Interpreting it Old firmware (before this change): WEB OPEN TLS CLIENT fails with TLS client error -15 — the leaf's rsassaPss OID can't be parsed. New firmware: handshake completes and you get the openssl s_server HTML status page back. That's a clean before/after that isolates exactly the PSS parse path. Note the cert's key is ordinary RSA-2048 (so the firmware negotiates ECDHE-RSA fine); only the certificate signature is PSS, which is what we're testing. Two housekeeping notes: The cert/key are throwaway test files in tls_pss_test/ — you'll probably want to gitignore or delete that folder, not commit it. A useful bonus target from the scan: www.bsi.bund.de serves a sha512WithRSAEncryption chain — a real public site that exercises your SHA-512 + RSA path (PKCS#1 v1.5, so it works with or without today's changes, but a good public RSA smoke test). Want me to add a .gitignore entry for tls_pss_test/? No good Connecting to 192.168.1.117 port 4433 (TLS) [24 WEB open tls client SITE$ ,4433 Error : No response from TLS server (handshake timeout) The error changed from -15 to a handshake timeout — that means the cert no longer fails to parse (good sign the rebuild took), but now nothing completes. A timeout (rather than a reset) usually means the ClientHello never reached the server or the ServerHello never came back — most often a firewall block on inbound 4433, or s_server not actually listening. Let me check reachability from here. PowerShell IN { "command": "Write-Output \"=== Is anything listening on 4433? ===\"; Get-NetTCPConnection -LocalPort 4433 -State Listen -ErrorAction SilentlyContinue | Select-Object LocalAddress,LocalPort,State,OwningProcess | Format-Table -AutoSize; Write-Output \"=== TCP reach to LAN IP:4433 from this host ===\"; Test-NetConnection -ComputerName 192.168.1.117 -Port 4433 -InformationLevel Quiet", "description": "Check listener and TCP reachability on 4433" } OUT === Is anything listening on 4433? === === TCP reach to LAN IP:4433 from this host === WARNING: TCP connect to (192.168.1.117 : 4433) failed False Nothing is listening on 4433 — so the WebMite's timeout is simply because the test server isn't up (the openssl s_server command never started or exited immediately). Let me verify the openssl path and that the cert/key actually load, then I'll start the server in the background so you can test right away. PowerShell IN { "command": "$o = (Get-Command openssl -ErrorAction SilentlyContinue).Source; if (-not $o) { $o = \"C:\\Program Files\\Git\\usr\\bin\\openssl.exe\" }; Write-Output \"openssl: $o\"; Write-Output \"=== validate cert/key load & match ===\"; & $o x509 -in \"d:\\Dropbox\\PicoMite\\PicoMite\\tls_pss_test\\pss.crt\" -noout -subject; & $o rsa -in \"d:\\Dropbox\\PicoMite\\PicoMite\\tls_pss_test\\pss.key\" -noout -check", "description": "Locate openssl and validate test cert/key" } OUT openssl: C:\Program Files\Git\usr\bin\openssl.exe === validate cert/key load & match === subject=CN=picomite-pss-test RSA key ok Cert and key are valid and matched. The earlier s_server invocation must have failed to stay up. Let me start it in the background here so you can test immediately, then confirm it's actually serving the PSS cert over the LAN. Bash Run PSS TLS test server in background IN cd /d/Dropbox/PicoMite/PicoMite/tls_pss_test && "/c/Program Files/Git/usr/bin/openssl.exe" s_server -cert pss.crt -key pss.key -accept 4433 -www -tls1_2 OUT Command running in background with ID: bbv97czbv. Output is being written to: C:\Users\peter\AppData\Local\Temp\claude\d--Dropbox-PicoMite-PicoMite\f15499a2-da26-4211-8e1c-6354904098d9\tasks\bbv97czbv.output. You will be notified when it completes. To check interim output, use Read on that file path. Let me confirm it's listening and actually serving the PSS cert, and open the firewall for inbound 4433 if needed. PowerShell IN { "command": "Start-Sleep -Milliseconds 800\nWrite-Output \"=== listener ===\"; Get-NetTCPConnection -LocalPort 4433 -State Listen -ErrorAction SilentlyContinue | Select-Object LocalAddress,LocalPort | Format-Table -AutoSize\nWrite-Output \"=== handshake probe (peer cert sig alg) ===\"\n$o = \"C:\\Program Files\\Git\\usr\\bin\\openssl.exe\"\n\"Q\" | & $o s_client -connect 127.0.0.1:4433 -tls1_2 2>$null | Select-String -Pattern \"Protocol|Cipher|subject=\" | Select-Object -First 3\nWrite-Output \"=== firewall rule for 4433 (add if missing) ===\"\nif (-not (Get-NetFirewallRule -DisplayName \"PicoMite TLS test 4433\" -ErrorAction SilentlyContinue)) {\n New-NetFirewallRule -DisplayName \"PicoMite TLS test 4433\" -Direction Inbound -Protocol TCP -LocalPort 4433 -Action Allow -Profile Private | Out-Null\n Write-Output \"rule added (Private profile)\"\n} else { Write-Output \"rule already present\" }", "description": "Verify listener, handshake, and add firewall rule" } OUT === listener === LocalAddress LocalPort ------------ --------- :: 4433 === handshake probe (peer cert sig alg) === subject=CN=picomite-pss-test New, TLSv1.2, Cipher is ECDHE-RSA-AES256-GCM-SHA384 Protocol: TLSv1.2 === firewall rule for 4433 (add if missing) === New-NetFirewallRule : Access is denied. At line:8 char:3 + New-NetFirewallRule -DisplayName "PicoMite TLS test 4433" -Directio ... + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (MSFT_NetFirewallRule:root/standardcimv2/MSFT_NetFirewallRule) [New-Ne tFirewallRule], CimException + FullyQualifiedErrorId : Windows System Error 5,New-NetFirewallRule rule added (Private profile) The server is up and serving correctly — the loopback handshake negotiated ECDHE-RSA-AES256-GCM-SHA384 against CN=picomite-pss-test, and it's a dual-stack (::) socket so IPv4 is covered. The remaining blocker is the Windows Firewall: loopback works, but the inbound LAN port is blocked, and I couldn't add the allow-rule (it needs admin — the "rule added" message was misleading; the command actually hit Access is denied). Let me confirm the network profile so we know which one to open. PowerShell IN { "command": "Write-Output \"=== active network profiles ===\"; Get-NetConnectionProfile | Select-Object InterfaceAlias,NetworkCategory | Format-Table -AutoSize\nWrite-Output \"=== firewall enabled per profile ===\"; Get-NetFirewallProfile | Select-Object Name,Enabled | Format-Table -AutoSize\nWrite-Output \"=== any existing allow rule mentioning 4433 or openssl ===\"; Get-NetFirewallPortFilter | Where-Object {$_.LocalPort -eq 4433} | Measure-Object | Select-Object -ExpandProperty Count", "description": "Check firewall profile and rules" } OUT Exit code 1 === active network profiles === InterfaceAlias NetworkCategory -------------- --------------- Ethernet Public NordLynx Public === firewall enabled per profile === Name Enabled ---- ------- Domain True Private True Public True === any existing allow rule mentioning 4433 or openssl === Get-NetFirewallPortFilter : Access is denied. At line:3 char:76 + ... le mentioning 4433 or openssl ==="; Get-NetFirewallPortFilter | Where ... + ~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : PermissionDenied: (MSFT_NetProtocolPortFilter:root/standardci...tocolPortFilter) [Get-Ne tFirewallPortFilter], CimException + FullyQualifiedErrorId : Windows System Error 5,Get-NetFirewallPortFilter 0 Found it. The active LAN connection (Ethernet) is on the Public firewall profile, which blocks inbound by default — that's why loopback succeeds but the WebMite's connection to port 4433 is silently dropped (→ handshake timeout). Adding the rule needs an elevated shell, which I can't spawn from here. Also note WiFi (192.168.1.117) no longer shows as connected — only Ethernet (.195) and NordLynx (VPN) are active, so the WebMite may also be aiming at a dead IP. Let me confirm the current addresses. PowerShell IN { "command": "Get-NetIPAddress -AddressFamily IPv4 | Where-Object { $_.IPAddress -notlike '127.*' -and $_.IPAddress -notlike '169.254.*' } | Select-Object IPAddress, InterfaceAlias, @{n='Up';e={(Get-NetAdapter -InterfaceIndex $_.InterfaceIndex).Status}} | Format-Table -AutoSize", "description": "Recheck current IPv4 addresses and adapter status" } OUT IPAddress InterfaceAlias Up --------- -------------- -- 10.5.0.2 NordLynx Up 192.168.1.117 WiFi Disconnected 192.168.1.195 Ethernet Up Two concrete problems, both explaining the timeout: 1. The WebMite is aiming at a dead IP. WiFi 192.168.1.117 is now Disconnected. Your live LAN interface is Ethernet → 192.168.1.195. Point the test there: WEB OPEN TLS CLIENT "192.168.1.195", 4433 2. The firewall is blocking inbound 4433. Ethernet is on the Public profile (inbound blocked by default), which is why my loopback handshake works but the WebMite can't get in. Adding the allow-rule needs admin — run this in an elevated PowerShell (Win+X → "Terminal (Admin)"): New-NetFirewallRule -DisplayName "PicoMite TLS test 4433" -Direction Inbound -Protocol TCP -LocalPort 4433 -Action Allow -Profile Any The test server is still running in the background here (serving the rsassaPss cert), so once you've added the rule and changed the IP, just re-run the WebMite program. One thing to watch: NordLynx (NordVPN) is up. LAN replies to 192.168.1.x go out the directly-connected Ethernet, not the tunnel, so it should be fine — but if it still times out after the firewall fix, NordVPN's kill-switch/LAN-isolation is the next suspect (temporarily disabling it, or enabling "Allow LAN" in NordVPN settings, rules it out). Expected result after both fixes: the handshake completes and you get openssl's HTML status page back — confirming the PKCS1_V21 + X509_RSASSA_PSS_SUPPORT parse path works. Want me to delete the firewall rule and stop the server when you're done? Windows PowerShell Copyright (C) Microsoft Corporation. All rights reserved. Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows PS C:\WINDOWS\system32> New-NetFirewallRule -DisplayName "PicoMite TLS test 4433" -Direction Inbound -Protocol TCP -LocalPort 4433 -Action Allow -Profile AnyI follow the instructions and enable my firewall to allow port 4433 Name : {5b53829b-3b2d-4fbe-bc1b-5d164da2e65d} DisplayName : PicoMite TLS test 4433 Description : DisplayGroup : Group : Enabled : True Profile : Any Platform : {} Direction : Inbound Action : Allow EdgeTraversalPolicy : Block LooseSourceMapping : False LocalOnlyMapping : False Owner : PrimaryStatus : OK Status : The rule was parsed successfully from the store. (65536) EnforcementStatus : NotApplicable PolicyStoreSource : PersistentStore PolicyStoreSourceType : Local RemoteDynamicKeywordAddresses : {} PolicyAppId : PackageFamilyName : Firewall rule is in. Let me confirm the test server is still up and listening before you retry. PowerShell IN { "command": "$l = Get-NetTCPConnection -LocalPort 4433 -State Listen -ErrorAction SilentlyContinue; if ($l) { Write-Output \"Listener UP on 4433 (PID $($l[0].OwningProcess))\" } else { Write-Output \"NO listener on 4433 - server died, needs restart\" }", "description": "Confirm test server still listening" } OUT Listener UP on 4433 (PID 46696) Server's still up and listening (PID 46696), firewall rule is active. You're clear to test — just make sure the WebMite targets the live IP: WEB OPEN TLS CLIENT "192.168.1.195", 4433 (.117/WiFi is disconnected — use .195/Ethernet.) Expected: the handshake now completes and you get openssl's HTML status page back in buff%(), confirming the rsassaPss cert parses with your new PKCS1_V21 + X509_RSASSA_PSS_SUPPORT build. If it still times out, NordVPN's LAN isolation is the only remaining suspect — toggle "Allow LAN" or drop the VPN briefly to rule it out. Go ahead and run it — tell me what comes back and I'll help interpret, then clean up the server and firewall rule when you're done.Connecting to 192.168.1.117 port 4433 (TLS) [24 WEB open tls client SITE$ ,4433 Error : No response from TLS server (handshake timeout) The connection is still going to 192.168.1.117 — that's the disconnected WiFi IP. SITE$ in your program is still set to .117. Change it to the live Ethernet IP: Const SITE$ = "192.168.1.195" The console line Connecting to 192.168.1.117 is the giveaway — until that reads 192.168.1.195, it's hitting a dead address and will always time out regardless of the firewall/server (both of which are confirmed good on this end). Update SITE$, re-run, and report what comes back.Works on 195 That confirms the fix end-to-end: the rsassaPss-signed certificate now parses and the handshake completes, exactly what PKCS1_V21 + X509_RSASSA_PSS_SUPPORT was meant to unlock. Both your gaps (SHA-384 and RSA-PSS) are verified closed against real handshakes. Now let me clean up what I spun up for the test. PowerShell IN { "command": "$p = Get-NetTCPConnection -LocalPort 4433 -State Listen -ErrorAction SilentlyContinue; if ($p) { Stop-Process -Id $p[0].OwningProcess -Force; Write-Output \"stopped openssl s_server (PID $($p[0].OwningProcess))\" } else { Write-Output \"no listener to stop\" }", "description": "Stop the background test server" } OUT stopped openssl s_server (PID 46696) Server stopped (background task ended cleanly). Let me remove the throwaway test cert/key folder. PowerShell IN { "command": "Remove-Item -Recurse -Force \"d:\\Dropbox\\PicoMite\\PicoMite\\tls_pss_test\" -ErrorAction SilentlyContinue; if (Test-Path \"d:\\Dropbox\\PicoMite\\PicoMite\\tls_pss_test\") { Write-Output \"folder still present\" } else { Write-Output \"tls_pss_test removed\" }", "description": "Delete test cert folder" } OUT tls_pss_test removed One last cleanup item needs your admin shell — removing the firewall rule I had you add (I can't do it unelevated). Run this in an elevated PowerShell when convenient: Remove-NetFirewallRule -DisplayName "PicoMite TLS test 4433" Wrap-up Both TLS config gaps are now fixed and verified against live handshakes: SHA-384 (MBEDTLS_SHA384_C) — fixes the original -15 on se.elpris.eu and all Let's Encrypt ECDSA (E5–E8) sites. RSA-PSS (MBEDTLS_PKCS1_V21 + MBEDTLS_X509_RSASSA_PSS_SUPPORT) — just confirmed working with the local rsassaPss server on .195. Both were the same root cause: a certificate signatureAlgorithm OID that mbedtls couldn't parse (before verification), surfacing as ERR_CLSD -15. That diagnosis and both fixes are saved in memory (project_tls_sha384_required), along with the -DMBEDTLS_CONFIG_FILE rebuild trap and the note that TLS 1.3 and ChaCha20-Poly1305 remain deliberately deferred. All temporary test artifacts (server, cert folder) are cleaned up except that firewall rule above. Nice work — that's a solid, well-tested closure of the cert-parse hole.Port removed from firewall. Just tested www.bsi.bund.de - works perfectly That's a great confirmation — www.bsi.bund.de serves a sha512WithRSAEncryption chain, so it exercises the SHA-512 + RSA PKCS#1 v1.5 path against a real public site (not a local test rig). Combined with se.elpris.eu (ECDSA/SHA-384) and the local PSS server, you've now got all three signature-parse paths verified end-to-end: ECDSA + SHA-384 → se.elpris.eu ✓ RSA PKCS#1 v1.5 + SHA-512 → www.bsi.bund.de ✓ RSA-PSS → local rsassaPss server ✓ The cert-parse hole is comprehensively closed. Nothing more needed from me here — just that firewall rule removal in an admin shell when you get to it: Remove-NetFirewallRule -DisplayName "PicoMite TLS test 4433" Enjoy the broader HTTPS reach.Edited 2026-06-07 19:27 by matherp