nrf5340 NCS 2.8 cannot get TLS socket to load CA certif due to net_sock_tls / mbedtls error -0x262e

Using Zephyr in NCS 2.8 on nrf5340/nrf7002 wifi combo, and trying to get https to work. I'm using the option where the TLS setup is done in the socket layer (enabled in prj.conf)

# use TLS in socket directly
CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
CONFIG_TLS_CREDENTIALS=y
CONFIG_TLS_MAX_CREDENTIALS_NUMBER=10
CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=10

However, I cannot get this to parse a server certificate (.der or .pem formats). Every time it fails when it tries to parse the certificate to load the url, with the following:

[00:00:24.298,767] <inf> app: host [timeapi.io/api/time/current/zone?timeZone=Europe%2FParis&id="app.sysctrl.checkhttp"&value="00:00:00"] port [NULL] path [/api/time/current/zone?timeZone=Europe%2FParis&id="app.sysctrl.checkhttp"&value="00:00:00"] query [?timeZone=E
[00:00:24.322,906] <inf> app: Looking up host [timeapi.io][443], asking for path[/api/time/current/zone?timeZone=Europe%2FParis&id="app.sysctrl.checkhttp"&value="00:00:00"]

[00:00:24.360,046] <inf> app: Resolved 86.105.246.247 (AF_INET)
[00:00:24.366,485] <inf> app: httpmgr: checking certs/ca for certs
[00:00:24.376,586] <inf> app: httpmgr : registered cert [certs/ca/timeapi.io.der]
[00:00:24.387,054] <inf> app: httpmgr : registered cert [certs/ca/www.infrafon.cloud.der]
[00:00:24.395,996] <inf> app: httpmgr: checking certs/cli for certs
[00:00:24.403,930] <inf> app: httpmgr: added 2 TLS certificates
[00:00:24.410,308] <inf> app: httpmgr : TLS setup OK on socket
[00:00:24.416,595] <inf> app: Connecting to timeapi.io:443

[00:00:24.455,474] <err> net_sock_tls: Failed to parse CA certificate, err: -0x262e
[00:00:24.463,867] <wrn> app: Failed to connect socket (-1), [22]
[00:00:24.470,947] <wrn> app: uimgr : http req fails to start to time;timeapi.io/.../zone

Note:

 - I'm using timeapi.io as an easy test URL, not really because this is my target server!

 - at init, I load (once) the certificates from my flash FS by mallocing space for the data and then calling tls_credential_add() to give the buffer to the socket TLS system referenced by a "security tag" id for each one

 - when I create the socket, I setsockopt(TLS_SEC_TAG_LIST) it with the list of all the security tag values I previously added the certificates under so it can reference them.

The problem occurs when I call connect() on the socket, as that's when the TLS layer tries to parse the certificates....

In the debugger; the fail is in 

x509.c : mbedtls_x509_crt_parse()

which calls 

mbedtls_x509_crt_parse_der()
which ends up in 
x509_crt_parse_der_core()
As far as I can tell, its when it calls in line 1168:
mbedtls_x509_get_sig_alg() (in x509.c)
that it gets a return value of  -0x262e. in this function This is the add of 
MBEDTLS_ERR_X509_UNKNOWN_SIG_ALG + the ret from 
mbedtls_oid_get_sig_alg()
This is a function created by lovely macro cin oid.c:
int mbedtls_oid_get_sig_alg(const mbedtls_asn1_buf *oid, mbedtls_md_type_t * md_alg, mbedtls_pk_type_t * pk_alg) {
  const oid_sig_alg_t *data = oid_sig_alg_from_asn1(oid);
  if (data == ((void *)0))
       return -0x002E;
  *(md_alg) = data->md_alg;
  *(pk_alg) = data->pk_alg;
  return 0;
}
(btw this kind of macro *** really makes the debugger hard to follow...)
The oid_sig_alg_from_asn1() function which tries to extract the OID of the certificate's signature algo from the asn.1 and map it to the mbedtls types... is another macro defined function in oid.c:
FN_OID_TYPED_FROM_ASN1(oid_sig_alg_t, sig_alg, oid_sig_alg)
which gives us:
static const oid_sig_alg_t *oid_sig_alg_from_asn1( const mbedtls_asn1_buf *oid) {
  const oid_sig_alg_t *p = (oid_sig_alg);
  const mbedtls_oid_descriptor_t *cur = (const mbedtls_oid_descriptor_t *) p;
  if (p == ((void *)0) || oid == ((void *)0))
    return ((void *)0);
  while (cur->asn1 != ((void *)0)) {
    if (cur->asn1_len == oid->len && memcmp(cur->asn1, oid->p, oid->len) == 0) {
      return p;
    }
    p++;
    cur = (const mbedtls_oid_descriptor_t *) p;
  }
  return ((void *)0);
}
Unpacking this, its searching for the oid given in the certificate in a static array (oid_sig_alg in oic.c) generated with a load of ifdefs depending on which crypto algos are compiled in...
The certificate is using PKCS#1 SHA-256 with RSA (the public key of the certificate of timeapi.io is 2048 size) according to chrome's handy popup...
And indeed, in my oid.c, only sha256 with ECDSA algos are enabled...in fact no RSA algo is enabled (MBEDTLS_RSA_C is not defined)
All this hidden behinf that errno=-22 from connect().... lucky I got that nice error log from the TLS layer eh?
So, it seems I just need to define :
MBEDTLS_RSA_C
However, the saga continues - this define is already set in the mbedtls config file I select (zephyr/modules/mbedtls/config/config-mini-tls1_2.h) in my prj.conf:
CONFIG_NRF_SECURITY=n
CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=n
CONFIG_CUSTOM_MBEDTLS_CFG_FILE=y
CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
CONFIG_MBEDTLS_PEM_CERTIFICATE_FORMAT=y
However, visual code studio tells me that in the build NRF_SECURITY is 'y', and the MBEDTLS_CFG_FILE is set to 'nrf-config.h'!
The only place I can find this file is in
build/generated/library_nrf_security_psa/nrf-config.h
... and it indeed does NOT define MBEDTLS_RSA_C....
(and why does it all have to be so complicated and wrapped in 27 layers of kconfig files and macros???)
So finally my question : how do I get the nrf wrappers round the zephyr KConfig to accept that I want to use a specific mbedtls config, which includes MBEDTLS_RSA_C?
Also, it seems the sockets TLS code is hardcoded to use mbedtls functions : is there an alternative (which might take up less flash space, eg tinycrypt?
Naively I though I could just do
CONFIG_NRF_SECURITY=n
CONFIG_MBEDTLS=n
CONFIG_TINYCRYPT=y
but no joy (although maybe this is because its forcing mbedtls/nrf_security?)


Parents
  • So finally my question : how do I get the nrf wrappers round the zephyr KConfig to accept that I want to use a specific mbedtls config, which includes MBEDTLS_RSA_C?

    If I add CONFIG_MBEDTLS_RSA_C=y, then my flash overflows...

    prj.conf:

    CONFIG_NRF_SECURITY=y
    CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=y
    # TLS configuration - don't want everything in mbedtls as far too big
    #CONFIG_CUSTOM_MBEDTLS_CFG_FILE=y
    #CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_DTLS=n
    CONFIG_MBEDTLS_TLS_VERSION_1_2=y
    CONFIG_MBEDTLS_RSA_C=y

    So, I thought I'd remove EC crypto (as I can use just RSA certificates)? 

    CONFIG_MBEDTLS_ECDH_C=n
    but KConfig is conflicted about this.
    warning: MBEDTLS_PK_WRITE_C (defined at C:/ncs/v2.8.0/nrf\subsys\nrf_security\Kconfig.legacy:875, C:/ncs/v2.8.0/zephyr/modules/mbedtls\Kconfig.tls-generic:470, modules\mbedtls\Kconfig.tls-generic:470) has direct dependencies (MBEDTLS_PK_C && NRF_SECURITY) || (MBEDTLS_BUILTIN && MBEDTLS_CFG_FILE = "config-tls-generic.h" && MBEDTLS) || (MBEDTLS_BUILTIN && MBEDTLS_CFG_FILE = "config-tls-generic.h" && MBEDTLS && 0) with value n, but is currently being y-selected by the following symbols:
    - WIFI_NM_WPA_SUPPLICANT_CRYPTO_ALT_LEGACY_NCS (defined at C:/ncs/v2.8.0/zephyr/modules/hostap/Kconfig:215, modules/hostap/Kconfig:215, modules\hostap\Kconfig:215), with value y, direct dependencies <choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND> || <choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND> || <choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND> (value: y), and select condition <choice WIFI_NM_WPA_SUPPLICANT_CRYPTO_BACKEND> (value: y)
    Adding
    CONFIG_MBEDTLS_ECDSA_C=n
    fixed KConfig, but now the compile fails:
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c: In function 'crypto_ec_key_parse_priv':
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2354:5: error: unknown type name 'mbedtls_pk_context'; did you mean 'mbedtls_dhm_context'?
     2354 |     mbedtls_pk_context *ctx = os_malloc(sizeof(*ctx));
          |     ^~~~~~~~~~~~~~~~~~
          |     mbedtls_dhm_context
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2357:5: warning: implicit declaration of function 'mbedtls_pk_init'; did you mean 'mbedtls_mpi_init'? [-Wimplicit-function-declaration]
     2357 |     mbedtls_pk_init(ctx);
          |     ^~~~~~~~~~~~~~~
          |     mbedtls_mpi_init
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2361:9: warning: implicit declaration of function 'mbedtls_pk_parse_key'; did you mean 'mbedtls_ecp_write_key'? [-Wimplicit-function-declaration]
     2361 |     if (mbedtls_pk_parse_key(ctx, der, der_len, NULL, 0, mbedtls_ctr_drbg_random, crypto_mbedtls_ctr_drbg()) == 0)
          |         ^~~~~~~~~~~~~~~~~~~~
          |         mbedtls_ecp_write_key
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2365:5: warning: implicit declaration of function 'mbedtls_pk_free'; did you mean 'mbedtls_mpi_free'? [-Wimplicit-function-declaration]
     2365 |     mbedtls_pk_free(ctx);
          |     ^~~~~~~~~~~~~~~
          |     mbedtls_mpi_free
    In file included from C:/ncs/v2.8.0/modules/crypto/mbedtls/include/mbedtls/oid.h:17,
                     from C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2371:
    C:/ncs/v2.8.0/modules/crypto/mbedtls/include/mbedtls/pk.h: At top level:
    C:/ncs/v2.8.0/modules/crypto/mbedtls/include/mbedtls/pk.h:310:6: warning: conflicting types for 'mbedtls_pk_init'; have 'void(mbedtls_pk_context *)'
      310 | void mbedtls_pk_init(mbedtls_pk_context *ctx);
          |      ^~~~~~~~~~~~~~~
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2357:5: note: previous implicit declaration of 'mbedtls_pk_init' with type 'void(mbedtls_pk_context *)'
     2357 |     mbedtls_pk_init(ctx);
          |     ^~~~~~~~~~~~~~~
    C:/ncs/v2.8.0/modules/crypto/mbedtls/include/mbedtls/pk.h:323:6: warning: conflicting types for 'mbedtls_pk_free'; have 'void(mbedtls_pk_context *)'
      323 | void mbedtls_pk_free(mbedtls_pk_context *ctx);
          |      ^~~~~~~~~~~~~~~
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2365:5: note: previous implicit declaration of 'mbedtls_pk_free' with type 'void(mbedtls_pk_context *)'
     2365 |     mbedtls_pk_free(ctx);
          |     ^~~~~~~~~~~~~~~
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c: In function 'crypto_mbedtls_pk_parse_subpubkey_compressed':
    C:/ncs/v2.8.0/modules/lib/hostap/src/crypto/crypto_mbedtls_alt.c:2473:14: warning: implicit declaration of function 'crypto_mbedtls_short_weierstrass_derive_y' [-Wimplicit-function-declaration]
     2473 |           || crypto_mbedtls_short_weierstrass_derive_y(ecp_kp_grp, Y, (*p & 1));
          |              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    [246/718] Building C object zephyr/lib/libc/common/CMakeFiles/lib__libc__common.dir/source/time/asctime.c.obj
    ninja: build stopped: subcommand failed.
    FATAL ERROR: command exited with status 1: 'C:\ncs\toolchains\2d382dcd92\opt\bin\cmake.EXE' --build 'C:\work\dev\if-device-nrf53\cc1-med\build'
    How can I get a minimum TLS1.2 capable mbedtls build?
  • Hi, 

    BrianW said:
    Even if I provision this certificate in the TLS socket, the connection fails with the handsshake error -0x2700. This happens both with a generated certificate and also with the traefik auto generated certificate.

    Could you try with adding CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y in the prj.conf? Please see refer to https://academy.nordicsemi.com/courses/wi-fi-fundamentals/lessons/lesson-5-wifi-fundamentals/topic/lesson-5-exercise-2-2/ 

    BrianW said:
    is it not possible to provide TLS with the server's certificate directly? Does it have to have a CA/CA certificate to work?

    You can add with TLS_CREDENTIAL_SERVER_CERTIFICATE. See TLS credentials subsystem

    BrianW said:
    - the certificates have validaity dates. Does this mean that my device has to have the correct time/date to be able to validate this info?

    Yes, the device needs to have the correct time and date to properly validate certificate validity periods. See the Sensor Device Threat Model documentation

    -Amanda H.

  • BrianW said:
    is it not possible to provide TLS with the server's certificate directly? Does it have to have a CA/CA certificate to work?

    You can add with TLS_CREDENTIAL_SERVER_CERTIFICATE. See TLS credentials subsystem

    This doesn't seem to work for me. Specifically for the test site 

    https://eu.httpbin.org

    if I load the DER certificate for the root CA of this site (as TLS_CREDENTIAL_CA_CERTIFICATE), it works. If I don't load it, but instead load the server's DER certificate (as TLS_CREDENTIAL_SERVER_CERTIFICATE), then it fails with -0x2700...

    (both certificates retrieved from Chrome web browser)

    The documentation as to exactly what the TLS_CREDENTIAL_SERVER_CERTIFICATE is expected to do is missing; but when I looked in the code it seemed more like provision of a certificate used to validate TLS operations when running a http server locally (ie server side not client side). Is this correct?

    Yes, the device needs to have the correct time and date to properly validate certificate validity periods. See the Sensor Device Threat Model documentation

    I found this discussion https://github.com/zephyrproject-rtos/zephyr/issues/35401, where it appears that it should be possible to disable the date/time validity check (my device does not have a secure date/time source) by doing

    CONFIG_MBEDTLS_HAVE_TIME_DATE=n
    However this made no difference to the operation and the kconfig says it was 'n' anyway!
    This makes sense, as the eu.httpbin.org CA certificate also has validity dates, but works....
    Could you try with adding CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y in the prj.conf?

    YES! this seems to be the magic for my server - fixes it for both 2048bit RSA CA certificates (which I had reconfigured my server to use, and also for 4096bit ones as served by the LetsEncrypt CA server.

    I was not aware of the SNI part of TLS, nor indeed that my server using multiple domains on the same host would need it! And hence https to my server failed, but other work...

    (obligatory moan :  if the code had a slightly more explicit log  than error -0x2700, or if this error was documented as being "you need SNI enabled" then I might have got here quicker...)

  • Just to provide a quick summary of the prj.conf I required to get my https connection to work


    CONFIG_NRF_SECURITY=y
    CONFIG_MBEDTLS_LIBRARY_NRF_SECURITY=y
    # TLS configuration - don't want everything in mbedtls as far too big
    #CONFIG_CUSTOM_MBEDTLS_CFG_FILE=y
    #CONFIG_MBEDTLS_CFG_FILE="config-mini-tls1_2.h"
    CONFIG_MBEDTLS=y
    CONFIG_MBEDTLS_DTLS=n
    CONFIG_MBEDTLS_TLS_VERSION_1_2=y
    CONFIG_MBEDTLS_RSA_C=y
    CONFIG_MBEDTLS_ECP_C=n
    #CONFIG_MBEDTLS_ECDH_C=n    this breaks kconfig
    #CONFIG_MBEDTLS_ECDSA_C=n   this fixes kconfig, but breaks compile of hostap

    # PSA WANT request does nothing as PSA is not actually enabled?
    CONFIG_PSA_WANT_RSA_KEY_SIZE_4096=y
    CONFIG_PSA_WANT_RSA_KEY_SIZE_2048=y

    CONFIG_MBEDTLS_HAVE_TIME_DATE=n
    CONFIG_MBEDTLS_SSL_SERVER_NAME_INDICATION=y

    CONFIG_MBEDTLS_SSL_IN_CONTENT_LEN=16384
    CONFIG_MBEDTLS_SSL_OUT_CONTENT_LEN=16384
    CONFIG_MBEDTLS_MPI_MAX_SIZE=512
    CONFIG_NET_SOCKETS_TLS_SET_MAX_FRAGMENT_LENGTH=n
    CONFIG_MBEDTLS_SSL_RENEGOTIATION=y
    CONFIG_NET_SOCKETS_TLS_MAX_CONTEXTS=3

    CONFIG_PSA_WANT_ECC_SECP_R1_256=y
    CONFIG_PSA_WANT_ECC_MONTGOMERY_255=y
    # use TLS in socket directly
    CONFIG_NET_SOCKETS_SOCKOPT_TLS=y
    CONFIG_TLS_CREDENTIALS=y
    CONFIG_TLS_MAX_CREDENTIALS_NUMBER=10
    CONFIG_NET_SOCKETS_TLS_MAX_CREDENTIALS=10

    As I said above, it was the requirement for SNI that was the last stumbling block... but the rest is probably required for me to use 2048 and 4096 RSA signed DER certificates loaded into the TLS socket.

    This does not include the various memory / buffer / max configs neccessary to get the code and the RAM to fit in a nrf53; your milage will definitely vary for the optimum values for your app!

    Thanks Amanda!

  • Hi all,

    I'm working with the nRF9151 and running into a similar frustrating issue around using PSA Crypto with mbedTLS in the nRF Connect SDK. Specifically, I'm trying to minimize flash and RAM usage for a secure HTTP client like  , and I’m finding the documentation around PSA integration with mbedTLS to be very limited and unclear.

    I started from the https_client sample in NCS. While it works, the flash and RAM footprint is quite large. I then explored using offloaded TLS (via modem firmware) to save space, but then had errors due to the 2048 KB secure socket buffer limit. So I went back to the built-in mbedTLS stack with a goal of trimming it down by disabling unused features.That’s when things broke.

    I'm trying to use PSA Crypto for all cryptographic operations, expecting that mbedTLS would then act as a thin wrapper over PSA. In particular, I enabled:

    CONFIG_MBEDTLS_PSA_CRYPTO_C=y
    CONFIG_MBEDTLS_USE_PSA_CRYPTO=y

    My understanding is that CONFIG_MBEDTLS_USE_PSA_CRYPTO=y tells mbedTLS to delegate all crypto (hashing, ECDSA, RSA, etc.) to PSA (TF-M or the CC3XX backend). This should ideally reduce flash usage by avoiding duplication.

    But here's the issue: the TLS handshake fails on certificate parsing when using certificates signed with RSA (e.g., sha256WithRSAEncryption). The root cause appears to be that:

    • The X.509 parser in mbedTLS still needs to recognize and handle RSA signature OIDs, even if PSA is doing the actual verification.

    • However, enabling CONFIG_MBEDTLS_RSA_C=y (which defines RSA support in mbedTLS) is not allowed when CONFIG_MBEDTLS_USE_PSA_CRYPTO=y is enabled. These configs are mutually exclusive.

    So I’m stuck in this awkward spot where:

    • PSA can handle RSA signatures,

    • But mbedTLS won't parse or recognize them because RSA is not compiled into the legacy code.

    • I end up disabling CONFIG_MBEDTLS_USE_PSA_CRYPTO and back to square one with my flash size. 

    What’s confusing is that the boundaries between PSA and legacy mbedTLS are poorly documented:

    • Which parts of mbedTLS are "legacy"?

    • What gets offloaded to PSA?

    • What minimal configuration is required for PSA-only builds?

    It seems like mbedTLS still depends on legacy code for parsing and algorithm mapping, even when crypto execution is handled by PSA. But this isn’t documented clearly, and the Kconfig dependencies make it easy to break things by disabling unused features. 

    It would also to have to some examples of MINIMAL builds. Please can you let me know if the above is a bug?

  • Reply Children
    • I am now on NCS 2.9, but still with just mbedtls as it seems the PSA integration in Zephyr/NCS is not yet fully operational. 

      I stil find it incredible how much flash/RAM is required to do a 'simple' https GET over wifi using the nordic solution. It would be (IMHO) very much to Nordics benefit if they put some effort into providing a minimal config setup that actually works in the real world (ie with mix of RSA up to 4K, common ECC curves etc) and doesn't use up 2/3 of the available resources to do so....

      btw one of the reasons to move to 2.9 was to get fixes in the wifi, and to put the "wifi patches' (aka the nrf70 firmware) into the external flash as a DFU able image - that saved me nearly 80kB flash right there.... I lost a little to mcuboot (68kB) to be able to get 3 image DFUable system with the serial recovery over USB vcom, but it was worth it to gain the 80kB of wifi fw space....

    Related