End-to-end Data Protection & Integrity

Service overview

End-to-end Data Protection provides bidirectional application layer encryption between your application platform and the R4/R5 module with no need to establish a permanent secure session between the two endpoints . The application data is encrypted on one endpoint (the module or your cloud application) and the corresponding decryption key and the relevant parameters are made available on the other endpoint. The secret keys are never exposed over the air.

In Upstream 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, obtaining the decryption key from Thingstream platform via REST API

In Downstream the data can be:

  • Encrypted on the cloud application with a key provided via REST API from the Thingstream service delivery platform.

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

  • Decrypted by the module at any time, using the proper AT Command

The process does not depend on the security of the transport layers or storage mechanisms used between the device and the end service. The following picture shows the steps for the upstream encryption.

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.

Two working modes are available:

  • the first one let's you to achieve an unmatched level of security by using a different encryption key for every message. Not that encryption keys are different also for each device

  • the second working mode let you to keep the same encryption parameters for up to 30 days and then a new set of encryption parameters is automatically generated by the platform. At any time you can decide to generate a new set, as described in a following section, at your wish. Your cloud application does not need to keep track of the active protection set since it can be always retrieved via REST API. This is the default working mode

Downstream (Cloud to Device)

In this scenario, your application platform needs to send a message to a particular device securely. The sequence diagram demonstrates the players that the interactions between them.

The diagram below demonstrates the steps from cloud to device.

In Downstream it is available only the default working mode mentioned above for upstream.

Key rotation

To enhance the security of your data, the cryptographic key used for end-to-end encryption should be renewed frequently. Key rotation is the procedure that let you to trigger the generation of a new fresh key set to be used for the encryption. It's worth to note that key is not accessible on device side, since encryption/decryption process is performed in the module, never exposing the key.

Remember that the keys are different for each device and are in a number sufficient to ensure more than a new key every day for the entire device lifetime

There are two types of key rotation available:

  1. CONTINUOUS

This rotation type specifies that on every encryption request, a new key will be generated and used. After setting this type on the cloud, following command needs to be run (just once) on the device:

AT+USECOPCMD="e2e_enc",0

This option is available only in upstream

  1. AUTOMATIC

This rotation type specifies that after a set number of days (configurable from 1 to 30), a new encryption key will be automatically generated and used by the encryption algorithm in the module.

Note: For key to be rotated on the device, the device needs to send a security heartbeat. When setting key rotation, please keep in mind that devices security heartbeat will be adjusted to reflect the number of days you want the key to be rotated. For example, if security heartbeat was initially set at 5 days, and you want the key to be rotated every 3 days, security heartbeat will be updated by the Thingstream platform to happen every 3 days.

After setting this working mode on the cloud, following command needs to be run (just once) on the device. Since this is is the default working mode, this AT Command shall be used only if previously the continuous working mode has ben set by you:

AT+USECOPCMD="e2e_enc",1

The following command line examples use cURL to set AUTOMATIC key rotation.

  • Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/key/rotate" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"ROTPublicUIDs":["00080008003a9e26"], "Type": "AUTOMATIC", "Days": 3}'

  • Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/key/rotate" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"ROTPublicUIDs\":[\"00080008003a9e26\"], \"Type\": \"AUTOMATIC\", \"Days\": 3}"

The benefit of the automatic rotation approach is that on your cloud application you do not need to retrieve a new set of protection parameters for every message and furthermore that the derivation of a new key set is done automatically, without any logit to be implemented on your cloud application or on your device. The tradeoff is:

  • a reduced level of security if compared to continuous rotation, but this level is considered good enough for most of the applications

  • some more complexity on your application since you need to build a logic on your application that stores the protection parameters and every time a packet is received checks if you have a valid set in your database or not. Other logics based on the timestamp could also be used.

You can avoid to add this complexity by always asking the valid key set to Thingstream platform, independently if you are using continuous or automatic rotation

