End-to-end Security

E2E Symmetric KMS

PSK (Pre-Shared Key) cipher suites are ideal for embedded systems that have very limited computing power and only talk to a very small number of servers. With PSK, each side of the connection already has an agreed upon key to use during the TLS handshake. This reduces resource consumption for each session.

E2E Symmetric KMS (Key Management System) is a highly scalable method for provisioning and managing session unique PSKs for application layer security. A PSK and a PSKIdentity are generated by the RoT within the module, and used to secure end-to-end communications (e.g. via DTLS) to a customer’s cloud service. In particular, during (D)TLS handshake, the module sends the PSKIdentity generated by the RoT inside the ClientKeyExchange message. Once the customer’s cloud service gets this information, it will send the PSKIdentity to the u-blox Thingstream platform (via a REST API) in order to retrieve the same PSK that was used by the module to encrypt the end-to-end communications. The u-blox Thingstream platform uses the PSKIdentity to re-generate the PSK, without having to communicate with the module.

This delivers the following advantages by:

  • Achieving up to eight times reduction in the secure communication data overhead, and therefore reducing cost for data consumption when compared with PKI-based TLS.

  • Simplifying both the development and the actual process of obtaining the key by delegating the key management to the u-blox solution.

Customers have the option of either:

  • Using their own communication stack: in this scenario, the customer requests a PSK and PSKIdentity pair from the module and uses it to establish a PSK based communication with its own (D)TLS stack.

  • Using the communication stacks provided by u-blox module: in this scenario the module is configured to automatically establish a PSK based secure connection when trying to connect to a (D)TLS endpoint using any Internet protocol based application provided by the module.


Use cases

Once the device has been bootstrapped and the pre-shared keys (PSK) provisioning functionality has been enabled, this functionality can be called to access the required keys, as described below:

  1. The customer’s application running on the device makes a request to the u-blox module and retrieves a PSK and PSKIdentity pair from the RoT by using the AT+USECPSK command.

  2. The customer configures their own (D)TLS stack to use the PSK and PSKIdentity that are retrieved to secure the communication with its endpoint.

  3. The PSKIdentity is sent during the (D)TLS handshake as the “Identity” element of the ClientKeyExchange message, and the PSK is used to encrypt/decrypt the (D)TLS session data.

  4. The customer’s remote service extracts the PSKIdentity from the ClientKeyExchange message and forwards it to the u-blox security service via the GetPSK API.

  5. The information received by the u-blox security service is used to:

    1. Validate the customer

    2. Validate the device making the request is assigned to this customer account

    3. Confirm that PSK provisioning is enabled on this device

  6. If all the actions in item 5 are true, i.e. the request is valid, then the u-blox security service passes the PSK back to the customer’s remote service in the GetPSK API response.

  7. The customer’s remote service can now use the received PSK to establish the (D)TLS session.

The following AT command can be used to retrieve an identity and PSK value pair for encrypting point-to-point communication:

The module includes a (D)TLS security layer profile manager, which handles security profiles containing (D)TLS connection properties. Each security profile can be associated to sockets or higher level applications (e.g., HTTP, MQTT) using the module internal (D)TLS stack. In general, the AT+USECPRF command is used to configure a security profile.

The following AT commands can be used to configure a security profile on the module with certain PSKIdentity and PSK:

This way, associating a TCP socket to security profile 0, PSKIdentity and PSK will be used to handle the TLS communication through that TCP socket.

Examples

Following are some examples of the procedure described above.

These are example commands and do not use real world API keys or authorization headers. Actual API secret key and authorization headers must first be created before running these commands.

  • Get PSK and PSKIdentity from RoT. For example, get a 16 bytes long PSK:

AT+USECPSK=16


+USECPSK: "11010008002F33244B115B0AEEB9","E5DE45B367DB690F2E17CF9D80A18E87"

OK

  • 11010008002F33244B115B0AEEB9 is the PSKIdentity in hex format: this has to be sent to the remote service during the (D)TLS handshake.

  • E5DE45B367DB690F2E17CF9D80A18E87 is the actual PSK in hex format: this can be used to encrypt the communication to the remote service.

  • Configure the (D)TLS stack to use the PSKIdentity and PSK generated by RoT.

This step depends on the (D)TLS stack used. We provide 2 examples:

  • Use OpenSSL command to test the connection to the remote service.

    • Call OpenSSL this way:

