Securing IoT software deployments with Mender and NXP EdgeLock™ SE050

Introduction


SE050 is a Common Criteria EAL 6+ certified secure element supporting both RSA and ECC asymmetric cryptographic algorithms by NXP, and providing a complete security solution for IoT devices. In this tutorial we are going to show how to use it with Mender on a Raspberry Pi, to provide an extra layer of security to the robust over the air IoT updates. The general idea behind it is to pass all the cryptographic operations to the secure element. It means that there is no need for the private key to exist
in any plain file, which in turn implies that it is much harder to use it in a malicious way, e.g.: to impersonate a device.

Prerequisites for this tutorial

We are assuming that you already have followed the Quick start guide with Raspberry Pi from NXP and have configured both the hardware and have installed EdgeLock SE050 Plug & Trust middleware to which we will presently refer to as “the middleware”.

You can find the instructions in sections “2.1 Hardware” and “3.1 Download EdgeLock SE050 Plug & Trust middleware” of the above document.

For the purpose of this tutorial, we will use /home/se050_middleware as the root of the middleware.

You should have seTool utility up and running:

root@raspberrypi:~# /home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool
App   :INFO :PlugAndTrust_v02.16.01_20200818
App   :INFO :Running /home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool
App   :INFO :If you want to over-ride the selection, use ENV=EX_SSS_BOOT_SSS_PORT or pass in command line arguments.
sss   :INFO :atr (Len=35)
      00 A0 00 00    03 96 04 03    E8 00 FE 02    0B 03 E8 08
      01 00 00 00    00 64 00 00    0A 4A 43 4F    50 34 20 41
      54 50 4F
sss   :WARN :Communication channel is Plain.
sss   :WARN :!!!Not recommended for production use.!!!
App   :WARN :Usage:
App   :WARN :	seTool genECC <keyId>
App   :WARN :	seTool setECC <keyId> <filename>
App   :WARN :	seTool getPublic <keyId> <filename>
App   :WARN :	seTool getRef <keyId> <filename>
App   :INFO :ex_sss Finished
App   :ERROR:ex_sss_entry Failed
App   :ERROR:!ERROR! ret != 0.

We are assuming that you also have Mender installed with at least version 2.4.0 or higher. You can do that following any method available, for instance: “Installing with the debian package”.

Please keep in mind that the NXP supported OpenSSL versions are 1.0.2 and 1.1.1.

We have to also be sure that you have libsss_engine.so on your library path, e.g.: in /usr/local/lib/libsss_engine.so. It should be available with the default build configuration. In case you ran into trouble at this point it is very often best to start from scratch.

Configure OpenSSL

Create a copy of the OpenSSL configuration file

In order to make the required modifications and not disturb the behaviour of your operating system, create a copy of your openssl.cnf configuration file. To find out where the file is, you can run the following command:

openssl version -d
OPENSSLDIR: "/usr/lib/ssl"

The configuration file in the above example is /usr/lib/ssl/openssl.cnf.

Copy the file:

cp /usr/lib/ssl/openssl.cnf /usr/lib/ssl/nxp_openssl.cnf

Modify OpenSSL configuration file

We need to change the openssl_conf directive, and point to our new configuration we are about to add. To this end, locate the line containing openssl_conf = default_conf, and modify to:

openssl_conf = nxp_engine

If you do not have uncommented openssl_conf = default_conf, then add the above line.

Next we need to add the OpenSSL Engine configuration. You can append the following to the end of the /usr/lib/ssl/openssl.cnf:

[nxp_engine]
engines = engine_section

[ engine_section ]
e4sss_se050 = e4sss_se050_section

[ e4sss_se050_section ]
dynamic_path = /usr/local/lib/libsss_engine.so
engine_id = e4sss
init = 1
default_algorithms = RSA,RAND,EC

Please note, that for OpenSSL 1.0.2 you can use:

default_algorithms = RAND,ECDSA,ECDH
default_algorithms = RSA,RAND,ECDSA,ECDH

while for OpenSSL 1.1.0 and 1.1.1

default_algorithms = RAND,EC
default_algorithms = RSA,RAND,EC

Key generation

Next step is to generate a key in the secure element; once we do it, the key will never leave the chip. We will use the seTool utility from the middleware and create the key based on the Elliptic Curve Cryptography (ECC). For the sake of this tutorialit does not matter which algorithm you choose.

Run the following command:

/home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool genECC 0x7f000001 /dev/i2c-1
Failed to export Enable pin : Device or resource busy
App   :INFO :PlugAndTrust_v02.16.01_20200818
App   :INFO :Running /home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool
App   :INFO :Using PortName='/dev/i2c-1' (CLI)
sss   :INFO :atr (Len=35)
     00 A0 00 00    03 96 04 03    E8 00 FE 02    0B 03 E8 08
     01 00 00 00    00 64 00 00    0A 4A 43 4F    50 34 20 41
     54 50 4F
sss   :WARN :Communication channel is Plain.
sss   :WARN :!!!Not recommended for production use.!!!
App   :INFO :ex_sss Finished

where 0x7f000001 is the so-called keyId; a sequence of characters (actually an ASCII representation of 4 bytes in hexadecimal format) that is used to identify our key.

As the private key never leaves the chip, we need a way to “refer to it”, i.e.: to tell the OpenSSL to pass the cryptographic operations to the secure element. To achieve this a concept of a reference key is used. We can get the reference key with the following command:

