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?)


  • 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!

  • Related