openssl s_client -psk_identity "11010008002F33244B115B0AEEB9" -psk "E5DE45B367DB690F2E17CF9D80A18E87" -connect <server_IP:server_port>

    • Use a modem internal socket to communicate to the remote service.

Configure a security profile (profile 0 in this example):

AT+USECPRF=0,8,"E5DE45B367DB690F2E17CF9D80A18E87",1

OK

AT+USECPRF=0,9,"11010008002F33244B115B0AEEB9"

OK

Then, create a socket and enable the secure option on the socket, associating the security profile 0 to it:

AT+USOCR=6

+USOCR: 0 <-- created socket with id 0

OK

AT+USOSEC=0,1,0 <-- associate socket 0 to security profile 0

OK

Connect the socket to the remote service:

AT+USOCO=0,"<server_IP>",<server_port>

  • Analyzing the communication between client and remote service, we can see the PSKIdentity being sent by the client in the ClientKeyExchange message during (D)TLS handshake:

  • On the server side.

The actual way to extract the PSKIdentity from the ClientKeyExchange message can vary and depends on the (D)TLS stack used.

For example, a server application using OpenSSL libraries and wishing to use PSKs for TLSv1.2 and below, must provide a callback function called when the server receives the ClientKeyExchange message. This function has the purpose of fetching the correct PSK starting from the PSKIdentity retrieved from the client. The callback function is set by calling SSL_CTX_set_psk_server_callback(SSL_CTX *ctx, SSL_psk_server_cb_func cb):

#include <openssl/ssl.h>

#include <openssl/err.h>

SSL_CTX *ctx;

const SSL_METHOD *method;

method = SSLv23_server_method();

ctx = SSL_CTX_new(method);

if (!ctx) {

ERR_print_errors_fp(stderr);

return -1;

}

SSL_CTX_set_ecdh_auto(ctx, 1);

SSL_CTX_use_psk_identity_hint(ctx, "hint hint");

SSL_CTX_set_psk_server_callback(ctx, psk_server_cb);

During (D)TLS handshake, when the server receives the ClientKeyExchange message, the psk_server_cb function is called.