It is worth noting that:

  • the protection parameters set does not expire, therefore you can always retrieve it from the Thingstream platform at any time using the API above listed

  • every time that a key set is requested to the Thingstream platform, it is counted as a charging event, independently if the keyset is a new one or an existing one. For example, let's assume that you have selected for the device the Daily plan that includes 31 request per month (quota), and you are implementing the automatic key rotation every 15 days. If your cloud application requests the key set every day, the platform will decrease the available quota of requests every day by one unit.

  • The 'rotation' terminology used in the current guide, does not mean that there is fixed amount of key set that is rotated. It is used to indicate that a new key set is derived every time, different on every rotation and different for each device, providing an unmatched level of security.

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. To enable the service, you shall select one of the available 'E2E Security plans' (Developer, Daily, Flex or Freedom) during device profile configuration or when change the state of a Security Thing from allocated to active.

  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.

Similar AT commands are available in case you need to preserve only integrity and authenticity and will be shown in the examples below.

  1. The u-blox module on the device encrypts the data and passes the authenticated and encrypted data back to the customer’s application that contains also the key identity parameter,, necessary to retrive the right key on cloud side

  2. The customer’s application can then forward the authenticated and encrypted data to your remote service using any available network interface.

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.

  1. The key identity shall be 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:

  • If the nibble is set to 0x1, then KI_LENGHT is 16 bytes (continuous rotation).

  • If the nibble is set to 0x2, then KI_LENGTH is 12 bytes (automatic rotation).

The key identity shall be forwarded to the u-blox security service via /e2e/uplink/protectionparameters/get 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:

    • Validate the customer.

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

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

  2. 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.

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

  4. 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 on device side

This operation corresponds to step 2 in flow diagram above.

Examples - upstream

Following are some examples of the procedure described above.

The prerequisites are:

  • You have already a device that has completed the bootstrap process and it is visible on your platform (as explained in the Getting started guide

  • You have assigned to the device (directly or thorough the Device Profile) any of the E2E Security plan. You can verify and eventually assign in the UI of Thingstream platform, selecting your Security Thing in the Things section (on the left side menù). To go on with the example you can use the Developer plan

  • You have a product and a Firmware release that support the function. At the end of this guide there is the list of products that support each service

Important note: all the API responses the you see in the current guide have the unique aim to show which is the response format. The data provided in the response cannot be reused, but you have to retrive your own valid protection parameters from the Thingstream platform.

Continuous key rotation

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 by you through the Thingstream service delivery platform before running these commands.

Check the Getting started guide and the Tools and Software section if you do not know how to do it

  • (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 you get back from the module 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 using every network interface

The encrypted and authenticated data returned by the AT+USECE2EDATAENC command is composed, 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:

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 /e2e/uplink/protectionparameters/get REST API. 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/e2e/uplink/protectionparameters/get" -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/e2e/uplink/protectionparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"EncryptedHeader\":\"1101000089291e26ec1aeb0074450000\"}"


If the data was only signed on the device (and not encrypted), following API needs to be called to get respective parameters to verify authenticity of the data on cloud:

Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/integrityparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"EncryptedHeader":" 290000020000192800bfff1"}'

Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/integrityparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"EncryptedHeader\":\"290000020000192800bfff1\"}"


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

{

'CipherSuite': 'AES_128_CS1_HMAC_SHA256',

'AESIV': 'KgAABAAIAD+yUgABAAAAAA==',

'KIV': 'KgAABAAIAD+yUgAB',

'EncryptionKey': 'iGoE1WPNYd6+vcJxZmB2QQ==',

'AuthenticationKey': 'CuHbo9SDN/Bbh3Qe8G5xYg==',

'MACLength': 128,

'KeyLength': 128,

'AuthKeyLength': 128,

'ROTPublicUID': '00041208003fa662'

}

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, you get back the original “HELLO” message.

Automatic key rotation

In case you are using the automatic rotation option, nothing changes on the device side. The same AT command can be used as above. except for the fact that you have to set the device, just once to use it issuing the AT Command AT+USECOPCMD=”e2e_enc”, 1

Remember that this is default option set in the module, therefore there is no need to send this command if you do not want to change the default working mode.

  • The AT command to send to encrypt data is

AT+USECE2EDATAENC=5

> HELLO

that returns the length of the encrypted data and the encrypted data itself:

+USECE2EDATAENC: 33, "<encrypted_data>"

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

2a0000041908003fa6620001697e2fe027bc63a0c668d2c6a7973043b2307f8228

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.

The most significant nibble (4 bits) of the encrypted data defines the key identity length. The nibble would be set to 0x2, and key identity is 12 bytes long.

In our example we have:

Since the most significant nibble is 0x2, we extract the first 12 bytes:

Key identity: 2a0000041908003fa6620001

  • On your cloud application you can use exactly the same API as for continuous rotation

Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/protectionparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"EncryptedHeader":"2a0000041908003fa6620001"}'

Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/protectionparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"EncryptedHeader\":\"2a0000041908003fa6620001\"}"