/home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool getRef 0x7f000001 /tmp/0x7f000001.ref /dev/i2c-1
>App   :INFO :PlugAndTrust_v02.16.01_20200818
>App   :INFO :Running >/home/se050_middleware/simw-top_build/raspbian_native_se050_t1oi2c/bin/seTool
>App   :INFO :Using PortName='/dev/i2c-1' (CLI)
>sss   :INFO :atr (Len=35)
>      00 A0 00 00    03 96 04 03    E8 00 FE 02    0B 03 E8 08
>      01 00 00 00    00 64 00 00    0A 4A 43 4F    50 34 20 41
>      54 50 4F
>sss   :WARN :Communication channel is Plain.
>sss   :WARN :!!!Not recommended for production use.!!!
>App   :INFO :ex_sss Finished

You can now read the extracted reference key:

cat /tmp/0x7f000001.ref
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgEAAAAAAAAAAAAAAA
AAAAAAAAfwAAAaWmtbalprW2EAChRANCAASLx76gj5OlhuqugoKHssp9Ocpo1FSO
fZsc1RHT5oPFf1YZnrcAMtBgAddiotiJJZLdBqpxKrh4XyZeLY17gzpP
-----END PRIVATE KEY-----

As you can see the reference key looks like any other ordinary key with one exception: once passed to the OpenSSL along with proper configuration in the openssl.cnf, it will trigger the use of the secure element.

Testing the setup

Create a certificate request using the private key we have generated with seTool genECC above. In the following, we are asking OpenSSL to access the key by reference (which we have got with seTool getRef).

export EX_SSS_BOOT_SSS_PORT=/dev/i2c-1; 
export OPENSSL_CONF=/etc/ssl/nxp_openssl.cnf;
openssl req -new -x509 -subj "/CN=Unit1" -engine e4sss \
-key /tmp/0x7f000001.ref -out /tmp/cert-e4sss.pem;
ssse-flw: EmbSe_Init(): Entry
App   :INFO :Using PortName='/dev/i2c-1' (ENV: EX_SSS_BOOT_SSS_PORT=/dev/i2c-1)
sss   :INFO :atr (Len=35)
     00 A0 00 00    03 96 04 03    E8 00 FE 02    0B 03 E8 08
     01 00 00 00    00 64 00 00    0A 4A 43 4F    50 34 20 41
     54 50 4F
sss   :WARN :Communication channel is Plain.
sss   :WARN :!!!Not recommended for production use.!!!
ssse-flw: Version: 1.0.5
ssse-flw: EmbSe_Init(): Exit
engine "e4sss" set.
ssse-flw: EmbSe_Rand invoked requesting 20 random bytes
ssse-dbg: Using keyId=0x7F000001
ssse-dbg: shaAlgo: 771
ssse-flw: SSS based sign (keyId=0x7F000001, dgstLen=32)
ssse-flw: SSS based sign called successfully (sigDERLen=71)
ssse-flw: EmbSe_ECDSA_Do_Sign success.
ssse-flw: EmbSe_Finish(): Entry
ssse-flw: EmbSe_Finish(): Exit
ssse-flw: EmbSe_Destroy(): Entry

where EX_SSS_BOOT_SSS_PORT points to a port, OPENSSL_CONF=/etc/ssl/nxp_openssl.cnf is the configuration file we have created, and -engine e4sss refers to the engine id given in our openssl.cnf:

...
[ e4sss_se050_section ]
dynamic_path = /usr/local/lib/libsss_engine.so
engine_id = e4sss
...

Configuring the Mender Client

At this point we should have:

  • a working secure element
  • working utilities from the middleware
  • OpenSSL configured to use the SE050 via the Engine subsystem
  • generated key with id 0x7f000001

Let’s proceed to edit the mender.conf, and add the following section (if not present):

    "HttpsClient": {
        "Certificate": "/tmp/cert-e4sss.pem",
        "Key": "/tmp/0x7f000001.ref",
        "SSLEngine": "e4sss"
    },

We also need to include the proper environment to the client process. You can do it by editing the /lib/systemd/system/mender-client.service file, and adding the two following lines to the `[Service] section:

...
[Service]
Environment=EX_SSS_BOOT_SSS_PORT=/dev/i2c-1
Environment=OPENSSL_CONF=/etc/ssl/nxp_openssl.cnf
...

At this point, after Mender Client restart, your device should appear in the Mender UI and should be fully functional.

The Certificate and Key configuration options point to a certificate and a private key that the client will use during the handshake. Those also are used in the signing of authorization requests. The latter is a bit harder to see, since it is implicit when OpenSSL is configured as above. However if we remove the two lines of Environment from the service definition, and restart the client,
we will see the signature verification errors reported:

Sep 29 13:10:06 raspberrypi mender[13534]:
time="2020-09-29T13:10:06+01:00"
level=error
msg="Authorize failed: transient error: authorization request failed: (request_id: ): authentication request rejected server error message: signature verification failed"
func="github.com/mendersoftware/mender/app.(*authorizeState).Handle"
file="/root/go/src/github.com/mendersoftware/mender/app/state.go:432"

These will be caused by the fact that the client uses the reference key for signing, which does not make much sense.

Final remarks

In this tutorial we have seen how you can use the NXP EdgeLock SE050 to handle the signing of auth requests and presenting the client identity during a TLS handshake. It ensures that the private key used could never be stolen, since it can never leave the chip.

We presented a path toward a more secure IoT solution based on Mender with the use of a hardware security element.

Useful links


If this tutorial was useful to you, please press like, or leave a thank you note to the contributor who put valuable time into this and made it available to you. It will be much appreciated!

nice article but it didn’t show how to bind the secure element chip to the host cpu.
without it all security measures are useless since you can use those keys without leaving the chip but on another (unauthorized) system…