static unsigned int psk_server_cb(SSL *ssl,

const char *identity,

unsigned char *psk,

unsigned int max_psk_len) {

char* py_psk;

if (identity == NULL) {

return 0;

}

/* Retrieve the PSK for the given identity */

/* … */

See OpenSSL documentation for more details (https://www.openssl.org/docs/).

Once the PSKIdentity is retrieved, the customer can obtain the PSK via the GetPSK REST API call (https://ssapi.services.u-blox.com/v1/GetPSK). Make sure to have an AuthToken to use for the Authorization header – if not, call the Authorize API to get one.

We show the API call using cURL: we have to specify the PSKIdentity in the KeyID parameter and, optionally, the PSK length (in bits) in the KeyLength parameter:

curl -X POST "https://ssapi.services.u-blox.com/v1/GetPSK" -H "accept: application/json" -H "Authorization: [AuthToken]" -H "Content-Type: application/json" -d "{ \"KeyID\": \"11010008002F33244B115B0AEEB9\", \"KeyLength\": \"128\"}"

In the previous C server application, it’s possible for example to use the libcurl library to perform the API request. See libcurl documentation for more details (https://curl.haxx.se/libcurl/c/).

  • The API returns a JSON containing the PSK encoded using base64:

{

"Key": "5d5Fs2fbaQ8uF8+dgKGOhw==",

"ROTPublicUID": "0002000089282245"

}

  • Considering the C server application from above, at this point the server can create a server socket (sock_fd) and wrap it with the OpenSSL context from before (ctx):

SSL *ssl;

const SSL_METHOD *method;

method = SSLv23_server_method();

SSL_CTX_set_ecdh_auto(ctx, 1);

SSL_CTX_use_psk_identity_hint(ctx, "hint hint");

SSL_CTX_set_psk_server_callback(ctx, psk_server_cb);

ssl = SSL_new(ctx);

if (ssl == NULL) {

// Failed to initialise SSL buffer

return NULL;

}

if (0 == SSL_set_fd(ssl, sock_fd)) {

// Failed to configure IO for the SSL socket

ERR_print_errors_fp(stderr);

return NULL;

};

if (SSL_accept(ssl) <= 0) {

// Failed to setup SSL socket as server

ERR_print_errors_fp(stderr);

SSL_free(ssl);

return NULL;

}

At this point, the server application can exchange data through the secure socket by using SSL_read() and SSL_write. See OpenSSL documentation for more details (https://www.openssl.org/docs/).

End-to-End Data Protection

End-to-end Data Protection provides application layer encryption to device and module applications. The application data is encrypted in the module and the corresponding decryption key and parameters are made available via the REST API in the cloud for decryption.

The data can be:

  • Encrypted on the device.

  • Transferred over the internet independent of the protocols, servers or platforms used to achieve this.

  • Decrypted at a time of own choosing in the future.

The process does not depend on the security of the transport layers or storage mechanisms used between the device and the end service.

Note that on step (4) of the sequence diagram below , the key identity is represented by the firsts 16 bytes of the payload.

Upstream (Device to cloud)

The application layer (device) wants to send a message to cloud securely without being involved in the security stack. The sequence diagram below demonstrate the main players and the interactions between them.


The diagram below demonstrates the steps from device to cloud.


Use cases

Once the device has been bootstrapped and the end-to-end data protection functionality has been enabled, the end-to-end encryption functionality can be called, as described below:

  1. The customer’s application running on the device makes an end-to-end encryption request to the u-blox module on the same device passing the data to be encrypted by using the AT+USECE2EDATAENC or the AT+USECE2EFILEENC command.

  2. The u-blox module on the device encrypts the data and passes the authenticated and encrypted data back to the customer’s application.

  3. The customer’s application can then forward the authenticated and encrypted data to the customer’s remote service using whatever means they choose. This may not be a direct call to the customer’s remote service but may involve the transporting of the data via 1 to [N] insecure internet servers (corresponding to step 3). During this transport phase the security and integrity of the data is still maintained as the data is encrypted and authenticated. The data can be retained by the customer’s remote service for decryption at a later date.

  4. The key identity is extracted from the authenticated and encrypted data by reading the first KI_LENGTH bytes, where KI_LENGTH is defined by the 4 most significant bits (nibble) in the encrypted data:

  5. The nibble is set to 0x1, that corresponds to a KI_LENGHT equal to 16 bytes.

The key identity is forwarded to the u-blox security service via GetE2EDecryptionParameters API call, so that the matching decryption key and decryption parameters can be retrieved.

  1. The information received by the u-blox security service is used to:

  2. Validate the customer.

  3. Validate the device making the request – Verify if this device is assigned to this customer account

  4. Confirm that end-to-end data protection is enabled on this device

  5. If all the actions in item 5 are true, i.e. the request is valid then the u-blox security service passes the decryption key and decryption parameters back to the customers’ remote service.

  6. The customers remote service can now decrypt and verify the authenticity of the data previously passed to it (see item 3).

  7. The decrypted data can now be used as required by the customer’s remote service.

The AT command +USECE2EDATAENC can be called to encrypt data:


This operation corresponds to step 2 in flow diagram above.

Examples

Following are some examples of the procedure described above.

These are example commands and do not use real world API keys or authorization headers. Actual API secret key and authorization headers must first be created before running these commands.

  • (Steps 1-2-3) Let’s encrypt a simple «HELLO» message using AT+USECE2EDATAENC.

Use m-center in HEX mode (enable the checkbox on the top in the AT terminal). This way it’s easier to parse the encrypted data returned by the AT command.

The AT command to send is:

AT+USECE2EDATAENC=5

> HELLO

The command returns the length of the encrypted data and the encrypted data itself:

+USECE2EDATAENC: 37, "<encrypted_data>"

In this case we get 37 bytes of encrypted data, which in hex format is:

1101000089291e26ec1aeb00744500000b874c81e91d623addf63bcebe64fe8b290efb4cd7

This is the encryption part of the process. At this point the encrypted data can be transferred to the recipient by secure or insecure means.

The encrypted and authenticated data returned by the AT+USECE2EDATAENC command is composed, in order, by:

  • The key identity to be used in order to request decryption parameters.

  • The cipher text.

  • The MAC tag used to authenticate the data.


  • (Step 4) Suppose we are ready to decrypt the message – this could happen on any host that has received the encrypted data at any time.

First thing to do is to extract the key identity from the encrypted data. The most significant nibble (4 bits) of the encrypted data define the key identity length:

The nibble is set to 0x1, that implies that the key identity is 16 bytes long.

In our example we have:

Since the most significant nibble is 0x1, we extract the first 16 bytes:

Key identity: 1101000089291e26ec1aeb0074450000

The data encrypted using the +USECE2EDATAENC and +USECE2EFILEENC AT commands can only be decrypted using a key obtained using the GetE2EDecryptionParameters REST API (https://ssapi.services.u-blox.com/v1/GetE2EDecryptionParameters). Make sure to have an AuthToken to use for the Authorization header – if not, call the Authorize API to get one.

Specifically, the REST API will provide the key and algorithm details needed to decrypt and verify the authenticity of the cipher text generated by +USECE2EDATAENC and +USECE2EFILEENC.

The following command line examples use cURL to retrieve the PSK and decryption details, by sending the key identity previously extracted in the “EncryptedHeader” parameter.

Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/GetE2EDecryptionParameters" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"EncryptedHeader":"1101000089291e26ec1aeb0074450000"}'

Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/GetE2EDecryptionParameters" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"EncryptedHeader\":\"1101000089291e26ec1aeb0074450000\"}"

  • (Steps 5-6) If the request succeeds, the API response contains a JSON with this information:

{

"CipherSuite": "AES_128_CCM",

"Key": "ds/zy+Co/XYVMa14bqFh3w==",

"MACLength": 128,

"Nonce": "iSkeJuwa6wB0RQAA"

}

The KI_LENGTH bytes long header returned by +USECE2EDATAENC and +USECE2EFILEENC, when converted to an HEX string, is used in the curl request as the value of the EncryptedHeader parameter.

The key and nonce values returned are base-64 encoded, and can be used to decode the ciphertext, i.e. the data returned by +USECE2EDATAENC and +USECE2EFILEENC after having removed the KI_LENGTH bytes header, using the algorithm specified, in this case AES with the CCM mode. The MAC (16 for CCM mode or 8 bytes for CCM_8 mode) is appended at the end of the encrypted payload and it must be verified by the customer.

  • (Step 7) From the API response we know:

    • That we need to use the AES_128_CCM algorithm to decrypt the message

    • The key and the nonce parameter to use as inputs for the algorithm

    • That the MAC tag is 128 bits long (16 bytes)

Since the MAC tag is appended to the cipher text in the encoded data, we can now split the response of AT+USECE2EDATAENC this way:

At this point we have all the inputs to decrypt and authenticate the encrypted data.

From the API response:

  • Algorithm to use.

  • Key and nonce.

From the encrypted AT+USECE2EDATAENC response:

    • Cipher text.

    • MAC tag.

We provide, as an example, a Python snippet which decrypts the cipher text by using the PyCryptodome package (https://pycryptodome.readthedocs.io/en/latest/):

from base64 import b64decode

from Cryptodome.Cipher import AES

import binascii

def aesccm_dec(enc_data, keyb64, nonceb64, mac_len):

'''

Decrypt a cipher text using AES-CCM algorithm.

:param enc_data: the encrypted data string returned by AT+USECE2EDATAENC

:param keyb64: the decryption key returned by GetE2EDecryptionParameters

:param nonceb64: the nonce returned by GetE2EDecryptionParameters

:mac_len: the MAC tag length returned by GetE2EDecryptionParameters

'''

# decryption key and nonce are given by the API response as base64 strings

# they need to be converted to bytes (binary data)

key = b64decode(keyb64)

nonce = b64decode(nonceb64)

# ciphertext and MAC tag are extracted from the encrypted data as hex strings

# get key identity and MAC tag lengths

mac_len = mac_len // 8 # convert to bytes

key_id_len = 16 if enc_data[0] == '1' else 11 # in bytes

# get ciphertext and MAC tag and convert them to bytes (binary data)

ciphertext = binascii.unhexlify(enc_data[key_id_len*2:-mac_len*2])

tag = binascii.unhexlify(enc_data[-mac_len*2:])

try:

cipher = AES.new(key, AES.MODE_CCM, nonce=nonce)

plaintext = cipher.decrypt_and_verify(ciphertext, tag)

print("The message is: " + plaintext.decode('utf-8'))

except (ValueError, KeyError) as ex:

print("Incorrect decryption")

print(ex)

Running this function with the data from the example we get back our original “HELLO” message.

Availability

The E2E security service suite is available from the following FW version an subsequent releases:

  • SARA-R410M-x3B-01 (x3B = 63B, 73B, 83B)

  • SARA-R422S-00B-00

  • SARA-R422M8S-00B-00

  • SARA-R500S-00B-00

  • SARA-R510S-00B-00

  • SARA-R510M8S-00B-00

  • ALEX-R510M8S-01B-00