If the data was only signed on the device (and not encrypted), following API needs to be called to get respective parameters to verify authenticity of the data on cloud:

Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/integrityparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"EncryptedHeader":" 2a0000041908003fa6620001"}'

Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/uplink/integrityparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"EncryptedHeader\":\"2a0000041908003fa6620001\"}"

If the request succeeds, API response would be:

  • If the request succeeds, the API response contains a JSON with this information in case of data encryption and signing

{

'CipherSuite': 'AES_128_CS1_HMAC_SHA256',

'AESIV': 'KgAABAAIAD+yUgABAAAAAA==',

'KIV': 'KgAABAAIAD+yUgAB',

'EncryptionKey': 'iGoE1WPNYd6+vcJxZmB2QQ==',

'AuthenticationKey': 'CuHbo9SDN/Bbh3Qe8G5xYg==',

'MACLength': 128,

'KeyLength': 128,

'AuthKeyLength': 128,

'ROTPublicUID': '00041208003fa662'

}

while this is the response format in case of signing only (integrity)

{

'CipherSuite': 'HMAC_SHA256',

'AuthenticationKey': 'NR9u5wz3zpF17M5aSkOJpA==',

'KIV': 'KgAABAAIAD+mYg0C',

'ROTPublicUID': '00040008003fa662',

'MACLength': 128,

'AuthKeyLength': 128

}

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 CBC-CS1 mode. The MAC (16 bytes) is appended at the end of the encrypted payload and it must be verified by the customer.

  • From the API response we know:

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

      • The key and the AESIV 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 plain text and whole data is encrypted so we can extract mac tag after decrypting the data first.

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

From the API response:

  • Algorithm to use.

  • Key and AESIV.

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/).

Sample code

from base64 import b64decode

import binascii

from Cryptodome.Cipher import AES

import hmac

import hashlib

import math



def to_decrypt_blocks(data, block_size):

l = len(data)

b = math.ceil(l / block_size)

partial_block_length = l % block_size

blocks = []

for i in range(0, b):

if (i == b - 2): # second last block

if partial_block_length > 0:

blocks.append(data[i * block_size:(i * block_size) + partial_block_length])

else:

blocks.append(data[i * block_size:(i * block_size) + block_size])

elif (i == b - 1):

blocks.append(data[-block_size:])

else:

blocks.append(data[i * block_size:(i * block_size) + block_size])


return blocks



def aescbc_sha256_dec(ENC_DATA, EncryptionKey, AuthenticationKey, KIV, AESIV, MACLength):

# AES-CBC-CS1-SHA256

key_bin = b64decode(EncryptionKey)

key = key_bin.hex()


auth_bin = b64decode(AuthenticationKey)


kiv_bin = b64decode(KIV)


aesiv = b64decode(AESIV)

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

# get key identity and MAC tag lengths

mac_len = MACLength // 8 # in bytes

key_id_len = 12 # in bytes


aes_block_size_hex = len(key)

aes_block_size_bin = len(key) // 2


ciphertext = ENC_DATA[key_id_len * 2:]


# pycrptodomex does not support CBC-CS1

# so completing partial block

blocks = to_decrypt_blocks(ciphertext, aes_block_size_hex)


padded_len = 0

# Padding partially complete block

if len(blocks) > 1:

last_block = blocks[len(blocks) - 1]

b = binascii.unhexlify(last_block)

cipher = AES.new(key_bin, AES.MODE_ECB)

a = cipher.decrypt(b)

ba = bytes(a)

