# Edge Secrets

Edge Secrets is a security concept designed to send, store, and manage information such as keys within the Edge Module for various application needs. These secrets are secured in transit and at rest as per the minimum cybersecurity recommendations.

This article explains the requirements and procedures to add and retrieve secrets from the device and cloud via Information Model APIs.

Device Secrets

Before going through this document we suggest you to read the Device Secrets article, which describes the underlying concept of secrets. You can ignore the information about Device API usage there, since Ability Edge exposes Module API instead.

The following notation is used in the descriptions:

  • Pk - Module Private Key
  • Pu - Module Public Key
  • Pu[IM] - Information Model Public Key (Used for device communication only)
  • Pk[IM] - Information Model Private Key
  • Sk - Module Symmetric Key (Recommended AES -128 bit)
  • Sk[IM] - Symmetric key (Generated by IM and used only within IM to encrypt the secret while storing the object model)
  • AES - Advanced Encryption Standard
  • MQTT - Message Queuing Telemetry Transport
  • IV - Initialization Vector

# Prerequisites and Preface

This section describes the essential factors which are required for the efficient functioning of the Edge Secrets:

  • Edge Secrets is supported by both Module API V1 and Module API V2. However, If you're using Module API V1 though, make sure to enable the support for Edge Secrets in the abb.ability.configuration object of your Edge (example below shows a fragment of the proper configuration):

    {
      "edge-proxy": {
        "image": { ... },
        "configuration": {
          "feature": {
            "EdgeSecrets": {
              "value": true
            }
          }
        }
      }
    }
    
  • Asymmetric encryption: RSA 2048 bit is a fundamental cryptography strategy.

  • Since RSA 2048 bit encryption puts a restriction of 245 bytes as the maximum size for the secrets, it is possible to use symmetric encryption on top of asymmetric encryption. It is recommended to use AES-128 bit keys in CBC mode for encryption and decryption.

  • IM encryption/Public key is exposed via MQTT topic event/encryption_key/model from the module.

  • Module RSA key/pair is stored in a file under Docker Swarm Secrets location run/secrets. This location is exposed by environment variables public_key_path and private_key_path for both the key pairs with the values as below:
    public_key_path=/run/secrets/<ModuleName>-public
    private_key_path=/run/secrets/<ModuleName>-private

  • Before implementing scenarios, make sure that your object model has secrets defined in its type definition. Otherwise, the Platform will not validate secrets and will not allow adding a secret, either in its device or configuration model types. A platform-defined attribute called secretType should be defined under the respective property to add a secret.

  • If the encryption/decryption has to be supported by the module itself, then the RSA key pair generated by the Edge Agent can be used to encrypt/decrypt secrets.

Below are some examples of type definitions that define their properties to be "secrets":

{
  "model": "abb.ability.device",
  "typeId": "abb.ability.edgeModule.device",
  "version": "1.0.0",
  "properties": {
    "secret1PeristentDevice": {
      "secretType": "persistent",
      "dataType": "string"
    }
  }
}   
{
  "model": "abb.ability.configuration",
  "typeId": "abb.ability.edgeModule.configuration",
  "version": "1.0.0",
  "properties": {
    "secret1PeristentConfig": {
      "secretType": "persistent",
      "dataType": "string"
    }
  }
}

# Secrets Type

The secrets can be of two types under secretType system attribute:

  1. Persistent: Secrets are encrypted with the Public key of the IM and stored in the IM.
  2. End-to-End: Secrets are encrypted with the Public key of the module and stored in the IM.

End-to-end secrets not supported on Ability Edge

Ability Edge does not support end-to-end secrets currently. This is due to the following reasons:

  • The module keys (Pu[d], Pk[d]) are lost whenever abb-iot-edge-replay-netconf is executed or if the Edge Machine is rebooted with abb-iot-edge-replay-netconf.service enabled. As a result, the encrypted secrets cannot be retrieved.
  • If a module is temporarily un-installed and later re-installed, the module keys (Pu[d], Pk[d]) are lost, and thereby, any encrypted secrets cannot be recovered.
  • Keys are also lost when device hardware fails, thereby making the secrets irrecoverable.

