WF(M)200 Series Secure Link

Introduction

Silicon Labs WF(M)200 chips have the ability to encrypt the SPI or SDIO serial link between the Host and the Device.

The Secure Link capability is only available for trusted chips.

The application note covers the following topics:

Encryption Bitmap

The encryption bitmap defines which messages should be exchanged as encrypted and which ones should remain clear.

It consists in a 32-byte array (i.e., 256 bits), little-endian encoded:

Each bit defines the required encryption state of a message type, based on its message id. For example, bit 11 defines the encryption state for WSM_HI_JOIN_REQ_BODY request (Id = 0x0b):

The whole bitmap can be set globally using a dedicated command HI_SL_CONFIGURE_REQ_BODY and therefore defines the encryption type for all messages (requests, confirmations, indications).

Some messages are never encrypted. The corresponding bit in the bitmap is meaningless for the following messages:

Depending on the Secure Link mode of the device, the bit corresponding to HI_SL_CONFIGURE_REQ_BODY must OR must not be set in the bitmap:

First Time Configuration for Enforced Mode

To prevent a way to bypass the message encryption required by the Host, configure an On-field Device with Secure Link capability to enforced mode by sending the HI_SET_SL_MAC_KEY_REQ_BODY message to the Device just after Reset. This message carries the value of the Secure Link MAC Key, which will be burned in Device OTP memory once and for all. This key is used in the process of the Session Key Computation to authenticate peers during the key exchange and prevent Man-In-The-Middle attacks.

Secure Link MAC Key is a shared secret that must be stored in the Host non-volatile memory as well.

Once burned, the Device is definitely configured in enforced mode.

Startup Sequence for Enforced Mode Device

The following diagram presents the mandatory startup sequence, which must occur before any encrypted message can be sent or received.

  1. After reset, Device sends a Startup Indication to the Host, indicating (among other information) its Secure Link capability (e.g., Secure Link in Enforced mode).

  2. Upon reception of the Startup indication, Host must send a HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY request which includes its curve25519 public key and an associated MAC (Message Authentication Code). Device acknowledges the request immediately by sending a confirmation, and starts computing its own curve25519 key pair and the resulting session key. After completion, Device sends back its own public key and MAC in a dedicated indication. The Host is in turn able to compute the session key, as explained in this section.

  3. After Session Key is computed, the Encryption Bitmap must be set to specify which messages should be encrypted or not. Host will issue a HI_SL_CONFIGURE_REQ_BODY request with the required bitmap as parameter. Device will acknowledge the new bitmap by sending it back in the confirmation message. In Enforced mode, this confirmation must be sent encrypted.

After the Encryption Bitmap is set, the Secure Link is fully configured and Host and Device can exchange encrypted messages.

Secure Link Enforced Diagram

Startup Sequence for Evaluation Mode Device

The following diagram presents the mandatory startup sequence for a chip in Evaluation mode. In this mode, Host is required to set-up a Secure Link MAC Key in the RAM of the Device by sending a Set SL MAC Key request. Once done, the key exchange procedure can continue as usual. On the contrary to Enforced mode, the HI_SL_CONFIGURE_REQ_BODY request must be sent in clear format.

Secure Link Evaluation Diagram

Session Key Computation Algorithm

HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY message has a special Algorithm flag that is used by the Host to specify which cryptographic algorithm should be ran to compute the session key:

The first algorithm provides strong security particularly by providing forward secrecy, but assumes that the Host processor embeds some cryptographic hardware capabilities, since curve25519 software implementations can take several seconds to compute on small MCUs. KDF on the other hand is very lightweight from a computational point of view and is particularly well suited for small MCUs without hardware support for elliptic curve. The counterpart is the loss of the forward secrecy property.

Host Software Implementation Guidelines

The following sections provide guidelines and code examples to help developing a Host driver with Secure Link capabilities.

Session Key Computation

The next sections present Session Key computation for curve25519 and KDF algorithms, which includes 3 main steps

Host Public Key Generation (Curve25519 Algorithm)

Prior to the mandatory key exchange, Host must generate a curve25519 key pair. The public key will be sent as part of the HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY payload. The other requested parameter is the MAC of the key, which is computed using SHA512-based HMAC algorithm. The key used for HMAC is the SL MAC Key.

Warning: Device is expecting the Host Public Key to be sent in little-endian format.

The following code gives an example of Public Key and MAC generation using mbed TLS library.

#define SL_MAC_KEY_SIZE       (256/8)
#define HOST_PUB_KEY_MAC_SIZE (512/8)
#define HOST_PUB_KEY_SIZE     (256/8)