second_last_block = binascii.unhexlify(blocks[len(blocks) - 2])

padded_len = aes_block_size_bin - len(second_last_block)

padded_block_bin = second_last_block + ba[-padded_len:]

blocks[len(blocks) - 2] = padded_block_bin.hex()


ciphertext = binascii.unhexlify(''.join(blocks))


try:

cipher = AES.new(key_bin, AES.MODE_CBC, iv=aesiv)

plaintext = cipher.decrypt(ciphertext)

plaintext = plaintext[:len(plaintext) - padded_len] # removing padded zeros from decrytped message

tag = plaintext[-mac_len:]


verify_data = kiv_bin + plaintext[0:len(plaintext) - 16]


hmac_bin = hmac.new(auth_bin, verify_data, hashlib.sha256).digest()

hmac_bin = hmac_bin[:16]

if hmac_bin == tag:

print("Message authenticity verified using authentication key")

else:

print("Failed to verify message authenticity.")

print("The message was: " + plaintext[:-16].decode('utf-8'))

except (ValueError, KeyError) as ex:

print("Incorrect decryption")

print(ex)


if __name__ == '__main__':

print("V2: using AES_128_CS1_HMAC_SHA256 decryption")

aescbc_sha256_dec(encrypted_data,EncryptionKey, AuthenticationKey, KIV, AESIV, MACLength)

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

Examples - downstream

The prerequisite to implement this example are the same one described in the upstream example section.

To encrypt in downstream (from your cloud application to the device,) you need to retrieve the protection parameters for that device using the proper REST API

https://ssapi.services.u-blox.com/v1/e2e/downlink/protectionparameters/get

Make sure to have an auth. token 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 encrypt and verify the authenticity of the cipher text, which will be used for decryption on the device side.

If you need only integrity refer to the following API

/e2e/downlink/integrityparameters/get

The following command line examples use cURL to retrieve the encryption details, by sending the ROTPublicUID of the device.

To encrypt data on the server, and send to device, you need to call following API and get encryption parameters:

Request to be run on Linux and macOS:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/downlink/protectionparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d '{"ROTPublicUID":"2000089282245"}'

Request to be run on Windows:

curl -X POST "https://ssapi.services.u-blox.com/v1/e2e/downlink/protectionparameters/get" -H "accept: application/json" -H "Content-Type: application/json" -H Authorization: [AuthToken] -d "{\"ROTPublicUID\":\"2000089282245\"}"

If the request succeeds, the API response contains a JSON with this information (for data encryption and signing):

{

"MACLength": 128,

"KeyLength": 128,

"CipherSuite": "AES_128_CS1_HMAC_SHA256",

"AuthenticationKey": "lTZfP2dO5IAXvJT8FlXV5A==",

"AuthKeyLength": 128,

"EncryptionKey": "EuPzZxheaAoGAQ/+X2mYbg==",

"KIV": "EQEBAIkoIrZnYX4vnjgCCC==",

"AESIV": "KgXYZgAAiSgisgADBBBBBB",

"ROTPublicUID": 2000089282245

}

A similar JSON is returned in case you are using only integrity, with only the relevant parameters

{

'CipherSuite': 'HMAC_SHA256',

'AuthenticationKey': 'NR9u5wz3zpF17M5aSkOJpA==',

'KIV': 'KgAABAAIAD+mYg0C',

'ROTPublicUID': '00040008003fa662',

'MACLength': 128,

'AuthKeyLength': 128

}

Following steps need to be followed to encrypt data in your cloud application with the given information:

  1. You need to calculate data signature first (see sample code in python)

def sha256_sign(dec_params):

# SHA256

auth_bin = b64decode(dec_params.AuthenticationKey)

kiv_bin = b64decode(dec_params.KIV)

mac_len = dec_params.MACLength // 8 # in bytes

key_id_len = 12 # in bytes

msg_bin = bytearray(MESSAGE, 'UTF-8')

msg_hex = msg_bin.hex()

tag_payload = kiv_bin + msg_bin

hmac_bin = hmac.new(auth_bin, tag_payload, hashlib.sha256).digest()