Each secret can be further divided into two categories:

  • Regular Secrets: A secret value less than 245 bytes is considered a Regular secret.
  • Large Secrets: Using asymmetric encryption (RSA 2048 bits key sizes) puts a restriction of 245 bytes as the maximum size for the secrets that can be encrypted. It is possible that certain secret values (e.g., private keys) are much larger than 245 bytes and do not fit into the block size for asymmetric encryption. One option is to split the secret value into multiple parts and encrypts each part at the source. The destination must decrypt each part and reconstruct the original secret.

# Storing Secrets

From the type definition perspective, the secrets can be stored either in the module or the device behind the module. The below use cases illustrate the example of storing the secret in the device behind the module. In any case, the module is considered the Secret Reader. In case if a secret needs to be stored in the module, use the module's Object Id instead of the device's.

# Adding Secret from Device and Cloud

This section describes the use cases and procedures to add and retrieve secrets from the device and cloud.

TIP

The mentioned use cases below are for adding the secrets from the device. It is possible to add the secrets from the cloud through IM APIs with similar steps except for one modification:

  • While adding from the cloud, the secretReader attribute needs to be added to the object model. It's a root-level property. This should be the Id of the module present in Principal Manager. So the module can read this secret.
{
"secretReaders": [
    "<Id of the Module as in Principal Manager>"
  ]
}

# Persistent Secret Use Cases

# Use Case 1: Encrypt and Decrypt Regular Secrets

# Adding Secret for Device Behind the Module
  1. Retrieve the Pu[IM] from the MQTT topic.

  2. Consider the secret value as secret1.

  3. Encrypt the secret using IM Public key generated in the above step with the below sequence:
    a. Get the byte array of the secret using UTF-8 encoding.
    b. Import the IM Public key retrieved in the RSA Crypto Service Provider. It should be retrieved in PEM format.
    c. Set the RSA Crypto Service Provider's key size as 2048.
    d. Set the RSA Crypto Service Provider's padding mode as Pkcs1.
    e. Convert the byte array secret value to Base64 String.

  4. Add the encrypted string value in the device model payload as below:

    "secret1PeristentDevice": {
      "secretType":  "persistent",
      "value": "<secret1 Encrypted Secret Value>",       
      "encryptionPublicKey": "<Pu [IM]>"        
    }
    