status_t exchange_sl_keys(const uint8_t* sl_mac_key)
{
// Public Key + MAC buffers
uint8_t HostPubKey[HOST_PUB_KEY_SIZE];
uint8_t HostPubKeyMac[HOST_PUB_KEY_MAC_SIZE];

// mbedtls objects declaration
mbedtls_ecdh_context host_context;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context host_drbg_context;
mbedtls_md_context_t host_message_digest_context;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA512;

// mbedtls objects init
mbedtls_ecdh_init(&host_context);
mbedtls_ctr_drbg_init(&host_drbg_context);
mbedtls_entropy_init(&entropy);

// Compute curve25519 key pair
result |= mbedtls_ctr_drbg_seed(&host_drbg_context, mbedtls_entropy_func, &entropy, (const unsigned char *)identifier, sizeof(identifier));
result |= mbedtls_ecp_group_load(&host_context.grp, MBEDTLS_ECP_DP_CURVE25519);
result |= mbedtls_ecdh_gen_public(&host_context.grp, &host_context.d, &host_context.Q, mbedtls_ctr_drbg_random, &host_drbg_context);
result |= mbedtls_mpi_write_binary(&host_context.Q.X, HostPubKey, HOST_PUB_KEY_SIZE);
ERROR_CHECK( result );

// Reverse key endianess to match Device requirement (little-endian)
reverse_bytes(HostPubKey, HOST_PUB_KEY_SIZE);

// Generate SHA512 HMAC of public key
mbedtls_md_init(&host_message_digest_context);
result |= mbedtls_md_setup(&host_message_digest_context, mbedtls_md_info_from_type(md_type), 1); //use hmac
result |= mbedtls_md_hmac_starts(&host_message_digest_context, sl_mac_key, SL_MAC_KEY_SIZE);
result |= mbedtls_md_hmac_update(&host_message_digest_context, HostPubKey, HOST_PUB_KEY_SIZE);
result |= mbedtls_md_hmac_finish(&host_message_digest_context, HostPubKeyMac);
ERROR_CHECK( result );
....

Host Random Number Generation (KDF Algorithm)

For KDF algorithm, Session key generation relies on two 256-bit random numbers generated by each peer of the Secure Link. So Host needs to generate a pure random 256-bit number that will be sent to the Device by HI_SL_EXCHANGE_PUB_KEYS_REQ_BODY Host Public Key parameter.

The other requested parameter is the MAC of the key, which is computed using SHA512-based HMAC algorithm as for curve25519. See previous section for a code example.

Device Public Key / Random Number Checking

When HI_SL_EXCHANGE_PUB_KEYS_IND_BODY gets back, Host must check the received Device Public Key (or Random number in case of KDF algorithm) by computing MAC on the received key and compare the result to the MAC received in the indication.

Warning: for curve25519, Device is sending its Public Key in little-endian format.

The following code is an example of Public Key checking. NcpPubKey and NcpPubKeyMac buffers are supposed to hold the values from the received indication.

// Temporary buffer
uint8_t temp_buffer[512/8];

// Compute the Hash and verify the public key/hashing
mbedtls_md_context_t device_hmac_context;

mbedtls_md_init(&device_hmac_context);
mbedtls_md_setup(&device_hmac_context, mbedtls_md_info_from_type(md_type), 1); //use hmac
mbedtls_md_hmac_starts(&device_hmac_context, sl_mac_key, SL_MAC_KEY_SIZE);
mbedtls_md_hmac_update(&device_hmac_context, NcpPubKey, NCP_PUB_KEY_SIZE);
mbedtls_md_hmac_finish(&device_hmac_context, temp_buffer);

// Check HMAC
if (memcmp(temp_buffer, NcpPubKeyMac, NCP_PUB_KEY_MAC_SIZE) == 0)
{           
    // Received key seems OK -> go on with session key computation
    ...

Session Key Computation (Curve25519 Algorithm)

After the Device Public key has been received and checked, it can be used to compute the Session Key. The following code is an example of Session Key computation based on the Device Public Key and Host Private Key. Note that a last pass of SHA256 is ran on the result of the curve25519 shared secret. The 16 lower bytes are extracted to form the final Session key.

// Calculate session key if public key/SHA512 digest matches
mbedtls_mpi_lset(&host_context.Qp.Z, 1);

// Revert endianess of Device key since mbedtls works in big-endian
reverse_bytes(NcpPubKey, NCP_PUB_KEY_SIZE);
mbedtls_mpi_read_binary(&host_context.Qp.X, NcpPubKey, NCP_PUB_KEY_SIZE);

// Compute curve25519 shared secret
if (mbedtls_ecdh_compute_shared(&host_context.grp, &host_context.z, &host_context.Qp, &host_context.d, mbedtls_ctr_drbg_random, &host_drbg_context) == 0)
{
    // Generate session key
    uint8_t shared_key_digest[256/8];
    mbedtls_mpi_write_binary(&host_context.z, temp_buffer, HOST_PUB_KEY_SIZE);
    // Revert bytes to match endianess of the Device
    reverse_bytes(temp_buffer, HOST_PUB_KEY_SIZE);    
    // Apply sha256 on shared secret
    mbedtls_sha256(temp_buffer, HOST_PUB_KEY_SIZE, shared_key_digest, 0);

    // Get only the lower 16 bytes
    memcpy(secure_link_session_key, shared_key_digest, 16); // Use the lower 16 bytes of the sha256
    ....
    ....
.....
// At then end, do not forget to clean-up all mbedtls objects...
mbedtls_ecdh_free(&host_context);
mbedtls_ctr_drbg_free(&host_drbg_context);
mbedtls_entropy_free(&entropy);

Session Key Computation (KDF Algorithm)

Once Device random number has been extracted and checked from SL_EXCHANGE_PUB_KEYS_IND_BODY, the Session Key can be computed. KDF algorithm is based on HMAC-SHA256 with the following inputs:

Only the first 16 bytes of the HMAC result are extracted to get the final Session Key.

The following Python code is an example implementation.

label = bytes("SecureLink!KeyDerivation", 'utf-8')
hmac_input = bytes(int2bytelist(1, 2, ENDIANNESS.LITTLE_ENDIAN)) +
             label +
             bytes(secure_link.host_pub_key) +
             bytes(secure_link.ineo_pub_key) +
             bytes(int2bytelist(128, 2, ENDIANNESS.LITTLE_ENDIAN))

session_key = hmac.new(bytes(secure_link.sl_mac_key), hmac_input, 'sha256').digest()[:16]

Message Encryption

When the Host has to send an encrypted request to the device, apply the following operations to the clear request message before sending:

This 32-bit length header includes the following information:

Immediately after reset, the Nonce counter value should be initialized at 0 then be incremented after each request sending. In the context of Host sending request to Device, the Nonce counter type must be set to b01 to indicate that a TX counter is sent.

The following table shows the encoding of bits[31:30]:

Bits[31:30]Meaning
b00The message is not encrypted
b01The message is encrypted - Nonce counter type is TX
b10The message is encrypted - Nonce counter type is RX
b11The message is encrypted - Nonce counter type is HP (Reserved)

The header is encoded as little-endian - bit 0 is the first bit of the lower address byte.

Clear Message Encryption

Encrypt both header and payload of the clear request using AES-CCM. Prior to running the encryption, the request must be padded with 0x00 so that the whole header+payload packet is 16-byte aligned. Note that an additional 16-byte CCM tag must be appended after the encrypted payload to allow message authentication by the Device.

AES-CCM Nonce Format

AES-CCM requires a 12-byte length buffer to be passed as input. Depending on the kind of the encrypted message (request, confirmation, indication), the Nonce structure should be as follows:

Message TypeNonce structure
TX (Requests)0x00000000, 0x00000000, TX Counter (4 bytes, Little-endian)
RX (Confirmations/Indications)0x00000000, RX Counter (4 bytes, Little-endian), 0x00000000
HP (reserved)HP Counter (4 bytes, Little-endian), 0x00000000, 0x00000000

Encryption Code Example

The following code gives an example of an AES-CCM encryption using mbed TLS library.

#define SECURE_LINK_CCM_TAG_SIZE                (16)
#define SECURE_LINK_SESSION_KEY_LENGTH          (16)
#define SECURE_LINK_SESSION_KEY_BIT_COUNT       (SECURE_LINK_SESSION_KEY_LENGTH * 8)
#define SECURE_LINK_NONCE_SIZE_BYTES            (12)                
#define HDR_MSG_LEN_SIZE                        (2)                

void encrypt_message( uint8_t* msg, uint8_t msg_length, uint8_t* session_key, uint8_t* nonce )
{
    mbedtls_ccm_context* ccm_context;
    uint8_t *encrypted_msg;
    uint16_t encrypted_msg_length;

    ccm_context = malloc( sizeof(mbedtls_ccm_context) );
    mbedtls_ccm_init( ccm_context );

    encrypted_msg_length = msg_length + SECURE_LINK_CCM_TAG_SIZE - HDR_MSG_LEN_SIZE;
    encrypted_msg = malloc( encrypted_msg_length );
    mbedtls_ccm_setkey( ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SECURE_LINK_SESSION_KEY_BIT_COUNT )

    mbedtls_ccm_encrypt_and_tag( ccm_context, msg_length, nonce, SECURE_LINK_NONCE_SIZE_BYTES, NULL, 0, msg + HDR_MSG_LEN_SIZE, encrypted_msg, encrypted_msg + msg_length - HDR_MSG_LEN_SIZE, SECURE_LINK_CCM_TAG_SIZE );

    // Copy back to same location (+ 2 bytes) to leave room for Secure Link header
    memcpy( msg + SECURE_LINK_HEADER_SIZE + HDR_MSG_LEN_SIZE, encrypted_msg, encrypted_msg_length );

    .... Send encrypted message ....

    free( encrypted_msg );
    free( ccm_context );
}

Message Decryption

When the Host has to receive an encrypted confirmation/indication from the device, apply the following operations to retrieve the clear message:

AES-CCM Nonce Building

If the header[31:30] bits are equal to 'b01', the header[29:0] remaining bits encode the value of the RX counter. This value should be extracted and used to form the whole 12-byte AES Nonce as described in this table.

Clear Message Decryption

After AES Nonce is created, the message payload can be decrypted and checked using AES-CCM.

After decryption, some CCM padding bytes might be removed if message encryption length is not a multiple of 16.

Decryption Code Example

The following code is an example of an AES-CCM decryption using mbed TLS library.

In the following code, msg is assumed pointing on the first encrypted byte (i.e., the second half of the request header) and msg_length is assumed to give the encryption length (which includes the CCM tag size)

status_t decrypt_message(uint8_t* msg, uint8_t msg_length, uint8_t* session_key, uint8_t* nonce)
{
    mbedtls_ccm_context* ccm_context;
    uint8_t*             unencrypted_msg;
    status_t  status = FAILURE;

    ccm_context      = malloc( sizeof(mbedtls_ccm_context) );
    unencrypted_msg = malloc( msg_length );

    mbedtls_ccm_init( ccm_context );

    if ( mbedtls_ccm_setkey( ccm_context, MBEDTLS_CIPHER_ID_AES, session_key, SECURE_LINK_SESSION_KEY_BIT_COUNT ) == 0 )
        {
        if(mbedtls_ccm_auth_decrypt( ccm_context, msg_length - SECURE_LINK_CCM_TAG_SIZE, nonce, SECURE_LINK_NONCE_SIZE_BYTES, NULL, 0, msg, unencrypted_msg, ( &msg[msg_length] ) - SECURE_LINK_CCM_TAG_SIZE, SECURE_LINK_CCM_TAG_SIZE ) != 0)
        {
            // Something failed ... (Authentication ?)
            status = FAILURE;
        } else {
            // Copy clear message back, without the CCM Tag
            memcpy( msg, unencrypted_msg, msg_length - SECURE_LINK_CCM_TAG_SIZE );
            status = SUCCESS;
        }
    }

    .... Process message ....

    // Free memory
    free( unencrypted_msg );
    free( ccm_context );

    return status;
}

Session Key Renegotiation

The Session key should be renegotiated before Nonce counter values exceed the max limit (2^30-1). The Host should track Nonce counter values and request Session Key renegotiation when needed.

Unless DisableSessionKeyProtection was set to 1 when configuring the Secure Link, the Device is checking Nonce limit violation on its side and will emit an error indication when detected.

The Host should use the following sequence to renegotiate the Session Key.

During the transition period when the key exchange request has been sent and indication has not arrived, some RX traffic may be sent by the Device which is encrypted with the current key. It means that Host should keep the current key value until the key exchange indication arrives. Also, Host should stop sending any new encrypted requests since Device is in the process of exchanging its key.

After the indication has arrived, all new RX traffic from Device will be encrypted with the new key. Since Host has to compute the new key on its side, it should buffer all RX traffic until the new key is computed. Then, it will be able to decrypt already received messages and resume sending new requests encrypted with the new key.

Contrary to the Startup sequence, sending an SL Configure request after exchanging the new keys is not mandatory since the original encryption bitmap has not been changed by the renegotiation process and remains valid.

Frequently Asked Questions

Device Trusted or Not

Upon Firmware startup, the Device is sending a first startup indication message. The Startup Indication includes a capabilities.sl_mode sub-field whose value indicates if the device is trusted (i.e., the Secure Link feature is available) or not, and its mode (Evaluation or Enforced).

Evaluation vs Enforced Modes

Evaluation Mode

Evaluation mode is mostly dedicated to Development Kits devices. In Evaluation mode, the OTP memory does not contain the Secure Link MAC Key value. A temporary key can be loaded in the RAM memory of the Device (using HI_SET_SL_MAC_KEY_REQ_BODY with RAM as key destination) that will be used to generate a Session Key for the current Power Cycle. This way, the Host can encrypt/decrypt messages to/from the Device and evaluate the Secure Link capability. In this mode, the default value for the Encryption Bitmap just after Reset is 0x0000...0, meaning that no encryption is required by default.

Enforced Mode

Enforced mode is mostly dedicated to On-field production devices. In Enforced mode, the value of the Secure Link MAC Key has already been burned in OTP memory (using HI_SET_SL_MAC_KEY_REQ_BODY with OTP as key destination) and cannot be modified. The default value for the Encryption Bitmap just after Reset requires all messages to be encrypted except for these exceptions.