Where:

  • KIV is the KIV returned by API

  • MESSAGE is the data to protect

  • AuthenticationKey is the key to use to compute the authentication code, as AuthenticationKey in API response above

  • MACLength is the MACLength by the API

NOTE: If the intention was to only sign the data, and not encrypt it, you don’t need to follow rest of the steps, and send following payload to device:

KIV + DATA + TAG

Whereas TAG is truncated to 16 bytes as shown in the above code example.

  1. Once data signature is calculated, you then need to encrypt data along with its signature

Sample code

def to_blocks(data, block_size):

l = len(data)

b = math.ceil(l / block_size)

blocks=[]

for i in range(0,b):

blocks.append(data[i*block_size:(i*block_size)+block_size])

return blocks


def aescbc_sha256_enc(dec_params):


key_bin = b64decode(dec_params.EncryptionKey)

key = key_bin.hex()


auth_bin = b64decode(dec_params.AuthenticationKey)


kiv_bin = b64decode(dec_params.KIV)

kiv_hex = kiv_bin.hex()


aesiv = b64decode(dec_params.AESIV)

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

# get key identity and MAC tag lengths

mac_len = dec_params.MACLength // 8 # in bytes

key_id_len = 12 # in bytes


aes_block_size_hex = len(key)

aes_block_size_bin = len(key) // 2


msg_bin = bytearray(MESSAGE, 'UTF-8')

msg_hex = msg_bin.hex()


tag_payload = kiv_bin + msg_bin


hmac_bin = hmac.new(auth_bin, tag_payload, hashlib.sha256).digest()


hmac_bin = hmac_bin[:16]


payload = msg_bin+hmac_bin

blocks = to_blocks(payload, aes_block_size_bin)

last_block = None

# encrypt block to block and follow the CS1 format

enc_blocks_bin = []

IV = aesiv

for i in range(0, len(blocks)):

# convert to binary the current block

b_bin = blocks[i]

if (i == len(blocks) - 1):

# calculate how mouch do we need to pad

padding_bin_len = aes_block_size_bin - len(b_bin)

# prepare pt 0 padding

padding = bytes([0] * (padding_bin_len))

paded_block_bin = b_bin + padding

# remove the XORED BYTES from previous encrypted block

enc_blocks_bin[-1] = enc_blocks_bin[-1][0:len(b_bin)]

b_bin = paded_block_bin



try:

cipher = AES.new(key_bin, AES.MODE_CBC, iv=IV)

ba = cipher.encrypt(b_bin)

except (ValueError, KeyError) as ex:

print("Incorrect decryption")

print(ex)

enc_blocks_bin.append(ba)

IV = ba


return (kiv_bin+b''.join(enc_blocks_bin)).hex()

Where:

  • EncryptionKey is the key to use for encryption, as EncryptionKey in API response above

  • Message is the clear message to encrypt

  • AESIV is the AES initialization vector, to use for encryption, as AESIV in API response above

Once encrypted, you need to prepend KIV to encrypted data and send that over to the device for decryption as shown in the code above:

KIV + ENCRYPTED_DATA

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.

After encrypting the data on the server and sending payload over to device, following steps are needed to decrypt it on the device:

  • Run following command to get decrypted data by giving it encrypted payload:

AT+USECE2EDATADEC=72

>

*••••••0¤C••HbØÆZÂ(l†a¹•þA1Gü•B

Ô’¿¬^Y»íQU

Response will be

+USECE2EDATADEC: 44," The quick brown fox jumps over the lazy dog."

  • If data was signed only (and not encrypted), following command is needed to verify integrity of the data:

AT+USECE2EDATAAUTHN=72

>

*••••••0¤C••HbØÆZÂ(l†a¹•þA1Gü•B

Ô’¿¬^Y»íQU

Response will be

+USECE2EDATAAUTHN: 44," The quick brown fox jumps over the lazy dog."


Availability

E2E Data protection service upstream with continuous key rotation is available in 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

E2E Data protection downstream, Automatic Key rotation feature, and E2E Data Integrity are available starting from the below firmware releases

  • SARA-R500S-01B-00, SARA-R510S-01B-00, SARA-R510M8S-01B-00

  • ALEX-R510M8S-01B-00