5A. (If you're using Module API V2) Send Model.Create on standard MQTT V2 topic for registering Device-1 with the secret value, added as above. (If the device already exists, use Model.Update for adding secrets). The sample topic will look as below:
from/module/{ModuleName}/action/model/create&objectId={objectId}&model=abb.ability.device&ack=all

5B. (If you're using Module API V1) Send Device.Create on standard MQTT V1 topic for registering Device-1 with the secret value, added as above. (If the device already exists, use Device.Update for adding secrets). The sample topic will look as below:
modules/{moduleId}/messages/events/type=deviceCreated&objectId={objectId}&ack=all

  1. Once the Device-1 object model has registered successfully along with secrets, then acknowledgment should be received as a success.
# Retrieving Secret for Device Behind the Module

1A. (If you're using Module API V2) Retrieve the Device-1 object model by invoking the Model.Query with the topic sample to publish as below:
from/module/{ModuleName}/action/model/query&ack=all , and the payload:
{"query": "models('abb.ability.device').hasObjectId('<Device1 Object Id>')"}

1B. (If you're using Module API V1) Retrieve the Device-1 object model by invoking the Model.Query with the topic sample to publish as below: modules/{moduleId}/messages/events/type=modelQuery&ack=all , and the payload: {"query": "models('abb.ability.device').hasObjectId('<Device1 Object Id>')"}

  1. Decrypt the secrets using the Private key of the module in the below sequence:
    a. Convert the secret value retrieved into a byte array from the Base64 string format.
    b. Import the PEM format Module Private key retrieved from the Docker Swarm Secrets to the RSA Crypto Service Provider.
    c. Set the RSA Crypto Service Provider's key size as 2048.
    d. Set the RSA Crypto Service Provider's padding mode as Pkcs1.
    e. Decrypt the secret value as a byte array.
    f. Convert the secret byte array to string using UTF-8 encoding.

# Use Case 2: Encrypt and Decrypt Large Secrets

# Adding Secret for Device Behind Module
  1. Retrieve the Pu[IM] from the MQTT topic.
  2. Consider the secret value as below (i.e., some connection string):

Provider=OraOLEDB.Oracle;Data Source= (DESCRIPTION=(CID=GTU_APP)(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP) (HOST=myDatabaseHost-12-54-43-222123-765)(PORT=8080))) (CONNECT_DATA=(SID=MyOracleSID)(SERVER=DEDICATED)));User Id= adminuser123456;Password=qpd6Hsy6nehydbfyey3b;

  1. Generate an Sk as below:
    a. Initialize AES Crypto Service Provider with key size as 128 bits.
    b. Set the Cipher mode as CBC.
    c. Generate the Sk.
    d. Convert the key to the Base64 string.

  2. Encrypt the secret using Sk generated in the above step with Symmetric encryption as below:
    a. Initialize AES Crypto Service Provider with key size as 128 bits.
    b. Set the Cipher mode as CBC.
    c. Generate a random IV for the AES provider.
    d. Get the Sk byte array by UTF-8 encoding.
    e. Create the Encryptor using the Sk, with a random IV generated.
    f. Initialize Streamwriter with the above Encryptor, encrypt it and get the byte array of the secret value.
    g. Convert the secret value to the Base64 string.

  3. Encrypt the Sk with IM Public key using Asymmetric encryption as below:
    a. Import the IM Public key retrieved to the RSA Crypto Service Provider. It should be retrieved in PEM format.
    b. Set the RSA Crypto Service Provider's key size as 2048.
    c. Set the RSA Crypto Service Provider's padding mode as Pkcs1.
    d. Encrypt the Sk using IM Public key.
    e. Convert the byte array of Sk value to Base64String format.

  4. Add the encrypted string value in the device model payload as below:

    "secret1PeristentDevice": {
      "secretType":  "persistent",
      "value": "<secret1 Encrypted Secret Value>",
      "symmetricKey": "<Sk Encrypted Value>", 
      "encryptionPublicKey": "<Pu [IM]>"
    }
    

7A. (If you're using Module API V2) Send Model.Create on standard MQTT V2 topic for registering Device-1 with the secret value (if the device already exists use Model.Update for adding secrets). The sample topic will look as below:
from/module/{ModuleName}/action/model/create&objectId={Device1Object Id}&model=abb.ability.device&ack=all

7B. (If you're using Module API V1) Send Device.Create on standard MQTT V1 topic for registering Device-1 with the secret value (if the device already exists use Device.Update for adding the secrets). The sample topic for device creation will look as below: modules/{moduleId}/messages/events/type=deviceCreated&objectId={Device1Object Id}&ack=all

  1. Once the Device-1 object model has registered successfully along with secrets, then acknowledgment should be received as a success.
# Retrieving Secret for Device Behind Module

1A. (If you're using Module API V2) Retrieve the Device-1 object model by invoking the Model.Query with the topic sample to publish as below:
from/module/{ModuleName}/action/model/query&ack=all payload:
{"query": "models('abb.ability.device').hasObjectId('<Device1 Object Id>')"}

1B. (If you're using Module API V1) Retrieve the Device-1 object model by invoking the Model.Query with the topic sample to publish as below:
modules/{moduleId}/messages/events/type=modelQuery&ack=all payload:
{"query": "models('abb.ability.device').hasObjectId('<Device1 Object Id>')"}

  1. Decrypt the Sk using the Private key of the module in the below sequence using Asymmetric decryption:
    a. Convert the Sk value retrieved into byte array from the Base64 string format.
    b. Import the PEM format Module Private key retrieved from the Docker Swarm Secrets to the RSA Crypto Service Provider.
    c. Set the RSA Crypto Service Provider's key size as 2048.
    d. Set the RSA Crypto Service Provider's padding mode as Pkcs1. e. Decrypt the Sk value as a byte array.
    f. Convert the secret byte array to string using UTF-8 encoding.
  2. Decrypt the secret value using the above Sk with Symmetric decryption as below:
    a. Convert the secret value retrieved as a byte array from the Base64 string.
    b. Initialize AES Crypto Service Provider with key size as 128 bits.
    c. Set the Cipher mode as CBC.
    d. Get the Sk byte array from UTF-8 encoding. Set the AES Provider key with this value.
    e. Pull the IV for the Sk.
    f. Decrypt the secret value using Sk and IV as defined earlier and use an instance of the StreamReader to get the value.
Last updated: 10/20/2021, 8:16:41 AM
Feedback