Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 07:03 08 Jun 2026 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : More Claude magic - this is genuinely stunning (and frightening?)

Author Message
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 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}"
done

Claude 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 Any

I 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
 
PhenixRising
Guru

Joined: 07/11/2023
Location: United Kingdom
Posts: 1938
Posted: 12:29pm 07 Jun 2026
Copy link to clipboard 
Print this post

Ah-ha, I see what you did there 😂🤣😂🤣😂🤣
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3769
Posted: 03:11pm 07 Jun 2026
Copy link to clipboard 
Print this post

  Quote  I see what you did there


I don't, exactly. I mean, I see that you asked Claude to investigate and fix the encryption issue (and then fix other potential issues), however: this isn't just chatbot Claude, but (I guess), agentic Claude, with a lot of abilities to perform actions on your computer.

What is your setup like?
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on FOTS
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 11442
Posted: 03:22pm 07 Jun 2026
Copy link to clipboard 
Print this post

This is Claude code working within VSCode. It does ask for anything that affects the computer including searches but it clearly has the ability to do some pretty clever manipulations.
 
lizby
Guru

Joined: 17/05/2016
Location: United States
Posts: 3769
Posted: 08:22pm 07 Jun 2026
Copy link to clipboard 
Print this post

I'd guess some serious tokens were burned in the course of this interaction, with API pricing. Any idea what the cost of this episode was?

After one API venture (successful but not sustainable for me at $5/day for 5 days), I'm just bumming along with chatbot subscriptions to Gemini, Claude, and ChatGPT--adequate for my not intensive-at-present use, where supposedly the companies are subsidizing heavy use to the tune of $8-$13 per dollar of subscription. But there is evident throttling which wasn't taking place 4-5 months ago.
PicoMite, Armmite F4, SensorKits, MMBasic Hardware, Games, etc. on FOTS
 
Print this page


To reply to this topic, you need to log in.

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2026